minovative-mind-cli 1.5.1 → 2.0.0

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 (75) hide show
  1. package/README.md +51 -45
  2. package/dist/commands/chat.js +7 -2
  3. package/dist/services/agent/slashCommands.js +156 -30
  4. package/dist/services/agent/toolLoop.d.ts +1 -1
  5. package/dist/services/agent/toolLoop.js +7 -2
  6. package/dist/services/agent/types.d.ts +2 -0
  7. package/dist/services/agent-tools.d.ts +9 -4
  8. package/dist/services/agent-tools.js +145 -21
  9. package/dist/services/agent.d.ts +8 -0
  10. package/dist/services/agent.js +285 -40
  11. package/dist/services/ai.d.ts +19 -5
  12. package/dist/services/ai.js +167 -35
  13. package/dist/services/changeLogger.d.ts +142 -0
  14. package/dist/services/changeLogger.js +132 -3
  15. package/dist/services/contextAgent.d.ts +6 -1
  16. package/dist/services/contextAgent.js +95 -14
  17. package/dist/services/embeddingIndex.d.ts +82 -0
  18. package/dist/services/embeddingIndex.js +613 -0
  19. package/dist/services/investigationComplexity.d.ts +45 -0
  20. package/dist/services/investigationComplexity.js +91 -0
  21. package/dist/services/metrics.d.ts +18 -0
  22. package/dist/services/metrics.js +7 -0
  23. package/dist/services/orchestration/fileLockRegistry.d.ts +125 -0
  24. package/dist/services/orchestration/fileLockRegistry.js +276 -0
  25. package/dist/services/orchestration/investigationAgent.d.ts +85 -0
  26. package/dist/services/orchestration/investigationAgent.js +359 -0
  27. package/dist/services/orchestration/investigationOrchestrator.d.ts +53 -0
  28. package/dist/services/orchestration/investigationOrchestrator.js +180 -0
  29. package/dist/services/orchestration/messageBus.d.ts +162 -0
  30. package/dist/services/orchestration/messageBus.js +225 -0
  31. package/dist/services/orchestration/orchestrator.d.ts +45 -0
  32. package/dist/services/orchestration/orchestrator.js +214 -0
  33. package/dist/services/orchestration/readCache.d.ts +79 -0
  34. package/dist/services/orchestration/readCache.js +108 -0
  35. package/dist/services/orchestration/scopedTools.d.ts +57 -0
  36. package/dist/services/orchestration/scopedTools.js +172 -0
  37. package/dist/services/orchestration/subAgent.d.ts +58 -0
  38. package/dist/services/orchestration/subAgent.js +187 -0
  39. package/dist/services/orchestration/taskGraph.d.ts +129 -0
  40. package/dist/services/orchestration/taskGraph.js +254 -0
  41. package/dist/services/proxyClient.d.ts +25 -0
  42. package/dist/services/proxyClient.js +60 -0
  43. package/dist/utils/asyncContext.d.ts +16 -0
  44. package/dist/utils/asyncContext.js +25 -0
  45. package/dist/utils/config.d.ts +3 -1
  46. package/dist/utils/config.js +3 -1
  47. package/dist/utils/contextPrompts.js +3 -2
  48. package/dist/utils/dependencyTracer/modules/api.d.ts +9 -0
  49. package/dist/utils/dependencyTracer/modules/api.js +62 -0
  50. package/dist/utils/dependencyTracer/modules/graph.d.ts +9 -0
  51. package/dist/utils/dependencyTracer/modules/graph.js +23 -0
  52. package/dist/utils/dependencyTracer/modules/profiles.d.ts +7 -0
  53. package/dist/utils/dependencyTracer/modules/profiles.js +120 -0
  54. package/dist/utils/dependencyTracer/modules/resolver.d.ts +7 -0
  55. package/dist/utils/dependencyTracer/modules/resolver.js +51 -0
  56. package/dist/utils/dependencyTracer/modules/types.d.ts +4 -0
  57. package/dist/utils/dependencyTracer/modules/types.js +1 -0
  58. package/dist/utils/dependencyTracer/modules/walker.d.ts +1 -0
  59. package/dist/utils/dependencyTracer/modules/walker.js +48 -0
  60. package/dist/utils/dependencyTracer.js +31 -17
  61. package/dist/utils/excludedExtensions.js +0 -1
  62. package/dist/utils/historyPrompt.d.ts +9 -0
  63. package/dist/utils/historyPrompt.js +87 -0
  64. package/dist/utils/logo.js +7 -7
  65. package/dist/utils/paste.d.ts +21 -0
  66. package/dist/utils/paste.js +22 -1
  67. package/dist/utils/profiles.d.ts +2 -0
  68. package/dist/utils/profiles.js +44 -0
  69. package/dist/utils/projectStorage.js +10 -7
  70. package/dist/utils/systemPrompts.d.ts +6 -3
  71. package/dist/utils/systemPrompts.js +106 -5
  72. package/dist/utils/types.d.ts +33 -0
  73. package/dist/utils/types.js +1 -0
  74. package/oclif.manifest.json +2 -2
  75. package/package.json +4 -3
