@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
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projects Overlay
|
|
3
|
+
*
|
|
4
|
+
* Interactive overlay for viewing and managing projects.
|
|
5
|
+
* Uses TabbedListOverlay for consistent tabbed list behavior.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Tabbed filtering by status (All, Active, Paused, Completed, Archived)
|
|
9
|
+
* - Paginated list with search
|
|
10
|
+
* - Project detail preview
|
|
11
|
+
* - Archive/Restore workflow
|
|
12
|
+
* - Delete workflow with path validation
|
|
13
|
+
* - Keyboard navigation (vim-style + arrows)
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as terminal from './terminal.js';
|
|
17
|
+
import { TabbedListOverlay, BaseScreen, stay, popScreen, closeOverlay, isEnter, isCtrlC, isEscape, isBackspace, isClose, renderBorder, } from './base/index.js';
|
|
18
|
+
import { projectRepository, workItemRepository } from '../db/repositories/index.js';
|
|
19
|
+
import { clearActiveProject, getActiveProject } from '../tools/project-db.js';
|
|
20
|
+
import { validateDeletePath } from '../utils/path-safety.js';
|
|
21
|
+
import { getAllowedDeletePaths } from '../settings/paths.js';
|
|
22
|
+
import { clearProjectAnchors } from '../anchors/index.js';
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Constants
|
|
25
|
+
// =============================================================================
|
|
26
|
+
const PAGE_SIZE = 10;
|
|
27
|
+
const TABS = [
|
|
28
|
+
{ id: 'all', label: 'All' },
|
|
29
|
+
{ id: 'active', label: 'Active' },
|
|
30
|
+
{ id: 'paused', label: 'Paused' },
|
|
31
|
+
{ id: 'completed', label: 'Complete' },
|
|
32
|
+
{ id: 'archived', label: 'Archived' },
|
|
33
|
+
];
|
|
34
|
+
const STATUS_ICONS = {
|
|
35
|
+
active: '●',
|
|
36
|
+
paused: '◐',
|
|
37
|
+
completed: '✓',
|
|
38
|
+
archived: '◌',
|
|
39
|
+
};
|
|
40
|
+
const STATUS_LABELS = {
|
|
41
|
+
active: 'Active',
|
|
42
|
+
paused: 'Paused',
|
|
43
|
+
completed: 'Complete',
|
|
44
|
+
archived: 'Archived',
|
|
45
|
+
};
|
|
46
|
+
// =============================================================================
|
|
47
|
+
// Helper Functions
|
|
48
|
+
// =============================================================================
|
|
49
|
+
function truncate(str, maxLen) {
|
|
50
|
+
if (str.length <= maxLen)
|
|
51
|
+
return str;
|
|
52
|
+
return str.slice(0, maxLen - 3) + '...';
|
|
53
|
+
}
|
|
54
|
+
function renderProgressBar(percent, width, s) {
|
|
55
|
+
const filled = Math.round((percent / 100) * width);
|
|
56
|
+
const empty = width - filled;
|
|
57
|
+
return s.primary('█'.repeat(filled)) + s.muted('░'.repeat(empty));
|
|
58
|
+
}
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Archive Confirm Screen
|
|
61
|
+
// =============================================================================
|
|
62
|
+
class ArchiveConfirmScreen extends BaseScreen {
|
|
63
|
+
confirmState;
|
|
64
|
+
styles;
|
|
65
|
+
onComplete;
|
|
66
|
+
constructor(confirmState, styles, onComplete) {
|
|
67
|
+
super();
|
|
68
|
+
this.confirmState = confirmState;
|
|
69
|
+
this.styles = styles;
|
|
70
|
+
this.onComplete = onComplete;
|
|
71
|
+
}
|
|
72
|
+
render() {
|
|
73
|
+
const s = this.styles;
|
|
74
|
+
const cols = terminal.getTerminalWidth();
|
|
75
|
+
const border = renderBorder(cols, s);
|
|
76
|
+
const project = this.confirmState.targetProject;
|
|
77
|
+
const lines = [];
|
|
78
|
+
if (!project) {
|
|
79
|
+
lines.push(border);
|
|
80
|
+
lines.push(s.error(' No project selected'));
|
|
81
|
+
lines.push(border);
|
|
82
|
+
return lines;
|
|
83
|
+
}
|
|
84
|
+
const isArchived = project.status === 'archived';
|
|
85
|
+
const action = isArchived ? 'RESTORE' : 'ARCHIVE';
|
|
86
|
+
lines.push(border);
|
|
87
|
+
lines.push(` ${s.primaryBold(action + ' PROJECT')}`);
|
|
88
|
+
lines.push('');
|
|
89
|
+
if (isArchived) {
|
|
90
|
+
lines.push(` ${s.foreground('Restore this project?')}`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(s.muted(' Project: ') + project.displayName);
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(s.foreground(' The project will be marked as "active" and visible again.'));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
lines.push(` ${s.warning('Archive this project?')}`);
|
|
98
|
+
lines.push('');
|
|
99
|
+
lines.push(s.muted(' Project: ') + project.displayName);
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push(s.foreground(' The project will be hidden from the active view.'));
|
|
102
|
+
lines.push(s.foreground(' You can restore it later from the Archived tab.'));
|
|
103
|
+
}
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push(border);
|
|
106
|
+
lines.push(s.muted(' Enter Confirm · Esc Cancel'));
|
|
107
|
+
lines.push(border);
|
|
108
|
+
return lines;
|
|
109
|
+
}
|
|
110
|
+
handleKey(data) {
|
|
111
|
+
if (isCtrlC(data)) {
|
|
112
|
+
return closeOverlay({ action: 'cancel' });
|
|
113
|
+
}
|
|
114
|
+
if (isEscape(data)) {
|
|
115
|
+
this.confirmState.targetProject = null;
|
|
116
|
+
return popScreen();
|
|
117
|
+
}
|
|
118
|
+
if (isEnter(data)) {
|
|
119
|
+
const project = this.confirmState.targetProject;
|
|
120
|
+
if (!project)
|
|
121
|
+
return popScreen();
|
|
122
|
+
const isArchived = project.status === 'archived';
|
|
123
|
+
const newStatus = isArchived ? 'active' : 'archived';
|
|
124
|
+
projectRepository.update(project.id, { status: newStatus });
|
|
125
|
+
// Clear as active if archiving the active project
|
|
126
|
+
if (!isArchived && getActiveProject()?.id === project.id) {
|
|
127
|
+
clearActiveProject();
|
|
128
|
+
}
|
|
129
|
+
this.onComplete();
|
|
130
|
+
this.confirmState.targetProject = null;
|
|
131
|
+
return popScreen();
|
|
132
|
+
}
|
|
133
|
+
return stay(false);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// Delete Confirm Screen
|
|
138
|
+
// =============================================================================
|
|
139
|
+
class DeleteConfirmScreen extends BaseScreen {
|
|
140
|
+
confirmState;
|
|
141
|
+
styles;
|
|
142
|
+
onComplete;
|
|
143
|
+
constructor(confirmState, styles, onComplete) {
|
|
144
|
+
super();
|
|
145
|
+
this.confirmState = confirmState;
|
|
146
|
+
this.styles = styles;
|
|
147
|
+
this.onComplete = onComplete;
|
|
148
|
+
}
|
|
149
|
+
render() {
|
|
150
|
+
const s = this.styles;
|
|
151
|
+
const cols = terminal.getTerminalWidth();
|
|
152
|
+
const border = renderBorder(cols, s);
|
|
153
|
+
const project = this.confirmState.targetProject;
|
|
154
|
+
const validation = this.confirmState.deleteValidation;
|
|
155
|
+
const lines = [];
|
|
156
|
+
if (!project) {
|
|
157
|
+
lines.push(border);
|
|
158
|
+
lines.push(s.error(' No project selected'));
|
|
159
|
+
lines.push(border);
|
|
160
|
+
return lines;
|
|
161
|
+
}
|
|
162
|
+
lines.push(border);
|
|
163
|
+
lines.push(` ${s.error('DELETE PROJECT')}`);
|
|
164
|
+
lines.push('');
|
|
165
|
+
// If validation failed, show error state
|
|
166
|
+
if (validation && !validation.valid) {
|
|
167
|
+
lines.push(s.error(' CANNOT DELETE: Path validation failed'));
|
|
168
|
+
lines.push('');
|
|
169
|
+
lines.push(s.muted(' Project: ') + project.displayName);
|
|
170
|
+
lines.push(s.muted(' Path: ') + project.path);
|
|
171
|
+
lines.push('');
|
|
172
|
+
lines.push(s.error(` Error: ${validation.reason || 'Unknown error'}`));
|
|
173
|
+
lines.push('');
|
|
174
|
+
lines.push(s.foreground(' Allowed directories:'));
|
|
175
|
+
for (const allowed of getAllowedDeletePaths()) {
|
|
176
|
+
lines.push(s.muted(` • ${allowed}`));
|
|
177
|
+
}
|
|
178
|
+
lines.push('');
|
|
179
|
+
lines.push(s.foreground(' Options:'));
|
|
180
|
+
lines.push(s.muted(' 1. Move the project to an allowed directory'));
|
|
181
|
+
lines.push(s.muted(' 2. Add path to allowedPaths in /config'));
|
|
182
|
+
lines.push(s.muted(' 3. Delete files manually and remove from database only'));
|
|
183
|
+
lines.push('');
|
|
184
|
+
lines.push(border);
|
|
185
|
+
lines.push(s.muted(' Esc Back'));
|
|
186
|
+
lines.push(border);
|
|
187
|
+
return lines;
|
|
188
|
+
}
|
|
189
|
+
lines.push(s.warning(' WARNING: This action cannot be undone!'));
|
|
190
|
+
lines.push('');
|
|
191
|
+
lines.push(s.muted(' Project: ') + project.displayName);
|
|
192
|
+
lines.push(s.muted(' Name: ') + project.name);
|
|
193
|
+
lines.push(s.muted(' Path: ') + project.path);
|
|
194
|
+
lines.push('');
|
|
195
|
+
// Show validation status
|
|
196
|
+
if (validation && validation.valid) {
|
|
197
|
+
if (fs.existsSync(project.path + '/.compilr')) {
|
|
198
|
+
lines.push(s.success(' ✓ Path is within allowed directory'));
|
|
199
|
+
lines.push(s.success(' ✓ Project has .compilr marker'));
|
|
200
|
+
}
|
|
201
|
+
else if (fs.existsSync(project.path + '/.git')) {
|
|
202
|
+
lines.push(s.success(' ✓ Path is within allowed directory'));
|
|
203
|
+
lines.push(s.success(' ✓ Project has .git repository'));
|
|
204
|
+
}
|
|
205
|
+
else if (fs.existsSync(project.path + '/package.json')) {
|
|
206
|
+
lines.push(s.success(' ✓ Path is within allowed directory'));
|
|
207
|
+
lines.push(s.success(' ✓ Project has package.json'));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
lines.push(s.success(' ✓ Path is within allowed directory'));
|
|
211
|
+
}
|
|
212
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
213
|
+
lines.push('');
|
|
214
|
+
for (const warning of validation.warnings) {
|
|
215
|
+
lines.push(s.warning(` ⚠ ${warning}`));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
lines.push('');
|
|
219
|
+
}
|
|
220
|
+
lines.push(s.foreground(' This will permanently delete:'));
|
|
221
|
+
lines.push(s.muted(' • Database record (project, work items, documents)'));
|
|
222
|
+
lines.push(s.muted(' • Project files on disk'));
|
|
223
|
+
if (project.status !== 'archived') {
|
|
224
|
+
lines.push('');
|
|
225
|
+
lines.push(s.warning(' Note: Project will be archived first, then deleted.'));
|
|
226
|
+
}
|
|
227
|
+
lines.push('');
|
|
228
|
+
lines.push(s.muted(' ' + '─'.repeat(cols - 4)));
|
|
229
|
+
lines.push('');
|
|
230
|
+
lines.push(s.foreground(' Type the project name to confirm deletion:'));
|
|
231
|
+
lines.push('');
|
|
232
|
+
lines.push(` > ${this.confirmState.deleteConfirmInput}█`);
|
|
233
|
+
if (this.confirmState.deleteError) {
|
|
234
|
+
lines.push('');
|
|
235
|
+
lines.push(s.error(` ${this.confirmState.deleteError}`));
|
|
236
|
+
}
|
|
237
|
+
lines.push('');
|
|
238
|
+
lines.push(border);
|
|
239
|
+
lines.push(s.muted(' Enter Confirm · Esc Cancel'));
|
|
240
|
+
lines.push(border);
|
|
241
|
+
return lines;
|
|
242
|
+
}
|
|
243
|
+
handleKey(data) {
|
|
244
|
+
const char = data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f
|
|
245
|
+
? String.fromCharCode(data[0])
|
|
246
|
+
: null;
|
|
247
|
+
if (isCtrlC(data)) {
|
|
248
|
+
return closeOverlay({ action: 'cancel' });
|
|
249
|
+
}
|
|
250
|
+
if (isEscape(data)) {
|
|
251
|
+
this.confirmState.targetProject = null;
|
|
252
|
+
this.confirmState.deleteConfirmInput = '';
|
|
253
|
+
this.confirmState.deleteError = null;
|
|
254
|
+
this.confirmState.deleteValidation = null;
|
|
255
|
+
return popScreen();
|
|
256
|
+
}
|
|
257
|
+
if (isBackspace(data)) {
|
|
258
|
+
this.confirmState.deleteConfirmInput = this.confirmState.deleteConfirmInput.slice(0, -1);
|
|
259
|
+
this.confirmState.deleteError = null;
|
|
260
|
+
return stay();
|
|
261
|
+
}
|
|
262
|
+
if (char) {
|
|
263
|
+
this.confirmState.deleteConfirmInput += char;
|
|
264
|
+
this.confirmState.deleteError = null;
|
|
265
|
+
return stay();
|
|
266
|
+
}
|
|
267
|
+
if (isEnter(data)) {
|
|
268
|
+
const project = this.confirmState.targetProject;
|
|
269
|
+
if (!project)
|
|
270
|
+
return popScreen();
|
|
271
|
+
// If validation failed, don't allow Enter to proceed
|
|
272
|
+
if (this.confirmState.deleteValidation && !this.confirmState.deleteValidation.valid) {
|
|
273
|
+
return stay(false);
|
|
274
|
+
}
|
|
275
|
+
if (this.confirmState.deleteConfirmInput !== project.name) {
|
|
276
|
+
this.confirmState.deleteError = `Type "${project.name}" exactly to confirm`;
|
|
277
|
+
return stay();
|
|
278
|
+
}
|
|
279
|
+
// Archive first if not already archived
|
|
280
|
+
if (project.status !== 'archived') {
|
|
281
|
+
projectRepository.update(project.id, { status: 'archived' });
|
|
282
|
+
}
|
|
283
|
+
// Delete from database
|
|
284
|
+
projectRepository.delete(project.id);
|
|
285
|
+
// Clear project-specific anchors
|
|
286
|
+
clearProjectAnchors(String(project.id));
|
|
287
|
+
// Clear as active project if needed
|
|
288
|
+
if (getActiveProject()?.id === project.id) {
|
|
289
|
+
clearActiveProject();
|
|
290
|
+
}
|
|
291
|
+
// Delete filesystem folder (only if validation passed and path exists)
|
|
292
|
+
const projectPath = project.path;
|
|
293
|
+
const validation = this.confirmState.deleteValidation;
|
|
294
|
+
if (projectPath && validation?.valid && fs.existsSync(projectPath)) {
|
|
295
|
+
try {
|
|
296
|
+
fs.rmSync(projectPath, { recursive: true, force: true });
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Ignore delete errors - DB is already cleaned up
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
this.onComplete();
|
|
303
|
+
this.confirmState.targetProject = null;
|
|
304
|
+
this.confirmState.deleteConfirmInput = '';
|
|
305
|
+
this.confirmState.deleteError = null;
|
|
306
|
+
this.confirmState.deleteValidation = null;
|
|
307
|
+
return popScreen();
|
|
308
|
+
}
|
|
309
|
+
return stay(false);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
// =============================================================================
|
|
313
|
+
// Projects Overlay
|
|
314
|
+
// =============================================================================
|
|
315
|
+
class ProjectsOverlay extends TabbedListOverlay {
|
|
316
|
+
/** State for confirmation screens */
|
|
317
|
+
confirmState = {
|
|
318
|
+
targetProject: null,
|
|
319
|
+
deleteConfirmInput: '',
|
|
320
|
+
deleteError: null,
|
|
321
|
+
deleteValidation: null,
|
|
322
|
+
};
|
|
323
|
+
constructor() {
|
|
324
|
+
// Load projects from database
|
|
325
|
+
const { projects } = projectRepository.list({ status: 'all' });
|
|
326
|
+
const config = {
|
|
327
|
+
title: 'Projects',
|
|
328
|
+
tabs: TABS,
|
|
329
|
+
items: projects,
|
|
330
|
+
pageSize: PAGE_SIZE,
|
|
331
|
+
filterByTab: (project, tabId) => {
|
|
332
|
+
if (tabId === 'all') {
|
|
333
|
+
// 'all' shows everything except archived
|
|
334
|
+
return project.status !== 'archived';
|
|
335
|
+
}
|
|
336
|
+
return project.status === tabId;
|
|
337
|
+
},
|
|
338
|
+
getSearchText: (project) => `${project.displayName} ${project.name} ${project.path} ${project.type}`,
|
|
339
|
+
renderItem: (project, isSelected, s) => {
|
|
340
|
+
const cols = terminal.getTerminalWidth();
|
|
341
|
+
const nameW = Math.min(24, Math.floor(cols * 0.25));
|
|
342
|
+
const typeW = 10;
|
|
343
|
+
const statusW = 12;
|
|
344
|
+
const pathW = Math.max(20, cols - nameW - typeW - statusW - 12);
|
|
345
|
+
const cursor = isSelected ? s.primary('❯ ') : ' ';
|
|
346
|
+
const statusIcon = STATUS_ICONS[project.status];
|
|
347
|
+
const statusLabel = `${statusIcon} ${STATUS_LABELS[project.status]}`;
|
|
348
|
+
const row = [
|
|
349
|
+
truncate(project.displayName, nameW - 1).padEnd(nameW),
|
|
350
|
+
project.type.padEnd(typeW),
|
|
351
|
+
statusLabel.padEnd(statusW),
|
|
352
|
+
truncate(project.path, pathW - 1),
|
|
353
|
+
].join('');
|
|
354
|
+
if (isSelected) {
|
|
355
|
+
return `${cursor}${s.primary(row)}`;
|
|
356
|
+
}
|
|
357
|
+
return `${cursor}${row}`;
|
|
358
|
+
},
|
|
359
|
+
showCount: true,
|
|
360
|
+
emptyMessage: 'No projects found. Use /new to create one.',
|
|
361
|
+
noResultsMessage: 'No projects match the search.',
|
|
362
|
+
footerHints: (searchMode) => {
|
|
363
|
+
if (searchMode) {
|
|
364
|
+
return 'Type to filter · ↑↓/jk Navigate · Enter Open · Esc Exit search';
|
|
365
|
+
}
|
|
366
|
+
return '↑↓/jk Navigate · ←→/hl Tabs · / Search · Enter Open · x Archive · d Delete · q/Esc Close';
|
|
367
|
+
},
|
|
368
|
+
renderSelectedPreview: (project, s) => {
|
|
369
|
+
const lines = [];
|
|
370
|
+
lines.push(s.muted(' ' + '─'.repeat(60)));
|
|
371
|
+
// Last activity
|
|
372
|
+
const lastActivity = project.lastActivityAt
|
|
373
|
+
? new Date(project.lastActivityAt).toLocaleDateString('en-US', {
|
|
374
|
+
year: 'numeric',
|
|
375
|
+
month: 'short',
|
|
376
|
+
day: 'numeric',
|
|
377
|
+
hour: '2-digit',
|
|
378
|
+
minute: '2-digit',
|
|
379
|
+
})
|
|
380
|
+
: 'Never';
|
|
381
|
+
lines.push(` ${s.muted('Last Activity:')} ${lastActivity}`);
|
|
382
|
+
// Work items progress
|
|
383
|
+
const { items: workItems, total } = workItemRepository.query({ project_id: project.id });
|
|
384
|
+
const completed = workItems.filter(w => w.status === 'completed').length;
|
|
385
|
+
if (total > 0) {
|
|
386
|
+
const percent = Math.round((completed / total) * 100);
|
|
387
|
+
const progressBar = renderProgressBar(percent, 20, s);
|
|
388
|
+
lines.push(` ${s.muted('Work Items:')} ${progressBar} ${String(completed)}/${String(total)} (${String(percent)}%)`);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
lines.push(` ${s.muted('Work Items:')} ${s.muted('None')}`);
|
|
392
|
+
}
|
|
393
|
+
return lines;
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
super(config);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* No detail screen - we handle Enter to close with result instead
|
|
400
|
+
*/
|
|
401
|
+
createDetailScreen(_project) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Refresh projects from database after archive/delete
|
|
406
|
+
*/
|
|
407
|
+
refreshProjects() {
|
|
408
|
+
const { projects } = projectRepository.list({ status: 'all' });
|
|
409
|
+
this.state.items = projects;
|
|
410
|
+
// Re-apply tab filter
|
|
411
|
+
const tabId = TABS[this.state.currentTab]?.id ?? 'all';
|
|
412
|
+
let filtered = projects.filter((p) => {
|
|
413
|
+
if (tabId === 'all')
|
|
414
|
+
return p.status !== 'archived';
|
|
415
|
+
return p.status === tabId;
|
|
416
|
+
});
|
|
417
|
+
// Re-apply search filter
|
|
418
|
+
if (this.state.searchQuery) {
|
|
419
|
+
const query = this.state.searchQuery.toLowerCase();
|
|
420
|
+
filtered = filtered.filter((p) => p.displayName.toLowerCase().includes(query) ||
|
|
421
|
+
p.name.toLowerCase().includes(query) ||
|
|
422
|
+
p.path.toLowerCase().includes(query));
|
|
423
|
+
}
|
|
424
|
+
this.state.filteredItems = filtered;
|
|
425
|
+
this.state.selectedIndex = Math.min(this.state.selectedIndex, Math.max(0, filtered.length - 1));
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Override to intercept custom keys before TabbedListOverlay handles them
|
|
429
|
+
*/
|
|
430
|
+
handleKey(data) {
|
|
431
|
+
const char = data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f
|
|
432
|
+
? String.fromCharCode(data[0])
|
|
433
|
+
: null;
|
|
434
|
+
// Only intercept when on the main list screen (screen stack size = 1)
|
|
435
|
+
if (this.screenStack.size() === 1) {
|
|
436
|
+
// Ctrl+C always closes with cancel result
|
|
437
|
+
if (isCtrlC(data)) {
|
|
438
|
+
this.close({ action: 'cancel' });
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Don't intercept if in search mode
|
|
442
|
+
if (!this.state.searchMode) {
|
|
443
|
+
// q/Esc closes with cancel result
|
|
444
|
+
if (isClose(data)) {
|
|
445
|
+
this.close({ action: 'cancel' });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
// Enter opens workflow (instead of detail view)
|
|
449
|
+
if (isEnter(data) && this.state.filteredItems.length > 0) {
|
|
450
|
+
const selected = this.state.filteredItems[this.state.selectedIndex];
|
|
451
|
+
this.close({ action: 'open-workflow', projectId: selected.id });
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
// 'x' archive/restore
|
|
455
|
+
if ((char === 'x' || char === 'X') && this.state.filteredItems.length > 0) {
|
|
456
|
+
const project = this.state.filteredItems[this.state.selectedIndex];
|
|
457
|
+
this.confirmState.targetProject = project;
|
|
458
|
+
this.screenStack.push(new ArchiveConfirmScreen(this.confirmState, this.styles, () => { this.refreshProjects(); }));
|
|
459
|
+
this.update();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
// 'd' delete
|
|
463
|
+
if ((char === 'd' || char === 'D') && this.state.filteredItems.length > 0) {
|
|
464
|
+
const project = this.state.filteredItems[this.state.selectedIndex];
|
|
465
|
+
this.confirmState.targetProject = project;
|
|
466
|
+
this.confirmState.deleteConfirmInput = '';
|
|
467
|
+
this.confirmState.deleteError = null;
|
|
468
|
+
this.confirmState.deleteValidation = validateDeletePath(project.path);
|
|
469
|
+
this.screenStack.push(new DeleteConfirmScreen(this.confirmState, this.styles, () => { this.refreshProjects(); }));
|
|
470
|
+
this.update();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// Let TabbedListOverlay handle everything else
|
|
476
|
+
super.handleKey(data);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// =============================================================================
|
|
480
|
+
// Export Function (Backward Compatible)
|
|
481
|
+
// =============================================================================
|
|
482
|
+
export async function showProjectsOverlay() {
|
|
483
|
+
return new ProjectsOverlay().show();
|
|
484
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Provider Interfaces
|
|
3
|
+
*
|
|
4
|
+
* Components implement these interfaces to provide content for rendering.
|
|
5
|
+
* The TerminalRenderer collects lines from providers and writes to terminal.
|
|
6
|
+
*
|
|
7
|
+
* Key principle: Providers return string[], never write to terminal directly.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Base interface for all content providers.
|
|
11
|
+
* Returns lines to render. Empty array = nothing to show.
|
|
12
|
+
*/
|
|
13
|
+
export interface ContentProvider {
|
|
14
|
+
/** Returns lines to render. Empty array = nothing to show */
|
|
15
|
+
getLines(): string[];
|
|
16
|
+
/** Optional: get cursor position within this component's content */
|
|
17
|
+
getCursorPosition?(): CursorPosition | null;
|
|
18
|
+
/** Optional: does this provider need keyboard input? */
|
|
19
|
+
hasKeyboardFocus?(): boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Cursor position within rendered content
|
|
23
|
+
*/
|
|
24
|
+
export interface CursorPosition {
|
|
25
|
+
/** Row within the provider's content (0-indexed) */
|
|
26
|
+
row: number;
|
|
27
|
+
/** Column within the row (0-indexed) */
|
|
28
|
+
col: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Menu options available in startup menu
|
|
32
|
+
*/
|
|
33
|
+
export type MenuOption = 'init' | 'projects' | 'continue' | 'chat' | 'config' | 'help' | 'exit';
|
|
34
|
+
/**
|
|
35
|
+
* Actions from menu keyboard handling
|
|
36
|
+
*/
|
|
37
|
+
export type MenuAction = 'up' | 'down' | 'select' | 'exit' | null;
|
|
38
|
+
/**
|
|
39
|
+
* Provider for the startup menu (MENU mode)
|
|
40
|
+
*/
|
|
41
|
+
export interface MenuProvider extends ContentProvider {
|
|
42
|
+
/** Returns full menu screen content */
|
|
43
|
+
getLines(): string[];
|
|
44
|
+
/** Handle keyboard input, return action taken */
|
|
45
|
+
handleKey(key: Buffer): MenuAction;
|
|
46
|
+
/** Get currently selected menu option */
|
|
47
|
+
getSelectedOption(): MenuOption;
|
|
48
|
+
/** Get highlighted index (for rendering) */
|
|
49
|
+
getHighlightedIndex(): number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Provider for subagent status display
|
|
53
|
+
*/
|
|
54
|
+
export interface SubagentProvider extends ContentProvider {
|
|
55
|
+
/** Returns subagent status lines (empty if no active subagents) */
|
|
56
|
+
getLines(): string[];
|
|
57
|
+
/** Check if any subagent is currently active */
|
|
58
|
+
hasActiveSubagent(): boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Provider for spinner/progress display
|
|
62
|
+
*/
|
|
63
|
+
export interface SpinnerProvider extends ContentProvider {
|
|
64
|
+
/** Returns spinner line when agent is running (empty when idle) */
|
|
65
|
+
getLines(): string[];
|
|
66
|
+
/** Check if agent is currently running */
|
|
67
|
+
isAgentRunning(): boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Provider for todo list display
|
|
71
|
+
*/
|
|
72
|
+
export interface TodoProvider extends ContentProvider {
|
|
73
|
+
/** Returns todo list (empty when agent running or no todos) */
|
|
74
|
+
getLines(): string[];
|
|
75
|
+
/** Get count of todos */
|
|
76
|
+
getTodoCount(): number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Provider for queued messages display
|
|
80
|
+
*/
|
|
81
|
+
export interface QueueProvider extends ContentProvider {
|
|
82
|
+
/** Returns queued messages (empty if none) */
|
|
83
|
+
getLines(): string[];
|
|
84
|
+
/** Get count of queued messages */
|
|
85
|
+
getQueueCount(): number;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Actions from input keyboard handling
|
|
89
|
+
*/
|
|
90
|
+
export type InputAction = {
|
|
91
|
+
type: 'submit';
|
|
92
|
+
value: string;
|
|
93
|
+
} | {
|
|
94
|
+
type: 'command';
|
|
95
|
+
value: string;
|
|
96
|
+
} | {
|
|
97
|
+
type: 'cancel';
|
|
98
|
+
} | {
|
|
99
|
+
type: 'change';
|
|
100
|
+
} | {
|
|
101
|
+
type: 'escape';
|
|
102
|
+
} | null;
|
|
103
|
+
/**
|
|
104
|
+
* Provider for input prompt
|
|
105
|
+
*/
|
|
106
|
+
export interface InputProvider extends ContentProvider {
|
|
107
|
+
/** Returns separator + input prompt lines (may be multi-line) */
|
|
108
|
+
getLines(): string[];
|
|
109
|
+
/** Get cursor position within input content */
|
|
110
|
+
getCursorPosition(): CursorPosition;
|
|
111
|
+
/** Handle keyboard input, return action taken */
|
|
112
|
+
handleKey(key: Buffer): InputAction;
|
|
113
|
+
/** Get current input value */
|
|
114
|
+
getValue(): string;
|
|
115
|
+
/** Set input value programmatically */
|
|
116
|
+
setValue(value: string): void;
|
|
117
|
+
/** Clear input */
|
|
118
|
+
clear(): void;
|
|
119
|
+
/** Is input in queue mode (typing queued while agent runs) */
|
|
120
|
+
isQueueMode(): boolean;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Provider for mode indicator line
|
|
124
|
+
*/
|
|
125
|
+
export interface ModeProvider extends ContentProvider {
|
|
126
|
+
/** Returns mode indicator line */
|
|
127
|
+
getLines(): string[];
|
|
128
|
+
/** Get current mode name */
|
|
129
|
+
getModeName(): string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Actions from overlay keyboard handling
|
|
133
|
+
*/
|
|
134
|
+
export type OverlayAction = {
|
|
135
|
+
type: 'close';
|
|
136
|
+
} | {
|
|
137
|
+
type: 'close';
|
|
138
|
+
result: unknown;
|
|
139
|
+
} | {
|
|
140
|
+
type: 'navigate';
|
|
141
|
+
direction: 'up' | 'down' | 'left' | 'right';
|
|
142
|
+
} | {
|
|
143
|
+
type: 'select';
|
|
144
|
+
} | {
|
|
145
|
+
type: 'scroll';
|
|
146
|
+
direction: 'up' | 'down';
|
|
147
|
+
} | {
|
|
148
|
+
type: 'tab';
|
|
149
|
+
direction: 'next' | 'prev';
|
|
150
|
+
} | null;
|
|
151
|
+
/**
|
|
152
|
+
* Provider for full-screen overlays
|
|
153
|
+
*/
|
|
154
|
+
export interface OverlayProvider extends ContentProvider {
|
|
155
|
+
/** Returns full overlay content */
|
|
156
|
+
getLines(): string[];
|
|
157
|
+
/** Check if overlay is active */
|
|
158
|
+
isActive(): boolean;
|
|
159
|
+
/** Handle keyboard input, return action taken */
|
|
160
|
+
handleKey(key: Buffer): OverlayAction;
|
|
161
|
+
/** Get cursor position if overlay needs visible cursor */
|
|
162
|
+
getCursorPosition(): CursorPosition | null;
|
|
163
|
+
/** Get overlay title (for header) */
|
|
164
|
+
getTitle(): string;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* All providers needed by TerminalRenderer
|
|
168
|
+
*/
|
|
169
|
+
export interface ProviderRegistry {
|
|
170
|
+
menu?: MenuProvider;
|
|
171
|
+
subagent?: SubagentProvider;
|
|
172
|
+
spinner?: SpinnerProvider;
|
|
173
|
+
todo?: TodoProvider;
|
|
174
|
+
queue?: QueueProvider;
|
|
175
|
+
input?: InputProvider;
|
|
176
|
+
mode?: ModeProvider;
|
|
177
|
+
overlay?: OverlayProvider;
|
|
178
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Provider Interfaces
|
|
3
|
+
*
|
|
4
|
+
* Components implement these interfaces to provide content for rendering.
|
|
5
|
+
* The TerminalRenderer collects lines from providers and writes to terminal.
|
|
6
|
+
*
|
|
7
|
+
* Key principle: Providers return string[], never write to terminal directly.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|