minovative-mind-cli 2.12.0 → 2.13.1

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.
@@ -41,7 +41,7 @@ export function getScopedToolDeclarations() {
41
41
  },
42
42
  content: {
43
43
  type: 'STRING',
44
- description: 'The semantic intent or message content',
44
+ description: 'The semantic intent or message content (concise summary, max 500 chars)',
45
45
  },
46
46
  affectedFiles: {
47
47
  type: 'ARRAY',
@@ -58,10 +58,23 @@ export function getScopedToolDeclarations() {
58
58
  },
59
59
  {
60
60
  name: 'read_messages',
61
- description: 'Fetch any new messages or activity from other agents on the bus.',
61
+ description: 'Fetch intelligent updates from other agents on the bus. Can filter by file, agent ID, or signal type.',
62
62
  parameters: {
63
63
  type: 'OBJECT',
64
- properties: {},
64
+ properties: {
65
+ file: {
66
+ type: 'STRING',
67
+ description: 'Optional: Filter updates touching a specific file path (e.g., "src/services/auth.ts")',
68
+ },
69
+ fromAgent: {
70
+ type: 'STRING',
71
+ description: 'Optional: Filter updates from a specific collaborator agent ID',
72
+ },
73
+ type: {
74
+ type: 'STRING',
75
+ description: 'Optional: Filter by signal type ("completion", "warning", "discovery", "request")',
76
+ },
77
+ },
65
78
  },
66
79
  },
67
80
  ];
