@compilr-dev/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/agent.d.ts +74 -1
- package/dist/agent.js +259 -76
- package/dist/anchors/index.d.ts +9 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/project-anchors.d.ts +79 -0
- package/dist/anchors/project-anchors.js +202 -0
- package/dist/commands/handler-types.d.ts +68 -0
- package/dist/commands/handler-types.js +8 -0
- package/dist/commands/handlers/agent-commands.d.ts +13 -0
- package/dist/commands/handlers/agent-commands.js +305 -0
- package/dist/commands/handlers/design-commands.d.ts +15 -0
- package/dist/commands/handlers/design-commands.js +334 -0
- package/dist/commands/handlers/index.d.ts +20 -0
- package/dist/commands/handlers/index.js +43 -0
- package/dist/commands/handlers/overlay-commands.d.ts +21 -0
- package/dist/commands/handlers/overlay-commands.js +287 -0
- package/dist/commands/handlers/project-commands.d.ts +11 -0
- package/dist/commands/handlers/project-commands.js +167 -0
- package/dist/commands/handlers/simple-commands.d.ts +19 -0
- package/dist/commands/handlers/simple-commands.js +144 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/registry.d.ts +50 -0
- package/dist/commands/registry.js +75 -0
- package/dist/commands-v2/handlers/context.d.ts +13 -0
- package/dist/commands-v2/handlers/context.js +348 -0
- package/dist/commands-v2/handlers/core.d.ts +13 -0
- package/dist/commands-v2/handlers/core.js +165 -0
- package/dist/commands-v2/handlers/debug.d.ts +11 -0
- package/dist/commands-v2/handlers/debug.js +159 -0
- package/dist/commands-v2/handlers/index.d.ts +12 -0
- package/dist/commands-v2/handlers/index.js +24 -0
- package/dist/commands-v2/handlers/project.d.ts +22 -0
- package/dist/commands-v2/handlers/project.js +814 -0
- package/dist/commands-v2/handlers/settings.d.ts +15 -0
- package/dist/commands-v2/handlers/settings.js +235 -0
- package/dist/commands-v2/index.d.ts +13 -0
- package/dist/commands-v2/index.js +15 -0
- package/dist/commands-v2/registry.d.ts +37 -0
- package/dist/commands-v2/registry.js +80 -0
- package/dist/commands-v2/types.d.ts +75 -0
- package/dist/commands-v2/types.js +7 -0
- package/dist/commands.js +110 -7
- package/dist/index.js +288 -29
- package/dist/input-handlers/index.d.ts +7 -0
- package/dist/input-handlers/index.js +7 -0
- package/dist/input-handlers/memory-handler.d.ts +26 -0
- package/dist/input-handlers/memory-handler.js +68 -0
- package/dist/repl-helpers.d.ts +63 -0
- package/dist/repl-helpers.js +318 -0
- package/dist/repl-v2.d.ts +155 -0
- package/dist/repl-v2.js +774 -0
- package/dist/repl.d.ts +32 -4
- package/dist/repl.js +250 -977
- package/dist/settings/index.d.ts +23 -0
- package/dist/settings/index.js +48 -0
- package/dist/settings/paths.d.ts +110 -0
- package/dist/settings/paths.js +264 -0
- package/dist/templates/compilr-md.js +7 -4
- package/dist/templates/index.js +3 -4
- package/dist/themes/colors.js +3 -1
- package/dist/themes/registry.d.ts +5 -36
- package/dist/themes/registry.js +11 -95
- package/dist/themes/types.d.ts +3 -38
- package/dist/themes/types.js +2 -2
- package/dist/tools/anchor-tools.d.ts +31 -0
- package/dist/tools/anchor-tools.js +255 -0
- package/dist/tools/backlog-wrappers.d.ts +54 -0
- package/dist/tools/backlog-wrappers.js +338 -0
- package/dist/tools/backlog.js +1 -1
- package/dist/tools/db-tools.d.ts +65 -0
- package/dist/tools/db-tools.js +19 -0
- package/dist/tools/document-db.d.ts +43 -0
- package/dist/tools/document-db.js +220 -0
- package/dist/tools/project-db.d.ts +102 -0
- package/dist/tools/project-db.js +370 -0
- package/dist/tools/workitem-db.d.ts +103 -0
- package/dist/tools/workitem-db.js +549 -0
- package/dist/tools.js +13 -3
- package/dist/ui/agents-overlay-v2.d.ts +43 -0
- package/dist/ui/agents-overlay-v2.js +809 -0
- package/dist/ui/agents-overlay.d.ts +5 -5
- package/dist/ui/agents-overlay.js +782 -420
- package/dist/ui/anchors-overlay.d.ts +12 -0
- package/dist/ui/anchors-overlay.js +775 -0
- package/dist/ui/arch-type-overlay.d.ts +1 -6
- package/dist/ui/arch-type-overlay.js +175 -203
- package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
- package/dist/ui/ask-user-overlay-v2.js +555 -0
- package/dist/ui/ask-user-overlay.d.ts +2 -2
- package/dist/ui/ask-user-overlay.js +443 -535
- package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
- package/dist/ui/ask-user-simple-overlay.js +182 -209
- package/dist/ui/backlog-overlay.d.ts +16 -1
- package/dist/ui/backlog-overlay.js +525 -659
- package/dist/ui/base/index.d.ts +26 -0
- package/dist/ui/base/index.js +33 -0
- package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
- package/dist/ui/base/inline-overlay-utils.js +320 -0
- package/dist/ui/base/inline-overlay.d.ts +159 -0
- package/dist/ui/base/inline-overlay.js +257 -0
- package/dist/ui/base/key-utils.d.ts +15 -0
- package/dist/ui/base/key-utils.js +30 -0
- package/dist/ui/base/overlay-base-v2.d.ts +193 -0
- package/dist/ui/base/overlay-base-v2.js +246 -0
- package/dist/ui/base/overlay-base.d.ts +156 -0
- package/dist/ui/base/overlay-base.js +238 -0
- package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
- package/dist/ui/base/overlay-lifecycle.js +159 -0
- package/dist/ui/base/overlay-types.d.ts +185 -0
- package/dist/ui/base/overlay-types.js +7 -0
- package/dist/ui/base/render-utils.d.ts +8 -0
- package/dist/ui/base/render-utils.js +11 -0
- package/dist/ui/base/screen-stack.d.ts +148 -0
- package/dist/ui/base/screen-stack.js +184 -0
- package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
- package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
- package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
- package/dist/ui/base/tabbed-list-overlay.js +369 -0
- package/dist/ui/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/commands-overlay-v2.js +441 -0
- package/dist/ui/commands-overlay.d.ts +7 -2
- package/dist/ui/commands-overlay.js +384 -355
- package/dist/ui/config-overlay.d.ts +5 -4
- package/dist/ui/config-overlay.js +243 -513
- package/dist/ui/conversation.d.ts +75 -4
- package/dist/ui/conversation.js +374 -161
- package/dist/ui/docs-overlay.d.ts +17 -0
- package/dist/ui/docs-overlay.js +303 -0
- package/dist/ui/ephemeral.d.ts +1 -1
- package/dist/ui/ephemeral.js +1 -1
- package/dist/ui/features/index.d.ts +34 -0
- package/dist/ui/features/index.js +34 -0
- package/dist/ui/features/input-feature.d.ts +85 -0
- package/dist/ui/features/input-feature.js +238 -0
- package/dist/ui/features/list-feature.d.ts +155 -0
- package/dist/ui/features/list-feature.js +244 -0
- package/dist/ui/features/pagination-feature.d.ts +154 -0
- package/dist/ui/features/pagination-feature.js +238 -0
- package/dist/ui/features/search-feature.d.ts +148 -0
- package/dist/ui/features/search-feature.js +185 -0
- package/dist/ui/features/tab-feature.d.ts +194 -0
- package/dist/ui/features/tab-feature.js +307 -0
- package/dist/ui/footer-v2.d.ts +222 -0
- package/dist/ui/footer-v2.js +1349 -0
- package/dist/ui/footer.d.ts +107 -0
- package/dist/ui/footer.js +359 -67
- package/dist/ui/guardrail-overlay.d.ts +29 -0
- package/dist/ui/guardrail-overlay.js +145 -0
- package/dist/ui/help-overlay-v2.d.ts +34 -0
- package/dist/ui/help-overlay-v2.js +309 -0
- package/dist/ui/help-overlay.d.ts +16 -0
- package/dist/ui/help-overlay.js +316 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -3
- package/dist/ui/init-overlay-v2.d.ts +34 -0
- package/dist/ui/init-overlay-v2.js +600 -0
- package/dist/ui/init-overlay.d.ts +12 -2
- package/dist/ui/init-overlay.js +349 -270
- package/dist/ui/input-prompt-v2.d.ts +1 -0
- package/dist/ui/input-prompt-v2.js +14 -6
- package/dist/ui/input-prompt.d.ts +116 -33
- package/dist/ui/input-prompt.js +536 -337
- package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay-v2.js +114 -0
- package/dist/ui/iteration-limit-overlay.d.ts +2 -2
- package/dist/ui/iteration-limit-overlay.js +92 -128
- package/dist/ui/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/keys-overlay-v2.js +248 -0
- package/dist/ui/keys-overlay.d.ts +1 -0
- package/dist/ui/keys-overlay.js +203 -141
- package/dist/ui/line-utils.d.ts +88 -0
- package/dist/ui/line-utils.js +150 -0
- package/dist/ui/live-region.d.ts +161 -0
- package/dist/ui/live-region.js +387 -0
- package/dist/ui/mascot/expressions.d.ts +32 -0
- package/dist/ui/mascot/expressions.js +213 -0
- package/dist/ui/mascot/index.d.ts +8 -0
- package/dist/ui/mascot/index.js +8 -0
- package/dist/ui/mascot/renderer.d.ts +19 -0
- package/dist/ui/mascot/renderer.js +97 -0
- package/dist/ui/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/mascot-overlay-v2.js +138 -0
- package/dist/ui/mascot-overlay.d.ts +21 -0
- package/dist/ui/mascot-overlay.js +146 -0
- package/dist/ui/model-overlay-v2.d.ts +49 -0
- package/dist/ui/model-overlay-v2.js +118 -0
- package/dist/ui/model-overlay.d.ts +27 -0
- package/dist/ui/model-overlay.js +221 -0
- package/dist/ui/model-warning-overlay.js +3 -5
- package/dist/ui/new-overlay.d.ts +34 -0
- package/dist/ui/new-overlay.js +604 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
- package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
- package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
- package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
- package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
- package/dist/ui/overlay/index.d.ts +33 -0
- package/dist/ui/overlay/index.js +35 -0
- package/dist/ui/overlay/key-utils.d.ts +6 -0
- package/dist/ui/overlay/key-utils.js +6 -0
- package/dist/ui/overlay/overlay-types.d.ts +128 -0
- package/dist/ui/overlay/overlay-types.js +22 -0
- package/dist/ui/overlay/types.d.ts +135 -0
- package/dist/ui/overlay/types.js +22 -0
- package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
- package/dist/ui/overlays/help-overlay-v2.js +198 -0
- package/dist/ui/overlays/index.d.ts +11 -0
- package/dist/ui/overlays/index.js +11 -0
- package/dist/ui/overlays.d.ts +0 -4
- package/dist/ui/overlays.js +0 -444
- package/dist/ui/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/permission-overlay-v2.js +380 -0
- package/dist/ui/permission-overlay.d.ts +1 -1
- package/dist/ui/permission-overlay.js +186 -298
- package/dist/ui/projects-overlay.d.ts +19 -0
- package/dist/ui/projects-overlay.js +484 -0
- package/dist/ui/providers/types.d.ts +178 -0
- package/dist/ui/providers/types.js +9 -0
- package/dist/ui/render-modes.d.ts +36 -0
- package/dist/ui/render-modes.js +44 -0
- package/dist/ui/startup-menu.d.ts +36 -0
- package/dist/ui/startup-menu.js +236 -0
- package/dist/ui/subagent-renderer.d.ts +117 -0
- package/dist/ui/subagent-renderer.js +334 -0
- package/dist/ui/terminal-codes.d.ts +94 -0
- package/dist/ui/terminal-codes.js +124 -0
- package/dist/ui/terminal-renderer.d.ts +221 -0
- package/dist/ui/terminal-renderer.js +751 -0
- package/dist/ui/terminal-ui.d.ts +463 -0
- package/dist/ui/terminal-ui.js +2296 -0
- package/dist/ui/terminal.d.ts +20 -0
- package/dist/ui/terminal.js +72 -0
- package/dist/ui/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/theme-overlay-v2.js +135 -0
- package/dist/ui/theme-overlay.d.ts +24 -0
- package/dist/ui/theme-overlay.js +127 -0
- package/dist/ui/todo-zone.js +53 -25
- package/dist/ui/tool-formatters.d.ts +16 -0
- package/dist/ui/tool-formatters.js +516 -0
- package/dist/ui/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/tools-overlay-v2.js +218 -0
- package/dist/ui/tools-overlay.d.ts +10 -2
- package/dist/ui/tools-overlay.js +172 -220
- package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/tutorial-overlay.d.ts +1 -0
- package/dist/ui/tutorial-overlay.js +400 -302
- package/dist/ui/workflow-overlay.d.ts +22 -0
- package/dist/ui/workflow-overlay.js +636 -0
- package/dist/utils/debug-log.d.ts +28 -0
- package/dist/utils/debug-log.js +57 -0
- package/dist/utils/model-tiers.js +1 -1
- package/dist/utils/path-safety.d.ts +56 -0
- package/dist/utils/path-safety.js +239 -0
- package/dist/workflow/guided-mode-injector.d.ts +42 -0
- package/dist/workflow/guided-mode-injector.js +191 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/step-criteria.d.ts +62 -0
- package/dist/workflow/step-criteria.js +150 -0
- package/dist/workflow/step-tracker.d.ts +92 -0
- package/dist/workflow/step-tracker.js +141 -0
- package/package.json +12 -5
package/dist/repl.js
CHANGED
|
@@ -10,320 +10,34 @@
|
|
|
10
10
|
* - Input queueing during agent execution
|
|
11
11
|
* - Esc to cancel running agent
|
|
12
12
|
*/
|
|
13
|
-
import { builtinSkills } from '@compilr-dev/agents';
|
|
14
13
|
import { exec } from 'child_process';
|
|
15
14
|
import { getToolPermissionInfo } from './agent.js';
|
|
16
15
|
import * as fs from 'fs';
|
|
17
16
|
import * as path from 'path';
|
|
18
17
|
import pc from 'picocolors';
|
|
19
18
|
import { debug, debugError } from './debug.js';
|
|
19
|
+
import { debugLog } from './utils/debug-log.js';
|
|
20
20
|
import { getNextMode, MODE_INFO } from './ui/types.js';
|
|
21
21
|
import { createInitialState, updateTodos, setAgentRunning } from './state.js';
|
|
22
22
|
import { Footer } from './ui/footer.js';
|
|
23
23
|
import * as conversation from './ui/conversation.js';
|
|
24
24
|
import * as overlays from './ui/overlays.js';
|
|
25
25
|
import * as terminal from './ui/terminal.js';
|
|
26
|
+
import { TerminalRenderer, RenderMode } from './ui/terminal-renderer.js';
|
|
26
27
|
import { resolveCommand } from './commands.js';
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
import { showConfigOverlay } from './ui/config-overlay.js';
|
|
30
|
-
import { showInitOverlay } from './ui/init-overlay.js';
|
|
31
|
-
import { showBacklogOverlay } from './ui/backlog-overlay.js';
|
|
32
|
-
import { showKeysOverlay } from './ui/keys-overlay.js';
|
|
33
|
-
import { showTutorialOverlay } from './ui/tutorial-overlay.js';
|
|
28
|
+
// Note: All overlay imports (showConfigOverlay, showModelOverlay, showThemeOverlay, showMascotOverlay, etc.)
|
|
29
|
+
// are now handled by command handlers in ./commands/handlers/
|
|
34
30
|
import { showToolsOverlay } from './ui/tools-overlay.js';
|
|
35
|
-
import { showModelWarningOverlay } from './ui/model-warning-overlay.js';
|
|
36
|
-
import { showArchTypeOverlay } from './ui/arch-type-overlay.js';
|
|
37
31
|
import { registerFooterCallbacks } from './ui/overlay-controller.js';
|
|
38
|
-
import { getCustomCommandRegistry } from './commands/index.js';
|
|
32
|
+
import { getCustomCommandRegistry, commandRegistry, registerAllHandlers } from './commands/index.js';
|
|
39
33
|
import { getPermissionMode } from './settings/index.js';
|
|
40
|
-
|
|
41
|
-
import { findBacklogPath, parseBacklogItems } from './tools/backlog.js';
|
|
34
|
+
// Note: findBacklogPath, parseBacklogItems moved to command handlers
|
|
42
35
|
import { isOverlayActive } from './shared-handlers.js';
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
const TOOL_KEYWORDS = {
|
|
49
|
-
read_file: ['read', 'show', 'display', 'look at', 'view', 'what is in', 'content of'],
|
|
50
|
-
write_file: ['write', 'create', 'save', 'make a file'],
|
|
51
|
-
edit: ['edit', 'modify', 'change', 'update', 'fix', 'replace'],
|
|
52
|
-
bash: ['run', 'execute', 'command', 'shell', 'npm', 'git', 'pip', 'ls', 'pwd', 'mkdir', 'build', 'install'],
|
|
53
|
-
grep: ['search', 'find', 'grep', 'look for', 'where is', 'occurrences'],
|
|
54
|
-
glob: ['list files', 'find files', 'what files', 'show files', 'directory', 'pattern'],
|
|
55
|
-
todo_write: ['todo', 'task', 'plan', 'steps'],
|
|
56
|
-
todo_read: ['todo', 'task', 'plan', 'steps'],
|
|
57
|
-
backlog_read: ['backlog', 'requirements', 'features', 'bugs', 'items'],
|
|
58
|
-
backlog_write: ['backlog', 'requirements', 'features', 'bugs', 'add item', 'add feature'],
|
|
59
|
-
git_status: ['git status', 'changes', 'modified', 'staged'],
|
|
60
|
-
git_diff: ['git diff', 'differences', 'what changed'],
|
|
61
|
-
git_log: ['git log', 'history', 'commits'],
|
|
62
|
-
git_commit: ['commit', 'save changes'],
|
|
63
|
-
git_branch: ['branch', 'branches'],
|
|
64
|
-
detect_project: ['project type', 'what kind of project', 'framework'],
|
|
65
|
-
find_project_root: ['project root', 'root directory'],
|
|
66
|
-
run_tests: ['test', 'tests', 'run tests', 'check tests'],
|
|
67
|
-
run_lint: ['lint', 'linting', 'check code', 'style'],
|
|
68
|
-
};
|
|
69
|
-
/**
|
|
70
|
-
* Select relevant tool names based on user input intent
|
|
71
|
-
*/
|
|
72
|
-
function selectToolNamesByIntent(input, allToolNames) {
|
|
73
|
-
const lower = input.toLowerCase();
|
|
74
|
-
// If input is very short or a question, use all tools
|
|
75
|
-
if (input.length < 10 || input.includes('?')) {
|
|
76
|
-
return allToolNames;
|
|
77
|
-
}
|
|
78
|
-
const selected = new Set();
|
|
79
|
-
// Add tools based on keyword matches
|
|
80
|
-
for (const [tool, keywords] of Object.entries(TOOL_KEYWORDS)) {
|
|
81
|
-
if (keywords.some((kw) => lower.includes(kw)) && allToolNames.includes(tool)) {
|
|
82
|
-
selected.add(tool);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
// Always include todo tools (they're used implicitly)
|
|
86
|
-
if (allToolNames.includes('todo_write'))
|
|
87
|
-
selected.add('todo_write');
|
|
88
|
-
if (allToolNames.includes('todo_read'))
|
|
89
|
-
selected.add('todo_read');
|
|
90
|
-
// If nothing matched, use all tools
|
|
91
|
-
if (selected.size === 0) {
|
|
92
|
-
return allToolNames;
|
|
93
|
-
}
|
|
94
|
-
return Array.from(selected);
|
|
95
|
-
}
|
|
96
|
-
// =============================================================================
|
|
97
|
-
// Build Command Types and Constants
|
|
98
|
-
// =============================================================================
|
|
99
|
-
const PRIORITY_ORDER = ['critical', 'high', 'medium', 'low'];
|
|
100
|
-
// =============================================================================
|
|
101
|
-
// Project Detection Helpers
|
|
102
|
-
// =============================================================================
|
|
103
|
-
/**
|
|
104
|
-
* Detect if we're in a compilr project (has .compilr folder or -docs sibling)
|
|
105
|
-
* Returns project path from config.json if available
|
|
106
|
-
*/
|
|
107
|
-
function detectCompilrProject() {
|
|
108
|
-
const cwd = process.cwd();
|
|
109
|
-
// First try to load config and get paths from there
|
|
110
|
-
const config = loadCompilrConfig(cwd);
|
|
111
|
-
if (config) {
|
|
112
|
-
return {
|
|
113
|
-
found: true,
|
|
114
|
-
backlogPath: findBacklogPath(cwd),
|
|
115
|
-
projectPath: config.paths.project || null,
|
|
116
|
-
docsPath: config.paths.docs,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
// Check for .compilr folder in current directory
|
|
120
|
-
const compilrDir = path.join(cwd, '.compilr');
|
|
121
|
-
if (fs.existsSync(compilrDir)) {
|
|
122
|
-
const backlogPath = path.join(compilrDir, 'backlog.md');
|
|
123
|
-
return {
|
|
124
|
-
found: true,
|
|
125
|
-
backlogPath: fs.existsSync(backlogPath) ? backlogPath : null,
|
|
126
|
-
projectPath: cwd,
|
|
127
|
-
docsPath: null,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
// Check for -docs sibling folder (two-repo pattern)
|
|
131
|
-
const parentDir = path.dirname(cwd);
|
|
132
|
-
const projectName = path.basename(cwd);
|
|
133
|
-
const docsDir = path.join(parentDir, `${projectName}-docs`);
|
|
134
|
-
const docsBacklogPath = path.join(docsDir, '01-planning', 'backlog.md');
|
|
135
|
-
if (fs.existsSync(docsBacklogPath)) {
|
|
136
|
-
return {
|
|
137
|
-
found: true,
|
|
138
|
-
backlogPath: docsBacklogPath,
|
|
139
|
-
projectPath: cwd,
|
|
140
|
-
docsPath: docsDir,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
// Check if we're in the docs repo itself
|
|
144
|
-
const inDocsBacklog = path.join(cwd, '01-planning', 'backlog.md');
|
|
145
|
-
if (fs.existsSync(inDocsBacklog)) {
|
|
146
|
-
// We're in -docs folder, try to find sibling code folder
|
|
147
|
-
const baseName = projectName.replace(/-docs$/, '');
|
|
148
|
-
const codeDir = path.join(parentDir, baseName);
|
|
149
|
-
return {
|
|
150
|
-
found: true,
|
|
151
|
-
backlogPath: inDocsBacklog,
|
|
152
|
-
projectPath: fs.existsSync(codeDir) ? codeDir : null,
|
|
153
|
-
docsPath: cwd,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// Check for project subfolders (running from parent folder after /init)
|
|
157
|
-
try {
|
|
158
|
-
const entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
159
|
-
for (const entry of entries) {
|
|
160
|
-
if (!entry.isDirectory())
|
|
161
|
-
continue;
|
|
162
|
-
// Check for .compilr in subfolder (single-repo pattern)
|
|
163
|
-
const subCompilrDir = path.join(cwd, entry.name, '.compilr');
|
|
164
|
-
if (fs.existsSync(subCompilrDir)) {
|
|
165
|
-
const backlogPath = path.join(subCompilrDir, 'backlog.md');
|
|
166
|
-
return {
|
|
167
|
-
found: true,
|
|
168
|
-
backlogPath: fs.existsSync(backlogPath) ? backlogPath : null,
|
|
169
|
-
projectPath: path.join(cwd, entry.name),
|
|
170
|
-
docsPath: null,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
// Check for -docs subfolder (two-repo pattern)
|
|
174
|
-
if (entry.name.endsWith('-docs')) {
|
|
175
|
-
const docsBacklog = path.join(cwd, entry.name, '01-planning', 'backlog.md');
|
|
176
|
-
if (fs.existsSync(docsBacklog)) {
|
|
177
|
-
const baseName = entry.name.replace(/-docs$/, '');
|
|
178
|
-
const codeDir = path.join(cwd, baseName);
|
|
179
|
-
return {
|
|
180
|
-
found: true,
|
|
181
|
-
backlogPath: docsBacklog,
|
|
182
|
-
projectPath: fs.existsSync(codeDir) ? codeDir : null,
|
|
183
|
-
docsPath: path.join(cwd, entry.name),
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
catch {
|
|
190
|
-
// Ignore read errors
|
|
191
|
-
}
|
|
192
|
-
return { found: false, backlogPath: null, projectPath: null, docsPath: null };
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Check if backlog has any items using the shared parser
|
|
196
|
-
*/
|
|
197
|
-
function hasBacklogItems(backlogPath) {
|
|
198
|
-
if (!backlogPath || !fs.existsSync(backlogPath)) {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
const content = fs.readFileSync(backlogPath, 'utf-8');
|
|
203
|
-
const items = parseBacklogItems(content);
|
|
204
|
-
return items.length > 0;
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Get skill prompt by name from builtinSkills
|
|
212
|
-
*/
|
|
213
|
-
function getSkillPrompt(name) {
|
|
214
|
-
const skill = builtinSkills.find((s) => s.name === name);
|
|
215
|
-
return skill?.prompt ?? null;
|
|
216
|
-
}
|
|
217
|
-
// =============================================================================
|
|
218
|
-
// Build Command Helpers
|
|
219
|
-
// =============================================================================
|
|
220
|
-
/**
|
|
221
|
-
* Select the best backlog item to build.
|
|
222
|
-
* If requestedId is provided, finds that specific item.
|
|
223
|
-
* Otherwise, auto-picks the highest priority 📋 item.
|
|
224
|
-
*/
|
|
225
|
-
function selectBuildItem(items, requestedId) {
|
|
226
|
-
// If specific ID requested, find it
|
|
227
|
-
if (requestedId && requestedId.toLowerCase() !== 'scaffold') {
|
|
228
|
-
const searchId = requestedId.toUpperCase();
|
|
229
|
-
const item = items.find(i => i.id === searchId);
|
|
230
|
-
if (!item)
|
|
231
|
-
return null;
|
|
232
|
-
if (item.status !== '📋') {
|
|
233
|
-
// Item exists but not in planned status
|
|
234
|
-
return null; // Handler will show appropriate message
|
|
235
|
-
}
|
|
236
|
-
return item;
|
|
237
|
-
}
|
|
238
|
-
// Filter to only 📋 (planned) items
|
|
239
|
-
const planned = items.filter(i => i.status === '📋');
|
|
240
|
-
if (planned.length === 0)
|
|
241
|
-
return null;
|
|
242
|
-
// Sort by priority, then by ID
|
|
243
|
-
planned.sort((a, b) => {
|
|
244
|
-
const prioA = PRIORITY_ORDER.indexOf(a.priority.toLowerCase());
|
|
245
|
-
const prioB = PRIORITY_ORDER.indexOf(b.priority.toLowerCase());
|
|
246
|
-
if (prioA !== prioB)
|
|
247
|
-
return prioA - prioB;
|
|
248
|
-
return a.id.localeCompare(b.id); // REQ-001 before REQ-002
|
|
249
|
-
});
|
|
250
|
-
return planned[0];
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Find dependency IDs mentioned in item title/description.
|
|
254
|
-
* Looks for patterns like "depends on REQ-001", "blocked by BUG-002", etc.
|
|
255
|
-
*/
|
|
256
|
-
function findDependencies(item) {
|
|
257
|
-
const text = `${item.title} ${item.description}`;
|
|
258
|
-
const patterns = [
|
|
259
|
-
/depends\s+on\s+([A-Z]+-\d+)/gi,
|
|
260
|
-
/blocked\s+by\s+([A-Z]+-\d+)/gi,
|
|
261
|
-
/requires\s+([A-Z]+-\d+)/gi,
|
|
262
|
-
/after\s+([A-Z]+-\d+)/gi,
|
|
263
|
-
/needs\s+([A-Z]+-\d+)/gi,
|
|
264
|
-
];
|
|
265
|
-
const deps = [];
|
|
266
|
-
for (const pattern of patterns) {
|
|
267
|
-
const matches = text.matchAll(pattern);
|
|
268
|
-
for (const match of matches) {
|
|
269
|
-
deps.push(match[1].toUpperCase());
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
return [...new Set(deps)];
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Get list of dependencies that are not yet completed.
|
|
276
|
-
*/
|
|
277
|
-
function getUnmetDependencies(item, allItems) {
|
|
278
|
-
const depIds = findDependencies(item);
|
|
279
|
-
return allItems.filter(i => depIds.includes(i.id) && i.status !== '✅');
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Check if the project has a code foundation (src/, package.json, etc.)
|
|
283
|
-
*/
|
|
284
|
-
function hasProjectFoundation() {
|
|
285
|
-
const cwd = process.cwd();
|
|
286
|
-
const indicators = [
|
|
287
|
-
'src',
|
|
288
|
-
'lib',
|
|
289
|
-
'app',
|
|
290
|
-
'package.json',
|
|
291
|
-
'requirements.txt',
|
|
292
|
-
'Cargo.toml',
|
|
293
|
-
'go.mod',
|
|
294
|
-
'pom.xml',
|
|
295
|
-
'build.gradle',
|
|
296
|
-
'setup.py',
|
|
297
|
-
'pyproject.toml',
|
|
298
|
-
];
|
|
299
|
-
for (const indicator of indicators) {
|
|
300
|
-
const fullPath = path.join(cwd, indicator);
|
|
301
|
-
if (fs.existsSync(fullPath)) {
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
// Also check in project subfolders (for two-repo pattern)
|
|
306
|
-
try {
|
|
307
|
-
const entries = fs.readdirSync(cwd, { withFileTypes: true });
|
|
308
|
-
for (const entry of entries) {
|
|
309
|
-
if (!entry.isDirectory())
|
|
310
|
-
continue;
|
|
311
|
-
// Skip docs folder and hidden folders
|
|
312
|
-
if (entry.name.endsWith('-docs') || entry.name.startsWith('.'))
|
|
313
|
-
continue;
|
|
314
|
-
for (const indicator of indicators) {
|
|
315
|
-
const subPath = path.join(cwd, entry.name, indicator);
|
|
316
|
-
if (fs.existsSync(subPath)) {
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
catch {
|
|
323
|
-
// Ignore read errors
|
|
324
|
-
}
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
36
|
+
import { getActiveProject } from './tools/project-db.js';
|
|
37
|
+
import { getAnchorManager } from './anchors/index.js';
|
|
38
|
+
import { isMemoryInput, handleMemoryInput } from './input-handlers/index.js';
|
|
39
|
+
// Helper functions extracted to reduce file size
|
|
40
|
+
import { selectToolNamesByIntent } from './repl-helpers.js';
|
|
327
41
|
// =============================================================================
|
|
328
42
|
// REPL Class
|
|
329
43
|
// =============================================================================
|
|
@@ -336,19 +50,25 @@ export class REPL {
|
|
|
336
50
|
showFiltering;
|
|
337
51
|
onModeChange;
|
|
338
52
|
onAgentFinish;
|
|
53
|
+
clearSubagentTracking;
|
|
339
54
|
startTime;
|
|
55
|
+
initialCommand;
|
|
340
56
|
state;
|
|
341
57
|
footer;
|
|
58
|
+
renderer;
|
|
342
59
|
// Session stats
|
|
343
60
|
sessionInputTokens = 0;
|
|
344
61
|
sessionOutputTokens = 0;
|
|
345
62
|
sessionRequests = 0;
|
|
346
63
|
// Running state
|
|
347
64
|
isRunning = false;
|
|
65
|
+
shouldReturnToMenu = false;
|
|
348
66
|
agentRunning = false;
|
|
349
67
|
abortController = null;
|
|
350
68
|
// Promise for waiting on REPL exit
|
|
351
69
|
exitResolve = null;
|
|
70
|
+
// Text buffer flush function (set during processMessage, allows permission handler to flush)
|
|
71
|
+
textBufferFlusher = null;
|
|
352
72
|
constructor(options) {
|
|
353
73
|
this.agent = options.agent;
|
|
354
74
|
this.model = options.model;
|
|
@@ -358,7 +78,9 @@ export class REPL {
|
|
|
358
78
|
this.showFiltering = options.showFiltering ?? false;
|
|
359
79
|
this.onModeChange = options.onModeChange;
|
|
360
80
|
this.onAgentFinish = options.onAgentFinish;
|
|
81
|
+
this.clearSubagentTracking = options.clearSubagentTracking;
|
|
361
82
|
this.startTime = new Date();
|
|
83
|
+
this.initialCommand = options.initialCommand;
|
|
362
84
|
this.state = createInitialState(this.model);
|
|
363
85
|
// Map permission mode setting to AgentMode
|
|
364
86
|
const permissionMode = getPermissionMode();
|
|
@@ -368,6 +90,19 @@ export class REPL {
|
|
|
368
90
|
showSeparators: true,
|
|
369
91
|
initialMode,
|
|
370
92
|
});
|
|
93
|
+
// Initialize TerminalRenderer in REPL mode
|
|
94
|
+
// TODO: Scroll regions disabled until incremental rebuild is complete
|
|
95
|
+
this.renderer = new TerminalRenderer({
|
|
96
|
+
initialMode: RenderMode.REPL,
|
|
97
|
+
useScrollRegions: false, // DISABLED: Use legacy mode for now
|
|
98
|
+
debug: false,
|
|
99
|
+
});
|
|
100
|
+
// Connect footer to renderer (enables external renderer mode on footer)
|
|
101
|
+
this.renderer.setFooter(this.footer);
|
|
102
|
+
// Connect conversation output to renderer
|
|
103
|
+
conversation.setRenderer(this.renderer);
|
|
104
|
+
// Note: renderer.start() is called in run(), not here
|
|
105
|
+
// This ensures proper startup sequence: footer.start() → renderer.start() → printWelcome()
|
|
371
106
|
// Notify external state handler of initial mode
|
|
372
107
|
if (this.onModeChange && initialMode !== 'normal') {
|
|
373
108
|
this.onModeChange(initialMode);
|
|
@@ -378,8 +113,60 @@ export class REPL {
|
|
|
378
113
|
if (options.onFooterReady) {
|
|
379
114
|
options.onFooterReady(() => { this.footer.pauseAnimation(); }, () => { this.footer.resumeAnimation(); }, (action) => { this.footer.setSuggestion(action); });
|
|
380
115
|
}
|
|
116
|
+
// Provide text buffer flush function (for permission handler to flush before overlay)
|
|
117
|
+
if (options.onTextBufferReady) {
|
|
118
|
+
options.onTextBufferReady(() => {
|
|
119
|
+
if (this.textBufferFlusher) {
|
|
120
|
+
this.textBufferFlusher();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// Provide subagent tracking callbacks (for agent to report subagent activity)
|
|
125
|
+
if (options.onSubagentReady) {
|
|
126
|
+
options.onSubagentReady({
|
|
127
|
+
onStart: (agentId, agentType, description) => {
|
|
128
|
+
this.footer.onSubagentStart(agentId, agentType, description);
|
|
129
|
+
},
|
|
130
|
+
onToolUse: (agentId, toolName, summary) => {
|
|
131
|
+
this.footer.onSubagentToolUse(agentId, toolName, summary);
|
|
132
|
+
},
|
|
133
|
+
onEnd: (agentId, success, tokenCount, error) => {
|
|
134
|
+
this.footer.onSubagentEnd(agentId, success, tokenCount, error);
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
}
|
|
381
138
|
// Register footer callbacks for overlay controller (used by tools like ask_user)
|
|
382
139
|
registerFooterCallbacks(() => { this.footer.pauseAnimation(); }, () => { this.footer.resumeAnimation(); });
|
|
140
|
+
// Register all command handlers (idempotent - safe to call multiple times)
|
|
141
|
+
registerAllHandlers();
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Reload project-specific anchors when switching projects
|
|
145
|
+
* Clears old project anchors and loads new project anchors
|
|
146
|
+
*/
|
|
147
|
+
reloadProjectAnchors(oldProjectId, newProjectId) {
|
|
148
|
+
// Clear old project anchors (if any)
|
|
149
|
+
if (oldProjectId) {
|
|
150
|
+
const clearedCount = this.agent.clearAnchors({ projectId: oldProjectId });
|
|
151
|
+
debug('anchors', `Cleared ${String(clearedCount)} anchors for project ${oldProjectId}`);
|
|
152
|
+
}
|
|
153
|
+
// Load new project anchors from disk
|
|
154
|
+
const newProjectAnchors = getAnchorManager(newProjectId).getAll({ scope: 'persistent' });
|
|
155
|
+
debug('anchors', `Loading ${String(newProjectAnchors.length)} anchors for project ${newProjectId}`);
|
|
156
|
+
// Add new project anchors to the agent
|
|
157
|
+
for (const anchor of newProjectAnchors) {
|
|
158
|
+
this.agent.addAnchor({
|
|
159
|
+
id: anchor.id,
|
|
160
|
+
content: anchor.content,
|
|
161
|
+
priority: anchor.priority,
|
|
162
|
+
scope: anchor.scope,
|
|
163
|
+
tags: anchor.tags,
|
|
164
|
+
projectId: newProjectId,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (newProjectAnchors.length > 0) {
|
|
168
|
+
conversation.printInfo(` Anchors: ${String(newProjectAnchors.length)} loaded for project`);
|
|
169
|
+
}
|
|
383
170
|
}
|
|
384
171
|
/**
|
|
385
172
|
* Set up Footer event handlers
|
|
@@ -394,16 +181,24 @@ export class REPL {
|
|
|
394
181
|
});
|
|
395
182
|
// Handle slash commands
|
|
396
183
|
this.footer.on('command', (command, args) => {
|
|
397
|
-
|
|
398
|
-
|
|
184
|
+
debugLog('REPL:command', `received /${command}`);
|
|
185
|
+
// NOTE: Don't pause footer here - let overlays pause when ready to render
|
|
186
|
+
// This prevents blank screen flash when overlays have slow constructors
|
|
399
187
|
void this.handleCommand(command, args).then((shouldContinue) => {
|
|
400
|
-
|
|
188
|
+
debugLog('REPL:command', 'handler complete');
|
|
401
189
|
if (!shouldContinue) {
|
|
190
|
+
// Exiting - don't restart input, just resolve
|
|
402
191
|
this.isRunning = false;
|
|
403
192
|
if (this.exitResolve) {
|
|
404
193
|
this.exitResolve();
|
|
405
194
|
}
|
|
406
195
|
}
|
|
196
|
+
else {
|
|
197
|
+
// Overlays handle their own pause/resume via lifecycle
|
|
198
|
+
// For non-overlay commands, we need to restart the input prompt
|
|
199
|
+
// (finishInput() stopped it when user pressed Enter)
|
|
200
|
+
this.footer.restartInput();
|
|
201
|
+
}
|
|
407
202
|
});
|
|
408
203
|
});
|
|
409
204
|
// Handle Esc - abort running agent
|
|
@@ -450,18 +245,37 @@ export class REPL {
|
|
|
450
245
|
*/
|
|
451
246
|
async run() {
|
|
452
247
|
this.isRunning = true;
|
|
453
|
-
// Set terminal title and
|
|
248
|
+
// Set terminal title, clear screen, and show welcome
|
|
454
249
|
terminal.setTitle('compilr');
|
|
455
250
|
terminal.clearScreen();
|
|
456
|
-
conversation.printWelcome(this.model, this.version);
|
|
251
|
+
conversation.printWelcome(this.model, this.version, getActiveProject()?.displayName ?? null);
|
|
457
252
|
// Start footer (begins render loop and input capture)
|
|
458
253
|
this.footer.start();
|
|
254
|
+
// Start renderer (in legacy mode, this is mostly a no-op)
|
|
255
|
+
this.renderer.start();
|
|
256
|
+
// Set project name in footer
|
|
257
|
+
const activeProject = getActiveProject();
|
|
258
|
+
if (activeProject) {
|
|
259
|
+
this.footer.setProjectName(activeProject.displayName);
|
|
260
|
+
}
|
|
261
|
+
// Execute initial command if provided (e.g., /design from project menu)
|
|
262
|
+
if (this.initialCommand) {
|
|
263
|
+
const cmd = this.initialCommand;
|
|
264
|
+
// Give footer a moment to render before running command
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
void this.handleCommand(cmd, '');
|
|
267
|
+
}, 100);
|
|
268
|
+
}
|
|
459
269
|
// Wait for exit signal
|
|
460
270
|
await this.waitForExit();
|
|
461
271
|
// Cleanup
|
|
462
272
|
this.footer.stop();
|
|
463
|
-
|
|
464
|
-
|
|
273
|
+
this.renderer.stop();
|
|
274
|
+
// Only print goodbye when actually exiting (not when returning to menu)
|
|
275
|
+
if (!this.shouldReturnToMenu) {
|
|
276
|
+
terminal.writeLine('');
|
|
277
|
+
conversation.printInfo('Goodbye!');
|
|
278
|
+
}
|
|
465
279
|
}
|
|
466
280
|
/**
|
|
467
281
|
* Wait for REPL exit
|
|
@@ -511,16 +325,28 @@ export class REPL {
|
|
|
511
325
|
stop() {
|
|
512
326
|
this.isRunning = false;
|
|
513
327
|
this.footer.stop();
|
|
328
|
+
this.renderer.stop();
|
|
514
329
|
if (this.exitResolve) {
|
|
515
330
|
this.exitResolve();
|
|
516
331
|
}
|
|
517
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Check if the user requested to return to the startup menu
|
|
335
|
+
*/
|
|
336
|
+
wantsReturnToMenu() {
|
|
337
|
+
return this.shouldReturnToMenu;
|
|
338
|
+
}
|
|
518
339
|
/**
|
|
519
340
|
* Handle submitted input (processes message and queued inputs)
|
|
520
341
|
*/
|
|
521
342
|
async handleSubmit(input) {
|
|
522
343
|
// Clear any previous suggestion when new input is submitted
|
|
523
344
|
this.footer.setSuggestion(null);
|
|
345
|
+
// Handle memory prefix: "# note" creates a persistent anchor
|
|
346
|
+
if (isMemoryInput(input)) {
|
|
347
|
+
handleMemoryInput(input, { agent: this.agent, footer: this.footer });
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
524
350
|
await this.processMessage(input);
|
|
525
351
|
// Process queued inputs (FIFO)
|
|
526
352
|
while (this.footer.hasQueuedInput() && this.isRunning) {
|
|
@@ -533,6 +359,57 @@ export class REPL {
|
|
|
533
359
|
// ===========================================================================
|
|
534
360
|
// Command Handling
|
|
535
361
|
// ===========================================================================
|
|
362
|
+
/**
|
|
363
|
+
* Create command context for handler execution.
|
|
364
|
+
*/
|
|
365
|
+
getCommandContext() {
|
|
366
|
+
return {
|
|
367
|
+
// Agent and model
|
|
368
|
+
agent: this.agent,
|
|
369
|
+
model: this.model,
|
|
370
|
+
currentModel: this.currentModel,
|
|
371
|
+
provider: this.provider,
|
|
372
|
+
version: this.version,
|
|
373
|
+
// UI components
|
|
374
|
+
footer: this.footer,
|
|
375
|
+
state: this.state,
|
|
376
|
+
// Session info
|
|
377
|
+
startTime: this.startTime,
|
|
378
|
+
sessionInputTokens: this.sessionInputTokens,
|
|
379
|
+
sessionOutputTokens: this.sessionOutputTokens,
|
|
380
|
+
sessionRequests: this.sessionRequests,
|
|
381
|
+
// Callbacks
|
|
382
|
+
onModeChange: this.onModeChange,
|
|
383
|
+
setShouldReturnToMenu: (value) => {
|
|
384
|
+
this.shouldReturnToMenu = value;
|
|
385
|
+
},
|
|
386
|
+
// REPL methods (bound to this instance)
|
|
387
|
+
processMessage: (message, options) => this.processMessage(message, options),
|
|
388
|
+
handleCommand: (cmd, cmdArgs) => this.handleCommand(cmd, cmdArgs),
|
|
389
|
+
setModel: (model) => {
|
|
390
|
+
this.setModel(model);
|
|
391
|
+
},
|
|
392
|
+
// UI helper methods
|
|
393
|
+
showTools: () => this.showTools(),
|
|
394
|
+
showTokens: () => {
|
|
395
|
+
this.showTokens();
|
|
396
|
+
},
|
|
397
|
+
showContext: () => {
|
|
398
|
+
this.showContext();
|
|
399
|
+
},
|
|
400
|
+
showStatus: () => {
|
|
401
|
+
this.showStatus();
|
|
402
|
+
},
|
|
403
|
+
showTodos: () => {
|
|
404
|
+
this.showTodos();
|
|
405
|
+
},
|
|
406
|
+
handleCompact: () => this.handleCompact(),
|
|
407
|
+
handleExport: (exportArgs) => this.handleExport(exportArgs),
|
|
408
|
+
reloadProjectAnchors: (oldId, newId) => {
|
|
409
|
+
this.reloadProjectAnchors(oldId, newId);
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
536
413
|
/**
|
|
537
414
|
* Handle slash command
|
|
538
415
|
* Returns false if REPL should exit
|
|
@@ -559,639 +436,15 @@ export class REPL {
|
|
|
559
436
|
conversation.printInfo('Type /help to see available commands');
|
|
560
437
|
return true;
|
|
561
438
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const backlogResult = await showBacklogOverlay();
|
|
568
|
-
if (backlogResult.modified) {
|
|
569
|
-
conversation.printSuccess('Backlog updated.');
|
|
570
|
-
}
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
case 'commands':
|
|
574
|
-
await showCommandsOverlay();
|
|
575
|
-
return true;
|
|
576
|
-
case 'exit':
|
|
577
|
-
return false;
|
|
578
|
-
case 'help':
|
|
579
|
-
await overlays.showHelp();
|
|
580
|
-
return true;
|
|
581
|
-
case 'init': {
|
|
582
|
-
const initResult = await showInitOverlay();
|
|
583
|
-
if (initResult.created) {
|
|
584
|
-
// Show success message with created files info
|
|
585
|
-
conversation.printSuccess(`Project created: ${initResult.projectPath ?? ''}`);
|
|
586
|
-
if (initResult.docsPath) {
|
|
587
|
-
conversation.printSuccess(`Docs repo created: ${initResult.docsPath}`);
|
|
588
|
-
}
|
|
589
|
-
conversation.printInfo('');
|
|
590
|
-
conversation.printInfo('Next steps:');
|
|
591
|
-
conversation.printInfo(` cd ${initResult.projectPath?.split('/').pop() ?? 'project'}`);
|
|
592
|
-
conversation.printInfo(' npm install');
|
|
593
|
-
conversation.printInfo(' npm run dev');
|
|
594
|
-
conversation.printInfo('');
|
|
595
|
-
// Resume footer animation before processing message
|
|
596
|
-
this.footer.resumeAnimation();
|
|
597
|
-
// Ask agent to read the COMPILR.md and understand the project
|
|
598
|
-
const compilrMdPath = initResult.docsPath
|
|
599
|
-
? `${initResult.docsPath}/COMPILR.md`
|
|
600
|
-
: `${initResult.projectPath ?? '.'}/COMPILR.md`;
|
|
601
|
-
await this.processMessage(`I just created a new project using /init. Please read ${compilrMdPath} to understand the project context and confirm you're ready to help me build it.
|
|
602
|
-
|
|
603
|
-
(Note to user: When you want to define requirements and populate the backlog, type /design)`);
|
|
604
|
-
}
|
|
605
|
-
return true;
|
|
606
|
-
}
|
|
607
|
-
case 'keys': {
|
|
608
|
-
const keysResult = await showKeysOverlay();
|
|
609
|
-
if (keysResult.changed) {
|
|
610
|
-
conversation.printSuccess('API keys updated.');
|
|
611
|
-
conversation.printInfo('Restart the CLI to use the new keys.');
|
|
612
|
-
}
|
|
613
|
-
return true;
|
|
614
|
-
}
|
|
615
|
-
case 'clear':
|
|
616
|
-
terminal.clearScreen();
|
|
617
|
-
conversation.printWelcome(this.model, this.version);
|
|
618
|
-
return true;
|
|
619
|
-
case 'compact':
|
|
620
|
-
await this.handleCompact();
|
|
621
|
-
return true;
|
|
622
|
-
case 'config': {
|
|
623
|
-
// Get context stats for usage tab
|
|
624
|
-
const contextManager = this.agent.getContextManager();
|
|
625
|
-
const history = this.agent.getHistory();
|
|
626
|
-
let contextUsed = 0;
|
|
627
|
-
let contextMax = 200000;
|
|
628
|
-
if (contextManager) {
|
|
629
|
-
const stats = contextManager.getStats(history.length);
|
|
630
|
-
contextUsed = stats.currentTokens;
|
|
631
|
-
contextMax = stats.maxTokens;
|
|
632
|
-
}
|
|
633
|
-
const configResult = await showConfigOverlay({
|
|
634
|
-
version: this.version,
|
|
635
|
-
cwd: process.cwd(),
|
|
636
|
-
model: this.currentModel,
|
|
637
|
-
provider: this.provider,
|
|
638
|
-
toolCount: this.agent.getToolDefinitions().length,
|
|
639
|
-
startTime: this.startTime,
|
|
640
|
-
// Usage stats
|
|
641
|
-
inputTokens: this.sessionInputTokens,
|
|
642
|
-
outputTokens: this.sessionOutputTokens,
|
|
643
|
-
requests: this.sessionRequests,
|
|
644
|
-
contextUsed,
|
|
645
|
-
contextMax,
|
|
646
|
-
messageCount: history.length,
|
|
647
|
-
// Model change callback for hot-switch
|
|
648
|
-
onModelChange: (model) => {
|
|
649
|
-
this.setModel(model);
|
|
650
|
-
},
|
|
651
|
-
});
|
|
652
|
-
// Handle model change result (for hot-switch)
|
|
653
|
-
if (configResult.modelChanged) {
|
|
654
|
-
this.currentModel = configResult.modelChanged;
|
|
655
|
-
}
|
|
656
|
-
// Refresh prompt in case theme changed
|
|
657
|
-
this.footer.refreshPrompt();
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
case 'tools':
|
|
661
|
-
await this.showTools();
|
|
662
|
-
return true;
|
|
663
|
-
case 'tokens':
|
|
664
|
-
this.showTokens();
|
|
665
|
-
return true;
|
|
666
|
-
case 'context':
|
|
667
|
-
this.showContext();
|
|
668
|
-
return true;
|
|
669
|
-
case 'export':
|
|
670
|
-
await this.handleExport(args);
|
|
671
|
-
return true;
|
|
672
|
-
case 'model': {
|
|
673
|
-
const modelResult = await showConfigOverlay({
|
|
674
|
-
model: this.currentModel,
|
|
675
|
-
provider: this.provider,
|
|
676
|
-
initialMode: 'model-selector',
|
|
677
|
-
// Don't print in callback - overlay cleanup would clear it
|
|
678
|
-
// We'll print after overlay closes instead
|
|
679
|
-
});
|
|
680
|
-
// Handle model change result
|
|
681
|
-
if (modelResult.modelChanged) {
|
|
682
|
-
this.currentModel = modelResult.modelChanged;
|
|
683
|
-
// Clear footer, print message, re-render footer
|
|
684
|
-
this.footer.clearForOutput();
|
|
685
|
-
conversation.printSuccess(`Model switched to: ${modelResult.modelChanged}`);
|
|
686
|
-
this.footer.forceRender();
|
|
687
|
-
}
|
|
688
|
-
return true;
|
|
689
|
-
}
|
|
690
|
-
case 'status':
|
|
691
|
-
this.showStatus();
|
|
692
|
-
return true;
|
|
693
|
-
case 'todos':
|
|
694
|
-
this.showTodos();
|
|
695
|
-
return true;
|
|
696
|
-
case 'plan':
|
|
697
|
-
// Switch to plan mode
|
|
698
|
-
this.footer.setMode('plan');
|
|
699
|
-
if (this.onModeChange) {
|
|
700
|
-
this.onModeChange('plan');
|
|
701
|
-
}
|
|
702
|
-
conversation.printInfo('📋 Switched to Plan mode');
|
|
703
|
-
conversation.printInfo('The agent will create execution plans without performing actions.');
|
|
704
|
-
conversation.printInfo('Use Shift+Tab to switch back to Normal or Auto-accept mode.');
|
|
705
|
-
return true;
|
|
706
|
-
case 'design': {
|
|
707
|
-
// Check if project is initialized
|
|
708
|
-
const designProject = detectCompilrProject();
|
|
709
|
-
if (!designProject.found) {
|
|
710
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
711
|
-
return true;
|
|
712
|
-
}
|
|
713
|
-
// Check model tier - /design works best with large models
|
|
714
|
-
if (!modelMeetsTier(this.currentModel, '/design')) {
|
|
715
|
-
const modelTier = getModelTier(this.currentModel);
|
|
716
|
-
// Show warning overlay
|
|
717
|
-
const choice = await showModelWarningOverlay({
|
|
718
|
-
command: '/design',
|
|
719
|
-
currentModel: this.currentModel,
|
|
720
|
-
currentTier: modelTier.tier,
|
|
721
|
-
suggestedModel: modelTier.suggestedUpgrade,
|
|
722
|
-
alternativeCommand: '/sketch',
|
|
723
|
-
});
|
|
724
|
-
switch (choice) {
|
|
725
|
-
case 'cancel':
|
|
726
|
-
return true;
|
|
727
|
-
case 'alternative':
|
|
728
|
-
// Run /sketch instead (command name without leading slash)
|
|
729
|
-
return this.handleCommand('sketch', '');
|
|
730
|
-
case 'switch':
|
|
731
|
-
// Hot-switch to the suggested model
|
|
732
|
-
if (modelTier.suggestedUpgrade) {
|
|
733
|
-
this.currentModel = modelTier.suggestedUpgrade;
|
|
734
|
-
conversation.printSuccess(`Switched to ${modelTier.suggestedUpgrade}`);
|
|
735
|
-
conversation.printInfo('');
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
conversation.printWarning('No suggested upgrade available. Continuing with current model.');
|
|
739
|
-
conversation.printInfo('');
|
|
740
|
-
}
|
|
741
|
-
break;
|
|
742
|
-
case 'continue':
|
|
743
|
-
// Proceed with current model
|
|
744
|
-
break;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
// Get design skill prompt
|
|
748
|
-
const designPrompt = getSkillPrompt('design');
|
|
749
|
-
if (!designPrompt) {
|
|
750
|
-
conversation.printError('Design skill not found.');
|
|
751
|
-
return true;
|
|
752
|
-
}
|
|
753
|
-
// Note: Plan mode is not yet implemented, so we run in normal mode
|
|
754
|
-
// The skill prompt guides the agent's behavior
|
|
755
|
-
conversation.printInfo('📋 Starting project design...');
|
|
756
|
-
conversation.printInfo('');
|
|
757
|
-
// Resume footer animation before processing message
|
|
758
|
-
this.footer.resumeAnimation();
|
|
759
|
-
// Inject design skill and send initial message
|
|
760
|
-
// Show short message to user, but send full skill prompt to agent
|
|
761
|
-
await this.processMessage(`I want to design my project and create the backlog.
|
|
762
|
-
|
|
763
|
-
${designPrompt}
|
|
764
|
-
|
|
765
|
-
Please start by using todo_write to track the design phases, then begin with Phase 1: Vision. Use the ask_user tool to gather information efficiently.`, {
|
|
766
|
-
displayMessage: 'Start the design process for my project.',
|
|
767
|
-
});
|
|
768
|
-
return true;
|
|
769
|
-
}
|
|
770
|
-
case 'sketch': {
|
|
771
|
-
// Check if project is initialized
|
|
772
|
-
const sketchProject = detectCompilrProject();
|
|
773
|
-
if (!sketchProject.found) {
|
|
774
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
775
|
-
return true;
|
|
776
|
-
}
|
|
777
|
-
// Get sketch skill prompt
|
|
778
|
-
const sketchPrompt = getSkillPrompt('sketch');
|
|
779
|
-
if (!sketchPrompt) {
|
|
780
|
-
conversation.printError('Sketch skill not found.');
|
|
781
|
-
return true;
|
|
782
|
-
}
|
|
783
|
-
conversation.printInfo('✏️ Starting quick project sketch...');
|
|
784
|
-
conversation.printInfo('');
|
|
785
|
-
// Resume footer animation before processing message
|
|
786
|
-
this.footer.resumeAnimation();
|
|
787
|
-
// Inject sketch skill and send initial message
|
|
788
|
-
// Uses ask_user_simple for simpler questions (one at a time)
|
|
789
|
-
await this.processMessage(`I want to quickly outline my project.
|
|
790
|
-
|
|
791
|
-
${sketchPrompt}
|
|
792
|
-
|
|
793
|
-
Please start by asking me about the type of application I'm building using the ask_user_simple tool.`, {
|
|
794
|
-
displayMessage: 'Quick project outline.',
|
|
795
|
-
});
|
|
796
|
-
return true;
|
|
797
|
-
}
|
|
798
|
-
case 'refine': {
|
|
799
|
-
// Check if project is initialized - use same path detection as /backlog
|
|
800
|
-
const refineBacklogPath = findBacklogPath();
|
|
801
|
-
if (!refineBacklogPath) {
|
|
802
|
-
conversation.printError('No backlog found. Run /init first to create a project.');
|
|
803
|
-
return true;
|
|
804
|
-
}
|
|
805
|
-
// Check if backlog has items
|
|
806
|
-
if (!hasBacklogItems(refineBacklogPath)) {
|
|
807
|
-
conversation.printError('No backlog items found. Run /design first to create initial requirements.');
|
|
808
|
-
return true;
|
|
809
|
-
}
|
|
810
|
-
// Check if a specific item ID was provided (e.g., /refine REQ-001)
|
|
811
|
-
const itemId = args.trim().toUpperCase();
|
|
812
|
-
const isFocusedRefine = itemId && /^[A-Z]+-\d{3}$/.test(itemId);
|
|
813
|
-
// For full refine mode (no item ID), check model tier and warn
|
|
814
|
-
if (!isFocusedRefine && !modelMeetsTier(this.currentModel, '/refine')) {
|
|
815
|
-
const modelTier = getModelTier(this.currentModel);
|
|
816
|
-
const choice = await showModelWarningOverlay({
|
|
817
|
-
command: '/refine',
|
|
818
|
-
currentModel: this.currentModel,
|
|
819
|
-
currentTier: modelTier.tier,
|
|
820
|
-
suggestedModel: modelTier.suggestedUpgrade,
|
|
821
|
-
alternativeCommand: '/refine <ITEM-ID>',
|
|
822
|
-
});
|
|
823
|
-
switch (choice) {
|
|
824
|
-
case 'cancel':
|
|
825
|
-
return true;
|
|
826
|
-
case 'alternative':
|
|
827
|
-
// Show hint about using specific item ID
|
|
828
|
-
conversation.printInfo('💡 Tip: Use /refine <ITEM-ID> for focused refinement with smaller models.');
|
|
829
|
-
conversation.printInfo(' Example: /refine REQ-001');
|
|
830
|
-
conversation.printInfo('');
|
|
831
|
-
return true;
|
|
832
|
-
case 'switch':
|
|
833
|
-
if (modelTier.suggestedUpgrade) {
|
|
834
|
-
this.currentModel = modelTier.suggestedUpgrade;
|
|
835
|
-
conversation.printSuccess(`Switched to ${this.currentModel}`);
|
|
836
|
-
}
|
|
837
|
-
break;
|
|
838
|
-
case 'continue':
|
|
839
|
-
// Continue with current model
|
|
840
|
-
break;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
// Get appropriate skill prompt
|
|
844
|
-
const skillName = isFocusedRefine ? 'refine-item' : 'refine';
|
|
845
|
-
const refinePrompt = getSkillPrompt(skillName);
|
|
846
|
-
if (!refinePrompt) {
|
|
847
|
-
conversation.printError(`${skillName} skill not found.`);
|
|
848
|
-
return true;
|
|
849
|
-
}
|
|
850
|
-
if (isFocusedRefine) {
|
|
851
|
-
conversation.printInfo(`🔄 Refining item ${itemId}...`);
|
|
852
|
-
}
|
|
853
|
-
else {
|
|
854
|
-
conversation.printInfo('🔄 Starting requirements refinement...');
|
|
855
|
-
}
|
|
856
|
-
conversation.printInfo('');
|
|
857
|
-
// Resume footer animation before processing message
|
|
858
|
-
this.footer.resumeAnimation();
|
|
859
|
-
// Build message based on mode
|
|
860
|
-
const userIntent = isFocusedRefine
|
|
861
|
-
? `I want to refine backlog item ${itemId}.`
|
|
862
|
-
: 'I want to refine my project requirements.';
|
|
863
|
-
const agentInstructions = isFocusedRefine
|
|
864
|
-
? `Please use backlog_read with id:"${itemId}" to get the item details, then guide me through refining it.`
|
|
865
|
-
: 'Please start by using backlog_read with limit:10 to get an overview, then ask me what I\'d like to focus on using ask_user_simple.';
|
|
866
|
-
await this.processMessage(`${userIntent}
|
|
867
|
-
|
|
868
|
-
${refinePrompt}
|
|
869
|
-
|
|
870
|
-
${agentInstructions}`, {
|
|
871
|
-
displayMessage: userIntent,
|
|
872
|
-
});
|
|
873
|
-
return true;
|
|
874
|
-
}
|
|
875
|
-
case 'arch': {
|
|
876
|
-
// Check if project is initialized
|
|
877
|
-
const archProject = detectCompilrProject();
|
|
878
|
-
if (!archProject.found) {
|
|
879
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
880
|
-
return true;
|
|
881
|
-
}
|
|
882
|
-
// Check model tier - /arch requires large models
|
|
883
|
-
if (!modelMeetsTier(this.currentModel, '/arch')) {
|
|
884
|
-
const modelTier = getModelTier(this.currentModel);
|
|
885
|
-
const choice = await showModelWarningOverlay({
|
|
886
|
-
command: '/arch',
|
|
887
|
-
currentModel: this.currentModel,
|
|
888
|
-
currentTier: modelTier.tier,
|
|
889
|
-
suggestedModel: modelTier.suggestedUpgrade,
|
|
890
|
-
// No alternative command for /arch - it's inherently complex
|
|
891
|
-
});
|
|
892
|
-
switch (choice) {
|
|
893
|
-
case 'cancel':
|
|
894
|
-
return true;
|
|
895
|
-
case 'switch':
|
|
896
|
-
if (modelTier.suggestedUpgrade) {
|
|
897
|
-
this.currentModel = modelTier.suggestedUpgrade;
|
|
898
|
-
conversation.printSuccess(`Switched to ${modelTier.suggestedUpgrade}`);
|
|
899
|
-
conversation.printInfo('');
|
|
900
|
-
}
|
|
901
|
-
else {
|
|
902
|
-
conversation.printWarning('No suggested upgrade available. Continuing with current model.');
|
|
903
|
-
conversation.printInfo('');
|
|
904
|
-
}
|
|
905
|
-
break;
|
|
906
|
-
case 'continue':
|
|
907
|
-
// Proceed with current model
|
|
908
|
-
break;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
// Show type selection overlay
|
|
912
|
-
const archChoice = await showArchTypeOverlay();
|
|
913
|
-
if (!archChoice) {
|
|
914
|
-
// User cancelled
|
|
915
|
-
return true;
|
|
916
|
-
}
|
|
917
|
-
// Get architecture skill prompt
|
|
918
|
-
const archPrompt = getSkillPrompt('architecture');
|
|
919
|
-
if (!archPrompt) {
|
|
920
|
-
conversation.printError('Architecture skill not found.');
|
|
921
|
-
return true;
|
|
922
|
-
}
|
|
923
|
-
// Build the document type description for display
|
|
924
|
-
const docTypeLabels = {
|
|
925
|
-
'adr': 'Architecture Decision Record',
|
|
926
|
-
'diagram': 'System Diagram',
|
|
927
|
-
'data-model': 'Data Model',
|
|
928
|
-
'api': 'API Design',
|
|
929
|
-
'custom': 'Custom Documentation',
|
|
930
|
-
};
|
|
931
|
-
const displayType = archChoice.customTopic
|
|
932
|
-
? `Custom: ${archChoice.customTopic}`
|
|
933
|
-
: docTypeLabels[archChoice.type];
|
|
934
|
-
conversation.printInfo(`📐 Creating ${displayType}...`);
|
|
935
|
-
conversation.printInfo('');
|
|
936
|
-
// Resume footer animation before processing message
|
|
937
|
-
this.footer.resumeAnimation();
|
|
938
|
-
// Replace placeholders in skill prompt
|
|
939
|
-
const finalPrompt = archPrompt
|
|
940
|
-
.replace('{{doc_type}}', archChoice.type)
|
|
941
|
-
.replace('{{#if custom_topic}}Custom Topic: {{custom_topic}}{{/if}}', archChoice.customTopic ? `Custom Topic: ${archChoice.customTopic}` : '');
|
|
942
|
-
await this.processMessage(`I want to create architecture documentation.
|
|
943
|
-
|
|
944
|
-
${finalPrompt}
|
|
945
|
-
|
|
946
|
-
Please start by reading any existing PRD.md and using backlog_read to understand the project context. Then use ask_user to gather the information needed for this ${archChoice.type} document.`, {
|
|
947
|
-
displayMessage: `Create ${displayType} documentation.`,
|
|
948
|
-
});
|
|
949
|
-
return true;
|
|
950
|
-
}
|
|
951
|
-
case 'note': {
|
|
952
|
-
// Check if project is initialized
|
|
953
|
-
const noteProject = detectCompilrProject();
|
|
954
|
-
if (!noteProject.found) {
|
|
955
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
956
|
-
return true;
|
|
957
|
-
}
|
|
958
|
-
// Get session-notes skill prompt
|
|
959
|
-
const notePrompt = getSkillPrompt('session-notes');
|
|
960
|
-
if (!notePrompt) {
|
|
961
|
-
conversation.printError('Session-notes skill not found.');
|
|
962
|
-
return true;
|
|
963
|
-
}
|
|
964
|
-
// Check for optional title argument
|
|
965
|
-
const noteTitle = args.trim();
|
|
966
|
-
conversation.printInfo('📝 Creating session note...');
|
|
967
|
-
conversation.printInfo('');
|
|
968
|
-
// Resume footer animation before processing message
|
|
969
|
-
this.footer.resumeAnimation();
|
|
970
|
-
// Build message based on whether title was provided
|
|
971
|
-
const titleInstruction = noteTitle
|
|
972
|
-
? `The session title is: "${noteTitle}"`
|
|
973
|
-
: 'Please ask me for a title using ask_user_simple, or generate one from the session summary.';
|
|
974
|
-
await this.processMessage(`I want to create a session note capturing what we've done.
|
|
975
|
-
|
|
976
|
-
${notePrompt}
|
|
977
|
-
|
|
978
|
-
${titleInstruction}
|
|
979
|
-
|
|
980
|
-
Review the conversation context to understand what was accomplished, then create the session note file.`, {
|
|
981
|
-
displayMessage: noteTitle
|
|
982
|
-
? `Create session note: "${noteTitle}"`
|
|
983
|
-
: 'Create a session note for this session.',
|
|
984
|
-
});
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
case 'prd': {
|
|
988
|
-
// Check if project is initialized
|
|
989
|
-
const prdProject = detectCompilrProject();
|
|
990
|
-
if (!prdProject.found) {
|
|
991
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
// Get prd skill prompt
|
|
995
|
-
const prdPrompt = getSkillPrompt('prd');
|
|
996
|
-
if (!prdPrompt) {
|
|
997
|
-
conversation.printError('PRD skill not found.');
|
|
998
|
-
return true;
|
|
999
|
-
}
|
|
1000
|
-
// Check for optional section argument
|
|
1001
|
-
const prdSection = args.trim().toLowerCase();
|
|
1002
|
-
const validSections = ['vision', 'scope', 'technical', 'success'];
|
|
1003
|
-
const sectionInstruction = validSections.includes(prdSection)
|
|
1004
|
-
? `The user wants to update the "${prdSection}" section specifically.`
|
|
1005
|
-
: 'Ask the user which section they want to update using ask_user_simple.';
|
|
1006
|
-
conversation.printInfo('📄 Opening PRD for updates...');
|
|
1007
|
-
conversation.printInfo('');
|
|
1008
|
-
// Resume footer animation before processing message
|
|
1009
|
-
this.footer.resumeAnimation();
|
|
1010
|
-
await this.processMessage(`I want to update the Product Requirements Document.
|
|
1011
|
-
|
|
1012
|
-
${prdPrompt}
|
|
1013
|
-
|
|
1014
|
-
${sectionInstruction}
|
|
1015
|
-
|
|
1016
|
-
Start by reading the existing PRD.md file to understand current state.`, {
|
|
1017
|
-
displayMessage: prdSection
|
|
1018
|
-
? `Update PRD: ${prdSection} section`
|
|
1019
|
-
: 'Update the Product Requirements Document',
|
|
1020
|
-
});
|
|
1021
|
-
return true;
|
|
1022
|
-
}
|
|
1023
|
-
case 'build': {
|
|
1024
|
-
const itemId = args.trim() || undefined;
|
|
1025
|
-
// Handle /build scaffold - redirect to scaffold handler
|
|
1026
|
-
if (itemId?.toLowerCase() === 'scaffold') {
|
|
1027
|
-
return this.handleScaffoldCommand();
|
|
1028
|
-
}
|
|
1029
|
-
// Check if project is initialized
|
|
1030
|
-
const buildProject = detectCompilrProject();
|
|
1031
|
-
if (!buildProject.found) {
|
|
1032
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
1033
|
-
return true;
|
|
1034
|
-
}
|
|
1035
|
-
// Read backlog using the shared path detection
|
|
1036
|
-
const buildBacklogPath = findBacklogPath();
|
|
1037
|
-
if (!buildBacklogPath) {
|
|
1038
|
-
conversation.printError('No backlog found. Run /design or /sketch first.');
|
|
1039
|
-
return true;
|
|
1040
|
-
}
|
|
1041
|
-
// Parse backlog items
|
|
1042
|
-
let buildItems = [];
|
|
1043
|
-
try {
|
|
1044
|
-
const backlogContent = fs.readFileSync(buildBacklogPath, 'utf-8');
|
|
1045
|
-
buildItems = parseBacklogItems(backlogContent);
|
|
1046
|
-
}
|
|
1047
|
-
catch {
|
|
1048
|
-
conversation.printError('Failed to read backlog file.');
|
|
1049
|
-
return true;
|
|
1050
|
-
}
|
|
1051
|
-
if (buildItems.length === 0) {
|
|
1052
|
-
conversation.printError('No backlog items found. Run /design or /sketch first.');
|
|
1053
|
-
return true;
|
|
1054
|
-
}
|
|
1055
|
-
// Get project path for context
|
|
1056
|
-
const projectPathForBuild = buildProject.projectPath || process.cwd();
|
|
1057
|
-
const projectPathContext = `**IMPORTANT: Project Directory**
|
|
1058
|
-
All code files MUST be created in: ${projectPathForBuild}
|
|
1059
|
-
Do NOT create files in the current working directory if it differs from the project path.
|
|
1060
|
-
`;
|
|
1061
|
-
// Check foundation
|
|
1062
|
-
const foundationExists = hasProjectFoundation();
|
|
1063
|
-
if (!foundationExists) {
|
|
1064
|
-
// Let agent handle foundation check via skill
|
|
1065
|
-
conversation.printInfo('🔍 Checking project foundation...');
|
|
1066
|
-
conversation.printInfo('');
|
|
1067
|
-
this.footer.resumeAnimation();
|
|
1068
|
-
await this.processMessage(`I want to build a feature from the backlog, but first check if the project has a foundation.
|
|
1069
|
-
|
|
1070
|
-
${projectPathContext}
|
|
1071
|
-
|
|
1072
|
-
Use detect_project to check the current state. If there's no code foundation (no src/, no package.json or equivalent), use ask_user_simple to ask:
|
|
1073
|
-
"No project foundation detected. Would you like to:"
|
|
1074
|
-
- "Create scaffold first (recommended)"
|
|
1075
|
-
- "Proceed with feature anyway"
|
|
1076
|
-
- "Cancel"
|
|
1077
|
-
|
|
1078
|
-
If user chooses scaffold, read COMPILR.md and PRD.md for tech stack info, then create the project scaffold following these guidelines:
|
|
1079
|
-
|
|
1080
|
-
${getSkillPrompt('scaffold') ?? ''}
|
|
1081
|
-
|
|
1082
|
-
After scaffold is done (or if user chose to proceed anyway), continue with building the feature.`, { displayMessage: 'Check project foundation before building.' });
|
|
1083
|
-
return true;
|
|
1084
|
-
}
|
|
1085
|
-
// Select item
|
|
1086
|
-
const item = selectBuildItem(buildItems, itemId);
|
|
1087
|
-
if (!item) {
|
|
1088
|
-
if (itemId) {
|
|
1089
|
-
const existingItem = buildItems.find(i => i.id.toUpperCase() === itemId.toUpperCase());
|
|
1090
|
-
if (existingItem) {
|
|
1091
|
-
conversation.printError(`Item ${itemId} is already ${existingItem.status === '✅' ? 'completed' : 'in progress'}.`);
|
|
1092
|
-
}
|
|
1093
|
-
else {
|
|
1094
|
-
conversation.printError(`Item ${itemId} not found in backlog.`);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
else {
|
|
1098
|
-
conversation.printInfo('No pending items in backlog. All done! 🎉');
|
|
1099
|
-
}
|
|
1100
|
-
return true;
|
|
1101
|
-
}
|
|
1102
|
-
// Check dependencies
|
|
1103
|
-
const unmetDeps = getUnmetDependencies(item, buildItems);
|
|
1104
|
-
const depsWarning = unmetDeps.length > 0
|
|
1105
|
-
? `\n\n**⚠️ UNMET DEPENDENCIES:**\n${unmetDeps.map(d => `- ${d.id}: ${d.title} (${d.status})`).join('\n')}\n\nThese items are not yet completed. Ask user to confirm before proceeding.`
|
|
1106
|
-
: '';
|
|
1107
|
-
// Get build skill prompt and replace placeholders
|
|
1108
|
-
const buildPromptTemplate = getSkillPrompt('build');
|
|
1109
|
-
if (!buildPromptTemplate) {
|
|
1110
|
-
conversation.printError('Build skill not found.');
|
|
1111
|
-
return true;
|
|
1112
|
-
}
|
|
1113
|
-
const buildPrompt = buildPromptTemplate
|
|
1114
|
-
.replace(/\{\{item_id\}\}/g, item.id)
|
|
1115
|
-
.replace(/\{\{item_title\}\}/g, item.title)
|
|
1116
|
-
.replace(/\{\{item_description\}\}/g, item.description)
|
|
1117
|
-
.replace(/\{\{item_type\}\}/g, item.type)
|
|
1118
|
-
.replace(/\{\{item_priority\}\}/g, item.priority);
|
|
1119
|
-
conversation.printInfo(`🔨 Building ${item.id}: ${item.title}`);
|
|
1120
|
-
conversation.printInfo('');
|
|
1121
|
-
this.footer.resumeAnimation();
|
|
1122
|
-
await this.processMessage(`I want to implement backlog item ${item.id}.
|
|
1123
|
-
|
|
1124
|
-
${projectPathContext}
|
|
1125
|
-
|
|
1126
|
-
${buildPrompt}${depsWarning}`, { displayMessage: `Build ${item.id}: ${item.title}` });
|
|
1127
|
-
return true;
|
|
1128
|
-
}
|
|
1129
|
-
case 'scaffold': {
|
|
1130
|
-
return this.handleScaffoldCommand();
|
|
1131
|
-
}
|
|
1132
|
-
case 'tutorial': {
|
|
1133
|
-
await showTutorialOverlay();
|
|
1134
|
-
return true;
|
|
1135
|
-
}
|
|
1136
|
-
default:
|
|
1137
|
-
// This shouldn't happen if resolveCommand works correctly
|
|
1138
|
-
conversation.printWarning(`Unknown command: /${command}`);
|
|
1139
|
-
return true;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
/**
|
|
1143
|
-
* Handle /scaffold command (shared between /scaffold and /build scaffold)
|
|
1144
|
-
*/
|
|
1145
|
-
async handleScaffoldCommand() {
|
|
1146
|
-
// Check if project is initialized
|
|
1147
|
-
const scaffoldProject = detectCompilrProject();
|
|
1148
|
-
if (!scaffoldProject.found) {
|
|
1149
|
-
conversation.printError('No .compilr project found. Run /init first.');
|
|
1150
|
-
return true;
|
|
439
|
+
// Try command registry first (for extracted handlers)
|
|
440
|
+
const ctx = this.getCommandContext();
|
|
441
|
+
const registryResult = await commandRegistry.execute(resolved, args, ctx);
|
|
442
|
+
if (registryResult !== null) {
|
|
443
|
+
return registryResult;
|
|
1151
444
|
}
|
|
1152
|
-
//
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
All code files MUST be created in: ${projectPathForScaffold}
|
|
1156
|
-
Do NOT create files in the current working directory if it differs from the project path.
|
|
1157
|
-
`;
|
|
1158
|
-
// Check if foundation already exists
|
|
1159
|
-
const foundationExists = hasProjectFoundation();
|
|
1160
|
-
if (foundationExists) {
|
|
1161
|
-
// Let agent confirm with user
|
|
1162
|
-
conversation.printInfo('🔍 Project files detected...');
|
|
1163
|
-
conversation.printInfo('');
|
|
1164
|
-
this.footer.resumeAnimation();
|
|
1165
|
-
await this.processMessage(`User wants to create a project scaffold, but a foundation may already exist.
|
|
1166
|
-
|
|
1167
|
-
${scaffoldPathContext}
|
|
1168
|
-
|
|
1169
|
-
Use detect_project to analyze the current state. Then use ask_user_simple:
|
|
1170
|
-
"Project files already exist. What would you like to do?"
|
|
1171
|
-
- "Continue anyway (may overwrite)"
|
|
1172
|
-
- "Cancel"
|
|
1173
|
-
|
|
1174
|
-
If continuing, read COMPILR.md and PRD.md for tech stack info, then create the scaffold:
|
|
1175
|
-
|
|
1176
|
-
${getSkillPrompt('scaffold') ?? ''}`, { displayMessage: 'Create project scaffold (foundation exists).' });
|
|
1177
|
-
return true;
|
|
1178
|
-
}
|
|
1179
|
-
// Get scaffold skill prompt
|
|
1180
|
-
const scaffoldPrompt = getSkillPrompt('scaffold');
|
|
1181
|
-
if (!scaffoldPrompt) {
|
|
1182
|
-
conversation.printError('Scaffold skill not found.');
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
conversation.printInfo('🏗️ Creating project scaffold...');
|
|
1186
|
-
conversation.printInfo('');
|
|
1187
|
-
this.footer.resumeAnimation();
|
|
1188
|
-
await this.processMessage(`I want to create the project scaffold (foundation).
|
|
1189
|
-
|
|
1190
|
-
${scaffoldPathContext}
|
|
1191
|
-
|
|
1192
|
-
${scaffoldPrompt}
|
|
1193
|
-
|
|
1194
|
-
Read COMPILR.md and PRD.md first to understand the tech stack, then create the appropriate scaffold.`, { displayMessage: 'Create project scaffold.' });
|
|
445
|
+
// All commands are now handled by the registry
|
|
446
|
+
// This shouldn't happen if resolveCommand works correctly
|
|
447
|
+
conversation.printWarning(`Unknown command: /${command}`);
|
|
1195
448
|
return true;
|
|
1196
449
|
}
|
|
1197
450
|
// ===========================================================================
|
|
@@ -1249,6 +502,10 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1249
502
|
this.agentRunning = true;
|
|
1250
503
|
setAgentRunning(this.state, true);
|
|
1251
504
|
this.footer.setAgentRunning(true);
|
|
505
|
+
// Clear subagent tracking to prevent stale data from previous runs
|
|
506
|
+
// (both in agent.ts and in footer's SubagentRenderer)
|
|
507
|
+
this.clearSubagentTracking?.();
|
|
508
|
+
this.footer.clearSubagents();
|
|
1252
509
|
this.abortController = new AbortController();
|
|
1253
510
|
let totalInputTokens = 0;
|
|
1254
511
|
let totalOutputTokens = 0;
|
|
@@ -1258,6 +515,16 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1258
515
|
let lastToolInput = null;
|
|
1259
516
|
// Event-based rendering: accumulate text, render complete blocks
|
|
1260
517
|
let textAccumulator = '';
|
|
518
|
+
// Set up text buffer flusher so permission handler can flush before showing overlay
|
|
519
|
+
this.textBufferFlusher = () => {
|
|
520
|
+
if (textAccumulator.trim()) {
|
|
521
|
+
this.footer.clearForOutput();
|
|
522
|
+
conversation.printAssistantResponse(textAccumulator.trim());
|
|
523
|
+
terminal.writeLine('');
|
|
524
|
+
this.footer.forceRender();
|
|
525
|
+
textAccumulator = '';
|
|
526
|
+
}
|
|
527
|
+
};
|
|
1261
528
|
debug('processMessage', 'Starting agent stream', {
|
|
1262
529
|
inputLength: cleanedInput.length,
|
|
1263
530
|
toolCount: selectedNames.length,
|
|
@@ -1370,48 +637,32 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1370
637
|
this.footer.forceRender();
|
|
1371
638
|
continue;
|
|
1372
639
|
}
|
|
1373
|
-
//
|
|
1374
|
-
this.footer.clearForOutput();
|
|
1375
|
-
// Handle edit tool - diff was already shown on tool_start
|
|
640
|
+
// Handle edit tool - diff was already shown on tool_start (no output needed)
|
|
1376
641
|
if (toolName === 'edit') {
|
|
1377
642
|
// Just clear the tool indicator, diff was shown before permission prompt
|
|
1378
643
|
this.footer.setCurrentTool(null);
|
|
1379
644
|
this.footer.forceRender();
|
|
1380
645
|
continue;
|
|
1381
646
|
}
|
|
1382
|
-
// Handle task tool
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
const description = typeof toolInput.description === 'string' ? toolInput.description : '';
|
|
1386
|
-
// Format result summary for subagent
|
|
1387
|
-
let resultSummary = '';
|
|
1388
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1389
|
-
if (result && typeof result === 'object') {
|
|
1390
|
-
const resultObj = result;
|
|
1391
|
-
if (resultObj.error) {
|
|
1392
|
-
const errorObj = resultObj.error;
|
|
1393
|
-
const errorMsg = typeof errorObj.message === 'string' ? errorObj.message : JSON.stringify(resultObj.error);
|
|
1394
|
-
resultSummary = pc.red(`Error: ${errorMsg.slice(0, 60)}`);
|
|
1395
|
-
}
|
|
1396
|
-
else if (resultObj.result && typeof resultObj.result === 'object') {
|
|
1397
|
-
const r = resultObj.result;
|
|
1398
|
-
const iterations = typeof r.iterations === 'number' ? r.iterations : 1;
|
|
1399
|
-
// Try to get a summary from the result
|
|
1400
|
-
if (typeof r.result === 'string') {
|
|
1401
|
-
const preview = r.result.slice(0, 60);
|
|
1402
|
-
resultSummary = pc.dim(`${String(iterations)} iteration(s) - ${preview}${r.result.length > 60 ? '...' : ''}`);
|
|
1403
|
-
}
|
|
1404
|
-
else {
|
|
1405
|
-
resultSummary = pc.dim(`${String(iterations)} iteration(s)`);
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
// Print as "task(explore): Search for files" format
|
|
1410
|
-
conversation.printToolExecution(`task(${pc.cyan(subagentType)})`, description, resultSummary);
|
|
647
|
+
// Handle task tool - print sub-agent completion summary to scrolling zone
|
|
648
|
+
// Format: ● Explore(description) \n ⎿ Done (stats)
|
|
649
|
+
if (toolName === 'task') {
|
|
1411
650
|
this.footer.setCurrentTool(null);
|
|
651
|
+
// Get stats BEFORE clearing (so we have the data to print)
|
|
652
|
+
const stats = this.footer.getLastSubagentStats();
|
|
653
|
+
// Clear footer BEFORE clearing subagent data (so lastRenderHeight is still accurate)
|
|
654
|
+
this.footer.clearForOutput();
|
|
655
|
+
// Print the completion summary to scrolling zone
|
|
656
|
+
if (stats) {
|
|
657
|
+
conversation.printSubagentResponse(stats);
|
|
658
|
+
}
|
|
659
|
+
// Now clear the subagent data
|
|
660
|
+
this.footer.clearSubagents();
|
|
1412
661
|
this.footer.forceRender();
|
|
1413
662
|
continue;
|
|
1414
663
|
}
|
|
664
|
+
// Clear footer for output (for tools that actually print output)
|
|
665
|
+
this.footer.clearForOutput();
|
|
1415
666
|
// Build argument summary for other tools
|
|
1416
667
|
let argSummary = '';
|
|
1417
668
|
if (toolInput) {
|
|
@@ -1448,6 +699,23 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1448
699
|
this.footer.setCurrentTool(null);
|
|
1449
700
|
this.footer.forceRender();
|
|
1450
701
|
}
|
|
702
|
+
else if (event.type === 'guardrail_warning') {
|
|
703
|
+
// Show guardrail warning to user (non-blocking)
|
|
704
|
+
const result = event.result;
|
|
705
|
+
const name = result.guardrail?.name ?? 'Unknown';
|
|
706
|
+
const message = event.message || result.guardrail?.message || 'Risky operation detected';
|
|
707
|
+
const category = result.guardrail?.tags?.[0] ?? 'general';
|
|
708
|
+
this.footer.clearForOutput();
|
|
709
|
+
conversation.printWarning(`[${category}] ${name}: ${message}`);
|
|
710
|
+
this.footer.forceRender();
|
|
711
|
+
}
|
|
712
|
+
else if (event.type === 'guardrail_blocked') {
|
|
713
|
+
// Show guardrail block message (operation was prevented)
|
|
714
|
+
const message = event.message || 'Operation blocked by guardrail';
|
|
715
|
+
this.footer.clearForOutput();
|
|
716
|
+
conversation.printError(`Blocked: ${message}`);
|
|
717
|
+
this.footer.forceRender();
|
|
718
|
+
}
|
|
1451
719
|
}
|
|
1452
720
|
debug('processMessage', 'Stream completed', {
|
|
1453
721
|
hasTextOutput,
|
|
@@ -1481,6 +749,8 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1481
749
|
this.sessionInputTokens += totalInputTokens;
|
|
1482
750
|
this.sessionOutputTokens += totalOutputTokens;
|
|
1483
751
|
this.sessionRequests++;
|
|
752
|
+
// Clear subagents BEFORE printing stats so footer won't show them afterward
|
|
753
|
+
this.footer.clearSubagents();
|
|
1484
754
|
// Show token usage
|
|
1485
755
|
if (totalInputTokens > 0 || totalOutputTokens > 0) {
|
|
1486
756
|
this.footer.clearForOutput();
|
|
@@ -1489,6 +759,7 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1489
759
|
conversation.printInfo(`[Tokens: ${total.toLocaleString()} (${totalInputTokens.toLocaleString()} in / ${totalOutputTokens.toLocaleString()} out) - ${String(llmCalls)} LLM call(s)]`);
|
|
1490
760
|
conversation.printInfo(`[Session: ${sessionTotal.toLocaleString()} total (${String(this.sessionRequests)} requests)]`);
|
|
1491
761
|
terminal.writeLine('');
|
|
762
|
+
this.footer.forceRender();
|
|
1492
763
|
}
|
|
1493
764
|
// Stop spinner and re-render footer (AFTER all output is printed)
|
|
1494
765
|
this.agentRunning = false;
|
|
@@ -1509,6 +780,11 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1509
780
|
setAgentRunning(this.state, false);
|
|
1510
781
|
this.footer.setAgentRunning(false);
|
|
1511
782
|
this.abortController = null;
|
|
783
|
+
// Clear text buffer flusher
|
|
784
|
+
this.textBufferFlusher = null;
|
|
785
|
+
// Ensure footer is rendered and isPaused is reset
|
|
786
|
+
// (clearForOutput sets isPaused=true, forceRender resets it)
|
|
787
|
+
this.footer.forceRender();
|
|
1512
788
|
// Notify that agent has finished (for applying deferred suggestions)
|
|
1513
789
|
this.onAgentFinish?.();
|
|
1514
790
|
}
|
|
@@ -1519,12 +795,16 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1519
795
|
async handleCompact() {
|
|
1520
796
|
const contextManager = this.agent.getContextManager();
|
|
1521
797
|
if (!contextManager) {
|
|
798
|
+
this.footer.clearForOutput();
|
|
1522
799
|
conversation.printWarning('Context manager not available');
|
|
800
|
+
this.footer.forceRender();
|
|
1523
801
|
return;
|
|
1524
802
|
}
|
|
1525
803
|
const history = this.agent.getHistory();
|
|
1526
804
|
if (history.length === 0) {
|
|
805
|
+
this.footer.clearForOutput();
|
|
1527
806
|
conversation.printInfo('No conversation history to compact');
|
|
807
|
+
this.footer.forceRender();
|
|
1528
808
|
return;
|
|
1529
809
|
}
|
|
1530
810
|
const stats = contextManager.getStats(history.length);
|
|
@@ -1630,15 +910,8 @@ Read COMPILR.md and PRD.md first to understand the tech stack, then create the a
|
|
|
1630
910
|
permission,
|
|
1631
911
|
};
|
|
1632
912
|
});
|
|
1633
|
-
//
|
|
1634
|
-
|
|
1635
|
-
try {
|
|
1636
|
-
await showToolsOverlay(tools);
|
|
1637
|
-
}
|
|
1638
|
-
finally {
|
|
1639
|
-
this.footer.resumeAnimation();
|
|
1640
|
-
this.footer.forceRender();
|
|
1641
|
-
}
|
|
913
|
+
// Overlay lifecycle handles footer pause/resume
|
|
914
|
+
await showToolsOverlay(tools);
|
|
1642
915
|
}
|
|
1643
916
|
showTokens() {
|
|
1644
917
|
this.footer.clearForOutput();
|