min-agent 0.5.1 → 0.6.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.
Files changed (169) hide show
  1. package/README.md +18 -18
  2. package/dist/cli.js +538 -12
  3. package/docs/API.md +6 -6
  4. package/package.json +6 -6
  5. package/skills/self-config/SKILL.md +4 -2
  6. package/skills/self-config/reference.md +4 -1
  7. package/dist/agent.js +0 -1388
  8. package/dist/assistant-stream.js +0 -131
  9. package/dist/cli/commands/chat.js +0 -13
  10. package/dist/cli/commands/ctx.js +0 -7
  11. package/dist/cli/commands/exec.js +0 -35
  12. package/dist/cli/commands/history.js +0 -58
  13. package/dist/cli/commands/index.js +0 -249
  14. package/dist/cli/commands/init.js +0 -18
  15. package/dist/cli/commands/mcp.js +0 -173
  16. package/dist/cli/commands/memory.js +0 -87
  17. package/dist/cli/commands/models.js +0 -21
  18. package/dist/cli/commands/permission.js +0 -12
  19. package/dist/cli/commands/rules.js +0 -33
  20. package/dist/cli/commands/sandbox.js +0 -13
  21. package/dist/cli/commands/serve.js +0 -9
  22. package/dist/cli/commands/setup.js +0 -56
  23. package/dist/cli/commands/shared.js +0 -25
  24. package/dist/cli/commands/skills.js +0 -119
  25. package/dist/cli/commands/think.js +0 -12
  26. package/dist/cli/commands/update.js +0 -7
  27. package/dist/cli/commands/write-config.js +0 -52
  28. package/dist/cli/errors.js +0 -36
  29. package/dist/cli/exec-prompt.js +0 -26
  30. package/dist/cli/option-helpers.js +0 -65
  31. package/dist/cli/program.js +0 -223
  32. package/dist/cli/setup/detect.js +0 -17
  33. package/dist/cli/setup/flags.js +0 -12
  34. package/dist/cli/setup/flow.js +0 -108
  35. package/dist/cli/setup/provider-form.js +0 -102
  36. package/dist/cli/setup/ui.js +0 -534
  37. package/dist/clipboard.js +0 -142
  38. package/dist/code-mode.js +0 -184
  39. package/dist/compaction.js +0 -644
  40. package/dist/config.js +0 -363
  41. package/dist/confirm.js +0 -172
  42. package/dist/context-window.js +0 -256
  43. package/dist/ctx-cli.js +0 -30
  44. package/dist/ctx.js +0 -80
  45. package/dist/doom-loop.js +0 -278
  46. package/dist/fetch-timeout.js +0 -152
  47. package/dist/http-approvals.js +0 -60
  48. package/dist/http.js +0 -119
  49. package/dist/instructions.js +0 -196
  50. package/dist/logger.js +0 -95
  51. package/dist/markdown.js +0 -259
  52. package/dist/mcp.js +0 -1036
  53. package/dist/memory-cli.js +0 -33
  54. package/dist/memory.js +0 -295
  55. package/dist/model-catalog.js +0 -285
  56. package/dist/ollama-model.js +0 -234
  57. package/dist/ollama-openai-bridge.js +0 -383
  58. package/dist/output.js +0 -76
  59. package/dist/paste-handler.js +0 -41
  60. package/dist/permission-cli.js +0 -40
  61. package/dist/plugins.js +0 -131
  62. package/dist/pricing.js +0 -119
  63. package/dist/provider.js +0 -63
  64. package/dist/question-format.js +0 -60
  65. package/dist/reasoning-stream.js +0 -158
  66. package/dist/sandbox-cli.js +0 -79
  67. package/dist/sandbox.js +0 -403
  68. package/dist/save-throttle.js +0 -45
  69. package/dist/scope.js +0 -23
  70. package/dist/serve/common.js +0 -425
  71. package/dist/serve/routes-chat.js +0 -367
  72. package/dist/serve/routes-mcp.js +0 -212
  73. package/dist/serve/routes-memory.js +0 -95
  74. package/dist/serve/routes-meta.js +0 -268
  75. package/dist/serve/routes-sessions.js +0 -61
  76. package/dist/serve/routes-skills.js +0 -70
  77. package/dist/serve.js +0 -138
  78. package/dist/sessions.js +0 -265
  79. package/dist/skills.js +0 -599
  80. package/dist/structured-output.js +0 -29
  81. package/dist/synthetic.js +0 -7
  82. package/dist/think-cli.js +0 -36
  83. package/dist/thinking-wire.js +0 -239
  84. package/dist/thinking.js +0 -166
  85. package/dist/title-gen.js +0 -55
  86. package/dist/token-display.js +0 -39
  87. package/dist/tool-display.js +0 -178
  88. package/dist/tool-output.js +0 -126
  89. package/dist/tools/apply_patch.js +0 -265
  90. package/dist/tools/atomic-file.js +0 -35
  91. package/dist/tools/backend.js +0 -61
  92. package/dist/tools/bash.js +0 -231
  93. package/dist/tools/code_search.js +0 -98
  94. package/dist/tools/edit.js +0 -68
  95. package/dist/tools/explore.js +0 -232
  96. package/dist/tools/glob.js +0 -40
  97. package/dist/tools/grep.js +0 -174
  98. package/dist/tools/index.js +0 -34
  99. package/dist/tools/question.js +0 -54
  100. package/dist/tools/read.js +0 -109
  101. package/dist/tools/search-searxng.js +0 -223
  102. package/dist/tools/search-serper.js +0 -189
  103. package/dist/tools/task.js +0 -165
  104. package/dist/tools/todo.js +0 -213
  105. package/dist/tools/web_fetch.js +0 -195
  106. package/dist/tools/web_search.js +0 -228
  107. package/dist/tools/write.js +0 -55
  108. package/dist/tui/App.js +0 -138
  109. package/dist/tui/ConfirmBar.js +0 -66
  110. package/dist/tui/CtxPicker.js +0 -68
  111. package/dist/tui/InputBar.js +0 -618
  112. package/dist/tui/MessageList.js +0 -718
  113. package/dist/tui/ModelPicker.js +0 -113
  114. package/dist/tui/QuestionBar.js +0 -136
  115. package/dist/tui/SessionPicker.js +0 -79
  116. package/dist/tui/Spinner.js +0 -6
  117. package/dist/tui/StatusBar.js +0 -22
  118. package/dist/tui/ThinkPicker.js +0 -75
  119. package/dist/tui/agent-runner.js +0 -223
  120. package/dist/tui/bracketed-paste.js +0 -37
  121. package/dist/tui/caret-pos.js +0 -179
  122. package/dist/tui/caret.js +0 -69
  123. package/dist/tui/click-count.js +0 -13
  124. package/dist/tui/diff-view.js +0 -61
  125. package/dist/tui/drag-state.js +0 -49
  126. package/dist/tui/hydrate.js +0 -129
  127. package/dist/tui/index.js +0 -264
  128. package/dist/tui/input-history.js +0 -125
  129. package/dist/tui/layout.js +0 -105
  130. package/dist/tui/mouse.js +0 -46
  131. package/dist/tui/overlay-input.js +0 -12
  132. package/dist/tui/paste-draft.js +0 -173
  133. package/dist/tui/prompt-queue.js +0 -24
  134. package/dist/tui/selection.js +0 -232
  135. package/dist/tui/session-switch.js +0 -28
  136. package/dist/tui/slash-commands.js +0 -129
  137. package/dist/tui/slash-handler.js +0 -615
  138. package/dist/tui/text-width.js +0 -113
  139. package/dist/tui/theme.js +0 -12
  140. package/dist/tui/token-info.js +0 -7
  141. package/dist/tui/tool-children.js +0 -19
  142. package/dist/tui/types.js +0 -1
  143. package/dist/tui/undo-stack.js +0 -14
  144. package/dist/tui/use-sgr-mouse.js +0 -29
  145. package/dist/tui-chat.js +0 -478
  146. package/dist/updater.js +0 -116
  147. package/dist/xml-search.js +0 -194
  148. package/docs/superpowers/plans/2026-08-16-batch1-tui-improvements.md +0 -1510
  149. package/docs/superpowers/plans/2026-08-16-batch2-cli-tools-api.md +0 -2105
  150. package/docs/superpowers/plans/2026-08-16-batch3-config-engineering.md +0 -1595
  151. package/docs/superpowers/plans/2026-08-16-input-caret.md +0 -782
  152. package/docs/superpowers/plans/2026-08-20-tui-completeness.md +0 -873
  153. package/docs/superpowers/plans/2026-08-20-unified-tui-default.md +0 -631
  154. package/docs/superpowers/plans/2026-08-23-cli-setup.md +0 -501
  155. package/docs/superpowers/plans/2026-08-23-input-paste-attachments.md +0 -475
  156. package/docs/superpowers/plans/2026-08-23-thinking-wire-profile.md +0 -450
  157. package/docs/superpowers/specs/2026-08-16-batch1-tui-improvements-design.md +0 -183
  158. package/docs/superpowers/specs/2026-08-16-batch2-cli-tools-api-design.md +0 -220
  159. package/docs/superpowers/specs/2026-08-16-batch3-config-engineering-design.md +0 -196
  160. package/docs/superpowers/specs/2026-08-16-input-caret-design.md +0 -63
  161. package/docs/superpowers/specs/2026-08-17-mouse-selection-design.md +0 -116
  162. package/docs/superpowers/specs/2026-08-20-config-http-alignment-design.md +0 -47
  163. package/docs/superpowers/specs/2026-08-20-mcp-plugins-alignment-design.md +0 -37
  164. package/docs/superpowers/specs/2026-08-20-sandbox-permissions-design.md +0 -68
  165. package/docs/superpowers/specs/2026-08-20-tui-completeness-design.md +0 -273
  166. package/docs/superpowers/specs/2026-08-20-unified-tui-default-design.md +0 -165
  167. package/docs/superpowers/specs/2026-08-23-cli-setup-design.md +0 -282
  168. package/docs/superpowers/specs/2026-08-23-input-paste-attachments-design.md +0 -174
  169. package/docs/superpowers/specs/2026-08-23-thinking-wire-profile-design.md +0 -140
