@probelabs/probe 0.6.0-rc254 → 0.6.0-rc255

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 (47) hide show
  1. package/README.md +166 -3
  2. package/bin/binaries/probe-v0.6.0-rc255-aarch64-apple-darwin.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc255-aarch64-unknown-linux-musl.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc255-x86_64-apple-darwin.tar.gz +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc255-x86_64-pc-windows-msvc.zip +0 -0
  6. package/bin/binaries/probe-v0.6.0-rc255-x86_64-unknown-linux-musl.tar.gz +0 -0
  7. package/build/agent/ProbeAgent.d.ts +1 -1
  8. package/build/agent/ProbeAgent.js +24 -16
  9. package/build/agent/acp/tools.js +2 -1
  10. package/build/agent/acp/tools.test.js +2 -1
  11. package/build/agent/index.js +1425 -410
  12. package/build/agent/tools.js +0 -28
  13. package/build/delegate.js +3 -0
  14. package/build/index.js +2 -0
  15. package/build/tools/common.js +6 -5
  16. package/build/tools/edit.js +457 -65
  17. package/build/tools/fileTracker.js +318 -0
  18. package/build/tools/fuzzyMatch.js +271 -0
  19. package/build/tools/hashline.js +131 -0
  20. package/build/tools/lineEditHeuristics.js +138 -0
  21. package/build/tools/symbolEdit.js +119 -0
  22. package/build/tools/vercel.js +40 -9
  23. package/cjs/agent/ProbeAgent.cjs +1528 -514
  24. package/cjs/index.cjs +1556 -540
  25. package/index.d.ts +189 -1
  26. package/package.json +1 -1
  27. package/src/agent/ProbeAgent.d.ts +1 -1
  28. package/src/agent/ProbeAgent.js +24 -16
  29. package/src/agent/acp/tools.js +2 -1
  30. package/src/agent/acp/tools.test.js +2 -1
  31. package/src/agent/index.js +14 -3
  32. package/src/agent/tools.js +0 -28
  33. package/src/delegate.js +3 -0
  34. package/src/index.js +2 -0
  35. package/src/tools/common.js +6 -5
  36. package/src/tools/edit.js +457 -65
  37. package/src/tools/fileTracker.js +318 -0
  38. package/src/tools/fuzzyMatch.js +271 -0
  39. package/src/tools/hashline.js +131 -0
  40. package/src/tools/lineEditHeuristics.js +138 -0
  41. package/src/tools/symbolEdit.js +119 -0
  42. package/src/tools/vercel.js +40 -9
  43. package/bin/binaries/probe-v0.6.0-rc254-aarch64-apple-darwin.tar.gz +0 -0
  44. package/bin/binaries/probe-v0.6.0-rc254-aarch64-unknown-linux-musl.tar.gz +0 -0
  45. package/bin/binaries/probe-v0.6.0-rc254-x86_64-apple-darwin.tar.gz +0 -0
  46. package/bin/binaries/probe-v0.6.0-rc254-x86_64-pc-windows-msvc.zip +0 -0
  47. package/bin/binaries/probe-v0.6.0-rc254-x86_64-unknown-linux-musl.tar.gz +0 -0
