thatgfsj-code 3.0.3 → 3.0.6

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 (212) hide show
  1. package/README.md +64 -21
  2. package/dist/app/index.d.ts +93 -22
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +232 -40
  5. package/dist/app/index.js.map +1 -1
  6. package/dist/cache/smartModel.d.ts +29 -22
  7. package/dist/cache/smartModel.d.ts.map +1 -1
  8. package/dist/cache/smartModel.js +33 -40
  9. package/dist/cache/smartModel.js.map +1 -1
  10. package/dist/cmd/index.d.ts +12 -20
  11. package/dist/cmd/index.d.ts.map +1 -1
  12. package/dist/cmd/index.js +192 -146
  13. package/dist/cmd/index.js.map +1 -1
  14. package/dist/config/index.d.ts +4 -0
  15. package/dist/config/index.d.ts.map +1 -1
  16. package/dist/config/index.js +9 -6
  17. package/dist/config/index.js.map +1 -1
  18. package/dist/config/types.d.ts +5 -0
  19. package/dist/config/types.d.ts.map +1 -1
  20. package/dist/llm/anthropic.d.ts +1 -1
  21. package/dist/llm/anthropic.d.ts.map +1 -1
  22. package/dist/llm/anthropic.js +116 -84
  23. package/dist/llm/anthropic.js.map +1 -1
  24. package/dist/llm/gemini.d.ts +27 -3
  25. package/dist/llm/gemini.d.ts.map +1 -1
  26. package/dist/llm/gemini.js +196 -73
  27. package/dist/llm/gemini.js.map +1 -1
  28. package/dist/llm/index.d.ts +20 -1
  29. package/dist/llm/index.d.ts.map +1 -1
  30. package/dist/llm/index.js +71 -10
  31. package/dist/llm/index.js.map +1 -1
  32. package/dist/llm/openai.d.ts +5 -1
  33. package/dist/llm/openai.d.ts.map +1 -1
  34. package/dist/llm/openai.js +96 -56
  35. package/dist/llm/openai.js.map +1 -1
  36. package/dist/mcp/client.d.ts +84 -34
  37. package/dist/mcp/client.d.ts.map +1 -1
  38. package/dist/mcp/client.js +291 -126
  39. package/dist/mcp/client.js.map +1 -1
  40. package/dist/session/compactor.d.ts +27 -7
  41. package/dist/session/compactor.d.ts.map +1 -1
  42. package/dist/session/compactor.js +78 -23
  43. package/dist/session/compactor.js.map +1 -1
  44. package/dist/session/index.d.ts +96 -33
  45. package/dist/session/index.d.ts.map +1 -1
  46. package/dist/session/index.js +278 -60
  47. package/dist/session/index.js.map +1 -1
  48. package/dist/tools/file.d.ts +10 -2
  49. package/dist/tools/file.d.ts.map +1 -1
  50. package/dist/tools/file.js +54 -5
  51. package/dist/tools/file.js.map +1 -1
  52. package/dist/tools/git.d.ts +16 -2
  53. package/dist/tools/git.d.ts.map +1 -1
  54. package/dist/tools/git.js +61 -18
  55. package/dist/tools/git.js.map +1 -1
  56. package/dist/tools/nwt.d.ts.map +1 -1
  57. package/dist/tools/nwt.js +8 -1
  58. package/dist/tools/nwt.js.map +1 -1
  59. package/dist/tools/search.d.ts +8 -1
  60. package/dist/tools/search.d.ts.map +1 -1
  61. package/dist/tools/search.js +105 -34
  62. package/dist/tools/search.js.map +1 -1
  63. package/dist/tools/shell.d.ts +0 -4
  64. package/dist/tools/shell.d.ts.map +1 -1
  65. package/dist/tools/shell.js +11 -26
  66. package/dist/tools/shell.js.map +1 -1
  67. package/dist/tools/types.d.ts +7 -0
  68. package/dist/tools/types.d.ts.map +1 -1
  69. package/dist/tools/types.js.map +1 -1
  70. package/dist/tui/app.d.ts.map +1 -1
  71. package/dist/tui/app.js +90 -18
  72. package/dist/tui/app.js.map +1 -1
  73. package/dist/tui/components/ChatMessage.d.ts.map +1 -1
  74. package/dist/tui/components/ChatMessage.js +9 -2
  75. package/dist/tui/components/ChatMessage.js.map +1 -1
  76. package/dist/tui/components/ConfirmPrompt.d.ts +21 -0
  77. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  78. package/dist/tui/components/ConfirmPrompt.js +45 -0
  79. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  80. package/dist/tui/components/Header.d.ts +10 -5
  81. package/dist/tui/components/Header.d.ts.map +1 -1
  82. package/dist/tui/components/Header.js +13 -5
  83. package/dist/tui/components/Header.js.map +1 -1
  84. package/dist/tui/components/InitWizard.d.ts.map +1 -1
  85. package/dist/tui/components/InitWizard.js +8 -13
  86. package/dist/tui/components/InitWizard.js.map +1 -1
  87. package/dist/tui/components/StatusBar.d.ts +7 -0
  88. package/dist/tui/components/StatusBar.d.ts.map +1 -1
  89. package/dist/tui/components/StatusBar.js +8 -3
  90. package/dist/tui/components/StatusBar.js.map +1 -1
  91. package/dist/tui/components/Thinking.d.ts +5 -0
  92. package/dist/tui/components/Thinking.d.ts.map +1 -1
  93. package/dist/tui/components/Thinking.js +24 -2
  94. package/dist/tui/components/Thinking.js.map +1 -1
  95. package/dist/tui/components/ToolCall.d.ts +6 -1
  96. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  97. package/dist/tui/components/ToolCall.js +50 -22
  98. package/dist/tui/components/ToolCall.js.map +1 -1
  99. package/dist/tui/components/UserInput.d.ts +5 -0
  100. package/dist/tui/components/UserInput.d.ts.map +1 -1
  101. package/dist/tui/components/UserInput.js +9 -13
  102. package/dist/tui/components/UserInput.js.map +1 -1
  103. package/dist/tui/hooks/useChat.d.ts +1 -0
  104. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  105. package/dist/tui/hooks/useChat.js +36 -16
  106. package/dist/tui/hooks/useChat.js.map +1 -1
  107. package/dist/tui/hooks/useCommands.d.ts +7 -1
  108. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  109. package/dist/tui/hooks/useCommands.js +83 -49
  110. package/dist/tui/hooks/useCommands.js.map +1 -1
  111. package/dist/tui/theme.d.ts +34 -0
  112. package/dist/tui/theme.d.ts.map +1 -0
  113. package/dist/tui/theme.js +34 -0
  114. package/dist/tui/theme.js.map +1 -0
  115. package/dist/tui/welcome.d.ts.map +1 -1
  116. package/dist/tui/welcome.js +9 -7
  117. package/dist/tui/welcome.js.map +1 -1
  118. package/dist/types.d.ts +16 -1
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/utils/diff.d.ts +5 -0
  121. package/dist/utils/diff.d.ts.map +1 -1
  122. package/dist/utils/diff.js +15 -0
  123. package/dist/utils/diff.js.map +1 -1
  124. package/dist/version.d.ts +12 -0
  125. package/dist/version.d.ts.map +1 -0
  126. package/dist/version.js +29 -0
  127. package/dist/version.js.map +1 -0
  128. package/package.json +19 -7
  129. package/CHANGELOG.md +0 -192
  130. package/DEVELOPMENT.md +0 -286
  131. package/ROADMAP.md +0 -91
  132. package/docs/API_KEY_GUIDE.md +0 -236
  133. package/docs/FAQ.md +0 -182
  134. package/install.bat +0 -63
  135. package/install.ps1 +0 -238
  136. package/install.sh +0 -113
  137. package/src/app/index.ts +0 -197
  138. package/src/cache/fingerprint.ts +0 -101
  139. package/src/cache/index.ts +0 -21
  140. package/src/cache/smartModel.ts +0 -145
  141. package/src/cache/stats.ts +0 -199
  142. package/src/cache/volatile.ts +0 -47
  143. package/src/cmd/index.tsx +0 -288
  144. package/src/config/index.ts +0 -158
  145. package/src/config/providers.ts +0 -234
  146. package/src/config/types.ts +0 -71
  147. package/src/hooks/index.ts +0 -111
  148. package/src/llm/anthropic.ts +0 -413
  149. package/src/llm/gemini.ts +0 -169
  150. package/src/llm/index.ts +0 -302
  151. package/src/llm/openai.ts +0 -243
  152. package/src/llm/provider.ts +0 -71
  153. package/src/mcp/client.ts +0 -330
  154. package/src/prompts/index.ts +0 -260
  155. package/src/session/compactor.ts +0 -103
  156. package/src/session/index.ts +0 -181
  157. package/src/session/message.ts +0 -42
  158. package/src/skills/brainstorming.ts +0 -43
  159. package/src/skills/code-review.ts +0 -45
  160. package/src/skills/executing-plans.ts +0 -27
  161. package/src/skills/frontend-design.ts +0 -35
  162. package/src/skills/git-workflow.ts +0 -36
  163. package/src/skills/improve-architecture.ts +0 -38
  164. package/src/skills/index.ts +0 -136
  165. package/src/skills/neuroweave.ts +0 -47
  166. package/src/skills/playwright.ts +0 -72
  167. package/src/skills/prototype.ts +0 -30
  168. package/src/skills/subagent.ts +0 -28
  169. package/src/skills/supabase.ts +0 -44
  170. package/src/skills/systematic-debugging.ts +0 -44
  171. package/src/skills/tdd.ts +0 -39
  172. package/src/skills/triage.ts +0 -35
  173. package/src/skills/verification.ts +0 -31
  174. package/src/skills/writing-plans.ts +0 -42
  175. package/src/tools/file.ts +0 -143
  176. package/src/tools/git.ts +0 -132
  177. package/src/tools/index.ts +0 -111
  178. package/src/tools/nwt.ts +0 -598
  179. package/src/tools/search.ts +0 -263
  180. package/src/tools/shell.ts +0 -136
  181. package/src/tools/types.ts +0 -122
  182. package/src/tui/app.tsx +0 -159
  183. package/src/tui/components/ChatList.tsx +0 -41
  184. package/src/tui/components/ChatMessage.tsx +0 -54
  185. package/src/tui/components/Header.tsx +0 -66
  186. package/src/tui/components/InitWizard.tsx +0 -251
  187. package/src/tui/components/Markdown.tsx +0 -35
  188. package/src/tui/components/ModelSelector.tsx +0 -107
  189. package/src/tui/components/StatusBar.tsx +0 -30
  190. package/src/tui/components/Thinking.tsx +0 -17
  191. package/src/tui/components/ToolCall.tsx +0 -102
  192. package/src/tui/components/UserInput.tsx +0 -151
  193. package/src/tui/hooks/useChat.ts +0 -287
  194. package/src/tui/hooks/useCommands.ts +0 -273
  195. package/src/tui/index.ts +0 -6
  196. package/src/tui/welcome.ts +0 -177
  197. package/src/types.ts +0 -104
  198. package/src/utils/diff.ts +0 -71
  199. package/src/utils/project.ts +0 -99
  200. package/src/utils/stableStringify.ts +0 -47
  201. package/src/utils/thinking.ts +0 -119
  202. package/tests/cache/fingerprint.test.ts +0 -75
  203. package/tests/cache/providerCatalog.test.ts +0 -31
  204. package/tests/cache/smartModel.test.ts +0 -112
  205. package/tests/cache/stableStringify.test.ts +0 -43
  206. package/tests/cache/stats.test.ts +0 -146
  207. package/tests/cache/volatile.test.ts +0 -75
  208. package/tests/smoke-pollution.mjs +0 -78
  209. package/tests/smoke-thinking.mjs +0 -110
  210. package/tests/smoke-tool-stream.mjs +0 -69
  211. package/tests/smoke-tool.mjs +0 -117
  212. package/tsconfig.json +0 -21
