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.
- package/README.md +64 -21
- package/dist/app/index.d.ts +93 -22
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +232 -40
- package/dist/app/index.js.map +1 -1
- package/dist/cache/smartModel.d.ts +29 -22
- package/dist/cache/smartModel.d.ts.map +1 -1
- package/dist/cache/smartModel.js +33 -40
- package/dist/cache/smartModel.js.map +1 -1
- package/dist/cmd/index.d.ts +12 -20
- package/dist/cmd/index.d.ts.map +1 -1
- package/dist/cmd/index.js +192 -146
- package/dist/cmd/index.js.map +1 -1
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +9 -6
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +5 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/llm/anthropic.d.ts +1 -1
- package/dist/llm/anthropic.d.ts.map +1 -1
- package/dist/llm/anthropic.js +116 -84
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/gemini.d.ts +27 -3
- package/dist/llm/gemini.d.ts.map +1 -1
- package/dist/llm/gemini.js +196 -73
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/index.d.ts +20 -1
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +71 -10
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/openai.d.ts +5 -1
- package/dist/llm/openai.d.ts.map +1 -1
- package/dist/llm/openai.js +96 -56
- package/dist/llm/openai.js.map +1 -1
- package/dist/mcp/client.d.ts +84 -34
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +291 -126
- package/dist/mcp/client.js.map +1 -1
- package/dist/session/compactor.d.ts +27 -7
- package/dist/session/compactor.d.ts.map +1 -1
- package/dist/session/compactor.js +78 -23
- package/dist/session/compactor.js.map +1 -1
- package/dist/session/index.d.ts +96 -33
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +278 -60
- package/dist/session/index.js.map +1 -1
- package/dist/tools/file.d.ts +10 -2
- package/dist/tools/file.d.ts.map +1 -1
- package/dist/tools/file.js +54 -5
- package/dist/tools/file.js.map +1 -1
- package/dist/tools/git.d.ts +16 -2
- package/dist/tools/git.d.ts.map +1 -1
- package/dist/tools/git.js +61 -18
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/nwt.d.ts.map +1 -1
- package/dist/tools/nwt.js +8 -1
- package/dist/tools/nwt.js.map +1 -1
- package/dist/tools/search.d.ts +8 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +105 -34
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/shell.d.ts +0 -4
- package/dist/tools/shell.d.ts.map +1 -1
- package/dist/tools/shell.js +11 -26
- package/dist/tools/shell.js.map +1 -1
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/tui/app.d.ts.map +1 -1
- package/dist/tui/app.js +90 -18
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/components/ChatMessage.d.ts.map +1 -1
- package/dist/tui/components/ChatMessage.js +9 -2
- package/dist/tui/components/ChatMessage.js.map +1 -1
- package/dist/tui/components/ConfirmPrompt.d.ts +21 -0
- package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
- package/dist/tui/components/ConfirmPrompt.js +45 -0
- package/dist/tui/components/ConfirmPrompt.js.map +1 -0
- package/dist/tui/components/Header.d.ts +10 -5
- package/dist/tui/components/Header.d.ts.map +1 -1
- package/dist/tui/components/Header.js +13 -5
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/InitWizard.d.ts.map +1 -1
- package/dist/tui/components/InitWizard.js +8 -13
- package/dist/tui/components/InitWizard.js.map +1 -1
- package/dist/tui/components/StatusBar.d.ts +7 -0
- package/dist/tui/components/StatusBar.d.ts.map +1 -1
- package/dist/tui/components/StatusBar.js +8 -3
- package/dist/tui/components/StatusBar.js.map +1 -1
- package/dist/tui/components/Thinking.d.ts +5 -0
- package/dist/tui/components/Thinking.d.ts.map +1 -1
- package/dist/tui/components/Thinking.js +24 -2
- package/dist/tui/components/Thinking.js.map +1 -1
- package/dist/tui/components/ToolCall.d.ts +6 -1
- package/dist/tui/components/ToolCall.d.ts.map +1 -1
- package/dist/tui/components/ToolCall.js +50 -22
- package/dist/tui/components/ToolCall.js.map +1 -1
- package/dist/tui/components/UserInput.d.ts +5 -0
- package/dist/tui/components/UserInput.d.ts.map +1 -1
- package/dist/tui/components/UserInput.js +9 -13
- package/dist/tui/components/UserInput.js.map +1 -1
- package/dist/tui/hooks/useChat.d.ts +1 -0
- package/dist/tui/hooks/useChat.d.ts.map +1 -1
- package/dist/tui/hooks/useChat.js +36 -16
- package/dist/tui/hooks/useChat.js.map +1 -1
- package/dist/tui/hooks/useCommands.d.ts +7 -1
- package/dist/tui/hooks/useCommands.d.ts.map +1 -1
- package/dist/tui/hooks/useCommands.js +83 -49
- package/dist/tui/hooks/useCommands.js.map +1 -1
- package/dist/tui/theme.d.ts +34 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +34 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/welcome.d.ts.map +1 -1
- package/dist/tui/welcome.js +9 -7
- package/dist/tui/welcome.js.map +1 -1
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/diff.d.ts +5 -0
- package/dist/utils/diff.d.ts.map +1 -1
- package/dist/utils/diff.js +15 -0
- package/dist/utils/diff.js.map +1 -1
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +29 -0
- package/dist/version.js.map +1 -0
- package/package.json +19 -7
- package/CHANGELOG.md +0 -192
- package/DEVELOPMENT.md +0 -286
- package/ROADMAP.md +0 -91
- package/docs/API_KEY_GUIDE.md +0 -236
- package/docs/FAQ.md +0 -182
- package/install.bat +0 -63
- package/install.ps1 +0 -238
- package/install.sh +0 -113
- package/src/app/index.ts +0 -197
- package/src/cache/fingerprint.ts +0 -101
- package/src/cache/index.ts +0 -21
- package/src/cache/smartModel.ts +0 -145
- package/src/cache/stats.ts +0 -199
- package/src/cache/volatile.ts +0 -47
- package/src/cmd/index.tsx +0 -288
- package/src/config/index.ts +0 -158
- package/src/config/providers.ts +0 -234
- package/src/config/types.ts +0 -71
- package/src/hooks/index.ts +0 -111
- package/src/llm/anthropic.ts +0 -413
- package/src/llm/gemini.ts +0 -169
- package/src/llm/index.ts +0 -302
- package/src/llm/openai.ts +0 -243
- package/src/llm/provider.ts +0 -71
- package/src/mcp/client.ts +0 -330
- package/src/prompts/index.ts +0 -260
- package/src/session/compactor.ts +0 -103
- package/src/session/index.ts +0 -181
- package/src/session/message.ts +0 -42
- package/src/skills/brainstorming.ts +0 -43
- package/src/skills/code-review.ts +0 -45
- package/src/skills/executing-plans.ts +0 -27
- package/src/skills/frontend-design.ts +0 -35
- package/src/skills/git-workflow.ts +0 -36
- package/src/skills/improve-architecture.ts +0 -38
- package/src/skills/index.ts +0 -136
- package/src/skills/neuroweave.ts +0 -47
- package/src/skills/playwright.ts +0 -72
- package/src/skills/prototype.ts +0 -30
- package/src/skills/subagent.ts +0 -28
- package/src/skills/supabase.ts +0 -44
- package/src/skills/systematic-debugging.ts +0 -44
- package/src/skills/tdd.ts +0 -39
- package/src/skills/triage.ts +0 -35
- package/src/skills/verification.ts +0 -31
- package/src/skills/writing-plans.ts +0 -42
- package/src/tools/file.ts +0 -143
- package/src/tools/git.ts +0 -132
- package/src/tools/index.ts +0 -111
- package/src/tools/nwt.ts +0 -598
- package/src/tools/search.ts +0 -263
- package/src/tools/shell.ts +0 -136
- package/src/tools/types.ts +0 -122
- package/src/tui/app.tsx +0 -159
- package/src/tui/components/ChatList.tsx +0 -41
- package/src/tui/components/ChatMessage.tsx +0 -54
- package/src/tui/components/Header.tsx +0 -66
- package/src/tui/components/InitWizard.tsx +0 -251
- package/src/tui/components/Markdown.tsx +0 -35
- package/src/tui/components/ModelSelector.tsx +0 -107
- package/src/tui/components/StatusBar.tsx +0 -30
- package/src/tui/components/Thinking.tsx +0 -17
- package/src/tui/components/ToolCall.tsx +0 -102
- package/src/tui/components/UserInput.tsx +0 -151
- package/src/tui/hooks/useChat.ts +0 -287
- package/src/tui/hooks/useCommands.ts +0 -273
- package/src/tui/index.ts +0 -6
- package/src/tui/welcome.ts +0 -177
- package/src/types.ts +0 -104
- package/src/utils/diff.ts +0 -71
- package/src/utils/project.ts +0 -99
- package/src/utils/stableStringify.ts +0 -47
- package/src/utils/thinking.ts +0 -119
- package/tests/cache/fingerprint.test.ts +0 -75
- package/tests/cache/providerCatalog.test.ts +0 -31
- package/tests/cache/smartModel.test.ts +0 -112
- package/tests/cache/stableStringify.test.ts +0 -43
- package/tests/cache/stats.test.ts +0 -146
- package/tests/cache/volatile.test.ts +0 -75
- package/tests/smoke-pollution.mjs +0 -78
- package/tests/smoke-thinking.mjs +0 -110
- package/tests/smoke-tool-stream.mjs +0 -69
- package/tests/smoke-tool.mjs +0 -117
- 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
|
-
}
|
package/src/prompts/index.ts
DELETED
|
@@ -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
|
-
}
|
package/src/session/compactor.ts
DELETED
|
@@ -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
|
-
}
|