@@ -1,282 +0,0 @@
1
- # CLI setup 向导改版 — 设计文档
2
-
3
- **日期:** 2026-08-23
4
- **状态:** 已批准
5
- **范围:** `min-agent setup` 作为全局 Provider 向导与管理器;TTY 下未配置的交互会话可直进同一套首次向导。不改 HTTP API、权限/沙箱/思考/记忆命令、`init`。
6
-
7
- ---
8
-
9
- ## 背景
10
-
11
- 当前 `runSetup()` 写在 `config.ts` 里,用 `readline` 编号问卷:一次只能做一步,默认「完成」什么都不改;首次强制名叫 `default`;不能删除、不能改名、不收集 `contextWindow`;模型列表整表打印、不可筛选(与 TUI `/model` 脱节);已有 API Key 会当默认值回显;校验失败 `process.exit(1)`;无非交互 flag;无测试。未配置时 `min-agent` 只打印 `Not configured. Run: min-agent setup` 并退出。
12
-
13
- `permission` / `sandbox` / `think` / `memory` 已有独立命令;`init` 只建 `.min-agent/`。setup 不应变成第二套设置中心。
14
-
15
- ## 设计决策(已与用户确认)
16
-
17
- | 决策 | 结论 |
18
- |------|------|
19
- | 方案 | B:Provider 向导 + 可循环管理器,不把权限/沙箱/思考/记忆塞进向导 |
20
- | UI | 一次性 Ink 应用;不抽 `ModelPicker`(它绑在会话 overlay 上) |
21
- | `config.ts` | 只留读写、`fetchModels`、`isConfigured`、`getActiveProvider`;向导搬走 |
22
- | Flag 命名 | setup 不用 `--model`(根命令已占用);写入默认模型用 `--default-model` |
23
- | `--yes` | 复用根上已有 `-y/--yes`,setup 里表示跳过覆盖/删除确认,不改 permission |
24
- | 未配置会话 | TTY:直进首次向导(无单独 readline Y/n);非 TTY:保持硬错误 |
25
- | 密钥 | 只写 `~/.min-agent/config.json`;本轮不做 `setup --project` |
26
- | 文案 | 向导/管理器中文;CLI `--help` 描述保持英文 |
27
-
28
- ## 目标与非目标
29
-
30
- **目标:**
31
-
32
- - 安装后在 TTY 上两分钟内能配好一个可用 Provider 并进入对话
33
- - 再次运行 setup 是循环管理器(添加 / 切换 / 修改 / 删除 / 测连),不是一次性问卷
34
- - 模型列表可筛选,交互质量与 `/model` 同级
35
- - flag 齐了可不进向导,便于脚本
36
- - 完整 Key 不出现在终端输出
37
- - 纯函数与非交互路径可测;Ink 全向导不在 CI 里跑
38
-
39
- **非目标:**
40
-
41
- - Anthropic 原生协议
42
- - setup 内配置权限、沙箱、思考、记忆
43
- - 项目级密钥或 `setup --project`
44
- - 把 setup 做成全配置 TUI
45
- - 本轮做 i18n
46
- - 抽出跨 TUI/setup 的共用 picker 组件
47
-
48
- ---
49
-
50
- ## 1. 模块边界
51
-
52
- ```
53
- src/cli/commands/setup.ts 入口:解析 flag,分流非交互 / Ink
54
- src/cli/setup/flow.ts 编排:首次向导、管理器循环、未配置衔接
55
- src/cli/setup/provider-form.ts 纯函数:校验、默认名、重名、写入形状
56
- src/cli/setup/detect.ts 短超时探测本机 Ollama(GET /api/tags)
57
- src/cli/setup/ui.tsx 一次性 Ink 向导
58
- ```
59
-
60
- `fetchModels` 仍在 `config.ts`。模型缓存改为按 `baseURL` 分桶(文件仍叫 `models-cache.json`,值为 `Record<string, string[]>`,key 为规范化后的 baseURL)。若现有文件是旧的 `string[]` 或无法解析,视为空缓存并在下次成功拉取后按新格式覆盖。**setup 的测连与向导里的「是否拉到列表」走实时请求,成功才更新缓存;失败或空列表不得把过期缓存当成成功。** `min-agent models` 与 TUI `/model` 仍可在实时失败时回退该 provider 自己的缓存。
61
-
62
- 向导用 `ink.render()`,结束即 `unmount`。从会话进来时:向导结束且 `isConfigured()` 后,再启动现有 TUI(两段串行,不同时占 stdin)。
63
-
64
- 非 TTY 不启动 Ink。
65
-
66
- ---
67
-
68
- ## 2. CLI 表面
69
-
70
- 根命令已有 `--model`(本轮模型)和 `--yes`(本轮全部允许)。Commander 把同名 option 解析到根上,setup **不得**再声明 `--model`。
71
-
72
- setup 子命令自己的参数:
73
-
74
- | Flag | 含义 |
75
- |------|------|
76
- | `--type <type>` | `openai-compatible` / `openai` / `ollama` |
77
- | `--url <url>` | Base URL |
78
- | `--api-key <key>` | 密钥(ollama 可省) |
79
- | `--name <name>` | Provider 名 |
80
- | `--default-model <id>` | 写入配置的默认模型 |
81
- | `--context-window <n>` | 可选 token 窗口,须 ≥ 1 的整数 |
82
- | `--switch <name>` | 只切换 `activeProvider` |
83
- | `--remove <name>` | 删除 |
84
-
85
- `--yes` / `-y`:setup 通过 `program.opts().yes`(与其它子命令读根 option 的方式一致)读取。在 setup 中表示跳过覆盖/删除确认,不调用 `setAutoApprove`。
86
-
87
- **非交互判定:** 出现 `--type`、`--switch`、`--remove` 任一则不进向导。`--type` 与 `--switch` / `--remove` 同时出现 → `CliError`(一次只做一件事)。
88
-
89
- 命令描述(help):`Configure providers (interactively, or with flags)`。
90
-
91
- ---
92
-
93
- ## 3. 谁会进向导
94
-
95
- | 入口 | 未配置 + TTY | 未配置 + 非 TTY |
96
- |------|----------------|-----------------|
97
- | `min-agent` / `chat` / `code` / 直接带一句 | 首次向导,成功后再进 TUI | `Not configured.` 退出 1,hint `Run: min-agent setup` |
98
- | `min-agent setup` | 向导(已有可用 Provider 则管理器) | 必须带 `--type` / `--switch` / `--remove`,否则退出 1 |
99
- | `exec` / `serve` / `models` | **不**进向导 | 同左 |
100
-
101
- `isConfigured()` 不变:当前 Provider 同时有 `baseURL` 与 `apiKey`。
102
-
103
- 现有 `cli-invoke.test.ts` 对空 argv 使用 `stdin: "ignore"`,继续走非 TTY 硬错误,语义不变。
104
-
105
- 从会话直进向导时,第一屏为 S0(开始配置 / 退出)。用户直接跑 `min-agent setup` 时跳过 S0。不在 Ink 之前再用 readline 问 Y/n。
106
-
107
- 带首条 prompt、`--resume` 或 `-i` 的未配置会话:向导成功后,把原有 session 参数原样交给 `startTuiSession`(prompt 仍作为第一轮立刻发送)。向导退出则不启动 TUI、不发送。
108
-
109
- ---
110
-
111
- ## 4. 首次向导
112
-
113
- Esc / Ctrl+C 任意一步退出且不写盘。
114
-
115
- **S0 未配置入口**(仅从会话直进)
116
-
117
- - 选项:开始配置 / 退出
118
- - 退出或 Esc → 退出码 1,hint:`min-agent setup`
119
-
120
- **S1 类型**
121
-
122
- - OpenAI 兼容接口
123
- - OpenAI
124
- - Ollama(本地)
125
-
126
- `detect.ts` 在 800ms 内请求 `http://localhost:11434/api/tags`。成功则 Ollama 项旁标注「已检测到」,默认光标落在它上面;否则默认「兼容接口」。探测失败静默,不报错。
127
-
128
- **S2 连接信息**
129
-
130
- | 类型 | 问什么 | 默认 |
131
- |------|--------|------|
132
- | openai-compatible | URL → Key(掩码) | URL:`https://api.openai.com/v1` |
133
- | openai | 只问 Key | URL 写死 `https://api.openai.com/v1` |
134
- | ollama | 只问 URL | 探测成功用探测到的地址(补 `/v1`),否则 `http://localhost:11434/v1`;Key 固定 `ollama` |
135
-
136
- URL 空、非 ollama 且 Key 空 → 本屏报错,不前进。Ollama URL 走现有 `normalizeOllamaBaseURL`。
137
-
138
- **S3 拉模型**
139
-
140
- 显示「正在获取模型列表…」。实时请求失败或空列表 → S3b,不当成成功。
141
-
142
- - **S3a 有列表:** 可筛选选择器(输入过滤、↑↓、Enter、Esc 回 S2)。过滤无匹配时 Enter 把当前输入当模型名。
143
- - **S3b 无列表:** 「无法获取模型列表,请手动输入」。预填:ollama `llama3`,openai `gpt-4o`,兼容接口空。空提交不允许。仅兼容接口在此之后问窗口:跳过 / 手动输入 ≥ 1 的整数。ollama / openai 不问(运行时探测)。
144
-
145
- **S4 名称**
146
-
147
- 预填 `suggestName(type, url, existingNames)`:
148
-
149
- - `openai` → `openai`
150
- - `ollama` → `ollama`
151
- - 兼容接口 → URL 主机名去掉 `www.`,`.` 换成 `-`;解析失败则 `provider`
152
- - 与已有名冲突 → `openai-2`、`openai-3`…
153
-
154
- 空提交用预填。非法名(空、含 `/` 或 `:`)本屏报错。
155
-
156
- **S5 摘要 + 写入**
157
-
158
- 展示类型、URL、Key 掩码、模型、名称。确认后追加到 `providers`,`activeProvider` 设为该名。`saveConfig` 原子写,保留未知字段。成功后打印实际保存路径。从会话进来则接着启动 TUI。
159
-
160
- 首次也不再写死名称 `default`。
161
-
162
- ---
163
-
164
- ## 5. 管理器循环
165
-
166
- 已有至少一个可用 Provider 时进入。选完一项(除「完成」)后回到列表,直到主动退出。
167
-
168
- **H0 列表**
169
-
170
- 每行:`名称 URL 默认模型`,当前项标 `*`。动作:添加 / 设为当前 / 修改 / 删除 / 测试连接 / 完成。
171
-
172
- | 动作 | 行为 |
173
- |------|------|
174
- | 添加 | S1–S5;成功后设为当前并回 H0 |
175
- | 设为当前 | 仅一个则提示无需切换;多个则选一个,立刻 `saveConfig`,回 H0 |
176
- | 修改 | 先选目标(默认当前),再 S1–S4,名称默认原名。改名冲突则本屏报错。Key 空提交 = 保留原 Key,占位「已保存 (…xxxx),回车保留」。若改的是当前项且改了名,同步 `activeProvider` |
177
- | 删除 | 选目标 → 确认屏回显名称。删的是当前项则 `activeProvider` 改为剩余第一项;删光则去掉 `activeProvider`,下次进入变首次向导。无 `--yes` 必须确认 |
178
- | 测试连接 | 选目标 → 实时拉列表。成功:「可访问,N 个模型」。失败:HTTP 状态 + 一句可执行提示(检查地址、密钥,或确认 Ollama 已启动)。不把缓存当成功 |
179
- | 完成 / Esc | 退出码 0(直接跑 setup 时) |
180
-
181
- 管理器内每次成功操作立刻写盘。Esc 只退出向导,不回滚已保存的步骤。
182
-
183
- ---
184
-
185
- ## 6. 非交互
186
-
187
- | 动作 | 必填 | 失败 |
188
- |------|------|------|
189
- | `--type ollama` | 无。URL 默认本地。模型:实时拉到则用第一个,否则 `llama3` | 名称冲突且无 `--yes` |
190
- | `--type openai` | `--api-key`。无 `--default-model` 则实时拉列表取第一,再没有则报错 | Key 空;模型仍空 |
191
- | `--type openai-compatible` | `--url`、`--api-key`。模型规则同 openai | URL/Key 空;模型仍空 |
192
- | `--switch` | 名字必须存在 | 找不到 |
193
- | `--remove` | 名字必须存在。非 TTY 且无 `--yes` 则拒绝 | 找不到;TTY 无 `--yes` 则进入确认(若已判定非交互且非 TTY,直接失败) |
194
-
195
- 同名已存在:交互问覆盖 / 换名;非交互必须 `--yes` 才覆盖,否则退出 1。
196
-
197
- `--url` 对 ollama 仍做 `normalizeOllamaBaseURL`。`--name` 缺省时用 `suggestName`。`--context-window` 若给出则写入该 Provider。
198
-
199
- 添加失败不写盘。覆盖与删除才改已有条目。
200
-
201
- ---
202
-
203
- ## 7. 错误与安全
204
-
205
- 向导/管理器标题:「配置服务商」。字段「名称」不用 Provider 当主文案。
206
-
207
- | 场景 | 文案 | 码 |
208
- |------|------|-----|
209
- | 非 TTY 未配置(会话) | `Not configured.` / hint `Run: min-agent setup` | 1 |
210
- | 非 TTY 无 flag 的 setup | `无法在非交互环境完成配置。` / hint 列出可用 flag | 1 |
211
- | `--type` 与 `--switch`/`--remove` 同时出现 | `一次只能执行一种操作。` | 1 |
212
- | 缺字段 | `API 密钥不能为空。` / `API 地址不能为空。` / `默认模型不能为空。` | 1 |
213
- | 名称不合法 | `名称不能为空,且不能包含 / 或 :。` | 1 |
214
- | 同名且非交互无 `--yes` | `已存在同名服务商 "x"。` / hint `加上 --yes 以覆盖` | 1 |
215
- | `--switch`/`--remove` 找不到 | `找不到服务商 "x"。` | 1 |
216
- | 非 TTY 删除无 `--yes` | `删除需要确认。` / hint `加上 --yes` | 1 |
217
- | `--context-window` 非法 | `上下文窗口必须是大于 0 的整数。` | 1 |
218
- | 测连失败 | `无法连接到该服务商。` + 状态码 + 下一句可执行提示 | 管理器内不退出进程 |
219
- | 非交互添加时拉模型失败且无 `--default-model` | 网络/探测异常用码 2;openai 类模型仍空用码 1 | 1 或 2 |
220
- | 向导 Esc / 选退出 | 不写盘。从会话进来:码 1 + hint `min-agent setup`。直接 `setup`:码 0 | — |
221
- | 保存成功 | `已保存到 <实际路径>` | 0 |
222
-
223
- 校验用 `CliError`,不再在 collect 路径里 `process.exit`。完整 Key 不进 stdout/stderr/摘要;只显示 `…` + 末 4 位(短于 4 位则全掩码为 `****`)。测连错误体若含 `Bearer` 或 `sk-` 形态则剥掉。
224
-
225
- ---
226
-
227
- ## 8. 测试
228
-
229
- **纯函数**(`provider-form.ts`、名称建议、掩码;`detect` 用 mock `fetch`):
230
-
231
- - 默认名:openai / ollama / 主机名 / 冲突后缀
232
- - 非法名、重名覆盖、改名同步 `activeProvider`
233
- - 删除当前项后的 `activeProvider`;删光后 `isConfigured() === false`
234
- - ollama 不需要 Key;兼容接口缺 URL/Key 失败
235
- - 写入 JSON 保留未知字段
236
- - 掩码:空、短于 4 位、正常 Key
237
-
238
- **非交互 CLI**(隔离 `MIN_AGENT_CONFIG_DIR`,stdin ignore):
239
-
240
- - `setup --type ollama` 写入后可配置
241
- - `setup --type openai --api-key k --default-model gpt-4o`
242
- - 缺 Key 退出 1
243
- - 同名无 `--yes` 失败;有 `--yes` 覆盖
244
- - `--switch` / `--remove --yes`
245
- - `--type` + `--remove` 互斥
246
- - 非 TTY 无 flag 的 `setup` 退出 1 且不写盘
247
- - 完整 Key 不出现在 stdout/stderr
248
- - 空 argv 未配置仍 `Not configured`(现有用例保持)
249
-
250
- **分流:** `flow.ts` 注入 `isTty`,断言非 TTY 不 `render`。列表过滤抽纯函数单测。不在 CI 跑完整 Ink 向导,不打真实 OpenAI/Ollama。
251
-
252
- **缓存:** 按 baseURL 分桶;A 的缓存不能被 B 读到。测连失败不把缓存当成功。
253
-
254
- ---
255
-
256
- ## 9. 文档同步
257
-
258
- 必须改:
259
-
260
- - `README.md` 中英:setup 仍是引导配置服务商;增加非交互三例(ollama、兼容接口、`--switch`);不写内部模块名
261
- - `src/cli/program.ts` 帮助:setup 一行带常用 flag;Examples 加一条非交互
262
- - `skills/self-config/SKILL.md` Providers:CLI `min-agent setup` 及 `--type` / `--url` / `--api-key` / `--name` / `--default-model` / `--switch` / `--remove`;`--yes` 在 setup 中表示跳过确认
263
- - `skills/self-config/reference.md`:字段不变;补一句交互与非交互添加都走 setup,密钥只写全局
264
-
265
- 不改:
266
-
267
- - `docs/API.md`(HTTP 无 setup 接口)
268
- - `exec` / `serve` / `models` 的未配置硬错误
269
- - TUI `/help`(无 setup 斜杠命令)
270
- - 权限 / 沙箱 / 思考 / 记忆命令
271
-
272
- slash 里「运行 min-agent setup」可保留。
273
-
274
- ---
275
-
276
- ## 10. 实现顺序建议
277
-
278
- 1. `provider-form.ts` + 缓存分桶 + 测试
279
- 2. 非交互 `setup` flag 与 CLI 测试
280
- 3. Ink 首次向导 + 管理器
281
- 4. 会话未配置 TTY 衔接
282
- 5. README / help / self-config
@@ -1,174 +0,0 @@
1
- # 输入框粘贴图片与文本片段设计
2
-
3
- 日期: 2026-08-23
4
- 状态: 已批准(方案 1:输入框内原子标记)
5
-
6
- ## 问题
7
-
8
- 交互会话的输入框不能把剪贴板图片挂到当前草稿:贴图只能 `/paste`(立刻发送)或 `/attach <路径>`。多行文本粘贴会整段铺进输入框,末尾换行还可能被当成 Enter 误发送。
9
-
10
- ## 目标
11
-
12
- 1. 剪贴板图片用 Ctrl+V 插到光标处,显示为 `[图片 #N]`,写完说明再 Enter 发送
13
- 2. 一次粘贴只要超过一行,收成 `[粘贴 #N · X 行]`,发送时带上全文
14
- 3. 单行粘贴仍直接插入
15
- 4. 标记在输入框里是原子块:左右移、退格、Delete、点击都不拆开
16
- 5. 只有附件、没打字也可以发送
17
-
18
- ## 不做
19
-
20
- - 不改 HTTP `/v1/paste`,不新增配置项,不改 `skills/self-config`
21
- - 不把 Cmd+V 从终端抢过来(macOS 贴图用 Ctrl+V)
22
- - 不把附件正文写入 `input-history.json`
23
- - 不做拖拽文件进终端、不做输入框内 Shift 选区
24
- - `/paste [t]` 仍是立刻发送剪贴板图片,不改成挂草稿
25
-
26
- ## 设计决策
27
-
28
- | 决策 | 结论 |
29
- |------|------|
30
- | 呈现 | 草稿字符串里每个附件一个私有区码点;屏幕上画成标记 |
31
- | 文本门槛 | 规范化后仍含换行 → 片段;否则当普通字插入 |
32
- | 贴图快捷键 | Ctrl+V 读系统剪贴板;无图时静默 |
33
- | `/paste` | 保持立刻发送 |
34
- | 斜杠命令 | `display` 去空白后以 `/` 开头则走现有命令处理,附件不进入对话 |
35
- | 历史 | 只存展开后的短标记字面量,不存图片/长文;翻历史不会恢复附件 |
36
-
37
- ---
38
-
39
- ## 1. 数据模型
40
-
41
- `InputBar` 继续用 `{ value, caretIndex }`。`caretIndex` 仍是 `Array.from(value)` 的码点下标。
42
-
43
- 每个附件占用 **一个** Unicode 私有区码点,从 `U+E000` 起按插入顺序分配,本条草稿内不复用。旁路表:
44
-
45
- ```ts
46
- type DraftAttachment =
47
- | { kind: "image"; id: number; data: Buffer; mimeType: string }
48
- | { kind: "snippet"; id: number; text: string; lineCount: number }
49
- ```
50
-
51
- `id` 按类型分开从 1 递增:`[图片 #1]` 与 `[粘贴 #1 · 3 行]` 可以同时存在。
52
-
53
- 屏幕标记(用户可见,无实现细节):
54
-
55
- - 图片:`[图片 #${id}]`
56
- - 片段:`[粘贴 #${id} · ${lineCount} 行]`
57
-
58
- 未在旁路表中的私有区字符按宽 1 的 `�` 绘制,不当附件。
59
-
60
- 每次编辑后扫描 `value`:旁路表里码点已不在 `value` 中的条目删除(退格、Ctrl+U/K/W、Ctrl+C 清空都走这条)。Ctrl+C 清空或提交成功后:`value` 清空,旁路表清空,两个计数器回到 1。
61
-
62
- 折行与点击:该码点是一个 cluster,显示宽度为 `min(displayWidth(标记), 输入区宽度)`。过宽时单独占一行,绘制时末尾用 `…` 截断;点击该行任一单元格都落在这一个码点上。硬件光标列用展开后的显示宽度计算,与现有 IME 锚点一致。
63
-
64
- ---
65
-
66
- ## 2. 文本粘贴
67
-
68
- 挂载输入框时打开 bracketed paste(`CSI ? 2004 h`),卸载时关闭(`CSI ? 2004 l`),与现有鼠标协议一起写 stdout。
69
-
70
- 在 InputBar 已有的 raw stdin 监听里拼包:`ESC[200~` 起、`ESC[201~` 止。包收齐前 `useInput` 丢掉这段输入(含 Ink 剥掉 ESC 后的 `[200~` / `[201~` 碎片),避免插两遍,也避免粘贴里的换行走现有「末尾 `\r`/`\n` 当 Enter」逻辑。
71
-
72
- 粘贴正文处理(纯函数,可单测):
73
-
74
- 1. `\r\n` 与 `\r` 全部变成 `\n`
75
- 2. 去掉**末尾一个** `\n`(编辑器复制常带)
76
- 3. 若仍含 `\n`:分配片段码点,插入光标处,`lineCount = text.split("\n").length`
77
- 4. 否则:当普通文本插入(与打字相同)
78
-
79
- 片段正文上限 **1_000_000** 个 UTF-16 码元。超过则不插入,系统提示 `粘贴内容过大,未附加`。
80
-
81
- 单行过长仍插入,不收成片段。
82
-
83
- ---
84
-
85
- ## 3. 图片粘贴
86
-
87
- `useInput` 在现有 `Ctrl+B` 之后、吞掉其它 Ctrl 组合之前处理 `key.ctrl && input === "v"`:调用已有 `getClipboardImage()`。
88
-
89
- - 有图且 `data.length <=` 现有 `MAX_IMAGE_BYTES`(5MB):插入图片码点,MIME 用剪贴板返回值
90
- - 有图但超过 5MB:提示 `图片超过 5MB,未附加`,不插入
91
- - 无图:静默返回(不提示),避免想贴字时误按 Ctrl+V 刷警告
92
-
93
- Mac 上 Cmd+V 仍由终端注入文字。README 与 `/help` 写明贴图用 Ctrl+V。
94
-
95
- ---
96
-
97
- ## 4. 编辑
98
-
99
- 因为每个标记是一个码点,现有 `moveCaretHorizontal` / `backspaceAt` / `deleteAt` 已整块移动或删除。`Ctrl+W` / `Ctrl+K` / `Ctrl+U` 按现有「删一段字符串」处理,事后扫描丢掉孤儿附件。
100
-
101
- 点击:先按展开后的显示列命中 cluster,再映射回 `value` 码点下标;点在标记上时 caret 落在该码点前(与现在点在某字前一致),再按右键一次跳过整块。
102
-
103
- Enter:`display` 去空白后非空,**或**旁路表非空,即可发送。发送后清空草稿与表。
104
-
105
- ---
106
-
107
- ## 5. 提交与展示
108
-
109
- `onSubmit` 改为接收一次提交的结构化结果:
110
-
111
- ```ts
112
- type SubmittedPrompt = {
113
- display: string
114
- content: string | Array<{ type: "text"; text: string } | { type: "image"; image: Buffer; mimeType: string }>
115
- }
116
- ```
117
-
118
- `display`:把 `value` 里的附件码点换成标记文案,用于对话区用户气泡和输入历史。
119
-
120
- `content` 按 `value` 从左到右展开:
121
-
122
- - 连续普通字合成一个 `text` part
123
- - 片段换成一个 `text` part,正文前后各单独一行分隔:`--- 粘贴 #${id} ---`
124
- - 图片一个 `image` part(`Buffer` + `mimeType`)
125
-
126
- 若没有任何图片 part,`content` 收成单个字符串(与现在 `messages.push({ content: text })` 兼容)。有图片则为数组。
127
-
128
- `tui-chat`:`addMessage` 用 `display`;`messages.push({ role: "user", content })` 用 `content`。`lastUserMessage` 存 `display`。
129
-
130
- `display` 去空白后以 `/` 开头:原样交给现有 `handleSlashCommand`,旁路表丢弃、不写入 `messages`(避免 `/help` 误带一张图)。否则按上面的 `content` 入列。
131
-
132
- 超限提示不进输入框正文:`InputBar` 增加 `onNotice(text: string)`,`tui-chat` 接到现有系统消息通道(与 `/paste` 失败提示同一处)。
133
-
134
- 输入历史:`pushInput` 写入 `display`(短标记,无二进制、无片段全文)。从历史上召回的标记是普通字符,旁路表为空。
135
-
136
- ---
137
-
138
- ## 6. 文件与同步
139
-
140
- | 文件 | 改动 |
141
- |------|------|
142
- | `src/tui/paste-draft.ts`(新) | 规范化粘贴、单行/多行判定、码点分配、展开 `display`/`content`、孤儿回收;无 React |
143
- | `src/tui/bracketed-paste.ts`(新) | 从 stdin 块里扫描 bracketed paste 包 |
144
- | `src/tui/caret-pos.ts` / `text-width.ts` | cluster 宽度可注入「此码点显示宽」;`visualRows` / 点击换算认识附件宽 |
145
- | `src/tui/InputBar.tsx` | 打开/关闭 bracketed paste、粘贴包、Ctrl+V、旁路表、`onNotice`、按新规则提交 |
146
- | `src/tui/index.tsx` / `App.tsx` | `onSubmit` 改为 `SubmittedPrompt`;透传 `onNotice` |
147
- | `src/tui-chat.ts` | 按 `display`/`content` 入列;斜杠判定见上;`onNotice` 走系统消息 |
148
- | `src/tui/slash-handler.ts` | `/help` 快捷键增加 Ctrl+V 贴图、多行粘贴收成片段 |
149
- | `README.md` | 中英快捷键各补一句 |
150
- | `docs/API.md` | 不改(TUI 便利,协议不变) |
151
-
152
- `/paste`、`/attach`、CLI `-i` 行为不变。
153
-
154
- ---
155
-
156
- ## 7. 测试
157
-
158
- 纯函数(`tests/paste-draft.test.ts`、`tests/bracketed-paste.test.ts`,必要时扩 `tests/caret-pos.test.ts`):
159
-
160
- - 单行;`a\nb`;`a\nb\n` 去掉末尾换行后仍是两行片段;仅 `hello\n` → 单行插入
161
- - 片段展开顺序:字 + 图 + 片段不错位;无图时 `content` 为字符串
162
- - 删除码点后旁路表回收;清空后计数从 1 再起
163
- - 超 1_000_000 码元拒绝;标记过宽时点击仍落在该码点
164
- - bracketed paste 扫描:跨 chunk、内含 `\n`/`\r`、无结束符时保持缓冲
165
-
166
- `InputBar` 不测 Ink 渲染。stdin 夹具覆盖:粘贴多行不提交;粘贴中的 `useInput` 碎片不插入。
167
-
168
- ## 错误文案
169
-
170
- | 情况 | 文案 |
171
- |------|------|
172
- | 图片 > 5MB | `图片超过 5MB,未附加` |
173
- | 片段 > 1_000_000 码元 | `粘贴内容过大,未附加` |
174
- | Ctrl+V 无图 | (无文案) |
@@ -1,140 +0,0 @@
1
- # 思考线协议适配 — 设计文档
2
-
3
- **日期:** 2026-08-23
4
- **状态:** 已批准(启发式 + 400 改参重试并缓存)
5
-
6
- ## 问题
7
-
8
- 用户配置是统一的 `off | low | medium | high | max`,但上游 chat 请求的思考字段因模型和网关而异。min-agent 目前一律发送:
9
-
10
- ```json
11
- { "reasoning_effort": "<档位>", "thinking": { "type": "enabled" | "disabled" } }
12
- ```
13
-
14
- MiniMax-M3(经 Console Go 等网关)只接受 `thinking.type` 为 `adaptive` 或 `disabled`,收到 `enabled` 会 400。官方 OpenAI 则可能根本不认 `thinking` 对象。
15
-
16
- 模型目录(`models.lonae.com`)只提供上下文窗口和 effort 列表,**不提供** `thinking.type` 的合法取值。MiniMax-M3 的 `reasoningOptions` 甚至是空数组,目录查找成功也无法避免这次 400。
17
-
18
- ## 目标
19
-
20
- 1. 用户侧配置、`/think`、CLI `--think`、HTTP `/v1/thinking` 仍只用五档英文强度,不暴露 `adaptive` / `enabled` / `xhigh`
21
- 2. 发出请求前按 `(providerHost, modelId)` 选择线协议:启发式先发对
22
- 3. 若仍 400 且错误可解析为思考字段约束,静默改 profile、**同一请求只重试一次**,并把学到的 profile 写入本地缓存
23
- 4. 目录继续只负责窗口和档位列表;线协议与目录解耦
24
-
25
- ## 不做
26
-
27
- - 不扩展 Lonae catalog schema
28
- - 不把 `adaptive` 放进 `/think` 选择器或配置值
29
- - 不为 MiniMax-M3 把选择器收成「开/关」(非 `off` 一律当开思考;档位无深度时仍发对应 `reasoning_effort`,由上游忽略)
30
- - 不重试非思考类 400,不二次以上重试
31
- - 不把 wire profile 写入 `config.json`
32
- - 不改变 compaction、reasoning 流式折叠、TUI 思考块展示
33
-
34
- ## 设计决策
35
-
36
- | 决策 | 结论 |
37
- |------|------|
38
- | 用户配置 | 仍是 `off\|low\|medium\|high\|max`;`thinkingEnabled = thinking !== "off"` |
39
- | 线协议层 | 独立 wire profile,不塞进 Lonae 缓存条目 |
40
- | 解析顺序 | 学习缓存 → 启发式 → 默认 `enabled-disabled` + `reasoning_effort` |
41
- | 注入点 | 现有 `modelFetch` / `withThinkingBody`,可读写 URL 与 JSON body |
42
- | 400 | 仅当 body 可解析为思考字段约束时改 profile 并重试一次;对用户静默 |
43
- | 缓存文件 | `~/.min-agent/thinking-wire-cache.json`(`MIN_AGENT_CONFIG_DIR` 下同路径),TTL 7 天 |
44
- | OpenAI 官方 | `provider.type === "openai"` → 不发 `thinking` 对象 |
45
- | MiniMax | `modelId` 或 hostname 含 `minimax`(大小写不敏感)→ `adaptive-disabled` |
46
-
47
- ---
48
-
49
- ## 1. Wire profile
50
-
51
- ```ts
52
- type ThinkingTypeMode = "enabled-disabled" | "adaptive-disabled" | "omit"
53
- type EffortMode = "reasoning_effort" | "omit"
54
-
55
- interface ThinkingWireProfile {
56
- version: 1
57
- thinkingType: ThinkingTypeMode
58
- effort: EffortMode
59
- timestamp: number
60
- }
61
- ```
62
-
63
- `applyThinkingToChatBody(body, spec, profile)`:
64
-
65
- - `effort === "reasoning_effort"`:写 `reasoning_effort`(现有 `toWireEffort` 结果);`omit` 则删除该键
66
- - `thinkingType === "omit"`:删除 `thinking`
67
- - 否则写 `thinking.type`:开思考时 `enabled` 或 `adaptive`,关思考时 `disabled`
68
-
69
- `ThinkingRequestBody` 仍表示用户意图(`reasoningEffort` + `thinkingEnabled`),不把 profile 放进 ALS。fetch 时用请求 URL 的 host 和 body 里的 `model` 解析 profile。
70
-
71
- 缓存键:`${hostname.toLowerCase()}::${modelId.toLowerCase()}`。hostname 从请求 URL 取;取不到则只用 `modelId`。
72
-
73
- ---
74
-
75
- ## 2. 启发式
76
-
77
- 在学习缓存未命中时:
78
-
79
- 1. `modelId` 或 hostname 包含 `minimax` → `{ thinkingType: "adaptive-disabled", effort: "reasoning_effort" }`
80
- 2. 当前 provider `type === "openai"` → `{ thinkingType: "omit", effort: "reasoning_effort" }`
81
- 3. 否则 → `{ thinkingType: "enabled-disabled", effort: "reasoning_effort" }`
82
-
83
- 启发式结果不写磁盘。只有 400 学到的 profile 才持久化,以便覆盖错误启发式。
84
-
85
- `toWireEffort` 规则不变:`off` → `none`;`max` 仍按目录/`openai` 映射 `xhigh`。
86
-
87
- ---
88
-
89
- ## 3. 400 探测
90
-
91
- 仅处理 chat completions 的 JSON body 请求。流程:
92
-
93
- 1. 按当前 profile 注入后 `fetch`
94
- 2. 若 `response.ok` 或状态不是 400:原样返回(仍走现有 `foldReasoningInChatResponse`)
95
- 3. 读 400 body(`text()`)。用纯函数 `parseThinkingWireHint(text)` 得到对 profile 的补丁
96
- 4. 补丁相对当前 profile **有变化** 才更新内存+磁盘,用新 profile 重写 body,**重试一次**
97
- 5. 第二次无论成败都不再为思考字段重试;失败则把第二次响应交给 SDK(若第二次未发出,把第一次 400 用已读文本重建 `Response`)
98
-
99
- `parseThinkingWireHint` 识别:
100
-
101
- | 错误文本形态 | 补丁 |
102
- |--------------|------|
103
- | `thinking.type` 且 `allowed` 列表含 `adaptive` 与 `disabled` | `thinkingType: "adaptive-disabled"` |
104
- | `thinking.type` 且 `allowed` 含 `enabled` 与 `disabled` | `thinkingType: "enabled-disabled"` |
105
- | `Unknown parameter` / `unknown parameter` 指向 `thinking` | `thinkingType: "omit"` |
106
- | 同上指向 `reasoning_effort` | `effort: "omit"` |
107
-
108
- 列表解析忽略大小写与空白。无法识别或补丁无变化 → 不重试。
109
-
110
- 并发:两个请求可能同时 400 再各写一次缓存,可接受,不引入锁。内存 Map + 磁盘与 `model-catalog` 相同模式。TTL 7 天;过期条目当未命中,可再次学习。`version !== 1` 的条目忽略。
111
-
112
- ---
113
-
114
- ## 4. 错误与界面
115
-
116
- 重试对用户不可见:没有「改参」「adaptive」提示。两次都失败时沿用现有 `AI_APICallError` 展示。
117
-
118
- `/think`、状态栏「思考 high」、HTTP `GET/POST /v1/thinking` 的字段与文案不变。
119
-
120
- ---
121
-
122
- ## 5. 文档与测试
123
-
124
- 同步:
125
-
126
- - `skills/self-config/SKILL.md`:请求按模型/网关映射 `thinking` / `reasoning_effort`,并提及学习缓存文件;不写探测实现细节
127
- - `skills/self-config/reference.md`:环境变量表旁可注明缓存文件名(若该文件已列目录缓存)
128
- - `docs/API.md`:用户可见 thinking 枚举不变;不必描述 wire 字段
129
- - `README.md`:不增加线协议说明
130
- - `/help`:不改
131
-
132
- 测试(纯函数 + fetch 包装):
133
-
134
- - `applyThinkingToChatBody` 三种 `thinkingType` × 开/关、`effort` omit
135
- - 启发式:minimax 模型名、minimax host、openai type、默认
136
- - `parseThinkingWireHint`:本次 MiniMax 原文、unknown parameter、无法识别
137
- - fetch:400 可解析 → 第二次请求 body 已改且只发两次;不可解析 → 只发一次
138
- - 缓存读写、过期、version 不匹配
139
-
140
- 实现落点:`src/thinking.ts` 扩展拼 body 与 hint 解析;新建 `src/thinking-wire.ts`(profile 解析、启发式、缓存)以免 `thinking.ts` 再膨胀;`src/provider.ts` 的 `modelFetch` 接 400 重试。测试放 `tests/thinking.test.ts` 与 `tests/thinking-wire.test.ts`。