package/dist/app/index.js CHANGED
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * App - Core application singleton
3
- * Simplified: directly uses LLMService (which has built-in agent loop)
4
3
  *
5
- * v3.0.0+: streamResponse yields structured StreamChunk
6
- * - runPrompt streams { type: 'text' } chunks to stdout and captures the
7
- * final usage for cache stats recording.
4
+ * v3.0.5 wiring release:
5
+ * - MCP: servers from ~/.thatgfsj/mcp.json are connected at startup
6
+ * (per-server failures are non-fatal) and their tools join the
7
+ * registry before the system prompt is built. Child processes are
8
+ * disconnected on exit.
9
+ * - Permission pipeline: tool confirmations now actually reach the UI —
10
+ * App owns the mode ('ask' default, 'accept' via --yolo) and a
11
+ * pluggable confirmHandler (TUI prompt / headless auto-deny). With no
12
+ * handler and 'ask' mode, write/execute tool calls are DENIED instead
13
+ * of silently executed.
14
+ * - reloadModel(): /model hot-swaps provider+model without restarting;
15
+ * tools and the system prompt are rebuilt, TTL resolution resets.
16
+ * - applyTtl(): /ttl takes effect immediately (config + provider marker).
17
+ * - streamResponse() accepts an AbortSignal that reaches the provider
18
+ * fetch calls, so cancelling a round stops token generation.
8
19
  */
