minovative-mind-cli 1.5.0 → 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.
- package/README.md +51 -45
- package/dist/commands/chat.js +10 -5
- package/dist/services/agent/slashCommands.js +163 -37
- package/dist/services/agent/toolLoop.d.ts +1 -1
- package/dist/services/agent/toolLoop.js +7 -2
- package/dist/services/agent/types.d.ts +2 -0
- package/dist/services/agent-tools.d.ts +9 -4
- package/dist/services/agent-tools.js +145 -21
- package/dist/services/agent.d.ts +8 -0
- package/dist/services/agent.js +294 -38
- package/dist/services/ai.d.ts +19 -5
- package/dist/services/ai.js +167 -35
- package/dist/services/changeLogger.d.ts +142 -0
- package/dist/services/changeLogger.js +132 -3
- package/dist/services/contextAgent.d.ts +6 -1
- package/dist/services/contextAgent.js +95 -14
- package/dist/services/embeddingIndex.d.ts +82 -0
- package/dist/services/embeddingIndex.js +613 -0
- package/dist/services/investigationComplexity.d.ts +45 -0
- package/dist/services/investigationComplexity.js +91 -0
- package/dist/services/metrics.d.ts +18 -0
- package/dist/services/metrics.js +7 -0
- package/dist/services/orchestration/fileLockRegistry.d.ts +125 -0
- package/dist/services/orchestration/fileLockRegistry.js +276 -0
- package/dist/services/orchestration/investigationAgent.d.ts +85 -0
- package/dist/services/orchestration/investigationAgent.js +359 -0
- package/dist/services/orchestration/investigationOrchestrator.d.ts +53 -0
- package/dist/services/orchestration/investigationOrchestrator.js +180 -0
- package/dist/services/orchestration/messageBus.d.ts +162 -0
- package/dist/services/orchestration/messageBus.js +225 -0
- package/dist/services/orchestration/orchestrator.d.ts +45 -0
- package/dist/services/orchestration/orchestrator.js +214 -0
- package/dist/services/orchestration/readCache.d.ts +79 -0
- package/dist/services/orchestration/readCache.js +108 -0
- package/dist/services/orchestration/scopedTools.d.ts +57 -0
- package/dist/services/orchestration/scopedTools.js +172 -0
- package/dist/services/orchestration/subAgent.d.ts +58 -0
- package/dist/services/orchestration/subAgent.js +187 -0
- package/dist/services/orchestration/taskGraph.d.ts +129 -0
- package/dist/services/orchestration/taskGraph.js +254 -0
- package/dist/services/proxyClient.d.ts +25 -0
- package/dist/services/proxyClient.js +60 -0
- package/dist/utils/asyncContext.d.ts +16 -0
- package/dist/utils/asyncContext.js +25 -0
- package/dist/utils/config.d.ts +3 -1
- package/dist/utils/config.js +3 -1
- package/dist/utils/contextPrompts.js +3 -2
- package/dist/utils/dependencyTracer/modules/api.d.ts +9 -0
- package/dist/utils/dependencyTracer/modules/api.js +62 -0
- package/dist/utils/dependencyTracer/modules/graph.d.ts +9 -0
- package/dist/utils/dependencyTracer/modules/graph.js +23 -0
- package/dist/utils/dependencyTracer/modules/profiles.d.ts +7 -0
- package/dist/utils/dependencyTracer/modules/profiles.js +120 -0
- package/dist/utils/dependencyTracer/modules/resolver.d.ts +7 -0
- package/dist/utils/dependencyTracer/modules/resolver.js +51 -0
- package/dist/utils/dependencyTracer/modules/types.d.ts +4 -0
- package/dist/utils/dependencyTracer/modules/types.js +1 -0
- package/dist/utils/dependencyTracer/modules/walker.d.ts +1 -0
- package/dist/utils/dependencyTracer/modules/walker.js +48 -0
- package/dist/utils/dependencyTracer.js +31 -17
- package/dist/utils/excludedExtensions.js +0 -1
- package/dist/utils/historyPrompt.d.ts +9 -0
- package/dist/utils/historyPrompt.js +87 -0
- package/dist/utils/logo.d.ts +2 -0
- package/dist/utils/logo.js +31 -10
- package/dist/utils/paste.d.ts +21 -0
- package/dist/utils/paste.js +22 -1
- package/dist/utils/profiles.d.ts +2 -0
- package/dist/utils/profiles.js +44 -0
- package/dist/utils/projectStorage.js +10 -7
- package/dist/utils/systemPrompts.d.ts +6 -3
- package/dist/utils/systemPrompts.js +106 -5
- package/dist/utils/types.d.ts +33 -0
- package/dist/utils/types.js +1 -0
- package/oclif.manifest.json +2 -2
- package/package.json +5 -3
package/dist/services/ai.js
CHANGED
|
@@ -1,10 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { GEMINI_MODELS, DEFAULT_MODEL, MAX_OUTPUT_TOKENS } from '../utils/config.js';
|
|
2
|
+
import { getToolDeclarations } from './agent-tools.js';
|
|
3
3
|
import { ProxyClient } from './proxyClient.js';
|
|
4
4
|
import { getAuthorizedIdToken } from './auth.js';
|
|
5
5
|
import { debugLog } from '../utils/logger.js';
|
|
6
|
-
import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
|
|
6
|
+
import { GENERAL_CHAT_INSTRUCTION, PLAN_EXECUTION_INSTRUCTION, PLAN_MODE_INSTRUCTION, CONTEXT_SYSTEM_INSTRUCTION, INTENT_ROUTER_SYSTEM_INSTRUCTION, WEB_SEARCH_SYSTEM_INSTRUCTION, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, } from '../utils/systemPrompts.js';
|
|
7
|
+
import { getMetricCollector } from './metrics.js';
|
|
8
|
+
// ─── Model Overrides (for Regression Testing) ────────────────────────
|
|
9
|
+
let contextModelOverride = null;
|
|
10
|
+
let contextTempOverride = null;
|
|
11
|
+
let executionModelOverride = null;
|
|
12
|
+
let executionTempOverride = null;
|
|
13
|
+
export function setModelOverride(agent, overrideStr) {
|
|
14
|
+
const [model, temp] = overrideStr.split(':');
|
|
15
|
+
if (agent === 'context') {
|
|
16
|
+
contextModelOverride = model || null;
|
|
17
|
+
if (temp)
|
|
18
|
+
contextTempOverride = parseFloat(temp);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
executionModelOverride = model || null;
|
|
22
|
+
if (temp)
|
|
23
|
+
executionTempOverride = parseFloat(temp);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function clearModelOverrides() {
|
|
27
|
+
contextModelOverride = null;
|
|
28
|
+
contextTempOverride = null;
|
|
29
|
+
executionModelOverride = null;
|
|
30
|
+
executionTempOverride = null;
|
|
31
|
+
}
|
|
7
32
|
// ─── AI Service ──────────────────────────────────────────────────────
|
|
33
|
+
let globalActiveModel = DEFAULT_MODEL;
|
|
34
|
+
export function setGlobalActiveModel(model) {
|
|
35
|
+
globalActiveModel = model;
|
|
36
|
+
}
|
|
37
|
+
export function getGlobalActiveModel() {
|
|
38
|
+
return globalActiveModel;
|
|
39
|
+
}
|
|
40
|
+
let globalLatestUsageMetadata = undefined;
|
|
41
|
+
export function getGlobalLatestUsageMetadata() {
|
|
42
|
+
return globalLatestUsageMetadata;
|
|
43
|
+
}
|
|
8
44
|
const proxyClient = new ProxyClient();
|
|
9
45
|
// ─── History Limits ──────────────────────────────────────────────────
|
|
10
46
|
/** Maximum number of Content entries to keep in the sliding history window. */
|
|
@@ -91,7 +127,7 @@ export class ProxyChatSession {
|
|
|
91
127
|
this.history = this.history.slice(trimCount);
|
|
92
128
|
}
|
|
93
129
|
}
|
|
94
|
-
async sendMessage(message, additionalText, abortSignal) {
|
|
130
|
+
async sendMessage(message, additionalText, abortSignal, onChunk) {
|
|
95
131
|
const idToken = await getAuthorizedIdToken();
|
|
96
132
|
if (!idToken) {
|
|
97
133
|
throw new Error('You are not signed in. Please run `minovative-mind-cli login` first.');
|
|
@@ -102,18 +138,29 @@ export class ProxyChatSession {
|
|
|
102
138
|
newParts = [{ text: truncatePartText(message) }];
|
|
103
139
|
}
|
|
104
140
|
else {
|
|
105
|
-
// It's an array of function responses
|
|
106
|
-
newParts =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
: m.functionResponse.response.output
|
|
141
|
+
// It's an array of function responses
|
|
142
|
+
newParts = [];
|
|
143
|
+
for (const m of message) {
|
|
144
|
+
newParts.push({
|
|
145
|
+
functionResponse: {
|
|
146
|
+
name: m.functionResponse.name,
|
|
147
|
+
response: {
|
|
148
|
+
...m.functionResponse.response,
|
|
149
|
+
output: typeof m.functionResponse.response.output === 'string'
|
|
150
|
+
? truncatePartText(m.functionResponse.response.output)
|
|
151
|
+
: m.functionResponse.response.output,
|
|
152
|
+
},
|
|
114
153
|
},
|
|
115
|
-
}
|
|
116
|
-
|
|
154
|
+
});
|
|
155
|
+
// If the tool execution returned inlineData (e.g. reading a PDF), append it as a sibling Part
|
|
156
|
+
if (m.functionResponse.response.inlineData) {
|
|
157
|
+
newParts.push({
|
|
158
|
+
inlineData: m.functionResponse.response.inlineData
|
|
159
|
+
});
|
|
160
|
+
// Remove it from the textual output payload to avoid schema violations in functionResponse
|
|
161
|
+
delete newParts[newParts.length - 2].functionResponse.response.inlineData;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
117
164
|
}
|
|
118
165
|
if (additionalText) {
|
|
119
166
|
newParts.push({ text: truncatePartText(additionalText) });
|
|
@@ -125,8 +172,20 @@ export class ProxyChatSession {
|
|
|
125
172
|
// Prune old history before sending to keep payload bounded
|
|
126
173
|
this.pruneHistory();
|
|
127
174
|
const effectiveGenerationConfig = { ...this.generationConfig };
|
|
128
|
-
const result = await proxyClient.generateFunctionCallViaProxy(idToken, this.modelName, this.history, this.tools, undefined,
|
|
175
|
+
const result = await proxyClient.generateFunctionCallViaProxy(idToken, this.modelName, this.history, this.tools, undefined, // toolConfig
|
|
176
|
+
this.systemInstruction, effectiveGenerationConfig, onChunk ? { onChunk } : undefined, // streamCallbacks
|
|
177
|
+
abortSignal);
|
|
129
178
|
this.latestUsageMetadata = result.usageMetadata;
|
|
179
|
+
if (result.usageMetadata) {
|
|
180
|
+
globalLatestUsageMetadata = result.usageMetadata;
|
|
181
|
+
}
|
|
182
|
+
// Track token usage metrics
|
|
183
|
+
if (result.usageMetadata) {
|
|
184
|
+
const collector = getMetricCollector();
|
|
185
|
+
if (collector) {
|
|
186
|
+
collector.recordTokenUsage(result.usageMetadata.promptTokens || 0, result.usageMetadata.candidatesTokens || 0, result.usageMetadata.cachedTokens || 0);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
130
189
|
// Append model response to history
|
|
131
190
|
let modelParts = [];
|
|
132
191
|
const allFunctionCalls = [...(result.functionCalls || [])];
|
|
@@ -184,9 +243,12 @@ export class ProxyChatSession {
|
|
|
184
243
|
* Creates a new shared multi-turn chat session via the proxy.
|
|
185
244
|
*/
|
|
186
245
|
export function createSharedChatSession() {
|
|
187
|
-
|
|
246
|
+
let model = executionModelOverride || getGlobalActiveModel();
|
|
247
|
+
if (model === 'auto')
|
|
248
|
+
model = GEMINI_MODELS.FLASH_3_5; // Will be overridden per-turn in executeSingleTurn
|
|
249
|
+
return new ProxyChatSession(model, GENERAL_CHAT_INSTRUCTION, [], {
|
|
188
250
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
189
|
-
temperature: 1,
|
|
251
|
+
temperature: executionTempOverride !== null ? executionTempOverride : 1,
|
|
190
252
|
topP: 0.95,
|
|
191
253
|
topK: 40,
|
|
192
254
|
});
|
|
@@ -200,22 +262,35 @@ export function getGeneralChatConfig() {
|
|
|
200
262
|
export function getPlanExecutionConfig() {
|
|
201
263
|
return {
|
|
202
264
|
systemInstruction: PLAN_EXECUTION_INSTRUCTION,
|
|
203
|
-
tools: [{ functionDeclarations:
|
|
265
|
+
tools: [{ functionDeclarations: getToolDeclarations() }],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export function getPlanModeConfig() {
|
|
269
|
+
return {
|
|
270
|
+
systemInstruction: PLAN_MODE_INSTRUCTION,
|
|
271
|
+
tools: [], // No tools allowed in plan mode
|
|
204
272
|
};
|
|
205
273
|
}
|
|
206
274
|
/**
|
|
207
275
|
* Compresses a large string of text using gemini-3.1-flash-lite.
|
|
208
276
|
* Used for shrinking context payloads to prevent OOM/choking.
|
|
209
277
|
*/
|
|
210
|
-
export async function compressTextUsingFlashLite(text, instruction = 'Summarize the following text concisely. Preserve the most critical technical details, function names, and architecture logic. Keep it under 1500 characters.') {
|
|
211
|
-
if (!text || text.length < 1000)
|
|
278
|
+
export async function compressTextUsingFlashLite(text, instruction = 'Summarize the following text concisely. Preserve the most critical technical details, function names, and architecture logic. Keep it under 1500 characters.', inlineData) {
|
|
279
|
+
if (!text || (text.length < 1000 && !inlineData))
|
|
212
280
|
return text; // Don't compress tiny texts
|
|
213
281
|
try {
|
|
214
282
|
const idToken = await getAuthorizedIdToken();
|
|
215
283
|
if (!idToken)
|
|
216
284
|
return text;
|
|
217
|
-
|
|
218
|
-
|
|
285
|
+
let model = getGlobalActiveModel();
|
|
286
|
+
if (model === 'auto')
|
|
287
|
+
model = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
288
|
+
const parts = [{ text }];
|
|
289
|
+
if (inlineData) {
|
|
290
|
+
parts.push({ inlineData });
|
|
291
|
+
}
|
|
292
|
+
const contents = [{ role: 'user', parts }];
|
|
293
|
+
const result = await proxyClient.generateFunctionCallViaProxy(idToken, model, contents, [], // no tools
|
|
219
294
|
undefined, instruction, { temperature: 0.2 });
|
|
220
295
|
let textPart = '';
|
|
221
296
|
if (result.parts) {
|
|
@@ -236,9 +311,13 @@ export async function compressTextUsingFlashLite(text, instruction = 'Summarize
|
|
|
236
311
|
}
|
|
237
312
|
}
|
|
238
313
|
// ─── Context Agent Service ───────────────────────────────────────────
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
314
|
+
/**
|
|
315
|
+
* Returns the tool declarations for the read-only Context Agent.
|
|
316
|
+
* Extracted as a reusable function so investigation sub-agents can import
|
|
317
|
+
* the same schema without duplicating the declarations.
|
|
318
|
+
*/
|
|
319
|
+
export function getContextToolDeclarations() {
|
|
320
|
+
return [
|
|
242
321
|
{
|
|
243
322
|
functionDeclarations: [
|
|
244
323
|
{
|
|
@@ -271,7 +350,7 @@ export function createContextAgentSession() {
|
|
|
271
350
|
},
|
|
272
351
|
{
|
|
273
352
|
name: 'read_file',
|
|
274
|
-
description: 'Read a single file. Use startLine and endLine to read specific chunks of massive files.',
|
|
353
|
+
description: 'Read a single file. Supports text files and native parsing of .pdf files (including math and diagrams). Use startLine and endLine to read specific chunks of massive text files.',
|
|
275
354
|
parameters: {
|
|
276
355
|
type: 'OBJECT',
|
|
277
356
|
properties: {
|
|
@@ -346,7 +425,8 @@ export function createContextAgentSession() {
|
|
|
346
425
|
"language's native AST parser (e.g., TypeScript compiler API, Python ast module, go/parser). " +
|
|
347
426
|
'The script is executed from a temporary directory and automatically cleaned up after execution. ' +
|
|
348
427
|
'Output should be structured JSON to stdout with name, type, startLine, endLine for each code element. ' +
|
|
349
|
-
'Use the results to make precise read_file calls with exact startLine/endLine instead of guessing.'
|
|
428
|
+
'Use the results to make precise read_file calls with exact startLine/endLine instead of guessing. ' +
|
|
429
|
+
'CRITICAL: Do not use this tool on binary, document, or non-code files (e.g. PDF, image, audio, docx).',
|
|
350
430
|
parameters: {
|
|
351
431
|
type: 'OBJECT',
|
|
352
432
|
properties: {
|
|
@@ -393,26 +473,75 @@ export function createContextAgentSession() {
|
|
|
393
473
|
required: ['query'],
|
|
394
474
|
},
|
|
395
475
|
},
|
|
476
|
+
{
|
|
477
|
+
name: 'semantic_search',
|
|
478
|
+
description: 'Search the codebase by meaning and concept rather than exact text match. ' +
|
|
479
|
+
'Use this when you need to find code related to a concept, pattern, or behavior ' +
|
|
480
|
+
"but don't know the exact variable or function names to grep for. " +
|
|
481
|
+
'Examples: "error handling for API requests", "user authentication flow", ' +
|
|
482
|
+
'"database connection pooling logic". Returns ranked results with file paths, ' +
|
|
483
|
+
'line ranges, and similarity scores.',
|
|
484
|
+
parameters: {
|
|
485
|
+
type: 'OBJECT',
|
|
486
|
+
properties: {
|
|
487
|
+
query: {
|
|
488
|
+
type: 'STRING',
|
|
489
|
+
description: "Natural language description of what you're looking for in the codebase.",
|
|
490
|
+
},
|
|
491
|
+
topK: {
|
|
492
|
+
type: 'NUMBER',
|
|
493
|
+
description: 'Number of results to return. Defaults to 5, maximum 15.',
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
required: ['query'],
|
|
497
|
+
},
|
|
498
|
+
},
|
|
396
499
|
],
|
|
397
500
|
},
|
|
398
501
|
];
|
|
399
|
-
|
|
502
|
+
}
|
|
503
|
+
export function createContextAgentSession() {
|
|
504
|
+
const contextTools = getContextToolDeclarations();
|
|
505
|
+
let model = contextModelOverride || getGlobalActiveModel();
|
|
506
|
+
if (model === 'auto')
|
|
507
|
+
model = GEMINI_MODELS.FLASH_3_5;
|
|
508
|
+
return new ProxyChatSession(model, CONTEXT_SYSTEM_INSTRUCTION, contextTools, {
|
|
400
509
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
401
|
-
temperature: 1,
|
|
510
|
+
temperature: contextTempOverride !== null ? contextTempOverride : 1,
|
|
402
511
|
topP: 0.95,
|
|
403
512
|
topK: 40,
|
|
404
513
|
});
|
|
405
514
|
}
|
|
406
515
|
// ─── Intent Router Service ───────────────────────────────────────────
|
|
407
|
-
export const INTENT_ROUTER_MODEL = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
408
516
|
export function createIntentRouterSession() {
|
|
409
|
-
|
|
517
|
+
let model = getGlobalActiveModel();
|
|
518
|
+
if (model === 'auto')
|
|
519
|
+
model = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
520
|
+
return new ProxyChatSession(model, INTENT_ROUTER_SYSTEM_INSTRUCTION, [], // no tools
|
|
521
|
+
{ temperature: 0, responseMimeType: 'application/json' });
|
|
522
|
+
}
|
|
523
|
+
// ─── Execution Complexity Router Service ──────────────────────────────
|
|
524
|
+
export function createExecutionComplexitySession() {
|
|
525
|
+
let model = getGlobalActiveModel();
|
|
526
|
+
if (model === 'auto')
|
|
527
|
+
model = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
528
|
+
return new ProxyChatSession(model, EXECUTION_COMPLEXITY_SYSTEM_INSTRUCTION, [], // no tools
|
|
529
|
+
{ temperature: 0, responseMimeType: 'application/json' });
|
|
530
|
+
}
|
|
531
|
+
// ─── Investigation Complexity Router Service ─────────────────────────
|
|
532
|
+
export function createInvestigationComplexitySession() {
|
|
533
|
+
let model = getGlobalActiveModel();
|
|
534
|
+
if (model === 'auto')
|
|
535
|
+
model = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
536
|
+
return new ProxyChatSession(model, INVESTIGATION_COMPLEXITY_SYSTEM_INSTRUCTION, [], // no tools
|
|
410
537
|
{ temperature: 0, responseMimeType: 'application/json' });
|
|
411
538
|
}
|
|
412
539
|
// ─── Web Search Agent Service ───────────────────────────────────────────
|
|
413
|
-
export const WEB_SEARCH_AGENT_MODEL = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
414
540
|
export function createWebSearchAgentSession() {
|
|
415
|
-
|
|
541
|
+
let model = getGlobalActiveModel();
|
|
542
|
+
if (model === 'auto')
|
|
543
|
+
model = GEMINI_MODELS.FLASH_3_5;
|
|
544
|
+
return new ProxyChatSession(model, WEB_SEARCH_SYSTEM_INSTRUCTION, [{ googleSearch: {} }], {
|
|
416
545
|
maxOutputTokens: MAX_OUTPUT_TOKENS,
|
|
417
546
|
temperature: 1,
|
|
418
547
|
topP: 0.95,
|
|
@@ -432,7 +561,10 @@ export async function generateChatTitle(firstMessage) {
|
|
|
432
561
|
return firstMessage.substring(0, maxLength);
|
|
433
562
|
const instruction = "You are a helpful assistant that generates extremely concise chat titles (max 4-5 words) based on a user's first message. Output ONLY the title, no quotes, no markdown, no punctuation.";
|
|
434
563
|
const contents = [{ role: 'user', parts: [{ text: firstMessage.substring(0, 500) }] }];
|
|
435
|
-
|
|
564
|
+
let model = getGlobalActiveModel();
|
|
565
|
+
if (model === 'auto')
|
|
566
|
+
model = GEMINI_MODELS.FLASH_LITE_3_1;
|
|
567
|
+
const result = await proxyClient.generateFunctionCallViaProxy(idToken, model, contents, [], // no tools
|
|
436
568
|
undefined, instruction, { temperature: 0.2 });
|
|
437
569
|
let title = '';
|
|
438
570
|
if (result.parts) {
|
|
@@ -1,31 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a single file modification tracked within a workspace changeset.
|
|
3
|
+
* Captures the state of a file before a change was applied, facilitating reversion.
|
|
4
|
+
*/
|
|
1
5
|
export interface FileChange {
|
|
6
|
+
/**
|
|
7
|
+
* Relative or absolute file path of the affected file.
|
|
8
|
+
*/
|
|
2
9
|
filePath: string;
|
|
10
|
+
/**
|
|
11
|
+
* The original content of the file before any modification in the changeset.
|
|
12
|
+
* A value of `null` indicates the file was newly created and did not exist previously.
|
|
13
|
+
*/
|
|
3
14
|
originalContent: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* The nature of the file operation that occurred.
|
|
17
|
+
* - 'create': The file was newly written to disk.
|
|
18
|
+
* - 'modify': An existing file was updated.
|
|
19
|
+
* - 'delete': An existing file was removed.
|
|
20
|
+
*/
|
|
4
21
|
action: 'create' | 'modify' | 'delete';
|
|
5
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Represents a transactional unit of workspace modifications.
|
|
25
|
+
* Groups related file changes under a shared timestamp and intent description.
|
|
26
|
+
*/
|
|
6
27
|
export interface ChangeSet {
|
|
28
|
+
/**
|
|
29
|
+
* Unix epoch timestamp (in milliseconds) when this changeset was initiated.
|
|
30
|
+
*/
|
|
7
31
|
timestamp: number;
|
|
32
|
+
/**
|
|
33
|
+
* Developer or system-provided description explaining the intent of the changes (e.g. "Edit index.ts").
|
|
34
|
+
*/
|
|
8
35
|
description: string;
|
|
36
|
+
/**
|
|
37
|
+
* Array of individual file modifications tracking the original states prior to this changeset.
|
|
38
|
+
*/
|
|
9
39
|
changes: FileChange[];
|
|
40
|
+
/**
|
|
41
|
+
* The execution outcome status of this changeset.
|
|
42
|
+
* - 'partial': The change operation is still in progress or was interrupted.
|
|
43
|
+
* - 'complete': All planned modifications in the transaction finished successfully.
|
|
44
|
+
*/
|
|
10
45
|
status?: 'complete' | 'partial';
|
|
11
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Service class responsible for tracking, persisting, and reverting file modifications in the workspace.
|
|
49
|
+
* Uses a local project state file (`revert_state.json`) to persist state across CLI invocations.
|
|
50
|
+
*/
|
|
12
51
|
declare class ChangeLogger {
|
|
52
|
+
/**
|
|
53
|
+
* Ordered history stack of committed changesets, oldest first, newest last.
|
|
54
|
+
*/
|
|
13
55
|
private changeStack;
|
|
56
|
+
/**
|
|
57
|
+
* The active changeset currently receiving recorded modifications.
|
|
58
|
+
* Null if no change transaction is actively in progress.
|
|
59
|
+
*/
|
|
14
60
|
private currentChangeSet;
|
|
61
|
+
/**
|
|
62
|
+
* Absolute path to the active workspace root directory.
|
|
63
|
+
*/
|
|
15
64
|
private workspaceRoot;
|
|
65
|
+
/**
|
|
66
|
+
* Maximum number of historical changesets retained in memory and on disk.
|
|
67
|
+
* Older changesets are discarded on save once this limit is exceeded.
|
|
68
|
+
*/
|
|
16
69
|
private readonly MAX_HISTORY;
|
|
70
|
+
/**
|
|
71
|
+
* Whether the change logger is currently enabled.
|
|
72
|
+
*/
|
|
73
|
+
private isEnabled;
|
|
74
|
+
getIsEnabled(): boolean;
|
|
75
|
+
setIsEnabled(enabled: boolean): void;
|
|
76
|
+
/**
|
|
77
|
+
* Initializes the ChangeLogger by setting the workspace root and loading existing
|
|
78
|
+
* history from the local `.minovativemind/revert_state.json` cache file.
|
|
79
|
+
*
|
|
80
|
+
* @param workspaceRoot - The absolute path to the project workspace root.
|
|
81
|
+
*/
|
|
17
82
|
init(workspaceRoot: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Persists the current committed change stack to the local cache directory.
|
|
85
|
+
* Enforces the `MAX_HISTORY` retention policy by slicing out older entries.
|
|
86
|
+
* Does nothing if the workspace root has not yet been initialized.
|
|
87
|
+
*/
|
|
18
88
|
private saveState;
|
|
89
|
+
/**
|
|
90
|
+
* Retrieves a copy of the active history stack.
|
|
91
|
+
*
|
|
92
|
+
* @returns A shallow copy of the array containing all tracked changesets.
|
|
93
|
+
*/
|
|
19
94
|
getHistory(): ChangeSet[];
|
|
95
|
+
/**
|
|
96
|
+
* Traverses backward through the history stack, popping and collecting changesets
|
|
97
|
+
* until a changeset matching the specified timestamp is encountered (inclusive).
|
|
98
|
+
* Persists the resulting truncated stack after execution.
|
|
99
|
+
*
|
|
100
|
+
* @param timestamp - The unique timestamp identifier of the target changeset to revert back to.
|
|
101
|
+
* @returns An array of popped changesets ordered from newest to oldest.
|
|
102
|
+
*/
|
|
20
103
|
popUntil(timestamp: number): ChangeSet[];
|
|
104
|
+
/**
|
|
105
|
+
* Initiates a new transactional changeset. If a previous changeset was in progress and
|
|
106
|
+
* uncommitted, it is automatically finalized and committed to the history stack first.
|
|
107
|
+
*
|
|
108
|
+
* @param description - Descriptive label summarizing the purpose of the new changeset.
|
|
109
|
+
*/
|
|
21
110
|
startChangeSet(description: string): void;
|
|
111
|
+
/**
|
|
112
|
+
* Records a file change within the active changeset.
|
|
113
|
+
* If no changeset is active, an anonymous default changeset is automatically initialized.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* Idempotency Guard: If the specified file already has an entry in the current changeset,
|
|
117
|
+
* the original state is preserved. This ensures that multiple successive modifications to
|
|
118
|
+
* the same file within a single transaction map back to the true original state prior to
|
|
119
|
+
* the onset of the transaction.
|
|
120
|
+
*
|
|
121
|
+
* @param filePath - The relative or absolute path of the file being altered.
|
|
122
|
+
* @param originalContent - The text content of the file prior to the change, or null if created.
|
|
123
|
+
* @param action - The classification of the operation ('create', 'modify', or 'delete').
|
|
124
|
+
*/
|
|
22
125
|
logChange(filePath: string, originalContent: string | null, action: 'create' | 'modify' | 'delete'): void;
|
|
126
|
+
/**
|
|
127
|
+
* Marks the status of the active changeset as 'complete', indicating that all scheduled
|
|
128
|
+
* operations within the scope of this transaction finished without interruption.
|
|
129
|
+
*/
|
|
23
130
|
markComplete(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Retrieves a list of file paths that have been modified in the currently active changeset.
|
|
133
|
+
*
|
|
134
|
+
* @returns An array of string file paths, or an empty array if no changeset is active.
|
|
135
|
+
*/
|
|
136
|
+
getChangedFiles(): string[];
|
|
137
|
+
/**
|
|
138
|
+
* Commits the active changeset to the history stack and persists the state to disk,
|
|
139
|
+
* provided that the changeset actually contains one or more recorded file modifications.
|
|
140
|
+
* Resets the active changeset state to null.
|
|
141
|
+
*/
|
|
24
142
|
commitChangeSet(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Retrieves the most recently committed changeset from the history stack without removing it.
|
|
145
|
+
*
|
|
146
|
+
* @returns The latest committed ChangeSet, or null if the stack is currently empty.
|
|
147
|
+
*/
|
|
25
148
|
getLastChangeSet(): ChangeSet | null;
|
|
149
|
+
/**
|
|
150
|
+
* Returns the active, uncommitted changeset currently receiving modifications.
|
|
151
|
+
*
|
|
152
|
+
* @returns The in-progress ChangeSet, or null if no changeset is active.
|
|
153
|
+
*/
|
|
26
154
|
getCurrentChangeSet(): ChangeSet | null;
|
|
155
|
+
/**
|
|
156
|
+
* Removes and returns the most recently committed changeset from the history stack.
|
|
157
|
+
* Persists the newly truncated stack state back to the cache.
|
|
158
|
+
*
|
|
159
|
+
* @returns The removed ChangeSet, or null if no historical changes exist.
|
|
160
|
+
*/
|
|
27
161
|
popLastChangeSet(): ChangeSet | null;
|
|
162
|
+
/**
|
|
163
|
+
* Checks whether the history stack contains any tracked changesets.
|
|
164
|
+
*
|
|
165
|
+
* @returns True if at least one changeset is recorded; false otherwise.
|
|
166
|
+
*/
|
|
28
167
|
hasChanges(): boolean;
|
|
29
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Singleton instance of the ChangeLogger service exported for application-wide tracking.
|
|
171
|
+
*/
|
|
30
172
|
export declare const changeLogger: ChangeLogger;
|
|
31
173
|
export {};
|