thatgfsj-code 3.0.4 → 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 (205) 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/cmd/index.d.ts +12 -20
  7. package/dist/cmd/index.d.ts.map +1 -1
  8. package/dist/cmd/index.js +192 -146
  9. package/dist/cmd/index.js.map +1 -1
  10. package/dist/config/index.d.ts +4 -0
  11. package/dist/config/index.d.ts.map +1 -1
  12. package/dist/config/index.js +5 -0
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/config/types.d.ts +5 -0
  15. package/dist/config/types.d.ts.map +1 -1
  16. package/dist/llm/anthropic.d.ts +1 -1
  17. package/dist/llm/anthropic.d.ts.map +1 -1
  18. package/dist/llm/anthropic.js +116 -84
  19. package/dist/llm/anthropic.js.map +1 -1
  20. package/dist/llm/gemini.d.ts +27 -3
  21. package/dist/llm/gemini.d.ts.map +1 -1
  22. package/dist/llm/gemini.js +196 -73
  23. package/dist/llm/gemini.js.map +1 -1
  24. package/dist/llm/index.d.ts +20 -1
  25. package/dist/llm/index.d.ts.map +1 -1
  26. package/dist/llm/index.js +66 -6
  27. package/dist/llm/index.js.map +1 -1
  28. package/dist/llm/openai.d.ts +5 -1
  29. package/dist/llm/openai.d.ts.map +1 -1
  30. package/dist/llm/openai.js +96 -56
  31. package/dist/llm/openai.js.map +1 -1
  32. package/dist/mcp/client.d.ts +84 -34
  33. package/dist/mcp/client.d.ts.map +1 -1
  34. package/dist/mcp/client.js +291 -126
  35. package/dist/mcp/client.js.map +1 -1
  36. package/dist/session/compactor.d.ts +27 -7
  37. package/dist/session/compactor.d.ts.map +1 -1
  38. package/dist/session/compactor.js +78 -23
  39. package/dist/session/compactor.js.map +1 -1
  40. package/dist/session/index.d.ts +96 -33
  41. package/dist/session/index.d.ts.map +1 -1
  42. package/dist/session/index.js +278 -60
  43. package/dist/session/index.js.map +1 -1
  44. package/dist/tools/file.d.ts +10 -2
  45. package/dist/tools/file.d.ts.map +1 -1
  46. package/dist/tools/file.js +54 -5
  47. package/dist/tools/file.js.map +1 -1
  48. package/dist/tools/git.d.ts +16 -2
  49. package/dist/tools/git.d.ts.map +1 -1
  50. package/dist/tools/git.js +61 -18
  51. package/dist/tools/git.js.map +1 -1
  52. package/dist/tools/nwt.d.ts.map +1 -1
  53. package/dist/tools/nwt.js +8 -1
  54. package/dist/tools/nwt.js.map +1 -1
  55. package/dist/tools/search.d.ts +8 -1
  56. package/dist/tools/search.d.ts.map +1 -1
  57. package/dist/tools/search.js +105 -34
  58. package/dist/tools/search.js.map +1 -1
  59. package/dist/tools/shell.d.ts +0 -4
  60. package/dist/tools/shell.d.ts.map +1 -1
  61. package/dist/tools/shell.js +11 -26
  62. package/dist/tools/shell.js.map +1 -1
  63. package/dist/tools/types.d.ts +7 -0
  64. package/dist/tools/types.d.ts.map +1 -1
  65. package/dist/tools/types.js.map +1 -1
  66. package/dist/tui/app.d.ts.map +1 -1
  67. package/dist/tui/app.js +90 -18
  68. package/dist/tui/app.js.map +1 -1
  69. package/dist/tui/components/ChatMessage.d.ts.map +1 -1
  70. package/dist/tui/components/ChatMessage.js +9 -2
  71. package/dist/tui/components/ChatMessage.js.map +1 -1
  72. package/dist/tui/components/ConfirmPrompt.d.ts +21 -0
  73. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  74. package/dist/tui/components/ConfirmPrompt.js +45 -0
  75. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  76. package/dist/tui/components/Header.d.ts +10 -5
  77. package/dist/tui/components/Header.d.ts.map +1 -1
  78. package/dist/tui/components/Header.js +13 -5
  79. package/dist/tui/components/Header.js.map +1 -1
  80. package/dist/tui/components/StatusBar.d.ts +7 -0
  81. package/dist/tui/components/StatusBar.d.ts.map +1 -1
  82. package/dist/tui/components/StatusBar.js +8 -3
  83. package/dist/tui/components/StatusBar.js.map +1 -1
  84. package/dist/tui/components/Thinking.d.ts +5 -0
  85. package/dist/tui/components/Thinking.d.ts.map +1 -1
  86. package/dist/tui/components/Thinking.js +24 -2
  87. package/dist/tui/components/Thinking.js.map +1 -1
  88. package/dist/tui/components/ToolCall.d.ts +6 -1
  89. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  90. package/dist/tui/components/ToolCall.js +50 -22
  91. package/dist/tui/components/ToolCall.js.map +1 -1
  92. package/dist/tui/components/UserInput.d.ts +5 -0
  93. package/dist/tui/components/UserInput.d.ts.map +1 -1
  94. package/dist/tui/components/UserInput.js +9 -13
  95. package/dist/tui/components/UserInput.js.map +1 -1
  96. package/dist/tui/hooks/useChat.d.ts +1 -0
  97. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  98. package/dist/tui/hooks/useChat.js +36 -16
  99. package/dist/tui/hooks/useChat.js.map +1 -1
  100. package/dist/tui/hooks/useCommands.d.ts +7 -1
  101. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  102. package/dist/tui/hooks/useCommands.js +77 -41
  103. package/dist/tui/hooks/useCommands.js.map +1 -1
  104. package/dist/tui/theme.d.ts +34 -0
  105. package/dist/tui/theme.d.ts.map +1 -0
  106. package/dist/tui/theme.js +34 -0
  107. package/dist/tui/theme.js.map +1 -0
  108. package/dist/tui/welcome.d.ts.map +1 -1
  109. package/dist/tui/welcome.js +9 -7
  110. package/dist/tui/welcome.js.map +1 -1
  111. package/dist/types.d.ts +16 -1
  112. package/dist/types.d.ts.map +1 -1
  113. package/dist/utils/diff.d.ts +5 -0
  114. package/dist/utils/diff.d.ts.map +1 -1
  115. package/dist/utils/diff.js +15 -0
  116. package/dist/utils/diff.js.map +1 -1
  117. package/dist/version.d.ts +12 -0
  118. package/dist/version.d.ts.map +1 -0
  119. package/dist/version.js +29 -0
  120. package/dist/version.js.map +1 -0
  121. package/package.json +19 -7
  122. package/CHANGELOG.md +0 -192
  123. package/DEVELOPMENT.md +0 -286
  124. package/ROADMAP.md +0 -91
  125. package/docs/API_KEY_GUIDE.md +0 -236
  126. package/docs/FAQ.md +0 -182
  127. package/install.bat +0 -63
  128. package/install.ps1 +0 -238
  129. package/install.sh +0 -113
  130. package/src/app/index.ts +0 -197
  131. package/src/cache/fingerprint.ts +0 -101
  132. package/src/cache/index.ts +0 -21
  133. package/src/cache/smartModel.ts +0 -133
  134. package/src/cache/stats.ts +0 -199
  135. package/src/cache/volatile.ts +0 -47
  136. package/src/cmd/index.tsx +0 -288
  137. package/src/config/index.ts +0 -156
  138. package/src/config/providers.ts +0 -234
  139. package/src/config/types.ts +0 -71
  140. package/src/hooks/index.ts +0 -111
  141. package/src/llm/anthropic.ts +0 -413
  142. package/src/llm/gemini.ts +0 -169
  143. package/src/llm/index.ts +0 -303
  144. package/src/llm/openai.ts +0 -243
  145. package/src/llm/provider.ts +0 -71
  146. package/src/mcp/client.ts +0 -330
  147. package/src/prompts/index.ts +0 -260
  148. package/src/session/compactor.ts +0 -103
  149. package/src/session/index.ts +0 -181
  150. package/src/session/message.ts +0 -42
  151. package/src/skills/brainstorming.ts +0 -43
  152. package/src/skills/code-review.ts +0 -45
  153. package/src/skills/executing-plans.ts +0 -27
  154. package/src/skills/frontend-design.ts +0 -35
  155. package/src/skills/git-workflow.ts +0 -36
  156. package/src/skills/improve-architecture.ts +0 -38
  157. package/src/skills/index.ts +0 -136
  158. package/src/skills/neuroweave.ts +0 -47
  159. package/src/skills/playwright.ts +0 -72
  160. package/src/skills/prototype.ts +0 -30
  161. package/src/skills/subagent.ts +0 -28
  162. package/src/skills/supabase.ts +0 -44
  163. package/src/skills/systematic-debugging.ts +0 -44
  164. package/src/skills/tdd.ts +0 -39
  165. package/src/skills/triage.ts +0 -35
  166. package/src/skills/verification.ts +0 -31
  167. package/src/skills/writing-plans.ts +0 -42
  168. package/src/tools/file.ts +0 -143
  169. package/src/tools/git.ts +0 -132
  170. package/src/tools/index.ts +0 -111
  171. package/src/tools/nwt.ts +0 -598
  172. package/src/tools/search.ts +0 -263
  173. package/src/tools/shell.ts +0 -136
  174. package/src/tools/types.ts +0 -122
  175. package/src/tui/app.tsx +0 -159
  176. package/src/tui/components/ChatList.tsx +0 -41
  177. package/src/tui/components/ChatMessage.tsx +0 -54
  178. package/src/tui/components/Header.tsx +0 -66
  179. package/src/tui/components/InitWizard.tsx +0 -247
  180. package/src/tui/components/Markdown.tsx +0 -35
  181. package/src/tui/components/ModelSelector.tsx +0 -107
  182. package/src/tui/components/StatusBar.tsx +0 -30
  183. package/src/tui/components/Thinking.tsx +0 -17
  184. package/src/tui/components/ToolCall.tsx +0 -102
  185. package/src/tui/components/UserInput.tsx +0 -151
  186. package/src/tui/hooks/useChat.ts +0 -287
  187. package/src/tui/hooks/useCommands.ts +0 -271
  188. package/src/tui/index.ts +0 -6
  189. package/src/tui/welcome.ts +0 -177
  190. package/src/types.ts +0 -104
  191. package/src/utils/diff.ts +0 -71
  192. package/src/utils/project.ts +0 -99
  193. package/src/utils/stableStringify.ts +0 -47
  194. package/src/utils/thinking.ts +0 -119
  195. package/tests/cache/fingerprint.test.ts +0 -75
  196. package/tests/cache/providerCatalog.test.ts +0 -31
  197. package/tests/cache/smartModel.test.ts +0 -100
  198. package/tests/cache/stableStringify.test.ts +0 -43
  199. package/tests/cache/stats.test.ts +0 -146
  200. package/tests/cache/volatile.test.ts +0 -75
  201. package/tests/smoke-pollution.mjs +0 -78
  202. package/tests/smoke-thinking.mjs +0 -110
  203. package/tests/smoke-tool-stream.mjs +0 -69
  204. package/tests/smoke-tool.mjs +0 -117
  205. package/tsconfig.json +0 -21