package/index.d.ts CHANGED
@@ -13,8 +13,10 @@ export interface ProbeAgentOptions {
13
13
  systemPrompt?: string;
14
14
  /** Predefined prompt type (persona) */
15
15
  promptType?: 'code-explorer' | 'engineer' | 'code-review' | 'support' | 'architect';
16
- /** Allow the use of the 'implement' tool for code editing */
16
+ /** Allow the use of the 'edit' and 'create' tools for code editing */
17
17
  allowEdit?: boolean;
18
+ /** Annotate search/extract output with line hashes for integrity verification */
19
+ hashLines?: boolean;
18
20
  /** Architecture context filename to embed from repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present) */
19
21
  architectureFileName?: string;
20
22
  /** Search directory path */
@@ -562,12 +564,196 @@ export declare function listFilesByLevel(
562
564
  */
563
565
  export declare const DEFAULT_SYSTEM_MESSAGE: string;
564
566
 
567
+ /**
568
+ * Content record for a tracked symbol
569
+ */
570
+ export interface ContentRecord {
571
+ /** SHA-256 content hash (first 16 hex chars) */
572
+ contentHash: string;
573
+ /** 1-indexed start line */
574
+ startLine: number;
575
+ /** 1-indexed end line */
576
+ endLine: number;
577
+ /** Symbol name, if from a symbol extract */
578
+ symbolName: string | null;
579
+ /** How the content was obtained: 'extract' or 'edit' */
580
+ source: string;
581
+ /** When the record was created (ms since epoch) */
582
+ timestamp: number;
583
+ }
584
+
585
+ /**
586
+ * Result of a staleness check
587
+ */
588
+ export interface FileCheckResult {
589
+ /** Whether the file is safe to edit */
590
+ ok: boolean;
591
+ /** Reason for rejection: 'untracked' or 'stale' */
592
+ reason?: 'untracked' | 'stale';
593
+ /** Human-readable message explaining the rejection */
594
+ message?: string;
595
+ }
596
+
597
+ /**
598
+ * Compute a SHA-256 content hash for a code block.
599
+ * Normalizes trailing whitespace per line for robustness.
600
+ */
601
+ export declare function computeContentHash(content: string): string;
602
+
603
+ /**
604
+ * Per-session content-aware file state tracker for safe multi-edit workflows.
605
+ * Two-tier tracking: file-level "seen" flag + symbol-level content hashes.
606
+ * Edits proceed when the target symbol hasn't changed, even if other parts of the file changed.
607
+ */
608
+ export declare class FileTracker {
609
+ constructor(options?: { debug?: boolean });
610
+
611
+ /** Mark a file as seen (read via search/extract) */
612
+ markFileSeen(resolvedPath: string): void;
613
+
614
+ /** Check if a file has been seen in this session */
615
+ isFileSeen(resolvedPath: string): boolean;
616
+
617
+ /** Store a content hash for a symbol */
618
+ trackSymbolContent(resolvedPath: string, symbolName: string, code: string, startLine: number, endLine: number, source?: string): void;
619
+
620
+ /** Look up a stored content record for a symbol */
621
+ getSymbolRecord(resolvedPath: string, symbolName: string): ContentRecord | null;
622
+
623
+ /** Check if a symbol's current content matches what was stored */
624
+ checkSymbolContent(resolvedPath: string, symbolName: string, currentCode: string): FileCheckResult;
625
+
626
+ /** Track files from extract target strings (marks as seen, hashes symbol targets) */
627
+ trackFilesFromExtract(targets: string[], cwd: string): Promise<void>;
628
+
629
+ /** Track files from probe search/extract output text (marks as seen) */
630
+ trackFilesFromOutput(output: string, cwd: string): Promise<void>;
631
+
632
+ /** Check if a file is safe to edit (seen-check only) */
633
+ checkBeforeEdit(resolvedPath: string): FileCheckResult;
634
+
635
+ /** Mark file as seen after write, invalidate content records */
636
+ trackFileAfterWrite(resolvedPath: string): Promise<void>;
637
+
638
+ /** Update stored hash after successful symbol write */
639
+ trackSymbolAfterWrite(resolvedPath: string, symbolName: string, code: string, startLine: number, endLine: number): void;
640
+
641
+ /** Remove all content records for a file */
642
+ invalidateFileRecords(resolvedPath: string): void;
643
+
644
+ /** Quick sync check if a file is being tracked (alias for isFileSeen) */
645
+ isTracked(resolvedPath: string): boolean;
646
+
647
+ /** Clear all tracking state */
648
+ clear(): void;
649
+ }
650
+
651
+ /**
652
+ * Edit tool configuration options
653
+ */
654
+ export interface EditToolOptions {
655
+ /** Debug mode */
656
+ debug?: boolean;
657
+ /** Allowed directories for file operations */
658
+ allowedFolders?: string[];
659
+ /** Working directory for resolving relative paths */
660
+ cwd?: string;
661
+ /** Workspace root for relative path display */
662
+ workspaceRoot?: string;
663
+ /** File tracker for staleness detection (created automatically by ProbeAgent) */
664
+ fileTracker?: FileTracker;
665
+ }
666
+
667
+ /**
668
+ * Edit tool parameters (text mode)
669
+ */
670
+ export interface EditTextParams {
671
+ /** Path to the file to edit */
672
+ file_path: string;
673
+ /** Text to find in the file (copy verbatim) */
674
+ old_string: string;
675
+ /** Replacement text */
676
+ new_string: string;
677
+ /** Replace all occurrences (default: false) */
678
+ replace_all?: boolean;
679
+ }
680
+
681
+ /**
682
+ * Edit tool parameters (symbol replace mode)
683
+ */
684
+ export interface EditSymbolReplaceParams {
685
+ /** Path to the file to edit */
686
+ file_path: string;
687
+ /** Symbol name to replace (e.g. "myFunction", "MyClass.myMethod") */
688
+ symbol: string;
689
+ /** New code to replace the symbol with */
690
+ new_string: string;
691
+ }
692
+
693
+ /**
694
+ * Edit tool parameters (symbol insert mode)
695
+ */
696
+ export interface EditSymbolInsertParams {
697
+ /** Path to the file to edit */
698
+ file_path: string;
699
+ /** Symbol name to insert near */
700
+ symbol: string;
701
+ /** New code to insert */
702
+ new_string: string;
703
+ /** Insert before or after the symbol */
704
+ position: 'before' | 'after';
705
+ }
706
+
707
+ /**
708
+ * Line-targeted edit parameters
709
+ */
710
+ export interface EditLineTargetedParams {
711
+ /** Path to the file to edit */
712
+ file_path: string;
713
+ /** New code content */
714
+ new_string: string;
715
+ /** Line reference (e.g. "42" or "42:ab" with hash) */
716
+ start_line: string;
717
+ /** End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line. */
718
+ end_line?: string;
719
+ /** Insert before or after the line instead of replacing */
720
+ position?: 'before' | 'after';
721
+ }
722
+
723
+ /**
724
+ * Create tool parameters
725
+ */
726
+ export interface CreateParams {
727
+ /** Path where the file should be created */
728
+ file_path: string;
729
+ /** Content to write to the file */
730
+ content: string;
731
+ /** Overwrite if file exists (default: false) */
732
+ overwrite?: boolean;
733
+ }
734
+
735
+ /**
736
+ * Create edit tool instance
737
+ */
738
+ export declare function editTool(options?: EditToolOptions): {
739
+ execute(params: EditTextParams | EditSymbolReplaceParams | EditSymbolInsertParams | EditLineTargetedParams): Promise<string>;
740
+ };
741
+
742
+ /**
743
+ * Create create tool instance
744
+ */
745
+ export declare function createTool(options?: EditToolOptions): {
746
+ execute(params: CreateParams): Promise<string>;
747
+ };
748
+
565
749
  /**
566
750
  * Schema definitions
567
751
  */
568
752
  export declare const searchSchema: any;
569
753
  export declare const querySchema: any;
570
754
  export declare const extractSchema: any;
755
+ export declare const editSchema: any;
756
+ export declare const createSchema: any;
571
757
  export declare const attemptCompletionSchema: any;
572
758
 
573
759
  /**
@@ -576,6 +762,8 @@ export declare const attemptCompletionSchema: any;
576
762
  export declare const searchToolDefinition: any;
577
763
  export declare const queryToolDefinition: any;
578
764
  export declare const extractToolDefinition: any;
765
+ export declare const editToolDefinition: string;
766
+ export declare const createToolDefinition: string;
579
767
  export declare const attemptCompletionToolDefinition: any;
580
768
 
581
769
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc254",
3
+ "version": "0.6.0-rc255",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -35,7 +35,7 @@ export interface ProbeAgentOptions {
35
35
  systemPrompt?: string;
36
36
  /** Predefined prompt type (persona) */
37
37
  promptType?: 'code-explorer' | 'code-searcher' | 'engineer' | 'code-review' | 'support' | 'architect';
38
- /** Allow the use of the 'implement' tool for code editing */
38
+ /** Allow the use of the 'edit' and 'create' tools for code editing */
39
39
  allowEdit?: boolean;
40
40
  /** Enable the delegate tool for task distribution to subagents */
41
41
  enableDelegate?: boolean;
@@ -57,7 +57,6 @@ import {
57
57
  useSkillToolDefinition,
58
58
  readImageToolDefinition,
59
59
  attemptCompletionToolDefinition,
60
- implementToolDefinition,
61
60
  editToolDefinition,
62
61
  createToolDefinition,
63
62
  googleSearchToolDefinition,
@@ -66,6 +65,7 @@ import {
66
65
  parseXmlToolCallWithThinking
67
66
  } from './tools.js';
68
67
  import { createMessagePreview, detectUnrecognizedToolCall, detectStuckResponse, areBothStuckResponses } from '../tools/common.js';
68
+ import { FileTracker } from '../tools/fileTracker.js';
69
69
  import {
70
70
  createWrappedTools,
71
71
  listFilesToolInstance,
@@ -178,7 +178,7 @@ export class ProbeAgent {
178
178
  * @param {string} [options.customPrompt] - Custom prompt to replace the default system message
179
179
  * @param {string} [options.systemPrompt] - Alias for customPrompt; takes precedence when both are provided
180
180
  * @param {string} [options.promptType] - Predefined prompt type (code-explorer, code-searcher, architect, code-review, support)
181
- * @param {boolean} [options.allowEdit=false] - Allow the use of the 'implement' tool
181
+ * @param {boolean} [options.allowEdit=false] - Allow the use of the 'edit' and 'create' tools
182
182
  * @param {boolean} [options.enableDelegate=false] - Enable the delegate tool for task distribution to subagents
183
183
  * @param {boolean} [options.enableExecutePlan=false] - Enable the execute_plan DSL orchestration tool
184
184
  * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present)
@@ -229,6 +229,7 @@ export class ProbeAgent {
229
229
  this.customPrompt = options.systemPrompt || options.customPrompt || null;
230
230
  this.promptType = options.promptType || 'code-explorer';
231
231
  this.allowEdit = !!options.allowEdit;
232
+ this.hashLines = options.hashLines !== undefined ? !!options.hashLines : this.allowEdit;
232
233
  this.enableDelegate = !!options.enableDelegate;
233
234
  this.enableExecutePlan = !!options.enableExecutePlan;
234
235
  this.debug = options.debug || process.env.DEBUG === '1';
@@ -328,7 +329,8 @@ export class ProbeAgent {
328
329
  if (this.debug) {
329
330
  console.log(`[DEBUG] Generated session ID for agent: ${this.sessionId}`);
330
331
  console.log(`[DEBUG] Maximum tool iterations configured: ${MAX_TOOL_ITERATIONS}`);
331
- console.log(`[DEBUG] Allow Edit (implement tool): ${this.allowEdit}`);
332
+ console.log(`[DEBUG] Allow Edit: ${this.allowEdit}`);
333
+ console.log(`[DEBUG] Hash Lines: ${this.hashLines}`);
332
334
  console.log(`[DEBUG] Search delegation enabled: ${this.searchDelegate}`);
333
335
  console.log(`[DEBUG] Workspace root: ${this.workspaceRoot}`);
334
336
  console.log(`[DEBUG] Working directory (cwd): ${this.cwd}`);
@@ -831,9 +833,12 @@ export class ProbeAgent {
831
833
  cwd: this.cwd,
832
834
  workspaceRoot: this.workspaceRoot,
833
835
  allowedFolders: this.allowedFolders,
836
+ // File state tracking for safe multi-edit workflows (only when editing is enabled)
837
+ fileTracker: this.allowEdit ? new FileTracker({ debug: this.debug }) : null,
834
838
  outline: this.outline,
835
839
  searchDelegate: this.searchDelegate,
836
840
  allowEdit: this.allowEdit,
841
+ hashLines: this.hashLines,
837
842
  enableDelegate: this.enableDelegate,
838
843
  enableExecutePlan: this.enableExecutePlan,
839
844
  enableBash: this.enableBash,
@@ -2553,16 +2558,12 @@ ${extractGuidance}
2553
2558
  }
2554
2559
 
2555
2560
  // Edit tools (require both allowEdit flag AND allowedTools permission)
2556
- if (this.allowEdit && isToolAllowed('implement')) {
2557
- toolDefinitions += `${implementToolDefinition}\n`;
2558
- }
2559
2561
  if (this.allowEdit && isToolAllowed('edit')) {
2560
2562
  toolDefinitions += `${editToolDefinition}\n`;
2561
2563
  }
2562
2564
  if (this.allowEdit && isToolAllowed('create')) {
2563
2565
  toolDefinitions += `${createToolDefinition}\n`;
2564
2566
  }
2565
-
2566
2567
  // Bash tool (require both enableBash flag AND allowedTools permission)
2567
2568
  if (this.enableBash && isToolAllowed('bash')) {
2568
2569
  toolDefinitions += `${bashToolDefinition}\n`;
@@ -2645,7 +2646,7 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
2645
2646
  availableToolsList += '- query: Search code using structural AST patterns.\n';
2646
2647
  }
2647
2648
  if (isToolAllowed('extract')) {
2648
- availableToolsList += '- extract: Extract specific code blocks or lines from files.\n';
2649
+ availableToolsList += '- extract: Extract specific code blocks or lines from files. Use with symbol targets (e.g. "file.js#funcName") to get line numbers for line-targeted editing.\n';
2649
2650
  }
2650
2651
  if (isToolAllowed('listFiles')) {
2651
2652
  availableToolsList += '- listFiles: List files and directories in a specified location.\n';
@@ -2662,11 +2663,8 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
2662
2663
  if (isToolAllowed('readImage')) {
2663
2664
  availableToolsList += '- readImage: Read and load an image file for AI analysis.\n';
2664
2665
  }
2665
- if (this.allowEdit && isToolAllowed('implement')) {
2666
- availableToolsList += '- implement: Implement a feature or fix a bug using aider.\n';
2667
- }
2668
2666
  if (this.allowEdit && isToolAllowed('edit')) {
2669
- availableToolsList += '- edit: Edit files using exact string replacement.\n';
2667
+ availableToolsList += '- edit: Edit files using text replacement, AST-aware symbol operations, or line-targeted editing.\n';
2670
2668
  }
2671
2669
  if (this.allowEdit && isToolAllowed('create')) {
2672
2670
  availableToolsList += '- create: Create new files with specified content.\n';
@@ -2757,8 +2755,14 @@ Follow these instructions carefully:
2757
2755
  8. Once the task is fully completed, use the '<attempt_completion>' tool to provide the final result. This is the ONLY way to signal completion.
2758
2756
  9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.${this.allowEdit ? `
2759
2757
  10. When modifying files, choose the appropriate tool:
2760
- - Use 'edit' for precise changes to existing files (requires exact string match)
2761
- - Use 'create' for new files or complete file rewrites` : ''}
2758
+ - Use 'edit' for all code modifications:
2759
+ * For small changes (a line or a few lines), use old_string + new_string — copy old_string verbatim from the file.
2760
+ * For rewriting entire functions/classes/methods, use the symbol parameter instead (no exact text matching needed).
2761
+ * For editing specific lines from search/extract output, use start_line (and optionally end_line) with the line numbers shown in the output.${this.hashLines ? ' Line references include content hashes (e.g. "42:ab") for integrity verification.' : ''}
2762
+ * For editing inside large functions: first use extract with the symbol target (e.g. "file.js#myFunction") to see the function with line numbers${this.hashLines ? ' and hashes' : ''}, then use start_line/end_line to surgically edit specific lines within it.
2763
+ - Use 'create' for new files or complete file rewrites.
2764
+ - If an edit fails, read the error message — it tells you exactly how to fix the call and retry.
2765
+ - The system tracks which files you've seen via search/extract. If you try to edit a file you haven't read, or one that changed since you last read it, the edit will fail with instructions to re-read first. Always use extract before editing to ensure you have current file content.` : ''}
2762
2766
  </instructions>
2763
2767
  `;
2764
2768
 
@@ -3420,8 +3424,11 @@ Follow these instructions carefully:
3420
3424
  validTools.push('attempt_completion');
3421
3425
 
3422
3426
  // Edit tools (require both allowEdit flag AND allowedTools permission)
3423
- if (this.allowEdit && this.allowedTools.isEnabled('implement')) {
3424
- validTools.push('implement', 'edit', 'create');
3427
+ if (this.allowEdit && this.allowedTools.isEnabled('edit')) {
3428
+ validTools.push('edit');
3429
+ }
3430
+ if (this.allowEdit && this.allowedTools.isEnabled('create')) {
3431
+ validTools.push('create');
3425
3432
  }
3426
3433
  // Bash tool (require both enableBash flag AND allowedTools permission)
3427
3434
  if (this.enableBash && this.allowedTools.isEnabled('bash')) {
@@ -3804,6 +3811,7 @@ Follow these instructions carefully:
3804
3811
  mcpConfigPath: this.mcpConfigPath, // Inherit MCP config path
3805
3812
  enableBash: this.enableBash, // Inherit bash enablement
3806
3813
  bashConfig: this.bashConfig, // Inherit bash configuration
3814
+ allowEdit: this.allowEdit, // Inherit edit/create permission
3807
3815
  allowedTools: allowedToolsForDelegate, // Inherit allowed tools from parent
3808
3816
  debug: this.debug,
3809
3817
  tracer: this.tracer
@@ -162,7 +162,8 @@ export class ACPToolManager {
162
162
  return ToolCallKind.extract;
163
163
  case 'delegate':
164
164
  return ToolCallKind.execute;
165
- case 'implement':
165
+ case 'edit':
166
+ case 'create':
166
167
  return ToolCallKind.edit;
167
168
  default:
168
169
  return ToolCallKind.execute;
@@ -117,7 +117,8 @@ describe('ACPToolManager', () => {
117
117
  expect(toolManager.getToolKind('query')).toBe(ToolCallKind.query);
118
118
  expect(toolManager.getToolKind('extract')).toBe(ToolCallKind.extract);
119
119
  expect(toolManager.getToolKind('delegate')).toBe(ToolCallKind.execute);
120
- expect(toolManager.getToolKind('implement')).toBe(ToolCallKind.edit);
120
+ expect(toolManager.getToolKind('edit')).toBe(ToolCallKind.edit);
121
+ expect(toolManager.getToolKind('create')).toBe(ToolCallKind.edit);
121
122
  expect(toolManager.getToolKind('unknown')).toBe(ToolCallKind.execute);
122
123
  });
123
124
  });
@@ -124,7 +124,8 @@ function parseArgs() {
124
124
  schema: null,
125
125
  provider: null,
126
126
  model: null,
127
- allowEdit: false,
127
+ allowEdit: process.env.ALLOW_EDIT === '1' || false,
128
+ hashLines: process.env.HASH_LINES !== undefined ? process.env.HASH_LINES === '1' : undefined,
128
129
  enableDelegate: false,
129
130
  verbose: false,
130
131
  help: false,
@@ -167,6 +168,10 @@ function parseArgs() {
167
168
  config.verbose = true;
168
169
  } else if (arg === '--allow-edit') {
169
170
  config.allowEdit = true;
171
+ } else if (arg === '--hash-lines') {
172
+ config.hashLines = true;
173
+ } else if (arg === '--no-hash-lines') {
174
+ config.hashLines = false;
170
175
  } else if (arg === '--enable-delegate') {
171
176
  config.enableDelegate = true;
172
177
  } else if (arg === '--no-delegate') {
@@ -275,12 +280,14 @@ Options:
275
280
  --schema <schema|file> Output schema (JSON, XML, any format - text or file path)
276
281
  --provider <name> Force AI provider: anthropic, openai, google
277
282
  --model <name> Override model name
278
- --allow-edit Enable code modification capabilities
283
+ --allow-edit Enable code modification capabilities (edit + create tools)
284
+ --hash-lines Annotate search/extract output with line hashes (default: on when --allow-edit)
285
+ --no-hash-lines Disable line hash annotations even with --allow-edit
279
286
  --enable-delegate Enable delegate tool for task distribution to subagents
280
287
  --allowed-tools <tools> Filter available tools (comma-separated list)
281
288
  Use '*' or 'all' for all tools (default)
282
289
  Use 'none' or '' for no tools (raw AI mode)
283
- Specific tools: search,query,extract,listFiles,searchFiles,listSkills,useSkill
290
+ Specific tools: search,query,extract,edit,create,listFiles,searchFiles,listSkills,useSkill
284
291
  Supports exclusion: '*,!bash' (all except bash)
285
292
  --disable-tools Disable all tools (raw AI mode, no code analysis)
286
293
  Convenience flag equivalent to --allowed-tools none
@@ -318,6 +325,8 @@ Environment Variables:
318
325
  FORCE_PROVIDER Force specific provider (anthropic, openai, google)
319
326
  MODEL_NAME Override model name
320
327
  MAX_RESPONSE_TOKENS Maximum tokens for AI response
328
+ ALLOW_EDIT Enable code modification (set to '1')
329
+ HASH_LINES Annotate output with line hashes (set to '1'; default: on with ALLOW_EDIT)
321
330
  DEBUG Enable verbose mode (set to '1')
322
331
 
323
332
  Examples:
@@ -334,6 +343,8 @@ Examples:
334
343
  probe agent "Explain this code" --allowed-tools search,extract # Only search and extract
335
344
  probe agent "What is this project about?" --allowed-tools none # Raw AI mode (no tools)
336
345
  probe agent "Tell me about this project" --disable-tools # Raw AI mode (convenience flag)
346
+ probe agent "Fix the off-by-one error" --allow-edit --path ./src # Enable code editing
347
+ ALLOW_EDIT=1 probe agent "Refactor the login flow" # Edit via env var
337
348
  probe agent --mcp # Start MCP server mode
338
349
  probe agent --acp # Start ACP server mode
339
350
 
@@ -87,7 +87,6 @@ export function createTools(configOptions) {
87
87
  if (configOptions.allowEdit && isToolAllowed('create')) {
88
88
  tools.createTool = createTool(configOptions);
89
89
  }
90
-
91
90
  return tools;
92
91
  }
93
92
 
@@ -132,33 +131,6 @@ export {
132
131
  parseXmlToolCall
133
132
  };
134
133
 
135
- // Define the implement tool XML definition
136
- export const implementToolDefinition = `
137
- ## implement
138
- Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
139
-
140
- Parameters:
141
- - task: (required) The task description. Should be as detailed as possible, ideally pointing to exact files which needs be modified or created.
142
- - autoCommits: (optional) Whether to enable auto-commits in aider. Default is false.
143
-
144
- Usage Example:
145
-
146
- <examples>
147
-
148
- User: Can you implement a function to calculate Fibonacci numbers in main.js?
149
- <implement>
150
- <task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
151
- </implement>
152
-
153
- User: Can you implement a function to calculate Fibonacci numbers in main.js with auto-commits?
154
- <implement>
155
- <task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
156
- <autoCommits>true</autoCommits>
157
- </implement>
158
-
159
- </examples>
160
- `;
161
-
162
134
  // Define the listFiles tool XML definition
163
135
  export const listFilesToolDefinition = `
164
136
  ## listFiles
package/src/delegate.js CHANGED
@@ -346,6 +346,7 @@ const DEFAULT_DELEGATE_TIMEOUT = parseInt(process.env.DELEGATE_TIMEOUT, 10) || 3
346
346
  * @param {Object} [options.tracer=null] - Telemetry tracer instance
347
347
  * @param {boolean} [options.enableBash=false] - Enable bash tool (inherited from parent)
348
348
  * @param {Object} [options.bashConfig] - Bash configuration (inherited from parent)
349
+ * @param {boolean} [options.allowEdit=false] - Allow edit/create tools (inherited from parent)
349
350
  * @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root
350
351
  * @param {string} [options.promptType='code-researcher'] - Prompt type for the subagent
351
352
  * @param {Array<string>|null} [options.allowedTools] - Allowed tools for the subagent (null = default)
@@ -373,6 +374,7 @@ export async function delegate({
373
374
  model = null,
374
375
  enableBash = false,
375
376
  bashConfig = null,
377
+ allowEdit = false,
376
378
  architectureFileName = null,
377
379
  promptType = 'code-researcher',
378
380
  allowedTools = null,
@@ -462,6 +464,7 @@ export async function delegate({
462
464
  model, // Inherit from parent
463
465
  enableBash, // Inherit from parent
464
466
  bashConfig, // Inherit from parent
467
+ allowEdit, // Inherit from parent
465
468
  architectureFileName,
466
469
  allowedTools,
467
470
  disableTools,
package/src/index.js CHANGED
@@ -51,6 +51,7 @@ import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool } from
51
51
  import { createExecutePlanTool, getExecutePlanToolDefinition, createCleanupExecutePlanTool, getCleanupExecutePlanToolDefinition } from './tools/executePlan.js';
52
52
  import { bashTool } from './tools/bash.js';
53
53
  import { editTool, createTool } from './tools/edit.js';
54
+ import { FileTracker } from './tools/fileTracker.js';
54
55
  import { ProbeAgent } from './agent/ProbeAgent.js';
55
56
  import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
56
57
  import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
@@ -98,6 +99,7 @@ export {
98
99
  bashTool,
99
100
  editTool,
100
101
  createTool,
102
+ FileTracker,
101
103
  // Export tool instances
102
104
  listFilesToolInstance,
103
105
  searchFilesToolInstance,
@@ -269,6 +269,8 @@ User: Read file inside the dependency
269
269
  </extract>
270
270
 
271
271
  </examples>
272
+
273
+ **Edit Integration:** The line numbers shown in extract output (e.g. "42 | code") can be used directly with the edit tool's start_line/end_line parameters for precise line-targeted editing. To edit inside a large function: extract it by symbol name first (e.g. "file.js#myFunction"), then use the line numbers from the output to make surgical edits with start_line/end_line.
272
274
  `;
273
275
 
274
276
  export const delegateToolDefinition = `
@@ -431,7 +433,7 @@ Capabilities:
431
433
 
432
434
  export const searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.';
433
435
  export const queryDescription = 'Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.';
434
- export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.';
436
+ export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.';
435
437
  export const delegateDescription = 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.';
436
438
  export const bashDescription = 'Execute bash commands for system exploration and development tasks. Secure by default with built-in allow/deny lists.';
437
439
  export const analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
@@ -450,7 +452,6 @@ export const DEFAULT_VALID_TOOLS = [
450
452
  'useSkill',
451
453
  'listFiles',
452
454
  'searchFiles',
453
- 'implement',
454
455
  'bash',
455
456
  'task',
456
457
  'attempt_completion'
@@ -496,9 +497,9 @@ function getValidParamsForTool(toolName) {
496
497
 
497
498
  const schema = schemaMap[toolName];
498
499
  if (!schema) {
499
- // For tools without schema (listFiles, searchFiles, implement), return common params
500
+ // For tools without schema (listFiles, searchFiles), return common params
500
501
  // These are the shared params that appear across multiple tools
501
- return ['path', 'directory', 'pattern', 'recursive', 'includeHidden', 'task', 'files', 'autoCommits', 'result'];
502
+ return ['path', 'directory', 'pattern', 'recursive', 'includeHidden', 'task', 'files', 'result'];
502
503
  }
503
504
 
504
505
  // For attempt_completion, it has custom validation, just return 'result'
@@ -688,7 +689,7 @@ export function detectUnrecognizedToolCall(xmlString, validTools) {
688
689
  // Common tool names that AI might try to use (these should appear as top-level tags)
689
690
  const knownToolNames = [
690
691
  'search', 'query', 'extract', 'listFiles', 'searchFiles',
691
- 'listSkills', 'useSkill', 'readImage', 'implement', 'edit',
692
+ 'listSkills', 'useSkill', 'readImage', 'edit',
692
693
  'create', 'delegate', 'bash', 'task', 'attempt_completion',
693
694
  'attempt_complete', 'read_file', 'write_file', 'run_command',
694
695
  'grep', 'find', 'cat', 'list_directory'