@@ -80,8 +93,9 @@ export function getScopedToolDeclarations() {
80
93
  * @param locks - Shared file lock registry instance
81
94
  * @param onProgress - Callback to notify parent of sub-agent progress
82
95
  * @param options - Optional sub-path auto-focus and override configuration
96
+ * @param taskScope - Optional task scope metadata (targetFiles, dependsOn)
83
97
  */
84
- export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress, options) {
98
+ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus, locks, onProgress, options, taskScope) {
85
99
  // Update heartbeat so the orchestrator knows we are making progress
86
100
  onProgress();
87
101
  const timestamp = Date.now();
@@ -89,23 +103,27 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
89
103
  let targetDesc = 'workspace';
90
104
  let status = 'success';
91
105
  let result = null;
92
- let resultSummary = undefined;
106
+ let resultSummary;
93
107
  try {
94
108
  // ─── Orchestration-Specific Tools ────────────────────────────────
95
109
  if (name === 'post_message') {
96
110
  const type = args.type;
111
+ let content = String(args.content || '');
112
+ if (content.length > 500) {
113
+ content = content.substring(0, 500) + '... (truncated)';
114
+ }
97
115
  const signal = {
98
116
  type,
99
117
  timestamp,
100
118
  fromAgent: agentId,
101
- content: args.content,
119
+ content,
102
120
  };
103
121
  if (type === 'discovery')
104
122
  signal.affectedFiles = args.affectedFiles || [];
105
123
  if (type === 'request')
106
124
  signal.toAgent = args.toAgent || 'all';
107
125
  if (type === 'completion')
108
- signal.summary = args.content; // map completion summary
126
+ signal.summary = content; // map completion summary
109
127
  const accepted = bus.postSignal(signal);
110
128
  if (!accepted) {
111
129
  return { error: `Message bus rejected signal. You have hit the max signal cap (${MessageBus.MAX_SIGNALS_PER_AGENT}). Stop sending signals.` };
@@ -113,7 +131,18 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
113
131
  return { success: true, message: 'Message posted to bus.' };
114
132
  }
115
133
  if (name === 'read_messages') {
116
- const unread = bus.getUnread(agentId);
134
+ const unread = bus.queryBus({
135
+ agentId,
136
+ file: args.file,
137
+ fromAgent: args.fromAgent,
138
+ type: args.type,
139
+ onlyMutations: true,
140
+ targetFiles: taskScope?.targetFiles,
141
+ dependsOn: taskScope?.dependsOn,
142
+ });
143
+ if (unread.activities.length === 0 && unread.signals.length === 0) {
144
+ return { status: 'No new peer messages or workspace mutations.' };
145
+ }
117
146
  return {
118
147
  activityLogs: MessageBus.formatActivityEntries(unread.activities),
119
148
  semanticSignals: MessageBus.formatSignals(unread.signals),
@@ -157,9 +186,16 @@ export async function executeScopedTool(name, args, workspaceRoot, agentId, bus,
157
186
  // If we got a file lock and had context from a previous writer, we should
158
187
  // inject that diff info back into the agent's tool return payload so it
159
188
  // knows someone else changed the file while it was queued.
160
- if (diffContext && typeof result === 'object') {
189
+ if (diffContext && typeof result === 'object' && result !== null) {
161
190
  result._orchestrationWarning = `NOTICE: Another agent modified this file while you were waiting. Diff context: ${diffContext}`;
162
191
  }
192
+ // In-band relevance-gated urgent signals (warnings, targeted requests, or upstream completions)
193
+ if (typeof result === 'object' && result !== null && typeof bus?.peekUrgentSignals === 'function') {
194
+ const urgentSignals = bus.peekUrgentSignals(agentId, taskScope);
195
+ if (urgentSignals && urgentSignals.length > 0) {
196
+ result._orchestrationNotice = MessageBus.formatSignals(urgentSignals);
197
+ }
198
+ }
163
199
  }
164
200
  finally {
165
201
  // Release the lock if we grabbed one
@@ -1,11 +1,12 @@
1
1
  /**
2
- * @fileoverview Sub-Agent Runner for Orchestration.
2
+ * @file Sub-Agent Runner for Orchestration.
3
3
  *
4
4
  * Implements the lifecycle, health monitoring, and tool-loop execution for a single
5
5
  * parallelized sub-agent.
6
6
  */
7
7
  import { MessageBus } from './messageBus.js';
8
8
  import { FileLockRegistry } from './fileLockRegistry.js';
9
+ import { TaskScope } from './scopedTools.js';
9
10
  /**
10
11
  * Scopes workspace file blocks within a context string by replacing full implementation
11
12
  * bodies with compact AST declaration outlines (types, classes, function signatures),
@@ -53,31 +54,20 @@ export declare class SubAgentRunner {
53
54
  private readonly globalContext;
54
55
  private readonly onProgress?;
55
56
  private readonly onTool?;
57
+ private readonly taskScope?;
58
+ /** Max time without a tool call or response before the agent is considered stalled */
59
+ static readonly STALL_TIMEOUT_MS = 300000;
56
60
  private chat;
57
- private lastHeartbeat;
58
61
  private creditsUsed;
59
62
  private inputTokens;
63
+ private lastHeartbeat;
60
64
  private outputTokens;
61
- /** Max time without a tool call or response before the agent is considered stalled */
62
- static readonly STALL_TIMEOUT_MS = 300000;
63
- constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined, onTool?: ((msg: string) => void) | undefined);
64
- /**
65
- * Constructs the base system instruction for this specific agent,
66
- * injecting scoped AST outlines for referenced workspace files to eliminate
67
- * multi-megabyte context bloat during parallel sub-agent execution.
68
- */
69
- private buildSystemInstruction;
70
65
  /**
71
66
  * Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
72
67
  * isn't marked as stalled while performing long-running commands.
73
68
  */
74
69
  private pingHeartbeat;
75
- /**
76
- * Executes the sub-agent with a health monitor harness.
77
- * Runs the tool loop until the model stops returning function calls,
78
- * an error occurs, or the stall timeout is hit.
79
- */
80
- execute(signal: AbortSignal): Promise<SubAgentResult>;
70
+ constructor(taskId: string, intent: string, workspaceRoot: string, bus: MessageBus, locks: FileLockRegistry, globalContext: string, onProgress?: ((msg: string) => void) | undefined, onTool?: ((msg: string) => void) | undefined, taskScope?: TaskScope | undefined);
81
71
  /**
82
72
  * Compacts older tool responses in the chat session history into concise references
83
73
  * (e.g. `[Read X lines from file.ts - tool execution completed]`), drastically reducing
@@ -87,6 +77,18 @@ export declare class SubAgentRunner {
87
77
  * @param turnsToKeep Number of recent turns to preserve in full (default: 1).
88
78
  */
89
79
  compactOlderToolResponses(turnsToKeep?: number): void;
80
+ /**
81
+ * Executes the sub-agent with a health monitor harness.
82
+ * Runs the tool loop until the model stops returning function calls,
83
+ * an error occurs, or the stall timeout is hit.
84
+ */
85
+ execute(signal: AbortSignal): Promise<SubAgentResult>;
86
+ /**
87
+ * Constructs the base system instruction for this specific agent,
88
+ * injecting scoped AST outlines for referenced workspace files to eliminate
89
+ * multi-megabyte context bloat during parallel sub-agent execution.
90
+ */
91
+ private buildSystemInstruction;
90
92
  /**
91
93
  * Accumulates token usage from the chat session.
92
94
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Sub-Agent Runner for Orchestration.
2
+ * @file Sub-Agent Runner for Orchestration.
3
3
  *
4
4
  * Implements the lifecycle, health monitoring, and tool-loop execution for a single
5
5
  * parallelized sub-agent.
@@ -64,6 +64,12 @@ function findMatchingFunctionCall(modelEntry, toolName, partIndex) {
64
64
  * @returns Compacted tool execution reference string.
65
65
  */
66
66
  export function createCompactToolReference(toolName, output, args) {
67
+ if (toolName === 'read_messages') {
68
+ return '[read_messages - peer activity reviewed]';
69
+ }
70
+ if (toolName === 'post_message') {
71
+ return '[post_message - coordination signal posted]';
72
+ }
67
73
  if (typeof output !== 'string') {
68
74
  return `[${toolName} tool execution completed]`;
69
75
  }
@@ -73,8 +79,8 @@ export function createCompactToolReference(toolName, output, args) {
73
79
  }
74
80
  const lineCount = output.split('\n').length;
75
81
  if (toolName === 'read_file') {
76
- const file = args?.filePath ? `from ${args.filePath}` : 'from file';
77
- return `[Read ${lineCount} lines ${file} - tool execution completed]`;
82
+ // Preserve full workspace_file content so the sub-agent retains source code in working memory
83
+ return output;
78
84
  }
79
85
  if (toolName === 'grep_search' || toolName === 'search_codebase') {
80
86
  const pattern = args?.pattern ? `for "${args.pattern}"` : '';
@@ -126,14 +132,25 @@ export class SubAgentRunner {
126
132
  globalContext;
127
133
  onProgress;
128
134
  onTool;
135
+ taskScope;
136
+ /** Max time without a tool call or response before the agent is considered stalled */
137
+ static STALL_TIMEOUT_MS = 300_000;
129
138
  chat;
130
- lastHeartbeat = Date.now();
131
139
  creditsUsed = 0;
132
140
  inputTokens = 0;
141
+ lastHeartbeat = Date.now();
133
142
  outputTokens = 0;
134
- /** Max time without a tool call or response before the agent is considered stalled */
135
- static STALL_TIMEOUT_MS = 300_000;
136
- constructor(taskId, intent, workspaceRoot, bus, locks, globalContext, onProgress, onTool) {
143
+ /**
144
+ * Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
145
+ * isn't marked as stalled while performing long-running commands.
146
+ */
147
+ pingHeartbeat = (msg) => {
148
+ this.lastHeartbeat = Date.now();
149
+ if (msg && this.onProgress) {
150
+ this.onProgress(msg);
151
+ }
152
+ };
153
+ constructor(taskId, intent, workspaceRoot, bus, locks, globalContext, onProgress, onTool, taskScope) {
137
154
  this.taskId = taskId;
138
155
  this.intent = intent;
139
156
  this.workspaceRoot = workspaceRoot;
@@ -142,6 +159,7 @@ export class SubAgentRunner {
142
159
  this.globalContext = globalContext;
143
160
  this.onProgress = onProgress;
144
161
  this.onTool = onTool;
162
+ this.taskScope = taskScope;
145
163
  let model = getGlobalActiveModel();
146
164
  if (model === GEMINI_MODELS.AUTO)
147
165
  model = GEMINI_MODELS.FLASH;
@@ -154,44 +172,56 @@ export class SubAgentRunner {
154
172
  });
155
173
  }
156
174
  /**
157
- * Constructs the base system instruction for this specific agent,
158
- * injecting scoped AST outlines for referenced workspace files to eliminate
159
- * multi-megabyte context bloat during parallel sub-agent execution.
160
- */
161
- buildSystemInstruction() {
162
- const scopedContext = scopeContext(this.globalContext);
163
- return (`<identity>\n` +
164
- `You are an autonomous Senior software developer sub-agent executing a specific portion of a larger task.\n` +
165
- `</identity>\n\n` +
166
- `<task_info>\n` +
167
- `Your task ID is: ${this.taskId}\n` +
168
- `Your objective: ${this.intent}\n` +
169
- `</task_info>\n\n` +
170
- `<reference_context>\n` +
171
- `DO NOT IMPLEMENT THIS FULL REQUEST. THIS IS JUST FOR CONTEXT.\n\n` +
172
- `${scopedContext}\n` +
173
- `</reference_context>\n\n` +
174
- `<critical_guidelines>\n` +
175
- `1. You are ONE worker in a team. Focus EXCLUSIVELY on your specific objective: "${this.intent}".\n` +
176
- `2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents handle other tasks.\n` +
177
- `3. EXECUTION WORKFLOW:\n` +
178
- ` - Step 1 (Investigate): Use 'grep_search', 'read_file', or 'list_directory' to inspect the codebase as needed.\n` +
179
- ` - Step 2 (Execute): Use 'modify_file' or 'write_file' to implement the required changes.\n` +
180
- ` - Step 3 (Verify): Use 'run_debug_script', 'run_fuzz_probe', 'check_heap_delta', 'check_behavioral_drift', or 'run_command' to test your changes if necessary.\n` +
181
- ` - Step 4 (Conclude): Once your specific objective is fully met, stop calling tools and return a text summary of your changes.\n` +
182
- `4. Coordinate: Use 'read_messages' to check for updates from other agents. Use 'post_message' if you discover breaking changes affecting others.\n` +
183
- `</critical_guidelines>`);
184
- }
185
- /**
186
- * Updates the heartbeat. Passed to `executeScopedTool` to ensure the agent
187
- * isn't marked as stalled while performing long-running commands.
175
+ * Compacts older tool responses in the chat session history into concise references
176
+ * (e.g. `[Read X lines from file.ts - tool execution completed]`), drastically reducing
177
+ * context window bloat across multi-turn sub-agent executions while preserving
178
+ * Gemini's functionCall <-> functionResponse protocol.
179
+ *
180
+ * @param turnsToKeep Number of recent turns to preserve in full (default: 1).
188
181
  */
189
- pingHeartbeat = (msg) => {
190
- this.lastHeartbeat = Date.now();
191
- if (msg && this.onProgress) {
192
- this.onProgress(msg);
182
+ compactOlderToolResponses(turnsToKeep = 1) {
183
+ const rawHistory = this.chat.getRawHistory();
184
+ // 1 turn = 1 user Content + 1 model Content pair (2 entries)
185
+ const cutoffIndex = Math.max(0, rawHistory.length - turnsToKeep * 2);
186
+ if (cutoffIndex <= 0)
187
+ return;
188
+ for (let i = 0; i < cutoffIndex; i++) {
189
+ const entry = rawHistory[i];
190
+ if (entry && entry.role === 'user' && Array.isArray(entry.parts)) {
191
+ const prevModelEntry = i > 0 ? rawHistory[i - 1] : undefined;
192
+ let funcRespIdx = 0;
193
+ for (const part of entry.parts) {
194
+ if (part && typeof part === 'object' && 'functionResponse' in part && part.functionResponse) {
195
+ const funcResp = part.functionResponse;
196
+ const toolName = funcResp.name || 'tool';
197
+ const matchingCall = findMatchingFunctionCall(prevModelEntry, toolName, funcRespIdx);
198
+ const args = matchingCall?.args;
199
+ if (funcResp.response && typeof funcResp.response === 'object') {
200
+ const respObj = funcResp.response;
201
+ if (toolName === 'read_messages') {
202
+ funcResp.response = { output: '[read_messages - peer activity reviewed]' };
203
+ }
204
+ else if (toolName === 'post_message') {
205
+ funcResp.response = { output: '[post_message - coordination signal posted]' };
206
+ }
207
+ else if (typeof respObj.output === 'string') {
208
+ respObj.output = createCompactToolReference(toolName, respObj.output, args);
209
+ }
210
+ else if (typeof respObj.result === 'string') {
211
+ respObj.result = createCompactToolReference(toolName, respObj.result, args);
212
+ }
213
+ else if (typeof respObj.output === 'object' && respObj.output !== null) {
214
+ respObj.output = createCompactToolReference(toolName, respObj.output, args);
215
+ }
216
+ }
217
+ funcRespIdx++;
218
+ }
219
+ }
220
+ }
193
221
  }
194
- };
222
+ // Also trigger proxy chat session pruning to collapse any remaining long fields
223
+ this.chat.pruneToolOutputHistory();
224
+ }
195
225
  /**
196
226
  * Executes the sub-agent with a health monitor harness.
197
227
  * Runs the tool loop until the model stops returning function calls,
@@ -243,7 +273,7 @@ export class SubAgentRunner {
243
273
  debugLog(`SubAgent [${this.taskId}]: Executing tool ${call.name}`);
244
274
  let responseData;
245
275
  try {
246
- responseData = await executeScopedTool(call.name, call.args, this.workspaceRoot, this.taskId, this.bus, this.locks, this.pingHeartbeat);
276
+ responseData = await executeScopedTool(call.name, call.args, this.workspaceRoot, this.taskId, this.bus, this.locks, this.pingHeartbeat, undefined, this.taskScope);
247
277
  }
248
278
  catch (err) {
249
279
  responseData = { error: err.message || String(err) };
@@ -309,46 +339,33 @@ export class SubAgentRunner {
309
339
  });
310
340
  }
311
341
  /**
312
- * Compacts older tool responses in the chat session history into concise references
313
- * (e.g. `[Read X lines from file.ts - tool execution completed]`), drastically reducing
314
- * context window bloat across multi-turn sub-agent executions while preserving
315
- * Gemini's functionCall <-> functionResponse protocol.
316
- *
317
- * @param turnsToKeep Number of recent turns to preserve in full (default: 1).
342
+ * Constructs the base system instruction for this specific agent,
343
+ * injecting scoped AST outlines for referenced workspace files to eliminate
344
+ * multi-megabyte context bloat during parallel sub-agent execution.
318
345
  */
319
- compactOlderToolResponses(turnsToKeep = 1) {
320
- const rawHistory = this.chat.getRawHistory();
321
- // 1 turn = 1 user Content + 1 model Content pair (2 entries)
322
- const cutoffIndex = Math.max(0, rawHistory.length - turnsToKeep * 2);
323
- if (cutoffIndex <= 0)
324
- return;
325
- for (let i = 0; i < cutoffIndex; i++) {
326
- const entry = rawHistory[i];
327
- if (entry && entry.role === 'user' && Array.isArray(entry.parts)) {
328
- const prevModelEntry = i > 0 ? rawHistory[i - 1] : undefined;
329
- let funcRespIdx = 0;
330
- for (const part of entry.parts) {
331
- if (part && typeof part === 'object' && 'functionResponse' in part && part.functionResponse) {
332
- const funcResp = part.functionResponse;
333
- const toolName = funcResp.name || 'tool';
334
- const matchingCall = findMatchingFunctionCall(prevModelEntry, toolName, funcRespIdx);
335
- const args = matchingCall?.args;
336
- if (funcResp.response && typeof funcResp.response === 'object') {
337
- const respObj = funcResp.response;
338
- if (typeof respObj.output === 'string') {
339
- respObj.output = createCompactToolReference(toolName, respObj.output, args);
340
- }
341
- if (typeof respObj.result === 'string') {
342
- respObj.result = createCompactToolReference(toolName, respObj.result, args);
343
- }
344
- }
345
- funcRespIdx++;
346
- }
347
- }
348
- }
349
- }
350
- // Also trigger proxy chat session pruning to collapse any remaining long fields
351
- this.chat.pruneToolOutputHistory();
346
+ buildSystemInstruction() {
347
+ const scopedContext = scopeContext(this.globalContext);
348
+ return (`<identity>\n` +
349
+ `You are an autonomous Senior software developer sub-agent executing a specific portion of a larger task.\n` +
350
+ `</identity>\n\n` +
351
+ `<task_info>\n` +
352
+ `Your task ID is: ${this.taskId}\n` +
353
+ `Your objective: ${this.intent}\n` +
354
+ `</task_info>\n\n` +
355
+ `<reference_context>\n` +
356
+ `DO NOT IMPLEMENT THIS FULL REQUEST. THIS IS JUST FOR CONTEXT.\n\n` +
357
+ `${scopedContext}\n` +
358
+ `</reference_context>\n\n` +
359
+ `<critical_guidelines>\n` +
360
+ `1. You are ONE worker in a team. Focus EXCLUSIVELY on your specific objective: "${this.intent}".\n` +
361
+ `2. DO NOT attempt to fulfill the entire original user request in the reference context. Other agents handle other tasks.\n` +
362
+ `3. EXECUTION WORKFLOW:\n` +
363
+ ` - Step 1 (Investigate): Use 'grep_search', 'read_file', or 'list_directory' to inspect the codebase as needed.\n` +
364
+ ` - Step 2 (Execute): Use 'modify_file' or 'write_file' to implement the required changes.\n` +
365
+ ` - Step 3 (Verify): Use 'run_debug_script', 'run_fuzz_probe', 'check_heap_delta', 'check_behavioral_drift', or 'run_command' to test your changes if necessary.\n` +
366
+ ` - Step 4 (Conclude): Once your specific objective is fully met, stop calling tools and return a text summary of your changes.\n` +
367
+ `4. Coordinate: Use 'read_messages' to check for updates from other agents. Use 'post_message' if you discover breaking changes affecting others.\n` +
368
+ `</critical_guidelines>`);
352
369
  }
353
370
  /**
354
371
  * Accumulates token usage from the chat session.
@@ -1,53 +1,83 @@
1
1
  import { ValidationResult } from './localSyntaxValidator.js';
2
+ /**
3
+ * Represents the result of a successful fuzzy match operation within a source file.
4
+ */
2
5
  export interface MatchResult {
6
+ /** The starting character byte offset of the matched region in the file content. */
3
7
  start: number;
8
+ /** The ending character byte offset of the matched region in the file content. */
4
9
  end: number;
10
+ /** The specific matching strategy that successfully resolved the snippet (e.g. 'Exact Match', 'Whitespace-Normalized Match', or Levenshtein details). */
5
11
  strategy: string;
6
12
  }
13
+ /**
14
+ * Represents a discrete search-and-replace block within a multi-block patch operation.
15
+ */
7
16
  export interface PatchBlock {
17
+ /** The target search snippet to locate within the source file. */
8
18
  search: string;
19
+ /** The replacement content to substitute into the matched region. */
9
20
  replace: string;
10
21
  }
22
+ /**
23
+ * Represents the comprehensive result of applying one or more patch blocks to a file.
24
+ */
11
25
  export interface PatchApplicationResult {
26
+ /** Indicates whether all patch blocks were successfully applied and optional syntax validation passed. */
12
27
  success: boolean;
28
+ /** The final transformed file content after patch application. */
13
29
  content: string;
30
+ /** The count of successfully applied patch blocks. */
14
31
  appliedBlocks: number;
32
+ /** The count of patch blocks that failed matching or application. */
15
33
  failedBlocks: number;
34
+ /** A collection of error or diagnostic messages encountered during patching. */
16
35
  errors: string[];
36
+ /** Optional syntax validation results from {@link localValidate} when syntax validation is enabled. */
17
37
  syntaxValidation?: ValidationResult;
18
38
  }
39
+ /**
40
+ * Configuration options governing patch application behavior.
41
+ */
19
42
  export interface ApplyPatchOptions {
43
+ /** Whether to perform local AST-aware syntax validation on the resulting content. Defaults to false. */
20
44
  validateSyntax?: boolean;
21
45
  }
22
46
  /**
23
- * Trims each line, collapses multiple spaces/tabs to single space,
24
- * and normalizes line endings.
47
+ * Normalizes whitespace in a text snippet by trimming each line, collapsing internal
48
+ * runs of spaces and tabs into a single space, and standardizing line endings.
49
+ *
50
+ * @param text - The raw input text string to normalize.
51
+ * @returns The whitespace-normalized string.
25
52
  */
26
53
  export declare function normalizeWhitespace(text: string): string;
27
54
  /**
28
- * Returns 0-1 similarity ratio.
29
- * 1.0 is exact match, 0.0 is completely different.
55
+ * Calculates the normalized Levenshtein similarity ratio between two strings on a scale from 0.0 to 1.0.
56
+ *
57
+ * @param a - The first string to compare.
58
+ * @param b - The second string to compare.
59
+ * @returns A similarity score between 0.0 (completely dissimilar) and 1.0 (identical).
30
60
  */
31
61
  export declare function levenshteinSimilarity(a: string, b: string): number;
32
62
  /**
33
- * Finds the best match for `searchContent` inside `fileContent`.
34
- * Uses a pipeline of strategies: Exact -> Whitespace-normalized -> Levenshtein.
35
- * Enforces uniqueness and higher confidence thresholds for non-unique search snippets across repetitive files.
63
+ * Finds the best match for a `searchContent` snippet inside `fileContent` using a robust 3-tier
64
+ * deterministic strategy pipeline:
65
+ *
66
+ * 1. **Exact Match**: Checks for literal substring occurrence. Enforces strict uniqueness (fails if ambiguous).
67
+ * 2. **Whitespace-Normalized Match**: Compares line-by-line after stripping leading/trailing whitespace and collapsing interior spaces. Enforces uniqueness.
68
+ * 3. **Levenshtein Similarity Matching**: Evaluates sliding window similarity across file regions with adaptive confidence thresholds (0.92 for short snippets, 0.88 for longer content) and distinct region grouping to disambiguate repetitive code blocks.
69
+ *
70
+ * @param fileContent - The full content of the source file being searched.
71
+ * @param searchContent - The search snippet to locate within the file.
72
+ * @returns A {@link MatchResult} containing start/end offsets and strategy metadata, or `null` if no unique or confident match could be determined.
36
73
  */
37
74
  export declare function findBestMatch(fileContent: string, searchContent: string): MatchResult | null;
38
- export declare function applyMatch(fileContent: string, match: MatchResult, replaceContent: string): string;
39
75
  /**
40
- * Parses differential patch block string into structured PatchBlock objects.
41
- * Supports blocks formatted as:
42
- * <<<<< SEARCH
43
- * search content
44
- * =====
45
- * replace content
46
- * >>>>> REPLACE
76
+ * Applies a specific match result substitution to source file content.
77
+ *
78
+ * @param fileContent - The original source file content.
79
+ * @param match - The {@link MatchResult} specifying character offsets where the replacement occurs.
80
+ * @param replaceContent - The replacement string to substitute into the matched region.
81
+ * @returns The transformed file content string.
47
82
  */
48
- export declare function parsePatchBlocks(patchText: string): PatchBlock[];
49
- /**
50
- * Applies a sequence of search/replace patch blocks to file content
51
- * using fuzzy matching, and validates local syntax on the result.
52
- */
53
- export declare function applyPatch(fileContent: string, patchTextOrBlocks: string | PatchBlock[], filePath?: string, options?: ApplyPatchOptions): PatchApplicationResult;
83
+ export declare function applyMatch(fileContent: string, match: MatchResult, replaceContent: string): string;