min-agent 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -31
- package/dist/agent.js +1233 -485
- package/dist/assistant-stream.js +11 -7
- package/dist/cli/commands/chat.js +10 -0
- package/dist/cli/commands/exec.js +32 -0
- package/dist/cli/commands/history.js +58 -0
- package/dist/cli/commands/index.js +224 -0
- package/dist/cli/commands/init.js +18 -0
- package/dist/cli/commands/mcp.js +173 -0
- package/dist/cli/commands/memory.js +69 -0
- package/dist/cli/commands/models.js +21 -0
- package/dist/cli/commands/permission.js +12 -0
- package/dist/cli/commands/rules.js +33 -0
- package/dist/cli/commands/sandbox.js +13 -0
- package/dist/cli/commands/serve.js +9 -0
- package/dist/cli/commands/setup.js +4 -0
- package/dist/cli/commands/shared.js +16 -0
- package/dist/cli/commands/skills.js +119 -0
- package/dist/cli/commands/update.js +7 -0
- package/dist/cli/commands/write-config.js +30 -0
- package/dist/cli/errors.js +36 -0
- package/dist/cli/exec-prompt.js +26 -0
- package/dist/cli/option-helpers.js +53 -0
- package/dist/cli/program.js +180 -0
- package/dist/cli.js +7 -632
- package/dist/clipboard.js +59 -23
- package/dist/code-mode.js +35 -17
- package/dist/compaction.js +457 -169
- package/dist/config.js +298 -38
- package/dist/confirm.js +105 -9
- package/dist/context-window.js +156 -75
- package/dist/doom-loop.js +268 -26
- package/dist/fetch-timeout.js +152 -0
- package/dist/http-approvals.js +60 -0
- package/dist/http.js +119 -0
- package/dist/instructions.js +72 -33
- package/dist/logger.js +95 -0
- package/dist/markdown.js +35 -50
- package/dist/mcp.js +847 -102
- package/dist/memory.js +128 -45
- package/dist/output.js +42 -31
- package/dist/paste-handler.js +3 -3
- package/dist/permission-cli.js +43 -0
- package/dist/plugins.js +76 -11
- package/dist/pricing.js +119 -0
- package/dist/provider.js +34 -15
- package/dist/question-format.js +60 -0
- package/dist/sandbox-cli.js +82 -0
- package/dist/sandbox.js +403 -0
- package/dist/save-throttle.js +45 -0
- package/dist/serve/common.js +404 -0
- package/dist/serve/routes-chat.js +347 -0
- package/dist/serve/routes-mcp.js +212 -0
- package/dist/serve/routes-memory.js +66 -0
- package/dist/serve/routes-meta.js +205 -0
- package/dist/serve/routes-sessions.js +61 -0
- package/dist/serve/routes-skills.js +70 -0
- package/dist/serve.js +74 -635
- package/dist/sessions.js +197 -15
- package/dist/skills.js +531 -77
- package/dist/synthetic.js +7 -0
- package/dist/title-gen.js +9 -2
- package/dist/token-display.js +36 -0
- package/dist/tool-display.js +178 -0
- package/dist/tool-output.js +53 -46
- package/dist/tools/apply_patch.js +265 -0
- package/dist/tools/atomic-file.js +35 -0
- package/dist/tools/backend.js +61 -0
- package/dist/tools/bash.js +186 -71
- package/dist/tools/code_search.js +13 -6
- package/dist/tools/edit.js +26 -9
- package/dist/tools/explore.js +144 -16
- package/dist/tools/glob.js +7 -3
- package/dist/tools/grep.js +153 -14
- package/dist/tools/index.js +9 -24
- package/dist/tools/question.js +31 -30
- package/dist/tools/read.js +77 -15
- package/dist/tools/search-searxng.js +223 -0
- package/dist/tools/search-serper.js +189 -0
- package/dist/tools/task.js +100 -33
- package/dist/tools/todo.js +178 -67
- package/dist/tools/web_fetch.js +158 -46
- package/dist/tools/web_search.js +217 -29
- package/dist/tools/write.js +34 -11
- package/dist/tui/App.js +89 -6
- package/dist/tui/ConfirmBar.js +57 -4
- package/dist/tui/InputBar.js +504 -44
- package/dist/tui/MessageList.js +674 -20
- package/dist/tui/ModelPicker.js +113 -0
- package/dist/tui/QuestionBar.js +136 -0
- package/dist/tui/SessionPicker.js +79 -0
- package/dist/tui/StatusBar.js +14 -12
- package/dist/tui/agent-runner.js +223 -0
- package/dist/tui/caret-pos.js +177 -0
- package/dist/tui/caret.js +69 -0
- package/dist/tui/click-count.js +13 -0
- package/dist/tui/diff-view.js +61 -0
- package/dist/tui/drag-state.js +49 -0
- package/dist/tui/hydrate.js +129 -0
- package/dist/tui/index.js +189 -31
- package/dist/tui/input-history.js +125 -0
- package/dist/tui/layout.js +88 -0
- package/dist/tui/mouse.js +46 -0
- package/dist/tui/prompt-queue.js +24 -0
- package/dist/tui/selection.js +226 -0
- package/dist/tui/session-switch.js +28 -0
- package/dist/tui/slash-commands.js +106 -0
- package/dist/tui/slash-handler.js +545 -0
- package/dist/tui/text-width.js +113 -0
- package/dist/tui/theme.js +12 -0
- package/dist/tui/token-info.js +7 -0
- package/dist/tui/tool-children.js +19 -0
- package/dist/tui/undo-stack.js +14 -0
- package/dist/tui/use-sgr-mouse.js +29 -0
- package/dist/tui-chat.js +346 -330
- package/dist/updater.js +116 -0
- package/dist/xml-search.js +194 -0
- package/docs/API.md +410 -32
- package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +1510 -0
- package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +2105 -0
- package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +1595 -0
- package/docs/superpowers/plans/2026-08-16-input-caret.md +782 -0
- package/docs/superpowers/plans/2026-08-20-tui-completeness.md +873 -0
- package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +631 -0
- package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +183 -0
- package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +220 -0
- package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +196 -0
- package/docs/superpowers/specs/2026-08-16-input-caret-design.md +63 -0
- package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +116 -0
- package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +47 -0
- package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +37 -0
- package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +68 -0
- package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +273 -0
- package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +165 -0
- package/package.json +12 -8
- package/skills/self-config/SKILL.md +90 -0
- package/skills/self-config/reference.md +149 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# 消息区鼠标拖拽选择与自动复制设计
|
|
2
|
+
|
|
3
|
+
日期:2026-08-17
|
|
4
|
+
状态:已批准(用户确认方案 A:应用内拖拽选择 + 自动复制)
|
|
5
|
+
|
|
6
|
+
## 问题
|
|
7
|
+
|
|
8
|
+
min-agent TUI 启用了 SGR 鼠标跟踪(1000 模式),终端原生拖拽选择被鼠标协议接管,
|
|
9
|
+
消息区(内容区)文本无法用鼠标选取、复制。参考 opencode / claude code 的交互:
|
|
10
|
+
在消息区按住左键拖拽高亮文本,松开自动复制到系统剪贴板。
|
|
11
|
+
|
|
12
|
+
## 目标
|
|
13
|
+
|
|
14
|
+
1. 消息区按住左键拖拽 → 选区实时高亮(反显)
|
|
15
|
+
2. 松开 → 自动复制到系统剪贴板(不占用 Ctrl+C,它仍用于退出)
|
|
16
|
+
3. 选区保持高亮直到下次点击/按键,状态栏短暂提示"已复制"
|
|
17
|
+
4. 原有功能不回归:单击展开工具行、滚轮滚动、输入框点击定位光标、Ctrl+C 退出
|
|
18
|
+
|
|
19
|
+
## 设计
|
|
20
|
+
|
|
21
|
+
### 1. 鼠标协议升级(`src/tui/mouse.ts`)
|
|
22
|
+
|
|
23
|
+
- `MOUSE_ENABLE` = `\x1b[?1000h\x1b[?1002h\x1b[?1006h`(1002 = 按住按钮时报告拖拽移动)
|
|
24
|
+
- `MOUSE_DISABLE` 同步加 `?1002l`
|
|
25
|
+
- 新增 `MOTION = 32` 常量:SGR 移动事件按钮码 = 基础按钮 + 32;修饰键(Shift=4 等)
|
|
26
|
+
与基础按钮同在一个字段,`button & 15 === 0` 判断为无修饰键的纯左键
|
|
27
|
+
|
|
28
|
+
### 2. 剪贴板写入(`src/clipboard.ts`)
|
|
29
|
+
|
|
30
|
+
新增 `writeClipboard(text: string): boolean`,平台分发与现有读剪贴板一致:
|
|
31
|
+
- macOS:`pbcopy`(execSync input 管道,避免 shell 引号问题)
|
|
32
|
+
- Linux:`xclip -selection clipboard`,失败回退 `xsel --clipboard --input`
|
|
33
|
+
- Windows:PowerShell `Set-Clipboard -Value ([Console]::In.ReadToEnd())`
|
|
34
|
+
|
|
35
|
+
失败静默返回 false,不弹错。
|
|
36
|
+
|
|
37
|
+
### 3. 选区纯函数模块(`src/tui/selection.ts`,新)
|
|
38
|
+
|
|
39
|
+
全部按 `charWidth` 处理 CJK 宽度,输入为 ANSI 行与 0-based 显示列:
|
|
40
|
+
|
|
41
|
+
- `stripAnsi(line)`:去掉 `\x1b[...m` 颜色码
|
|
42
|
+
- `applySelection(line, start, end)`:在选区边界注入 `\x1b[7m` / `\x1b[27m`(反显),
|
|
43
|
+
保留原有样式;选区内遇到 `\x1b[0m`(RESET)时在其后重注入 `\x1b[7m`,保证跨样式
|
|
44
|
+
重置时高亮不断裂。与选区有交叠的字符(含全角)整字符高亮,不动字符边界
|
|
45
|
+
- `extractText(line, start, end)`:按显示列切出选区纯文本(剥 ANSI)
|
|
46
|
+
- `selectionRanges(rows, anchor, current)`:把拖拽锚点/当前点(0-based 坐标:
|
|
47
|
+
行 = `areaRows` 索引,列 = 行内显示列,由 MessageList 从终端坐标换算)归一为
|
|
48
|
+
与 `rows` 对齐的逐行选区,流式语义:
|
|
49
|
+
- 同行:`[min(col), max(col))`
|
|
50
|
+
- 跨行(向下拖):首行 `[anchor.col, 行尾)`,中间行整行,末行 `[0, current.col)`
|
|
51
|
+
- 跨行(向上拖):对称反转
|
|
52
|
+
- `rows` 中 null(指示行/空白行)跳过;坐标夹在消息区范围内
|
|
53
|
+
- 返回 `(start,end | null)[]`,与 `rows` 对齐
|
|
54
|
+
|
|
55
|
+
### 4. MessageList 集成(`src/tui/MessageList.tsx`)
|
|
56
|
+
|
|
57
|
+
**状态**:`selection: { anchor: Cell; cur: Cell } | null`(终端 1-based 坐标);
|
|
58
|
+
`pendingPress` ref 记录按下点,用于区分单击与拖拽——按下只记录,出现移动事件才
|
|
59
|
+
激活选区,松开未移动才算点击(工具行展开,逻辑从"按下即触发"改为"松开且未移动")。
|
|
60
|
+
|
|
61
|
+
**行映射**:渲染时用与 MessageRow 相同的逻辑(`sliceLines`)构建 `areaRows:
|
|
62
|
+
(string | null)[]`,与终端行一一对应(1 行 = 1 项,指示行/用户消息空行 = null)。
|
|
63
|
+
点击与选区换算共用该映射,选区随每次渲染实时重算(流式输出时选区跟随内容)。
|
|
64
|
+
|
|
65
|
+
**坐标换算**:行号(1-based)→ `areaRows` 索引(减 1);列 = 终端列 - 2(全部行有
|
|
66
|
+
`paddingLeft={1}`,第一字符在终端列 2),夹到行宽内。
|
|
67
|
+
|
|
68
|
+
**高亮渲染**:每个消息 part 把选区范围换算为"shown 行空间",以新 prop
|
|
69
|
+
`selection?: (CellRange | null)[]` 传入 `MessageRow`,按角色逐行注入反显
|
|
70
|
+
(用户消息首行的 "> "、工具行首行的 "⚡ name ▸ " 前缀单独处理,选区可跨前缀)。
|
|
71
|
+
memo 比较追加 `prev.selection === next.selection`;无选区时传 `undefined`,
|
|
72
|
+
身份稳定,不影响流式渲染性能。
|
|
73
|
+
|
|
74
|
+
**松手复制**:提取各选中行文本(剥 ANSI、按列切片)以 `\n` 拼接 →
|
|
75
|
+
`writeClipboard` → 选区保持高亮,直到下次点击(按下即清)、按键或滚轮滚动时清除;
|
|
76
|
+
复制成功且文本非空时调用 `onCopyNotice` 回调。
|
|
77
|
+
|
|
78
|
+
**事件过滤**:仅处理无修饰键(`button & 15 === 0`)且落在消息区(`row ≤ areaRows.length`)
|
|
79
|
+
的事件;带修饰键(Shift)的拖拽不接管,留给支持原生 Shift 选择的终端。
|
|
80
|
+
|
|
81
|
+
### 5. 复制反馈
|
|
82
|
+
|
|
83
|
+
- `TuiState` 增 `copyNotice?: string`;`TuiRenderer.showCopyNotice(text)` 设置并在
|
|
84
|
+
2.5s 后自动清除
|
|
85
|
+
- App 增 `onCopyNotice` 回调 prop 传给 MessageList(实现为 TuiRenderer.showCopyNotice)
|
|
86
|
+
- StatusBar:把提示渲染在分隔线行的右端(占用分隔线自身宽度,不增加行数,布局不跳动)
|
|
87
|
+
|
|
88
|
+
### 6. 文件改动
|
|
89
|
+
|
|
90
|
+
| 文件 | 改动 |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `src/tui/mouse.ts` | 启用/禁用序列加 1002、`MOTION` 常量 |
|
|
93
|
+
| `src/tui/selection.ts`(新) | 选区纯函数:stripAnsi/applySelection/extractText/selectionRanges |
|
|
94
|
+
| `src/clipboard.ts` | `writeClipboard()` |
|
|
95
|
+
| `src/tui/MessageList.tsx` | selection state、press/drag/release 事件流、areaRows 映射、MessageRow 高亮渲染、onCopyNotice |
|
|
96
|
+
| `src/tui/types.ts` | `TuiState.copyNotice` |
|
|
97
|
+
| `src/tui/index.tsx` | `TuiRenderer.showCopyNotice()`、App 增 onCopyNotice prop |
|
|
98
|
+
| `src/tui/App.tsx` | 传 onCopyNotice 给 MessageList |
|
|
99
|
+
| `src/tui/StatusBar.tsx` | 分隔线行右端渲染"✓ 已复制"提示 |
|
|
100
|
+
| `src/tui-chat.ts` | `/help` 快捷键区加"鼠标拖拽 选择文本并自动复制" |
|
|
101
|
+
|
|
102
|
+
### 7. 测试
|
|
103
|
+
|
|
104
|
+
- `tests/selection.test.ts`(新):stripAnsi / extractText(含 CJK 边界)/
|
|
105
|
+
applySelection(注入、RESET 重注入、空选区不变)/ selectionRanges(同行、
|
|
106
|
+
向下、向上、null 行跳过、越界夹取)
|
|
107
|
+
- `tests/mouse.test.ts`:补拖拽移动事件解析(button 32)、启用序列含 `?1002h`
|
|
108
|
+
- 运行 `bun run typecheck`(仓库根)与 `bun test`(packages/min-agent)
|
|
109
|
+
|
|
110
|
+
## 不做(YAGNI)
|
|
111
|
+
|
|
112
|
+
- 双击选词、三击选段
|
|
113
|
+
- 键盘选择模式(tmux 风格)
|
|
114
|
+
- 跨行选区自动去除 "> " / "⚡ name ▸ " 前缀(复制所见即所得)
|
|
115
|
+
- 复制保留 Markdown 格式/富文本
|
|
116
|
+
- 选区随流式输出"追字"(选区锚定终端坐标,内容变化时重算)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 配置与 HTTP 对齐(第 2 批)— 设计文档
|
|
2
|
+
|
|
3
|
+
**日期:** 2026-08-20
|
|
4
|
+
**状态:** 已批准(范围来自五批拆分;语义对齐现有 TUI)
|
|
5
|
+
**范围:** 项目级 config/记忆、`exec` 写 history 与 `--resume`、HTTP 的 plan/budget/diff/redo 与确认/提问回调
|
|
6
|
+
|
|
7
|
+
后续批次不在本文:MCP resources/prompts、沙箱、exec JSON 流、原生 Anthropic/Google、hooks、Browser/LSP、主题。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 设计决策
|
|
12
|
+
|
|
13
|
+
| 决策 | 结论 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| 项目配置可覆盖字段 | `activeProvider`、`defaultModel`、`sampling`、`budget`、`permission`、`compaction`;**不**写 providers/apiKey |
|
|
16
|
+
| 合并规则 | 项目字段有值则覆盖全局;sampling/budget/compaction 浅合并 |
|
|
17
|
+
| 密钥 | 仍只存在 `~/.min-agent/config.json` |
|
|
18
|
+
| 项目记忆 | `.min-agent/memory.json`;system prompt 先项目后全局 |
|
|
19
|
+
| 记忆默认写入 | 存在 `.min-agent/` 则默认 project,否则 global;可显式 `scope` |
|
|
20
|
+
| exec 会话 | 每次结束后 `saveSession`;`--resume <id>` 加载后追加本轮消息 |
|
|
21
|
+
| HTTP plan | `POST /v1/chat` 增加 `plan_mode` |
|
|
22
|
+
| HTTP budget/diff/redo | 独立端点,语义对齐 `/budget` `/diff` `/redo` |
|
|
23
|
+
| 确认/提问 | serve 默认仍自动批准;请求 `auto_approve: false` 且必须 `stream: true`;SSE `confirm`/`question` + `POST /v1/approvals` `/v1/answers`;超时 5 分钟视为拒绝/跳过 |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. 项目级配置
|
|
28
|
+
|
|
29
|
+
`ProjectConfig` 增加上述字段。`mergeProjectOverGlobal` + `getEffectiveConfig()`。
|
|
30
|
+
|
|
31
|
+
`getActiveProvider` / 采样 / 预算 / 权限 / compaction 读 effective config。`saveConfig` 仍写全局;`saveProjectConfig` 写项目。TUI `/budget [n] [--project]` 可写项目。
|
|
32
|
+
|
|
33
|
+
## 2. 项目级记忆
|
|
34
|
+
|
|
35
|
+
文件格式与全局相同。列表/删除按 scope 编索引。HTTP `GET /v1/memory` 增加 `project_memories`(原 `memories` 仍为全局)。
|
|
36
|
+
|
|
37
|
+
## 3. exec
|
|
38
|
+
|
|
39
|
+
`runAgent` 支持 `resumeSessionId`。结束后打印 `min-agent exec --resume <id>`。会话不存在则退出错误。
|
|
40
|
+
|
|
41
|
+
## 4. HTTP
|
|
42
|
+
|
|
43
|
+
- `plan_mode` → `RunOptions.planMode`
|
|
44
|
+
- `GET/POST /v1/budget`(POST 可 `scope: project`)
|
|
45
|
+
- `GET /v1/diff`
|
|
46
|
+
- `POST /v1/chat/redo` `{ session_id }`:取出最后一条用户文本再跑一轮
|
|
47
|
+
- 确认门闩:`AsyncLocalStorage`,不改变 process 级 `-y`;SSE 等待配套 POST
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# MCP 与插件对齐(第 3 批)— 设计文档
|
|
2
|
+
|
|
3
|
+
**日期:** 2026-08-20
|
|
4
|
+
**状态:** 已批准(范围来自五批拆分;语义对齐现有 TUI / 内置工具)
|
|
5
|
+
**范围:** MCP resources / prompts、插件危险操作走确认;计划模式只保留只读 MCP/插件
|
|
6
|
+
|
|
7
|
+
后续批次不在本文:沙箱、exec JSON 流、原生 Anthropic/Google、hooks、Browser/LSP/Notebook、主题。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 设计决策
|
|
12
|
+
|
|
13
|
+
| 决策 | 结论 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| resources/prompts 暴露方式 | 四个只读 agent 工具(`mcp_list_resources` / `mcp_read_resource` / `mcp_list_prompts` / `mcp_get_prompt`),不把每条 resource 注册成独立工具 |
|
|
16
|
+
| 连接失败面 | 服务器未声明或 list 失败时记空列表,**不**让整次 MCP 连接失败 |
|
|
17
|
+
| 列表上限 | resources 200、templates 100、prompts 100;工具输出仍走 `truncateToolOutput` |
|
|
18
|
+
| 插件确认 | 默认确认;`readOnly: true` 或 `dangerous: false` 跳过 |
|
|
19
|
+
| 计划模式 | 去掉无 `readOnlyHint` 的 MCP 工具、非 `readOnly` 插件;catalog 工具保留 |
|
|
20
|
+
| HTTP | `GET /v1/mcp` 增加计数;`GET .../resources`、`GET .../prompts`、`POST .../resources/read`、`POST .../prompts/get` |
|
|
21
|
+
| serve 默认 | 仍自动批准(与第 2 批一致);未设 `readOnly` 的插件在 TUI/`exec`(无 `-y`)会询问 |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. MCP resources / prompts
|
|
26
|
+
|
|
27
|
+
连接成功后根据 `getServerCapabilities()` 分页拉取。缓存供状态栏/HTTP 列表;`read` / `get` 走实时 RPC。
|
|
28
|
+
|
|
29
|
+
多个已连接服务器时,`read`/`get` 若 URI/名称不唯一则必须带 `server`。二进制 resource 不把大段 base64 塞进上下文。
|
|
30
|
+
|
|
31
|
+
## 2. 插件确认
|
|
32
|
+
|
|
33
|
+
`PluginToolDef` 增加 `readOnly?: boolean`、`dangerous?: boolean`。确认文案面向用户:「运行自定义工具 {id}」加参数摘要。拒绝则工具返回说明,不抛错。
|
|
34
|
+
|
|
35
|
+
## 3. 文档与测试
|
|
36
|
+
|
|
37
|
+
README 插件示例标明 `readOnly`;`/mcp` 与 `mcp info` 显示资源/提示计数;API.md 同步新端点。测试覆盖:无 capability 的服务器仍能连;带 resources/prompts 的 stdio 服务器可 list/read;插件默认确认、`readOnly` 跳过。
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# 沙箱与权限分级(第 4 批)— 设计文档
|
|
2
|
+
|
|
3
|
+
**日期:** 2026-08-20
|
|
4
|
+
**状态:** 已批准(范围来自五批拆分;语义对齐现有 TUI / `confirm` / `/plan`)
|
|
5
|
+
**范围:** 权限档位、工作区文件隔离、命令级网络/写盘隔离;HTTP/CLI/TUI 同步
|
|
6
|
+
|
|
7
|
+
后续批次不在本文:exec JSON 流、原生 Anthropic/Google、hooks、Browser/LSP/Notebook、主题。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 设计决策
|
|
12
|
+
|
|
13
|
+
| 决策 | 结论 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| 两轴分开 | **确认**(`permission`)与 **隔离**(`sandbox`)正交。`--yes` / `allow-all` 只跳过确认,不自动关掉隔离 |
|
|
16
|
+
| 确认档位 | `ask`(默认)/ `accept-edits` / `allow-all`。`accept-edits` 自动放行 write/edit/apply_patch,危险命令与自定义工具仍询问 |
|
|
17
|
+
| 隔离档位 | `off`(默认)/ `workspace` / `strict`。`workspace`:文件工具可任意读(凭据除外)、只写入工作区+临时目录+包管理缓存;`strict`:读和写都限制在工作区(及 extra 根) |
|
|
18
|
+
| 默认 | 未配置时 `sandbox.mode=off`、`network=allow`。`MIN_AGENT_SANDBOX` 覆盖当前进程;单独 `--sandbox` / `--network` 写入配置并退出;带消息时才作为本轮覆盖 |
|
|
19
|
+
| 凭据 | 即使 `sandbox=off`,文件工具也禁止读写密钥类路径(`~/.ssh` 私钥、`~/.min-agent/config.json` 等) |
|
|
20
|
+
| 命令隔离 | macOS 用 `sandbox-exec`,Linux 有 `bwrap` 时用 bubblewrap;没有则仍执行路径策略 + 网络命令启发式,不悄悄降级成无隔离执行 |
|
|
21
|
+
| 网络 | `network=deny` 时:OS 配置禁止命令出网;无 OS 后端则拦截常见联网命令;`search_web` / `web_fetch` 一并拒绝。MCP 进程不受本批约束 |
|
|
22
|
+
| `/plan` | 仍是去掉写入类工具;与 sandbox 叠加,不互相替代 |
|
|
23
|
+
| HTTP | 请求可收紧、不可放宽。`serve` 默认仍自动批准,隔离默认仍为 off |
|
|
24
|
+
| 自定义工具 | 进程内执行,文件隔离管不到;继续靠 `readOnly` / 确认。文案不提实现细节 |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. 配置
|
|
29
|
+
|
|
30
|
+
全局与项目 `.min-agent/config.json` 均可写(项目覆盖全局,字段级合并):
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"permission": "ask",
|
|
35
|
+
"sandbox": {
|
|
36
|
+
"mode": "off",
|
|
37
|
+
"network": "allow",
|
|
38
|
+
"extraWriteRoots": [],
|
|
39
|
+
"extraReadRoots": []
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`sandbox` 也可写成字符串 `"workspace"`。未配置时默认 `off`。密钥仍只存在全局配置。
|
|
45
|
+
|
|
46
|
+
生效顺序:环境变量 / CLI 覆盖 → 配置合并 → 每请求 ALS(只收紧)。
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 2. 工具行为
|
|
51
|
+
|
|
52
|
+
- `read` / `write` / `edit` / `apply_patch` / `glob` / `grep`:解析 realpath(含符号链接逃逸),按档位拒绝并返回面向用户的说明。
|
|
53
|
+
- `bash` / explore 只读 bash:工作目录必须落在允许的写根;按档位套 OS 配置。
|
|
54
|
+
- `search_web` / `web_fetch`:`network=deny` 时直接拒绝。
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 3. 界面与 HTTP
|
|
59
|
+
|
|
60
|
+
- TUI:`/permission`、`/sandbox`(可 `--project` / `--global` 写入配置);`/help` 同步。
|
|
61
|
+
- CLI:`sandbox` 子命令;单独 `--sandbox` / `--network` 写入配置并退出;带消息/`--resume`/`--model`/`--provider` 时才作为本轮覆盖;`--permission`;`--yes` 仍只表示自动批准。
|
|
62
|
+
- HTTP:`GET/POST /v1/permission`、`GET/POST /v1/sandbox`;`/v1/chat` 增加 `sandbox`、`network`(只收紧)。`GET /v1/meta` 附带当前隔离摘要。
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 4. 文档与测试
|
|
67
|
+
|
|
68
|
+
README / API.md / AGENTS.md / `/help` 同步。测试覆盖:路径策略、凭据拒绝、symlink 逃逸、档位收紧、`accept-edits`、无 OS 后端时的网络启发式、HTTP 只收紧。有系统隔离后端时加一条写盘拒绝的集成断言。
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# TUI 补齐(第 1 批)— 设计文档
|
|
2
|
+
|
|
3
|
+
**日期:** 2026-08-20
|
|
4
|
+
**状态:** 已批准
|
|
5
|
+
**范围:** 会话内恢复、`/reload`、输入历史持久化、编辑键、消息区选择增强、子 agent 工具行
|
|
6
|
+
**实现路径:** 在现有 TUI 上按模块补;新文件只放纯函数,界面仍走 `slash-handler` / `InputBar` / `MessageList` / `task`
|
|
7
|
+
|
|
8
|
+
后续批次不在本文:项目级 config、HTTP plan/budget、MCP resources、沙箱、exec JSON、原生 Anthropic/Google、hooks、Browser/LSP、主题可配。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 背景
|
|
13
|
+
|
|
14
|
+
TUI 已能拖拽复制、`/model` 选择器、内存输入历史、`task` 子 agent。缺口是:
|
|
15
|
+
|
|
16
|
+
- `/sessions` 只能列表,恢复必须退出再 `--resume`
|
|
17
|
+
- `--resume` 只把 `ModelMessage[]` 灌进 agent,消息区仍是空的
|
|
18
|
+
- 没有 `/reload` 重读规则
|
|
19
|
+
- 输入历史上次退出即丢
|
|
20
|
+
- Home/End、折行上下、点击斜杠菜单未接
|
|
21
|
+
- 双击选词、三击选段、复制去装饰前缀、键盘选择模式未做
|
|
22
|
+
- 子 agent 进度打在 stderr,TUI 里只看到一条 `task` 结果
|
|
23
|
+
|
|
24
|
+
## 设计决策(已与用户确认)
|
|
25
|
+
|
|
26
|
+
| 决策 | 结论 |
|
|
27
|
+
|------|------|
|
|
28
|
+
| 会话恢复 UX | `/sessions` 与 `/resume` 无参打开选择器(像 `/model`),带 id 直接切换 |
|
|
29
|
+
| 切换当前会话 | 有消息则自动保存,再加载目标 |
|
|
30
|
+
| 输入历史 | 全局 `~/.min-agent/input-history.json`,上限 1000 |
|
|
31
|
+
| 子 agent | 普通工具行,点开看子工具调用和结果 |
|
|
32
|
+
| 键盘选择 | 消息区模式,**Ctrl+B** 进入;不在输入框做 Shift 选区 |
|
|
33
|
+
| 双击/三击 | 选中后立刻复制(与拖拽松手一致) |
|
|
34
|
+
|
|
35
|
+
## 目标与非目标
|
|
36
|
+
|
|
37
|
+
**目标:**
|
|
38
|
+
|
|
39
|
+
- 会话内切换并画出历史;`--resume` 启动同样画出历史
|
|
40
|
+
- `/reload` 重读规则并更新 runner 的 system prompt
|
|
41
|
+
- 输入历史上限 1000、原子落盘
|
|
42
|
+
- Home/End、折行 ↑↓(到顶/底再翻历史)、点击斜杠/参数菜单
|
|
43
|
+
- 双击选词、三击选段、复制去掉 `> ` / `⚡ name ▸ ` / 结果缩进
|
|
44
|
+
- Ctrl+B 消息区选择模式:方向键扩选,Enter 复制退出,Esc 取消
|
|
45
|
+
- TUI 中 `task` 可展开子工具列表;非 TUI 仍用 stderr
|
|
46
|
+
|
|
47
|
+
**非目标:**
|
|
48
|
+
|
|
49
|
+
- 输入框 Shift 选区、主题可配、项目级记忆/config、HTTP 新端点
|
|
50
|
+
- 恢复思考过程、子 agent 思考文本、子 agent 再调 `task`/`explore`
|
|
51
|
+
- 改会话文件格式
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 1. 会话切换与 `/reload`
|
|
56
|
+
|
|
57
|
+
### 1.1 选择器
|
|
58
|
+
|
|
59
|
+
新增 `SessionPicker.tsx`,交互对齐 `ModelPicker`(替换输入框、输入过滤、↑↓/PgUpDn、Enter、Esc)。列表来自 `listSessions()`,展示 `id`、日期、标题、消息数;当前会话标 `←`。过滤匹配 id 或标题。无会话时不打开选择器,系统提示「无已保存的会话」。
|
|
60
|
+
|
|
61
|
+
`TuiState` 增加 `sessionPicker?: boolean`。`TuiRenderer.showSessionPicker()` / `hideSessionPicker()`。`TuiCallbacks` 增加 `onSessionPick(id)` / `onSessionCancel()`。`App` footer 与 `modelPicker` 互斥:同一时刻只出现一种 picker / confirm / question / input。
|
|
62
|
+
|
|
63
|
+
### 1.2 命令
|
|
64
|
+
|
|
65
|
+
| 输入 | 行为 |
|
|
66
|
+
|------|------|
|
|
67
|
+
| `/sessions` 或 `/resume` | 打开选择器 |
|
|
68
|
+
| `/sessions <id>` 或 `/resume <id>` | 立即切换 |
|
|
69
|
+
| `/resume` | `/sessions` 的别名(`slash-commands.ts` 的 `aliases`) |
|
|
70
|
+
|
|
71
|
+
为 `sessions` / `resume` 注册 Tab 补全:`listSessions()` 的 id,description 用标题。
|
|
72
|
+
|
|
73
|
+
`/help` 与菜单文案改为「列出/切换已保存会话」。
|
|
74
|
+
|
|
75
|
+
### 1.3 `switchSession(id)`
|
|
76
|
+
|
|
77
|
+
抽到 `src/tui/session-switch.ts`(协调逻辑)+ `src/tui/hydrate.ts`(纯函数)。步骤:
|
|
78
|
+
|
|
79
|
+
1. agent 正在跑(`runner.getController()` 非空)→ 拒绝:「请先等待当前回合结束或按 Esc 取消」
|
|
80
|
+
2. `id === sessionId` → 「已是当前会话」
|
|
81
|
+
3. `loadSession(id)` 为空 → 「会话不存在」
|
|
82
|
+
4. `messages.length > 0` → `sessionId = saveSession(messages, sessionId)`
|
|
83
|
+
5. 就地替换:`messages.length = 0`;`messages.push(...session.messages)`(runner 持有同一引用)
|
|
84
|
+
6. `setSessionId(id)`;`clearStack(undo)`;`tracker.resetContext()`
|
|
85
|
+
7. `lastUserMessage` = 最后一条 user 文本(无则 `null`)
|
|
86
|
+
8. `tui.update({ messages: hydrateMessages(session.messages) })`
|
|
87
|
+
9. 系统提示 `✓ 已切换到: {title}`
|
|
88
|
+
|
|
89
|
+
`planMode` 不随会话重置。不重建 system prompt。
|
|
90
|
+
|
|
91
|
+
### 1.4 hydrate
|
|
92
|
+
|
|
93
|
+
`hydrateMessages(messages: ModelMessage[]): TuiMessage[]` 有损映射:
|
|
94
|
+
|
|
95
|
+
- `user`:文本拼接;仅图片 parts 时内容为 `[📎 图片]`
|
|
96
|
+
- `assistant`:只保留 text parts;tool-call parts 忽略(工具结果走 `tool` 角色)
|
|
97
|
+
- `tool`:一条工具行,`toolName` + 结果摘要(复用 `summarizeToolResult` / `toolResultText`);无 name 则跳过
|
|
98
|
+
- 其它 / 无法解析:跳过,不抛
|
|
99
|
+
|
|
100
|
+
不恢复 `thinking`。用于 `--resume` 启动(`tui.start()` 之后立刻 `tui.update({ messages: hydrate(...) })`)和会话内切换。
|
|
101
|
+
|
|
102
|
+
### 1.5 `/reload`
|
|
103
|
+
|
|
104
|
+
- `loadInstructions()` + `scanProject()` + `buildCodeSystemPrompt()`
|
|
105
|
+
- `AgentRunnerDeps.codeSystemPrompt: string` 改为 `getSystemPrompt: () => string`;`tui-chat` 用可变闭包持有最新 prompt
|
|
106
|
+
- 系统提示 `✓ 已重载规则(N 字符)`
|
|
107
|
+
- 不重连 MCP、不重扫 skills
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 2. 输入历史与编辑键
|
|
112
|
+
|
|
113
|
+
### 2.1 持久化
|
|
114
|
+
|
|
115
|
+
文件:`~/.min-agent/input-history.json`
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{ "entries": ["...", "..."] }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
最新在末尾。`HISTORY_LIMIT = 1000`(现为 500)。连续重复仍去重。斜杠命令也记录。
|
|
122
|
+
|
|
123
|
+
- 读:`InputBar` 挂载时 `loadInputHistory()` → `createHistory()` 填 `entries`
|
|
124
|
+
- 写:`pushInput` 之后 `saveInputHistory(entries)`,原子写(tmp + rename),与 `memory.ts` 相同
|
|
125
|
+
- 坏 JSON / 非数组:备份为 `input-history.json.corrupt-{ts}`,当空历史
|
|
126
|
+
- 写失败:静默,不挡输入
|
|
127
|
+
- 不写 logger(避免把用户输入打进运行日志)
|
|
128
|
+
|
|
129
|
+
纯函数放在现有 `input-history.ts`:`parseHistoryFile` / `serializeHistoryFile`。
|
|
130
|
+
|
|
131
|
+
### 2.2 Home / End
|
|
132
|
+
|
|
133
|
+
与现有 Ctrl+A / Ctrl+E 相同:`moveToLineStart` / `moveToLineEnd`(逻辑行,按 `\n`)。Ink 若提供 `key.home` / `key.end` 则用;否则在 raw stdin 里解析 CSI `H` / `F` / `1~` / `4~`(与 Delete 扫描同一通道)。
|
|
134
|
+
|
|
135
|
+
### 2.3 折行 ↑↓
|
|
136
|
+
|
|
137
|
+
菜单或参数菜单打开时:↑↓ 仍只移动菜单(现状)。
|
|
138
|
+
|
|
139
|
+
否则:
|
|
140
|
+
|
|
141
|
+
- 先按视觉行移动光标(`visualRows` + 保持显示列,换算用现有 `caretIndexFromClick` 同类逻辑)
|
|
142
|
+
- 已在第一视觉行再 ↑ → 翻更早历史
|
|
143
|
+
- 已在最后视觉行再 ↓ → 翻更新历史
|
|
144
|
+
|
|
145
|
+
新增 `moveCaretVertical(value, caretIndex, direction, maxWidth): number`(`caret-pos.ts`)。
|
|
146
|
+
|
|
147
|
+
### 2.4 点击菜单
|
|
148
|
+
|
|
149
|
+
SGR 点击落在斜杠菜单或参数菜单的某一选项行 → 与键盘选中后 Enter(命令菜单)或 Tab(参数菜单)相同:`acceptCommand` / `acceptArg`。点击菜单框外仍按现有逻辑(点输入区定位光标)。用 footer 行数反推菜单绝对行,与输入框点击定位同一套坐标。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 3. 消息区选择
|
|
154
|
+
|
|
155
|
+
拖拽选择状态机保留。复制路径统一走 `copySelection(text)`:去前缀 → `writeClipboard` → 成功且非空才 `showCopyNotice`。
|
|
156
|
+
|
|
157
|
+
### 3.1 去前缀(只影响剪贴板)
|
|
158
|
+
|
|
159
|
+
对 `areaRows` 里被选中的每一行,剥 ANSI 后:
|
|
160
|
+
|
|
161
|
+
- 用户消息首行:去掉开头 `> `
|
|
162
|
+
- 工具消息首行:去掉从 `⚡ ` 起到第一个 `▸ ` 或 `▾ `(含其后一个空格)
|
|
163
|
+
- 工具展开结果行:去掉开头两个空格
|
|
164
|
+
- 其它行不变
|
|
165
|
+
|
|
166
|
+
高亮仍按屏幕所见(含前缀)。函数:`stripCopyDecorations(line: string, kind: "user-head" | "tool-head" | "tool-body" | "plain"): string`。`kind` 由该行对应的 `TuiMessage.role` 与是否为首行/结果行决定。
|
|
167
|
+
|
|
168
|
+
### 3.2 双击 / 三击
|
|
169
|
+
|
|
170
|
+
SGR 不带 click count。在 `click` outcome 上记 `{ row, col, at, count }`:
|
|
171
|
+
|
|
172
|
+
- 400ms 内、同一行、列差 ≤ 2 → `count++`,否则重置为 1
|
|
173
|
+
- `count === 2`:该行(剥 ANSI)按空白界选词,高亮并复制
|
|
174
|
+
- `count === 3`:该行所属那条消息当前可见的全部行,高亮并复制
|
|
175
|
+
- `count === 1`:保持现有单击(工具行展开)
|
|
176
|
+
|
|
177
|
+
词界:连续非空白为一个词;行首/行尾夹紧。CJK 与拉丁一样按空白切,不按字素再切。
|
|
178
|
+
|
|
179
|
+
### 3.3 Ctrl+B 选择模式
|
|
180
|
+
|
|
181
|
+
`TuiState.selectionMode?: boolean`。`App` 在处理 Ctrl+C 旁监听 Ctrl+B:
|
|
182
|
+
|
|
183
|
+
- 确认框 / 提问 / picker 打开时忽略
|
|
184
|
+
- agent 正在跑时拒绝进入(避免与 Esc 取消抢键),不提示或仅短提示「请先结束或取消当前回合」
|
|
185
|
+
- 进入后:`InputBar` 视为 disabled(不接收编辑键);状态栏分隔线提示 `选择模式 ↑↓←→扩选 Enter复制 Esc退出`
|
|
186
|
+
|
|
187
|
+
起点:上次成功的消息区点击坐标;若无,则可见区第一条非空文本行、列 0。`anchor` 固定,方向键只动 `cur`(与拖拽扩选同一套 `selectionRanges`)。Home/End 在模式内:行首 / 行尾显示列。
|
|
188
|
+
|
|
189
|
+
- Enter:复制(去前缀)并退出模式,选区保持到下次点击/按键(与拖拽相同)
|
|
190
|
+
- Esc:退出模式,清除选区,不复制
|
|
191
|
+
- Ctrl+C:仍中断/退出进程,顺带退出选择模式
|
|
192
|
+
- 滚轮:与现有一样清选区;选择模式保持,光标行随滚动夹紧
|
|
193
|
+
|
|
194
|
+
Ctrl+B 在 `InputBar` 里目前被 `key.ctrl` 总拦截吞掉;改为由 `App` 先处理,或 `InputBar` 在 Ctrl+B 时 `onSelectionMode()`。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 4. 子 agent 工具行
|
|
199
|
+
|
|
200
|
+
`src/tools/task.ts` 的 `runSubAgent` 增加可选:
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
onSubToolCall?: (name: string, input: unknown) => void
|
|
204
|
+
onSubToolResult?: (name: string, output: unknown, isError: boolean) => void
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
有回调时不写 stderr 进度;无回调时保持现有 `console.error`(`exec` / 测试)。
|
|
208
|
+
|
|
209
|
+
`TuiMessage` 增加:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
toolChildren?: { name: string; summary: string; result?: string; isError?: boolean }[]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`createTaskTool` 在 TUI 路径由 `agent-runner` 传入回调:按父级 `toolCallId` 调用 `tui.appendToolChild(...)`。未展开:仍只显示 `description` 与最终 `→ 摘要`(不在摘要行刷当前子工具名)。展开:在结果之上列出子工具名、参数摘要、结果(截断规则同普通工具行)。
|
|
216
|
+
|
|
217
|
+
子 agent 仍删除 `task` / `explore`。最终回复仍是父级 `task` 的 `toolResult`。思考文本不展示。
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 5. 错误处理
|
|
222
|
+
|
|
223
|
+
| 场景 | 行为 |
|
|
224
|
+
|------|------|
|
|
225
|
+
| 切换 id 不存在 | 提示,留在当前会话 |
|
|
226
|
+
| 切换时 agent 在跑 | 拒绝 |
|
|
227
|
+
| `--resume` 指向不存在的 id | 维持现状(当新会话) |
|
|
228
|
+
| 历史文件损坏 | 备份后空历史 |
|
|
229
|
+
| 历史写入失败 | 静默 |
|
|
230
|
+
| 复制失败 | 不高亮「已复制」 |
|
|
231
|
+
| hydrate 未知形状 | 跳过该条 |
|
|
232
|
+
| Ctrl+B 时 picker/confirm 开着 | 忽略 |
|
|
233
|
+
| Ctrl+B 时 agent 在跑 | 不进入选择模式 |
|
|
234
|
+
|
|
235
|
+
## 6. 测试
|
|
236
|
+
|
|
237
|
+
| 文件 | 覆盖 |
|
|
238
|
+
|------|------|
|
|
239
|
+
| `tests/hydrate.test.ts` | user 文本/图片、assistant 去掉 tool-call、tool 行、未知跳过 |
|
|
240
|
+
| `tests/input-history.test.ts`(改) | 解析/序列化、上限 1000、坏文件 |
|
|
241
|
+
| `tests/caret-pos.test.ts`(改) | `moveCaretVertical` 折行、顶/底夹紧;Home/End 已有则补物理键映射若抽出 |
|
|
242
|
+
| `tests/selection.test.ts`(改) | 去前缀、选词范围、三击=整条可见行 |
|
|
243
|
+
| `tests/drag-state.test.ts`(改)或新文件 | 400ms 双击计数(若把点击计数抽成纯函数) |
|
|
244
|
+
| `tests/session-switch` 或 hydrate+save 组合 | 拒绝运行中切换的守卫可测则测;picker 不测 Ink |
|
|
245
|
+
|
|
246
|
+
仓库根 `bun run typecheck`;`packages/min-agent` 下 `bun test`。
|
|
247
|
+
|
|
248
|
+
## 7. 文档同步
|
|
249
|
+
|
|
250
|
+
- `README.md` 交互命令表:`/sessions`、`/resume`、`/reload`;快捷键补 Ctrl+B、Home/End、鼠标双击/三击
|
|
251
|
+
- `/help` 与 `slash-commands.ts`
|
|
252
|
+
- Configuration 或日志旁注明输入历史路径 `~/.min-agent/input-history.json`
|
|
253
|
+
- `AGENTS.md`:用户配置目录补一条 input-history(若该文件已列 `~/.min-agent/` 内容)
|
|
254
|
+
|
|
255
|
+
无新 HTTP 端点,不改 `docs/API.md`。
|
|
256
|
+
|
|
257
|
+
## 影响范围
|
|
258
|
+
|
|
259
|
+
- `src/tui-chat.ts`、`src/tui/agent-runner.ts`、`src/tui/slash-handler.ts`、`src/tui/slash-commands.ts`
|
|
260
|
+
- `src/tui/App.tsx`、`src/tui/index.tsx`、`src/tui/types.ts`、`src/tui/InputBar.tsx`、`src/tui/MessageList.tsx`、`src/tui/StatusBar.tsx`
|
|
261
|
+
- 新:`src/tui/SessionPicker.tsx`、`src/tui/hydrate.ts`、`src/tui/session-switch.ts`(若逻辑超过 slash-handler 可读范围)
|
|
262
|
+
- `src/tui/input-history.ts`、`src/tui/caret-pos.ts`、`src/tui/selection.ts`、`src/tui/drag-state.ts`
|
|
263
|
+
- `src/tools/task.ts`、`src/tool-display.ts`(子工具摘要复用)
|
|
264
|
+
- `README.md`、`/help`、`packages/min-agent/AGENTS.md`
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 自审
|
|
269
|
+
|
|
270
|
+
- 无 TBD:选择模式起点、双击时限、历史路径与上限、hydrate 规则、`/reload` 不含 MCP/skills 已写死
|
|
271
|
+
- 与「增量补模块」一致:新 UI 只加 SessionPicker;选择模式复用 `selectionRanges`
|
|
272
|
+
- 单批可落地:不包含后四批
|
|
273
|
+
- `--resume` 画历史与会话内切换共用 hydrate,避免两套映射
|