20
+ import { existsSync, readFileSync } from 'fs';
21
+ import { join } from 'path';
22
+ import { homedir } from 'os';
9
23
  import { ConfigManager } from '../config/index.js';
10
24
  import { LLMService } from '../llm/index.js';
11
25
  import { SessionManager } from '../session/index.js';
@@ -15,6 +29,22 @@ import { SystemPromptBuilder } from '../prompts/index.js';
15
29
  import { SkillRegistry } from '../skills/index.js';
16
30
  import { CacheStatsStore } from '../cache/stats.js';
17
31
  import { compressThinking } from '../utils/thinking.js';
32
+ import { MCPServerManager } from '../mcp/client.js';
33
+ /** Read ~/.thatgfsj/mcp.json. Missing or corrupted file = no servers. */
34
+ export function loadMcpConfig() {
35
+ const p = join(homedir(), '.thatgfsj', 'mcp.json');
36
+ if (!existsSync(p))
37
+ return {};
38
+ try {
39
+ const data = JSON.parse(readFileSync(p, 'utf-8'));
40
+ if (data && typeof data === 'object')
41
+ return data;
42
+ return {};
43
+ }
44
+ catch {
45
+ return {};
46
+ }
47
+ }
18
48
  export class App {
19
49
  config;
20
50
  llm;
@@ -29,10 +59,13 @@ export class App {
29
59
  * and the /cache command.
30
60
  */
31
61
  cacheStats;
62
+ /** v3.0.5: connected MCP servers (empty manager when none configured). */
63
+ mcp;
64
+ /** v3.0.5: startup results per configured MCP server, for /mcp output. */
65
+ mcpStartupResults = [];
32
66
  /**
33
- * v2.2.5 (product 0.4.2): toggle block compression. Default
34
- * true. Toggled by `--show-thinking` on the CLI or `/thinking on|off`
35
- * in the REPL.
67
+ * v2.2.5: toggle thinking block compression. Default true. Toggled by
68
+ * `--show-thinking` on the CLI or `/thinking on|off` in the REPL.
36
69
  */
37
70
  showThinking = false;
38
71
  /**
@@ -41,7 +74,18 @@ export class App {
41
74
  * is '5m' or '1h' and stays sticky.
42
75
  */
43
76
  resolvedTtl = null;
44
- constructor(config, llm, session, tools, hooks, prompts, skills, cacheStats) {
77
+ /**
78
+ * v3.0.5: permission mode. 'ask' requires confirmation for write/execute
79
+ * tool actions; 'accept' (--yolo) allows everything.
80
+ */
81
+ permissionMode = 'ask';
82
+ /**
83
+ * v3.0.5: pluggable confirmation UI. The TUI installs an Ink prompt;
84
+ * headless mode leaves it unset → write/execute actions are denied with
85
+ * a note (add --yolo to allow them).
86
+ */
87
+ confirmHandler;
88
+ constructor(config, llm, session, tools, hooks, prompts, skills, cacheStats, mcp) {
45
89
  this.config = config;
46
90
  this.llm = llm;
47
91
  this.session = session;
@@ -50,6 +94,7 @@ export class App {
50
94
  this.prompts = prompts;
51
95
  this.skills = skills;
52
96
  this.cacheStats = cacheStats;
97
+ this.mcp = mcp;
53
98
  }
54
99
  static async create() {
55
100
  const config = await ConfigManager.load();
@@ -57,32 +102,184 @@ export class App {
57
102
  const llm = LLMService.fromConfig(aiConfig);
58
103
  const cacheStats = new CacheStatsStore();
59
104
  const session = new SessionManager(config.get().contextLength || 50);
60
- // v3.0.0: do not mutate messages when context grows instead, surface
61
- // a "consider /new" toast via onSuggestNewSession. The TUI wires this
62
- // up in app.tsx; in CLI single-prompt mode it's a no-op (one-shot).
63
- session.onSuggestNewSession = (info) => {
64
- console.warn(`\n ⚠️ 上下文较长(${info.currentLength}/${info.max})。` +
65
- `建议调 /new 开新会话(NWT 已自动归档历史)\n`);
105
+ // Default toast goes to stderr so it can not corrupt the Ink frame;
106
+ // the TUI replaces this with a proper in-app message.
107
+ session.onAutoCompact = (info) => {
108
+ process.stderr.write(`\n ⚠️ 上下文较长(${info.before} 条),已自动压缩到 ${info.after} 条。可随时 /new 开新会话(NWT 已自动归档历史)\n`);
66
109
  };
67
110
  const tools = new ToolRegistry();
68
111
  const hooks = new HookManager();
69
112
  const skills = new SkillRegistry();
70
- // Register tools with LLM service
113
+ let mcpResults = [];
114
+ // v3.0.5: connect MCP servers BEFORE the prompt is built so their tools
115
+ // are visible to the model from turn one. A failing server is logged,
116
+ // never fatal.
117
+ const mcp = new MCPServerManager();
118
+ const mcpConfig = loadMcpConfig();
119
+ const mcpServerDefs = mcpConfig.mcpServers || mcpConfig.servers || {};
120
+ if (Object.keys(mcpServerDefs).length > 0) {
121
+ appLog('正在连接 MCP 服务器…');
122
+ mcpResults = await mcp.connectFromConfig(mcpConfig);
123
+ for (const r of mcpResults) {
124
+ if (r.ok) {
125
+ appLog(`✓ MCP ${r.name}(${r.tools} 个工具)`);
126
+ }
127
+ else {
128
+ appLog(`✗ MCP ${r.name}: ${r.error}`);
129
+ }
130
+ }
131
+ for (const tool of mcp.getAllTools()) {
132
+ tools.register(tool);
133
+ }
134
+ }
135
+ // Kill MCP child processes on exit (also covers Ctrl+C via 'exit').
136
+ process.on('exit', () => {
137
+ try {
138
+ mcp.disconnectAll();
139
+ }
140
+ catch { /* best-effort */ }
141
+ });
142
+ // Register tools with LLM service (after MCP tools joined the registry)
71
143
  llm.registerTools(tools.list());
72
144
  // Auto-init NWT timeline
73
145
  const nwtTool = tools.get('nwt');
74
146
  if (nwtTool) {
75
147
  await nwtTool.execute({ action: 'init' });
76
148
  }
77
- // Build system prompt with active skills
149
+ // Build system prompt with the FULL tool list (including MCP tools)
78
150
  const prompts = new SystemPromptBuilder({
79
151
  cwd: process.cwd(),
80
152
  tools: tools.list(),
81
153
  permissionMode: 'ask',
82
154
  skillsPrompt: skills.getActivePrompts(),
83
155
  });
156
+ const app = new App(config, llm, session, tools, hooks, prompts, skills, cacheStats, mcp);
157
+ app.mcpStartupResults = mcpResults;
158
+ // v3.0.5: route tool confirmations through App (mode + handler aware)
159
+ app.applyToolContext();
84
160
  session.addMessage('system', prompts.build());
85
- return new App(config, llm, session, tools, hooks, prompts, skills, cacheStats);
161
+ return app;
162
+ }
163
+ /**
164
+ * v3.0.5: keep registry and LLMService tool contexts in sync. All asks
165
+ * funnel through requestConfirmation so the permission mode and the
166
+ * active UI handler apply uniformly.
167
+ */
168
+ applyToolContext() {
169
+ const ctx = {
170
+ workingDirectory: process.cwd(),
171
+ confirmAction: (msg) => this.requestConfirmation({ message: msg }),
172
+ confirmEdit: (info) => this.requestConfirmation({ message: info.message }),
173
+ };
174
+ this.tools.setContext(ctx);
175
+ this.llm.setToolContext(ctx);
176
+ }
177
+ /**
178
+ * v3.0.5: central permission decision.
179
+ * - 'accept' mode (--yolo): always allowed.
180
+ * - handler installed (TUI): ask it, 60s timeout denies.
181
+ * - no handler (headless): deny with a hint on stderr.
182
+ */
183
+ async requestConfirmation(req) {
184
+ if (this.permissionMode === 'accept')
185
+ return true;
186
+ if (!this.confirmHandler) {
187
+ process.stderr.write(`\n ⛔ 已拒绝:${firstLine(req.message)}\n (headless 模式默认拒绝写入/执行操作;如需放行请加 --yolo)\n`);
188
+ return false;
189
+ }
190
+ try {
191
+ const answer = await Promise.race([
192
+ this.confirmHandler(req),
193
+ new Promise((resolve) => {
194
+ const t = setTimeout(() => resolve(false), 60000);
195
+ t.unref?.();
196
+ }),
197
+ ]);
198
+ return !!answer;
199
+ }
200
+ catch {
201
+ return false;
202
+ }
203
+ }
204
+ /**
205
+ * v3.0.5: single entry for toggling auto-accept. Also refreshes the
206
+ * system prompt's permission-mode section so the model knows tool calls
207
+ * no longer need user approval.
208
+ */
209
+ setYolo(on) {
210
+ this.permissionMode = on ? 'accept' : 'ask';
211
+ this.rebuildSystemPrompt();
212
+ }
213
+ /**
214
+ * v3.0.5: hot-swap the model (and provider, if /provider changed it on
215
+ * disk). Rebuilds LLMService, re-registers every current tool, resets TTL
216
+ * resolution and refreshes the system prompt. /model previously only
217
+ * wrote the config file and required a restart.
218
+ */
219
+ async reloadModel() {
220
+ const aiConfig = this.config.getAIConfig();
221
+ const llm = LLMService.fromConfig(aiConfig);
222
+ llm.registerTools(this.tools.list());
223
+ this.llm = llm;
224
+ this.resolvedTtl = null;
225
+ this.applyToolContext();
226
+ this.rebuildSystemPrompt();
227
+ }
228
+ /**
229
+ * v3.0.5: apply a TTL pin immediately (config write-through + provider
230
+ * marker). /ttl previously only updated a display value; the wire format
231
+ * kept the old TTL until restart.
232
+ */
233
+ async applyTtl(ttl) {
234
+ const current = this.config.get().cache ?? { enabled: true, strategy: 'auto' };
235
+ await this.config.save({ cache: { ...current, ttl } });
236
+ this.llm.setTtl(ttl);
237
+ this.resolvedTtl = ttl;
238
+ }
239
+ /** v3.0.5: rebuild the system prompt (tools / permission mode changed). */
240
+ rebuildSystemPrompt() {
241
+ this.prompts.setTools(this.tools.list());
242
+ this.prompts.setPermissionMode(this.permissionMode === 'accept' ? 'accept' : 'ask');
243
+ const built = this.prompts.build();
244
+ this.session.replaceSystemMessage(built);
245
+ }
246
+ /**
247
+ * v3.0.5: TUI hook-up — show MCP startup results as an in-chat message.
248
+ * Returns a short multi-line report (empty when no servers configured).
249
+ */
250
+ mcpStatusText() {
251
+ const configured = Object.keys(loadMcpConfig().mcpServers || loadMcpConfig().servers || {}).length;
252
+ if (configured === 0) {
253
+ return [
254
+ 'MCP 未配置。配置文件: ~/.thatgfsj/mcp.json',
255
+ '',
256
+ ' {',
257
+ ' "mcpServers": {',
258
+ ' "名称": { "command": "npx", "args": ["-y", "包名"] }',
259
+ ' }',
260
+ ' }',
261
+ '',
262
+ '(兼容 "servers" 键名;修改后重启生效)',
263
+ ].join('\n');
264
+ }
265
+ const lines = ['MCP 服务器:'];
266
+ for (const r of this.mcpStartupResults) {
267
+ lines.push(r.ok
268
+ ? ` ✓ ${r.name} — ${r.tools} 个工具`
269
+ : ` ✗ ${r.name} — ${r.error}`);
270
+ }
271
+ const status = this.mcp.getStatus();
272
+ if (status.length > 0) {
273
+ lines.push('');
274
+ lines.push('当前状态:');
275
+ for (const s of status) {
276
+ const toolList = s.tools.length > 0
277
+ ? s.tools.slice(0, 8).join(', ') + (s.tools.length > 8 ? ` 等 ${s.tools.length} 个` : '')
278
+ : '(无工具)';
279
+ lines.push(` ${s.connected ? '●' : '○'} ${s.name}: ${toolList}`);
280
+ }
281
+ }
282
+ return lines.join('\n');
86
283
  }
87
284
  /**
88
285
  * Stream a response for the current session messages.
@@ -91,15 +288,12 @@ export class App {
91
288
  * Yields structured StreamChunks. Returns the final ChatResponse (with usage
92
289
  * if the provider reported it) so the caller can record cache stats.
93
290
  *
94
- * Implementation note: we drain the inner stream manually so the final
95
- * ChatResponse returned by LLMService.chatStream is propagated as this
96
- * generator's return value. Using yield* doesn't carry the return value
97
- * through TS's AsyncGenerator<T, R> type inference in this version of
98
- * TypeScript, so we wrap with an inner for-await and explicit return.
291
+ * v3.0.5: accepts { signal } propagated into provider fetch calls so
292
+ * cancellation actually aborts the HTTP request.
99
293
  */
100
- async *streamResponse(messages) {
294
+ async *streamResponse(messages, opts) {
101
295
  const msgs = messages || this.session.getMessages();
102
- const inner = this.llm.chatStream(msgs);
296
+ const inner = this.llm.chatStream(msgs, { signal: opts?.signal });
103
297
  const debugUsage = !!process.env.GFCODE_DEBUG_USAGE;
104
298
  // v3.0.3: read TTL the LLMService resolved this round (sticky per session).
105
299
  this.resolvedTtl = this.llm.getResolvedTTL();
@@ -113,32 +307,22 @@ export class App {
113
307
  }
114
308
  catch { /* best-effort */ }
115
309
  if (debugUsage) {
116
- // v3.0.0 DEBUG: dump raw usage fields to stderr so the user can
117
- // confirm whether the upstream provider/relay forwards cache stats.
118
- // Enable with: GFCODE_DEBUG_USAGE=1 gfcode ...
119
310
  process.stderr.write('[debug_usage] ' + JSON.stringify(next.value.usage) + '\n');
120
311
  }
121
312
  }
122
313
  yield next.value;
123
314
  next = await inner.next();
124
315
  }
125
- // The generator's return value (ChatResponse with usage) is propagated
126
- // to callers via `for await ... await streamResponse.next()` semantics.
127
316
  return next.value;
128
317
  }
129
318
  /**
130
319
  * Run a single prompt (non-interactive mode)
131
320
  *
132
- * v2.2.4 (port from v2.1.0): persistence of the assistant message
133
- * uses addMessageSafe, which drops the message if it contains
134
- * pollution markers like "[已中断]".
321
+ * v2.2.4: persistence of the assistant message uses addMessageSafe, which
322
+ * drops the message if it contains pollution markers like "[已中断]".
135
323
  *
136
- * v2.2.5 (product 0.4.2): persistence also strips blocks
137
- * (and similar reasoning delimiters) when showThinking is false,
138
- * so the conversation log stays compact.
139
- *
140
- * v3.0.0: yields structured StreamChunks; final usage is captured
141
- * into onUsage callback for cache stats persistence.
324
+ * v3.0.0: yields structured StreamChunks; final usage is captured.
325
+ * v3.0.5: persists the session to disk when the round completes.
142
326
  */
143
327
  async runPrompt(prompt, onUsage) {
144
328
  this.session.addMessage('user', prompt);
@@ -162,10 +346,9 @@ export class App {
162
346
  throw err;
163
347
  }
164
348
  console.log();
165
- // v2.2.5: compress blocks before persisting.
166
349
  const toPersist = compressThinking(fullResponse, this.showThinking);
167
350
  this.session.addMessageSafe('assistant', toPersist);
168
- // v3.0.0: forward usage to caller (CLI single-shot mode records stats too)
351
+ this.session.persist();
169
352
  if (finalUsage && onUsage) {
170
353
  try {
171
354
  onUsage(finalUsage);
@@ -175,4 +358,13 @@ export class App {
175
358
  return fullResponse;
176
359
  }
177
360
  }
361
+ // ---- helpers ------------------------------------------------------------
362
+ function appLog(msg) {
363
+ // Startup messages go to stderr so they never corrupt Ink / --json stdout.
364
+ process.stderr.write(` ${msg}\n`);
365
+ }
366
+ function firstLine(s) {
367
+ const line = s.split('\n').find(l => l.trim()) || s;
368
+ return line.length > 120 ? line.slice(0, 120) + '…' : line;
369
+ }
178
370
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"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"}
@@ -10,9 +10,10 @@
10
10
  * Tool-heavy turns → main model
11
11
  *
12
12
  * 2. Cache TTL
13
- * Short conversations → 5m (cheap write, expires fast)
14
- * Long conversations (>15 turns) 1h (cache hits pay back the
15
- * expensive write each round)
13
+ * v3.0.4: default to 1h (long-task TTL). We cannot predict task
14
+ * length at round 0, so we choose the TTL that cannot expire
15
+ * mid-task. 5m is opt-in only (user pins it via /ttl 5m or the
16
+ * init wizard).
16
17
  *
17
18
  * Why TTL is decided per-ROUND but CHANGES per-CONVERSATION:
18
19
  * The Anthropic cache_control marker is part of the request body.
@@ -24,14 +25,11 @@
24
25
  * round) and never change it. Subsequent rounds get the same TTL,
25
26
  * which keeps the cache prefix stable and lets Anthropic match it.
26
27
  *
27
- * The "smart" part is that we look at the FULL conversation so far,
28
- * not just the last input. A 16-turn conversation that started with a
29
- * short prompt but grew into a long refactor still gets 1h.
30
- *
31
- * Trade-off: the first round is evaluated with 0 history, so it
32
- * defaults to 5m. If the conversation then grows long, future rounds
33
- * still use 5m for the system-token cache (because we already wrote
34
- * it with 5m). The tool definition cache gets the same TTL.
28
+ * Trade-off: 1h write costs 2x input price (vs 1.25x for 5m), but
29
+ * reads are the same 0.1x. A long task hitting cache 2-3 times pays
30
+ * back the extra write cost; a short task pays it once and moves on.
31
+ * That is strictly better than guessing 5m and having the cache expire
32
+ * mid-task.
35
33
  */
36
34
  import type { ChatMessage } from '../types.js';
37
35
  export interface SmartModelDecision {
@@ -67,19 +65,28 @@ export declare function totalConversationChars(messages: ChatMessage[]): number;
67
65
  /**
68
66
  * Decide the cache TTL for the current round.
69
67
  *
70
- * Inputs:
71
- * - messages: full conversation so far (system + user + assistant + tool)
72
- * - previousTTL: TTL chosen in a prior round (null on round 0)
68
+ * v3.0.4: The previous implementation tried to *predict* the task length
69
+ * from the conversation so far (short sessions 5m, long 1h). That
70
+ * was wrong: at the moment the decision is made (first round), the task
71
+ * hasn't started yet — there is no way to know whether it will be a
72
+ * 30-second question or a 3-hour refactor. Predicting 5m for a task
73
+ * that turns out to be long means the cache expires mid-task (5-minute
74
+ * TTL counts from the last HIT, and agent work often has >5m gaps while
75
+ * tools run / user reads output), so every round after the gap re-writes
76
+ * the whole prefix at full price.
73
77
  *
74
- * Decision matrix:
75
- * - round 0 + any input length 5m (default; user can override via init)
76
- * - round 1-14 + total chars < 50k → 5m (short sessions, 5m is enough)
77
- * - round 15+ OR total chars > 50k 1h (long sessions, 1h amortizes the write)
78
+ * Correct rule: DEFAULT TO THE LONG TTL (1h) and don't guess.
79
+ * - 1h costs 2x input price to WRITE (vs 1.25x for 5m), but reads are
80
+ * the same 0.1x for both.
81
+ * - A long task that hits cache even 2-3 times pays back the extra
82
+ * write cost. A short task only pays the extra write once.
83
+ * - Users who are SURE the session is short can pin 5m via
84
+ * /ttl 5m or the init wizard — the default stays 1h.
78
85
  *
79
- * Stability rule: once a TTL is chosen, it is reused for every
80
- * subsequent round in the same session. Changing TTL between rounds
81
- * would invalidate the Anthropic cache prefix and cost more than it
82
- * saves.
86
+ * Stability rule (unchanged): once a TTL is chosen, it is reused for
87
+ * every subsequent round in the same session. Changing TTL between
88
+ * rounds would invalidate the Anthropic cache prefix and cost more
89
+ * than it saves.
83
90
  *
84
91
  * The `isFirstDecision` flag tells the caller whether to apply the
85
92
  * decision (first round) or reuse the previous one (later rounds).
@@ -1 +1 @@
1
- {"version":3,"file":"smartModel.d.ts","sourceRoot":"","sources":["../../src/cache/smartModel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,kBAAkB,CAUlG;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAYtE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,WAAW,EAAE,EACvB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAC9B,gBAAgB,CA2BlB"}
1
+ {"version":3,"file":"smartModel.d.ts","sourceRoot":"","sources":["../../src/cache/smartModel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,kBAAkB,CAUlG;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CAYtE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,WAAW,EAAE,EACvB,WAAW,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAC9B,gBAAgB,CAQlB"}
@@ -10,9 +10,10 @@
10
10
  * Tool-heavy turns → main model
11
11
  *
12
12
  * 2. Cache TTL
13
- * Short conversations → 5m (cheap write, expires fast)
14
- * Long conversations (>15 turns) 1h (cache hits pay back the
15
- * expensive write each round)
13
+ * v3.0.4: default to 1h (long-task TTL). We cannot predict task
14
+ * length at round 0, so we choose the TTL that cannot expire
15
+ * mid-task. 5m is opt-in only (user pins it via /ttl 5m or the
16
+ * init wizard).
16
17
  *
17
18
  * Why TTL is decided per-ROUND but CHANGES per-CONVERSATION:
18
19
  * The Anthropic cache_control marker is part of the request body.
@@ -24,14 +25,11 @@
24
25
  * round) and never change it. Subsequent rounds get the same TTL,
25
26
  * which keeps the cache prefix stable and lets Anthropic match it.
26
27
  *
27
- * The "smart" part is that we look at the FULL conversation so far,
28
- * not just the last input. A 16-turn conversation that started with a
29
- * short prompt but grew into a long refactor still gets 1h.
30
- *
31
- * Trade-off: the first round is evaluated with 0 history, so it
32
- * defaults to 5m. If the conversation then grows long, future rounds
33
- * still use 5m for the system-token cache (because we already wrote
34
- * it with 5m). The tool definition cache gets the same TTL.
28
+ * Trade-off: 1h write costs 2x input price (vs 1.25x for 5m), but
29
+ * reads are the same 0.1x. A long task hitting cache 2-3 times pays
30
+ * back the extra write cost; a short task pays it once and moves on.
31
+ * That is strictly better than guessing 5m and having the cache expire
32
+ * mid-task.
35
33
  */
36
34
  /**
37
35
  * Heuristic: downgrade when
@@ -78,19 +76,28 @@ export function totalConversationChars(messages) {
78
76
  /**
79
77
  * Decide the cache TTL for the current round.
80
78
  *
81
- * Inputs:
82
- * - messages: full conversation so far (system + user + assistant + tool)
83
- * - previousTTL: TTL chosen in a prior round (null on round 0)
79
+ * v3.0.4: The previous implementation tried to *predict* the task length
80
+ * from the conversation so far (short sessions 5m, long 1h). That
81
+ * was wrong: at the moment the decision is made (first round), the task
82
+ * hasn't started yet — there is no way to know whether it will be a
83
+ * 30-second question or a 3-hour refactor. Predicting 5m for a task
84
+ * that turns out to be long means the cache expires mid-task (5-minute
85
+ * TTL counts from the last HIT, and agent work often has >5m gaps while
86
+ * tools run / user reads output), so every round after the gap re-writes
87
+ * the whole prefix at full price.
84
88
  *
85
- * Decision matrix:
86
- * - round 0 + any input length 5m (default; user can override via init)
87
- * - round 1-14 + total chars < 50k → 5m (short sessions, 5m is enough)
88
- * - round 15+ OR total chars > 50k 1h (long sessions, 1h amortizes the write)
89
+ * Correct rule: DEFAULT TO THE LONG TTL (1h) and don't guess.
90
+ * - 1h costs 2x input price to WRITE (vs 1.25x for 5m), but reads are
91
+ * the same 0.1x for both.
92
+ * - A long task that hits cache even 2-3 times pays back the extra
93
+ * write cost. A short task only pays the extra write once.
94
+ * - Users who are SURE the session is short can pin 5m via
95
+ * /ttl 5m or the init wizard — the default stays 1h.
89
96
  *
90
- * Stability rule: once a TTL is chosen, it is reused for every
91
- * subsequent round in the same session. Changing TTL between rounds
92
- * would invalidate the Anthropic cache prefix and cost more than it
93
- * saves.
97
+ * Stability rule (unchanged): once a TTL is chosen, it is reused for
98
+ * every subsequent round in the same session. Changing TTL between
99
+ * rounds would invalidate the Anthropic cache prefix and cost more
100
+ * than it saves.
94
101
  *
95
102
  * The `isFirstDecision` flag tells the caller whether to apply the
96
103
  * decision (first round) or reuse the previous one (later rounds).
@@ -99,23 +106,9 @@ export function decideTTL(messages, previousTTL) {
99
106
  if (previousTTL) {
100
107
  return { ttl: previousTTL, reason: 'reused-from-previous-round', isFirstDecision: false };
101
108
  }
102
- const turnCount = messages.filter(m => m.role === 'user' || m.role === 'assistant').length;
103
- const totalChars = totalConversationChars(messages);
104
- // Round 0: empty / first input. Default to 5m.
105
- if (turnCount === 0) {
106
- return { ttl: '5m', reason: 'first-round-default', isFirstDecision: true };
107
- }
108
- // Multi-turn but small conversation → 5m
109
- if (turnCount < 15 && totalChars < 50_000) {
110
- return { ttl: '5m', reason: `short-session-${turnCount}-turns`, isFirstDecision: true };
111
- }
112
- // Long conversations → 1h
113
- if (turnCount >= 15) {
114
- return { ttl: '1h', reason: `long-session-${turnCount}-turns`, isFirstDecision: true };
115
- }
116
- if (totalChars >= 50_000) {
117
- return { ttl: '1h', reason: `long-context-${totalChars}-chars`, isFirstDecision: true };
118
- }
119
- return { ttl: '5m', reason: 'fallback', isFirstDecision: true };
109
+ // Default: long-task TTL. We cannot know the task length upfront, so
110
+ // we pick the TTL that cannot expire mid-task. 5m is only used when
111
+ // the user explicitly pins it.
112
+ return { ttl: '1h', reason: 'default-long-task', isFirstDecision: true };
120
113
  }
121
114
  //# sourceMappingURL=smartModel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"smartModel.js","sourceRoot":"","sources":["../../src/cache/smartModel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"smartModel.js","sourceRoot":"","sources":["../../src/cache/smartModel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAkBH;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,QAAuB,EAAE,aAAqB;IAC5E,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACzD,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrF,IAAI,kBAAkB,EAAE,CAAC;QACvB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAuB;IAC5D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAClC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QAC5B,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YACpC,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC5B,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM;oBAAE,KAAK,IAAK,GAAW,CAAC,IAAI,CAAC,MAAM,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,SAAS,CACvB,QAAuB,EACvB,WAA+B;IAE/B,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,4BAA4B,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAC5F,CAAC;IACD,qEAAqE;IACrE,oEAAoE;IACpE,+BAA+B;IAC/B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AAC3E,CAAC"}