Current section
Files
Jump to
Current section
Files
lib/mix/tasks/tui.ex
defmodule Mix.Tasks.Tui do
@shortdoc "启动 CMDC TUI 终端界面"
@moduledoc """
启动 CMDC TUI 终端界面。
## 用法
# 纯输入测试(无 Agent)
mix tui
# 连接 LLM Agent(无工具)
LLM_API_KEY=sk-xxx LLM_BASE_URL=https://... mix tui
# 连接 LLM Agent(带工具)
LLM_API_KEY=sk-xxx LLM_TOOLS=shell,read_file,list_dir mix tui
## 环境变量
- `LLM_API_KEY` — LLM API 密钥(必须,连接 Agent 时)
- `LLM_BASE_URL` — LLM API 地址(可选,默认 Anthropic 官方)
- `LLM_MODEL_ID` — 模型 ID(默认 MiniMax-M2.7)
- `LLM_SYSTEM_PROMPT` — 系统提示词(默认 "你是一个 AI 助手。")
- `LLM_TOOLS` — 启用的工具列表,逗号分隔(可选)
- `LLM_WORKING_DIR` — Agent 工作目录(默认当前目录)
## 可用工具
shell, read_file, write_file, edit_file, list_dir, glob, grep,
ask_user, write_todos, compact_conversation, task
"""
use Mix.Task
@tool_map %{
"shell" => CMDC.Tool.Shell,
"read_file" => CMDC.Tool.ReadFile,
"write_file" => CMDC.Tool.WriteFile,
"edit_file" => CMDC.Tool.EditFile,
"list_dir" => CMDC.Tool.ListDir,
"glob" => CMDC.Tool.Glob,
"grep" => CMDC.Tool.Grep,
"ask_user" => CMDC.Tool.AskUser,
"write_todos" => CMDC.Tool.WriteTodos,
"compact_conversation" => CMDC.Tool.CompactConversation,
"task" => CMDC.Tool.Task
}
@impl true
def run(_args) do
Mix.Task.run("app.start")
api_key = System.get_env("LLM_API_KEY")
base_url = System.get_env("LLM_BASE_URL")
model_id = System.get_env("LLM_MODEL_ID", "MiniMax-M2.7")
system_prompt = System.get_env("LLM_SYSTEM_PROMPT", "你是一个 AI 助手。")
tools_env = System.get_env("LLM_TOOLS", "")
working_dir = System.get_env("LLM_WORKING_DIR", File.cwd!())
opts =
if api_key do
model =
if base_url do
%{provider: :anthropic, id: model_id, base_url: base_url}
else
"anthropic:#{model_id}"
end
tools = parse_tools(tools_env)
agent_opts =
[
model: model,
system_prompt: system_prompt,
provider_opts: [api_key: api_key],
working_dir: working_dir
] ++ if(tools != [], do: [tools: tools], else: [])
case CMDC.create_agent(agent_opts) do
{:ok, session} ->
tool_names = Enum.map(tools, & &1.name())
tool_info = if tools != [], do: " | tools: #{Enum.join(tool_names, ",")}", else: ""
IO.puts("Agent ready: #{model_id}#{tool_info}")
[session: session, session_id: "live", model_name: model_id]
{:error, reason} ->
Mix.shell().error("Agent 创建失败: #{inspect(reason)}")
System.halt(1)
end
else
[]
end
CmdcTui.start(opts)
end
defp parse_tools(""), do: []
defp parse_tools(tools_str) do
tools_str
|> String.split(",", trim: true)
|> Enum.map(&String.trim/1)
|> Enum.flat_map(fn name ->
case Map.get(@tool_map, name) do
nil ->
IO.puts("Warning: unknown tool '#{name}', skipping. Available: #{available_tools()}")
[]
mod ->
[mod]
end
end)
end
defp available_tools, do: @tool_map |> Map.keys() |> Enum.sort() |> Enum.join(", ")
end