codeep 1.2.10 → 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.
Files changed (88) hide show
  1. package/dist/api/index.js +7 -7
  2. package/dist/config/index.js +3 -3
  3. package/dist/config/providers.d.ts +6 -0
  4. package/dist/config/providers.js +41 -2
  5. package/dist/config/providers.test.js +31 -2
  6. package/dist/hooks/index.js +1 -1
  7. package/dist/hooks/useAgent.js +3 -3
  8. package/dist/renderer/App.js +8 -8
  9. package/dist/renderer/ChatUI.js +3 -3
  10. package/dist/renderer/Screen.js +1 -1
  11. package/dist/renderer/components/Export.js +1 -1
  12. package/dist/renderer/components/Help.js +1 -1
  13. package/dist/renderer/components/Intro.js +1 -1
  14. package/dist/renderer/components/Login.js +3 -3
  15. package/dist/renderer/components/Logout.js +1 -1
  16. package/dist/renderer/components/Modal.js +2 -2
  17. package/dist/renderer/components/Permission.js +2 -2
  18. package/dist/renderer/components/Search.js +1 -1
  19. package/dist/renderer/components/SelectScreen.js +1 -1
  20. package/dist/renderer/components/Settings.js +3 -3
  21. package/dist/renderer/components/Status.js +1 -1
  22. package/dist/renderer/demo-app.js +1 -1
  23. package/dist/renderer/demo.js +1 -1
  24. package/dist/renderer/index.js +9 -9
  25. package/dist/renderer/main.js +31 -31
  26. package/dist/utils/agent.js +10 -10
  27. package/dist/utils/agent.test.js +1 -1
  28. package/dist/utils/codeReview.js +1 -1
  29. package/dist/utils/context.js +1 -1
  30. package/dist/utils/git.test.js +1 -1
  31. package/dist/utils/gitignore.test.js +1 -1
  32. package/dist/utils/keychain.js +1 -1
  33. package/dist/utils/project.test.js +1 -1
  34. package/dist/utils/ratelimit.js +1 -1
  35. package/dist/utils/ratelimit.test.js +1 -1
  36. package/dist/utils/retry.test.js +1 -1
  37. package/dist/utils/smartContext.js +1 -1
  38. package/dist/utils/smartContext.test.js +1 -1
  39. package/dist/utils/taskPlanner.js +2 -2
  40. package/dist/utils/tools.d.ts +64 -4
  41. package/dist/utils/tools.js +212 -7
  42. package/dist/utils/tools.test.js +1 -1
  43. package/dist/utils/validation.test.js +1 -1
  44. package/dist/utils/verify.js +1 -1
  45. package/package.json +1 -1
  46. package/bin/codeep.js +0 -2
  47. package/dist/app.d.ts +0 -2
  48. package/dist/app.js +0 -1501
  49. package/dist/components/AgentActions.d.ts +0 -18
  50. package/dist/components/AgentActions.js +0 -122
  51. package/dist/components/AgentProgress.d.ts +0 -59
  52. package/dist/components/AgentProgress.js +0 -368
  53. package/dist/components/Export.d.ts +0 -8
  54. package/dist/components/Export.js +0 -27
  55. package/dist/components/Help.d.ts +0 -6
  56. package/dist/components/Help.js +0 -7
  57. package/dist/components/Input.d.ts +0 -9
  58. package/dist/components/Input.js +0 -334
  59. package/dist/components/Loading.d.ts +0 -17
  60. package/dist/components/Loading.js +0 -52
  61. package/dist/components/Login.d.ts +0 -7
  62. package/dist/components/Login.js +0 -77
  63. package/dist/components/Logo.d.ts +0 -8
  64. package/dist/components/Logo.js +0 -89
  65. package/dist/components/LogoutPicker.d.ts +0 -8
  66. package/dist/components/LogoutPicker.js +0 -61
  67. package/dist/components/Message.d.ts +0 -10
  68. package/dist/components/Message.js +0 -242
  69. package/dist/components/MessageList.d.ts +0 -10
  70. package/dist/components/MessageList.js +0 -42
  71. package/dist/components/ProjectPermission.d.ts +0 -7
  72. package/dist/components/ProjectPermission.js +0 -65
  73. package/dist/components/Search.d.ts +0 -10
  74. package/dist/components/Search.js +0 -30
  75. package/dist/components/SessionPicker.d.ts +0 -9
  76. package/dist/components/SessionPicker.js +0 -88
  77. package/dist/components/Sessions.d.ts +0 -12
  78. package/dist/components/Sessions.js +0 -119
  79. package/dist/components/Settings.d.ts +0 -9
  80. package/dist/components/Settings.js +0 -198
  81. package/dist/components/Spinner.d.ts +0 -34
  82. package/dist/components/Spinner.js +0 -38
  83. package/dist/components/Status.d.ts +0 -2
  84. package/dist/components/Status.js +0 -13
  85. package/dist/components/StreamingMessage.d.ts +0 -14
  86. package/dist/components/StreamingMessage.js +0 -19
  87. package/dist/index.d.ts +0 -2
  88. package/dist/index.js +0 -42
