Imported from LemonTeaViTA/SageCLI (
AGENTS.md). Install upstream withnpx skills add LemonTeaViTA/SageCLI. Copyright stays with the author.
AGENTS.md
仓库给 Agent / 新线程使用的首读入口。详细行为描述见 docs/agents-reference.md。
信息优先级
- 代码实际行为 > 2.
AGENTS.md> 3.README.md> 4.ROADMAP.md> 5.CLAUDE.md
ROADMAP.md 代表演进方向,不代表已交付。
项目快照
- 项目名:
SageCLI - 定位:面向商业使用的 Java Agent CLI 产品,对标 Claude Code
- 已交付 21 期(ReAct → Plan+DAG → Memory → RAG → Multi-Agent → HITL → 并行工具 → 多模型 → 联网 → MCP 核心 → MCP 高级 → 长上下文 → Chrome DevTools → CDP 会话复用 → Skill → TUI → LSP 诊断 → Side-Git 快照 → Prompt 分层 → Runtime API → 图片输入)
- 下一步:OAuth / sampling / recovery 作为后续 MCP 增强
- Banner 版本:
v16.1.0,Maven 产物:sagecli-1.0-SNAPSHOT.jar(两者不一致是正常状态)
运行前提
- Java 17+ / Maven
- 至少一个 API Key:
GLM_API_KEY/DEEPSEEK_API_KEY/STEP_API_KEY/KIMI_API_KEY
常用命令
cp .env.example .env
mvn clean package # 默认跳过测试,优先产出可手工验收 jar
java -jar target/sagecli-1.0-SNAPSHOT.jar
mvn test -Pquick # 常规回归
mvn test -Pphase16-smoke # TUI 相关
mvn test -Dtest=XxxTest -DskipTests=false # 针对性
mvn test -DskipTests=false # 全量回归
架构概览
三条主执行路径,共享 ToolRegistry / MemoryManager / SnapshotService:
| 路径 | 入口 | 触发 |
|---|---|---|
| ReAct | Agent.java |
默认模式 |
| Plan-and-Execute | PlanExecuteAgent.java |
/plan |
| Multi-Agent | AgentOrchestrator.java |
/team |
/plan / /team 是粘性模式:一旦切入就持续生效,之后每条任务都走该模式,直到用户 /react 退回默认 ReAct、或 /plan↔/team 互切。不再"只对下一条任务生效、执行完自动回 ReAct"。运行中任务用 ESC 中断(不改模式)。
一个对话窗口内三模式共享同一上下文:plan/team 每条任务播种 ReAct 的完整 conversationHistory(去掉其 system 消息),任务跑完再经 Agent.appendExternalTurn() 把"用户输入 + 结果摘要"并入共享历史,超窗自动压缩。所以切回 ReAct / 下一轮 plan/team 都能接上前文,persistTurn 三模式统一走 delta 持久化。
plan 单任务连续失败保护:PlanExecuteAgent 连续 N 轮(默认 2,paicli.plan.max.consecutive.failed.iterations)工具调用全部失败(策略拒绝/异常/超时,靠 ToolExecutionResult.failed() 结构化判定,不匹配文本)即抛 TaskStalledException 判任务失败 → 进自动重规划,避免耗满 5 轮把一堆"🛡️ 策略拒绝"当成"完成"。
会话恢复:对话按项目分组持久化到 ~/.paicli/sessions/<projectHash>/<sessionId>.jsonl(存全 toolCalls/toolCallId,恢复后协议正确);/sessions 列表、/resume <id> 恢复指定、/continue 接最近一次。恢复经 Agent.restoreConversationHistory(),会同步重建 MemoryManager 短期记忆。
可观测性账本:成本/token 账本从 AgentBudget 抽离到 cost/CostLedger(按模型分桶,区分 cache_read 读缓存命中 vs cache_creation 写缓存),跨会话独立 sidecar ~/.paicli/sessions/<projectHash>/<sessionId>.cost.json 持久化(与 message JSONL 分离,后者要无损重放给 LLM)。三条执行路径(Agent/PlanExecuteAgent/SubAgent)收尾把本 run 账本 merge 进 MemoryManager 会话账本;/cost 看本会话 per-model 明细 + 项目历史聚合。定价为占位/参考值,实际计费以平台账单为准、可用 config pricing 覆盖。
核心内置工具 12 个:read_file / write_file / edit_file / list_dir / glob_files / grep_code / execute_command / create_project / search_code / web_search / web_fetch / revert_turn
代码库理解默认走 Claude Code 式实时探索:glob_files 找候选文件、grep_code 精确定位符号或字符串、read_file 按需读取具体行段、edit_file 局部 str_replace 式修改已有文件(优先于 write_file 整文件覆写)。search_code 是 RAG 语义辅助,适合模糊自然语言、关键词不明确、常规搜索无果、巨型/跨知识检索场景,不作为精确代码定位的首选。
MCP 动态工具:mcp__{server}__{tool}(+ resources 虚拟工具)
仓库结构
src/main/java/com/paicli/
├── agent/ Agent.java, PlanExecuteAgent.java, SubAgent.java, AgentOrchestrator.java
├── cli/ Main.java, CliCommandParser.java, PlanReviewInputParser.java
├── browser/ BrowserSession, BrowserGuard, SensitivePagePolicy
├── llm/ GLMClient, DeepSeekClient, StepClient, KimiClient, QwenClient
├── session/ SessionStore, SessionMessageRecord(会话无损持久化与 /resume /continue 恢复,按项目分组)
├── context/ ContextProfile, ContextMode, TokenUsageFormatter
├── cost/ CostLedger, ModelUsage, CostSnapshot(可观测性账本:per-model 成本/token,跨会话 sidecar 持久化)
├── memory/ MemoryManager, ConversationHistoryCompactor, LongTermMemory
├── plan/ Planner, ExecutionPlan, Task
├── rag/ CodeIndex, CodeRetriever, VectorStore, CodeChunker
├── lsp/ LspManager, LspDiagnosticFormatter
├── prompt/ PromptAssembler, PromptContext, PromptRepository
├── image/ ImageReferenceParser
├── runtime/ api/ (RuntimeApiServer) + task/ (DurableTaskManager)
├── snapshot/ SideGitManager, SnapshotService
├── tool/ ToolRegistry
├── mcp/ McpClient, McpServerManager, transport/, resources/, mention/
├── hitl/ HitlToolRegistry, ApprovalPolicy, TerminalHitlHandler
├── web/ SearchProvider, WebFetcher, HtmlExtractor, NetworkPolicy
├── policy/ PathGuard, CommandGuard, AuditLog
├── skill/ SkillRegistry, SkillContextBuffer, SkillIndexFormatter
└── render/ Renderer, InlineRenderer, PlainRenderer, RendererFactory
启动与 inline 渲染当前约定:
- 开屏 Banner 使用无右边框的简洁布局,避免 CJK/ANSI 字宽导致右侧竖线错位;Phase 22 后默认是 π 主题彩色 logo + Qoder 风格首屏,只展示模型、MCP、Skill、ReAct 状态和三条 getting-started tips,不再把 MCP server 明细刷成启动日志。
- inline 模式使用 JLine 4 的 LineReader 编辑能力,默认提示符是
*,右提示显示message / @path / @image。 - 默认 CLI 启动路径应先
Renderer.start()并初始化底部 dock;inline 首屏不要在readLine前裸写 stdout,而是通过InlineRenderer.installStartupScreen(...)挂到LineReader.CALLBACK_INIT,首次进入输入时用printAbove一次性显示完整 Banner + tips,避免 logo 被 LineReader 首次重绘滚出可视区域。 BottomStatusBar现在是 JLineStatus托管的底部 dock:由 JLine 维护滚动区域和状态行位置,不再手写\n/moveUp/CLEAR_TO_EOS清屏。输入期会把 LineReader 光标定位到 dock 上方一行,让*输入行和 Status 同处底部区域;dock 保留两类信息:上层模式 + MCP/Skill 摘要,下层 Auto Model / model / phase / ctx 百分比与 token / cost / elapsed / cwd。- 普通任务和斜杠命令提交后,
Main会把本轮原始输入以暗色整行块写回 transcript:输入态左提示仍是*,提交回显左提示改为>;单行输入只占一行,不额外追加空白行。普通任务随后再展开 MCP resource / 本地@path并进入 Agent;不要只依赖 JLine 提交行残留,否则 activity 重绘或 dock 刷新可能让用户输入从可见历史里消失。 - ReAct LLM 调用期间,inline renderer 使用固定高度 live thinking 区动态显示
Thinking...和灰色竖线 reasoning 预览;该区域只能清理自己刚打印的几行,不能用独立 JLineDisplay.update()/CLEAR_TO_EOS向上覆盖 transcript。content 或 tool call 开始前先清掉 live 区,再把完整 reasoning 引用块落到正文区,正文回答用低调标记起始,不再刷强标题。 - 交互期输出应优先走
Renderer.stream();Main、PlanExecuteAgent、Planner、AgentOrchestrator都支持把输出流接到 inline renderer,避免直接争抢 stdout。CodeIndex的索引进度通过ProgressListener注入,/index应绑定到当前 renderer 输出流。 - Phase 22 开始,
InlineRenderer可绑定当前LineReader;当LineReader.isReading()为 true 时,Renderer.stream()的完整行输出优先通过LineReader#printAbove显示在输入行上方,未绑定 / 非读取态 / 测试路径回退到原PrintStream。 - ReAct 正常结束后不再把
📊 Token: ...打进正文区;token/cost/elapsed 会保留在底部强状态行,phase 回到idle。 - 默认 CLI 启动路径应尽早建立
Terminal -> LineReader -> Renderer,启动 Banner、模型加载、MCP 启动、Skill summary、ReAct 提示和退出提示都应走Renderer.stream();除 fatal bootstrap / runtime API / legacy TUI 降级外,不要在交互主路径新增裸System.out.println。 - 启动期 MCP 不得阻塞首屏:CLI 默认最多等待 8 秒(
PAICLI_MCP_STARTUP_WAIT_SECONDS/-Dpaicli.mcp.startup.wait.seconds可调),超时后保留未完成 server 为STARTING并后台继续初始化;/mcp查看最新状态。 LineReader使用PaiCliHighlighter做输入实时高亮:slash 命令、@引用、@image:、@clipboard、敏感词和明显危险 shell 片段会在编辑阶段被标记;不要把这类视觉提示混入最终提交文本。LineReader使用PaiCliCompleter做上下文补全:/modelprovider、/mcp子命令与 server、/skill子命令与 skill name、/task//browser//snapshot子命令、@image:本地路径、本地@path和 MCP resource@server:uri引用都应从同一个 completer 出口维护。- 普通用户输入进入 Agent 前会先展开 MCP resource mention,再由
LocalPathMentionExpander展开本地@path:文件会内联为<file>块,目录会内联为<directory>列表;绝对路径或符号链接逃逸项目根时保持原文不展开。 LineReader使用PaiCliHistory持久化输入历史到~/.paicli/history/input.history;如果paicli.history.file/PAICLI_HISTORY_FILE指向目录,也会自动使用该目录下的input.history,避免把目录当文件读;默认忽略空白、重复、明显密钥/Bearer、base64 图片和超长输入,用户可用/history clear清空本机输入历史。- JLine 交互升级计划记录在
docs/phase-22-jline-interaction-upgrade.md。
关键行为约束(Agent 必读)
Memory
- 长期记忆只通过
/save或用户明确要求保存;不要自动提取事实 - 长期记忆只保存跨会话稳定事实,不保存临时指令;默认项目级作用域,跨项目通用偏好才用 global
- 长期记忆必须可审计和可删除:
/memory list//memory search <关键词>//memory delete <id>//memory clear - 两道压缩不要混淆:shortTermMemory 压缩 vs conversationHistory 压缩(后者是防 window 超限的关键)
HITL + 策略层
- 拦截顺序:HitlToolRegistry → ToolRegistry → PathGuard/CommandGuard
- 用户无法批准策略拒绝的请求
- PathGuard 强制路径限定在项目根内
- CommandGuard 是辅助黑名单,不是主防线
Plan 审阅交互
Enter执行 /Ctrl+O展开 /ESC取消 /I补充重规划- 方向键不应被误判为 ESC
- 涉及改动要连 raw mode 和回退路径一起看
并行工具
- 三条路径都走
executeTools(),不手写 for-loop - 默认最多 4 个并发,结果保持原始顺序
Web + Browser
- 已知 URL 先
web_fetch,SPA/防爬墙 fallback 到 Chrome DevTools MCP - 浏览器读取优先
take_snapshot,不默认take_screenshot - 公开页面不要提前切 shared 模式
Skill
- system prompt 索引段注入三处提示词,上限 20 个 / 4KB
load_skill→ SkillContextBuffer → 下一轮 user message 前置注入
修改时的硬规则
1. 改行为 → 同步文档
AGENTS.md / README.md / ROADMAP.md(仅状态变化时)
2. 改命令入口 → 联动
Main.java + CliCommandParser.java + 测试 + README.md + AGENTS.md
未识别的 /xxx 在 CLI 层直接报"未知命令",不回退给 Agent。
3. 改 Plan 审阅交互 → 联动
Main.java + PlanReviewInputParser.java + 测试 + 手工验证
4. 改工具集 → 联动
ToolRegistry.java + Agent/PlanExecuteAgent/SubAgent 提示词 + 可能 Planner 提示词 + 文档
5. 改模型/接口 → 联动
对应 Client + LlmClientFactory.java + .env.example + 文档
5.1 改 Embedding → EmbeddingClient + VectorStore + .env.example + 文档
5.2 改 Web/搜索 → web/ 相关 + ToolRegistry + .env.example + 文档 + 测试
5.3 改 Memory → MemoryManager + LongTermMemory + TokenBudget + 测试 + 文档
5.4 改 HITL/策略 → policy/ + ToolRegistry + HitlToolRegistry + 提示词 + .env.example + 文档 + 测试
5.5 改 MCP → mcp/ + ToolRegistry + HITL + AuditLog + 提示词 + 文档 + 测试
6. 不提交 .env / 真实 API Key / target/ 产物
7. 保持代码可读性,不过度抽象
验证路径
| 场景 | 命令 |
|---|---|
| 代码搜索工具 | mvn test -Dtest=ToolRegistryTest,ApprovalPolicyTest |
| 命令解析 | mvn test -Dtest=CliCommandParserTest,PlanReviewInputParserTest,MainInputNormalizationTest |
| DAG/Plan | mvn test -Dtest=ExecutionPlanTest |
| Multi-Agent | mvn test -Dtest=AgentRoleTest,AgentMessageTest,AgentOrchestratorTest |
| TUI/终端 | mvn test -Pphase16-smoke |
| RAG | mvn test -Dtest=CodeChunkerTest,CodeAnalyzerTest,VectorStoreTest,CodeIndexTest |
| 常规回归 | mvn test -Pquick |
给新线程的导航
- 先看本文件 → 2.
README.md→ 3.Main.java→ 4. 按任务进入对应模块
| 任务类型 | 先看 |
|---|---|
| CLI 命令 | Main.java + CliCommandParser.java |
| 规划/DAG | PlanExecuteAgent.java + Planner.java + ExecutionPlan.java |
| 工具调用 | ToolRegistry.java + Agent.java |
| 代码搜索 | ToolRegistry.java (glob_files / grep_code / read_file) |
| 模型/API | llm/*Client.java + LlmClientFactory.java |
| RAG 语义辅助 | CodeRetriever.java + CodeIndex.java + VectorStore.java |
| Multi-Agent | AgentOrchestrator.java + SubAgent.java |
| MCP | McpServerManager.java + McpClient.java |
| TUI/渲染 | render/Renderer.java + RendererFactory.java |
当前已知边界
以下在路线图但未交付:容器/VM 沙箱 / MCP OAuth + sampling + server 自动重启
不要把 ROADMAP.md 中"将来要做"误读成"现在已有"。
持续维护约定
形成稳定协作规则时直接补进本文件,不要只留在聊天记录里。详细实现细节补到 docs/agents-reference.md。
