一个大道至简的命令行 AI 编程工具:在 Docker 容器里让模型通过“只提供命令行”完成编程任务,并用左右分栏 TUI 提升 16:9 屏幕下的可用性。
在项目目录下:
docker build --build-arg UID="$(id -u)" --build-arg GID="$(id -g)" -t cmd-ai-dev:latest .在你的项目目录(要让 AI 修改的代码库目录)运行:
docker run -it --rm \
--network host \
-e OPENAI_API_KEY="你的key" \
-e OPENAI_MODEL="你的模型名" \
-e OPENAI_BASE_URL="你的base_url(可选,OpenAI-compatible 时用)" \
-v "$PWD:/workspace" \
--shm-size=2g \
-e OPENAI_SUPPORTS_VISION="是否支持视觉,填false或true" \
cmd-ai-dev:latest如果你要挂载git配置到容器,并且你拥有以下每个目录/文件,则添加:
-v "$HOME/.gitconfig:/home/ai/.gitconfig:ro" \
-v "$HOME/.config/git:/home/ai/.config/git:ro" \
-v "$HOME/.ssh:/home/ai/.ssh:ro" \
-v "$HOME/.git-credentials:/home/ai/.git-credentials:ro" \
-v "$HOME/.netrc:/home/ai/.netrc:ro" \
-v "$HOME/.gnupg:/home/ai/.gnupg" \
-e GNUPGHOME="/home/ai/.gnupg" \建议检查这些目录/文件在你的电脑上是否存在,只添加存在的。
比如有的人的电脑没有$HOME/.config/git
启动后:
- 左侧:模型输出 / 命令执行记录
- 右侧:多行输入框(适合粘贴长文本)
- 快捷键(终端兼容性优先):
Ctrl+S发送Ctrl+T停止命令链Ctrl+R重置会话Ctrl+Q退出
也提供按钮:发送 / 停止 / 重置 / 退出
在项目目录下:
docker build --build-arg UID="$(id -u)" --build-arg GID="$(id -g)" -t cmd-ai-dev:latest .把下面内容粘贴到 ~/.bashrc,然后 source ~/.bashrc:
cmd_ai_dev() {
# 关键:用交互子 bash 包一层,避免主 shell 在 Tab 补全时崩溃
# 用 CMD_AI_DEV_INNER 防止递归
if [ -z "${CMD_AI_DEV_INNER:-}" ]; then
CMD_AI_DEV_INNER=1 bash -i -c 'cmd_ai_dev "$@"' bash "$@"
return $?
fi
set -e
# ====== 你需要填的配置 ======
local OPENAI_API_KEY="填你的key"
local OPENAI_BASE_URL="填你的base_url(可留空)"
local OPENAI_MODEL="填你的模型名"
local OPENAI_SUPPORTS_VISION="是否支持视觉,填false或true"
local IMAGE_NAME="${CMD_AI_DEV_IMAGE:-cmd-ai-dev:latest}"
# ===========================
local proj_dir="${1:-$PWD}"
if [ ! -d "$proj_dir" ]; then
echo "目录不存在:$proj_dir" >&2
return 1
fi
proj_dir="$(cd "$proj_dir" && pwd)"
local proj_base parent_base
proj_base="$(basename "$proj_dir")"
parent_base="$(basename "$(dirname "$proj_dir")")"
local cname="cmd-ai-dev-${parent_base}-${proj_base}"
cname="$(echo "$cname" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')"
local base_url_args=()
if [ -n "$OPENAI_BASE_URL" ]; then
base_url_args=(-e "OPENAI_BASE_URL=$OPENAI_BASE_URL")
fi
local gitconfig_args=()
if [ -e "$HOME/.gitconfig" ]; then
gitconfig_args=(-v "$HOME/.gitconfig:/home/ai/.gitconfig:ro")
fi
local config_git_args=()
if [ -e "$HOME/.config/git" ]; then
config_git_args=(-v "$HOME/.config/git:/home/ai/.config/git:ro")
fi
local ssh_args=()
if [ -e "$HOME/.ssh" ]; then
ssh_args=(-v "$HOME/.ssh:/home/ai/.ssh:ro")
fi
local git_credentials_args=()
if [ -e "$HOME/.git-credentials" ]; then
git_credentials_args=(-v "$HOME/.git-credentials:/home/ai/.git-credentials:ro")
fi
local netrc_args=()
if [ -e "$HOME/.netrc" ]; then
netrc_args=(-v "$HOME/.netrc:/home/ai/.netrc:ro")
fi
local gnupg_args=()
local gnupg_exec_args=()
if [ -e "$HOME/.gnupg" ]; then
gnupg_args=(-v "$HOME/.gnupg:/home/ai/.gnupg" -e GNUPGHOME="/home/ai/.gnupg")
gnupg_exec_args=(-e GNUPGHOME="/home/ai/.gnupg")
fi
if docker container inspect "$cname" >/dev/null 2>&1; then
# 容器存在
if [ "$(docker inspect -f '{{.State.Running}}' "$cname" 2>/dev/null)" = "true" ]; then
# 运行中:exec 进去跑工具(并把 key/model/base_url 注入)
docker exec -it \
"${gnupg_exec_args[@]}" \
-e "OPENAI_API_KEY=$OPENAI_API_KEY" \
"${base_url_args[@]}" \
-e "OPENAI_MODEL=$OPENAI_MODEL" \
-e "OPENAI_SUPPORTS_VISION=$OPENAI_SUPPORTS_VISION" \
-e "TERM=${TERM:-xterm-256color}" \
"$cname" cmd-ai-dev
else
# 已存在但未运行:直接 start -ai(注意:env 使用创建容器时的 env;如果你改了 key/model,建议 rm 容器重建)
docker start -ai "$cname"
fi
else
# 容器不存在:创建并运行(host 网络 + 映射 /workspace)
docker run -it --name "$cname" --network host \
"${gitconfig_args[@]}" \
"${config_git_args[@]}" \
"${ssh_args[@]}" \
"${git_credentials_args[@]}" \
"${netrc_args[@]}" \
"${gnupg_args[@]}" \
-e "OPENAI_API_KEY=$OPENAI_API_KEY" \
"${base_url_args[@]}" \
-e "OPENAI_MODEL=$OPENAI_MODEL" \
-e "OPENAI_SUPPORTS_VISION=$OPENAI_SUPPORTS_VISION" \
-e "TERM=${TERM:-xterm-256color}" \
-v "$proj_dir:/workspace" \
--shm-size=2g \
"$IMAGE_NAME"
fi
}
alias cmd-ai-dev=cmd_ai_dev之后你就可以:
cmd-ai-dev
# 或指定目录
cmd-ai-dev /path/to/your/project如果要修改OPENAI_MODEL、OPENAI_BASE_URL、OPENAI_API_KEY、OPENAI_SUPPORTS_VISION则需要先删除已存在的cmd-ai-dev容器,再执行cmd-ai-dev命令
/workspace:用户项目目录(映射到宿主机)/workspace-ai:AI 工作目录(默认不映射;用于临时脚本、日志、中间产物,避免污染仓库)/workspace-ai/session.json:会话上下文(单会话)/workspace-ai/cmdout_*.log:命令输出落盘日志/workspace-ai/transcript.log:左侧显示内容的完整记录(便于复制)
模型需要执行命令时输出:
<cmd>
<time_out>60</time_out>
这里是要执行的命令(可多行)
</cmd>
这里是模型的回答(可选)
工具执行后回给模型:
<cmdout>
[exit=... timeout=0/1 interrupted=0/1]
这里是命令输出(可能截断)
</cmdout>
这里是用户的交互(可选)
- 单文件实现:核心逻辑集中在一个
cmd-ai-dev.py,没有复杂的项目结构。 - 依赖的 AI 模型易于更改:对 OpenAI-compatible 接口友好,封装层便于替换为自发 HTTP 请求。
- 给模型高灵活性但不污染系统/项目:
- 模型可通过命令行做几乎任何事(读写文件、跑脚本、安装包等)
- 但运行在 Docker 容器中,宿主机更安全
- AI 的临时产物默认落在
/workspace-ai,避免污染你的 Git 仓库
- 工具极简:只把“命令行”作为工具提供给模型,不引入可能受限或陌生的工具体系。
- 不依赖函数调用:自定义
<cmd>/<cmdout>格式,即使模型不支持 function calling 也能工作。 - 左右分栏布局:模型输出在左、用户输入在右,对 16:9 屏幕更友好,适合长输出与长输入并存的场景。
- 支持 Markdown 渲染:模型的叙述输出会以 Markdown 方式渲染(
<cmd>/<cmdout>仍保持工具样式)。
- 终端兼容性:不同终端对 TUI、IME(中文标点等)、选中复制、组合键的支持差异很大。
- 为降低风险,提供按钮操作与备选快捷键,并将左侧内容落盘到
/workspace-ai/transcript.log作为复制兜底。 - 尝试按住shift以实现用鼠标选中文本。
- 为降低风险,提供按钮操作与备选快捷键,并将左侧内容落盘到
- 如果想查看AI工作目录的文件,你可以告诉它“请你运行/usr/bin/code-server,不要设置密码。”
- 你可以对接OpenAI API之外的任何接口/平台,将它作为支持AI编程工具的模型。
- 你可以在容器外宿主机上执行
google-chrome --remote-debugging-port=9222 --user-data-dir=./cdp-profile,这将打开一个浏览器,然后你取消这段代码self.llm: LLMClient = ChatZAISDKClient()的注释并重新构建镜像,就可以将 https://chat.z.ai 网站作为支持AI编程工具的模型了。执行命令时,如果找不到google-chrome,你需要指定google-chrome二进制可执行文件的路径。 - 当你使用网站作为支持AI编程工具的模型时,则无需填写OPENAI_BASE_URL、OPENAI_API_KEY、OPENAI_MODEL、OPENAI_SUPPORTS_VISION。
- 新增了环境变量OPENAI_SUPPORTS_VISION,它代表模型是否支持视觉,填false或true,暂只对
self.llm: LLMClient = OpenAISDKClient()做了适配,未适配网站作为支持AI编程工具的模型,因为传图片到网站可能稳定较差,复杂度稍度高。 - 新增了操作浏览器功能,模型可以操作浏览器来搜索网页或者进行其他浏览器操作了🎉。
本项目在编写过程中参考并借助了 GPT-5.2-High 生成的代码建议与改进方案。