@@ -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.js';
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.js';
42
- import { config, getApiKey } from '../config/index.js';
43
- import { getProviderBaseUrl, getProviderAuthHeader, supportsNativeTools } from '../config/providers.js';
44
- import { startSession, endSession, undoLastAction, undoAllActions, getCurrentSession, getRecentSessions, formatSession } from './history.js';
45
- import { runAllVerifications, formatErrorsForAgent, hasVerificationErrors, getVerificationSummary } from './verify.js';
46
- import { gatherSmartContext, formatSmartContext, extractTargetFile } from './smartContext.js';
47
- import { planTasks, formatTaskPlan } from './taskPlanner.js';
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);
@@ -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.js';
14
+ import { loadProjectRules, formatAgentResult } from './agent';
15
15
  // Cast mocked functions for convenience
16
16
  const mockExistsSync = existsSync;
17
17
  const mockReadFileSync = readFileSync;
@@ -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.js';
6
+ import { getChangedFiles } from './git';
7
7
  // Common code patterns that indicate issues
8
8
  const CODE_PATTERNS = [
9
9
  // Security issues
@@ -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.js';
7
+ import { logger } from './logger';
8
8
  // Context storage directory
9
9
  const CONTEXT_DIR = join(homedir(), '.codeep', 'contexts');
10
10
  /**
@@ -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.js';
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.js';
8
+ import { loadIgnoreRules, isIgnored } from './gitignore';
9
9
  const mockExistsSync = existsSync;
10
10
  const mockReadFileSync = readFileSync;
11
11
  beforeEach(() => {
@@ -1,5 +1,5 @@
1
1
  import keytar from 'keytar';
2
- import { logger } from './logger.js';
2
+ import { logger } from './logger';
3
3
  const SERVICE_NAME = 'codeep';
4
4
  class KeychainStorage {
5
5
  getAccountName(providerId) {
@@ -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.js';
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(() => {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Rate limiting utility to prevent API abuse
3
3
  */
4
- import { config } from '../config/index.js';
4
+ import { config } from '../config/index';
5
5
  class RateLimiter {
6
6
  requests = [];
7
7
  config;
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- import { checkApiRateLimit, checkCommandRateLimit, resetRateLimits, getRateLimitStatus, } from './ratelimit.js';
2
+ import { checkApiRateLimit, checkCommandRateLimit, resetRateLimits, getRateLimitStatus, } from './ratelimit';
3
3
  describe('ratelimit utilities', () => {
4
4
  beforeEach(() => {
5
5
  // Reset rate limiters before each test
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
- import { withRetry, isNetworkError, isTimeoutError, fetchWithTimeout, } from './retry.js';
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.js';
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.js';
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.js';
5
- import { getProviderBaseUrl, getProviderAuthHeader } from '../config/providers.js';
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
  */
@@ -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
  */
@@ -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.js';
13
- import { recordWrite, recordEdit, recordDelete, recordMkdir, recordCommand } from './history.js';
14
- import { loadIgnoreRules, isIgnored } from './gitignore.js';
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 Object.entries(AGENT_TOOLS)) {
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 Object.entries(AGENT_TOOLS).map(([name, tool]) => {
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 Object.entries(AGENT_TOOLS).map(([name, tool]) => {
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;
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { getOpenAITools, getAnthropicTools, parseToolCalls, parseOpenAIToolCalls, parseAnthropicToolCalls, createActionLog, AGENT_TOOLS, } from './tools.js';
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.js';
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', () => {
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { existsSync, readFileSync } from 'fs';
6
6
  import { join } from 'path';
7
- import { executeCommand } from './shell.js';
7
+ import { executeCommand } from './shell';
8
8
  const DEFAULT_OPTIONS = {
9
9
  runBuild: true,
10
10
  runTest: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeep",
3
- "version": "1.2.10",
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
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- import '../dist/renderer/main.js';
package/dist/app.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import React from 'react';
2
- export declare const App: React.FC;