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/src/mcp/client.ts DELETED
@@ -1,330 +0,0 @@
1
- /**
2
- * S07: MCP Client (Model Context Protocol)
3
- * Connect to MCP servers and expose their tools as agent tools
4
- *
5
- * MCP turns the agent from a closed system into an open platform.
6
- * Any server can expose tools via the MCP JSON-RPC 2.0 protocol.
7
- */
8
-
9
- import { spawn, ChildProcess } from 'child_process';
10
- import type { Tool, ToolResult } from '../tools/types.js';
11
-
12
- // ==================== MCP Types ====================
13
-
14
- interface MCPJsonRPCRequest {
15
- jsonrpc: '2.0';
16
- id: number | string;
17
- method: string;
18
- params?: any;
19
- }
20
-
21
- interface MCPJsonRPCResponse {
22
- jsonrpc: '2.0';
23
- id: number | string;
24
- result?: any;
25
- error?: { code: number; message: string; data?: any };
26
- }
27
-
28
- interface MCPTool {
29
- name: string;
30
- description: string;
31
- inputSchema: {
32
- type: 'object';
33
- properties: Record<string, any>;
34
- required?: string[];
35
- };
36
- }
37
-
38
- // ==================== MCP Client ====================
39
-
40
- export class MCPClient {
41
- private process: ChildProcess | null = null;
42
- private tools: Map<string, MCPTool> = new Map();
43
- private requestId = 0;
44
- private pendingRequests: Map<number | string, {
45
- resolve: (value: any) => void;
46
- reject: (reason: any) => void;
47
- }> = new Map();
48
- private name: string;
49
- private connected = false;
50
-
51
- constructor(name: string) {
52
- this.name = name;
53
- }
54
-
55
- /**
56
- * S07: Connect to an MCP server
57
- * @param command Command to run (e.g., 'npx', 'node')
58
- * @param args Arguments (e.g., ['-y', '@modelcontextprotocol/server-filesystem', '/path/to/dir'])
59
- */
60
- async connect(command: string, args: string[]): Promise<void> {
61
- return new Promise((resolve, reject) => {
62
- this.process = spawn(command, args, {
63
- stdio: ['pipe', 'pipe', 'pipe']
64
- });
65
-
66
- let buffer = '';
67
-
68
- this.process.stdout?.on('data', (data: Buffer) => {
69
- buffer += data.toString();
70
- const lines = buffer.split('\n');
71
- buffer = lines.pop() || '';
72
-
73
- for (const line of lines) {
74
- if (line.trim()) {
75
- this.handleMessage(line);
76
- }
77
- }
78
- });
79
-
80
- this.process.stderr?.on('data', (data: Buffer) => {
81
- console.error(`[MCP ${this.name}] stderr:`, data.toString().trim());
82
- });
83
-
84
- this.process.on('error', (err) => {
85
- reject(err);
86
- });
87
-
88
- this.process.on('close', (code) => {
89
- this.connected = false;
90
- console.log(`[MCP ${this.name}] exited with code ${code}`);
91
- });
92
-
93
- // Initialize MCP connection
94
- this.sendRequest('initialize', {
95
- protocolVersion: '2024-11-05',
96
- capabilities: { tools: {} },
97
- clientInfo: { name: 'thatgfsj-code', version: '1.0.0' }
98
- }).then(() => {
99
- this.connected = true;
100
- // Send initialized notification
101
- this.sendNotification('initialized', {});
102
- // List available tools
103
- return this.listTools();
104
- }).then(() => {
105
- resolve();
106
- }).catch(reject);
107
-
108
- // Timeout
109
- setTimeout(() => reject(new Error('MCP connection timeout')), 30000);
110
- });
111
- }
112
-
113
- /**
114
- * S07: Handle incoming JSON-RPC message
115
- */
116
- private handleMessage(raw: string): void {
117
- try {
118
- const msg: MCPJsonRPCResponse = JSON.parse(raw);
119
-
120
- // Handle response
121
- const pending = this.pendingRequests.get(msg.id);
122
- if (pending) {
123
- this.pendingRequests.delete(msg.id);
124
- if (msg.error) {
125
- pending.reject(new Error(msg.error.message));
126
- } else {
127
- pending.resolve(msg.result);
128
- }
129
- return;
130
- }
131
- } catch {
132
- // Not JSON, ignore
133
- }
134
- }
135
-
136
- /**
137
- * S07: Send a JSON-RPC request
138
- */
139
- private sendRequest(method: string, params: any): Promise<any> {
140
- return new Promise((resolve, reject) => {
141
- if (!this.process?.connected) {
142
- reject(new Error('MCP process not connected'));
143
- return;
144
- }
145
-
146
- const id = ++this.requestId;
147
- this.pendingRequests.set(id, { resolve, reject });
148
-
149
- const request: MCPJsonRPCRequest = {
150
- jsonrpc: '2.0',
151
- id,
152
- method,
153
- params
154
- };
155
-
156
- this.process.stdin?.write(JSON.stringify(request) + '\n');
157
-
158
- // Timeout
159
- setTimeout(() => {
160
- if (this.pendingRequests.has(id)) {
161
- this.pendingRequests.delete(id);
162
- reject(new Error('MCP request timeout: ' + method));
163
- }
164
- }, 30000);
165
- });
166
- }
167
-
168
- /**
169
- * S07: Send a notification (no response expected)
170
- */
171
- private sendNotification(method: string, params: any): void {
172
- if (!this.process?.connected) return;
173
-
174
- const notification = {
175
- jsonrpc: '2.0',
176
- method,
177
- params
178
- };
179
-
180
- this.process.stdin?.write(JSON.stringify(notification) + '\n');
181
- }
182
-
183
- /**
184
- * S07: Call an MCP tool
185
- */
186
- async callTool(name: string, arguments_: Record<string, any>): Promise<ToolResult> {
187
- try {
188
- const result = await this.sendRequest('tools/call', {
189
- name,
190
- arguments: arguments_
191
- });
192
-
193
- return {
194
- success: true,
195
- output: typeof result === 'string' ? result : JSON.stringify(result)
196
- };
197
- } catch (error: any) {
198
- return {
199
- success: false,
200
- error: error.message
201
- };
202
- }
203
- }
204
-
205
- /**
206
- * S07: List tools from MCP server
207
- */
208
- private async listTools(): Promise<void> {
209
- try {
210
- const result = await this.sendRequest('tools/list', {});
211
- const tools: MCPTool[] = result.tools || [];
212
-
213
- this.tools.clear();
214
- for (const tool of tools) {
215
- this.tools.set(tool.name, tool);
216
- }
217
-
218
- console.log(`[MCP ${this.name}] Loaded ${tools.length} tools:`, [...this.tools.keys()].join(', '));
219
- } catch (error: any) {
220
- console.error(`[MCP ${this.name}] Failed to list tools:`, error.message);
221
- }
222
- }
223
-
224
- /**
225
- * S07: Get registered tools as agent Tool[]
226
- */
227
- getTools(): Tool[] {
228
- return [...this.tools.values()].map(mcpTool => ({
229
- name: this.name + ':' + mcpTool.name,
230
- description: mcpTool.description,
231
- parameters: Object.entries(mcpTool.inputSchema.properties || {}).map(([name, schema]: [string, any]) => ({
232
- name,
233
- type: schema.type || 'string',
234
- description: schema.description || '',
235
- required: (mcpTool.inputSchema.required || []).includes(name)
236
- })),
237
- execute: async (params: Record<string, any>): Promise<ToolResult> => {
238
- return this.callTool(mcpTool.name, params);
239
- }
240
- }));
241
- }
242
-
243
- /**
244
- * S07: Get tool names
245
- */
246
- getToolNames(): string[] {
247
- return [...this.tools.keys()];
248
- }
249
-
250
- /**
251
- * S07: Check if connected
252
- */
253
- isConnected(): boolean {
254
- return this.connected;
255
- }
256
-
257
- /**
258
- * S07: Disconnect from MCP server
259
- */
260
- disconnect(): void {
261
- if (this.process) {
262
- this.process.kill();
263
- this.process = null;
264
- }
265
- this.connected = false;
266
- this.tools.clear();
267
- this.pendingRequests.clear();
268
- }
269
- }
270
-
271
- // ==================== MCP Server Manager ====================
272
-
273
- export class MCPServerManager {
274
- private clients: Map<string, MCPClient> = new Map();
275
-
276
- /**
277
- * S07: Add and connect an MCP server
278
- */
279
- async addServer(name: string, command: string, args: string[]): Promise<void> {
280
- const client = new MCPClient(name);
281
- await client.connect(command, args);
282
- this.clients.set(name, client);
283
- }
284
-
285
- /**
286
- * S07: Get all tools from all servers
287
- */
288
- getAllTools(): Tool[] {
289
- const allTools: Tool[] = [];
290
- for (const client of this.clients.values()) {
291
- allTools.push(...client.getTools());
292
- }
293
- return allTools;
294
- }
295
-
296
- /**
297
- * S07: Get tool names from all servers
298
- */
299
- getAllToolNames(): string[] {
300
- const names: string[] = [];
301
- for (const [serverName, client] of this.clients) {
302
- for (const toolName of client.getToolNames()) {
303
- names.push(serverName + ':' + toolName);
304
- }
305
- }
306
- return names;
307
- }
308
-
309
- /**
310
- * S07: Disconnect a server
311
- */
312
- removeServer(name: string): void {
313
- const client = this.clients.get(name);
314
- if (client) {
315
- client.disconnect();
316
- this.clients.delete(name);
317
- }
318
- }
319
-
320
- /**
321
- * S07: Get server status
322
- */
323
- getStatus(): Record<string, boolean> {
324
- const status: Record<string, boolean> = {};
325
- for (const [name, client] of this.clients) {
326
- status[name] = client.isConnected();
327
- }
328
- return status;
329
- }
330
- }
@@ -1,260 +0,0 @@
1
- /**
2
- * System Prompt Builder - Dynamic prompt construction
3
- */
4
-
5
- import { readFileSync, existsSync, readdirSync } from 'fs';
6
- import { join } from 'path';
7
- import type { Tool } from '../tools/types.js';
8
-
9
- export interface SystemPromptConfig {
10
- cwd?: string;
11
- tools?: Tool[];
12
- includeProjectMd?: boolean;
13
- permissionMode?: 'accept' | 'deny' | 'ask';
14
- date?: Date;
15
- skillsPrompt?: string;
16
- }
17
-
18
- /**
19
- * v3.0.0: a single named fragment of the system prompt. The provider layer
20
- * attaches cache_control markers to volatile=false segments (Anthropic) and
21
- * the fingerprint module hashes only the non-volatile portion.
22
- */
23
- export interface SystemSegment {
24
- /** Stable identifier, used in fingerprints and logging. */
25
- name: string;
26
- /** Rendered text content. May be empty string if the section was a no-op. */
27
- content: string;
28
- /**
29
- * volatile=false: content never changes between requests within a session.
30
- * volatile=true: content may change (NWT events, current time).
31
- */
32
- volatile: boolean;
33
- }
34
-
35
- export class SystemPromptBuilder {
36
- private config: Required<SystemPromptConfig>;
37
-
38
- constructor(config: SystemPromptConfig = {}) {
39
- this.config = {
40
- cwd: config.cwd ?? process.cwd(),
41
- tools: config.tools ?? [],
42
- includeProjectMd: config.includeProjectMd ?? true,
43
- permissionMode: config.permissionMode ?? 'ask',
44
- date: config.date ?? new Date(),
45
- skillsPrompt: config.skillsPrompt ?? '',
46
- };
47
- }
48
-
49
- build(): string {
50
- return this.buildSegments()
51
- .map(s => s.content)
52
- .filter(Boolean)
53
- .join('\n\n');
54
- }
55
-
56
- /**
57
- * v3.0.0: build the system prompt as named segments instead of a flat
58
- * string. The provider layer (Anthropic) can attach cache_control markers
59
- * to the segments it caches, and the fingerprint module can hash the
60
- * static portion to detect when the immutable prefix changed.
61
- *
62
- * Ordering rule (Reasonix-style): immutable prefix first, volatile tail
63
- * last. NWT history and Date move to the tail because both can change
64
- * between rounds (Date: every build; NWT: when a new event is logged).
65
- * Keeping them out of the prefix means the upstream cache keeps its
66
- * prefix checkpoint stable across those changes.
67
- *
68
- * Segments with empty content are filtered out — they neither contribute
69
- * to the string output nor to any cache fingerprint.
70
- */
71
- buildSegments(): SystemSegment[] {
72
- return [
73
- // Immutable prefix (cacheable as a single block on Anthropic)
74
- { name: 'project-instructions', content: this.buildProjectInstructions(), volatile: false },
75
- { name: 'identity', content: this.buildIdentity(), volatile: false },
76
- { name: 'tool-instructions', content: this.buildToolInstructions(), volatile: false },
77
- { name: 'environment', content: this.buildEnvironment(), volatile: false },
78
- { name: 'permission-mode', content: this.buildPermissionMode(), volatile: false },
79
- { name: 'skills', content: this.buildSkills(), volatile: false },
80
- // Volatile tail — not part of the cache prefix
81
- { name: 'nwt-history', content: this.buildNwtHistory(), volatile: true },
82
- { name: 'date-info', content: this.buildDateInfo(), volatile: true },
83
- ].filter(s => s.content);
84
- }
85
-
86
- private buildIdentity(): string {
87
- return [
88
- 'You are Thatgfsj Code, an interactive coding agent.',
89
- '',
90
- 'IMPORTANT: You MUST follow the user configuration above (CLAUDE.md, SKILLS.md, etc).',
91
- 'At the start of each task, read SKILLS.md to check for relevant skills.',
92
- '',
93
- 'Tools available: file, shell, git, search, nwt.',
94
- '',
95
- 'Rules:',
96
- '- Follow the user\'s technical preferences from their config',
97
- '- Be concise but thorough',
98
- '- Verify changes after making them',
99
- '',
100
- '## NWT Auto-Logging (IMPORTANT)',
101
- '',
102
- 'After completing any meaningful task, you MUST evaluate whether to log it to NWT.',
103
- 'Call `nwt log` automatically when ANY of these conditions are true:',
104
- '',
105
- '1. You created, modified, or deleted 2+ files',
106
- '2. You made an architectural decision (chose a library, pattern, or approach)',
107
- '3. You fixed a bug',
108
- '4. You added a new feature or capability',
109
- '5. You refactored existing code',
110
- '6. You set up a new project or module',
111
- '7. You resolved a security issue',
112
- '8. You completed a multi-step task',
113
- '',
114
- 'Do NOT log:',
115
- '- Simple file reads or searches',
116
- '- Trivial single-line changes',
117
- '- Conversational questions with no code changes',
118
- '',
119
- 'When logging, use:',
120
- ' nwt log task="<imperative verb> <what>" summary="<what was done>" files="<files>" tags="<tags>"',
121
- '',
122
- 'Tags: feature, fix, refactor, decision, milestone, test, docs, config, security, init',
123
- '',
124
- 'IMPORTANT: Log silently without mentioning it to the user unless they ask.',
125
- ].join('\n');
126
- }
127
-
128
- private buildToolInstructions(): string {
129
- const tools = this.config.tools;
130
- if (tools.length === 0) return '## Tools\n\nNo tools are currently registered.';
131
-
132
- const toolDescs = tools.map(t => {
133
- const params = t.parameters
134
- .map(p => ` - ${p.name} (${p.type}${p.required ? ', required' : ''}): ${p.description}`)
135
- .join('\n');
136
- return `### ${t.name}\n${t.description}\n\nParameters:\n${params}`;
137
- }).join('\n\n');
138
-
139
- return `## Tools\n\nYou have access to the following tools:\n\n${toolDescs}\n\nTo use a tool, respond with a tool call.`;
140
- }
141
-
142
- private buildEnvironment(): string {
143
- return `## Environment\n\nWorking directory: ${this.config.cwd}`;
144
- }
145
-
146
- private buildPermissionMode(): string {
147
- const mode = this.config.permissionMode;
148
- const explanations: Record<string, string> = {
149
- accept: 'All tool calls are automatically allowed without confirmation.',
150
- deny: 'All tool calls are blocked. You may only read and discuss.',
151
- ask: 'Dangerous or destructive commands require user confirmation before execution.',
152
- };
153
- return `## Permission Mode\n\nCurrent mode: ${mode}\n\n${explanations[mode] || ''}`;
154
- }
155
-
156
- /**
157
- * Read project instruction files (generic, works for any user)
158
- */
159
- private buildProjectInstructions(): string {
160
- if (!this.config.includeProjectMd) return '';
161
-
162
- const cwd = this.config.cwd;
163
- const home = process.env.USERPROFILE || process.env.HOME || '';
164
-
165
- // Project-level files (any project can have these)
166
- const projectFiles = [
167
- 'CLAUDE.md', '.claude.md', 'AGENTS.md',
168
- 'Codex.md', '.Codex.md', 'CODEX.md', '.codex.md',
169
- 'CONVENTIONS.md', 'CONTRIBUTING.md',
170
- ];
171
-
172
- // User-level files (in home directory)
173
- const userDirs = ['.claude', '.Codex', '.agents'];
174
- const userFiles = ['CLAUDE.md', 'SKILLS.md', 'AGENTS.md', 'CONVENTIONS.md'];
175
-
176
- const paths: string[] = [];
177
-
178
- // Project-level
179
- for (const f of projectFiles) {
180
- paths.push(join(cwd, f));
181
- }
182
-
183
- // User-level
184
- for (const dir of userDirs) {
185
- for (const f of userFiles) {
186
- paths.push(join(home, dir, f));
187
- }
188
- }
189
-
190
- const sections: string[] = [];
191
- const MAX_LEN = 3000;
192
-
193
- for (const path of paths) {
194
- if (existsSync(path)) {
195
- try {
196
- let content = readFileSync(path, 'utf-8').trim();
197
- if (content) {
198
- const filename = path.split(/[/\\]/).pop();
199
- if (content.length > MAX_LEN) {
200
- content = content.slice(0, MAX_LEN) + '\n... (truncated)';
201
- }
202
- sections.push(`[${filename}]\n${content}`);
203
- }
204
- } catch {}
205
- }
206
- }
207
-
208
- if (sections.length === 0) return '';
209
- return `## User Configuration\n\n${sections.join('\n\n')}`;
210
- }
211
-
212
- /**
213
- * Auto-inject recent NWT history into system prompt
214
- */
215
- private buildNwtHistory(): string {
216
- try {
217
- const nwtDir = join(this.config.cwd, '.nwt', 'events');
218
- if (!existsSync(nwtDir)) return '';
219
-
220
- const files = readdirSync(nwtDir)
221
- .filter(f => f.endsWith('.json'))
222
- .sort()
223
- .slice(-10); // Last 10 events
224
-
225
- if (files.length === 0) return '';
226
-
227
- const events = files.map(f => {
228
- try {
229
- const ev = JSON.parse(readFileSync(join(nwtDir, f), 'utf-8'));
230
- const time = ev.timestamp?.split('T')[0] || '';
231
- const files = ev.files?.length > 0 ? ` [${ev.files.join(', ')}]` : '';
232
- return `- [${ev.id}] ${time} ${ev.task}: ${ev.summary}${files}`;
233
- } catch {
234
- return null;
235
- }
236
- }).filter(Boolean);
237
-
238
- if (events.length === 0) return '';
239
-
240
- return `## Project History (NWT)\n\nRecent evolution events:\n\n${events.join('\n')}`;
241
- } catch {
242
- return '';
243
- }
244
- }
245
-
246
- private buildSkills(): string {
247
- if (!this.config.skillsPrompt) return '';
248
- return `## Active Skills\n\n${this.config.skillsPrompt}`;
249
- }
250
-
251
- private buildDateInfo(): string {
252
- const now = this.config.date;
253
- const iso = now.toISOString().replace('T', ' ').split('.')[0];
254
- return `## Current Time\n\n${iso}`;
255
- }
256
-
257
- setTools(tools: Tool[]): this { this.config.tools = tools; return this; }
258
- setCwd(cwd: string): this { this.config.cwd = cwd; return this; }
259
- setPermissionMode(mode: 'accept' | 'deny' | 'ask'): this { this.config.permissionMode = mode; return this; }
260
- }
@@ -1,103 +0,0 @@
1
- /**
2
- * Context Compactor - Progressive compression for conversation history
3
- * Migrated from old src/core/context-compactor.ts
4
- *
5
- * Strategy:
6
- * 1. Within limit: no compression
7
- * 2. Beyond limit: keep system + recent N turns + summarize middle
8
- */
9
-
10
- import type { ChatMessage } from '../types.js';
11
-
12
- export interface CompactorConfig {
13
- /** Max messages before compression kicks in */
14
- maxMessages?: number;
15
- /** How many recent turns to always preserve */
16
- preserveRecent?: number;
17
- }
18
-
19
- export interface CompressionResult {
20
- originalCount: number;
21
- compactedCount: number;
22
- removedCount: number;
23
- }
24
-
25
- export class ContextCompactor {
26
- private maxMessages: number;
27
- private preserveRecent: number;
28
-
29
- constructor(config: CompactorConfig = {}) {
30
- this.maxMessages = config.maxMessages ?? 50;
31
- this.preserveRecent = config.preserveRecent ?? 10;
32
- }
33
-
34
- /**
35
- * Compact messages if needed
36
- */
37
- compact(messages: ChatMessage[]): { compacted: ChatMessage[]; result: CompressionResult } {
38
- if (messages.length <= this.maxMessages) {
39
- return {
40
- compacted: messages,
41
- result: { originalCount: messages.length, compactedCount: messages.length, removedCount: 0 },
42
- };
43
- }
44
-
45
- const systemMsg = messages.find(m => m.role === 'system');
46
- const others = messages.filter(m => m.role !== 'system');
47
-
48
- if (others.length <= this.preserveRecent) {
49
- return {
50
- compacted: messages,
51
- result: { originalCount: messages.length, compactedCount: messages.length, removedCount: 0 },
52
- };
53
- }
54
-
55
- // Keep recent N turns
56
- const recent = others.slice(-this.preserveRecent);
57
-
58
- // Summarize the middle portion
59
- const middle = others.slice(0, -this.preserveRecent);
60
- const summary: ChatMessage = {
61
- role: 'system',
62
- content: `[Earlier conversation summary: ${middle.length} messages covering ${this.extractTopics(middle)}]`,
63
- };
64
-
65
- const compacted = systemMsg
66
- ? [systemMsg, summary, ...recent]
67
- : [summary, ...recent];
68
-
69
- return {
70
- compacted,
71
- result: {
72
- originalCount: messages.length,
73
- compactedCount: compacted.length,
74
- removedCount: messages.length - compacted.length,
75
- },
76
- };
77
- }
78
-
79
- /**
80
- * Auto-compact if messages exceed limit
81
- */
82
- autoCompact(messages: ChatMessage[]): { compacted: ChatMessage[]; result: CompressionResult } {
83
- return this.compact(messages);
84
- }
85
-
86
- /**
87
- * Estimate token count (rough: ~4 chars per token)
88
- */
89
- estimateTokens(messages: ChatMessage[]): number {
90
- return messages.reduce((sum, m) => sum + Math.ceil(m.content.length / 4) + 10, 0);
91
- }
92
-
93
- /**
94
- * Extract topic summary from messages
95
- */
96
- private extractTopics(msgs: ChatMessage[]): string {
97
- const topics = msgs
98
- .filter(m => m.role === 'user')
99
- .map(m => m.content.slice(0, 30))
100
- .slice(0, 3);
101
- return topics.join(', ') || 'various topics';
102
- }
103
- }