codeep 1.2.11 → 1.2.13
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/bin/codeep.js +13 -1
- package/dist/config/providers.d.ts +6 -0
- package/dist/config/providers.js +41 -2
- package/dist/config/providers.test.js +31 -2
- package/dist/utils/agent.js +2 -2
- package/dist/utils/tools.d.ts +64 -4
- package/dist/utils/tools.js +209 -4
- package/dist/utils/tools.test.js +12 -7
- package/package.json +2 -1
- package/dist/app.d.ts +0 -2
- package/dist/app.js +0 -1501
- package/dist/components/AgentActions.d.ts +0 -18
- package/dist/components/AgentActions.js +0 -122
- package/dist/components/AgentProgress.d.ts +0 -59
- package/dist/components/AgentProgress.js +0 -368
- package/dist/components/Export.d.ts +0 -8
- package/dist/components/Export.js +0 -27
- package/dist/components/Help.d.ts +0 -6
- package/dist/components/Help.js +0 -7
- package/dist/components/Input.d.ts +0 -9
- package/dist/components/Input.js +0 -334
- package/dist/components/Loading.d.ts +0 -17
- package/dist/components/Loading.js +0 -52
- package/dist/components/Login.d.ts +0 -7
- package/dist/components/Login.js +0 -77
- package/dist/components/Logo.d.ts +0 -8
- package/dist/components/Logo.js +0 -89
- package/dist/components/LogoutPicker.d.ts +0 -8
- package/dist/components/LogoutPicker.js +0 -61
- package/dist/components/Message.d.ts +0 -10
- package/dist/components/Message.js +0 -242
- package/dist/components/MessageList.d.ts +0 -10
- package/dist/components/MessageList.js +0 -42
- package/dist/components/ProjectPermission.d.ts +0 -7
- package/dist/components/ProjectPermission.js +0 -65
- package/dist/components/Search.d.ts +0 -10
- package/dist/components/Search.js +0 -30
- package/dist/components/SessionPicker.d.ts +0 -9
- package/dist/components/SessionPicker.js +0 -88
- package/dist/components/Sessions.d.ts +0 -12
- package/dist/components/Sessions.js +0 -119
- package/dist/components/Settings.d.ts +0 -9
- package/dist/components/Settings.js +0 -198
- package/dist/components/Spinner.d.ts +0 -34
- package/dist/components/Spinner.js +0 -38
- package/dist/components/Status.d.ts +0 -2
- package/dist/components/Status.js +0 -13
- package/dist/components/StreamingMessage.d.ts +0 -14
- package/dist/components/StreamingMessage.js +0 -19
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -42
package/bin/codeep.js
CHANGED
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { spawn } from 'child_process';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const main = join(__dirname, '..', 'dist', 'renderer', 'main.js');
|
|
8
|
+
|
|
9
|
+
const child = spawn(process.execPath, [main, ...process.argv.slice(2)], {
|
|
10
|
+
stdio: 'inherit',
|
|
11
|
+
env: process.env,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
@@ -25,6 +25,11 @@ export interface ProviderConfig {
|
|
|
25
25
|
defaultProtocol: 'openai' | 'anthropic';
|
|
26
26
|
envKey?: string;
|
|
27
27
|
subscribeUrl?: string;
|
|
28
|
+
mcpEndpoints?: {
|
|
29
|
+
webSearch?: string;
|
|
30
|
+
webReader?: string;
|
|
31
|
+
zread?: string;
|
|
32
|
+
};
|
|
28
33
|
}
|
|
29
34
|
export declare const PROVIDERS: Record<string, ProviderConfig>;
|
|
30
35
|
export type ProviderId = keyof typeof PROVIDERS;
|
|
@@ -42,4 +47,5 @@ export declare function getProviderModels(providerId: string): {
|
|
|
42
47
|
}[];
|
|
43
48
|
export declare function getProviderBaseUrl(providerId: string, protocol: 'openai' | 'anthropic'): string | null;
|
|
44
49
|
export declare function getProviderAuthHeader(providerId: string, protocol: 'openai' | 'anthropic'): 'Bearer' | 'x-api-key';
|
|
50
|
+
export declare function getProviderMcpEndpoints(providerId: string): ProviderConfig['mcpEndpoints'] | null;
|
|
45
51
|
export declare function supportsNativeTools(providerId: string, protocol: 'openai' | 'anthropic'): boolean;
|
package/dist/config/providers.js
CHANGED
|
@@ -26,6 +26,41 @@ export const PROVIDERS = {
|
|
|
26
26
|
defaultProtocol: 'openai',
|
|
27
27
|
envKey: 'ZAI_API_KEY',
|
|
28
28
|
subscribeUrl: 'https://z.ai/subscribe?ic=NXYNXZOV14',
|
|
29
|
+
mcpEndpoints: {
|
|
30
|
+
webSearch: 'https://api.z.ai/api/mcp/web_search_prime/mcp',
|
|
31
|
+
webReader: 'https://api.z.ai/api/mcp/web_reader/mcp',
|
|
32
|
+
zread: 'https://api.z.ai/api/mcp/zread/mcp',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
'z.ai-cn': {
|
|
36
|
+
name: 'Z.AI China (ZhipuAI)',
|
|
37
|
+
description: 'GLM Coding Plan (China)',
|
|
38
|
+
protocols: {
|
|
39
|
+
openai: {
|
|
40
|
+
baseUrl: 'https://open.bigmodel.cn/api/coding/paas/v4',
|
|
41
|
+
authHeader: 'Bearer',
|
|
42
|
+
supportsNativeTools: true,
|
|
43
|
+
},
|
|
44
|
+
anthropic: {
|
|
45
|
+
baseUrl: 'https://open.bigmodel.cn/api/anthropic',
|
|
46
|
+
authHeader: 'x-api-key',
|
|
47
|
+
supportsNativeTools: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
models: [
|
|
51
|
+
{ id: 'glm-5', name: 'GLM-5', description: 'Most capable GLM model (Pro/Max subscription)' },
|
|
52
|
+
{ id: 'glm-4.7', name: 'GLM-4.7', description: 'Latest GLM model' },
|
|
53
|
+
{ id: 'glm-4.7-flash', name: 'GLM-4.7 Flash', description: 'Faster, lighter version' },
|
|
54
|
+
],
|
|
55
|
+
defaultModel: 'glm-4.7',
|
|
56
|
+
defaultProtocol: 'openai',
|
|
57
|
+
envKey: 'ZAI_CN_API_KEY',
|
|
58
|
+
subscribeUrl: 'https://open.bigmodel.cn/glm-coding',
|
|
59
|
+
mcpEndpoints: {
|
|
60
|
+
webSearch: 'https://open.bigmodel.cn/api/mcp/web_search_prime/mcp',
|
|
61
|
+
webReader: 'https://open.bigmodel.cn/api/mcp/web_reader/mcp',
|
|
62
|
+
zread: 'https://open.bigmodel.cn/api/mcp/zread/mcp',
|
|
63
|
+
},
|
|
29
64
|
},
|
|
30
65
|
'minimax': {
|
|
31
66
|
name: 'MiniMax',
|
|
@@ -43,9 +78,9 @@ export const PROVIDERS = {
|
|
|
43
78
|
},
|
|
44
79
|
},
|
|
45
80
|
models: [
|
|
46
|
-
{ id: 'MiniMax-M2.
|
|
81
|
+
{ id: 'MiniMax-M2.5', name: 'MiniMax M2.5', description: 'Latest MiniMax coding model' },
|
|
47
82
|
],
|
|
48
|
-
defaultModel: 'MiniMax-M2.
|
|
83
|
+
defaultModel: 'MiniMax-M2.5',
|
|
49
84
|
defaultProtocol: 'anthropic',
|
|
50
85
|
envKey: 'MINIMAX_API_KEY',
|
|
51
86
|
subscribeUrl: 'https://platform.minimax.io/subscribe/coding-plan?code=2lWvoWUhrp&source=link',
|
|
@@ -116,6 +151,10 @@ export function getProviderAuthHeader(providerId, protocol) {
|
|
|
116
151
|
return 'Bearer';
|
|
117
152
|
return provider.protocols[protocol]?.authHeader || 'Bearer';
|
|
118
153
|
}
|
|
154
|
+
export function getProviderMcpEndpoints(providerId) {
|
|
155
|
+
const provider = PROVIDERS[providerId];
|
|
156
|
+
return provider?.mcpEndpoints || null;
|
|
157
|
+
}
|
|
119
158
|
export function supportsNativeTools(providerId, protocol) {
|
|
120
159
|
const provider = PROVIDERS[providerId];
|
|
121
160
|
if (!provider)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { PROVIDERS, getProvider, getProviderList, getProviderModels, getProviderBaseUrl, getProviderAuthHeader, } from './providers.js';
|
|
2
|
+
import { PROVIDERS, getProvider, getProviderList, getProviderModels, getProviderBaseUrl, getProviderAuthHeader, getProviderMcpEndpoints, } from './providers.js';
|
|
3
3
|
describe('providers', () => {
|
|
4
4
|
describe('PROVIDERS constant', () => {
|
|
5
5
|
it('should have z.ai provider', () => {
|
|
6
6
|
expect(PROVIDERS['z.ai']).toBeDefined();
|
|
7
7
|
expect(PROVIDERS['z.ai'].name).toBe('Z.AI (ZhipuAI)');
|
|
8
8
|
});
|
|
9
|
+
it('should have z.ai-cn provider', () => {
|
|
10
|
+
expect(PROVIDERS['z.ai-cn']).toBeDefined();
|
|
11
|
+
expect(PROVIDERS['z.ai-cn'].name).toBe('Z.AI China (ZhipuAI)');
|
|
12
|
+
});
|
|
9
13
|
it('should have minimax provider', () => {
|
|
10
14
|
expect(PROVIDERS['minimax']).toBeDefined();
|
|
11
15
|
expect(PROVIDERS['minimax'].name).toBe('MiniMax');
|
|
@@ -68,10 +72,11 @@ describe('providers', () => {
|
|
|
68
72
|
expect(item.description).toBeDefined();
|
|
69
73
|
}
|
|
70
74
|
});
|
|
71
|
-
it('should include z.ai and minimax', () => {
|
|
75
|
+
it('should include z.ai, z.ai-cn, and minimax', () => {
|
|
72
76
|
const list = getProviderList();
|
|
73
77
|
const ids = list.map(p => p.id);
|
|
74
78
|
expect(ids).toContain('z.ai');
|
|
79
|
+
expect(ids).toContain('z.ai-cn');
|
|
75
80
|
expect(ids).toContain('minimax');
|
|
76
81
|
});
|
|
77
82
|
});
|
|
@@ -125,8 +130,32 @@ describe('providers', () => {
|
|
|
125
130
|
it('should have env key for z.ai', () => {
|
|
126
131
|
expect(PROVIDERS['z.ai'].envKey).toBe('ZAI_API_KEY');
|
|
127
132
|
});
|
|
133
|
+
it('should have env key for z.ai-cn', () => {
|
|
134
|
+
expect(PROVIDERS['z.ai-cn'].envKey).toBe('ZAI_CN_API_KEY');
|
|
135
|
+
});
|
|
128
136
|
it('should have env key for minimax', () => {
|
|
129
137
|
expect(PROVIDERS['minimax'].envKey).toBe('MINIMAX_API_KEY');
|
|
130
138
|
});
|
|
131
139
|
});
|
|
140
|
+
describe('MCP endpoints', () => {
|
|
141
|
+
it('should have MCP endpoints for z.ai', () => {
|
|
142
|
+
const endpoints = getProviderMcpEndpoints('z.ai');
|
|
143
|
+
expect(endpoints).not.toBeNull();
|
|
144
|
+
expect(endpoints.webSearch).toContain('api.z.ai');
|
|
145
|
+
expect(endpoints.webReader).toContain('api.z.ai');
|
|
146
|
+
expect(endpoints.zread).toContain('api.z.ai');
|
|
147
|
+
});
|
|
148
|
+
it('should have MCP endpoints for z.ai-cn', () => {
|
|
149
|
+
const endpoints = getProviderMcpEndpoints('z.ai-cn');
|
|
150
|
+
expect(endpoints).not.toBeNull();
|
|
151
|
+
expect(endpoints.webSearch).toContain('open.bigmodel.cn');
|
|
152
|
+
expect(endpoints.webReader).toContain('open.bigmodel.cn');
|
|
153
|
+
expect(endpoints.zread).toContain('open.bigmodel.cn');
|
|
154
|
+
});
|
|
155
|
+
it('should return null for providers without MCP endpoints', () => {
|
|
156
|
+
expect(getProviderMcpEndpoints('minimax')).toBeNull();
|
|
157
|
+
expect(getProviderMcpEndpoints('deepseek')).toBeNull();
|
|
158
|
+
expect(getProviderMcpEndpoints('nonexistent')).toBeNull();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
132
161
|
});
|
package/dist/utils/agent.js
CHANGED
|
@@ -919,7 +919,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
919
919
|
}
|
|
920
920
|
else {
|
|
921
921
|
// Actually execute the tool
|
|
922
|
-
toolResult = executeTool(toolCall, projectContext.root || process.cwd());
|
|
922
|
+
toolResult = await executeTool(toolCall, projectContext.root || process.cwd());
|
|
923
923
|
}
|
|
924
924
|
opts.onToolResult?.(toolResult, toolCall);
|
|
925
925
|
// Log action
|
|
@@ -1010,7 +1010,7 @@ export async function runAgent(prompt, projectContext, options = {}) {
|
|
|
1010
1010
|
const fixResults = [];
|
|
1011
1011
|
for (const toolCall of fixToolCalls) {
|
|
1012
1012
|
opts.onToolCall?.(toolCall);
|
|
1013
|
-
const toolResult = executeTool(toolCall, projectContext.root || process.cwd());
|
|
1013
|
+
const toolResult = await executeTool(toolCall, projectContext.root || process.cwd());
|
|
1014
1014
|
opts.onToolResult?.(toolResult, toolCall);
|
|
1015
1015
|
const actionLog = createActionLog(toolCall, toolResult);
|
|
1016
1016
|
actions.push(actionLog);
|
package/dist/utils/tools.d.ts
CHANGED
|
@@ -199,10 +199,70 @@ export declare const AGENT_TOOLS: {
|
|
|
199
199
|
};
|
|
200
200
|
};
|
|
201
201
|
};
|
|
202
|
+
web_search: {
|
|
203
|
+
name: string;
|
|
204
|
+
description: string;
|
|
205
|
+
parameters: {
|
|
206
|
+
query: {
|
|
207
|
+
type: string;
|
|
208
|
+
description: string;
|
|
209
|
+
required: boolean;
|
|
210
|
+
};
|
|
211
|
+
domain_filter: {
|
|
212
|
+
type: string;
|
|
213
|
+
description: string;
|
|
214
|
+
required: boolean;
|
|
215
|
+
};
|
|
216
|
+
recency: {
|
|
217
|
+
type: string;
|
|
218
|
+
description: string;
|
|
219
|
+
required: boolean;
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
web_read: {
|
|
224
|
+
name: string;
|
|
225
|
+
description: string;
|
|
226
|
+
parameters: {
|
|
227
|
+
url: {
|
|
228
|
+
type: string;
|
|
229
|
+
description: string;
|
|
230
|
+
required: boolean;
|
|
231
|
+
};
|
|
232
|
+
format: {
|
|
233
|
+
type: string;
|
|
234
|
+
description: string;
|
|
235
|
+
required: boolean;
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
github_read: {
|
|
240
|
+
name: string;
|
|
241
|
+
description: string;
|
|
242
|
+
parameters: {
|
|
243
|
+
repo: {
|
|
244
|
+
type: string;
|
|
245
|
+
description: string;
|
|
246
|
+
required: boolean;
|
|
247
|
+
};
|
|
248
|
+
action: {
|
|
249
|
+
type: string;
|
|
250
|
+
description: string;
|
|
251
|
+
required: boolean;
|
|
252
|
+
};
|
|
253
|
+
query: {
|
|
254
|
+
type: string;
|
|
255
|
+
description: string;
|
|
256
|
+
required: boolean;
|
|
257
|
+
};
|
|
258
|
+
path: {
|
|
259
|
+
type: string;
|
|
260
|
+
description: string;
|
|
261
|
+
required: boolean;
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
};
|
|
202
265
|
};
|
|
203
|
-
/**
|
|
204
|
-
* Format tool definitions for system prompt (text-based fallback)
|
|
205
|
-
*/
|
|
206
266
|
export declare function formatToolDefinitions(): string;
|
|
207
267
|
/**
|
|
208
268
|
* Get tools in OpenAI Function Calling format
|
|
@@ -230,7 +290,7 @@ export declare function parseToolCalls(response: string): ToolCall[];
|
|
|
230
290
|
/**
|
|
231
291
|
* Execute a tool call
|
|
232
292
|
*/
|
|
233
|
-
export declare function executeTool(toolCall: ToolCall, projectRoot: string): ToolResult
|
|
293
|
+
export declare function executeTool(toolCall: ToolCall, projectRoot: string): Promise<ToolResult>;
|
|
234
294
|
/**
|
|
235
295
|
* Create action log from tool result
|
|
236
296
|
*/
|
package/dist/utils/tools.js
CHANGED
|
@@ -12,6 +12,84 @@ import { join, dirname, relative, resolve, isAbsolute } from 'path';
|
|
|
12
12
|
import { executeCommand } from './shell.js';
|
|
13
13
|
import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history.js';
|
|
14
14
|
import { loadIgnoreRules, isIgnored } from './gitignore.js';
|
|
15
|
+
import { config, getApiKey } from '../config/index.js';
|
|
16
|
+
import { getProviderMcpEndpoints } from '../config/providers.js';
|
|
17
|
+
// Z.AI MCP tool names (available when user has any Z.AI API key)
|
|
18
|
+
const ZAI_MCP_TOOLS = ['web_search', 'web_read', 'github_read'];
|
|
19
|
+
// Z.AI provider IDs that have MCP endpoints
|
|
20
|
+
const ZAI_PROVIDER_IDS = ['z.ai', 'z.ai-cn'];
|
|
21
|
+
/**
|
|
22
|
+
* Find a Z.AI provider that has an API key configured.
|
|
23
|
+
* Returns the provider ID and API key, or null if none found.
|
|
24
|
+
* Prefers the active provider if it's Z.AI, otherwise checks all Z.AI providers.
|
|
25
|
+
*/
|
|
26
|
+
function getZaiMcpConfig() {
|
|
27
|
+
// First check if active provider is Z.AI
|
|
28
|
+
const activeProvider = config.get('provider');
|
|
29
|
+
if (ZAI_PROVIDER_IDS.includes(activeProvider)) {
|
|
30
|
+
const key = getApiKey(activeProvider);
|
|
31
|
+
const endpoints = getProviderMcpEndpoints(activeProvider);
|
|
32
|
+
if (key && endpoints?.webSearch && endpoints?.webReader && endpoints?.zread) {
|
|
33
|
+
return { providerId: activeProvider, apiKey: key, endpoints: endpoints };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Otherwise check all Z.AI providers for a configured key
|
|
37
|
+
for (const pid of ZAI_PROVIDER_IDS) {
|
|
38
|
+
const key = getApiKey(pid);
|
|
39
|
+
const endpoints = getProviderMcpEndpoints(pid);
|
|
40
|
+
if (key && endpoints?.webSearch && endpoints?.webReader && endpoints?.zread) {
|
|
41
|
+
return { providerId: pid, apiKey: key, endpoints: endpoints };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if Z.AI MCP tools are available (user has any Z.AI API key)
|
|
48
|
+
*/
|
|
49
|
+
function hasZaiMcpAccess() {
|
|
50
|
+
return getZaiMcpConfig() !== null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Call a Z.AI MCP endpoint via JSON-RPC 2.0
|
|
54
|
+
*/
|
|
55
|
+
async function callZaiMcp(endpoint, toolName, args, apiKey) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
const timeout = setTimeout(() => controller.abort(), 60000);
|
|
58
|
+
try {
|
|
59
|
+
const response = await fetch(endpoint, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: {
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
'Accept': 'application/json',
|
|
64
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
jsonrpc: '2.0',
|
|
68
|
+
id: Date.now().toString(),
|
|
69
|
+
method: 'tools/call',
|
|
70
|
+
params: { name: toolName, arguments: args },
|
|
71
|
+
}),
|
|
72
|
+
signal: controller.signal,
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const errorText = await response.text().catch(() => '');
|
|
76
|
+
throw new Error(`MCP error ${response.status}: ${errorText || response.statusText}`);
|
|
77
|
+
}
|
|
78
|
+
const data = await response.json();
|
|
79
|
+
if (data.error) {
|
|
80
|
+
throw new Error(data.error.message || JSON.stringify(data.error));
|
|
81
|
+
}
|
|
82
|
+
// MCP returns result.content as array of {type, text} blocks
|
|
83
|
+
const content = data.result?.content;
|
|
84
|
+
if (Array.isArray(content)) {
|
|
85
|
+
return content.map((c) => c.text || '').join('\n');
|
|
86
|
+
}
|
|
87
|
+
return typeof data.result === 'string' ? data.result : JSON.stringify(data.result);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
15
93
|
// Tool definitions for system prompt
|
|
16
94
|
export const AGENT_TOOLS = {
|
|
17
95
|
read_file: {
|
|
@@ -91,13 +169,51 @@ export const AGENT_TOOLS = {
|
|
|
91
169
|
url: { type: 'string', description: 'The URL to fetch content from', required: true },
|
|
92
170
|
},
|
|
93
171
|
},
|
|
172
|
+
web_search: {
|
|
173
|
+
name: 'web_search',
|
|
174
|
+
description: 'Search the web for real-time information. Returns titles, URLs, and summaries. Requires a Z.AI API key.',
|
|
175
|
+
parameters: {
|
|
176
|
+
query: { type: 'string', description: 'Search query', required: true },
|
|
177
|
+
domain_filter: { type: 'string', description: 'Limit results to specific domain (e.g. github.com)', required: false },
|
|
178
|
+
recency: { type: 'string', description: 'Time filter: oneDay, oneWeek, oneMonth, oneYear, noLimit', required: false },
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
web_read: {
|
|
182
|
+
name: 'web_read',
|
|
183
|
+
description: 'Fetch and parse a web page into clean readable text/markdown. Better than fetch_url for documentation and articles. Requires a Z.AI API key.',
|
|
184
|
+
parameters: {
|
|
185
|
+
url: { type: 'string', description: 'The URL to read', required: true },
|
|
186
|
+
format: { type: 'string', description: 'Output format: markdown or text (default: markdown)', required: false },
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
github_read: {
|
|
190
|
+
name: 'github_read',
|
|
191
|
+
description: 'Search documentation/code or read files from a public GitHub repository. Requires a Z.AI API key.',
|
|
192
|
+
parameters: {
|
|
193
|
+
repo: { type: 'string', description: 'GitHub repository in owner/repo format (e.g. facebook/react)', required: true },
|
|
194
|
+
action: { type: 'string', description: 'Action: search, tree, or read_file', required: true },
|
|
195
|
+
query: { type: 'string', description: 'Search query (for action=search)', required: false },
|
|
196
|
+
path: { type: 'string', description: 'File path (for action=read_file) or directory path (for action=tree)', required: false },
|
|
197
|
+
},
|
|
198
|
+
},
|
|
94
199
|
};
|
|
95
200
|
/**
|
|
96
201
|
* Format tool definitions for system prompt (text-based fallback)
|
|
97
202
|
*/
|
|
203
|
+
/**
|
|
204
|
+
* Get filtered tool entries (excludes Z.AI-only tools when not using Z.AI provider)
|
|
205
|
+
*/
|
|
206
|
+
function getFilteredToolEntries() {
|
|
207
|
+
const hasMcp = hasZaiMcpAccess();
|
|
208
|
+
return Object.entries(AGENT_TOOLS).filter(([name]) => {
|
|
209
|
+
if (ZAI_MCP_TOOLS.includes(name))
|
|
210
|
+
return hasMcp;
|
|
211
|
+
return true;
|
|
212
|
+
});
|
|
213
|
+
}
|
|
98
214
|
export function formatToolDefinitions() {
|
|
99
215
|
const lines = [];
|
|
100
|
-
for (const [name, tool] of
|
|
216
|
+
for (const [name, tool] of getFilteredToolEntries()) {
|
|
101
217
|
lines.push(`### ${name}`);
|
|
102
218
|
lines.push(tool.description);
|
|
103
219
|
lines.push('Parameters:');
|
|
@@ -113,7 +229,7 @@ export function formatToolDefinitions() {
|
|
|
113
229
|
* Get tools in OpenAI Function Calling format
|
|
114
230
|
*/
|
|
115
231
|
export function getOpenAITools() {
|
|
116
|
-
return
|
|
232
|
+
return getFilteredToolEntries().map(([name, tool]) => {
|
|
117
233
|
const properties = {};
|
|
118
234
|
const required = [];
|
|
119
235
|
for (const [param, info] of Object.entries(tool.parameters)) {
|
|
@@ -153,7 +269,7 @@ export function getOpenAITools() {
|
|
|
153
269
|
* Get tools in Anthropic Tool Use format
|
|
154
270
|
*/
|
|
155
271
|
export function getAnthropicTools() {
|
|
156
|
-
return
|
|
272
|
+
return getFilteredToolEntries().map(([name, tool]) => {
|
|
157
273
|
const properties = {};
|
|
158
274
|
const required = [];
|
|
159
275
|
for (const [param, info] of Object.entries(tool.parameters)) {
|
|
@@ -609,7 +725,7 @@ function validatePath(path, projectRoot) {
|
|
|
609
725
|
/**
|
|
610
726
|
* Execute a tool call
|
|
611
727
|
*/
|
|
612
|
-
export function executeTool(toolCall, projectRoot) {
|
|
728
|
+
export async function executeTool(toolCall, projectRoot) {
|
|
613
729
|
// Normalize tool name to handle case variations (WRITE_FILE -> write_file)
|
|
614
730
|
const tool = normalizeToolName(toolCall.tool);
|
|
615
731
|
const parameters = toolCall.parameters;
|
|
@@ -899,6 +1015,90 @@ export function executeTool(toolCall, projectRoot) {
|
|
|
899
1015
|
return { success: false, output: '', error: result.stderr || 'Failed to fetch URL', tool, parameters };
|
|
900
1016
|
}
|
|
901
1017
|
}
|
|
1018
|
+
// === Z.AI MCP Tools ===
|
|
1019
|
+
case 'web_search': {
|
|
1020
|
+
const mcpConfig = getZaiMcpConfig();
|
|
1021
|
+
if (!mcpConfig) {
|
|
1022
|
+
return { success: false, output: '', error: 'web_search requires a Z.AI API key. Configure one via /provider z.ai', tool, parameters };
|
|
1023
|
+
}
|
|
1024
|
+
const query = parameters.query;
|
|
1025
|
+
if (!query) {
|
|
1026
|
+
return { success: false, output: '', error: 'Missing required parameter: query', tool, parameters };
|
|
1027
|
+
}
|
|
1028
|
+
const args = { search_query: query };
|
|
1029
|
+
if (parameters.domain_filter)
|
|
1030
|
+
args.search_domain_filter = parameters.domain_filter;
|
|
1031
|
+
if (parameters.recency)
|
|
1032
|
+
args.search_recency_filter = parameters.recency;
|
|
1033
|
+
const result = await callZaiMcp(mcpConfig.endpoints.webSearch, 'webSearchPrime', args, mcpConfig.apiKey);
|
|
1034
|
+
const output = result.length > 15000 ? result.substring(0, 15000) + '\n\n... (truncated)' : result;
|
|
1035
|
+
return { success: true, output, tool, parameters };
|
|
1036
|
+
}
|
|
1037
|
+
case 'web_read': {
|
|
1038
|
+
const mcpConfig = getZaiMcpConfig();
|
|
1039
|
+
if (!mcpConfig) {
|
|
1040
|
+
return { success: false, output: '', error: 'web_read requires a Z.AI API key. Configure one via /provider z.ai', tool, parameters };
|
|
1041
|
+
}
|
|
1042
|
+
const url = parameters.url;
|
|
1043
|
+
if (!url) {
|
|
1044
|
+
return { success: false, output: '', error: 'Missing required parameter: url', tool, parameters };
|
|
1045
|
+
}
|
|
1046
|
+
try {
|
|
1047
|
+
new URL(url);
|
|
1048
|
+
}
|
|
1049
|
+
catch {
|
|
1050
|
+
return { success: false, output: '', error: 'Invalid URL format', tool, parameters };
|
|
1051
|
+
}
|
|
1052
|
+
const args = { url };
|
|
1053
|
+
if (parameters.format)
|
|
1054
|
+
args.return_format = parameters.format;
|
|
1055
|
+
const result = await callZaiMcp(mcpConfig.endpoints.webReader, 'webReader', args, mcpConfig.apiKey);
|
|
1056
|
+
const output = result.length > 15000 ? result.substring(0, 15000) + '\n\n... (truncated)' : result;
|
|
1057
|
+
return { success: true, output, tool, parameters };
|
|
1058
|
+
}
|
|
1059
|
+
case 'github_read': {
|
|
1060
|
+
const mcpConfig = getZaiMcpConfig();
|
|
1061
|
+
if (!mcpConfig) {
|
|
1062
|
+
return { success: false, output: '', error: 'github_read requires a Z.AI API key. Configure one via /provider z.ai', tool, parameters };
|
|
1063
|
+
}
|
|
1064
|
+
const repo = parameters.repo;
|
|
1065
|
+
const action = parameters.action;
|
|
1066
|
+
if (!repo) {
|
|
1067
|
+
return { success: false, output: '', error: 'Missing required parameter: repo', tool, parameters };
|
|
1068
|
+
}
|
|
1069
|
+
if (!repo.includes('/')) {
|
|
1070
|
+
return { success: false, output: '', error: 'Invalid repo format. Use owner/repo (e.g. facebook/react)', tool, parameters };
|
|
1071
|
+
}
|
|
1072
|
+
if (!action || !['search', 'tree', 'read_file'].includes(action)) {
|
|
1073
|
+
return { success: false, output: '', error: 'Invalid action. Must be: search, tree, or read_file', tool, parameters };
|
|
1074
|
+
}
|
|
1075
|
+
let mcpToolName;
|
|
1076
|
+
const args = { repo_name: repo };
|
|
1077
|
+
if (action === 'search') {
|
|
1078
|
+
mcpToolName = 'search_doc';
|
|
1079
|
+
const query = parameters.query;
|
|
1080
|
+
if (!query) {
|
|
1081
|
+
return { success: false, output: '', error: 'Missing required parameter: query (for action=search)', tool, parameters };
|
|
1082
|
+
}
|
|
1083
|
+
args.query = query;
|
|
1084
|
+
}
|
|
1085
|
+
else if (action === 'tree') {
|
|
1086
|
+
mcpToolName = 'get_repo_structure';
|
|
1087
|
+
if (parameters.path)
|
|
1088
|
+
args.dir_path = parameters.path;
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
mcpToolName = 'read_file';
|
|
1092
|
+
const filePath = parameters.path;
|
|
1093
|
+
if (!filePath) {
|
|
1094
|
+
return { success: false, output: '', error: 'Missing required parameter: path (for action=read_file)', tool, parameters };
|
|
1095
|
+
}
|
|
1096
|
+
args.file_path = filePath;
|
|
1097
|
+
}
|
|
1098
|
+
const result = await callZaiMcp(mcpConfig.endpoints.zread, mcpToolName, args, mcpConfig.apiKey);
|
|
1099
|
+
const output = result.length > 15000 ? result.substring(0, 15000) + '\n\n... (truncated)' : result;
|
|
1100
|
+
return { success: true, output, tool, parameters };
|
|
1101
|
+
}
|
|
902
1102
|
default:
|
|
903
1103
|
return { success: false, output: '', error: `Unknown tool: ${tool}`, tool, parameters };
|
|
904
1104
|
}
|
|
@@ -1014,11 +1214,16 @@ export function createActionLog(toolCall, result) {
|
|
|
1014
1214
|
create_directory: 'mkdir',
|
|
1015
1215
|
find_files: 'search',
|
|
1016
1216
|
fetch_url: 'fetch',
|
|
1217
|
+
web_search: 'fetch',
|
|
1218
|
+
web_read: 'fetch',
|
|
1219
|
+
github_read: 'fetch',
|
|
1017
1220
|
};
|
|
1018
1221
|
const target = toolCall.parameters.path ||
|
|
1019
1222
|
toolCall.parameters.command ||
|
|
1020
1223
|
toolCall.parameters.pattern ||
|
|
1021
1224
|
toolCall.parameters.url ||
|
|
1225
|
+
toolCall.parameters.query ||
|
|
1226
|
+
toolCall.parameters.repo ||
|
|
1022
1227
|
'unknown';
|
|
1023
1228
|
// For write/edit actions, include FULL content in details for live code view
|
|
1024
1229
|
let details;
|
package/dist/utils/tools.test.js
CHANGED
|
@@ -2,11 +2,15 @@ import { describe, it, expect } from 'vitest';
|
|
|
2
2
|
import { getOpenAITools, getAnthropicTools, parseToolCalls, parseOpenAIToolCalls, parseAnthropicToolCalls, createActionLog, AGENT_TOOLS, } from './tools.js';
|
|
3
3
|
// ─── CONSTANTS ───────────────────────────────────────────────────────────────
|
|
4
4
|
const ALL_TOOL_NAMES = Object.keys(AGENT_TOOLS);
|
|
5
|
+
// MCP tools are filtered out when no Z.AI API key is configured (e.g. in tests)
|
|
6
|
+
const ZAI_MCP_TOOLS = ['web_search', 'web_read', 'github_read'];
|
|
7
|
+
const CORE_TOOL_NAMES = ALL_TOOL_NAMES.filter(n => !ZAI_MCP_TOOLS.includes(n));
|
|
5
8
|
// ─── getOpenAITools ──────────────────────────────────────────────────────────
|
|
6
9
|
describe('getOpenAITools', () => {
|
|
7
10
|
it('should return one entry per AGENT_TOOLS definition', () => {
|
|
8
11
|
const tools = getOpenAITools();
|
|
9
|
-
|
|
12
|
+
// MCP tools are excluded when no Z.AI API key is configured
|
|
13
|
+
expect(tools).toHaveLength(CORE_TOOL_NAMES.length);
|
|
10
14
|
});
|
|
11
15
|
it('should wrap every tool in the OpenAI function-calling envelope', () => {
|
|
12
16
|
const tools = getOpenAITools();
|
|
@@ -20,10 +24,10 @@ describe('getOpenAITools', () => {
|
|
|
20
24
|
expect(Array.isArray(tool.function.parameters.required)).toBe(true);
|
|
21
25
|
}
|
|
22
26
|
});
|
|
23
|
-
it('should include all tool names from AGENT_TOOLS', () => {
|
|
27
|
+
it('should include all core tool names from AGENT_TOOLS', () => {
|
|
24
28
|
const tools = getOpenAITools();
|
|
25
29
|
const names = tools.map(t => t.function.name);
|
|
26
|
-
for (const name of
|
|
30
|
+
for (const name of CORE_TOOL_NAMES) {
|
|
27
31
|
expect(names).toContain(name);
|
|
28
32
|
}
|
|
29
33
|
});
|
|
@@ -71,7 +75,8 @@ describe('getOpenAITools', () => {
|
|
|
71
75
|
describe('getAnthropicTools', () => {
|
|
72
76
|
it('should return one entry per AGENT_TOOLS definition', () => {
|
|
73
77
|
const tools = getAnthropicTools();
|
|
74
|
-
|
|
78
|
+
// MCP tools are excluded when no Z.AI API key is configured
|
|
79
|
+
expect(tools).toHaveLength(CORE_TOOL_NAMES.length);
|
|
75
80
|
});
|
|
76
81
|
it('should use Anthropic tool-use shape (name, description, input_schema)', () => {
|
|
77
82
|
const tools = getAnthropicTools();
|
|
@@ -84,17 +89,17 @@ describe('getAnthropicTools', () => {
|
|
|
84
89
|
expect(Array.isArray(tool.input_schema.required)).toBe(true);
|
|
85
90
|
}
|
|
86
91
|
});
|
|
87
|
-
it('should include all tool names from AGENT_TOOLS', () => {
|
|
92
|
+
it('should include all core tool names from AGENT_TOOLS', () => {
|
|
88
93
|
const tools = getAnthropicTools();
|
|
89
94
|
const names = tools.map(t => t.name);
|
|
90
|
-
for (const name of
|
|
95
|
+
for (const name of CORE_TOOL_NAMES) {
|
|
91
96
|
expect(names).toContain(name);
|
|
92
97
|
}
|
|
93
98
|
});
|
|
94
99
|
it('should have same required params as OpenAI format', () => {
|
|
95
100
|
const openai = getOpenAITools();
|
|
96
101
|
const anthropic = getAnthropicTools();
|
|
97
|
-
for (const name of
|
|
102
|
+
for (const name of CORE_TOOL_NAMES) {
|
|
98
103
|
const oTool = openai.find(t => t.function.name === name);
|
|
99
104
|
const aTool = anthropic.find(t => t.name === name);
|
|
100
105
|
expect(aTool.input_schema.required).toEqual(oTool.function.parameters.required);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
4
4
|
"description": "AI-powered coding assistant built for the terminal. Multiple LLM providers, project-aware context, and a seamless development workflow.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"dev": "node --import tsx src/renderer/main.ts",
|
|
12
|
+
"prepack": "tsc; node scripts/fix-imports.js",
|
|
12
13
|
"build": "tsc && node scripts/fix-imports.js",
|
|
13
14
|
"start": "node dist/renderer/main.js",
|
|
14
15
|
"demo:renderer": "node --import tsx src/renderer/demo.ts",
|
package/dist/app.d.ts
DELETED