xibecode 0.3.5 → 0.3.8

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.
@@ -13,6 +13,51 @@ import { BrowserManager } from '../tools/browser.js';
13
13
  import * as os from 'os';
14
14
  import { SkillManager } from './skills.js';
15
15
  const execAsync = promisify(exec);
16
+ /**
17
+ * Main tool executor for XibeCode agent
18
+ *
19
+ * Provides 95+ tools across 8 categories for autonomous coding operations:
20
+ * - File Operations: read, write, edit, delete files
21
+ * - Git Operations: status, diff, commit, reset
22
+ * - Shell Commands: run commands, interactive shell
23
+ * - Web Operations: search, fetch URLs, HTTP requests
24
+ * - Context Operations: code search, file finding, context discovery
25
+ * - Test Operations: run tests, get results
26
+ * - Memory Operations: update neural memory
27
+ * - Browser Operations: web automation with Playwright
28
+ *
29
+ * Features:
30
+ * - Mode-based tool permissions
31
+ * - Safety checking for dangerous operations
32
+ * - Plugin support for custom tools
33
+ * - MCP integration for external tools
34
+ * - Dry-run mode for safe previews
35
+ * - Backup system for file operations
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const executor = new CodingToolExecutor('/project', {
40
+ * dryRun: false,
41
+ * pluginManager,
42
+ * memory
43
+ * });
44
+ *
45
+ * executor.setMode('agent');
46
+ *
47
+ * // Read a file
48
+ * const result = await executor.execute('read_file', { path: 'src/app.ts' });
49
+ *
50
+ * // Edit a file
51
+ * await executor.execute('edit_file', {
52
+ * path: 'src/app.ts',
53
+ * search: 'old code',
54
+ * replace: 'new code'
55
+ * });
56
+ * ```
57
+ *
58
+ * @category Tool Execution
59
+ * @since 0.1.0
60
+ */
16
61
  export class CodingToolExecutor {
17
62
  workingDir;
18
63
  contextManager;
@@ -28,6 +73,37 @@ export class CodingToolExecutor {
28
73
  platform;
29
74
  dryRun;
30
75
  testCommandOverride;
76
+ /**
77
+ * Creates a new CodingToolExecutor instance
78
+ *
79
+ * Initializes all tool subsystems including file operations, git utilities,
80
+ * test runner, safety checker, plugin manager, and optional MCP integration.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Basic usage
85
+ * const executor = new CodingToolExecutor('/project');
86
+ *
87
+ * // With options
88
+ * const executor = new CodingToolExecutor('/project', {
89
+ * dryRun: true,
90
+ * pluginManager: new PluginManager(),
91
+ * memory: new NeuralMemory('./.xibecode/memory.json')
92
+ * });
93
+ * ```
94
+ *
95
+ * @param workingDir - Working directory for file operations (default: process.cwd())
96
+ * @param options - Configuration options
97
+ * @param options.dryRun - Enable dry-run mode (preview changes without executing)
98
+ * @param options.testCommandOverride - Override test command detection
99
+ * @param options.pluginManager - Plugin manager instance for custom tools
100
+ * @param options.mcpClientManager - MCP client manager for external tool integration
101
+ * @param options.memory - Neural memory instance for persistent learning
102
+ * @param options.skillManager - Skill manager for loading custom workflows
103
+ *
104
+ * @category Constructor
105
+ * @since 0.1.0
106
+ */
31
107
  constructor(workingDir = process.cwd(), options) {
32
108
  this.workingDir = workingDir;
33
109
  this.contextManager = new ContextManager(workingDir);
@@ -46,6 +122,26 @@ export class CodingToolExecutor {
46
122
  this.testCommandOverride = options?.testCommandOverride;
47
123
  }
48
124
  currentMode = 'agent';
125
+ /**
126
+ * Set the current agent mode
127
+ *
128
+ * Changes the tool executor's operating mode, which affects:
129
+ * - Tool permissions (what tools are available)
130
+ * - Dry-run default (some modes preview changes by default)
131
+ * - Risk tolerance for operations
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * executor.setMode('plan'); // Read-only mode
136
+ * executor.setMode('agent'); // Full capabilities
137
+ * executor.setMode('security'); // Security-focused mode
138
+ * ```
139
+ *
140
+ * @param mode - Agent mode to switch to
141
+ *
142
+ * @category Mode Management
143
+ * @since 0.1.0
144
+ */
49
145
  setMode(mode) {
50
146
  this.currentMode = mode;
51
147
  const config = MODE_CONFIG[mode];
@@ -53,6 +149,11 @@ export class CodingToolExecutor {
53
149
  }
54
150
  /**
55
151
  * Safely parse tool input - handles string JSON, null, undefined
152
+ *
153
+ * @param input - Raw tool input (string, object, null, or undefined)
154
+ * @returns Parsed input as object
155
+ *
156
+ * @internal
56
157
  */
57
158
  parseInput(input) {
58
159
  if (!input)
@@ -69,6 +170,49 @@ export class CodingToolExecutor {
69
170
  return input;
70
171
  return {};
71
172
  }
173
+ /**
174
+ * Execute a tool with given input
175
+ *
176
+ * Main tool execution pipeline that:
177
+ * 1. Checks tool permissions for current mode
178
+ * 2. Routes MCP tools to external servers
179
+ * 3. Routes plugin tools to plugin manager
180
+ * 4. Performs safety assessment for risky operations
181
+ * 5. Executes the tool implementation
182
+ * 6. Returns structured result
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * // Read a file
187
+ * const result = await executor.execute('read_file', {
188
+ * path: 'src/app.ts'
189
+ * });
190
+ *
191
+ * if (result.success) {
192
+ * console.log(result.content);
193
+ * }
194
+ *
195
+ * // Edit a file
196
+ * await executor.execute('edit_file', {
197
+ * path: 'src/app.ts',
198
+ * search: 'const old = 1;',
199
+ * replace: 'const new = 2;'
200
+ * });
201
+ *
202
+ * // Run a command
203
+ * await executor.execute('run_command', {
204
+ * command: 'npm test'
205
+ * });
206
+ * ```
207
+ *
208
+ * @param toolName - Name of the tool to execute (e.g., 'read_file', 'edit_file')
209
+ * @param input - Tool input parameters (varies by tool)
210
+ * @returns Tool execution result with success/error status
211
+ *
212
+ * @see {@link getTools} for list of available tools
213
+ * @category Tool Execution
214
+ * @since 0.1.0
215
+ */
72
216
  async execute(toolName, input) {
73
217
  // Check tool permissions first
74
218
  const permission = isToolAllowed(this.currentMode, toolName);
@@ -327,6 +471,44 @@ export class CodingToolExecutor {
327
471
  return { error: true, success: false, message: 'Missing url' };
328
472
  return this.browserManager.getConsoleLogs(p.url);
329
473
  }
474
+ case 'run_visual_test': {
475
+ if (!p.url || typeof p.url !== 'string')
476
+ return { error: true, success: false, message: 'Missing url' };
477
+ if (!p.baseline_path || typeof p.baseline_path !== 'string')
478
+ return { error: true, success: false, message: 'Missing baseline_path' };
479
+ const outputDir = p.output_dir || '.playwright-baselines';
480
+ return this.browserManager.runVisualTest(p.url, this.resolvePath(p.baseline_path), this.resolvePath(outputDir));
481
+ }
482
+ case 'check_accessibility': {
483
+ if (!p.url || typeof p.url !== 'string')
484
+ return { error: true, success: false, message: 'Missing url' };
485
+ return this.browserManager.checkAccessibility(p.url);
486
+ }
487
+ case 'measure_performance': {
488
+ if (!p.url || typeof p.url !== 'string')
489
+ return { error: true, success: false, message: 'Missing url' };
490
+ return this.browserManager.measurePerformance(p.url);
491
+ }
492
+ case 'test_responsive': {
493
+ if (!p.url || typeof p.url !== 'string')
494
+ return { error: true, success: false, message: 'Missing url' };
495
+ const outputDir = p.output_dir || '.responsive-screenshots';
496
+ return this.browserManager.testResponsive(p.url, this.resolvePath(outputDir), p.viewports);
497
+ }
498
+ case 'capture_network': {
499
+ if (!p.url || typeof p.url !== 'string')
500
+ return { error: true, success: false, message: 'Missing url' };
501
+ return this.browserManager.captureNetworkRequests(p.url);
502
+ }
503
+ case 'run_playwright_test': {
504
+ if (!p.test_path || typeof p.test_path !== 'string')
505
+ return { error: true, success: false, message: 'Missing test_path' };
506
+ return this.browserManager.runPlaywrightTest(this.resolvePath(p.test_path), {
507
+ headed: p.headed,
508
+ browser: p.browser,
509
+ timeout: p.timeout,
510
+ });
511
+ }
330
512
  case 'search_skills_sh': {
331
513
  if (!p.query || typeof p.query !== 'string') {
332
514
  return { error: true, success: false, message: 'Missing required parameter: query (string)' };
@@ -340,9 +522,43 @@ export class CodingToolExecutor {
340
522
  return this.installSkillFromSkillsSh(p.skill_id);
341
523
  }
342
524
  default:
343
- return { error: true, success: false, message: `Unknown tool: ${toolName}. Available tools: read_file, read_multiple_files, write_file, edit_file, edit_lines, insert_at_line, verified_edit, list_directory, search_files, run_command, create_directory, delete_file, move_file, get_context, revert_file, run_tests, get_test_status, get_git_status, get_git_diff_summary, get_git_changed_files, create_git_checkpoint, revert_to_git_checkpoint, git_show_diff, get_mcp_status, grep_code, web_search, fetch_url, remember_lesson, take_screenshot, get_console_logs, search_skills_sh, install_skill_from_skills_sh` };
525
+ return { error: true, success: false, message: `Unknown tool: ${toolName}. Available tools: read_file, read_multiple_files, write_file, edit_file, edit_lines, insert_at_line, verified_edit, list_directory, search_files, run_command, create_directory, delete_file, move_file, get_context, revert_file, run_tests, get_test_status, get_git_status, get_git_diff_summary, get_git_changed_files, create_git_checkpoint, revert_to_git_checkpoint, git_show_diff, get_mcp_status, grep_code, web_search, fetch_url, remember_lesson, take_screenshot, get_console_logs, run_visual_test, check_accessibility, measure_performance, test_responsive, capture_network, run_playwright_test, search_skills_sh, install_skill_from_skills_sh` };
344
526
  }
345
527
  }
528
+ /**
529
+ * Get all available tools for the agent
530
+ *
531
+ * Returns an array of tool definitions including core tools, plugin tools,
532
+ * and MCP tools. Each tool includes:
533
+ * - name: Tool identifier
534
+ * - description: What the tool does (for Claude AI)
535
+ * - input_schema: JSON Schema for input validation
536
+ *
537
+ * Tools are grouped by category:
538
+ * - File Operations: read_file, write_file, edit_file, etc.
539
+ * - Git Operations: get_git_status, git_commit, etc.
540
+ * - Shell Commands: run_command, interactive_shell
541
+ * - Web Operations: web_search, fetch_url, http_request
542
+ * - Context Operations: grep_code, search_files, get_context
543
+ * - Test Operations: run_tests, get_test_status
544
+ * - Memory Operations: update_memory
545
+ * - Browser Operations: open_browser, browser_click, etc.
546
+ *
547
+ * @example
548
+ * ```typescript
549
+ * const tools = executor.getTools();
550
+ * console.log(`${tools.length} tools available`);
551
+ *
552
+ * // Find a specific tool
553
+ * const readFile = tools.find(t => t.name === 'read_file');
554
+ * console.log(readFile.description);
555
+ * ```
556
+ *
557
+ * @returns Array of tool definitions with schemas
558
+ *
559
+ * @category Tool Management
560
+ * @since 0.1.0
561
+ */
346
562
  getTools() {
347
563
  const coreTools = [
348
564
  {
@@ -852,6 +1068,90 @@ export class CodingToolExecutor {
852
1068
  required: ['url']
853
1069
  }
854
1070
  },
1071
+ {
1072
+ name: 'run_visual_test',
1073
+ description: 'Run visual regression testing by comparing screenshots. Creates baseline on first run, then compares subsequent screenshots against it. Perfect for catching unintended UI changes.',
1074
+ input_schema: {
1075
+ type: 'object',
1076
+ properties: {
1077
+ url: { type: 'string', description: 'URL to test (e.g., http://localhost:3000)' },
1078
+ baseline_path: { type: 'string', description: 'Path to baseline screenshot file (e.g., baselines/homepage.png)' },
1079
+ output_dir: { type: 'string', description: 'Directory for test output (default: .playwright-baselines)' }
1080
+ },
1081
+ required: ['url', 'baseline_path']
1082
+ }
1083
+ },
1084
+ {
1085
+ name: 'check_accessibility',
1086
+ description: 'Run accessibility audit on a webpage. Checks for missing alt text, form labels, heading hierarchy, color contrast, and other WCAG issues. Returns errors, warnings, and notices.',
1087
+ input_schema: {
1088
+ type: 'object',
1089
+ properties: {
1090
+ url: { type: 'string', description: 'URL to audit (e.g., http://localhost:3000)' }
1091
+ },
1092
+ required: ['url']
1093
+ }
1094
+ },
1095
+ {
1096
+ name: 'measure_performance',
1097
+ description: 'Measure Core Web Vitals and performance metrics: First Contentful Paint (FCP), Largest Contentful Paint (LCP), Cumulative Layout Shift (CLS), Time to Interactive (TTI), and DOM Content Loaded. Essential for UX and SEO.',
1098
+ input_schema: {
1099
+ type: 'object',
1100
+ properties: {
1101
+ url: { type: 'string', description: 'URL to measure (e.g., http://localhost:3000)' }
1102
+ },
1103
+ required: ['url']
1104
+ }
1105
+ },
1106
+ {
1107
+ name: 'test_responsive',
1108
+ description: 'Test a page across multiple viewport sizes (mobile, tablet, desktop). Takes screenshots at each breakpoint and reports any JavaScript errors. Perfect for responsive design testing.',
1109
+ input_schema: {
1110
+ type: 'object',
1111
+ properties: {
1112
+ url: { type: 'string', description: 'URL to test (e.g., http://localhost:3000)' },
1113
+ output_dir: { type: 'string', description: 'Directory for screenshots (default: .responsive-screenshots)' },
1114
+ viewports: {
1115
+ type: 'array',
1116
+ items: {
1117
+ type: 'object',
1118
+ properties: {
1119
+ name: { type: 'string' },
1120
+ width: { type: 'number' },
1121
+ height: { type: 'number' }
1122
+ }
1123
+ },
1124
+ description: 'Custom viewports. Default: mobile (375x667), tablet (768x1024), desktop (1280x800), desktop-large (1920x1080)'
1125
+ }
1126
+ },
1127
+ required: ['url']
1128
+ }
1129
+ },
1130
+ {
1131
+ name: 'capture_network',
1132
+ description: 'Capture all network requests made during page load. Shows URLs, methods, status codes, resource types, and timing. Useful for debugging API calls, detecting failed requests, and performance analysis.',
1133
+ input_schema: {
1134
+ type: 'object',
1135
+ properties: {
1136
+ url: { type: 'string', description: 'URL to load and monitor (e.g., http://localhost:3000)' }
1137
+ },
1138
+ required: ['url']
1139
+ }
1140
+ },
1141
+ {
1142
+ name: 'run_playwright_test',
1143
+ description: 'Execute a Playwright test file. Runs the test and returns pass/fail results with output. Great for running E2E tests, integration tests, and component tests.',
1144
+ input_schema: {
1145
+ type: 'object',
1146
+ properties: {
1147
+ test_path: { type: 'string', description: 'Path to the Playwright test file (e.g., tests/homepage.spec.ts)' },
1148
+ headed: { type: 'boolean', description: 'Run with visible browser window (default: false)' },
1149
+ browser: { type: 'string', enum: ['chromium', 'firefox', 'webkit'], description: 'Browser to use (default: chromium)' },
1150
+ timeout: { type: 'number', description: 'Test timeout in milliseconds (default: 120000)' }
1151
+ },
1152
+ required: ['test_path']
1153
+ }
1154
+ },
855
1155
  {
856
1156
  name: 'fetch_url',
857
1157
  description: 'Fetch and read content from any URL. HTML is automatically stripped to plain text. Use this to read documentation pages, API references, blog posts, or any web content. Supports HTML, JSON, and plain text.',
@@ -933,9 +1233,50 @@ export class CodingToolExecutor {
933
1233
  const pluginTools = this.pluginManager.getPluginTools();
934
1234
  return [...coreTools, ...mcpTools, ...pluginTools];
935
1235
  }
1236
+ /**
1237
+ * Resolve relative file path to absolute path
1238
+ *
1239
+ * @param filePath - Relative or absolute file path
1240
+ * @returns Absolute path resolved against working directory
1241
+ *
1242
+ * @internal
1243
+ */
936
1244
  resolvePath(filePath) {
937
1245
  return path.resolve(this.workingDir, filePath);
938
1246
  }
1247
+ /**
1248
+ * Read file contents
1249
+ *
1250
+ * Reads a file from the filesystem. Supports partial reading by line range
1251
+ * to avoid token limits for large files. Always returns UTF-8 encoded text.
1252
+ *
1253
+ * @example
1254
+ * ```typescript
1255
+ * // Read entire file
1256
+ * const result = await executor.execute('read_file', {
1257
+ * path: 'src/app.ts'
1258
+ * });
1259
+ *
1260
+ * // Read specific line range
1261
+ * const partial = await executor.execute('read_file', {
1262
+ * path: 'src/large-file.ts',
1263
+ * start_line: 100,
1264
+ * end_line: 200
1265
+ * });
1266
+ * ```
1267
+ *
1268
+ * @param filePath - Path to file (relative to working directory)
1269
+ * @param startLine - Optional start line for partial read (1-indexed)
1270
+ * @param endLine - Optional end line for partial read (inclusive)
1271
+ * @returns Object with path, content, and line info
1272
+ *
1273
+ * @throws {FileNotFoundError} If file doesn't exist
1274
+ * @throws {PermissionError} If file is not readable
1275
+ *
1276
+ * @category File Operations
1277
+ * @mode All modes
1278
+ * @since 0.1.0
1279
+ */
939
1280
  async readFile(filePath, startLine, endLine) {
940
1281
  const fullPath = this.resolvePath(filePath);
941
1282
  try {
@@ -977,6 +1318,33 @@ export class CodingToolExecutor {
977
1318
  .map((r, i) => ({ path: paths[i], error: r.reason.message })),
978
1319
  };
979
1320
  }
1321
+ /**
1322
+ * Write content to file
1323
+ *
1324
+ * Creates or overwrites a file with the given content. Automatically creates
1325
+ * parent directories if they don't exist. Creates a backup before overwriting
1326
+ * existing files.
1327
+ *
1328
+ * In dry-run mode, shows what would be written without making changes.
1329
+ *
1330
+ * @example
1331
+ * ```typescript
1332
+ * await executor.execute('write_file', {
1333
+ * path: 'src/new-file.ts',
1334
+ * content: 'export const hello = "world";'
1335
+ * });
1336
+ * ```
1337
+ *
1338
+ * @param filePath - Path to file (relative to working directory)
1339
+ * @param content - File content to write
1340
+ * @returns Object with success status, path, and file info
1341
+ *
1342
+ * @throws {PermissionError} If directory is not writable
1343
+ *
1344
+ * @category File Operations
1345
+ * @mode Write modes (agent, engineer, architect)
1346
+ * @since 0.1.0
1347
+ */
980
1348
  async writeFile(filePath, content) {
981
1349
  const fullPath = this.resolvePath(filePath);
982
1350
  if (this.dryRun) {
@@ -1005,6 +1373,47 @@ export class CodingToolExecutor {
1005
1373
  return { error: true, success: false, message: `Failed to write ${filePath}: ${error.message}` };
1006
1374
  }
1007
1375
  }
1376
+ /**
1377
+ * Edit file using search and replace
1378
+ *
1379
+ * Performs intelligent search-and-replace editing using the FileEditor's
1380
+ * smart edit strategy. Searches for exact string matches and replaces them.
1381
+ * Automatically handles multi-line strings and special characters.
1382
+ *
1383
+ * Creates a backup before editing. In dry-run mode, shows what would be
1384
+ * changed without modifying the file.
1385
+ *
1386
+ * @example
1387
+ * ```typescript
1388
+ * // Replace first occurrence
1389
+ * await executor.execute('edit_file', {
1390
+ * path: 'src/app.ts',
1391
+ * search: 'const oldValue = 1;',
1392
+ * replace: 'const newValue = 2;'
1393
+ * });
1394
+ *
1395
+ * // Replace all occurrences
1396
+ * await executor.execute('edit_file', {
1397
+ * path: 'src/app.ts',
1398
+ * search: 'oldName',
1399
+ * replace: 'newName',
1400
+ * all: true
1401
+ * });
1402
+ * ```
1403
+ *
1404
+ * @param filePath - Path to file (relative to working directory)
1405
+ * @param search - Exact string to search for (can be multi-line)
1406
+ * @param replace - Replacement string
1407
+ * @param all - Replace all occurrences (default: false, replaces first only)
1408
+ * @returns Object with success status, changes made, and diff
1409
+ *
1410
+ * @throws {FileNotFoundError} If file doesn't exist
1411
+ * @throws {SearchNotFoundError} If search string not found
1412
+ *
1413
+ * @category File Operations
1414
+ * @mode Write modes (agent, engineer, architect)
1415
+ * @since 0.1.0
1416
+ */
1008
1417
  async editFile(filePath, search, replace, all) {
1009
1418
  if (this.dryRun) {
1010
1419
  return {
@@ -1097,6 +1506,56 @@ export class CodingToolExecutor {
1097
1506
  return { error: true, success: false, message: `Failed to search files: ${error.message}` };
1098
1507
  }
1099
1508
  }
1509
+ /**
1510
+ * Execute a shell command
1511
+ *
1512
+ * Runs a command in a shell (bash on Unix, PowerShell on Windows).
1513
+ * Captures stdout and stderr. Supports stdin input for interactive commands.
1514
+ * Automatically times out after 120 seconds (configurable).
1515
+ *
1516
+ * Safety checks are performed before execution to block dangerous commands
1517
+ * like `rm -rf /`, malicious scripts, and other high-risk operations.
1518
+ *
1519
+ * @example
1520
+ * ```typescript
1521
+ * // Run a simple command
1522
+ * const result = await executor.execute('run_command', {
1523
+ * command: 'npm test'
1524
+ * });
1525
+ *
1526
+ * // Run with specific working directory
1527
+ * await executor.execute('run_command', {
1528
+ * command: 'ls -la',
1529
+ * cwd: './src'
1530
+ * });
1531
+ *
1532
+ * // Run with stdin input
1533
+ * await executor.execute('run_command', {
1534
+ * command: 'cat > output.txt',
1535
+ * input: 'Hello World'
1536
+ * });
1537
+ *
1538
+ * // Run with custom timeout
1539
+ * await executor.execute('run_command', {
1540
+ * command: 'npm install',
1541
+ * timeout: 300 // 5 minutes
1542
+ * });
1543
+ * ```
1544
+ *
1545
+ * @param command - Shell command to execute
1546
+ * @param cwd - Working directory (default: executor's working directory)
1547
+ * @param input - Optional stdin input for interactive commands
1548
+ * @param timeout - Timeout in seconds (default: 120)
1549
+ * @returns Object with stdout, stderr, exit code, and execution time
1550
+ *
1551
+ * @throws {SafetyError} If command is blocked by safety checker
1552
+ * @throws {TimeoutError} If command exceeds timeout
1553
+ *
1554
+ * @category Shell Commands
1555
+ * @mode Command modes (agent, engineer, debugger, tester)
1556
+ * @risk-level High
1557
+ * @since 0.1.0
1558
+ */
1100
1559
  async runCommand(command, cwd, input, timeout) {
1101
1560
  const workDir = cwd ? this.resolvePath(cwd) : this.workingDir;
1102
1561
  const timeoutMs = (timeout || 120) * 1000;