thatgfsj-code 2.2.9 → 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 (182) hide show
  1. package/CHANGELOG.md +15 -67
  2. package/dist/app/index.d.ts +28 -5
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +69 -10
  5. package/dist/app/index.js.map +1 -1
  6. package/dist/cache/fingerprint.d.ts +49 -0
  7. package/dist/cache/fingerprint.d.ts.map +1 -0
  8. package/dist/cache/fingerprint.js +100 -0
  9. package/dist/cache/fingerprint.js.map +1 -0
  10. package/dist/cache/index.d.ts +15 -0
  11. package/dist/cache/index.d.ts.map +1 -0
  12. package/dist/cache/index.js +15 -0
  13. package/dist/cache/index.js.map +1 -0
  14. package/dist/cache/smartModel.d.ts +40 -0
  15. package/dist/cache/smartModel.d.ts.map +1 -0
  16. package/dist/cache/smartModel.js +43 -0
  17. package/dist/cache/smartModel.js.map +1 -0
  18. package/dist/cache/stats.d.ts +93 -0
  19. package/dist/cache/stats.d.ts.map +1 -0
  20. package/dist/cache/stats.js +155 -0
  21. package/dist/cache/stats.js.map +1 -0
  22. package/dist/cache/volatile.d.ts +32 -0
  23. package/dist/cache/volatile.d.ts.map +1 -0
  24. package/dist/cache/volatile.js +44 -0
  25. package/dist/cache/volatile.js.map +1 -0
  26. package/dist/cmd/index.js +49 -57
  27. package/dist/cmd/index.js.map +1 -1
  28. package/dist/config/index.d.ts.map +1 -1
  29. package/dist/config/index.js +8 -0
  30. package/dist/config/index.js.map +1 -1
  31. package/dist/config/types.d.ts +14 -0
  32. package/dist/config/types.d.ts.map +1 -1
  33. package/dist/llm/anthropic.d.ts +55 -5
  34. package/dist/llm/anthropic.d.ts.map +1 -1
  35. package/dist/llm/anthropic.js +172 -26
  36. package/dist/llm/anthropic.js.map +1 -1
  37. package/dist/llm/index.d.ts +26 -8
  38. package/dist/llm/index.d.ts.map +1 -1
  39. package/dist/llm/index.js +83 -15
  40. package/dist/llm/index.js.map +1 -1
  41. package/dist/llm/openai.d.ts +28 -8
  42. package/dist/llm/openai.d.ts.map +1 -1
  43. package/dist/llm/openai.js +58 -8
  44. package/dist/llm/openai.js.map +1 -1
  45. package/dist/llm/provider.d.ts +40 -8
  46. package/dist/llm/provider.d.ts.map +1 -1
  47. package/dist/llm/provider.js +8 -1
  48. package/dist/llm/provider.js.map +1 -1
  49. package/dist/prompts/index.d.ts +32 -0
  50. package/dist/prompts/index.d.ts.map +1 -1
  51. package/dist/prompts/index.js +33 -11
  52. package/dist/prompts/index.js.map +1 -1
  53. package/dist/session/index.d.ts +32 -3
  54. package/dist/session/index.d.ts.map +1 -1
  55. package/dist/session/index.js +51 -36
  56. package/dist/session/index.js.map +1 -1
  57. package/dist/skills/brainstorming.js +33 -33
  58. package/dist/skills/code-review.js +35 -35
  59. package/dist/skills/executing-plans.js +18 -18
  60. package/dist/skills/frontend-design.js +25 -25
  61. package/dist/skills/git-workflow.js +26 -26
  62. package/dist/skills/improve-architecture.js +28 -28
  63. package/dist/skills/neuroweave.js +37 -37
  64. package/dist/skills/playwright.js +62 -62
  65. package/dist/skills/prototype.js +20 -20
  66. package/dist/skills/subagent.js +19 -19
  67. package/dist/skills/supabase.js +34 -34
  68. package/dist/skills/systematic-debugging.js +34 -34
  69. package/dist/skills/tdd.js +29 -29
  70. package/dist/skills/triage.js +25 -25
  71. package/dist/skills/verification.js +22 -22
  72. package/dist/skills/writing-plans.js +32 -32
  73. package/dist/tools/nwt.js +16 -16
  74. package/dist/tui/app.d.ts.map +1 -1
  75. package/dist/tui/app.js +9 -2
  76. package/dist/tui/app.js.map +1 -1
  77. package/dist/tui/components/Header.d.ts +8 -0
  78. package/dist/tui/components/Header.d.ts.map +1 -1
  79. package/dist/tui/components/Header.js +17 -4
  80. package/dist/tui/components/Header.js.map +1 -1
  81. package/dist/tui/components/InitWizard.d.ts +12 -1
  82. package/dist/tui/components/InitWizard.d.ts.map +1 -1
  83. package/dist/tui/components/InitWizard.js +58 -3
  84. package/dist/tui/components/InitWizard.js.map +1 -1
  85. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  86. package/dist/tui/components/ToolCall.js +1 -6
  87. package/dist/tui/components/ToolCall.js.map +1 -1
  88. package/dist/tui/hooks/useChat.d.ts +22 -0
  89. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  90. package/dist/tui/hooks/useChat.js +82 -46
  91. package/dist/tui/hooks/useChat.js.map +1 -1
  92. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  93. package/dist/tui/hooks/useCommands.js +57 -0
  94. package/dist/tui/hooks/useCommands.js.map +1 -1
  95. package/dist/tui/welcome.d.ts.map +1 -1
  96. package/dist/tui/welcome.js +2 -3
  97. package/dist/tui/welcome.js.map +1 -1
  98. package/dist/types.d.ts +79 -6
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js +7 -0
  101. package/dist/types.js.map +1 -1
  102. package/dist/utils/stableStringify.d.ts +21 -0
  103. package/dist/utils/stableStringify.d.ts.map +1 -0
  104. package/dist/utils/stableStringify.js +53 -0
  105. package/dist/utils/stableStringify.js.map +1 -0
  106. package/dist/utils/thinking.d.ts.map +1 -1
  107. package/dist/utils/thinking.js +0 -3
  108. package/dist/utils/thinking.js.map +1 -1
  109. package/package.json +6 -6
  110. package/src/app/index.ts +180 -124
  111. package/src/cache/fingerprint.ts +101 -0
  112. package/src/cache/index.ts +15 -0
  113. package/src/cache/smartModel.ts +52 -0
  114. package/src/cache/stats.ts +199 -0
  115. package/src/cache/volatile.ts +47 -0
  116. package/src/cmd/index.tsx +288 -292
  117. package/src/config/index.ts +156 -148
  118. package/src/config/providers.ts +234 -234
  119. package/src/config/types.ts +67 -53
  120. package/src/hooks/index.ts +111 -111
  121. package/src/llm/anthropic.ts +388 -243
  122. package/src/llm/gemini.ts +169 -169
  123. package/src/llm/index.ts +265 -200
  124. package/src/llm/openai.ts +243 -196
  125. package/src/llm/provider.ts +66 -34
  126. package/src/prompts/index.ts +260 -220
  127. package/src/session/compactor.ts +103 -103
  128. package/src/session/index.ts +181 -168
  129. package/src/session/message.ts +42 -42
  130. package/src/skills/brainstorming.ts +43 -43
  131. package/src/skills/code-review.ts +45 -45
  132. package/src/skills/executing-plans.ts +27 -27
  133. package/src/skills/frontend-design.ts +35 -35
  134. package/src/skills/git-workflow.ts +36 -36
  135. package/src/skills/improve-architecture.ts +38 -38
  136. package/src/skills/index.ts +136 -136
  137. package/src/skills/neuroweave.ts +47 -47
  138. package/src/skills/playwright.ts +72 -72
  139. package/src/skills/prototype.ts +30 -30
  140. package/src/skills/subagent.ts +28 -28
  141. package/src/skills/supabase.ts +44 -44
  142. package/src/skills/systematic-debugging.ts +44 -44
  143. package/src/skills/tdd.ts +39 -39
  144. package/src/skills/triage.ts +35 -35
  145. package/src/skills/verification.ts +31 -31
  146. package/src/skills/writing-plans.ts +42 -42
  147. package/src/tools/nwt.ts +598 -598
  148. package/src/tools/types.ts +122 -122
  149. package/src/tui/app.tsx +199 -186
  150. package/src/tui/components/ChatList.tsx +41 -41
  151. package/src/tui/components/ChatMessage.tsx +54 -54
  152. package/src/tui/components/Header.tsx +56 -29
  153. package/src/tui/components/InitWizard.tsx +217 -132
  154. package/src/tui/components/Markdown.tsx +35 -35
  155. package/src/tui/components/ModelSelector.tsx +87 -87
  156. package/src/tui/components/StatusBar.tsx +30 -30
  157. package/src/tui/components/Thinking.tsx +17 -17
  158. package/src/tui/components/ToolCall.tsx +102 -107
  159. package/src/tui/components/UserInput.tsx +131 -131
  160. package/src/tui/hooks/useChat.ts +287 -238
  161. package/src/tui/hooks/useCommands.ts +234 -176
  162. package/src/tui/index.ts +6 -6
  163. package/src/tui/welcome.ts +177 -178
  164. package/src/types.ts +104 -42
  165. package/src/utils/diff.ts +71 -71
  166. package/src/utils/project.ts +99 -99
  167. package/src/utils/stableStringify.ts +47 -0
  168. package/src/utils/thinking.ts +118 -121
  169. package/tests/cache/fingerprint.test.ts +75 -0
  170. package/tests/cache/stableStringify.test.ts +43 -0
  171. package/tests/cache/stats.test.ts +146 -0
  172. package/tests/cache/volatile.test.ts +75 -0
  173. package/tests/smoke-pollution.mjs +78 -0
  174. package/tests/smoke-thinking.mjs +110 -0
  175. package/tests/smoke-tool-stream.mjs +69 -0
  176. package/tests/smoke-tool.mjs +117 -0
  177. package/.nwt/meta.json +0 -5
  178. package/dist/version.d.ts +0 -16
  179. package/dist/version.d.ts.map +0 -1
  180. package/dist/version.js +0 -16
  181. package/dist/version.js.map +0 -1
  182. package/src/version.ts +0 -16
