minovative-mind-cli 2.10.0 → 2.11.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/dist/services/agent/commandApproval.js +5 -2
- package/dist/services/agent/slashCommands.js +78 -51
- package/dist/services/agent-tools.d.ts +4 -3
- package/dist/services/agent-tools.js +32 -79
- package/dist/services/agent.d.ts +5 -6
- package/dist/services/agent.js +11 -15
- package/dist/services/ai.d.ts +19 -0
- package/dist/services/ai.js +108 -1
- package/dist/services/chatHistoryService.d.ts +95 -2
- package/dist/services/chatHistoryService.js +236 -9
- package/dist/services/contextAgent.js +161 -77
- package/dist/services/orchestration/investigationAgent.js +101 -84
- package/dist/services/orchestration/investigationOrchestrator.js +6 -2
- package/dist/services/orchestration/orchestrator.js +6 -3
- package/dist/services/orchestration/scopedTools.js +5 -0
- package/dist/services/orchestration/subAgent.d.ts +31 -1
- package/dist/services/orchestration/subAgent.js +153 -2
- package/dist/utils/analysisRunner.d.ts +29 -0
- package/dist/utils/analysisRunner.js +200 -5
- package/dist/utils/contextPrompts.d.ts +19 -3
- package/dist/utils/contextPrompts.js +144 -26
- package/dist/utils/historyPrompt.d.ts +92 -1
- package/dist/utils/historyPrompt.js +166 -2
- package/dist/utils/symbolExtractor.d.ts +12 -0
- package/dist/utils/symbolExtractor.js +946 -0
- package/dist/utils/systemPrompts.d.ts +3 -3
- package/dist/utils/systemPrompts.js +10 -7
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,167 @@
|
|
|
1
1
|
import { TextPrompt } from '@clack/core';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
|
+
/**
|
|
4
|
+
* Approximate token count for a given text string using character and word heuristics.
|
|
5
|
+
* Defaults to a safe ratio of ~3.8 characters per token for multi-language code and text.
|
|
6
|
+
*
|
|
7
|
+
* @param text The input string to estimate.
|
|
8
|
+
* @returns Estimated number of tokens.
|
|
9
|
+
*/
|
|
10
|
+
export function estimateTokenCount(text) {
|
|
11
|
+
if (!text)
|
|
12
|
+
return 0;
|
|
13
|
+
const length = text.length;
|
|
14
|
+
if (length === 0)
|
|
15
|
+
return 0;
|
|
16
|
+
// Code and structured JSON typically have slightly higher token density (fewer chars/token)
|
|
17
|
+
// than plain English prose. 3.8 chars/token provides a conservative and safe ceiling.
|
|
18
|
+
return Math.ceil(length / 3.8);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Calculates dynamic token budget distributions for context injection, history retention,
|
|
22
|
+
* and system prompts based on the overall context window size.
|
|
23
|
+
*
|
|
24
|
+
* @param config Configuration parameters for the token budget.
|
|
25
|
+
* @returns An object containing granular token allocations.
|
|
26
|
+
*/
|
|
27
|
+
export function calculateDynamicTokenBudget(config) {
|
|
28
|
+
const { totalBudget, reservedForOutput = 8192, systemPromptBudget = 4096, safetyMarginPct = 0.1, } = config;
|
|
29
|
+
const rawAvailable = Math.max(0, totalBudget - reservedForOutput - systemPromptBudget);
|
|
30
|
+
const safetyMargin = Math.round(rawAvailable * Math.min(Math.max(safetyMarginPct, 0), 0.5));
|
|
31
|
+
const usableBudget = Math.max(0, rawAvailable - safetyMargin);
|
|
32
|
+
// Allocate 60% of usable budget to workspace context (files, tree, tools) and 40% to conversation history
|
|
33
|
+
const contextBudget = Math.round(usableBudget * 0.6);
|
|
34
|
+
const historyBudget = usableBudget - contextBudget;
|
|
35
|
+
return {
|
|
36
|
+
contextBudget,
|
|
37
|
+
historyBudget,
|
|
38
|
+
outputBudget: reservedForOutput,
|
|
39
|
+
systemBudget: systemPromptBudget,
|
|
40
|
+
safetyMargin,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Prunes a text string so that its estimated token count does not exceed the specified maximum.
|
|
45
|
+
*
|
|
46
|
+
* @param text The text to prune.
|
|
47
|
+
* @param maxTokens Maximum allowable tokens for this text.
|
|
48
|
+
* @param options Pruning options.
|
|
49
|
+
* @returns Pruned string, with an optional truncation marker.
|
|
50
|
+
*/
|
|
51
|
+
export function pruneTextToTokenBudget(text, maxTokens, options) {
|
|
52
|
+
if (!text || maxTokens <= 0)
|
|
53
|
+
return '';
|
|
54
|
+
const currentEstimated = estimateTokenCount(text);
|
|
55
|
+
if (currentEstimated <= maxTokens) {
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
const maxChars = Math.floor(maxTokens * 3.8);
|
|
59
|
+
const marker = options?.truncationMarker ?? '\n... [Context truncated to fit token budget] ...\n';
|
|
60
|
+
const effectiveMaxChars = Math.max(0, maxChars - marker.length);
|
|
61
|
+
if (options?.fromStart) {
|
|
62
|
+
const keepSlice = text.slice(text.length - effectiveMaxChars);
|
|
63
|
+
return `${marker}${keepSlice}`;
|
|
64
|
+
}
|
|
65
|
+
const keepSlice = text.slice(0, effectiveMaxChars);
|
|
66
|
+
return `${keepSlice}${marker}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Optimizes an array of history strings by trimming whitespace, deduplicating consecutive items,
|
|
70
|
+
* removing empty entries, and capping the array length to a maximum threshold.
|
|
71
|
+
*
|
|
72
|
+
* @param history Raw array of history entries.
|
|
73
|
+
* @param maxEntries Maximum number of entries to retain. Defaults to 100.
|
|
74
|
+
* @returns Cleaned and bounded array of history entries.
|
|
75
|
+
*/
|
|
76
|
+
export function optimizeHistoryForContext(history, maxEntries = 100) {
|
|
77
|
+
if (!history || !Array.isArray(history))
|
|
78
|
+
return [];
|
|
79
|
+
const cleaned = [];
|
|
80
|
+
for (let i = 0; i < history.length; i++) {
|
|
81
|
+
const entry = history[i]?.trim();
|
|
82
|
+
if (!entry)
|
|
83
|
+
continue;
|
|
84
|
+
// Deduplicate consecutive identical entries
|
|
85
|
+
if (cleaned.length > 0 && cleaned[cleaned.length - 1] === entry) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
cleaned.push(entry);
|
|
89
|
+
}
|
|
90
|
+
if (cleaned.length > maxEntries) {
|
|
91
|
+
return cleaned.slice(cleaned.length - maxEntries);
|
|
92
|
+
}
|
|
93
|
+
return cleaned;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Formats a list of conversation turns into a unified history context string, dynamically
|
|
97
|
+
* pruning older turns if the total estimated tokens exceed the allocated budget.
|
|
98
|
+
*
|
|
99
|
+
* @param history Array of conversation turns with role and text content.
|
|
100
|
+
* @param maxTokens Maximum allowable tokens for the formatted history.
|
|
101
|
+
* @param options Formatting options.
|
|
102
|
+
* @returns Formatted and budget-constrained conversation history string.
|
|
103
|
+
*/
|
|
104
|
+
export function formatHistoryWithTokenBudget(history, maxTokens = 16384, options) {
|
|
105
|
+
if (!history || history.length === 0 || maxTokens <= 0)
|
|
106
|
+
return '';
|
|
107
|
+
const keepRecent = options?.keepRecentTurns ?? 2;
|
|
108
|
+
const userLabel = options?.userLabel ?? 'User';
|
|
109
|
+
const modelLabel = options?.modelLabel ?? 'Assistant';
|
|
110
|
+
// Extract plain text for each turn
|
|
111
|
+
const turns = [];
|
|
112
|
+
for (const item of history) {
|
|
113
|
+
const label = item.role === 'user' ? userLabel : modelLabel;
|
|
114
|
+
let text = item.text || '';
|
|
115
|
+
if (!text && Array.isArray(item.parts)) {
|
|
116
|
+
text = item.parts
|
|
117
|
+
.map((p) => {
|
|
118
|
+
if (typeof p === 'string')
|
|
119
|
+
return p;
|
|
120
|
+
if (p.text)
|
|
121
|
+
return p.text;
|
|
122
|
+
if (p.functionCall)
|
|
123
|
+
return `[Tool Call: ${p.functionCall.name}]`;
|
|
124
|
+
if (p.functionResponse)
|
|
125
|
+
return `[Tool Result: ${p.functionResponse.name}]`;
|
|
126
|
+
return '';
|
|
127
|
+
})
|
|
128
|
+
.filter(Boolean)
|
|
129
|
+
.join('\n');
|
|
130
|
+
}
|
|
131
|
+
text = text.trim();
|
|
132
|
+
if (text) {
|
|
133
|
+
turns.push({
|
|
134
|
+
label,
|
|
135
|
+
text,
|
|
136
|
+
tokens: estimateTokenCount(`${label}: ${text}\n`),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (turns.length === 0)
|
|
141
|
+
return '';
|
|
142
|
+
// Build from the newest turns backwards to prioritize recent context
|
|
143
|
+
const selectedTurns = [];
|
|
144
|
+
let consumedTokens = 0;
|
|
145
|
+
let isTruncated = false;
|
|
146
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
147
|
+
const turn = turns[i];
|
|
148
|
+
const formatted = `${turn.label}: ${turn.text}`;
|
|
149
|
+
const turnTokens = turn.tokens;
|
|
150
|
+
if (consumedTokens + turnTokens <= maxTokens || selectedTurns.length < keepRecent) {
|
|
151
|
+
selectedTurns.unshift(formatted);
|
|
152
|
+
consumedTokens += turnTokens;
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
isTruncated = true;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
let result = selectedTurns.join('\n\n');
|
|
160
|
+
if (isTruncated) {
|
|
161
|
+
result = `[Older conversation history pruned to fit token budget]\n\n${result}`;
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
3
165
|
// Emulate clack/prompts styling characters
|
|
4
166
|
const S_BAR = '│';
|
|
5
167
|
const S_BAR_END = '└';
|
|
@@ -23,13 +185,15 @@ function symbol(state) {
|
|
|
23
185
|
}
|
|
24
186
|
}
|
|
25
187
|
/**
|
|
26
|
-
* A custom text prompt that supports command history navigation using Up/Down arrow keys
|
|
188
|
+
* A custom text prompt that supports command history navigation using Up/Down arrow keys
|
|
189
|
+
* with memory bounds and dynamic input optimization.
|
|
27
190
|
*
|
|
28
191
|
* @param opts - The configuration options for the prompt.
|
|
29
192
|
* @returns A promise that resolves to the user's input string or a symbol if cancelled.
|
|
30
193
|
*/
|
|
31
194
|
export const historyText = (opts) => {
|
|
32
|
-
const
|
|
195
|
+
const maxLen = opts.maxHistoryLength ?? 100;
|
|
196
|
+
const history = optimizeHistoryForContext(opts.history || [], maxLen);
|
|
33
197
|
let historyIndex = -1;
|
|
34
198
|
const prompt = new TextPrompt({
|
|
35
199
|
validate: opts.validate,
|
|
@@ -31,3 +31,15 @@ export declare function extractSymbolMetadata(content: string, filePath: string,
|
|
|
31
31
|
* @returns The filtered source string containing only the matched symbol blocks and omission markers
|
|
32
32
|
*/
|
|
33
33
|
export declare function extractSymbols(content: string, filePath: string, targetElements: string[]): string;
|
|
34
|
+
/**
|
|
35
|
+
* Extracts a compact declarations outline (type/interface definitions, class skeletons,
|
|
36
|
+
* and function signatures without full implementation bodies) across TypeScript/JavaScript,
|
|
37
|
+
* Python, Go, and Rust.
|
|
38
|
+
*
|
|
39
|
+
* This dramatically reduces token usage when injecting multi-file context into prompts.
|
|
40
|
+
*
|
|
41
|
+
* @param content - Raw source code content
|
|
42
|
+
* @param filePath - File path used to infer language and syntax rules
|
|
43
|
+
* @returns Compact declarations outline string
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractDeclarationsOutline(content: string, filePath: string): string;
|