thatgfsj-code 1.0.3 → 3.0.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 (184) hide show
  1. package/CHANGELOG.md +192 -0
  2. package/DEVELOPMENT.md +286 -0
  3. package/dist/app/index.d.ts +40 -3
  4. package/dist/app/index.d.ts.map +1 -1
  5. package/dist/app/index.js +87 -12
  6. package/dist/app/index.js.map +1 -1
  7. package/dist/cache/fingerprint.d.ts +49 -0
  8. package/dist/cache/fingerprint.d.ts.map +1 -0
  9. package/dist/cache/fingerprint.js +100 -0
  10. package/dist/cache/fingerprint.js.map +1 -0
  11. package/dist/cache/index.d.ts +15 -0
  12. package/dist/cache/index.d.ts.map +1 -0
  13. package/dist/cache/index.js +15 -0
  14. package/dist/cache/index.js.map +1 -0
  15. package/dist/cache/smartModel.d.ts +40 -0
  16. package/dist/cache/smartModel.d.ts.map +1 -0
  17. package/dist/cache/smartModel.js +43 -0
  18. package/dist/cache/smartModel.js.map +1 -0
  19. package/dist/cache/stats.d.ts +93 -0
  20. package/dist/cache/stats.d.ts.map +1 -0
  21. package/dist/cache/stats.js +155 -0
  22. package/dist/cache/stats.js.map +1 -0
  23. package/dist/cache/volatile.d.ts +32 -0
  24. package/dist/cache/volatile.d.ts.map +1 -0
  25. package/dist/cache/volatile.js +44 -0
  26. package/dist/cache/volatile.js.map +1 -0
  27. package/dist/cmd/index.d.ts +21 -0
  28. package/dist/cmd/index.d.ts.map +1 -1
  29. package/dist/cmd/index.js +155 -31
  30. package/dist/cmd/index.js.map +1 -1
  31. package/dist/config/index.d.ts.map +1 -1
  32. package/dist/config/index.js +8 -0
  33. package/dist/config/index.js.map +1 -1
  34. package/dist/config/types.d.ts +14 -0
  35. package/dist/config/types.d.ts.map +1 -1
  36. package/dist/llm/anthropic.d.ts +55 -5
  37. package/dist/llm/anthropic.d.ts.map +1 -1
  38. package/dist/llm/anthropic.js +172 -26
  39. package/dist/llm/anthropic.js.map +1 -1
  40. package/dist/llm/index.d.ts +26 -8
  41. package/dist/llm/index.d.ts.map +1 -1
  42. package/dist/llm/index.js +83 -15
  43. package/dist/llm/index.js.map +1 -1
  44. package/dist/llm/openai.d.ts +28 -8
  45. package/dist/llm/openai.d.ts.map +1 -1
  46. package/dist/llm/openai.js +58 -8
  47. package/dist/llm/openai.js.map +1 -1
  48. package/dist/llm/provider.d.ts +40 -8
  49. package/dist/llm/provider.d.ts.map +1 -1
  50. package/dist/llm/provider.js +8 -1
  51. package/dist/llm/provider.js.map +1 -1
  52. package/dist/prompts/index.d.ts +32 -0
  53. package/dist/prompts/index.d.ts.map +1 -1
  54. package/dist/prompts/index.js +33 -11
  55. package/dist/prompts/index.js.map +1 -1
  56. package/dist/session/index.d.ts +43 -3
  57. package/dist/session/index.d.ts.map +1 -1
  58. package/dist/session/index.js +114 -17
  59. package/dist/session/index.js.map +1 -1
  60. package/dist/skills/brainstorming.js +33 -33
  61. package/dist/skills/code-review.js +35 -35
  62. package/dist/skills/executing-plans.js +18 -18
  63. package/dist/skills/frontend-design.js +25 -25
  64. package/dist/skills/git-workflow.js +26 -26
  65. package/dist/skills/improve-architecture.js +28 -28
  66. package/dist/skills/neuroweave.js +37 -37
  67. package/dist/skills/playwright.js +62 -62
  68. package/dist/skills/prototype.js +20 -20
  69. package/dist/skills/subagent.js +19 -19
  70. package/dist/skills/supabase.js +34 -34
  71. package/dist/skills/systematic-debugging.js +34 -34
  72. package/dist/skills/tdd.js +29 -29
  73. package/dist/skills/triage.js +25 -25
  74. package/dist/skills/verification.js +22 -22
  75. package/dist/skills/writing-plans.js +32 -32
  76. package/dist/tools/nwt.js +16 -16
  77. package/dist/tui/app.d.ts.map +1 -1
  78. package/dist/tui/app.js +9 -2
  79. package/dist/tui/app.js.map +1 -1
  80. package/dist/tui/components/ChatList.d.ts.map +1 -1
  81. package/dist/tui/components/ChatList.js +4 -3
  82. package/dist/tui/components/ChatList.js.map +1 -1
  83. package/dist/tui/components/Header.d.ts +8 -0
  84. package/dist/tui/components/Header.d.ts.map +1 -1
  85. package/dist/tui/components/Header.js +17 -3
  86. package/dist/tui/components/Header.js.map +1 -1
  87. package/dist/tui/components/InitWizard.d.ts +12 -1
  88. package/dist/tui/components/InitWizard.d.ts.map +1 -1
  89. package/dist/tui/components/InitWizard.js +58 -3
  90. package/dist/tui/components/InitWizard.js.map +1 -1
  91. package/dist/tui/components/ToolCall.d.ts +1 -1
  92. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  93. package/dist/tui/components/ToolCall.js +4 -4
  94. package/dist/tui/components/ToolCall.js.map +1 -1
  95. package/dist/tui/hooks/useChat.d.ts +22 -0
  96. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  97. package/dist/tui/hooks/useChat.js +127 -50
  98. package/dist/tui/hooks/useChat.js.map +1 -1
  99. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  100. package/dist/tui/hooks/useCommands.js +78 -0
  101. package/dist/tui/hooks/useCommands.js.map +1 -1
  102. package/dist/types.d.ts +79 -6
  103. package/dist/types.d.ts.map +1 -1
  104. package/dist/types.js +7 -0
  105. package/dist/types.js.map +1 -1
  106. package/dist/utils/stableStringify.d.ts +21 -0
  107. package/dist/utils/stableStringify.d.ts.map +1 -0
  108. package/dist/utils/stableStringify.js +53 -0
  109. package/dist/utils/stableStringify.js.map +1 -0
  110. package/dist/utils/thinking.d.ts +59 -0
  111. package/dist/utils/thinking.d.ts.map +1 -0
  112. package/dist/utils/thinking.js +104 -0
  113. package/dist/utils/thinking.js.map +1 -0
  114. package/install.bat +63 -0
  115. package/install.ps1 +238 -0
  116. package/install.sh +113 -0
  117. package/package.json +7 -4
  118. package/src/app/index.ts +180 -108
  119. package/src/cache/fingerprint.ts +101 -0
  120. package/src/cache/index.ts +15 -0
  121. package/src/cache/smartModel.ts +52 -0
  122. package/src/cache/stats.ts +199 -0
  123. package/src/cache/volatile.ts +47 -0
  124. package/src/cmd/index.tsx +288 -160
  125. package/src/config/index.ts +156 -148
  126. package/src/config/providers.ts +234 -234
  127. package/src/config/types.ts +67 -53
  128. package/src/hooks/index.ts +111 -111
  129. package/src/llm/anthropic.ts +388 -243
  130. package/src/llm/gemini.ts +169 -169
  131. package/src/llm/index.ts +265 -200
  132. package/src/llm/openai.ts +243 -196
  133. package/src/llm/provider.ts +66 -34
  134. package/src/prompts/index.ts +260 -220
  135. package/src/session/compactor.ts +103 -103
  136. package/src/session/index.ts +181 -87
  137. package/src/session/message.ts +42 -42
  138. package/src/skills/brainstorming.ts +43 -43
  139. package/src/skills/code-review.ts +45 -45
  140. package/src/skills/executing-plans.ts +27 -27
  141. package/src/skills/frontend-design.ts +35 -35
  142. package/src/skills/git-workflow.ts +36 -36
  143. package/src/skills/improve-architecture.ts +38 -38
  144. package/src/skills/index.ts +136 -136
  145. package/src/skills/neuroweave.ts +47 -47
  146. package/src/skills/playwright.ts +72 -72
  147. package/src/skills/prototype.ts +30 -30
  148. package/src/skills/subagent.ts +28 -28
  149. package/src/skills/supabase.ts +44 -44
  150. package/src/skills/systematic-debugging.ts +44 -44
  151. package/src/skills/tdd.ts +39 -39
  152. package/src/skills/triage.ts +35 -35
  153. package/src/skills/verification.ts +31 -31
  154. package/src/skills/writing-plans.ts +42 -42
  155. package/src/tools/nwt.ts +598 -598
  156. package/src/tools/types.ts +122 -122
  157. package/src/tui/app.tsx +199 -186
  158. package/src/tui/components/ChatList.tsx +41 -32
  159. package/src/tui/components/ChatMessage.tsx +54 -54
  160. package/src/tui/components/Header.tsx +56 -28
  161. package/src/tui/components/InitWizard.tsx +217 -132
  162. package/src/tui/components/Markdown.tsx +35 -35
  163. package/src/tui/components/ModelSelector.tsx +87 -87
  164. package/src/tui/components/StatusBar.tsx +30 -30
  165. package/src/tui/components/Thinking.tsx +17 -17
  166. package/src/tui/components/ToolCall.tsx +102 -71
  167. package/src/tui/components/UserInput.tsx +131 -131
  168. package/src/tui/hooks/useChat.ts +287 -194
  169. package/src/tui/hooks/useCommands.ts +234 -154
  170. package/src/tui/index.ts +6 -6
  171. package/src/tui/welcome.ts +177 -177
  172. package/src/types.ts +104 -42
  173. package/src/utils/diff.ts +71 -71
  174. package/src/utils/project.ts +99 -99
  175. package/src/utils/stableStringify.ts +47 -0
  176. package/src/utils/thinking.ts +119 -0
  177. package/tests/cache/fingerprint.test.ts +75 -0
  178. package/tests/cache/stableStringify.test.ts +43 -0
  179. package/tests/cache/stats.test.ts +146 -0
  180. package/tests/cache/volatile.test.ts +75 -0
  181. package/tests/smoke-pollution.mjs +78 -0
  182. package/tests/smoke-thinking.mjs +110 -0
  183. package/tests/smoke-tool-stream.mjs +69 -0
  184. package/tests/smoke-tool.mjs +117 -0
