@codemieai/code 0.0.3 ā 0.0.4
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 +375 -325
- package/bin/codemie-claude.js +23 -0
- package/bin/codemie-code.js +49 -11
- package/bin/codemie-codex.js +12 -13
- package/dist/agents/adapters/claude-code.d.ts +5 -0
- package/dist/agents/adapters/claude-code.d.ts.map +1 -1
- package/dist/agents/adapters/claude-code.js +76 -18
- package/dist/agents/adapters/claude-code.js.map +1 -1
- package/dist/agents/adapters/codex.d.ts +5 -0
- package/dist/agents/adapters/codex.d.ts.map +1 -1
- package/dist/agents/adapters/codex.js +75 -17
- package/dist/agents/adapters/codex.js.map +1 -1
- package/dist/agents/codemie-code/agent.d.ts.map +1 -1
- package/dist/agents/codemie-code/agent.js +187 -21
- package/dist/agents/codemie-code/agent.js.map +1 -1
- package/dist/agents/codemie-code/config.d.ts.map +1 -1
- package/dist/agents/codemie-code/config.js +29 -27
- package/dist/agents/codemie-code/config.js.map +1 -1
- package/dist/agents/codemie-code/index.d.ts +16 -2
- package/dist/agents/codemie-code/index.d.ts.map +1 -1
- package/dist/agents/codemie-code/index.js +74 -6
- package/dist/agents/codemie-code/index.js.map +1 -1
- package/dist/agents/codemie-code/modes/contextAwarePlanning.d.ts +87 -0
- package/dist/agents/codemie-code/modes/contextAwarePlanning.d.ts.map +1 -0
- package/dist/agents/codemie-code/modes/contextAwarePlanning.js +957 -0
- package/dist/agents/codemie-code/modes/contextAwarePlanning.js.map +1 -0
- package/dist/agents/codemie-code/modes/planMode.d.ts +116 -0
- package/dist/agents/codemie-code/modes/planMode.d.ts.map +1 -0
- package/dist/agents/codemie-code/modes/planMode.js +537 -0
- package/dist/agents/codemie-code/modes/planMode.js.map +1 -0
- package/dist/agents/codemie-code/prompts.d.ts +29 -0
- package/dist/agents/codemie-code/prompts.d.ts.map +1 -1
- package/dist/agents/codemie-code/prompts.js +129 -0
- package/dist/agents/codemie-code/prompts.js.map +1 -1
- package/dist/agents/codemie-code/storage/todoStorage.d.ts +78 -0
- package/dist/agents/codemie-code/storage/todoStorage.d.ts.map +1 -0
- package/dist/agents/codemie-code/storage/todoStorage.js +225 -0
- package/dist/agents/codemie-code/storage/todoStorage.js.map +1 -0
- package/dist/agents/codemie-code/tokenUtils.js +1 -1
- package/dist/agents/codemie-code/tokenUtils.js.map +1 -1
- package/dist/agents/codemie-code/tools/index.d.ts +26 -0
- package/dist/agents/codemie-code/tools/index.d.ts.map +1 -1
- package/dist/agents/codemie-code/tools/index.js +182 -14
- package/dist/agents/codemie-code/tools/index.js.map +1 -1
- package/dist/agents/codemie-code/tools/planning.d.ts +53 -0
- package/dist/agents/codemie-code/tools/planning.d.ts.map +1 -0
- package/dist/agents/codemie-code/tools/planning.js +224 -0
- package/dist/agents/codemie-code/tools/planning.js.map +1 -0
- package/dist/agents/codemie-code/types.d.ts +170 -6
- package/dist/agents/codemie-code/types.d.ts.map +1 -1
- package/dist/agents/codemie-code/types.js.map +1 -1
- package/dist/agents/codemie-code/ui/progressTracker.d.ts +125 -0
- package/dist/agents/codemie-code/ui/progressTracker.d.ts.map +1 -0
- package/dist/agents/codemie-code/ui/progressTracker.js +343 -0
- package/dist/agents/codemie-code/ui/progressTracker.js.map +1 -0
- package/dist/agents/codemie-code/ui/todoPanel.d.ts +112 -0
- package/dist/agents/codemie-code/ui/todoPanel.d.ts.map +1 -0
- package/dist/agents/codemie-code/ui/todoPanel.js +318 -0
- package/dist/agents/codemie-code/ui/todoPanel.js.map +1 -0
- package/dist/agents/codemie-code/ui.d.ts +106 -10
- package/dist/agents/codemie-code/ui.d.ts.map +1 -1
- package/dist/agents/codemie-code/ui.js +913 -129
- package/dist/agents/codemie-code/ui.js.map +1 -1
- package/dist/agents/codemie-code/utils/progressionEnforcer.d.ts +87 -0
- package/dist/agents/codemie-code/utils/progressionEnforcer.d.ts.map +1 -0
- package/dist/agents/codemie-code/utils/progressionEnforcer.js +293 -0
- package/dist/agents/codemie-code/utils/progressionEnforcer.js.map +1 -0
- package/dist/agents/codemie-code/utils/todoParser.d.ts +41 -0
- package/dist/agents/codemie-code/utils/todoParser.d.ts.map +1 -0
- package/dist/agents/codemie-code/utils/todoParser.js +305 -0
- package/dist/agents/codemie-code/utils/todoParser.js.map +1 -0
- package/dist/agents/codemie-code/utils/todoValidator.d.ts +65 -0
- package/dist/agents/codemie-code/utils/todoValidator.d.ts.map +1 -0
- package/dist/agents/codemie-code/utils/todoValidator.js +249 -0
- package/dist/agents/codemie-code/utils/todoValidator.js.map +1 -0
- package/dist/agents/codemie-code/validators/planValidator.d.ts +94 -0
- package/dist/agents/codemie-code/validators/planValidator.d.ts.map +1 -0
- package/dist/agents/codemie-code/validators/planValidator.js +281 -0
- package/dist/agents/codemie-code/validators/planValidator.js.map +1 -0
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +7 -5
- package/dist/agents/registry.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +3 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +170 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/config.js +40 -13
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +209 -16
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/env.js +3 -3
- package/dist/cli/commands/env.js.map +1 -1
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +2 -1
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +15 -9
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +177 -11
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/tools.d.ts +6 -0
- package/dist/cli/commands/tools.d.ts.map +1 -0
- package/dist/cli/commands/tools.js +244 -0
- package/dist/cli/commands/tools.js.map +1 -0
- package/dist/cli/commands/version.js +1 -1
- package/dist/cli/commands/version.js.map +1 -1
- package/dist/cli/commands/workflow.d.ts +6 -0
- package/dist/cli/commands/workflow.d.ts.map +1 -0
- package/dist/cli/commands/workflow.js +424 -0
- package/dist/cli/commands/workflow.js.map +1 -0
- package/dist/cli/index.js +39 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/clients/adapters/github.d.ts +17 -0
- package/dist/clients/adapters/github.d.ts.map +1 -0
- package/dist/clients/adapters/github.js +150 -0
- package/dist/clients/adapters/github.js.map +1 -0
- package/dist/clients/adapters/gitlab.d.ts +17 -0
- package/dist/clients/adapters/gitlab.d.ts.map +1 -0
- package/dist/clients/adapters/gitlab.js +147 -0
- package/dist/clients/adapters/gitlab.js.map +1 -0
- package/dist/clients/registry.d.ts +20 -0
- package/dist/clients/registry.d.ts.map +1 -0
- package/dist/clients/registry.js +27 -0
- package/dist/clients/registry.js.map +1 -0
- package/dist/tools/detector.d.ts +33 -0
- package/dist/tools/detector.d.ts.map +1 -0
- package/dist/tools/detector.js +145 -0
- package/dist/tools/detector.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/manager.d.ts +21 -0
- package/dist/tools/manager.d.ts.map +1 -0
- package/dist/tools/manager.js +104 -0
- package/dist/tools/manager.js.map +1 -0
- package/dist/tools/registry.d.ts +8 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +36 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +41 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +5 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types/sso.d.ts +42 -0
- package/dist/types/sso.d.ts.map +1 -0
- package/dist/types/sso.js +2 -0
- package/dist/types/sso.js.map +1 -0
- package/dist/utils/agent-compatibility.d.ts +32 -0
- package/dist/utils/agent-compatibility.d.ts.map +1 -0
- package/dist/utils/agent-compatibility.js +140 -0
- package/dist/utils/agent-compatibility.js.map +1 -0
- package/dist/utils/codemie-integration-validator.d.ts +17 -0
- package/dist/utils/codemie-integration-validator.d.ts.map +1 -0
- package/dist/utils/codemie-integration-validator.js +105 -0
- package/dist/utils/codemie-integration-validator.js.map +1 -0
- package/dist/utils/codemie-model-fetcher.d.ts +11 -0
- package/dist/utils/codemie-model-fetcher.d.ts.map +1 -0
- package/dist/utils/codemie-model-fetcher.js +242 -0
- package/dist/utils/codemie-model-fetcher.js.map +1 -0
- package/dist/utils/config-loader.d.ts +23 -1
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-loader.js +73 -27
- package/dist/utils/config-loader.js.map +1 -1
- package/dist/utils/credential-store.d.ts +16 -0
- package/dist/utils/credential-store.d.ts.map +1 -0
- package/dist/utils/credential-store.js +109 -0
- package/dist/utils/credential-store.js.map +1 -0
- package/dist/utils/first-time.d.ts +1 -1
- package/dist/utils/first-time.d.ts.map +1 -1
- package/dist/utils/first-time.js +52 -71
- package/dist/utils/first-time.js.map +1 -1
- package/dist/utils/health-checker.d.ts.map +1 -1
- package/dist/utils/health-checker.js +5 -1
- package/dist/utils/health-checker.js.map +1 -1
- package/dist/utils/model-fetcher.d.ts.map +1 -1
- package/dist/utils/model-fetcher.js +15 -2
- package/dist/utils/model-fetcher.js.map +1 -1
- package/dist/utils/sso-auth.d.ts +15 -0
- package/dist/utils/sso-auth.d.ts.map +1 -0
- package/dist/utils/sso-auth.js +207 -0
- package/dist/utils/sso-auth.js.map +1 -0
- package/dist/utils/sso-gateway.d.ts +47 -0
- package/dist/utils/sso-gateway.d.ts.map +1 -0
- package/dist/utils/sso-gateway.js +298 -0
- package/dist/utils/sso-gateway.js.map +1 -0
- package/dist/workflows/detector.d.ts +37 -0
- package/dist/workflows/detector.d.ts.map +1 -0
- package/dist/workflows/detector.js +160 -0
- package/dist/workflows/detector.js.map +1 -0
- package/dist/workflows/index.d.ts +8 -0
- package/dist/workflows/index.d.ts.map +1 -0
- package/dist/workflows/index.js +8 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/installer.d.ts +24 -0
- package/dist/workflows/installer.d.ts.map +1 -0
- package/dist/workflows/installer.js +105 -0
- package/dist/workflows/installer.js.map +1 -0
- package/dist/workflows/registry.d.ts +29 -0
- package/dist/workflows/registry.d.ts.map +1 -0
- package/dist/workflows/registry.js +54 -0
- package/dist/workflows/registry.js.map +1 -0
- package/dist/workflows/templates/github/metadata.d.ts +6 -0
- package/dist/workflows/templates/github/metadata.d.ts.map +1 -0
- package/dist/workflows/templates/github/metadata.js +111 -0
- package/dist/workflows/templates/github/metadata.js.map +1 -0
- package/dist/workflows/templates/gitlab/metadata.d.ts +6 -0
- package/dist/workflows/templates/gitlab/metadata.d.ts.map +1 -0
- package/dist/workflows/templates/gitlab/metadata.js +14 -0
- package/dist/workflows/templates/gitlab/metadata.js.map +1 -0
- package/dist/workflows/types.d.ts +71 -0
- package/dist/workflows/types.d.ts.map +1 -0
- package/dist/workflows/types.js +5 -0
- package/dist/workflows/types.js.map +1 -0
- package/package.json +19 -6
- package/src/workflows/templates/github/code-ci.yml +529 -0
- package/src/workflows/templates/github/inline-fix.yml +665 -0
- package/src/workflows/templates/github/pr-review.yml +677 -0
- package/.claude/agents/README.md +0 -298
- package/.claude/agents/release-manager.md +0 -857
- package/.codemie/guides/git-workflow.md +0 -493
- package/CLAUDE.md +0 -225
- package/config.example.json +0 -10
- package/dist/agents/codemie-code/streaming/events.d.ts +0 -7
- package/dist/agents/codemie-code/streaming/events.d.ts.map +0 -1
- package/dist/agents/codemie-code/streaming/events.js +0 -7
- package/dist/agents/codemie-code/streaming/events.js.map +0 -1
- package/dist/agents/codemie-code/streaming/formatter.d.ts +0 -2
- package/dist/agents/codemie-code/streaming/formatter.d.ts.map +0 -1
- package/dist/agents/codemie-code/streaming/formatter.js +0 -2
- package/dist/agents/codemie-code/streaming/formatter.js.map +0 -1
- package/dist/agents/codemie-code/streaming/ui.d.ts +0 -2
- package/dist/agents/codemie-code/streaming/ui.d.ts.map +0 -1
- package/dist/agents/codemie-code/streaming/ui.js +0 -2
- package/dist/agents/codemie-code/streaming/ui.js.map +0 -1
- package/dist/agents/codemie-code/tools/command.d.ts +0 -2
- package/dist/agents/codemie-code/tools/command.d.ts.map +0 -1
- package/dist/agents/codemie-code/tools/command.js +0 -2
- package/dist/agents/codemie-code/tools/command.js.map +0 -1
- package/dist/agents/codemie-code/tools/filesystem.d.ts +0 -2
- package/dist/agents/codemie-code/tools/filesystem.d.ts.map +0 -1
- package/dist/agents/codemie-code/tools/filesystem.js +0 -2
- package/dist/agents/codemie-code/tools/filesystem.js.map +0 -1
- package/dist/agents/codemie-code/tools/git.d.ts +0 -2
- package/dist/agents/codemie-code/tools/git.d.ts.map +0 -1
- package/dist/agents/codemie-code/tools/git.js +0 -2
- package/dist/agents/codemie-code/tools/git.js.map +0 -1
- package/dist/agents/codemie-code/tools/security.d.ts +0 -2
- package/dist/agents/codemie-code/tools/security.d.ts.map +0 -1
- package/dist/agents/codemie-code/tools/security.js +0 -2
- package/dist/agents/codemie-code/tools/security.js.map +0 -1
- package/eslint.config.mjs +0 -43
- package/scripts/README.md +0 -80
- package/scripts/release.sh +0 -156
|
@@ -3,14 +3,32 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { formatToolMetadata } from './toolMetadata.js';
|
|
4
4
|
import { formatCost, formatTokens, formatTokenUsageSummary } from './tokenUtils.js';
|
|
5
5
|
import { hasClipboardImage, getClipboardImage } from '../../utils/clipboard.js';
|
|
6
|
+
import { TodoPanel } from './ui/todoPanel.js';
|
|
7
|
+
import { getProgressTracker } from './ui/progressTracker.js';
|
|
8
|
+
import { TodoStateManager } from './tools/planning.js';
|
|
6
9
|
/**
|
|
7
10
|
* Terminal UI interface for CodeMie Agent using Clack
|
|
8
11
|
*/
|
|
9
12
|
export class CodeMieTerminalUI {
|
|
10
13
|
agent;
|
|
11
14
|
currentSpinner;
|
|
15
|
+
todoPanel;
|
|
16
|
+
progressTracker;
|
|
17
|
+
planMode = false;
|
|
18
|
+
activePlanningPhase = null;
|
|
12
19
|
constructor(agent) {
|
|
13
20
|
this.agent = agent;
|
|
21
|
+
this.todoPanel = new TodoPanel({
|
|
22
|
+
showProgress: true,
|
|
23
|
+
compact: false
|
|
24
|
+
});
|
|
25
|
+
this.progressTracker = getProgressTracker({
|
|
26
|
+
realTimeUpdates: true,
|
|
27
|
+
showCelebrations: true,
|
|
28
|
+
compact: true
|
|
29
|
+
});
|
|
30
|
+
// Register for todo update events
|
|
31
|
+
TodoStateManager.addEventCallback(this.handleTodoUpdate.bind(this));
|
|
14
32
|
}
|
|
15
33
|
/**
|
|
16
34
|
* Start interactive terminal session
|
|
@@ -20,13 +38,19 @@ export class CodeMieTerminalUI {
|
|
|
20
38
|
intro(chalk.cyan('š¤ CodeMie Native Agent'));
|
|
21
39
|
const config = this.agent.getConfig();
|
|
22
40
|
if (config) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
// Use displayProvider for user-facing output, or fall back to normalized provider
|
|
42
|
+
const displayProvider = config.displayProvider || config.provider;
|
|
43
|
+
console.log(chalk.cyan('ā Configuration'));
|
|
44
|
+
console.log(` Provider: ${chalk.yellow(displayProvider)}`);
|
|
45
|
+
console.log(` Model: ${chalk.cyan(config.model)}`);
|
|
46
|
+
console.log(` Working Directory: ${chalk.dim(config.workingDirectory)}`);
|
|
47
|
+
console.log(` Mode: ${this.planMode ? chalk.green('Plan Mode') : chalk.yellow('Direct Mode')}`);
|
|
48
|
+
console.log('');
|
|
26
49
|
}
|
|
27
50
|
console.log(chalk.dim('Type /help for commands, /exit to quit'));
|
|
28
51
|
console.log(chalk.dim('Enter = send, Shift+Enter = new line, Cmd+V = paste text'));
|
|
29
|
-
console.log(chalk.dim('šø Tab = insert clipboard image ⢠Multiple images supported
|
|
52
|
+
console.log(chalk.dim('šø Tab = insert clipboard image ⢠Multiple images supported'));
|
|
53
|
+
console.log(chalk.dim('š” Press Ctrl+H for hotkeys, Ctrl+P to toggle plan mode\n'));
|
|
30
54
|
// Main interaction loop
|
|
31
55
|
while (true) {
|
|
32
56
|
const input = await this.getMultilineInput();
|
|
@@ -44,8 +68,8 @@ export class CodeMieTerminalUI {
|
|
|
44
68
|
}
|
|
45
69
|
if (trimmed === '')
|
|
46
70
|
continue;
|
|
47
|
-
// Execute the task with streaming UI
|
|
48
|
-
await this.
|
|
71
|
+
// Execute the task with streaming UI (respecting current mode)
|
|
72
|
+
await this.executeTaskWithCurrentMode(trimmed, input.images);
|
|
49
73
|
}
|
|
50
74
|
}
|
|
51
75
|
/**
|
|
@@ -55,127 +79,220 @@ export class CodeMieTerminalUI {
|
|
|
55
79
|
return new Promise((resolve) => {
|
|
56
80
|
if (!process.stdin.setRawMode) {
|
|
57
81
|
// Fallback for environments without raw mode
|
|
58
|
-
|
|
82
|
+
this.getFallbackInput().then(resolve).catch(() => resolve(null));
|
|
59
83
|
return;
|
|
60
84
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let lines = [];
|
|
65
|
-
let currentLine = '';
|
|
66
|
-
let isFirstLine = true;
|
|
67
|
-
let escapeSequence = '';
|
|
68
|
-
let images = [];
|
|
69
|
-
let imageCounter = 0;
|
|
70
|
-
const writePrompt = () => {
|
|
71
|
-
const prompt = isFirstLine ? '> ' : '... ';
|
|
72
|
-
process.stdout.write(prompt);
|
|
73
|
-
};
|
|
74
|
-
const cleanup = () => {
|
|
75
|
-
process.stdin.setRawMode(false);
|
|
76
|
-
process.stdin.pause();
|
|
77
|
-
process.stdin.removeAllListeners('data');
|
|
78
|
-
};
|
|
79
|
-
writePrompt();
|
|
80
|
-
process.stdin.on('data', (key) => {
|
|
81
|
-
const data = key.toString('utf8');
|
|
82
|
-
// Handle escape sequences
|
|
83
|
-
if (data.startsWith('\x1b')) {
|
|
84
|
-
escapeSequence += data;
|
|
85
|
-
// Wait for complete escape sequence (timeout after short delay)
|
|
86
|
-
setTimeout(() => {
|
|
87
|
-
escapeSequence = '';
|
|
88
|
-
}, 10);
|
|
85
|
+
let cleanupDone = false;
|
|
86
|
+
const performCleanup = () => {
|
|
87
|
+
if (cleanupDone)
|
|
89
88
|
return;
|
|
89
|
+
cleanupDone = true;
|
|
90
|
+
try {
|
|
91
|
+
if (process.stdin.setRawMode) {
|
|
92
|
+
process.stdin.setRawMode(false);
|
|
93
|
+
}
|
|
94
|
+
process.stdin.pause();
|
|
95
|
+
process.stdin.removeAllListeners('data');
|
|
96
|
+
process.stdin.removeAllListeners('error');
|
|
90
97
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (data === '\r\n' || data === '\n\r' || (escapeSequence && data === '\r')) {
|
|
94
|
-
// This is Shift+Enter - add line and continue
|
|
95
|
-
lines.push(currentLine);
|
|
96
|
-
currentLine = '';
|
|
97
|
-
isFirstLine = false;
|
|
98
|
-
process.stdout.write('\n');
|
|
99
|
-
writePrompt();
|
|
100
|
-
escapeSequence = '';
|
|
101
|
-
return;
|
|
98
|
+
catch {
|
|
99
|
+
// Ignore cleanup errors
|
|
102
100
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
};
|
|
102
|
+
// Set up error handling first
|
|
103
|
+
process.stdin.once('error', (error) => {
|
|
104
|
+
performCleanup();
|
|
105
|
+
const config = this.agent.getConfig();
|
|
106
|
+
if (config?.debug) {
|
|
107
|
+
console.error('[DEBUG] Stdin error:', error);
|
|
108
|
+
}
|
|
109
|
+
resolve(null);
|
|
110
|
+
});
|
|
111
|
+
try {
|
|
112
|
+
process.stdin.setRawMode(true);
|
|
113
|
+
process.stdin.resume();
|
|
114
|
+
process.stdin.setEncoding('utf8');
|
|
115
|
+
let lines = [];
|
|
116
|
+
let currentLine = '';
|
|
117
|
+
let isFirstLine = true;
|
|
118
|
+
let escapeSequence = '';
|
|
119
|
+
let images = [];
|
|
120
|
+
let imageCounter = 0;
|
|
121
|
+
const writePrompt = () => {
|
|
122
|
+
try {
|
|
123
|
+
const prompt = isFirstLine ? '> ' : '... ';
|
|
124
|
+
process.stdout.write(prompt);
|
|
109
125
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
lines.push(currentLine);
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore prompt write errors
|
|
113
128
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
images: images
|
|
119
|
-
});
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
// Ctrl+C
|
|
123
|
-
if (data === '\u0003') {
|
|
124
|
-
cleanup();
|
|
129
|
+
};
|
|
130
|
+
// Add timeout to prevent hanging indefinitely
|
|
131
|
+
const inputTimeout = setTimeout(() => {
|
|
132
|
+
performCleanup();
|
|
125
133
|
resolve(null);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
}, 30000); // 30 second timeout
|
|
135
|
+
writePrompt();
|
|
136
|
+
process.stdin.on('data', (key) => {
|
|
137
|
+
if (cleanupDone)
|
|
138
|
+
return; // Prevent processing after cleanup
|
|
139
|
+
try {
|
|
140
|
+
const data = key.toString('utf8');
|
|
141
|
+
// Handle escape sequences
|
|
142
|
+
if (data.startsWith('\x1b')) {
|
|
143
|
+
escapeSequence += data;
|
|
144
|
+
// Wait for complete escape sequence (timeout after short delay)
|
|
145
|
+
setTimeout(() => {
|
|
146
|
+
escapeSequence = '';
|
|
147
|
+
}, 10);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Check for Shift+Enter patterns on macOS
|
|
151
|
+
// Shift+Enter in macOS Terminal typically sends: \r\n or \n\r
|
|
152
|
+
if (data === '\r\n' || data === '\n\r' || (escapeSequence && data === '\r')) {
|
|
153
|
+
// This is Shift+Enter - add line and continue
|
|
154
|
+
lines.push(currentLine);
|
|
155
|
+
currentLine = '';
|
|
156
|
+
isFirstLine = false;
|
|
157
|
+
process.stdout.write('\n');
|
|
158
|
+
writePrompt();
|
|
159
|
+
escapeSequence = '';
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
// Regular Enter - send message
|
|
163
|
+
if (data === '\r' || data === '\n') {
|
|
164
|
+
if (currentLine.trim() === '' && lines.length === 0) {
|
|
165
|
+
// Empty input, continue asking
|
|
166
|
+
writePrompt();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// Send the message
|
|
170
|
+
if (currentLine.trim() !== '') {
|
|
171
|
+
lines.push(currentLine);
|
|
172
|
+
}
|
|
173
|
+
process.stdout.write('\n');
|
|
174
|
+
clearTimeout(inputTimeout);
|
|
175
|
+
performCleanup();
|
|
176
|
+
resolve({
|
|
177
|
+
text: lines.join('\n'),
|
|
178
|
+
images: images
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Ctrl+C
|
|
183
|
+
if (data === '\u0003') {
|
|
184
|
+
clearTimeout(inputTimeout);
|
|
185
|
+
performCleanup();
|
|
186
|
+
resolve(null);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Hotkeys for mode switching
|
|
190
|
+
// Ctrl+P - Toggle plan mode
|
|
191
|
+
if (data === '\u0010') {
|
|
192
|
+
this.handleHotkey('toggle-plan-mode');
|
|
193
|
+
// Mode change notification is handled in showModeChangeNotification()
|
|
194
|
+
writePrompt();
|
|
195
|
+
process.stdout.write(currentLine);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
// Ctrl+H - Show hotkey help
|
|
199
|
+
if (data === '\u0008') {
|
|
200
|
+
this.showHotkeyHelp();
|
|
201
|
+
writePrompt();
|
|
202
|
+
process.stdout.write(currentLine);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// Ctrl+T - Show current todos
|
|
206
|
+
if (data === '\u0014') {
|
|
207
|
+
this.handleHotkey('show-todos');
|
|
208
|
+
writePrompt();
|
|
209
|
+
process.stdout.write(currentLine);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// Ctrl+S - Show current mode status (changed from Ctrl+M to avoid Enter conflict)
|
|
213
|
+
if (data === '\u0013') {
|
|
214
|
+
this.showModeStatus();
|
|
215
|
+
writePrompt();
|
|
216
|
+
process.stdout.write(currentLine);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// Alt+M - Show mode status (Alt key sequences start with \x1b)
|
|
220
|
+
if (data === 'm' && escapeSequence.includes('\x1b')) {
|
|
221
|
+
this.showModeStatus();
|
|
222
|
+
writePrompt();
|
|
223
|
+
process.stdout.write(currentLine);
|
|
224
|
+
escapeSequence = '';
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Ctrl+I - Insert image from clipboard (Tab key)
|
|
228
|
+
if (data === '\u0009') {
|
|
229
|
+
// Check if there's an image in clipboard
|
|
230
|
+
hasClipboardImage().then(hasImage => {
|
|
231
|
+
if (hasImage) {
|
|
232
|
+
getClipboardImage().then(clipboardImage => {
|
|
233
|
+
if (clipboardImage) {
|
|
234
|
+
imageCounter++;
|
|
235
|
+
images.push(clipboardImage);
|
|
236
|
+
// Insert visual indicator in current line
|
|
237
|
+
const imageIndicator = chalk.blueBright(`[Image #${imageCounter}]`);
|
|
238
|
+
currentLine += imageIndicator;
|
|
239
|
+
process.stdout.write(imageIndicator);
|
|
240
|
+
console.log(chalk.green(`\nšø Image #${imageCounter} added from clipboard (${clipboardImage.mimeType})`));
|
|
241
|
+
writePrompt();
|
|
242
|
+
process.stdout.write(currentLine);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
console.log(chalk.yellow('\nā ļø No image found in clipboard'));
|
|
142
248
|
writePrompt();
|
|
143
249
|
process.stdout.write(currentLine);
|
|
144
250
|
}
|
|
251
|
+
}).catch(() => {
|
|
252
|
+
console.log(chalk.rgb(255, 120, 120)('\nā Error accessing clipboard'));
|
|
253
|
+
writePrompt();
|
|
254
|
+
process.stdout.write(currentLine);
|
|
145
255
|
});
|
|
256
|
+
return;
|
|
146
257
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
258
|
+
// Backspace
|
|
259
|
+
if (data === '\u007F' || data === '\b') {
|
|
260
|
+
if (currentLine.length > 0) {
|
|
261
|
+
currentLine = currentLine.slice(0, -1);
|
|
262
|
+
process.stdout.write('\b \b');
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
151
265
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
process.stdout.write('\b \b');
|
|
266
|
+
// Handle clipboard paste (Cmd+V on macOS, Ctrl+V on Windows/Linux)
|
|
267
|
+
// Pasted content can be multiple characters, so handle any printable text
|
|
268
|
+
if (data.length > 1 || (data.length === 1 && (data.charCodeAt(0) >= 32 || data === '\t'))) {
|
|
269
|
+
// Filter out non-printable characters except tabs
|
|
270
|
+
const printableData = data.split('').filter(char => char.charCodeAt(0) >= 32 || char === '\t').join('');
|
|
271
|
+
if (printableData.length > 0) {
|
|
272
|
+
currentLine += printableData;
|
|
273
|
+
process.stdout.write(printableData);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
escapeSequence = '';
|
|
164
277
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (printableData.length > 0) {
|
|
173
|
-
currentLine += printableData;
|
|
174
|
-
process.stdout.write(printableData);
|
|
278
|
+
catch (error) {
|
|
279
|
+
// Handle data processing errors gracefully
|
|
280
|
+
const config = this.agent.getConfig();
|
|
281
|
+
if (config?.debug) {
|
|
282
|
+
console.error('[DEBUG] Input processing error:', error);
|
|
283
|
+
}
|
|
284
|
+
// Continue processing - don't crash on single key errors
|
|
175
285
|
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
performCleanup();
|
|
290
|
+
const config = this.agent.getConfig();
|
|
291
|
+
if (config?.debug) {
|
|
292
|
+
console.error('[DEBUG] Input setup error:', error);
|
|
176
293
|
}
|
|
177
|
-
|
|
178
|
-
}
|
|
294
|
+
resolve(null);
|
|
295
|
+
}
|
|
179
296
|
});
|
|
180
297
|
}
|
|
181
298
|
/**
|
|
@@ -207,9 +324,15 @@ export class CodeMieTerminalUI {
|
|
|
207
324
|
note(`${chalk.cyan('/help')} - Show this help message\n` +
|
|
208
325
|
`${chalk.cyan('/clear')} - Clear conversation history\n` +
|
|
209
326
|
`${chalk.cyan('/stats')} - Show agent statistics\n` +
|
|
327
|
+
`${chalk.cyan('/todos')} - Show current todo list and progress\n` +
|
|
210
328
|
`${chalk.cyan('/config')} - Show configuration\n` +
|
|
211
329
|
`${chalk.cyan('/health')} - Run health check\n` +
|
|
212
330
|
`${chalk.cyan('/exit')} - Exit the agent\n\n` +
|
|
331
|
+
`${chalk.yellow('Hotkeys:')}\n` +
|
|
332
|
+
`- ${chalk.cyan('Ctrl+P')} - Toggle plan mode on/off\n` +
|
|
333
|
+
`- ${chalk.cyan('Ctrl+H')} - Show detailed hotkey help\n` +
|
|
334
|
+
`- ${chalk.cyan('Ctrl+T')} - Show current todo list\n` +
|
|
335
|
+
`- ${chalk.cyan('Ctrl+S')} - Show current mode status\n\n` +
|
|
213
336
|
`${chalk.yellow('Input Controls:')}\n` +
|
|
214
337
|
`- ${chalk.cyan('Enter')} - Send message\n` +
|
|
215
338
|
`- ${chalk.cyan('Shift+Enter')} - New line (multiline input)\n` +
|
|
@@ -218,7 +341,7 @@ export class CodeMieTerminalUI {
|
|
|
218
341
|
`- ${chalk.cyan('Ctrl+C')} - Cancel current input\n\n` +
|
|
219
342
|
`${chalk.yellow('Image Support:')}\n` +
|
|
220
343
|
`- Copy image/screenshot to clipboard\n` +
|
|
221
|
-
`- Press ${chalk.cyan('Tab')} to insert as ${chalk.
|
|
344
|
+
`- Press ${chalk.cyan('Tab')} to insert as ${chalk.blueBright('[Image #N]')}\n` +
|
|
222
345
|
`- Multiple images supported per message\n` +
|
|
223
346
|
`- AI analyzes both text and all images`, 'Available Commands');
|
|
224
347
|
break;
|
|
@@ -229,6 +352,9 @@ export class CodeMieTerminalUI {
|
|
|
229
352
|
case 'stats':
|
|
230
353
|
await this.showStats();
|
|
231
354
|
break;
|
|
355
|
+
case 'todos':
|
|
356
|
+
await this.showTodos();
|
|
357
|
+
break;
|
|
232
358
|
case 'config':
|
|
233
359
|
await this.showConfig();
|
|
234
360
|
break;
|
|
@@ -239,7 +365,7 @@ export class CodeMieTerminalUI {
|
|
|
239
365
|
outro(chalk.dim('Goodbye! š'));
|
|
240
366
|
return 'exit';
|
|
241
367
|
default:
|
|
242
|
-
note(chalk.
|
|
368
|
+
note(chalk.rgb(255, 120, 120)(`Unknown command: ${command}\nType /help for available commands`), 'Error');
|
|
243
369
|
break;
|
|
244
370
|
}
|
|
245
371
|
}
|
|
@@ -284,6 +410,17 @@ export class CodeMieTerminalUI {
|
|
|
284
410
|
}
|
|
285
411
|
this.currentSpinner.start(chalk.yellow(`Using ${event.toolName}...`));
|
|
286
412
|
break;
|
|
413
|
+
case 'tool_call_progress':
|
|
414
|
+
if (this.currentSpinner && event.toolProgress) {
|
|
415
|
+
const { percentage, operation, details } = event.toolProgress;
|
|
416
|
+
const progressBar = this.createToolProgressBar(percentage);
|
|
417
|
+
let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
|
|
418
|
+
if (details) {
|
|
419
|
+
message += ` (${details})`;
|
|
420
|
+
}
|
|
421
|
+
this.currentSpinner.message(chalk.cyan(message));
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
287
424
|
case 'tool_call_result':
|
|
288
425
|
if (this.currentSpinner) {
|
|
289
426
|
// Use enhanced metadata if available, otherwise fall back to basic message
|
|
@@ -368,8 +505,10 @@ export class CodeMieTerminalUI {
|
|
|
368
505
|
note('No configuration available', 'Config');
|
|
369
506
|
return;
|
|
370
507
|
}
|
|
508
|
+
// Use displayProvider for user-facing output, or fall back to normalized provider
|
|
509
|
+
const displayProvider = config.displayProvider || config.provider;
|
|
371
510
|
const configText = [
|
|
372
|
-
`Provider: ${chalk.yellow(
|
|
511
|
+
`Provider: ${chalk.yellow(displayProvider)}`,
|
|
373
512
|
`Model: ${chalk.cyan(config.model)}`,
|
|
374
513
|
`Base URL: ${chalk.dim(config.baseUrl)}`,
|
|
375
514
|
`Working Directory: ${chalk.dim(config.workingDirectory)}`,
|
|
@@ -450,6 +589,17 @@ export class CodeMieTerminalUI {
|
|
|
450
589
|
toolCallCount++;
|
|
451
590
|
taskSpinner.message(chalk.yellow(`Using ${event.toolName}...`));
|
|
452
591
|
break;
|
|
592
|
+
case 'tool_call_progress':
|
|
593
|
+
if (event.toolProgress) {
|
|
594
|
+
const { percentage, operation, details } = event.toolProgress;
|
|
595
|
+
const progressBar = this.createToolProgressBar(percentage);
|
|
596
|
+
let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
|
|
597
|
+
if (details) {
|
|
598
|
+
message += ` (${details})`;
|
|
599
|
+
}
|
|
600
|
+
taskSpinner.message(chalk.cyan(message));
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
453
603
|
case 'tool_call_result':
|
|
454
604
|
// Show enhanced tool info in single task mode too
|
|
455
605
|
if (event.toolMetadata) {
|
|
@@ -491,23 +641,98 @@ export class CodeMieTerminalUI {
|
|
|
491
641
|
}
|
|
492
642
|
}
|
|
493
643
|
/**
|
|
494
|
-
*
|
|
495
|
-
*/
|
|
496
|
-
showTaskWelcome(task) {
|
|
497
|
-
intro(chalk.cyan('š¤ CodeMie Native Agent'));
|
|
498
|
-
note(chalk.dim(`Task: ${task}`), 'Executing');
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Show task completion message
|
|
644
|
+
* Execute a task with planning mode and UI streaming (for plan mode)
|
|
502
645
|
*/
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
646
|
+
async executePlanningTask(task, _images = [], planOnly = false) {
|
|
647
|
+
const planSpinner = spinner();
|
|
648
|
+
this.currentSpinner = planSpinner; // Store reference to stop when progress starts
|
|
649
|
+
planSpinner.start(chalk.blueBright('š Starting planning phase...'));
|
|
650
|
+
try {
|
|
651
|
+
// Import PlanMode
|
|
652
|
+
const { PlanMode } = await import('./modes/planMode.js');
|
|
653
|
+
const planMode = new PlanMode(this.agent, {
|
|
654
|
+
requirePlanning: true,
|
|
655
|
+
enforceSequential: true,
|
|
656
|
+
showPlanningFeedback: true
|
|
657
|
+
});
|
|
658
|
+
// Create a UI-connected event callback that handles both planning and streaming events
|
|
659
|
+
const uiEventCallback = (event) => {
|
|
660
|
+
// Handle planning-specific events
|
|
661
|
+
this.handleStreamingEvent(event);
|
|
662
|
+
// Handle regular streaming events too
|
|
663
|
+
switch (event.type) {
|
|
664
|
+
case 'thinking_start':
|
|
665
|
+
planSpinner.message(chalk.dim('Thinking...'));
|
|
666
|
+
break;
|
|
667
|
+
case 'content_chunk':
|
|
668
|
+
// For planning phase, we might not want to show content chunks immediately
|
|
669
|
+
break;
|
|
670
|
+
case 'tool_call_start':
|
|
671
|
+
planSpinner.message(chalk.yellow(`Using ${event.toolName}...`));
|
|
672
|
+
break;
|
|
673
|
+
case 'tool_call_progress':
|
|
674
|
+
if (event.toolProgress) {
|
|
675
|
+
const { percentage, operation, details } = event.toolProgress;
|
|
676
|
+
const progressBar = this.createToolProgressBar(percentage);
|
|
677
|
+
let message = `${operation} ${Math.round(percentage)}% ${progressBar}`;
|
|
678
|
+
if (details) {
|
|
679
|
+
message += ` (${details})`;
|
|
680
|
+
}
|
|
681
|
+
planSpinner.message(chalk.cyan(message));
|
|
682
|
+
}
|
|
683
|
+
break;
|
|
684
|
+
case 'tool_call_result':
|
|
685
|
+
if (event.toolMetadata) {
|
|
686
|
+
const message = formatToolMetadata(event.toolName || 'tool', event.toolMetadata);
|
|
687
|
+
planSpinner.message(chalk.green(message));
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
planSpinner.message(chalk.dim('Processing...'));
|
|
691
|
+
}
|
|
692
|
+
break;
|
|
693
|
+
case 'complete':
|
|
694
|
+
planSpinner.stop();
|
|
695
|
+
break;
|
|
696
|
+
case 'error':
|
|
697
|
+
planSpinner.stop(chalk.red(`Error: ${event.error}`));
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
if (planOnly) {
|
|
702
|
+
// Only generate plan, don't execute
|
|
703
|
+
const planningResult = await planMode.planningPhase(task, uiEventCallback);
|
|
704
|
+
planSpinner.stop();
|
|
705
|
+
if (!planningResult.success) {
|
|
706
|
+
throw new Error(`Planning failed: ${planningResult.error}`);
|
|
707
|
+
}
|
|
708
|
+
return `š Plan generated successfully with ${planningResult.todos.length} steps:\n\n` +
|
|
709
|
+
planningResult.todos.map((todo, i) => `${i + 1}. ${todo.content}`).join('\n') +
|
|
710
|
+
`\n\nQuality Score: ${planningResult.qualityScore}/100\n` +
|
|
711
|
+
(planningResult.suggestions.length > 0 ?
|
|
712
|
+
`\nSuggestions:\n${planningResult.suggestions.map((s) => `⢠${s}`).join('\n')}` : '') +
|
|
713
|
+
`\n\nšÆ **Plan-only mode**: Plan created. Use --plan flag (without --plan-only) to execute this plan.`;
|
|
714
|
+
}
|
|
715
|
+
// Full planning + execution
|
|
716
|
+
const result = await planMode.executePlannedTask(task, uiEventCallback);
|
|
717
|
+
planSpinner.stop();
|
|
718
|
+
// Show success message with token usage
|
|
719
|
+
const stats = this.agent.getStats();
|
|
720
|
+
const summaryParts = [];
|
|
721
|
+
if (stats.totalTokens > 0) {
|
|
722
|
+
summaryParts.push(`${formatTokens(stats.totalTokens)} tokens`);
|
|
723
|
+
}
|
|
724
|
+
if (stats.estimatedTotalCost > 0) {
|
|
725
|
+
summaryParts.push(`${formatCost(stats.estimatedTotalCost)}`);
|
|
726
|
+
}
|
|
727
|
+
if (summaryParts.length > 0) {
|
|
728
|
+
console.log(chalk.dim(`${summaryParts.join(' ⢠')}\n`));
|
|
729
|
+
}
|
|
730
|
+
return result;
|
|
731
|
+
}
|
|
732
|
+
catch (error) {
|
|
733
|
+
planSpinner.stop(chalk.red('Planning failed'));
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
511
736
|
}
|
|
512
737
|
/**
|
|
513
738
|
* Check if we should show detailed information for a tool
|
|
@@ -611,6 +836,562 @@ export class CodeMieTerminalUI {
|
|
|
611
836
|
}
|
|
612
837
|
note(stepLines.join('\n'), 'Step Details');
|
|
613
838
|
}
|
|
839
|
+
/**
|
|
840
|
+
* Handle todo update events
|
|
841
|
+
*/
|
|
842
|
+
handleTodoUpdate(event) {
|
|
843
|
+
// Update internal state
|
|
844
|
+
this.todoPanel.update(event.todos);
|
|
845
|
+
// Only show progress tracker updates when NOT in active planning phase
|
|
846
|
+
// This prevents duplicate progress displays during context-aware planning
|
|
847
|
+
if (!this.activePlanningPhase) {
|
|
848
|
+
this.progressTracker.updateTodos(event.todos, event);
|
|
849
|
+
// Show visual feedback based on change type
|
|
850
|
+
if (event.changeType === 'create' && event.todos.length > 0) {
|
|
851
|
+
this.progressTracker.showPlanningComplete(event.todos.length);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
// During planning phase, update internal state but suppress visual feedback
|
|
856
|
+
this.progressTracker.updateTodos(event.todos, event, true);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Enable plan mode for structured planning
|
|
861
|
+
*/
|
|
862
|
+
enablePlanMode() {
|
|
863
|
+
this.planMode = true;
|
|
864
|
+
// Remove redundant messages - UI will show plan mode status in the configuration display
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Disable plan mode
|
|
868
|
+
*/
|
|
869
|
+
disablePlanMode() {
|
|
870
|
+
this.planMode = false;
|
|
871
|
+
this.progressTracker.stop();
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Show current todo status
|
|
875
|
+
*/
|
|
876
|
+
async showTodos() {
|
|
877
|
+
const _todos = this.todoPanel.getTodos();
|
|
878
|
+
if (_todos.length === 0) {
|
|
879
|
+
note('No todos found. Use a planning task to create todos automatically.', 'š Todo Status');
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const todoDisplay = this.todoPanel.render();
|
|
883
|
+
note(todoDisplay, 'š Current Todo List');
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Show planning phase welcome
|
|
887
|
+
*/
|
|
888
|
+
showPlanningWelcome() {
|
|
889
|
+
if (this.planMode) {
|
|
890
|
+
console.log(chalk.cyan('š” Plan mode features: structured planning, progress tracking, sequential execution'));
|
|
891
|
+
console.log('');
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Show task welcome with planning context
|
|
896
|
+
*/
|
|
897
|
+
showTaskWelcome(task) {
|
|
898
|
+
intro(chalk.cyan('š¤ CodeMie Native Agent'));
|
|
899
|
+
if (this.planMode) {
|
|
900
|
+
console.log(chalk.cyan('ā Task Execution'));
|
|
901
|
+
console.log(` Task: ${chalk.yellow(task)}`);
|
|
902
|
+
console.log(` Mode: ${chalk.cyan('Plan Mode')} - Structured planning enabled`);
|
|
903
|
+
console.log('');
|
|
904
|
+
this.showPlanningWelcome();
|
|
905
|
+
}
|
|
906
|
+
else {
|
|
907
|
+
console.log(chalk.cyan('ā Task Execution'));
|
|
908
|
+
console.log(` Task: ${chalk.yellow(task)}`);
|
|
909
|
+
console.log('');
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Show task completion with todo summary
|
|
914
|
+
*/
|
|
915
|
+
showTaskComplete() {
|
|
916
|
+
const _todos = this.todoPanel.getTodos();
|
|
917
|
+
const progressInfo = this.todoPanel.getProgressInfo();
|
|
918
|
+
if (progressInfo && progressInfo.total > 0) {
|
|
919
|
+
const stats = {
|
|
920
|
+
tasksCompleted: progressInfo.completed,
|
|
921
|
+
totalTime: undefined // Could track this if needed
|
|
922
|
+
};
|
|
923
|
+
this.progressTracker.showOverallCompletion(stats);
|
|
924
|
+
// Show final todo status if relevant
|
|
925
|
+
if (progressInfo.completed === progressInfo.total) {
|
|
926
|
+
note('All planned tasks completed successfully! š', 'ā
Task Complete');
|
|
927
|
+
}
|
|
928
|
+
else if (progressInfo.completed > 0) {
|
|
929
|
+
note(`Completed ${progressInfo.completed}/${progressInfo.total} planned tasks`, 'š Progress Summary');
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
outro(chalk.green('ā
Task completed!'));
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Show error with todo context
|
|
938
|
+
*/
|
|
939
|
+
showError(error) {
|
|
940
|
+
const _todos = this.todoPanel.getTodos();
|
|
941
|
+
const progressInfo = this.todoPanel.getProgressInfo();
|
|
942
|
+
let contextInfo = '';
|
|
943
|
+
if (progressInfo?.currentTodo) {
|
|
944
|
+
contextInfo = `\nš Error occurred while working on: ${progressInfo.currentTodo.content}`;
|
|
945
|
+
}
|
|
946
|
+
note(chalk.rgb(255, 120, 120)(`ā ${error}${contextInfo}`), 'Error');
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Execute task respecting current mode settings
|
|
950
|
+
*/
|
|
951
|
+
async executeTaskWithCurrentMode(task, images = []) {
|
|
952
|
+
if (this.planMode) {
|
|
953
|
+
// Use plan mode execution with confirmation
|
|
954
|
+
try {
|
|
955
|
+
const { PlanMode } = await import('./modes/planMode.js');
|
|
956
|
+
const planMode = new PlanMode(this.agent, {
|
|
957
|
+
requirePlanning: true,
|
|
958
|
+
enforceSequential: true,
|
|
959
|
+
showPlanningFeedback: true
|
|
960
|
+
});
|
|
961
|
+
// First, create the plan only - using UI-integrated planning
|
|
962
|
+
const planningResult = await planMode.planningPhase(task, (event) => {
|
|
963
|
+
// Handle planning-specific events with UI integration
|
|
964
|
+
this.handleStreamingEvent(event);
|
|
965
|
+
// Handle regular streaming events for spinner updates
|
|
966
|
+
switch (event.type) {
|
|
967
|
+
case 'thinking_start':
|
|
968
|
+
// Already handled in handleStreamingEvent
|
|
969
|
+
break;
|
|
970
|
+
case 'tool_call_start':
|
|
971
|
+
// Already handled in handleStreamingEvent
|
|
972
|
+
break;
|
|
973
|
+
case 'tool_call_result':
|
|
974
|
+
// Already handled in handleStreamingEvent
|
|
975
|
+
break;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
if (!planningResult.success) {
|
|
979
|
+
this.showError(`Planning failed: ${planningResult.error}`);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
// Show the plan to the user with proper formatting
|
|
983
|
+
const formattedPlan = this.formatPlanForDisplay(planningResult.todos, planningResult.qualityScore);
|
|
984
|
+
note(formattedPlan, 'Planning Complete');
|
|
985
|
+
// Ask for confirmation
|
|
986
|
+
const shouldExecute = await text({
|
|
987
|
+
message: 'Execute this plan?',
|
|
988
|
+
placeholder: 'Type "yes" to execute, or "no" to cancel',
|
|
989
|
+
validate: (value) => {
|
|
990
|
+
const normalized = value.toLowerCase().trim();
|
|
991
|
+
if (!['yes', 'y', 'no', 'n'].includes(normalized)) {
|
|
992
|
+
return 'Please type "yes" or "no"';
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
if (isCancel(shouldExecute)) {
|
|
997
|
+
note('Plan execution cancelled by user', 'Cancelled');
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const response = shouldExecute.toLowerCase().trim();
|
|
1001
|
+
if (['yes', 'y'].includes(response)) {
|
|
1002
|
+
// Execute the plan
|
|
1003
|
+
await planMode.executePlannedTask(task, (event) => {
|
|
1004
|
+
this.handleStreamingEvent(event);
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
note('Plan execution cancelled by user', 'Cancelled');
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
catch (error) {
|
|
1012
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
// Use direct execution
|
|
1017
|
+
await this.executeTaskWithUI(task, images);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* Handle streaming events from plan mode execution
|
|
1022
|
+
*/
|
|
1023
|
+
handleStreamingEvent(event) {
|
|
1024
|
+
switch (event.type) {
|
|
1025
|
+
case 'planning_start':
|
|
1026
|
+
this.activePlanningPhase = event.planningInfo?.phase || 'planning';
|
|
1027
|
+
// Don't duplicate the planning start message - handled by spinner
|
|
1028
|
+
break;
|
|
1029
|
+
case 'planning_complete':
|
|
1030
|
+
this.activePlanningPhase = null;
|
|
1031
|
+
console.log(chalk.green(`š Plan created with ${event.planningInfo?.totalSteps || 0} steps`));
|
|
1032
|
+
break;
|
|
1033
|
+
case 'planning_progress':
|
|
1034
|
+
this.handlePlanningProgress(event.planningProgress);
|
|
1035
|
+
break;
|
|
1036
|
+
case 'planning_tool_call':
|
|
1037
|
+
this.handlePlanningToolCall(event.planningToolCall);
|
|
1038
|
+
break;
|
|
1039
|
+
case 'content_chunk':
|
|
1040
|
+
if (event.content) {
|
|
1041
|
+
process.stdout.write(event.content);
|
|
1042
|
+
}
|
|
1043
|
+
break;
|
|
1044
|
+
case 'todo_update':
|
|
1045
|
+
// Todo updates are handled automatically by the TodoStateManager
|
|
1046
|
+
// During planning phase, we suppress duplicate visual feedback
|
|
1047
|
+
break;
|
|
1048
|
+
case 'error':
|
|
1049
|
+
this.activePlanningPhase = null;
|
|
1050
|
+
this.showError(event.error || 'Unknown error');
|
|
1051
|
+
break;
|
|
1052
|
+
default:
|
|
1053
|
+
// Handle other event types silently
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Handle planning progress streaming events
|
|
1059
|
+
*/
|
|
1060
|
+
currentPhase = '';
|
|
1061
|
+
currentTool = '';
|
|
1062
|
+
currentToolCall = '';
|
|
1063
|
+
handlePlanningProgress(progressInfo) {
|
|
1064
|
+
if (!progressInfo)
|
|
1065
|
+
return;
|
|
1066
|
+
// Keep the spinner running and use it to display progress updates
|
|
1067
|
+
const phaseNames = {
|
|
1068
|
+
'context_gathering': 'Discovery',
|
|
1069
|
+
'task_analysis': 'Analysis',
|
|
1070
|
+
'plan_generation': 'Planning',
|
|
1071
|
+
'plan_validation': 'Validation'
|
|
1072
|
+
};
|
|
1073
|
+
this.currentPhase = phaseNames[progressInfo.phase] || 'Planning';
|
|
1074
|
+
this.updateGlobalProgress(progressInfo.overallProgress || 0);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Handle planning tool call events
|
|
1078
|
+
*/
|
|
1079
|
+
handlePlanningToolCall(toolInfo) {
|
|
1080
|
+
if (!toolInfo)
|
|
1081
|
+
return;
|
|
1082
|
+
const toolNames = {
|
|
1083
|
+
'list_directory': 'Exploring',
|
|
1084
|
+
'read_file': 'Reading',
|
|
1085
|
+
'execute_command': 'Executing',
|
|
1086
|
+
'llm_analysis': 'Analyzing',
|
|
1087
|
+
'llm_plan_generation': 'Generating Plan',
|
|
1088
|
+
'plan_validation': 'Validating',
|
|
1089
|
+
'analyze_dependencies': 'Dependencies'
|
|
1090
|
+
};
|
|
1091
|
+
this.currentTool = toolNames[toolInfo.toolName] || 'Tool';
|
|
1092
|
+
// Format tool call details with arguments
|
|
1093
|
+
this.currentToolCall = this.formatToolCall(toolInfo.toolName, toolInfo.args);
|
|
1094
|
+
// Update progress with current tool info
|
|
1095
|
+
this.updateGlobalProgress();
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Format tool call with arguments for display
|
|
1099
|
+
*/
|
|
1100
|
+
formatToolCall(toolName, args) {
|
|
1101
|
+
if (!args || Object.keys(args).length === 0) {
|
|
1102
|
+
return toolName;
|
|
1103
|
+
}
|
|
1104
|
+
// Extract key arguments for concise display
|
|
1105
|
+
const formatArg = (key, value) => {
|
|
1106
|
+
if (typeof value === 'string') {
|
|
1107
|
+
// Truncate long strings and show just the relevant part
|
|
1108
|
+
if (key === 'path' || key === 'directory') {
|
|
1109
|
+
// Show just the last part of paths
|
|
1110
|
+
const parts = value.split('/');
|
|
1111
|
+
return parts[parts.length - 1] || value;
|
|
1112
|
+
}
|
|
1113
|
+
if (key === 'command') {
|
|
1114
|
+
// Show first word of commands
|
|
1115
|
+
return value.split(' ')[0];
|
|
1116
|
+
}
|
|
1117
|
+
if (value.length > 20) {
|
|
1118
|
+
return value.substring(0, 17) + '...';
|
|
1119
|
+
}
|
|
1120
|
+
return value;
|
|
1121
|
+
}
|
|
1122
|
+
if (Array.isArray(value)) {
|
|
1123
|
+
return `[${value.length} items]`;
|
|
1124
|
+
}
|
|
1125
|
+
if (typeof value === 'object') {
|
|
1126
|
+
return '{...}';
|
|
1127
|
+
}
|
|
1128
|
+
return String(value);
|
|
1129
|
+
};
|
|
1130
|
+
// Pick the most relevant arguments to show
|
|
1131
|
+
const relevantKeys = ['path', 'directory', 'file', 'command', 'query', 'pattern'];
|
|
1132
|
+
const argsToShow = [];
|
|
1133
|
+
for (const key of relevantKeys) {
|
|
1134
|
+
if (key in args) {
|
|
1135
|
+
argsToShow.push(formatArg(key, args[key]));
|
|
1136
|
+
if (argsToShow.length >= 2)
|
|
1137
|
+
break; // Show max 2 args
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
// If no relevant keys found, show first few keys
|
|
1141
|
+
if (argsToShow.length === 0) {
|
|
1142
|
+
const keys = Object.keys(args).slice(0, 2);
|
|
1143
|
+
for (const key of keys) {
|
|
1144
|
+
argsToShow.push(formatArg(key, args[key]));
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return argsToShow.length > 0 ? `${toolName} (${argsToShow.join(', ')})` : toolName;
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Update global progress display
|
|
1151
|
+
*/
|
|
1152
|
+
currentProgress = 0;
|
|
1153
|
+
updateGlobalProgress(progress) {
|
|
1154
|
+
if (progress !== undefined) {
|
|
1155
|
+
this.currentProgress = progress;
|
|
1156
|
+
}
|
|
1157
|
+
const progressBar = this.createProgressBar(this.currentProgress);
|
|
1158
|
+
let displayText = `Progress ${Math.round(this.currentProgress)}% ${progressBar}`;
|
|
1159
|
+
// Show tool call details if available, otherwise fall back to tool name or phase
|
|
1160
|
+
// This provides detailed info like "(Discovery, calling list_dir (src))"
|
|
1161
|
+
if (this.currentToolCall) {
|
|
1162
|
+
displayText += ` (${this.currentPhase}, calling ${this.currentToolCall})`;
|
|
1163
|
+
}
|
|
1164
|
+
else if (this.currentTool) {
|
|
1165
|
+
displayText += ` (${this.currentTool})`;
|
|
1166
|
+
}
|
|
1167
|
+
else if (this.currentPhase) {
|
|
1168
|
+
displayText += ` (${this.currentPhase})`;
|
|
1169
|
+
}
|
|
1170
|
+
// Use clack-style spinner update for reliable real-time display
|
|
1171
|
+
if (this.currentSpinner) {
|
|
1172
|
+
this.currentSpinner.message(displayText);
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
// Fallback to direct output if no spinner
|
|
1176
|
+
process.stdout.write('\r' + displayText);
|
|
1177
|
+
}
|
|
1178
|
+
// Only add newline when planning is completely finished (100%)
|
|
1179
|
+
if (this.currentProgress >= 100) {
|
|
1180
|
+
if (this.currentSpinner) {
|
|
1181
|
+
this.currentSpinner.stop(displayText);
|
|
1182
|
+
this.currentSpinner = undefined;
|
|
1183
|
+
}
|
|
1184
|
+
else {
|
|
1185
|
+
process.stdout.write('\n');
|
|
1186
|
+
}
|
|
1187
|
+
this.currentPhase = '';
|
|
1188
|
+
this.currentTool = '';
|
|
1189
|
+
this.currentToolCall = '';
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Create a simple progress bar
|
|
1194
|
+
*/
|
|
1195
|
+
createProgressBar(percentage, width = 20) {
|
|
1196
|
+
const filled = Math.round((percentage / 100) * width);
|
|
1197
|
+
const empty = width - filled;
|
|
1198
|
+
const filledBar = chalk.cyan('ā'.repeat(filled));
|
|
1199
|
+
const emptyBar = chalk.dim('ā'.repeat(empty));
|
|
1200
|
+
return `[${filledBar}${emptyBar}]`;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Create a tool progress bar (smaller for inline use)
|
|
1204
|
+
*/
|
|
1205
|
+
createToolProgressBar(percentage, width = 12) {
|
|
1206
|
+
const filled = Math.round((percentage / 100) * width);
|
|
1207
|
+
const empty = width - filled;
|
|
1208
|
+
const filledBar = chalk.green('ā'.repeat(filled));
|
|
1209
|
+
const emptyBar = chalk.dim('ā'.repeat(empty));
|
|
1210
|
+
return `[${filledBar}${emptyBar}]`;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Handle hotkey actions
|
|
1214
|
+
*/
|
|
1215
|
+
handleHotkey(action) {
|
|
1216
|
+
switch (action) {
|
|
1217
|
+
case 'toggle-plan-mode':
|
|
1218
|
+
this.togglePlanMode();
|
|
1219
|
+
break;
|
|
1220
|
+
case 'show-todos':
|
|
1221
|
+
this.showTodosHotkey();
|
|
1222
|
+
break;
|
|
1223
|
+
case 'show-help':
|
|
1224
|
+
this.showHotkeyHelp();
|
|
1225
|
+
break;
|
|
1226
|
+
case 'show-status':
|
|
1227
|
+
this.showModeStatus();
|
|
1228
|
+
break;
|
|
1229
|
+
default:
|
|
1230
|
+
console.log(chalk.red(`\nā Unknown hotkey action: ${action}`));
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Toggle plan mode on/off
|
|
1236
|
+
*/
|
|
1237
|
+
togglePlanMode() {
|
|
1238
|
+
const wasEnabled = this.planMode;
|
|
1239
|
+
if (this.planMode) {
|
|
1240
|
+
this.disablePlanMode();
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
this.enablePlanMode();
|
|
1244
|
+
}
|
|
1245
|
+
// Show enhanced visual feedback
|
|
1246
|
+
this.showModeChangeNotification(wasEnabled, this.planMode);
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Show enhanced visual feedback for mode changes
|
|
1250
|
+
*/
|
|
1251
|
+
showModeChangeNotification(wasEnabled, nowEnabled) {
|
|
1252
|
+
const modeIcon = nowEnabled ? 'š' : 'ā”';
|
|
1253
|
+
const statusText = nowEnabled ? 'enabled' : 'disabled';
|
|
1254
|
+
// Simple, concise mode change message
|
|
1255
|
+
console.log(chalk.blueBright(`\n${modeIcon} Plan mode ${statusText}`));
|
|
1256
|
+
// Start progress tracking when plan mode is enabled (but don't show 0/0 progress yet)
|
|
1257
|
+
if (nowEnabled) {
|
|
1258
|
+
this.progressTracker.start();
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Show todos via hotkey (non-blocking)
|
|
1263
|
+
*/
|
|
1264
|
+
showTodosHotkey() {
|
|
1265
|
+
const _todos = this.todoPanel.getTodos();
|
|
1266
|
+
if (_todos.length === 0) {
|
|
1267
|
+
console.log(chalk.dim('\nš No todos found'));
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
const todoDisplay = this.todoPanel.render();
|
|
1271
|
+
console.log(`\n${todoDisplay}`);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Show hotkey help
|
|
1276
|
+
*/
|
|
1277
|
+
showHotkeyHelp() {
|
|
1278
|
+
const helpText = `
|
|
1279
|
+
${chalk.bold.cyan('š„ Interactive Mode Hotkeys')}
|
|
1280
|
+
|
|
1281
|
+
Mode Control:
|
|
1282
|
+
${chalk.yellow('Ctrl+P')} Toggle plan mode on/off
|
|
1283
|
+
${chalk.yellow('Ctrl+S')} Show current mode status
|
|
1284
|
+
|
|
1285
|
+
Todo Management:
|
|
1286
|
+
${chalk.yellow('Ctrl+T')} Show current todo list and progress
|
|
1287
|
+
|
|
1288
|
+
General:
|
|
1289
|
+
${chalk.yellow('Ctrl+H')} Show this help
|
|
1290
|
+
${chalk.yellow('Tab')} Insert image from clipboard
|
|
1291
|
+
${chalk.yellow('Ctrl+C')} Cancel input / Exit
|
|
1292
|
+
|
|
1293
|
+
Input Controls:
|
|
1294
|
+
${chalk.yellow('Enter')} Send message
|
|
1295
|
+
${chalk.yellow('Shift+Enter')} New line (multiline input)
|
|
1296
|
+
|
|
1297
|
+
Chat Commands:
|
|
1298
|
+
${chalk.yellow('/help')} Show chat commands
|
|
1299
|
+
${chalk.yellow('/todos')} Show detailed todo information
|
|
1300
|
+
${chalk.yellow('/stats')} Show agent statistics
|
|
1301
|
+
${chalk.yellow('/exit')} Exit the session
|
|
1302
|
+
`;
|
|
1303
|
+
console.log(helpText);
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Show current mode status
|
|
1307
|
+
*/
|
|
1308
|
+
showModeStatus() {
|
|
1309
|
+
const _todos = this.todoPanel.getTodos();
|
|
1310
|
+
const progressInfo = this.todoPanel.getProgressInfo();
|
|
1311
|
+
let statusText = chalk.bold.blueBright('\nš Current Status\n');
|
|
1312
|
+
// Mode information
|
|
1313
|
+
statusText += `Mode: ${this.planMode ?
|
|
1314
|
+
chalk.green('Plan Mode (structured todos)') :
|
|
1315
|
+
chalk.yellow('Direct Mode (immediate execution)')}\n`;
|
|
1316
|
+
// Todo information
|
|
1317
|
+
if (progressInfo && progressInfo.total > 0) {
|
|
1318
|
+
statusText += `Todos: ${progressInfo.completed}/${progressInfo.total} completed (${progressInfo.percentage}%)\n`;
|
|
1319
|
+
if (progressInfo.currentTodo) {
|
|
1320
|
+
statusText += `Current: ${chalk.cyan(progressInfo.currentTodo.content)}\n`;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
else {
|
|
1324
|
+
statusText += 'Todos: None active\n';
|
|
1325
|
+
}
|
|
1326
|
+
// Agent status
|
|
1327
|
+
const agentStats = this.agent.getStats();
|
|
1328
|
+
if (agentStats) {
|
|
1329
|
+
statusText += `Session: ${agentStats.toolCalls} tool calls, ${agentStats.llmCalls} LLM calls\n`;
|
|
1330
|
+
}
|
|
1331
|
+
console.log(statusText);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Format plan for display with proper text wrapping
|
|
1335
|
+
*/
|
|
1336
|
+
formatPlanForDisplay(todos, qualityScore) {
|
|
1337
|
+
const maxWidth = Math.min(process.stdout.columns - 10, 100); // Leave some margin
|
|
1338
|
+
let formatted = `š **Plan Created** (${todos.length} steps):\n\n`;
|
|
1339
|
+
todos.forEach((todo, index) => {
|
|
1340
|
+
const stepNumber = `${index + 1}. `;
|
|
1341
|
+
const content = todo.content;
|
|
1342
|
+
// Wrap long lines properly
|
|
1343
|
+
const wrappedContent = this.wrapText(content, maxWidth - stepNumber.length);
|
|
1344
|
+
const lines = wrappedContent.split('\n');
|
|
1345
|
+
// First line with step number
|
|
1346
|
+
formatted += stepNumber + lines[0] + '\n';
|
|
1347
|
+
// Subsequent lines indented to align with content
|
|
1348
|
+
for (let i = 1; i < lines.length; i++) {
|
|
1349
|
+
formatted += ' '.repeat(stepNumber.length) + lines[i] + '\n';
|
|
1350
|
+
}
|
|
1351
|
+
// Add spacing between steps
|
|
1352
|
+
if (index < todos.length - 1) {
|
|
1353
|
+
formatted += '\n';
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
formatted += `\n\nQuality Score: ${qualityScore}/100`;
|
|
1357
|
+
return formatted;
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Wrap text to specified width
|
|
1361
|
+
*/
|
|
1362
|
+
wrapText(text, maxWidth) {
|
|
1363
|
+
if (text.length <= maxWidth) {
|
|
1364
|
+
return text;
|
|
1365
|
+
}
|
|
1366
|
+
const words = text.split(' ');
|
|
1367
|
+
const lines = [];
|
|
1368
|
+
let currentLine = '';
|
|
1369
|
+
for (const word of words) {
|
|
1370
|
+
// If adding this word would exceed the width
|
|
1371
|
+
if (currentLine.length + word.length + 1 > maxWidth) {
|
|
1372
|
+
if (currentLine) {
|
|
1373
|
+
lines.push(currentLine.trim());
|
|
1374
|
+
currentLine = word;
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
// Single word is longer than maxWidth, force break
|
|
1378
|
+
lines.push(word);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
else {
|
|
1382
|
+
if (currentLine) {
|
|
1383
|
+
currentLine += ' ' + word;
|
|
1384
|
+
}
|
|
1385
|
+
else {
|
|
1386
|
+
currentLine = word;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (currentLine) {
|
|
1391
|
+
lines.push(currentLine.trim());
|
|
1392
|
+
}
|
|
1393
|
+
return lines.join('\n');
|
|
1394
|
+
}
|
|
614
1395
|
/**
|
|
615
1396
|
* Cleanup resources
|
|
616
1397
|
*/
|
|
@@ -619,6 +1400,9 @@ export class CodeMieTerminalUI {
|
|
|
619
1400
|
this.currentSpinner.stop();
|
|
620
1401
|
this.currentSpinner = undefined;
|
|
621
1402
|
}
|
|
1403
|
+
// Clean up todo tracking
|
|
1404
|
+
this.progressTracker.stop();
|
|
1405
|
+
TodoStateManager.removeEventCallback(this.handleTodoUpdate.bind(this));
|
|
622
1406
|
}
|
|
623
1407
|
}
|
|
624
1408
|
//# sourceMappingURL=ui.js.map
|