codeep 1.2.11 → 1.2.12
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/dist/api/index.js +7 -7
- package/dist/config/index.js +3 -3
- 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/hooks/index.js +1 -1
- package/dist/hooks/useAgent.js +3 -3
- package/dist/renderer/App.js +8 -8
- package/dist/renderer/ChatUI.js +3 -3
- package/dist/renderer/Screen.js +1 -1
- package/dist/renderer/components/Export.js +1 -1
- package/dist/renderer/components/Help.js +1 -1
- package/dist/renderer/components/Intro.js +1 -1
- package/dist/renderer/components/Login.js +3 -3
- package/dist/renderer/components/Logout.js +1 -1
- package/dist/renderer/components/Modal.js +2 -2
- package/dist/renderer/components/Permission.js +2 -2
- package/dist/renderer/components/Search.js +1 -1
- package/dist/renderer/components/SelectScreen.js +1 -1
- package/dist/renderer/components/Settings.js +3 -3
- package/dist/renderer/components/Status.js +1 -1
- package/dist/renderer/demo-app.js +1 -1
- package/dist/renderer/demo.js +1 -1
- package/dist/renderer/index.js +9 -9
- package/dist/renderer/main.js +31 -31
- package/dist/utils/agent.js +10 -10
- package/dist/utils/agent.test.js +1 -1
- package/dist/utils/codeReview.js +1 -1
- package/dist/utils/context.js +1 -1
- package/dist/utils/git.test.js +1 -1
- package/dist/utils/gitignore.test.js +1 -1
- package/dist/utils/keychain.js +1 -1
- package/dist/utils/project.test.js +1 -1
- package/dist/utils/ratelimit.js +1 -1
- package/dist/utils/ratelimit.test.js +1 -1
- package/dist/utils/retry.test.js +1 -1
- package/dist/utils/smartContext.js +1 -1
- package/dist/utils/smartContext.test.js +1 -1
- package/dist/utils/taskPlanner.js +2 -2
- package/dist/utils/tools.d.ts +64 -4
- package/dist/utils/tools.js +212 -7
- package/dist/utils/tools.test.js +1 -1
- package/dist/utils/validation.test.js +1 -1
- package/dist/utils/verify.js +1 -1
- package/package.json +1 -1
- package/bin/codeep.js +0 -2
- 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/dist/utils/agent.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { existsSync, readFileSync } from 'fs';
|
|
5
5
|
import { join } from 'path';
|
|
6
|
-
import { recordTokenUsage, extractOpenAIUsage, extractAnthropicUsage } from './tokenTracker
|
|
6
|
+
import { recordTokenUsage, extractOpenAIUsage, extractAnthropicUsage } from './tokenTracker';
|
|
7
7
|
// Debug logging helper - only logs when CODEEP_DEBUG=1
|
|
8
8
|
const debug = (...args) => {
|
|
9
9
|
if (process.env.CODEEP_DEBUG === '1') {
|
|
@@ -38,13 +38,13 @@ function calculateDynamicTimeout(prompt, iteration, baseTimeout) {
|
|
|
38
38
|
const calculatedTimeout = baseTimeout * multiplier;
|
|
39
39
|
return Math.min(Math.max(calculatedTimeout, 120000), 300000);
|
|
40
40
|
}
|
|
41
|
-
import { parseToolCalls, executeTool, createActionLog, formatToolDefinitions, getOpenAITools, getAnthropicTools, parseOpenAIToolCalls, parseAnthropicToolCalls } from './tools
|
|
42
|
-
import { config, getApiKey } from '../config/index
|
|
43
|
-
import { getProviderBaseUrl, getProviderAuthHeader, supportsNativeTools } from '../config/providers
|
|
44
|
-
import { startSession, endSession, undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession } from './history
|
|
45
|
-
import { runAllVerifications, formatErrorsForAgent, hasVerificationErrors, getVerificationSummary } from './verify
|
|
46
|
-
import { gatherSmartContext, formatSmartContext, extractTargetFile } from './smartContext
|
|
47
|
-
import { planTasks, formatTaskPlan } from './taskPlanner
|
|
41
|
+
import { parseToolCalls, executeTool, createActionLog, formatToolDefinitions, getOpenAITools, getAnthropicTools, parseOpenAIToolCalls, parseAnthropicToolCalls } from './tools';
|
|
42
|
+
import { config, getApiKey } from '../config/index';
|
|
43
|
+
import { getProviderBaseUrl, getProviderAuthHeader, supportsNativeTools } from '../config/providers';
|
|
44
|
+
import { startSession, endSession, undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession } from './history';
|
|
45
|
+
import { runAllVerifications, formatErrorsForAgent, hasVerificationErrors, getVerificationSummary } from './verify';
|
|
46
|
+
import { gatherSmartContext, formatSmartContext, extractTargetFile } from './smartContext';
|
|
47
|
+
import { planTasks, formatTaskPlan } from './taskPlanner';
|
|
48
48
|
const DEFAULT_OPTIONS = {
|
|
49
49
|
maxIterations: 100, // Increased for large tasks
|
|
50
50
|
maxDuration: 20 * 60 * 1000, // 20 minutes
|
|
@@ -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/agent.test.js
CHANGED
|
@@ -11,7 +11,7 @@ vi.mock('fs', async (importOriginal) => {
|
|
|
11
11
|
});
|
|
12
12
|
import { existsSync, readFileSync } from 'fs';
|
|
13
13
|
import { join } from 'path';
|
|
14
|
-
import { loadProjectRules, formatAgentResult } from './agent
|
|
14
|
+
import { loadProjectRules, formatAgentResult } from './agent';
|
|
15
15
|
// Cast mocked functions for convenience
|
|
16
16
|
const mockExistsSync = existsSync;
|
|
17
17
|
const mockReadFileSync = readFileSync;
|
package/dist/utils/codeReview.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
5
5
|
import { join, extname, relative } from 'path';
|
|
6
|
-
import { getChangedFiles } from './git
|
|
6
|
+
import { getChangedFiles } from './git';
|
|
7
7
|
// Common code patterns that indicate issues
|
|
8
8
|
const CODE_PATTERNS = [
|
|
9
9
|
// Security issues
|
package/dist/utils/context.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from 'fs';
|
|
5
5
|
import { join, basename } from 'path';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
|
-
import { logger } from './logger
|
|
7
|
+
import { logger } from './logger';
|
|
8
8
|
// Context storage directory
|
|
9
9
|
const CONTEXT_DIR = join(homedir(), '.codeep', 'contexts');
|
|
10
10
|
/**
|
package/dist/utils/git.test.js
CHANGED
|
@@ -3,7 +3,7 @@ import { execSync } from 'child_process';
|
|
|
3
3
|
import { mkdirSync, rmSync, writeFileSync } from 'fs';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import { tmpdir } from 'os';
|
|
6
|
-
import { isGitRepository, getGitStatus, getGitDiff, getChangedFiles, suggestCommitMessage, createCommit, stageAll, formatDiffForDisplay, } from './git
|
|
6
|
+
import { isGitRepository, getGitStatus, getGitDiff, getChangedFiles, suggestCommitMessage, createCommit, stageAll, formatDiffForDisplay, } from './git';
|
|
7
7
|
// Create a temp directory for git tests
|
|
8
8
|
const TEST_DIR = join(tmpdir(), 'codeep-git-test-' + Date.now());
|
|
9
9
|
const NON_GIT_DIR = join(tmpdir(), 'codeep-non-git-test-' + Date.now());
|
|
@@ -5,7 +5,7 @@ vi.mock('fs', () => ({
|
|
|
5
5
|
readFileSync: vi.fn(),
|
|
6
6
|
}));
|
|
7
7
|
import { existsSync, readFileSync } from 'fs';
|
|
8
|
-
import { loadIgnoreRules, isIgnored } from './gitignore
|
|
8
|
+
import { loadIgnoreRules, isIgnored } from './gitignore';
|
|
9
9
|
const mockExistsSync = existsSync;
|
|
10
10
|
const mockReadFileSync = readFileSync;
|
|
11
11
|
beforeEach(() => {
|
package/dist/utils/keychain.js
CHANGED
|
@@ -2,7 +2,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
2
2
|
import { mkdirSync, rmSync, writeFileSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { tmpdir } from 'os';
|
|
5
|
-
import { isProjectDirectory, getProjectType, scanDirectory, generateTreeStructure, readProjectFile, deleteProjectFile, writeProjectFile, } from './project
|
|
5
|
+
import { isProjectDirectory, getProjectType, scanDirectory, generateTreeStructure, readProjectFile, deleteProjectFile, writeProjectFile, } from './project';
|
|
6
6
|
const TEST_DIR = join(tmpdir(), 'codeep-project-test-' + Date.now());
|
|
7
7
|
describe('project utilities', () => {
|
|
8
8
|
beforeEach(() => {
|
package/dist/utils/ratelimit.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { checkApiRateLimit, checkCommandRateLimit, resetRateLimits, getRateLimitStatus, } from './ratelimit
|
|
2
|
+
import { checkApiRateLimit, checkCommandRateLimit, resetRateLimits, getRateLimitStatus, } from './ratelimit';
|
|
3
3
|
describe('ratelimit utilities', () => {
|
|
4
4
|
beforeEach(() => {
|
|
5
5
|
// Reset rate limiters before each test
|
package/dist/utils/retry.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { withRetry, isNetworkError, isTimeoutError, fetchWithTimeout, } from './retry
|
|
2
|
+
import { withRetry, isNetworkError, isTimeoutError, fetchWithTimeout, } from './retry';
|
|
3
3
|
describe('retry utilities', () => {
|
|
4
4
|
describe('isNetworkError', () => {
|
|
5
5
|
it('should detect fetch TypeError', () => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { existsSync, readFileSync, statSync } from 'fs';
|
|
5
5
|
import { join, dirname, basename, extname, relative } from 'path';
|
|
6
|
-
import { loadIgnoreRules, isIgnored } from './gitignore
|
|
6
|
+
import { loadIgnoreRules, isIgnored } from './gitignore';
|
|
7
7
|
// Max context size (characters)
|
|
8
8
|
const MAX_CONTEXT_SIZE = 50000;
|
|
9
9
|
const MAX_FILES = 15;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { extractTargetFile, formatSmartContext, } from './smartContext
|
|
2
|
+
import { extractTargetFile, formatSmartContext, } from './smartContext';
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Helper to build a SmartContextResult quickly
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Task Planning - breaks down complex tasks into subtasks
|
|
3
3
|
*/
|
|
4
|
-
import { config, getApiKey } from '../config/index
|
|
5
|
-
import { getProviderBaseUrl, getProviderAuthHeader } from '../config/providers
|
|
4
|
+
import { config, getApiKey } from '../config/index';
|
|
5
|
+
import { getProviderBaseUrl, getProviderAuthHeader } from '../config/providers';
|
|
6
6
|
/**
|
|
7
7
|
* Ask AI to break down a complex task into subtasks
|
|
8
8
|
*/
|
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
|
@@ -9,9 +9,87 @@ const debug = (...args) => {
|
|
|
9
9
|
}
|
|
10
10
|
};
|
|
11
11
|
import { join, dirname, relative, resolve, isAbsolute } from 'path';
|
|
12
|
-
import { executeCommand } from './shell
|
|
13
|
-
import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history
|
|
14
|
-
import { loadIgnoreRules, isIgnored } from './gitignore
|
|
12
|
+
import { executeCommand } from './shell';
|
|
13
|
+
import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history';
|
|
14
|
+
import { loadIgnoreRules, isIgnored } from './gitignore';
|
|
15
|
+
import { config, getApiKey } from '../config/index';
|
|
16
|
+
import { getProviderMcpEndpoints } from '../config/providers';
|
|
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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { getOpenAITools, getAnthropicTools, parseToolCalls, parseOpenAIToolCalls, parseAnthropicToolCalls, createActionLog, AGENT_TOOLS, } from './tools
|
|
2
|
+
import { getOpenAITools, getAnthropicTools, parseToolCalls, parseOpenAIToolCalls, parseAnthropicToolCalls, createActionLog, AGENT_TOOLS, } from './tools';
|
|
3
3
|
// ─── CONSTANTS ───────────────────────────────────────────────────────────────
|
|
4
4
|
const ALL_TOOL_NAMES = Object.keys(AGENT_TOOLS);
|
|
5
5
|
// ─── getOpenAITools ──────────────────────────────────────────────────────────
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateInput, validateApiKey, validateCommandArgs, validateFilePath, sanitizeOutput, } from './validation
|
|
2
|
+
import { validateInput, validateApiKey, validateCommandArgs, validateFilePath, sanitizeOutput, } from './validation';
|
|
3
3
|
describe('validation utilities', () => {
|
|
4
4
|
describe('validateInput', () => {
|
|
5
5
|
it('should reject empty input', () => {
|
package/dist/utils/verify.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeep",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.12",
|
|
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",
|
package/bin/codeep.js
DELETED
package/dist/app.d.ts
DELETED