@@ -1,16 +1,62 @@
1
1
  import { readCache, writeCache } from '../utils/projectStorage.js';
2
+ /**
3
+ * Service class responsible for tracking, persisting, and reverting file modifications in the workspace.
4
+ * Uses a local project state file (`revert_state.json`) to persist state across CLI invocations.
5
+ */
2
6
  class ChangeLogger {
7
+ /**
8
+ * Ordered history stack of committed changesets, oldest first, newest last.
9
+ */
3
10
  changeStack = [];
11
+ /**
12
+ * The active changeset currently receiving recorded modifications.
13
+ * Null if no change transaction is actively in progress.
14
+ */
4
15
  currentChangeSet = null;
16
+ /**
17
+ * Absolute path to the active workspace root directory.
18
+ */
5
19
  workspaceRoot = '';
20
+ /**
21
+ * Maximum number of historical changesets retained in memory and on disk.
22
+ * Older changesets are discarded on save once this limit is exceeded.
23
+ */
6
24
  MAX_HISTORY = 10;
25
+ /**
26
+ * Whether the change logger is currently enabled.
27
+ */
28
+ isEnabled = true;
29
+ getIsEnabled() {
30
+ return this.isEnabled;
31
+ }
32
+ setIsEnabled(enabled) {
33
+ this.isEnabled = enabled;
34
+ this.saveState();
35
+ }
36
+ /**
37
+ * Initializes the ChangeLogger by setting the workspace root and loading existing
38
+ * history from the local `.minovativemind/revert_state.json` cache file.
39
+ *
40
+ * @param workspaceRoot - The absolute path to the project workspace root.
41
+ */
7
42
  init(workspaceRoot) {
8
43
  this.workspaceRoot = workspaceRoot;
9
44
  const savedState = readCache(workspaceRoot, 'revert_state.json');
10
- if (savedState && Array.isArray(savedState)) {
11
- this.changeStack = savedState;
45
+ if (savedState) {
46
+ if (Array.isArray(savedState)) {
47
+ this.changeStack = savedState;
48
+ }
49
+ else {
50
+ this.changeStack = savedState.history || [];
51
+ this.isEnabled = savedState.isEnabled ?? true;
52
+ }
12
53
  }
13
54
  }
55
+ /**
56
+ * Persists the current committed change stack to the local cache directory.
57
+ * Enforces the `MAX_HISTORY` retention policy by slicing out older entries.
58
+ * Does nothing if the workspace root has not yet been initialized.
59
+ */
14
60
  saveState() {
15
61
  if (!this.workspaceRoot)
16
62
  return;
@@ -18,11 +64,27 @@ class ChangeLogger {
18
64
  if (this.changeStack.length > this.MAX_HISTORY) {
19
65
  this.changeStack = this.changeStack.slice(-this.MAX_HISTORY);
20
66
  }
21
- writeCache(this.workspaceRoot, 'revert_state.json', this.changeStack);
67
+ writeCache(this.workspaceRoot, 'revert_state.json', {
68
+ history: this.changeStack,
69
+ isEnabled: this.isEnabled
70
+ });
22
71
  }
72
+ /**
73
+ * Retrieves a copy of the active history stack.
74
+ *
75
+ * @returns A shallow copy of the array containing all tracked changesets.
76
+ */
23
77
  getHistory() {
24
78
  return [...this.changeStack];
25
79
  }
80
+ /**
81
+ * Traverses backward through the history stack, popping and collecting changesets
82
+ * until a changeset matching the specified timestamp is encountered (inclusive).
83
+ * Persists the resulting truncated stack after execution.
84
+ *
85
+ * @param timestamp - The unique timestamp identifier of the target changeset to revert back to.
86
+ * @returns An array of popped changesets ordered from newest to oldest.
87
+ */
26
88
  popUntil(timestamp) {
27
89
  const changesToRevert = [];
28
90
  while (this.changeStack.length > 0) {
@@ -36,7 +98,15 @@ class ChangeLogger {
36
98
  this.saveState();
37
99
  return changesToRevert;
38
100
  }
101
+ /**
102
+ * Initiates a new transactional changeset. If a previous changeset was in progress and
103
+ * uncommitted, it is automatically finalized and committed to the history stack first.
104
+ *
105
+ * @param description - Descriptive label summarizing the purpose of the new changeset.
106
+ */
39
107
  startChangeSet(description) {
108
+ if (!this.isEnabled)
109
+ return;
40
110
  if (this.currentChangeSet) {
41
111
  this.commitChangeSet();
42
112
  }
@@ -47,7 +117,23 @@ class ChangeLogger {
47
117
  status: 'partial',
48
118
  };
49
119
  }
120
+ /**
121
+ * Records a file change within the active changeset.
122
+ * If no changeset is active, an anonymous default changeset is automatically initialized.
123
+ *
124
+ * @remarks
125
+ * Idempotency Guard: If the specified file already has an entry in the current changeset,
126
+ * the original state is preserved. This ensures that multiple successive modifications to
127
+ * the same file within a single transaction map back to the true original state prior to
128
+ * the onset of the transaction.
129
+ *
130
+ * @param filePath - The relative or absolute path of the file being altered.
131
+ * @param originalContent - The text content of the file prior to the change, or null if created.
132
+ * @param action - The classification of the operation ('create', 'modify', or 'delete').
133
+ */
50
134
  logChange(filePath, originalContent, action) {
135
+ if (!this.isEnabled)
136
+ return;
51
137
  if (!this.currentChangeSet) {
52
138
  // Create a default changeset if none was started explicitly
53
139
  this.startChangeSet('Anonymous change');
@@ -67,11 +153,30 @@ class ChangeLogger {
67
153
  action,
68
154
  });
69
155
  }
156
+ /**
157
+ * Marks the status of the active changeset as 'complete', indicating that all scheduled
158
+ * operations within the scope of this transaction finished without interruption.
159
+ */
70
160
  markComplete() {
71
161
  if (this.currentChangeSet) {
72
162
  this.currentChangeSet.status = 'complete';
73
163
  }
74
164
  }
165
+ /**
166
+ * Retrieves a list of file paths that have been modified in the currently active changeset.
167
+ *
168
+ * @returns An array of string file paths, or an empty array if no changeset is active.
169
+ */
170
+ getChangedFiles() {
171
+ if (!this.currentChangeSet)
172
+ return [];
173
+ return this.currentChangeSet.changes.map((c) => c.filePath);
174
+ }
175
+ /**
176
+ * Commits the active changeset to the history stack and persists the state to disk,
177
+ * provided that the changeset actually contains one or more recorded file modifications.
178
+ * Resets the active changeset state to null.
179
+ */
75
180
  commitChangeSet() {
76
181
  if (this.currentChangeSet && this.currentChangeSet.changes.length > 0) {
77
182
  this.changeStack.push(this.currentChangeSet);
@@ -79,14 +184,30 @@ class ChangeLogger {
79
184
  }
80
185
  this.currentChangeSet = null;
81
186
  }
187
+ /**
188
+ * Retrieves the most recently committed changeset from the history stack without removing it.
189
+ *
190
+ * @returns The latest committed ChangeSet, or null if the stack is currently empty.
191
+ */
82
192
  getLastChangeSet() {
83
193
  if (this.changeStack.length === 0)
84
194
  return null;
85
195
  return this.changeStack[this.changeStack.length - 1];
86
196
  }
197
+ /**
198
+ * Returns the active, uncommitted changeset currently receiving modifications.
199
+ *
200
+ * @returns The in-progress ChangeSet, or null if no changeset is active.
201
+ */
87
202
  getCurrentChangeSet() {
88
203
  return this.currentChangeSet;
89
204
  }
205
+ /**
206
+ * Removes and returns the most recently committed changeset from the history stack.
207
+ * Persists the newly truncated stack state back to the cache.
208
+ *
209
+ * @returns The removed ChangeSet, or null if no historical changes exist.
210
+ */
90
211
  popLastChangeSet() {
91
212
  if (this.changeStack.length === 0)
92
213
  return null;
@@ -96,8 +217,16 @@ class ChangeLogger {
96
217
  }
97
218
  return popped;
98
219
  }
220
+ /**
221
+ * Checks whether the history stack contains any tracked changesets.
222
+ *
223
+ * @returns True if at least one changeset is recorded; false otherwise.
224
+ */
99
225
  hasChanges() {
100
226
  return this.changeStack.length > 0;
101
227
  }
102
228
  }
229
+ /**
230
+ * Singleton instance of the ChangeLogger service exported for application-wide tracking.
231
+ */
103
232
  export const changeLogger = new ChangeLogger();
@@ -1,15 +1,20 @@
1
1
  export interface ContextAgentResult {
2
2
  projectTree: string;
3
3
  projectType: string;
4
- relevantFiles: Map<string, string>;
4
+ relevantFiles: Map<string, {
5
+ text: string;
6
+ inlineData?: any;
7
+ }>;
5
8
  summary: string;
6
9
  webSearchSummary?: string;
10
+ isParallel?: boolean;
7
11
  }
8
12
  export interface IntentRoute {
9
13
  needsContext: boolean;
10
14
  targetAgent: 'CHAT' | 'EXECUTE';
11
15
  }
12
16
  export declare function routeIntent(userRequest: string, chatHistory?: string): Promise<IntentRoute>;
17
+ export declare function evaluateExecutionComplexity(userRequest: string, investigationSummary: string | undefined, numRelevantFiles: number, chatHistory?: string): Promise<'EASY' | 'HARD'>;
13
18
  export declare function gatherContext(workspaceRoot: string, userRequest: string, chatHistory: string | undefined, inputHandler: {
14
19
  getAndClear: () => string;
15
20
  waitForPrompt: () => Promise<void>;
@@ -1,11 +1,15 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import pc from 'picocolors';
4
- import { createContextAgentSession, createIntentRouterSession, createWebSearchAgentSession } from './ai.js';
5
- import { listDirectory, grepSearch, readFile, traceDependencies, findRecentChanges } from './agent-tools.js';
4
+ import { createContextAgentSession, createIntentRouterSession, createWebSearchAgentSession, createExecutionComplexitySession, } from './ai.js';
5
+ import { evaluateInvestigationComplexity } from './investigationComplexity.js';
6
+ import { InvestigationOrchestrator } from './orchestration/investigationOrchestrator.js';
7
+ import { isSubAgentsEnabled, listDirectory, grepSearch, readFile, traceDependencies, findRecentChanges } from './agent-tools.js';
6
8
  import { debugLog } from '../utils/logger.js';
7
9
  import { buildDependencyGraph } from '../utils/dependencyTracer.js';
8
10
  import { runEphemeralScript } from '../utils/analysisRunner.js';
11
+ import { getMetricCollector } from './metrics.js';
12
+ const metrics = getMetricCollector();
9
13
  async function detectProjectType(workspaceRoot) {
10
14
  const types = [];
11
15
  const fileExists = async (fileName) => {
@@ -157,6 +161,26 @@ export async function routeIntent(userRequest, chatHistory = '') {
157
161
  return { needsContext: true, targetAgent: 'EXECUTE' };
158
162
  }
159
163
  }
164
+ export async function evaluateExecutionComplexity(userRequest, investigationSummary, numRelevantFiles, chatHistory = '') {
165
+ try {
166
+ const session = createExecutionComplexitySession();
167
+ let prompt = `User Request: "${userRequest}"
168
+ Investigation Summary: ${investigationSummary || 'None (0 files needed)'}
169
+ Number of Relevant Files: ${numRelevantFiles}`;
170
+ if (chatHistory) {
171
+ prompt = `Previous Conversation Context:\n${chatHistory}\n\n${prompt}`;
172
+ }
173
+ const result = await session.sendMessage(prompt);
174
+ const text = result.response.text()?.trim() || '{}';
175
+ const parsed = JSON.parse(text);
176
+ debugLog(`Execution Complexity Parsed: ${JSON.stringify(parsed)}`);
177
+ return parsed.complexity === 'EASY' ? 'EASY' : 'HARD';
178
+ }
179
+ catch (e) {
180
+ debugLog(`Execution Complexity Router failed, falling back to HARD. Error: ${String(e)}`);
181
+ return 'HARD';
182
+ }
183
+ }
160
184
  export async function gatherContext(workspaceRoot, userRequest, chatHistory = '', inputHandler, abortSignal, onProgress) {
161
185
  // Always skip slash commands for zero latency
162
186
  if (userRequest.startsWith('/')) {
@@ -174,6 +198,23 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
174
198
  projectTree = projectTree.substring(0, 30000) + '\n... (Project tree truncated due to size)';
175
199
  }
176
200
  const projectType = await detectProjectType(workspaceRoot);
201
+ // ─── Parallel Investigation Gate ─────────────────────────────
202
+ if (isSubAgentsEnabled()) {
203
+ // Determine complexity and domain breakdown
204
+ const approxFiles = projectTree.split('\n').length;
205
+ const complexity = await evaluateInvestigationComplexity(userRequest, projectType, approxFiles, chatHistory);
206
+ if (complexity.strategy === 'PARALLEL' && complexity.agentAssignments.length > 0) {
207
+ const orchestrator = new InvestigationOrchestrator();
208
+ const parallelResult = await orchestrator.runParallelInvestigation(userRequest, complexity.agentAssignments, workspaceRoot, projectTree, projectType, chatHistory, abortSignal, onProgress);
209
+ if (parallelResult !== null) {
210
+ // If parallel investigation succeeded, we are done. Return immediately.
211
+ parallelResult.isParallel = true;
212
+ return { contextResult: parallelResult, targetAgent, chainedMessages: [] };
213
+ }
214
+ // If it returned null, all agents crashed. Fall through to single agent fallback.
215
+ }
216
+ }
217
+ // ─── Single Agent Path (Fallback/Default) ────────────────────
177
218
  const session = createContextAgentSession();
178
219
  const relevantFiles = new Map();
179
220
  let summary = 'No relevant context found.';
@@ -185,7 +226,8 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
185
226
  currentMessage = `Previous Conversation Context:\n${chatHistory}\n\n` + currentMessage;
186
227
  }
187
228
  currentMessage += `\n\nStart investigating to find relevant files.`;
188
- const MAX_TURNS = 100;
229
+ const MAX_TURNS = Infinity;
230
+ let isInvestigationFinished = false;
189
231
  for (let turn = 0; turn < MAX_TURNS; turn++) {
190
232
  await inputHandler.waitForPrompt();
191
233
  const queuedMsg = inputHandler.getAndClear();
@@ -276,11 +318,12 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
276
318
  if (!relevantFiles.has(filePath)) {
277
319
  const readResult = await readFile(workspaceRoot, filePath);
278
320
  if (!readResult.error) {
279
- relevantFiles.set(filePath, readResult.output);
321
+ relevantFiles.set(filePath, { text: readResult.output, inlineData: readResult.inlineData });
280
322
  }
281
323
  }
282
324
  }
283
325
  isFinished = true;
326
+ isInvestigationFinished = true;
284
327
  // ── Auto-trace reverse dependencies ──
285
328
  // When the Context Agent finalizes its investigation, we automatically
286
329
  // discover files that DEPEND ON the selected files. This ensures the
@@ -306,7 +349,7 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
306
349
  if (!relevantFiles.has(dep)) {
307
350
  const readResult = await readFile(workspaceRoot, dep);
308
351
  if (!readResult.error) {
309
- relevantFiles.set(dep, readResult.output);
352
+ relevantFiles.set(dep, { text: readResult.output, inlineData: readResult.inlineData });
310
353
  added++;
311
354
  }
312
355
  }
@@ -336,7 +379,7 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
336
379
  for (const filePath of filesToRead) {
337
380
  const readResult = await readFile(workspaceRoot, filePath);
338
381
  if (!readResult.error) {
339
- relevantFiles.set(filePath, readResult.output);
382
+ relevantFiles.set(filePath, { text: readResult.output, inlineData: readResult.inlineData });
340
383
  output += `\n--- File: ${filePath} ---\n${readResult.output}\n`;
341
384
  }
342
385
  else {
@@ -371,10 +414,47 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
371
414
  },
372
415
  });
373
416
  }
417
+ else if (call.name === 'semantic_search') {
418
+ const query = args.query;
419
+ const topK = args.topK || 5;
420
+ let output = '';
421
+ try {
422
+ const { getEmbeddingIndex } = await import('./embeddingIndex.js');
423
+ const index = getEmbeddingIndex();
424
+ if (!index.isReady()) {
425
+ // Lazy load from disk or build if missing
426
+ const loaded = await index.load(workspaceRoot);
427
+ if (!loaded) {
428
+ if (onProgress)
429
+ onProgress('Building semantic search index (first run)...');
430
+ await index.buildIndex(workspaceRoot, onProgress);
431
+ await index.save(workspaceRoot);
432
+ }
433
+ }
434
+ const results = await index.search(query, topK);
435
+ if (results.length === 0) {
436
+ output = 'No semantically similar code found. (Index might be empty or embedding failed)';
437
+ }
438
+ else {
439
+ output = results
440
+ .map((r) => `[Score: ${r.score.toFixed(3)}] ${r.filePath}:${r.startLine}-${r.endLine}\n${r.preview}`)
441
+ .join('\n---\n');
442
+ }
443
+ }
444
+ catch (e) {
445
+ output = `Semantic search failed: ${e.message}`;
446
+ }
447
+ functionResponses.push({
448
+ functionResponse: {
449
+ name: call.name,
450
+ response: { output },
451
+ },
452
+ });
453
+ }
374
454
  else if (call.name === 'read_file') {
375
455
  const readRes = await readFile(workspaceRoot, args.filePath, args.startLine, args.endLine, args.targetElements);
376
456
  if (!readRes.error) {
377
- relevantFiles.set(args.filePath, readRes.output);
457
+ relevantFiles.set(args.filePath, { text: readRes.output, inlineData: readRes.inlineData });
378
458
  }
379
459
  functionResponses.push({
380
460
  functionResponse: {
@@ -452,14 +532,15 @@ export async function gatherContext(workspaceRoot, userRequest, chatHistory = ''
452
532
  // Prepare next turn
453
533
  currentMessage = functionResponses;
454
534
  }
535
+ const collector = getMetricCollector();
536
+ if (collector) {
537
+ collector.recordContextSelectedFiles(Array.from(relevantFiles.keys()));
538
+ if (!isInvestigationFinished || relevantFiles.size === 0) {
539
+ collector.recordInvestigationFailure();
540
+ }
541
+ }
455
542
  return {
456
- contextResult: {
457
- projectTree,
458
- projectType,
459
- relevantFiles,
460
- summary,
461
- webSearchSummary,
462
- },
543
+ contextResult: { projectTree, projectType, relevantFiles, summary, webSearchSummary, isParallel: false },
463
544
  targetAgent,
464
545
  chainedMessages,
465
546
  };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A single result from a semantic search query.
3
+ */
4
+ export interface SearchResult {
5
+ filePath: string;
6
+ startLine: number;
7
+ endLine: number;
8
+ label: string;
9
+ preview: string;
10
+ /** Cosine similarity score between 0.0 and 1.0 */
11
+ score: number;
12
+ }
13
+ /**
14
+ * Manages a persistent local vector index for semantic code search.
15
+ *
16
+ * Lifecycle:
17
+ * 1. On first `gatherContext` call, if no index exists on disk, `buildIndex()` is called.
18
+ * 2. After the Execution Agent modifies files, `updateIndex()` delta-reindexes only changed files.
19
+ * 3. `search()` embeds the query (1 API call) and scans the local index via cosine similarity.
20
+ * 4. Index is persisted to `.minovativemind/embeddings/index.json` via atomic writes.
21
+ */
22
+ export declare class EmbeddingIndex {
23
+ private chunks;
24
+ private modelVersion;
25
+ /**
26
+ * Builds the full index for a workspace by scanning all eligible source files,
27
+ * chunking them at function/class boundaries, and batch-embedding them.
28
+ *
29
+ * @param workspaceRoot - Absolute path to the workspace root directory.
30
+ * @param onProgress - Optional callback for user-facing progress messages.
31
+ */
32
+ buildIndex(workspaceRoot: string, onProgress?: (msg: string) => void): Promise<void>;
33
+ /**
34
+ * Delta-updates the index for files that have changed.
35
+ * Removes stale chunks for modified/deleted files, then re-chunks and
36
+ * re-embeds only the affected files.
37
+ *
38
+ * @param workspaceRoot - Absolute path to the workspace root.
39
+ * @param changedFiles - Array of relative file paths that were modified.
40
+ */
41
+ updateIndex(workspaceRoot: string, changedFiles: string[]): Promise<void>;
42
+ /**
43
+ * Executes a semantic search against the local index.
44
+ *
45
+ * 1. Embeds the query string (single API call).
46
+ * 2. Computes cosine similarity against all indexed chunks.
47
+ * 3. Returns the top-K results sorted by descending similarity score.
48
+ *
49
+ * @param query - Natural language description of what to search for.
50
+ * @param topK - Number of results to return (default 5, max 15).
51
+ * @returns Array of search results with file paths, line ranges, and scores.
52
+ */
53
+ search(query: string, topK?: number): Promise<SearchResult[]>;
54
+ /**
55
+ * Persists the index to disk using the atomic write pattern from projectStorage.
56
+ */
57
+ save(workspaceRoot: string): Promise<void>;
58
+ /**
59
+ * Loads the index from disk. Returns false if no index exists or the model
60
+ * version has changed (requiring a full rebuild).
61
+ */
62
+ load(workspaceRoot: string): Promise<boolean>;
63
+ /** Check if the index is loaded and contains chunks */
64
+ isReady(): boolean;
65
+ /** Get the number of chunks in the index */
66
+ get size(): number;
67
+ /**
68
+ * Recursively walks the workspace, reading and chunking eligible files.
69
+ * Respects .gitignore patterns and EXCLUDED_EXTENSIONS.
70
+ */
71
+ private walkAndChunk;
72
+ /**
73
+ * Generates a content hash for delta-detection.
74
+ * Uses SHA-256 of (filePath + content) to detect changes.
75
+ */
76
+ private hashChunk;
77
+ }
78
+ /**
79
+ * Returns the global singleton EmbeddingIndex instance.
80
+ * Lazily created on first access.
81
+ */
82
+ export declare function getEmbeddingIndex(): EmbeddingIndex;