package/src/llm/gemini.ts CHANGED
@@ -1,169 +1,169 @@
1
- /**
2
- * Google Gemini Provider
3
- */
4
-
5
- import type { ChatMessage, ChatResponse, ChatOptions, ToolCall } from '../types.js';
6
- import type { Tool } from '../tools/types.js';
7
- import type { LLMProvider, StreamChunk, ProviderConfig } from './provider.js';
8
-
9
- export class GeminiProvider implements LLMProvider {
10
- readonly name = 'gemini';
11
- protected config: ProviderConfig;
12
-
13
- constructor(config: ProviderConfig) {
14
- this.config = config;
15
- }
16
-
17
- buildTools(tools: Tool[]): any[] {
18
- return [{
19
- functionDeclarations: tools.map(tool => ({
20
- name: tool.name,
21
- description: tool.description,
22
- parameters: tool.inputSchema || {
23
- type: 'object',
24
- properties: Object.fromEntries(
25
- tool.parameters.map(p => [p.name, { type: p.type, description: p.description }])
26
- ),
27
- required: tool.parameters.filter(p => p.required).map(p => p.name),
28
- },
29
- })),
30
- }];
31
- }
32
-
33
- async chat(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): Promise<ChatResponse> {
34
- const body = this.buildRequest(messages, options, tools);
35
- const url = `${this.config.baseUrl}/models/${this.config.model}:generateContent?key=${this.config.apiKey}`;
36
-
37
- const response = await fetch(url, {
38
- method: 'POST',
39
- headers: { 'Content-Type': 'application/json' },
40
- body: JSON.stringify(body),
41
- });
42
-
43
- if (!response.ok) {
44
- const text = await response.text().catch(() => '');
45
- throw new Error(`Gemini API error ${response.status}: ${text}`);
46
- }
47
-
48
- const data = await response.json();
49
- const candidate = data.candidates?.[0];
50
- const parts = candidate?.content?.parts || [];
51
- const text = parts.map((p: any) => p.text).filter(Boolean).join('');
52
-
53
- const functionCalls = parts.filter((p: any) => p.functionCall).map((p: any) => ({
54
- id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
55
- type: 'function' as const,
56
- function: {
57
- name: p.functionCall.name,
58
- arguments: JSON.stringify(p.functionCall.args || {}),
59
- },
60
- }));
61
-
62
- return {
63
- content: text,
64
- role: 'assistant',
65
- usage: data.usageMetadata ? {
66
- prompt_tokens: data.usageMetadata.promptTokenCount || 0,
67
- completion_tokens: data.usageMetadata.candidatesTokenCount || 0,
68
- total_tokens: data.usageMetadata.totalTokenCount || 0,
69
- } : undefined,
70
- tool_calls: functionCalls.length > 0 ? functionCalls : undefined,
71
- };
72
- }
73
-
74
- async *chatStream(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): AsyncGenerator<StreamChunk, ChatResponse> {
75
- const body = this.buildRequest(messages, options, tools);
76
- const url = `${this.config.baseUrl}/models/${this.config.model}:streamGenerateContent?alt=sse&key=${this.config.apiKey}`;
77
-
78
- const response = await fetch(url, {
79
- method: 'POST',
80
- headers: { 'Content-Type': 'application/json' },
81
- body: JSON.stringify(body),
82
- });
83
-
84
- if (!response.ok || !response.body) {
85
- const text = await response.text().catch(() => '');
86
- throw new Error(`Gemini API error ${response.status}: ${text}`);
87
- }
88
-
89
- const reader = response.body.getReader();
90
- const decoder = new TextDecoder();
91
- let fullContent = '';
92
- let buffer = '';
93
- const functionCalls: ToolCall[] = [];
94
-
95
- try {
96
- while (true) {
97
- const { done, value } = await reader.read();
98
- if (done) break;
99
-
100
- buffer += decoder.decode(value, { stream: true });
101
- const lines = buffer.split('\n');
102
- buffer = lines.pop() || '';
103
-
104
- for (const line of lines) {
105
- const trimmed = line.trim();
106
- if (!trimmed || !trimmed.startsWith('data: ')) continue;
107
-
108
- try {
109
- const data = JSON.parse(trimmed.slice(6));
110
- const parts = data.candidates?.[0]?.content?.parts || [];
111
- for (const part of parts) {
112
- if (part.text) {
113
- fullContent += part.text;
114
- yield { type: 'text', content: part.text };
115
- }
116
- if (part.functionCall) {
117
- functionCalls.push({
118
- id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
119
- type: 'function',
120
- function: {
121
- name: part.functionCall.name,
122
- arguments: JSON.stringify(part.functionCall.args || {}),
123
- },
124
- });
125
- }
126
- }
127
- } catch {
128
- // Skip invalid JSON
129
- }
130
- }
131
- }
132
- } finally {
133
- reader.releaseLock();
134
- }
135
-
136
- if (functionCalls.length > 0) {
137
- yield { type: 'tool_calls', toolCalls: functionCalls };
138
- }
139
-
140
- return { content: fullContent, role: 'assistant', tool_calls: functionCalls.length > 0 ? functionCalls : undefined };
141
- }
142
-
143
- private buildRequest(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]) {
144
- const systemMsg = messages.find(m => m.role === 'system');
145
- const nonSystemMsgs = messages.filter(m => m.role !== 'system');
146
-
147
- const contents = nonSystemMsgs.map(m => ({
148
- role: m.role === 'assistant' ? 'model' : 'user',
149
- parts: [{ text: m.content }],
150
- }));
151
-
152
- const body: any = {
153
- contents,
154
- ...(systemMsg && {
155
- systemInstruction: { parts: [{ text: systemMsg.content }] },
156
- }),
157
- generationConfig: {
158
- temperature: options?.temperature ?? this.config.temperature,
159
- maxOutputTokens: options?.maxTokens ?? this.config.maxTokens,
160
- },
161
- };
162
-
163
- if (tools && tools.length > 0) {
164
- body.tools = this.buildTools(tools);
165
- }
166
-
167
- return body;
168
- }
169
- }
1
+ /**
2
+ * Google Gemini Provider
3
+ */
4
+
5
+ import type { ChatMessage, ChatResponse, ChatOptions, ToolCall } from '../types.js';
6
+ import type { Tool } from '../tools/types.js';
7
+ import type { LLMProvider, StreamChunk, ProviderConfig } from './provider.js';
8
+
9
+ export class GeminiProvider implements LLMProvider {
10
+ readonly name = 'gemini';
11
+ protected config: ProviderConfig;
12
+
13
+ constructor(config: ProviderConfig) {
14
+ this.config = config;
15
+ }
16
+
17
+ buildTools(tools: Tool[]): any[] {
18
+ return [{
19
+ functionDeclarations: tools.map(tool => ({
20
+ name: tool.name,
21
+ description: tool.description,
22
+ parameters: tool.inputSchema || {
23
+ type: 'object',
24
+ properties: Object.fromEntries(
25
+ tool.parameters.map(p => [p.name, { type: p.type, description: p.description }])
26
+ ),
27
+ required: tool.parameters.filter(p => p.required).map(p => p.name),
28
+ },
29
+ })),
30
+ }];
31
+ }
32
+
33
+ async chat(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): Promise<ChatResponse> {
34
+ const body = this.buildRequest(messages, options, tools);
35
+ const url = `${this.config.baseUrl}/models/${this.config.model}:generateContent?key=${this.config.apiKey}`;
36
+
37
+ const response = await fetch(url, {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify(body),
41
+ });
42
+
43
+ if (!response.ok) {
44
+ const text = await response.text().catch(() => '');
45
+ throw new Error(`Gemini API error ${response.status}: ${text}`);
46
+ }
47
+
48
+ const data = await response.json();
49
+ const candidate = data.candidates?.[0];
50
+ const parts = candidate?.content?.parts || [];
51
+ const text = parts.map((p: any) => p.text).filter(Boolean).join('');
52
+
53
+ const functionCalls = parts.filter((p: any) => p.functionCall).map((p: any) => ({
54
+ id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
55
+ type: 'function' as const,
56
+ function: {
57
+ name: p.functionCall.name,
58
+ arguments: JSON.stringify(p.functionCall.args || {}),
59
+ },
60
+ }));
61
+
62
+ return {
63
+ content: text,
64
+ role: 'assistant',
65
+ usage: data.usageMetadata ? {
66
+ prompt_tokens: data.usageMetadata.promptTokenCount || 0,
67
+ completion_tokens: data.usageMetadata.candidatesTokenCount || 0,
68
+ total_tokens: data.usageMetadata.totalTokenCount || 0,
69
+ } : undefined,
70
+ tool_calls: functionCalls.length > 0 ? functionCalls : undefined,
71
+ };
72
+ }
73
+
74
+ async *chatStream(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]): AsyncGenerator<StreamChunk, ChatResponse> {
75
+ const body = this.buildRequest(messages, options, tools);
76
+ const url = `${this.config.baseUrl}/models/${this.config.model}:streamGenerateContent?alt=sse&key=${this.config.apiKey}`;
77
+
78
+ const response = await fetch(url, {
79
+ method: 'POST',
80
+ headers: { 'Content-Type': 'application/json' },
81
+ body: JSON.stringify(body),
82
+ });
83
+
84
+ if (!response.ok || !response.body) {
85
+ const text = await response.text().catch(() => '');
86
+ throw new Error(`Gemini API error ${response.status}: ${text}`);
87
+ }
88
+
89
+ const reader = response.body.getReader();
90
+ const decoder = new TextDecoder();
91
+ let fullContent = '';
92
+ let buffer = '';
93
+ const functionCalls: ToolCall[] = [];
94
+
95
+ try {
96
+ while (true) {
97
+ const { done, value } = await reader.read();
98
+ if (done) break;
99
+
100
+ buffer += decoder.decode(value, { stream: true });
101
+ const lines = buffer.split('\n');
102
+ buffer = lines.pop() || '';
103
+
104
+ for (const line of lines) {
105
+ const trimmed = line.trim();
106
+ if (!trimmed || !trimmed.startsWith('data: ')) continue;
107
+
108
+ try {
109
+ const data = JSON.parse(trimmed.slice(6));
110
+ const parts = data.candidates?.[0]?.content?.parts || [];
111
+ for (const part of parts) {
112
+ if (part.text) {
113
+ fullContent += part.text;
114
+ yield { type: 'text', content: part.text };
115
+ }
116
+ if (part.functionCall) {
117
+ functionCalls.push({
118
+ id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
119
+ type: 'function',
120
+ function: {
121
+ name: part.functionCall.name,
122
+ arguments: JSON.stringify(part.functionCall.args || {}),
123
+ },
124
+ });
125
+ }
126
+ }
127
+ } catch {
128
+ // Skip invalid JSON
129
+ }
130
+ }
131
+ }
132
+ } finally {
133
+ reader.releaseLock();
134
+ }
135
+
136
+ if (functionCalls.length > 0) {
137
+ yield { type: 'tool_calls', toolCalls: functionCalls };
138
+ }
139
+
140
+ return { content: fullContent, role: 'assistant', tool_calls: functionCalls.length > 0 ? functionCalls : undefined };
141
+ }
142
+
143
+ private buildRequest(messages: ChatMessage[], options?: ChatOptions, tools?: Tool[]) {
144
+ const systemMsg = messages.find(m => m.role === 'system');
145
+ const nonSystemMsgs = messages.filter(m => m.role !== 'system');
146
+
147
+ const contents = nonSystemMsgs.map(m => ({
148
+ role: m.role === 'assistant' ? 'model' : 'user',
149
+ parts: [{ text: m.content }],
150
+ }));
151
+
152
+ const body: any = {
153
+ contents,
154
+ ...(systemMsg && {
155
+ systemInstruction: { parts: [{ text: systemMsg.content }] },
156
+ }),
157
+ generationConfig: {
158
+ temperature: options?.temperature ?? this.config.temperature,
159
+ maxOutputTokens: options?.maxTokens ?? this.config.maxTokens,
160
+ },
161
+ };
162
+
163
+ if (tools && tools.length > 0) {
164
+ body.tools = this.buildTools(tools);
165
+ }
166
+
167
+ return body;
168
+ }
169
+ }