@@ -1,177 +1,177 @@
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
- }
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 CHANGED
@@ -1,42 +1,104 @@
1
- /**
2
- * Global shared types for Thatgfsj Code
3
- * Only the most fundamental types used across modules
4
- */
5
-
6
- export type Role = 'system' | 'user' | 'assistant' | 'tool';
7
-
8
- export interface ChatMessage {
9
- role: Role;
10
- content: string;
11
- name?: string;
12
- tool_calls?: ToolCall[];
13
- tool_call_id?: string;
14
- reasoning_content?: string;
15
- }
16
-
17
- export interface ToolCall {
18
- id: string;
19
- type: 'function';
20
- function: {
21
- name: string;
22
- arguments: string;
23
- };
24
- }
25
-
26
- export interface ChatResponse {
27
- content: string;
28
- role: 'assistant';
29
- usage?: {
30
- prompt_tokens: number;
31
- completion_tokens: number;
32
- total_tokens: number;
33
- };
34
- tool_calls?: ToolCall[];
35
- reasoning_content?: string;
36
- }
37
-
38
- export interface ChatOptions {
39
- temperature?: number;
40
- maxTokens?: number;
41
- stream?: boolean;
42
- }
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 CHANGED
@@ -1,71 +1,71 @@
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
- }
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
+ }