@@ -1,271 +0,0 @@
1
- import { useCallback } from 'react';
2
- import type { App } from '../../app/index.js';
3
-
4
- interface CommandResult {
5
- handled: boolean;
6
- output?: string;
7
- action?: 'clear' | 'reinit';
8
- }
9
-
10
- // 中文命令别名映射
11
- const CMD_ALIASES: Record<string, string> = {
12
- '/模型': '/model',
13
- '/新建': '/new',
14
- '/清空': '/new',
15
- '/压缩': '/compact',
16
- '/技能': '/skills',
17
- '/技能管理': '/skills',
18
- '/mcp': '/mcp',
19
- '/帮助': '/help',
20
- '/服务商': '/provider',
21
- '/思考': '/thinking',
22
- '/缓存': '/cache',
23
- '/ttl': '/ttl',
24
- '/TTL': '/ttl',
25
- };
26
-
27
- export const COMMAND_LIST = [
28
- { name: '/模型', desc: '切换模型' },
29
- { name: '/服务商', desc: '更换服务商' },
30
- { name: '/新建', desc: '新建会话' },
31
- { name: '/压缩', desc: '压缩上下文' },
32
- { name: '/缓存', desc: '缓存命中率' },
33
- { name: '/ttl', desc: '查看/设置 Cache TTL' },
34
- { name: '/技能', desc: '管理技能' },
35
- { name: '/mcp', desc: 'MCP 设置' },
36
- { name: '/帮助', desc: '查看帮助' },
37
- ];
38
-
39
- export function useCommands(app: App) {
40
-
41
- const handleCommand = useCallback((input: string): CommandResult => {
42
- const cmd = input.trim();
43
- const parts = cmd.split(/\s+/);
44
- let name = parts[0].toLowerCase();
45
- const arg = parts.slice(1).join(' ');
46
-
47
- // 中文别名映射
48
- if (CMD_ALIASES[name]) {
49
- name = CMD_ALIASES[name];
50
- }
51
-
52
- // ── /model [name] ───────────────────────────────────
53
- if (name === '/model') {
54
- if (!arg) {
55
- const c = app.config.get();
56
- return {
57
- handled: true,
58
- output: [
59
- `当前: ${c.provider} / ${c.model}`,
60
- '',
61
- '用法: /模型 <名称>',
62
- ' /模型 deepseek-chat',
63
- ' /模型 gpt-4o',
64
- ' /模型 claude-sonnet-4-20250514',
65
- '',
66
- '或: /服务商 更换服务商',
67
- ].join('\n'),
68
- };
69
- }
70
- app.config.save({ model: arg });
71
- return { handled: true, output: `模型 → ${arg}` };
72
- }
73
-
74
- // ── /provider ───────────────────────────────────────
75
- if (name === '/provider') {
76
- return { handled: true, action: 'reinit' };
77
- }
78
-
79
- // ── /new, /clear ────────────────────────────────────
80
- if (name === '/new') {
81
- app.session.clear();
82
- return { handled: true, output: '新会话已创建。', action: 'clear' };
83
- }
84
-
85
- // ── /compact ────────────────────────────────────────
86
- if (name === '/compact') {
87
- const before = app.session.getMessageCount();
88
- app.session.truncate();
89
- const after = app.session.getMessageCount();
90
- return { handled: true, output: `上下文已压缩: ${before} → ${after} 条消息` };
91
- }
92
-
93
- // ── /cache — prompt cache hit-rate + cost savings ───
94
- // v3.0.0: surfaces CacheStatsStore snapshots. Sub-commands:
95
- // /cache show full breakdown
96
- // /cache reset zero the persistent stats
97
- if (name === '/cache') {
98
- if (arg === 'reset' || arg === '重置') {
99
- app.cacheStats.reset();
100
- return { handled: true, output: '✓ 缓存统计已重置' };
101
- }
102
- if (arg === 'off' || arg === '关') {
103
- // Toggle disable: writes through to provider config (currently a
104
- // no-op until M4.5 wires Config.cache). Print a hint.
105
- return {
106
- handled: true,
107
- output: [
108
- '✗ 关闭缓存功能:在 ~/.thatgfsj/config.json 添加 "cache": { "enabled": false } 后重启。',
109
- ' (提示:完全关闭会大幅增加 token 成本;推荐保留开启)',
110
- ].join('\n'),
111
- };
112
- }
113
- const s = app.cacheStats.snapshot();
114
- if (s.totalRequests === 0) {
115
- return {
116
- handled: true,
117
- output: [
118
- '⚡ 缓存统计',
119
- '─────────────────────────',
120
- ' 暂无请求。开启一段对话后再来查看。',
121
- '',
122
- '/cache reset · 重置统计',
123
- ].join('\n'),
124
- };
125
- }
126
- const hitRateStr = (s.hitRate * 100).toFixed(1) + '%';
127
- const recent = s.history.slice(-10);
128
- const lines = [
129
- '⚡ 缓存统计 (累计 ' + s.totalRequests + ' 轮)',
130
- '─────────────────────────',
131
- ` 命中率: ${hitRateStr} (${s.totalReadTokens.toLocaleString()} / ${s.totalInputTokens.toLocaleString()} tokens)`,
132
- ` 累计创建: ${s.totalCreationTokens.toLocaleString()} tokens`,
133
- ` 累计节省: ¥${s.estimatedSavingsCNY.toFixed(4)}`,
134
- '',
135
- '最近 10 轮:',
136
- ];
137
- for (let i = 0; i < recent.length; i++) {
138
- const r = recent[i];
139
- const tag = r.read > 0 ? '⚡' : '💸';
140
- const pct = (r.hitRate * 100).toFixed(0) + '%';
141
- lines.push(` #${s.history.length - recent.length + i + 1} [${tag}] ${String(r.read).padStart(5)} / ${String(r.input).padStart(5)} ${pct.padStart(4)}`);
142
- }
143
- lines.push('');
144
- lines.push('/cache reset · 重置统计');
145
- return { handled: true, output: lines.join('\n') };
146
- }
147
-
148
- // v3.0.3: /ttl — view or pin the cache TTL.
149
- // v3.0.4: default is 1h (long-task). We cannot predict task length
150
- // at round 0, so the default TTL is the one that cannot expire
151
- // mid-task. Pinning 5m is only for users who are sure the session
152
- // is short.
153
- //
154
- // /ttl show current effective TTL + decision reason
155
- // /ttl 5m|1h pin TTL for this session (sticky, no cache reset)
156
- // /ttl 1h back to the long-task default
157
- if (name === '/ttl') {
158
- const configTtl = (app.config.get() as any).cache?.ttl as 'auto' | '5m' | '1h' | undefined;
159
- const resolved = app.resolvedTtl;
160
- if (arg === '5m' || arg === '1h') {
161
- app.config.save({ cache: { ...(app.config.get() as any).cache, ttl: arg } });
162
- app.resolvedTtl = arg;
163
- return { handled: true, output: `✓ Cache TTL 已固定为 ${arg}(首次请求会重建缓存)` };
164
- }
165
- // No arg: show current state
166
- const effective = resolved ?? (configTtl === 'auto' ? '1h' : (configTtl ?? '1h'));
167
- const lines = [
168
- '⏱ Cache TTL',
169
- '─────────────────────────',
170
- ` 配置: ${configTtl ?? '1h'}`,
171
- ` 当前会话: ${resolved ?? '尚未评估'}`,
172
- '',
173
- '说明: 默认 1 小时(长任务)。任务开始前无法预知长度,',
174
- ' 5 分钟 TTL 会在长任务中途过期;1 小时缓存命中即可回本。',
175
- ' 只有你确定会话很短时才建议 /ttl 5m。',
176
- '',
177
- '/ttl 5m|1h 调整 TTL',
178
- ];
179
- return { handled: true, output: lines.join('\n') };
180
- }
181
-
182
- // ── /skills [id] ────────────────────────────────────
183
- if (name === '/skills') {
184
- if (arg) {
185
- if (app.skills.isActive(arg)) {
186
- app.skills.deactivate(arg);
187
- return { handled: true, output: `✗ 已关闭: ${arg}` };
188
- } else if (app.skills.activate(arg)) {
189
- return { handled: true, output: `✓ 已开启: ${arg}` };
190
- }
191
- return { handled: true, output: `未知技能: ${arg}` };
192
- }
193
-
194
- const all = app.skills.list();
195
- const lines = [
196
- '技能列表:',
197
- ...all.map(s => {
198
- const mark = app.skills.isActive(s.id) ? '✓' : '·';
199
- return ` ${mark} ${s.id} ${s.description}`;
200
- }),
201
- '',
202
- '用法: /技能 <id> 切换',
203
- ];
204
- return { handled: true, output: lines.join('\n') };
205
- }
206
-
207
- // ── /mcp ────────────────────────────────────────────
208
- if (name === '/mcp') {
209
- return {
210
- handled: true,
211
- output: [
212
- 'MCP 配置: ~/.thatgfsj/mcp.json',
213
- '',
214
- ' {',
215
- ' "servers": {',
216
- ' "名称": { "command": "npx", "args": ["-y", "server"] }',
217
- ' }',
218
- ' }',
219
- ].join('\n'),
220
- };
221
- }
222
-
223
- // ── /thinking on|off ─────────────────────────────────
224
- if (name === '/thinking' || name === '/思考') {
225
- if (!arg) {
226
- return {
227
- handled: true,
228
- output: `思考块显示: ${app.showThinking ? '开启 (显示完整 <think>...</think>)' : '关闭 (压缩为单行提示)'}`,
229
- };
230
- }
231
- const want = arg.toLowerCase();
232
- if (want === 'on' || want === '开启' || want === 'true' || want === '1') {
233
- app.showThinking = true;
234
- return { handled: true, output: '✓ 已开启完整思考块显示' };
235
- }
236
- if (want === 'off' || want === '关闭' || want === 'false' || want === '0') {
237
- app.showThinking = false;
238
- return { handled: true, output: '✓ 已关闭思考块显示 (压缩为单行提示)' };
239
- }
240
- return { handled: true, output: `用法: /thinking on|off (当前: ${app.showThinking ? 'on' : 'off'})` };
241
- }
242
-
243
- // ── /help ───────────────────────────────────────────
244
- if (name === '/help') {
245
- return {
246
- handled: true,
247
- output: [
248
- '命令列表:',
249
- ' /模型 <名称> 切换模型',
250
- ' /服务商 更换服务商',
251
- ' /新建 新建会话',
252
- ' /压缩 压缩上下文',
253
- ' /缓存 查看缓存命中率',
254
- ' /思考 [on|off] 切换思考块显示',
255
- ' /技能 [id] 管理技能',
256
- ' /mcp MCP 设置',
257
- ' /帮助 查看帮助',
258
- ' exit 退出',
259
- '',
260
- '快捷键:',
261
- ' ↑/↓ 历史记录',
262
- ' Ctrl+C 退出',
263
- ].join('\n'),
264
- };
265
- }
266
-
267
- return { handled: false };
268
- }, [app]);
269
-
270
- return { handleCommand };
271
- }
package/src/tui/index.ts DELETED
@@ -1,6 +0,0 @@
1
- /**
2
- * TUI Module - Ink-based Terminal UI
3
- */
4
-
5
- export { TuiApp } from './app.js';
6
- export { WelcomeScreen } from './welcome.js';
@@ -1,177 +0,0 @@
1
- /**
2
- * Welcome / Setup Wizard - Clean UI
3
- */
4
-
5
- import chalk from 'chalk';
6
- import readline from 'readline';
7
- import { existsSync, mkdirSync, writeFileSync } from 'fs';
8
- import { join } from 'path';
9
- import { homedir } from 'os';
10
- import { PROVIDERS, getModelsForProvider, listProviders, isCustomProvider } from '../config/providers.js';
11
- import type { ProviderName } from '../config/types.js';
12
-
13
- const line = chalk.gray('─'.repeat(52));
14
-
15
- export class WelcomeScreen {
16
-
17
- static show(hasApiKey: boolean): void {
18
- if (hasApiKey) return;
19
-
20
- console.log();
21
- console.log(chalk.cyan.bold(' ⚡ Thatgfsj Code') + chalk.gray(' v0.5.0'));
22
- console.log(chalk.gray(' AI Coding Assistant'));
23
- console.log(line);
24
- console.log();
25
- console.log(chalk.yellow(' ⚠ No API key configured'));
26
- console.log();
27
- console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode init') + chalk.gray(' to set up your provider.'));
28
- console.log();
29
- console.log(chalk.gray(' Supported providers:'));
30
- console.log(chalk.gray(' OpenAI · Claude · DeepSeek · Kimi · GLM · Gemini · 中转站'));
31
- console.log(line);
32
- console.log();
33
- }
34
-
35
- static async interactiveSetup(): Promise<void> {
36
- console.clear();
37
- console.log();
38
- console.log(chalk.cyan.bold(' ⚡ Thatgfsj Code') + chalk.gray(' - Setup'));
39
- console.log(line);
40
- console.log();
41
-
42
- const rl = readline.createInterface({
43
- input: process.stdin,
44
- output: process.stdout,
45
- });
46
-
47
- const ask = (prompt: string): Promise<string> =>
48
- new Promise(resolve => rl.question(prompt, answer => resolve(answer.trim())));
49
-
50
- try {
51
- // ── Step 1: Provider ────────────────────────────────
52
- console.log(chalk.bold(' 1. Choose Provider'));
53
- console.log();
54
- const providers = listProviders();
55
- providers.forEach((p, i) => {
56
- const num = chalk.cyan((i + 1).toString().padStart(2));
57
- console.log(` ${num} ${p.name}`);
58
- });
59
- console.log();
60
-
61
- const choice = await ask(chalk.cyan(' ❯ '));
62
- const selected = providers[parseInt(choice) - 1] || providers[0];
63
- const providerName = selected.key;
64
-
65
- // ── Step 2: API Key ─────────────────────────────────
66
- console.log();
67
- console.log(chalk.bold(' 2. API Key'));
68
- console.log();
69
-
70
- let baseUrl: string | undefined;
71
-
72
- if (isCustomProvider(providerName)) {
73
- console.log(chalk.yellow(' Custom Provider (Relay Station)'));
74
- console.log(chalk.gray(' OpenAI-compatible or Anthropic-compatible'));
75
- console.log();
76
- baseUrl = await ask(chalk.cyan(' Base URL ❯ '));
77
- if (!baseUrl) {
78
- console.log(chalk.red(' ❌ Base URL required'));
79
- rl.close();
80
- return;
81
- }
82
- baseUrl = baseUrl.replace(/\/+$/, '');
83
- }
84
-
85
- const apiKey = await ask(chalk.cyan(' API Key ❯ '));
86
-
87
- // ── Step 3: Model ───────────────────────────────────
88
- console.log();
89
- console.log(chalk.bold(' 3. Choose Model'));
90
- console.log();
91
-
92
- let model: string;
93
- if (isCustomProvider(providerName)) {
94
- console.log(chalk.gray(' Enter model name for your relay station'));
95
- console.log(chalk.gray(' e.g. gpt-4o-mini, claude-sonnet-4-20250514'));
96
- console.log();
97
- model = await ask(chalk.cyan(' Model ❯ '));
98
- if (!model) {
99
- console.log(chalk.red(' ❌ Model name required'));
100
- rl.close();
101
- return;
102
- }
103
- } else {
104
- const models = getModelsForProvider(providerName);
105
- models.forEach((m, i) => {
106
- const num = chalk.cyan((i + 1).toString().padStart(2));
107
- console.log(` ${num} ${chalk.white(m.name)} ${chalk.gray(m.desc)}`);
108
- });
109
- console.log();
110
- const modelChoice = await ask(chalk.cyan(' ❯ '));
111
- const selectedModel = models[parseInt(modelChoice) - 1] || models[0];
112
- model = selectedModel.id;
113
- }
114
-
115
- // ── Step 4: Context Length ───────────────────────────
116
- console.log();
117
- console.log(chalk.bold(' 4. Context Length'));
118
- console.log();
119
- console.log(chalk.gray(' Max messages kept in conversation history'));
120
- console.log(chalk.gray(' Higher = more context, more tokens used'));
121
- console.log();
122
- console.log(chalk.cyan(' 1') + ' 20 ' + chalk.gray('(short,节省 token)'));
123
- console.log(chalk.cyan(' 2') + ' 50 ' + chalk.gray('(default,推荐)'));
124
- console.log(chalk.cyan(' 3') + ' 100 ' + chalk.gray('(long,长对话)'));
125
- console.log(chalk.cyan(' 4') + ' 200 ' + chalk.gray('(very long,超长对话)'));
126
- console.log();
127
- const ctxChoice = await ask(chalk.cyan(' ❯ '));
128
- const ctxMap: Record<string, number> = { '1': 20, '2': 50, '3': 100, '4': 200 };
129
- const contextLength = ctxMap[ctxChoice] || 50;
130
-
131
- // ── Save ────────────────────────────────────────────
132
- this.saveConfig(providerName, model, apiKey, baseUrl, contextLength);
133
-
134
- // ── Done ────────────────────────────────────────────
135
- console.log();
136
- console.log(line);
137
- console.log(chalk.green.bold(' ✅ Configuration saved!'));
138
- console.log();
139
- console.log(chalk.gray(' Config: ') + chalk.white(join(homedir(), '.thatgfsj', 'config.json')));
140
- console.log(chalk.gray(' Run ') + chalk.cyan.bold('gfcode') + chalk.gray(' to start.'));
141
- console.log(line);
142
- console.log();
143
-
144
- } finally {
145
- rl.close();
146
- }
147
- }
148
-
149
- private static saveConfig(provider: ProviderName, model: string, apiKey: string, baseUrl?: string, contextLength = 50): void {
150
- const configDir = join(homedir(), '.thatgfsj');
151
- const configPath = join(configDir, 'config.json');
152
-
153
- if (!existsSync(configDir)) {
154
- mkdirSync(configDir, { recursive: true });
155
- }
156
-
157
- const config: Record<string, any> = {
158
- provider,
159
- model,
160
- apiKey,
161
- temperature: 0.7,
162
- maxTokens: 4096,
163
- contextLength,
164
- };
165
-
166
- if (baseUrl) config.baseUrl = baseUrl;
167
-
168
- writeFileSync(configPath, JSON.stringify(config, null, 2));
169
-
170
- console.log();
171
- console.log(chalk.gray(' Provider: ') + chalk.white(provider));
172
- console.log(chalk.gray(' Model: ') + chalk.white(model));
173
- if (baseUrl) {
174
- console.log(chalk.gray(' URL: ') + chalk.white(baseUrl));
175
- }
176
- }
177
- }
package/src/types.ts DELETED
@@ -1,104 +0,0 @@
1
- /**
2
- * Global shared types for Thatgfsj Code
3
- * Only the most fundamental types used across modules
4
- *
5
- * v3.0.0+: Extended for Reasonix-style prompt caching:
6
- * - ChatMessage.content now accepts ContentBlock[] (Anthropic-style)
7
- * - cache_control field for explicit Anthropic cache breakpoints
8
- * - usage extended with cache_creation_input_tokens / cache_read_input_tokens
9
- * (Anthropic) and prompt_cache_hit_tokens / prompt_cache_miss_tokens (DeepSeek)
10
- * - StreamChunk now has 'thinking' / 'usage' variants for structured streaming
11
- */
12
-
13
- export type Role = 'system' | 'user' | 'assistant' | 'tool';
14
-
15
- /**
16
- * Anthropic-style cache control. ttl: '5m' (default) | '1h'
17
- * - '5m': cheaper write cost, refreshed on each hit, expires 5 min after last hit
18
- * - '1h': more expensive write cost, expires 1 hour after last hit
19
- */
20
- export interface CacheControl {
21
- type: 'ephemeral';
22
- ttl?: '5m' | '1h';
23
- }
24
-
25
- /**
26
- * Anthropic-style content blocks. text carries optional cache_control so that
27
- * Anthropic prompt caching breakpoints can be placed inside messages.
28
- */
29
- export type ContentBlock =
30
- | { type: 'text'; text: string; cache_control?: CacheControl }
31
- | { type: 'image'; source: { type: 'base64'; media_type: string; data: string } };
32
-
33
- export interface ChatMessage {
34
- role: Role;
35
- /**
36
- * For 'system' / 'user' / 'assistant' (text only): a plain string.
37
- * For 'user' (multimodal) or 'assistant' (Anthropic tool_use mixed with text):
38
- * an array of ContentBlock.
39
- * 'tool' messages must use string content (carries tool result text).
40
- */
41
- content: string | ContentBlock[];
42
- name?: string;
43
- tool_calls?: ToolCall[];
44
- tool_call_id?: string;
45
- reasoning_content?: string;
46
- /**
47
- * Anthropic prompt caching marker attached to the message itself.
48
- * Only honored when the underlying provider supports it (anthropic, anthropic-relay).
49
- * OpenAI / Gemini / DeepSeek ignore it (DeepSeek uses automatic prefix cache).
50
- */
51
- cache_control?: CacheControl;
52
- }
53
-
54
- export interface ToolCall {
55
- id: string;
56
- type: 'function';
57
- function: {
58
- name: string;
59
- arguments: string;
60
- };
61
- }
62
-
63
- export interface Usage {
64
- prompt_tokens: number;
65
- completion_tokens: number;
66
- total_tokens: number;
67
- // Anthropic prompt cache fields
68
- cache_creation_input_tokens?: number;
69
- cache_read_input_tokens?: number;
70
- // DeepSeek prompt cache fields
71
- prompt_cache_hit_tokens?: number;
72
- prompt_cache_miss_tokens?: number;
73
- }
74
-
75
- export interface ChatResponse {
76
- content: string;
77
- role: 'assistant';
78
- usage?: Usage;
79
- tool_calls?: ToolCall[];
80
- reasoning_content?: string;
81
- }
82
-
83
- export interface ChatOptions {
84
- temperature?: number;
85
- maxTokens?: number;
86
- stream?: boolean;
87
- }
88
-
89
- /**
90
- * Structured streaming chunk. Replaces the legacy @@TOOL@@ sentinel-string
91
- * protocol that the previous version of useChat parsed by string-splitting.
92
- *
93
- * - 'text': regular model output
94
- * - 'tool_calls': one or more tool calls ready to dispatch
95
- * - 'thinking': reasoning content (stripped from final persistence; kept in
96
- * VolatileScratch for the current round only)
97
- * - 'usage': token usage + cache stats; emitted at end of stream if
98
- * the upstream provider returned them
99
- */
100
- export type StreamChunk =
101
- | { type: 'text'; content: string }
102
- | { type: 'tool_calls'; toolCalls: ToolCall[] }
103
- | { type: 'thinking'; content: string }
104
- | { type: 'usage'; usage: Usage };
package/src/utils/diff.ts DELETED
@@ -1,71 +0,0 @@
1
- /**
2
- * Diff Preview - Show file changes in a readable format
3
- * Migrated from old src/utils/diff-preview.ts
4
- */
5
-
6
- export interface DiffResult {
7
- original: string;
8
- modified: string;
9
- diff: string;
10
- added: number;
11
- removed: number;
12
- }
13
-
14
- export class DiffPreview {
15
- /**
16
- * Generate a simple line-by-line diff
17
- */
18
- static diff(original: string, modified: string): DiffResult {
19
- const originalLines = original.split('\n');
20
- const modifiedLines = modified.split('\n');
21
- const diffLines: string[] = [];
22
- let added = 0;
23
- let removed = 0;
24
-
25
- // Simple diff: show removed (-) and added (+) lines
26
- const maxLen = Math.max(originalLines.length, modifiedLines.length);
27
-
28
- for (let i = 0; i < maxLen; i++) {
29
- const orig = originalLines[i];
30
- const mod = modifiedLines[i];
31
-
32
- if (orig === mod) {
33
- diffLines.push(` ${orig}`);
34
- } else {
35
- if (orig !== undefined) {
36
- diffLines.push(`- ${orig}`);
37
- removed++;
38
- }
39
- if (mod !== undefined) {
40
- diffLines.push(`+ ${mod}`);
41
- added++;
42
- }
43
- }
44
- }
45
-
46
- return {
47
- original,
48
- modified,
49
- diff: diffLines.join('\n'),
50
- added,
51
- removed,
52
- };
53
- }
54
-
55
- /**
56
- * Format diff for display
57
- */
58
- static format(result: DiffResult): string {
59
- const lines = result.diff.split('\n').map(line => {
60
- if (line.startsWith('+')) return `\x1b[32m${line}\x1b[0m`;
61
- if (line.startsWith('-')) return `\x1b[31m${line}\x1b[0m`;
62
- return line;
63
- });
64
-
65
- return [
66
- `\x1b[36m--- Changes: -${result.removed} / +${result.added} ---\x1b[0m`,
67
- ...lines,
68
- `\x1b[36m--- End ---\x1b[0m`,
69
- ].join('\n');
70
- }
71
- }