minovative-mind-cli 2.10.0 → 2.11.2
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 +1 -0
- package/dist/commands/chat.d.ts +1 -1
- package/dist/commands/chat.js +2 -1
- package/dist/services/agent/commandApproval.js +5 -2
- package/dist/services/agent/slashCommands.js +213 -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 +38 -15
- package/dist/services/ai.d.ts +25 -0
- package/dist/services/ai.js +253 -2
- package/dist/services/chatHistoryService.d.ts +95 -2
- package/dist/services/chatHistoryService.js +236 -9
- package/dist/services/contextAgent.js +184 -89
- package/dist/services/orchestration/investigationAgent.js +100 -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/services/userProfileService.d.ts +97 -0
- package/dist/services/userProfileService.js +410 -0
- 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 +6 -4
- package/dist/utils/systemPrompts.js +77 -9
- package/oclif.manifest.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,6 +122,7 @@ Background tasks automatically route to dedicated auxiliary models with native `
|
|
|
122
122
|
| Command | What it does |
|
|
123
123
|
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
124
124
|
| `/config-key` | Configure custom Google AI Studio API key (BYOK mode) |
|
|
125
|
+
| `/profile` | View, inspect, delete, or reset global adaptive persona memory and AI side-notes |
|
|
125
126
|
| `/models` | Hot-swap the active model |
|
|
126
127
|
| `/plan` | Toggle plan mode to review implementation strategies |
|
|
127
128
|
| `/paste` | Multi-line input mode (cancel with Ctrl+C) |
|
package/dist/commands/chat.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { Command } from '@oclif/core';
|
|
|
9
9
|
export default class DefaultCommand extends Command {
|
|
10
10
|
/**
|
|
11
11
|
* The description displayed in the CLI help output.
|
|
12
|
-
* Details all supported slash commands (/config-key, /paste, /plan, /clear, /models,
|
|
12
|
+
* Details all supported slash commands (/config-key, /profile, /paste, /plan, /clear, /models,
|
|
13
13
|
* /debug, /auto-approve, /sub-agents, /stats, /revert, /chats, /workspaces, /commit)
|
|
14
14
|
* along with interactive chat controls.
|
|
15
15
|
*/
|
package/dist/commands/chat.js
CHANGED
|
@@ -17,7 +17,7 @@ import { updateWorkspaceStatus } from '../services/workspace.js';
|
|
|
17
17
|
export default class DefaultCommand extends Command {
|
|
18
18
|
/**
|
|
19
19
|
* The description displayed in the CLI help output.
|
|
20
|
-
* Details all supported slash commands (/config-key, /paste, /plan, /clear, /models,
|
|
20
|
+
* Details all supported slash commands (/config-key, /profile, /paste, /plan, /clear, /models,
|
|
21
21
|
* /debug, /auto-approve, /sub-agents, /stats, /revert, /chats, /workspaces, /commit)
|
|
22
22
|
* along with interactive chat controls.
|
|
23
23
|
*/
|
|
@@ -25,6 +25,7 @@ export default class DefaultCommand extends Command {
|
|
|
25
25
|
|
|
26
26
|
Inside the chat session, you can use the following commands in the slash menu:
|
|
27
27
|
/config-key - BYOK — Configure, clear, or view status of custom Google AI Studio API key
|
|
28
|
+
/profile - View, inspect, delete, or reset global adaptive persona memory and AI side-notes
|
|
28
29
|
/paste - Enter multi-line paste mode for long code snippets and prompts
|
|
29
30
|
/plan - Toggle plan mode to review step-by-step implementation strategies
|
|
30
31
|
/clear - Clear conversation history, reset terminal screen, and display logo
|
|
@@ -33,6 +33,9 @@ export function isCommandDestructive(command) {
|
|
|
33
33
|
// Massive permission shifts (chmod 777, recursive chowns)
|
|
34
34
|
/\bchmod\s+(?:-[R\w]*\s+)?777\b/i,
|
|
35
35
|
/\bchown\s+-[R\w]*\b/i,
|
|
36
|
+
// Privileged root escalation commands (require explicit interactive password / system-level access)
|
|
37
|
+
/\bsudo\b/i,
|
|
38
|
+
/\bsu\s+/i,
|
|
36
39
|
];
|
|
37
40
|
return destructivePatterns.some((pattern) => pattern.test(command));
|
|
38
41
|
}
|
|
@@ -49,10 +52,10 @@ export function isCommandSafe(command) {
|
|
|
49
52
|
/^\s*echo\b/i,
|
|
50
53
|
/^\s*grep\b/i,
|
|
51
54
|
/^\s*find\b/i,
|
|
52
|
-
/^\s*npm\s+(install|i|ci|run\b)/i,
|
|
55
|
+
/^\s*npm\s+(install|i|ci|run\b|test\b|t\b)/i,
|
|
53
56
|
/^\s*yarn\s+(install|add|build|lint|test|run\b)/i,
|
|
54
57
|
/^\s*pnpm\s+(install|i|add|build|lint|test|run\b)/i,
|
|
55
|
-
/^\s*bun\s+(install|i|add|run\b)/i,
|
|
58
|
+
/^\s*bun\s+(install|i|add|run\b|test\b)/i,
|
|
56
59
|
/^\s*cargo\s+(build|check|add|test|run\b)/i,
|
|
57
60
|
/^\s*go\s+(mod|get|build|test|run\b)/i,
|
|
58
61
|
/^\s*pip\s+(install|list|show)\b/i,
|
|
@@ -17,12 +17,14 @@ import { checkByokSubscription } from '../auth.js';
|
|
|
17
17
|
import { GEMINI_MODELS, isByokEnabled } from '../../utils/config.js';
|
|
18
18
|
import { loadCredentials, updateCredentialField } from '../../utils/credentialStore.js';
|
|
19
19
|
import { getGlobalActiveModel, setGlobalActiveModel, ProxyChatSession } from '../ai.js';
|
|
20
|
+
import { loadUserProfile, deleteSideNote, clearUserProfile, getUserProfilePath, } from '../userProfileService.js';
|
|
20
21
|
/**
|
|
21
22
|
* @file slashCommands.ts
|
|
22
23
|
* @description Interactive slash command handler module for Minovative Mind CLI.
|
|
23
24
|
*
|
|
24
25
|
* Supported slash commands:
|
|
25
26
|
* - `/config-key` : Configure, validate, toggle, or clear Bring Your Own Key (BYOK) Google AI Studio API credentials.
|
|
27
|
+
* - `/profile` : View, inspect, delete, or reset global adaptive persona memory and AI side-notes.
|
|
26
28
|
* - `/paste` : Enter multi-line paste mode using EOF tracking (`Ctrl+D` submission).
|
|
27
29
|
* - `/plan` : Toggle AI step-by-step implementation planning mode.
|
|
28
30
|
* - `/clear` : Clear conversation history, wipe terminal screen, and reset CLI header.
|
|
@@ -563,73 +565,87 @@ export async function handleSlashCommand(command, context) {
|
|
|
563
565
|
p.log.info(`${pc.dim('Workspace:')} ${brandFg(workspaceRoot)}`);
|
|
564
566
|
p.log.info(`${pc.dim('Commands:')} Type ${pc.yellow('/')} to open the command menu and "${pc.yellow('stop')}" to stop the ai generation. Type ${pc.yellow('exit')} to leave.`);
|
|
565
567
|
p.log.success(`Resumed session: ${session.title}`);
|
|
566
|
-
// Print the loaded history
|
|
567
|
-
|
|
568
|
+
// Print the loaded history cleanly: only user and AI conversational messages,
|
|
569
|
+
// with a clean indicator label if the turn involved tool/code changes.
|
|
570
|
+
let turnHadModifications = false;
|
|
571
|
+
let turnHadInspections = false;
|
|
572
|
+
let renderedIndicatorForTurn = false;
|
|
568
573
|
for (const item of session.history) {
|
|
569
574
|
if (item.role === 'user') {
|
|
575
|
+
const hasFunctionResponse = item.parts?.some((p) => p.functionResponse);
|
|
576
|
+
if (hasFunctionResponse) {
|
|
577
|
+
// Internal tool response from the execution loop — do not display as a user message
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
570
580
|
const textParts = item.parts
|
|
571
|
-
|
|
581
|
+
?.filter((p) => p.text)
|
|
572
582
|
.map((p) => p.text)
|
|
573
583
|
.join('');
|
|
574
|
-
if (textParts &&
|
|
584
|
+
if (textParts &&
|
|
585
|
+
!textParts.includes('SYSTEM CHECK: Please objectively verify') &&
|
|
586
|
+
!textParts.includes('[PREVIOUS CONVERSATION SUMMARY]') &&
|
|
587
|
+
!textParts.startsWith('[USER INTERRUPTION]')) {
|
|
588
|
+
// Reset turn tool flags for the new user prompt
|
|
589
|
+
turnHadModifications = false;
|
|
590
|
+
turnHadInspections = false;
|
|
591
|
+
renderedIndicatorForTurn = false;
|
|
575
592
|
p.log.step(pc.bgBlue(pc.white(` ${textParts} `)));
|
|
576
593
|
}
|
|
577
594
|
}
|
|
578
595
|
else if (item.role === 'model') {
|
|
579
|
-
//
|
|
580
|
-
for (const part of item.parts) {
|
|
596
|
+
// Track if any tool actions were called in this turn
|
|
597
|
+
for (const part of item.parts || []) {
|
|
581
598
|
if (part.functionCall) {
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
p.log.message(` ${pc.dim(`[ ] ${index + 1}.`)} ${task}`);
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
else if (call.name === 'update_todo_status') {
|
|
593
|
-
const status = args.status;
|
|
594
|
-
const taskIndex = args.taskIndex;
|
|
595
|
-
const taskText = sessionTasks[taskIndex - 1] || 'completed';
|
|
596
|
-
let icon = '[ ]';
|
|
597
|
-
let color = pc.dim;
|
|
598
|
-
if (status?.toLowerCase() === 'completed' || status?.toLowerCase() === 'done') {
|
|
599
|
-
icon = '[x]';
|
|
600
|
-
color = pc.green;
|
|
601
|
-
}
|
|
602
|
-
else if (status?.toLowerCase() === 'in_progress' || status?.toLowerCase() === 'started') {
|
|
603
|
-
icon = '[/]';
|
|
604
|
-
color = pc.yellow;
|
|
605
|
-
}
|
|
606
|
-
else if (status?.toLowerCase() === 'failed' || status?.toLowerCase() === 'error') {
|
|
607
|
-
icon = '[!]';
|
|
608
|
-
color = pc.red;
|
|
609
|
-
}
|
|
610
|
-
p.log.message(color(` ${icon} ${taskIndex}. ${taskText}`));
|
|
599
|
+
const name = part.functionCall.name;
|
|
600
|
+
if (name === 'modify_file' ||
|
|
601
|
+
name === 'write_file' ||
|
|
602
|
+
name === 'delete_file' ||
|
|
603
|
+
name === 'rename_file' ||
|
|
604
|
+
name === 'run_command') {
|
|
605
|
+
turnHadModifications = true;
|
|
611
606
|
}
|
|
612
|
-
else
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
catch (e) {
|
|
618
|
-
argsStr = String(call.args);
|
|
619
|
-
}
|
|
620
|
-
const argsPreview = argsStr.slice(0, 50);
|
|
621
|
-
p.log.message(pc.dim(` ├─ Executed tool`));
|
|
607
|
+
else if (name === 'read_file' ||
|
|
608
|
+
name === 'grep_search' ||
|
|
609
|
+
name === 'list_directory' ||
|
|
610
|
+
name === 'find_dependencies') {
|
|
611
|
+
turnHadInspections = true;
|
|
622
612
|
}
|
|
623
613
|
}
|
|
624
614
|
}
|
|
625
|
-
//
|
|
626
|
-
|
|
615
|
+
// Aggregate and extract model conversational text
|
|
616
|
+
let textParts = (item.parts || [])
|
|
627
617
|
.filter((p) => p.text && !p.text.includes('[TASK_FINISHED]'))
|
|
628
618
|
.map((p) => p.text)
|
|
629
619
|
.join('');
|
|
630
|
-
if
|
|
620
|
+
// Backward-compatibility: if text is empty, check finish_task args.summary
|
|
621
|
+
if (!textParts.trim()) {
|
|
622
|
+
const finishCall = (item.parts || []).find((p) => p.functionCall?.name === 'finish_task');
|
|
623
|
+
const finishArgs = finishCall?.functionCall?.args;
|
|
624
|
+
if (finishArgs?.summary) {
|
|
625
|
+
textParts = String(finishArgs.summary);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
if (textParts.includes('[ORCHESTRATOR_EXECUTION]')) {
|
|
629
|
+
turnHadModifications = true;
|
|
630
|
+
}
|
|
631
|
+
const cleanText = textParts
|
|
632
|
+
.replace(/\[TASK_FINISHED\]/g, '')
|
|
633
|
+
.replace(/\[ORCHESTRATOR_EXECUTION\]\n?/g, '')
|
|
634
|
+
.replace(/\[PREVIOUS CONVERSATION SUMMARY\]:[\s\S]*?(?=\n\n|$)/g, '')
|
|
635
|
+
.trim();
|
|
636
|
+
if (cleanText) {
|
|
637
|
+
if (!renderedIndicatorForTurn) {
|
|
638
|
+
if (turnHadModifications) {
|
|
639
|
+
p.log.message(pc.dim(' ⚡ [Mino applied workspace modifications & commands]'));
|
|
640
|
+
renderedIndicatorForTurn = true;
|
|
641
|
+
}
|
|
642
|
+
else if (turnHadInspections) {
|
|
643
|
+
p.log.message(pc.dim(' 🔍 [Inspected workspace context]'));
|
|
644
|
+
renderedIndicatorForTurn = true;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
631
647
|
console.log(`\n${pc.blue('◆')} ${pc.bold('Minovative Mind')} ${pc.dim('(History)')}\n`);
|
|
632
|
-
console.log(renderTerminalMarkdown(
|
|
648
|
+
console.log(renderTerminalMarkdown(cleanText));
|
|
633
649
|
}
|
|
634
650
|
}
|
|
635
651
|
}
|
|
@@ -1018,10 +1034,23 @@ export async function handleSlashCommand(command, context) {
|
|
|
1018
1034
|
<diff>
|
|
1019
1035
|
${diffOut}
|
|
1020
1036
|
</diff>`;
|
|
1021
|
-
const commitSystemPrompt =
|
|
1037
|
+
const commitSystemPrompt = `You are a strict Conventional Commits generator and expert software engineer.
|
|
1038
|
+
Output ONLY the raw commit message text. Absolutely NO conversational preambles, NO markdown code blocks (e.g. no \`\`\`), and NO post-explanations.
|
|
1039
|
+
|
|
1040
|
+
Strict Formatting Rules:
|
|
1041
|
+
1. First line must follow Conventional Commits format: type(scope): description
|
|
1042
|
+
- Allowed types: feat, fix, refactor, test, docs, chore, perf, style
|
|
1043
|
+
- Subject line must be in imperative mood (e.g. "add support for...", "fix bug in...") and strictly under 50 characters.
|
|
1044
|
+
2. Leave a single blank line after the subject.
|
|
1045
|
+
3. Provide a concise bulleted list (using '-' or '*') explaining the architectural "what" and "why" of the changes based on the provided diff. If diff spans multiple areas, prioritize the primary architectural change.`;
|
|
1022
1046
|
const commitAgent = new ProxyChatSession(GEMINI_MODELS.FLASH_LITE, commitSystemPrompt, [], {});
|
|
1023
1047
|
const commitResult = await commitAgent.sendMessage(prompt);
|
|
1024
|
-
|
|
1048
|
+
let commitMsg = commitResult.response.text().trim();
|
|
1049
|
+
// Sanitize markdown fences if model included them despite instructions
|
|
1050
|
+
commitMsg = commitMsg
|
|
1051
|
+
.replace(/^```[a-zA-Z]*\n?/gm, '')
|
|
1052
|
+
.replace(/```\s*$/gm, '')
|
|
1053
|
+
.trim();
|
|
1025
1054
|
commitSpinner.message('Committing...');
|
|
1026
1055
|
const tmpMsgPath = path.join(workspaceRoot, '.gemini-commit-msg.tmp');
|
|
1027
1056
|
await fs.writeFile(tmpMsgPath, commitMsg, 'utf-8');
|
|
@@ -1119,5 +1148,138 @@ ${diffOut}
|
|
|
1119
1148
|
}
|
|
1120
1149
|
return { shouldContinue: true };
|
|
1121
1150
|
}
|
|
1151
|
+
/**
|
|
1152
|
+
* `/profile` - Displays and manages global adaptive user profile traits, persona memory, and AI side-notes.
|
|
1153
|
+
*/
|
|
1154
|
+
if (lowerCommand === '/profile') {
|
|
1155
|
+
const profile = await loadUserProfile();
|
|
1156
|
+
const action = await p.select({
|
|
1157
|
+
message: 'Adaptive User Profile & AI Memory Bank:',
|
|
1158
|
+
options: [
|
|
1159
|
+
{
|
|
1160
|
+
value: 'view',
|
|
1161
|
+
label: 'View Profile & Observations',
|
|
1162
|
+
hint: `${profile.agentNotes.length} side-note(s) recorded`,
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
value: 'delete_note',
|
|
1166
|
+
label: 'Delete Specific Side-Note',
|
|
1167
|
+
hint: 'Select and remove an individual note',
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
value: 'clear_all',
|
|
1171
|
+
label: 'Reset Profile & Clear Memory',
|
|
1172
|
+
hint: 'Wipe all persona traits and side-notes',
|
|
1173
|
+
},
|
|
1174
|
+
{ value: 'cancel', label: 'Back to Chat', hint: 'Return to active session' },
|
|
1175
|
+
],
|
|
1176
|
+
});
|
|
1177
|
+
if (p.isCancel(action) || action === 'cancel') {
|
|
1178
|
+
return { shouldContinue: true };
|
|
1179
|
+
}
|
|
1180
|
+
if (action === 'view') {
|
|
1181
|
+
const hasStyle = profile.communicationStyle?.tonePreference ||
|
|
1182
|
+
profile.communicationStyle?.verbosity ||
|
|
1183
|
+
profile.communicationStyle?.formulationStyle;
|
|
1184
|
+
const hasCognitive = profile.cognitiveTraits?.architecturalStyle ||
|
|
1185
|
+
profile.cognitiveTraits?.decisionPreference ||
|
|
1186
|
+
profile.cognitiveTraits?.riskTolerance ||
|
|
1187
|
+
profile.cognitiveTraits?.delegationDepth ||
|
|
1188
|
+
profile.cognitiveTraits?.debuggingStyle ||
|
|
1189
|
+
profile.cognitiveTraits?.explanationFormat;
|
|
1190
|
+
const hasStrengths = profile.technicalPreferences?.strengths && profile.technicalPreferences.strengths.length > 0;
|
|
1191
|
+
const hasConventions = profile.technicalPreferences?.conventions && profile.technicalPreferences.conventions.length > 0;
|
|
1192
|
+
const hasNotes = profile.agentNotes && profile.agentNotes.length > 0;
|
|
1193
|
+
console.log(`\n${pc.bold(pc.cyan('🧠 Global Adaptive User Profile & Persona Memory'))}`);
|
|
1194
|
+
console.log(`${pc.dim('Storage:')} ${pc.dim(getUserProfilePath())}\n`);
|
|
1195
|
+
if (!hasStyle && !hasCognitive && !hasStrengths && !hasConventions && !hasNotes) {
|
|
1196
|
+
p.log.info(pc.yellow('No personalized observations recorded yet. Mino will learn your communication style and preferences organically as you chat.'));
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
if (hasStyle) {
|
|
1200
|
+
console.log(pc.bold('Communication Style:'));
|
|
1201
|
+
if (profile.communicationStyle?.tonePreference) {
|
|
1202
|
+
console.log(` ${pc.dim('•')} Tone & Demeanor: ${pc.green(profile.communicationStyle.tonePreference)}`);
|
|
1203
|
+
}
|
|
1204
|
+
if (profile.communicationStyle?.verbosity) {
|
|
1205
|
+
console.log(` ${pc.dim('•')} Output Formatting: ${pc.green(profile.communicationStyle.verbosity)}`);
|
|
1206
|
+
}
|
|
1207
|
+
if (profile.communicationStyle?.formulationStyle) {
|
|
1208
|
+
console.log(` ${pc.dim('•')} Thought Formulation: ${pc.green(profile.communicationStyle.formulationStyle)}`);
|
|
1209
|
+
}
|
|
1210
|
+
console.log('');
|
|
1211
|
+
}
|
|
1212
|
+
if (hasCognitive) {
|
|
1213
|
+
console.log(pc.bold('Decision-Making & Cognitive Traits:'));
|
|
1214
|
+
if (profile.cognitiveTraits?.architecturalStyle) {
|
|
1215
|
+
console.log(` ${pc.dim('•')} Architectural Orientation: ${pc.magenta(profile.cognitiveTraits.architecturalStyle)}`);
|
|
1216
|
+
}
|
|
1217
|
+
if (profile.cognitiveTraits?.decisionPreference) {
|
|
1218
|
+
console.log(` ${pc.dim('•')} Decision Autonomy: ${pc.magenta(profile.cognitiveTraits.decisionPreference)}`);
|
|
1219
|
+
}
|
|
1220
|
+
if (profile.cognitiveTraits?.riskTolerance) {
|
|
1221
|
+
console.log(` ${pc.dim('•')} Risk & Velocity: ${pc.magenta(profile.cognitiveTraits.riskTolerance)}`);
|
|
1222
|
+
}
|
|
1223
|
+
if (profile.cognitiveTraits?.delegationDepth) {
|
|
1224
|
+
console.log(` ${pc.dim('•')} Delegation Depth: ${pc.magenta(profile.cognitiveTraits.delegationDepth)}`);
|
|
1225
|
+
}
|
|
1226
|
+
if (profile.cognitiveTraits?.debuggingStyle) {
|
|
1227
|
+
console.log(` ${pc.dim('•')} Debugging Preference: ${pc.magenta(profile.cognitiveTraits.debuggingStyle)}`);
|
|
1228
|
+
}
|
|
1229
|
+
if (profile.cognitiveTraits?.explanationFormat) {
|
|
1230
|
+
console.log(` ${pc.dim('•')} Explanation Preference: ${pc.magenta(profile.cognitiveTraits.explanationFormat)}`);
|
|
1231
|
+
}
|
|
1232
|
+
console.log('');
|
|
1233
|
+
}
|
|
1234
|
+
if (hasStrengths || hasConventions) {
|
|
1235
|
+
console.log(pc.bold('Technical Preferences:'));
|
|
1236
|
+
if (hasStrengths) {
|
|
1237
|
+
console.log(` ${pc.dim('•')} Strengths: ${pc.cyan(profile.technicalPreferences.strengths.join(', '))}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (hasConventions) {
|
|
1240
|
+
console.log(` ${pc.dim('•')} Conventions: ${pc.cyan(profile.technicalPreferences.conventions.join(', '))}`);
|
|
1241
|
+
}
|
|
1242
|
+
console.log('');
|
|
1243
|
+
}
|
|
1244
|
+
if (hasNotes) {
|
|
1245
|
+
console.log(pc.bold(`AI Observations & Side-Notes (${profile.agentNotes.length}):`));
|
|
1246
|
+
profile.agentNotes.forEach((note, idx) => {
|
|
1247
|
+
console.log(` ${pc.yellow(`[${idx + 1}]`)} ${note}`);
|
|
1248
|
+
});
|
|
1249
|
+
console.log('');
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
else if (action === 'delete_note') {
|
|
1254
|
+
if (profile.agentNotes.length === 0) {
|
|
1255
|
+
p.log.warn('No side-notes recorded yet to delete.');
|
|
1256
|
+
}
|
|
1257
|
+
else {
|
|
1258
|
+
const noteChoices = profile.agentNotes.map((note, idx) => ({
|
|
1259
|
+
value: idx,
|
|
1260
|
+
label: `${idx + 1}. ${note.length > 70 ? note.slice(0, 67) + '...' : note}`,
|
|
1261
|
+
}));
|
|
1262
|
+
const selectedIndex = await p.select({
|
|
1263
|
+
message: 'Select a side-note to delete:',
|
|
1264
|
+
options: [...noteChoices, { value: -1, label: 'Cancel' }],
|
|
1265
|
+
});
|
|
1266
|
+
if (!p.isCancel(selectedIndex) && typeof selectedIndex === 'number' && selectedIndex >= 0) {
|
|
1267
|
+
await deleteSideNote(selectedIndex);
|
|
1268
|
+
p.log.success(`Deleted side-note [${selectedIndex + 1}].`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
else if (action === 'clear_all') {
|
|
1273
|
+
const confirmClear = await p.confirm({
|
|
1274
|
+
message: 'Are you sure you want to clear your global profile and all AI side-notes?',
|
|
1275
|
+
initialValue: false,
|
|
1276
|
+
});
|
|
1277
|
+
if (confirmClear && !p.isCancel(confirmClear)) {
|
|
1278
|
+
await clearUserProfile();
|
|
1279
|
+
p.log.success('User profile memory and side-notes have been reset.');
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return { shouldContinue: true };
|
|
1283
|
+
}
|
|
1122
1284
|
return { shouldContinue: true };
|
|
1123
1285
|
}
|
|
@@ -180,11 +180,12 @@ export declare function traceDependencies(workspaceRoot: string, filePath: strin
|
|
|
180
180
|
*/
|
|
181
181
|
export declare function findRecentChanges(workspaceRoot: string, dirPath?: string, minutes?: number, maxDepth?: number): Promise<ToolResult>;
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
183
|
+
* Executes an ephemeral debugging or validation script in the specified language runtime.
|
|
184
|
+
* Delegates to the sandboxed ephemeral script runner in `os.tmpdir()` with automatic ESM/CJS
|
|
185
|
+
* module resolution and multi-language runtime detection.
|
|
185
186
|
*
|
|
186
187
|
* @param workspaceRoot - Absolute path to the workspace root directory.
|
|
187
|
-
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, or `'
|
|
188
|
+
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, `'rust'`, `'c'`, `'cpp'`, `'ruby'`, `'php'`, `'java'`, or `'auto'`).
|
|
188
189
|
* @param code - The exact script source code to execute.
|
|
189
190
|
* @param abortSignal - Optional AbortSignal to cancel execution.
|
|
190
191
|
* @returns A promise resolving to a {@link ToolResult} containing execution output.
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { exec } from 'node:child_process';
|
|
15
15
|
import { promises as fs } from 'node:fs';
|
|
16
|
-
import os from 'node:os';
|
|
17
16
|
import path from 'node:path';
|
|
18
17
|
import { promisify } from 'node:util';
|
|
19
18
|
import { SchemaType } from '@google/generative-ai';
|
|
@@ -31,7 +30,7 @@ import { extractSymbols } from '../utils/symbolExtractor.js';
|
|
|
31
30
|
import { getMetricCollector } from './metrics.js';
|
|
32
31
|
import { getCurrentAgentId } from '../utils/asyncContext.js';
|
|
33
32
|
import { recordFileRead, hasAgentReadFile } from '../utils/fileReadGuard.js';
|
|
34
|
-
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, } from '../utils/analysisRunner.js';
|
|
33
|
+
import { runFuzzProbe, checkHeapDelta as runCheckHeapDelta, checkBehavioralDrift as runCheckBehavioralDrift, runEphemeralScript, } from '../utils/analysisRunner.js';
|
|
35
34
|
import ignore from 'ignore';
|
|
36
35
|
const execAsync = promisify(exec);
|
|
37
36
|
// ─── Tool Declarations for Gemini Function Calling ───────────────────
|
|
@@ -1112,6 +1111,13 @@ export async function listDirectory(workspaceRoot, dirPath, maxDepth = 3) {
|
|
|
1112
1111
|
* @returns A promise resolving to a {@link ToolResult} containing stdout/stderr or an error.
|
|
1113
1112
|
*/
|
|
1114
1113
|
export async function runCommand(workspaceRoot, command, abortSignal) {
|
|
1114
|
+
// Reject sudo or interactive root commands to prevent non-interactive subshell deadlocks and security escalation
|
|
1115
|
+
if (/\bsudo\b/i.test(command)) {
|
|
1116
|
+
return {
|
|
1117
|
+
output: '',
|
|
1118
|
+
error: "Execution blocked: Commands requiring 'sudo' or root privileges cannot be executed automatically in background tool loops. Please explain to the user in your text response that they must run this command manually in their own terminal.",
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1115
1121
|
try {
|
|
1116
1122
|
const { stdout, stderr } = await execAsync(command, {
|
|
1117
1123
|
cwd: workspaceRoot,
|
|
@@ -1351,102 +1357,49 @@ export async function findRecentChanges(workspaceRoot, dirPath = '.', minutes =
|
|
|
1351
1357
|
}
|
|
1352
1358
|
// ─── Tool Dispatcher ─────────────────────────────────────────────────
|
|
1353
1359
|
/**
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1360
|
+
* Executes an ephemeral debugging or validation script in the specified language runtime.
|
|
1361
|
+
* Delegates to the sandboxed ephemeral script runner in `os.tmpdir()` with automatic ESM/CJS
|
|
1362
|
+
* module resolution and multi-language runtime detection.
|
|
1356
1363
|
*
|
|
1357
1364
|
* @param workspaceRoot - Absolute path to the workspace root directory.
|
|
1358
|
-
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, or `'
|
|
1365
|
+
* @param language - Runtime language (`'node'`, `'ts-node'`, `'python'`, `'bash'`, `'go'`, `'rust'`, `'c'`, `'cpp'`, `'ruby'`, `'php'`, `'java'`, or `'auto'`).
|
|
1359
1366
|
* @param code - The exact script source code to execute.
|
|
1360
1367
|
* @param abortSignal - Optional AbortSignal to cancel execution.
|
|
1361
1368
|
* @returns A promise resolving to a {@link ToolResult} containing execution output.
|
|
1362
1369
|
*/
|
|
1363
1370
|
export async function runDebugScript(workspaceRoot, language, code, abortSignal) {
|
|
1364
|
-
const extMap = {
|
|
1365
|
-
node: '.js',
|
|
1366
|
-
'ts-node': '.ts',
|
|
1367
|
-
python: '.py',
|
|
1368
|
-
bash: '.sh',
|
|
1369
|
-
go: '.go',
|
|
1370
|
-
rust: '.rs',
|
|
1371
|
-
};
|
|
1372
|
-
const ext = extMap[language.toLowerCase()] || '.txt';
|
|
1373
|
-
const tmpFileName = `.minovative-scratch${ext}`;
|
|
1374
|
-
const absPath = path.join(workspaceRoot, tmpFileName);
|
|
1375
1371
|
try {
|
|
1376
1372
|
const p = await import('@clack/prompts');
|
|
1377
1373
|
const pc = (await import('picocolors')).default;
|
|
1378
|
-
p.log.info(pc.dim(`🛠️ Running temporary ${language} debug script...`));
|
|
1379
|
-
await
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
cmd = `npx ts-node ${tmpFileName}`;
|
|
1387
|
-
break;
|
|
1388
|
-
case 'python':
|
|
1389
|
-
cmd = `python3 ${tmpFileName}`;
|
|
1390
|
-
break;
|
|
1391
|
-
case 'bash':
|
|
1392
|
-
cmd = `bash ${tmpFileName}`;
|
|
1393
|
-
break;
|
|
1394
|
-
case 'go':
|
|
1395
|
-
cmd = `go run ${tmpFileName}`;
|
|
1396
|
-
break;
|
|
1397
|
-
case 'rust': {
|
|
1398
|
-
const binExt = os.platform() === 'win32' ? '.exe' : '';
|
|
1399
|
-
const binPath = tmpFileName.replace('.rs', binExt);
|
|
1400
|
-
const runCmd = os.platform() === 'win32' ? binPath : `./${binPath}`;
|
|
1401
|
-
cmd = `rustc ${tmpFileName} -o ${binPath} && ${runCmd}`;
|
|
1402
|
-
break;
|
|
1403
|
-
}
|
|
1404
|
-
default:
|
|
1405
|
-
return { output: '', error: `Unsupported language runtime: ${language}` };
|
|
1374
|
+
p.log.info(pc.dim(`🛠️ Running temporary ${language || 'script'} debug script...`));
|
|
1375
|
+
const res = await runEphemeralScript(workspaceRoot, language || 'auto', code, {
|
|
1376
|
+
abortSignal,
|
|
1377
|
+
timeoutMs: 60_000,
|
|
1378
|
+
});
|
|
1379
|
+
let finalOutput = '';
|
|
1380
|
+
if (res.exitCode !== 0) {
|
|
1381
|
+
finalOutput += `Script failed with exit code ${res.exitCode}.\n`;
|
|
1406
1382
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
const out = stdout.trim();
|
|
1410
|
-
const errOut = stderr.trim();
|
|
1411
|
-
let finalOutput = '';
|
|
1412
|
-
if (out)
|
|
1413
|
-
finalOutput += `[STDOUT]\n${out}\n`;
|
|
1414
|
-
if (errOut)
|
|
1415
|
-
finalOutput += `[STDERR]\n${errOut}\n`;
|
|
1416
|
-
if (!finalOutput)
|
|
1417
|
-
finalOutput = 'Script executed successfully with no output.';
|
|
1418
|
-
return { output: `<test_results>\n${sanitizeForCDATA(finalOutput)}\n</test_results>` };
|
|
1383
|
+
if (res.stdout) {
|
|
1384
|
+
finalOutput += `[STDOUT]\n${res.stdout}\n`;
|
|
1419
1385
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
const errOut = (err.stderr || '').trim();
|
|
1423
|
-
let finalOutput = `Script failed with exit code ${err.code || 1}.\n`;
|
|
1424
|
-
if (out)
|
|
1425
|
-
finalOutput += `[STDOUT]\n${out}\n`;
|
|
1426
|
-
if (errOut)
|
|
1427
|
-
finalOutput += `[STDERR]\n${errOut}\n`;
|
|
1428
|
-
return { output: `<test_results>\n${sanitizeForCDATA(finalOutput)}\n</test_results>` };
|
|
1386
|
+
if (res.stderr) {
|
|
1387
|
+
finalOutput += `[STDERR]\n${res.stderr}\n`;
|
|
1429
1388
|
}
|
|
1389
|
+
if (!finalOutput.trim()) {
|
|
1390
|
+
finalOutput = 'Script executed successfully with no output.';
|
|
1391
|
+
}
|
|
1392
|
+
return {
|
|
1393
|
+
output: `<test_results>\n${sanitizeForCDATA(finalOutput.trim())}\n</test_results>`,
|
|
1394
|
+
...(res.exitCode !== 0 ? { error: `Script failed with exit code ${res.exitCode}` } : {}),
|
|
1395
|
+
};
|
|
1430
1396
|
}
|
|
1431
1397
|
catch (err) {
|
|
1432
1398
|
return {
|
|
1433
1399
|
output: '',
|
|
1434
|
-
error: `Failed to
|
|
1400
|
+
error: `Failed to execute debug script: ${err instanceof Error ? err.message : String(err)}`,
|
|
1435
1401
|
};
|
|
1436
1402
|
}
|
|
1437
|
-
finally {
|
|
1438
|
-
try {
|
|
1439
|
-
await fs.rm(absPath, { force: true });
|
|
1440
|
-
if (language.toLowerCase() === 'rust') {
|
|
1441
|
-
const binExt = os.platform() === 'win32' ? '.exe' : '';
|
|
1442
|
-
const binPath = absPath.replace('.rs', binExt);
|
|
1443
|
-
await fs.rm(binPath, { force: true });
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
catch {
|
|
1447
|
-
// Ignore cleanup errors
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
1403
|
}
|
|
1451
1404
|
/**
|
|
1452
1405
|
* Executes a fuzz probing analysis script and formats the resulting pass/fail and crash statistics.
|
package/dist/services/agent.d.ts
CHANGED
|
@@ -112,15 +112,14 @@ export declare function executeSingleTurn(workspaceRoot: string, userInput: stri
|
|
|
112
112
|
} | void>;
|
|
113
113
|
/**
|
|
114
114
|
* Raw chat history entry count threshold to trigger chat history summarization.
|
|
115
|
-
*
|
|
115
|
+
* 2 entries = 1 complete conversational user/model turn.
|
|
116
116
|
*/
|
|
117
|
-
export declare const HISTORY_SUMMARIZATION_THRESHOLD =
|
|
117
|
+
export declare const HISTORY_SUMMARIZATION_THRESHOLD = 2;
|
|
118
118
|
/**
|
|
119
|
-
* Summarizes
|
|
120
|
-
* exceeds {@link HISTORY_SUMMARIZATION_THRESHOLD} entries.
|
|
119
|
+
* Summarizes chat history entries starting from the first full user/model turn.
|
|
121
120
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* Compresses preceding conversation history into a structured high-density summary pair
|
|
122
|
+
* in the active session while permanently preserving uncompressed history in fullHistory.
|
|
124
123
|
*
|
|
125
124
|
* @param chat - The active ProxyChatSession instance.
|
|
126
125
|
* @returns A promise resolving to true if history was summarized and updated; false otherwise.
|