@pellux/goodvibes-tui 1.1.0 → 1.9.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/CHANGELOG.md +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
3
|
-
import { listHookPointContracts } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
4
|
-
import type { HookDispatcher } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
5
|
-
import type { HookPointContract } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
6
|
-
import type { HookActivityRecord, HookActivityTracker } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
7
|
-
import type { HookAuthoringAction, HookSimulationResult } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
8
|
-
import type { HookChain, HookDefinition } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
9
|
-
import type { HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
10
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
11
|
-
import {
|
|
12
|
-
type ConfirmState,
|
|
13
|
-
handleConfirmInput,
|
|
14
|
-
renderConfirmLines,
|
|
15
|
-
} from './confirm-state.ts';
|
|
16
|
-
import {
|
|
17
|
-
buildKeyValueLine,
|
|
18
|
-
buildKeyboardHints,
|
|
19
|
-
buildPanelLine,
|
|
20
|
-
buildStatusPill,
|
|
21
|
-
DEFAULT_PANEL_PALETTE,
|
|
22
|
-
resolveScrollablePanelSection,
|
|
23
|
-
} from './polish.ts';
|
|
24
|
-
|
|
25
|
-
// Base chrome only — title band, state colors, and text tokens all come
|
|
26
|
-
// straight from DEFAULT_PANEL_PALETTE (WO-002).
|
|
27
|
-
const C = DEFAULT_PANEL_PALETTE;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* WO-134: widened past read-only listing to also carry the three managed-hook
|
|
31
|
-
* mutation methods the panel drives directly (t=toggle, x=remove, s=simulate),
|
|
32
|
-
* mirroring the /hooks enable|disable|remove|simulate subcommands
|
|
33
|
-
* (hooks-runtime.ts). The raw `HookWorkbench` class instance passed at
|
|
34
|
-
* bootstrap already implements all three.
|
|
35
|
-
*/
|
|
36
|
-
export interface HooksPanelWorkbenchView {
|
|
37
|
-
listManagedHooks(): Array<{ pattern: string; hook: HookDefinition }>;
|
|
38
|
-
listManagedChains(): HookChain[];
|
|
39
|
-
listRecentActions(limit?: number): HookAuthoringAction[];
|
|
40
|
-
getLastSimulation(): HookSimulationResult | null;
|
|
41
|
-
getHooksFilePath(): string;
|
|
42
|
-
toggleManagedHook(name: string, enabled: boolean): boolean;
|
|
43
|
-
removeManagedEntry(name: string): boolean;
|
|
44
|
-
simulate(eventPath: string, payload?: Record<string, unknown>): HookSimulationResult;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface HooksPanelDataSource {
|
|
48
|
-
listContracts(): HookPointContract[];
|
|
49
|
-
listHooks(): Array<{ pattern: string; hook: HookDefinition }>;
|
|
50
|
-
listChains(): HookChain[];
|
|
51
|
-
listRecentActivity(limit?: number): readonly HookActivityRecord[];
|
|
52
|
-
getWorkbench(): HooksPanelWorkbenchView;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function createDefaultDataSource(
|
|
56
|
-
hookDispatcher: Pick<HookDispatcher, 'listHooks' | 'getChains'>,
|
|
57
|
-
hookWorkbench: HookWorkbench,
|
|
58
|
-
hookActivityTracker: Pick<HookActivityTracker, 'listRecent'>,
|
|
59
|
-
): HooksPanelDataSource {
|
|
60
|
-
return {
|
|
61
|
-
listContracts: () => listHookPointContracts(),
|
|
62
|
-
listHooks: () => hookDispatcher.listHooks(),
|
|
63
|
-
listChains: () => hookDispatcher.getChains(),
|
|
64
|
-
listRecentActivity: (limit = 3) => hookActivityTracker.listRecent(limit),
|
|
65
|
-
getWorkbench: () => hookWorkbench,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type HookEntry = { pattern: string; hook: HookDefinition };
|
|
70
|
-
|
|
71
|
-
export class HooksPanel extends ScrollableListPanel<HookEntry> {
|
|
72
|
-
private readonly dataSource: HooksPanelDataSource;
|
|
73
|
-
/** Pending confirm for 'x' remove — destructive, so it goes through the project-standard confirm contract. Subject is the managed hook name. */
|
|
74
|
-
private confirmAction: ConfirmState<string> | null = null;
|
|
75
|
-
/** Toggled by 'a' — widens the Recent Activity window from a short glance to a fuller scrollable review. */
|
|
76
|
-
private _activityExpanded = false;
|
|
77
|
-
|
|
78
|
-
public constructor(
|
|
79
|
-
hookDispatcher: Pick<HookDispatcher, 'listHooks' | 'getChains'>,
|
|
80
|
-
hookWorkbench: HookWorkbench,
|
|
81
|
-
hookActivityTracker: Pick<HookActivityTracker, 'listRecent'>,
|
|
82
|
-
dataSource: HooksPanelDataSource = createDefaultDataSource(hookDispatcher, hookWorkbench, hookActivityTracker),
|
|
83
|
-
) {
|
|
84
|
-
super('hooks', 'Hooks', '▨', 'automation-control');
|
|
85
|
-
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
86
|
-
this.filterEnabled = true;
|
|
87
|
-
this.filterLabel = 'Filter hooks';
|
|
88
|
-
this.dataSource = dataSource;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
protected override filterMatches(entry: HookEntry, q: string): boolean {
|
|
92
|
-
return (entry.hook.name ?? '').toLowerCase().includes(q)
|
|
93
|
-
|| entry.pattern.toLowerCase().includes(q)
|
|
94
|
-
|| entry.hook.type.toLowerCase().includes(q);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
protected override getPalette() { return C; }
|
|
98
|
-
protected override getEmptyStateMessage() { return ' No hooks are currently registered.'; }
|
|
99
|
-
protected override getEmptyStateActions() {
|
|
100
|
-
return [
|
|
101
|
-
{ command: '/hooks', summary: 'review hook contracts and managed authoring actions' },
|
|
102
|
-
{ command: '/settings', summary: 'review hook/runtime behavior in the settings surface' },
|
|
103
|
-
];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
protected getItems(): readonly HookEntry[] {
|
|
107
|
-
return this.dataSource.listHooks();
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
protected renderItem(entry: HookEntry, index: number, selected: boolean, width: number): Line {
|
|
111
|
-
const bg = selected ? C.selectBg : undefined;
|
|
112
|
-
return buildPanelLine(width, [
|
|
113
|
-
[' ', C.label, bg],
|
|
114
|
-
[truncateDisplay(entry.hook.name ?? '(unnamed)', 20).padEnd(20), C.value, bg],
|
|
115
|
-
[` ${truncateDisplay(entry.pattern, 28).padEnd(28)}`, C.info, bg],
|
|
116
|
-
...buildStatusPill(entry.hook.enabled === false ? 'warn' : 'good', ` ${(entry.hook.enabled === false ? 'DISABLED' : 'ENABLED').padEnd(8)}`, { bg }),
|
|
117
|
-
[` ${entry.hook.type}`, C.dim, bg],
|
|
118
|
-
]);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
public handleInput(key: string): boolean {
|
|
122
|
-
if (this.lastError !== null) this.clearError();
|
|
123
|
-
|
|
124
|
-
if (this.confirmAction) {
|
|
125
|
-
const result = handleConfirmInput(this.confirmAction, key);
|
|
126
|
-
if (result === 'confirmed') {
|
|
127
|
-
this._removeManaged(this.confirmAction.subject);
|
|
128
|
-
this.confirmAction = null;
|
|
129
|
-
this.markDirty();
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
if (result === 'cancelled') {
|
|
133
|
-
this.confirmAction = null;
|
|
134
|
-
this.markDirty();
|
|
135
|
-
}
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (!this.filterActive) {
|
|
140
|
-
switch (key) {
|
|
141
|
-
case 'r':
|
|
142
|
-
this.markDirty();
|
|
143
|
-
return true;
|
|
144
|
-
case 't':
|
|
145
|
-
this._toggleSelected();
|
|
146
|
-
return true;
|
|
147
|
-
case 'x':
|
|
148
|
-
this._requestRemove();
|
|
149
|
-
return true;
|
|
150
|
-
case 's':
|
|
151
|
-
this._simulateSelected();
|
|
152
|
-
return true;
|
|
153
|
-
case 'a':
|
|
154
|
-
this._activityExpanded = !this._activityExpanded;
|
|
155
|
-
this.markDirty();
|
|
156
|
-
return true;
|
|
157
|
-
default:
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return super.handleInput(key);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
private _selectedEntry(): HookEntry | undefined {
|
|
166
|
-
return this.getSelectedItem();
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* WO-134: t=toggle — mirrors `/hooks enable|disable <name>` (hooks-runtime.ts:64-77),
|
|
171
|
-
* flipping the selected hook's managed enabled state via workbench.toggleManagedHook.
|
|
172
|
-
*/
|
|
173
|
-
private _toggleSelected(): void {
|
|
174
|
-
const entry = this._selectedEntry();
|
|
175
|
-
if (!entry) return;
|
|
176
|
-
const name = entry.hook.name;
|
|
177
|
-
if (!name) {
|
|
178
|
-
this.setError('This hook has no managed name to toggle.');
|
|
179
|
-
this.markDirty();
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
const nextEnabled = entry.hook.enabled === false;
|
|
183
|
-
const changed = this.dataSource.getWorkbench().toggleManagedHook(name, nextEnabled);
|
|
184
|
-
if (!changed) this.setError(`No managed hook named '${name}'.`);
|
|
185
|
-
this.markDirty();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
private _requestRemove(): void {
|
|
189
|
-
const entry = this._selectedEntry();
|
|
190
|
-
if (!entry) return;
|
|
191
|
-
const name = entry.hook.name;
|
|
192
|
-
if (!name) {
|
|
193
|
-
this.setError('This hook has no managed name to remove.');
|
|
194
|
-
this.markDirty();
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
this.confirmAction = { subject: name, label: name, verb: 'Remove' };
|
|
198
|
-
this.markDirty();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
private _removeManaged(name: string): void {
|
|
202
|
-
const removed = this.dataSource.getWorkbench().removeManagedEntry(name);
|
|
203
|
-
if (!removed) this.setError(`No managed hook or chain named '${name}'.`);
|
|
204
|
-
this.markDirty();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* WO-134: s=simulate — mirrors `/hooks simulate <eventPath>` (hooks-runtime.ts:78-93)
|
|
209
|
-
* using the selected hook's own pattern as the simulated event path. The
|
|
210
|
-
* result lands in workbench.getLastSimulation(), already rendered below.
|
|
211
|
-
*/
|
|
212
|
-
private _simulateSelected(): void {
|
|
213
|
-
const entry = this._selectedEntry();
|
|
214
|
-
if (!entry) return;
|
|
215
|
-
this.dataSource.getWorkbench().simulate(entry.pattern);
|
|
216
|
-
this.markDirty();
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
public render(width: number, height: number): Line[] {
|
|
220
|
-
this.clampSelection();
|
|
221
|
-
const hooks = this.dataSource.listHooks();
|
|
222
|
-
const contracts = this.dataSource.listContracts();
|
|
223
|
-
const chains = this.dataSource.listChains();
|
|
224
|
-
// Fixed-window stats stay stable regardless of the 'a' expand toggle below;
|
|
225
|
-
// only the displayed activity rows grow when expanded.
|
|
226
|
-
const recentActivityForStats = this.dataSource.listRecentActivity(3);
|
|
227
|
-
const activityLimit = this._activityExpanded ? 20 : 3;
|
|
228
|
-
const recentActivity = this.dataSource.listRecentActivity(activityLimit);
|
|
229
|
-
const workbench = this.dataSource.getWorkbench();
|
|
230
|
-
const managedHooks = workbench.listManagedHooks();
|
|
231
|
-
const managedChains = workbench.listManagedChains();
|
|
232
|
-
const recentAuthoring = workbench.listRecentActions(3);
|
|
233
|
-
const lastSimulation = workbench.getLastSimulation();
|
|
234
|
-
const intro = 'Hook contracts, active registrations, managed authoring, recent runtime activity, and simulation matches.';
|
|
235
|
-
|
|
236
|
-
const selected = this.getSelectedItem();
|
|
237
|
-
const contract = selected ? contracts.find((c) => c.pattern === selected.pattern) : undefined;
|
|
238
|
-
|
|
239
|
-
const detailLines: Line[] = [];
|
|
240
|
-
if (selected) {
|
|
241
|
-
detailLines.push(buildPanelLine(width, [
|
|
242
|
-
[' Hook: ', C.label],
|
|
243
|
-
[selected.hook.name ?? '(unnamed)', C.value],
|
|
244
|
-
[' Type: ', C.label],
|
|
245
|
-
[selected.hook.type, C.info],
|
|
246
|
-
[' Match: ', C.label],
|
|
247
|
-
[selected.hook.matcher ?? selected.hook.match, C.value],
|
|
248
|
-
]));
|
|
249
|
-
detailLines.push(buildPanelLine(width, [
|
|
250
|
-
[' Pattern: ', C.label],
|
|
251
|
-
[truncateDisplay(selected.pattern, Math.max(0, width - 12)), C.value],
|
|
252
|
-
]));
|
|
253
|
-
if (contract) {
|
|
254
|
-
detailLines.push(buildPanelLine(width, [
|
|
255
|
-
[' Contract: ', C.label],
|
|
256
|
-
[`${contract.authority} / ${contract.executionMode}`, C.info],
|
|
257
|
-
[' Policy: ', C.label],
|
|
258
|
-
[contract.failurePolicy, C.value],
|
|
259
|
-
]));
|
|
260
|
-
detailLines.push(buildPanelLine(width, [
|
|
261
|
-
[' Capabilities: ', C.label],
|
|
262
|
-
[`deny=${contract.canDeny ? 'yes' : 'no'} mutate=${contract.canMutateInput ? 'yes' : 'no'} inject=${contract.canInjectContext ? 'yes' : 'no'}`, C.dim],
|
|
263
|
-
]));
|
|
264
|
-
} else {
|
|
265
|
-
detailLines.push(buildPanelLine(width, [[' Contract: No exact contract registered for this pattern.', C.warn]]));
|
|
266
|
-
}
|
|
267
|
-
detailLines.push(buildPanelLine(width, [
|
|
268
|
-
[' Summary: ', C.label],
|
|
269
|
-
[`hooks=${hooks.length} chains=${chains.length} contracts=${contracts.length} managed=${managedHooks.length}/${managedChains.length}`, C.dim],
|
|
270
|
-
]));
|
|
271
|
-
detailLines.push(buildPanelLine(width, [
|
|
272
|
-
[' Hooks file: ', C.label],
|
|
273
|
-
[truncateDisplay(workbench.getHooksFilePath(), Math.max(0, width - 15)), C.dim],
|
|
274
|
-
]));
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const activityRows: Line[] = recentActivity.length === 0
|
|
278
|
-
? [buildPanelLine(width, [[' No hook activity recorded yet.', C.empty]])]
|
|
279
|
-
: recentActivity.map((record) => {
|
|
280
|
-
const color = !record.ok ? C.bad : record.decision === 'deny' ? C.warn : C.good;
|
|
281
|
-
return buildPanelLine(width, [
|
|
282
|
-
[' ', C.label],
|
|
283
|
-
[truncateDisplay(record.hookName, 18).padEnd(18), C.value],
|
|
284
|
-
[' ', C.label],
|
|
285
|
-
[truncateDisplay(record.path, 26).padEnd(26), C.info],
|
|
286
|
-
[' ', C.label],
|
|
287
|
-
[record.ok ? (record.decision ?? 'ok') : 'error', color],
|
|
288
|
-
]);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// WO-134: 'a' expands the activity window from a short glance (3 rows) to
|
|
292
|
-
// a fuller scrollable review (up to 20); resolveScrollablePanelSection
|
|
293
|
-
// appends a "+N more" summary row when even the expanded fetch overflows
|
|
294
|
-
// the available space, so the list never silently truncates unlabeled.
|
|
295
|
-
const activityLines: readonly Line[] = resolveScrollablePanelSection(width, height, {
|
|
296
|
-
palette: C,
|
|
297
|
-
section: {
|
|
298
|
-
scrollableLines: activityRows,
|
|
299
|
-
scrollOffset: 0,
|
|
300
|
-
minRows: this._activityExpanded ? 8 : 3,
|
|
301
|
-
appendWindowSummary: recentActivity.length > 0 ? { dimColor: C.dim } : undefined,
|
|
302
|
-
},
|
|
303
|
-
}).section.lines;
|
|
304
|
-
|
|
305
|
-
const authoringLines: Line[] = recentAuthoring.length === 0
|
|
306
|
-
? [buildPanelLine(width, [[' No managed hook authoring actions recorded yet.', C.empty]])]
|
|
307
|
-
: recentAuthoring.map((action) => buildPanelLine(width, [
|
|
308
|
-
[' ', C.label],
|
|
309
|
-
[truncateDisplay(action.kind, 14).padEnd(14), C.info],
|
|
310
|
-
[' ', C.label],
|
|
311
|
-
[truncateDisplay(action.target, Math.max(0, width - 20)), C.dim],
|
|
312
|
-
]));
|
|
313
|
-
if (lastSimulation) {
|
|
314
|
-
authoringLines.push(buildPanelLine(width, [
|
|
315
|
-
[' Last Simulation: ', C.label],
|
|
316
|
-
[truncateDisplay(lastSimulation.eventPath, Math.max(0, width - 20)), C.value],
|
|
317
|
-
]));
|
|
318
|
-
authoringLines.push(buildPanelLine(width, [
|
|
319
|
-
[' Matches: ', C.label],
|
|
320
|
-
[`hooks=${lastSimulation.matchedHooks.length} chains=${lastSimulation.matchedChains.length}`, C.dim],
|
|
321
|
-
]));
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Empty state: show extra context lines (hooks file, contracts, authoring) before base empty state
|
|
325
|
-
if (hooks.length === 0) {
|
|
326
|
-
const extraHeader: Line[] = [
|
|
327
|
-
buildPanelLine(width, [
|
|
328
|
-
[' Contracts: ', C.label],
|
|
329
|
-
[String(contracts.length), C.value],
|
|
330
|
-
[' Chains: ', C.label],
|
|
331
|
-
[String(chains.length), C.value],
|
|
332
|
-
[' Managed: ', C.label],
|
|
333
|
-
[String(managedHooks.length), C.info],
|
|
334
|
-
]),
|
|
335
|
-
buildPanelLine(width, [
|
|
336
|
-
[' Hooks file: ', C.label],
|
|
337
|
-
[truncateDisplay(workbench.getHooksFilePath(), Math.max(0, width - 15)), C.dim],
|
|
338
|
-
]),
|
|
339
|
-
];
|
|
340
|
-
if (recentAuthoring.length > 0) {
|
|
341
|
-
extraHeader.push(buildPanelLine(width, [
|
|
342
|
-
[' Authoring: ', C.label],
|
|
343
|
-
[truncateDisplay(`${recentAuthoring[0]!.kind} ${recentAuthoring[0]!.target}`, Math.max(0, width - 14)), C.info],
|
|
344
|
-
]));
|
|
345
|
-
}
|
|
346
|
-
if (lastSimulation) {
|
|
347
|
-
extraHeader.push(buildPanelLine(width, [
|
|
348
|
-
[' Last Simulation: ', C.label],
|
|
349
|
-
[truncateDisplay(lastSimulation.eventPath, Math.max(0, width - 20)), C.value],
|
|
350
|
-
]));
|
|
351
|
-
}
|
|
352
|
-
return this.renderList(width, height, {
|
|
353
|
-
title: 'Hooks Control Room',
|
|
354
|
-
header: extraHeader,
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Summary header — surface registration + activity counts first so the most
|
|
359
|
-
// important "what's firing" signal is visible without scrolling to the footer.
|
|
360
|
-
const denials = recentActivityForStats.filter((r) => r.ok && r.decision === 'deny').length;
|
|
361
|
-
const errors = recentActivityForStats.filter((r) => !r.ok).length;
|
|
362
|
-
const headerLines: Line[] = [
|
|
363
|
-
buildKeyValueLine(width, [
|
|
364
|
-
{ label: 'hooks', value: String(hooks.length), valueColor: C.info },
|
|
365
|
-
{ label: 'chains', value: String(chains.length), valueColor: C.value },
|
|
366
|
-
{ label: 'contracts', value: String(contracts.length), valueColor: C.value },
|
|
367
|
-
{ label: 'recent denials', value: String(denials), valueColor: denials > 0 ? C.warn : C.dim },
|
|
368
|
-
{ label: 'errors', value: String(errors), valueColor: errors > 0 ? C.bad : C.dim },
|
|
369
|
-
], C),
|
|
370
|
-
];
|
|
371
|
-
|
|
372
|
-
const hints = this.filterActive
|
|
373
|
-
? [{ keys: 'type', label: 'filter' }, { keys: 'Enter', label: 'apply' }, { keys: 'Esc', label: 'clear' }]
|
|
374
|
-
: [
|
|
375
|
-
{ keys: 'Up/Down', label: 'move' },
|
|
376
|
-
{ keys: 't', label: 'toggle' },
|
|
377
|
-
{ keys: 'x', label: 'remove' },
|
|
378
|
-
{ keys: 's', label: 'simulate' },
|
|
379
|
-
{ keys: 'a', label: this._activityExpanded ? 'collapse activity' : 'expand activity' },
|
|
380
|
-
{ keys: 'r', label: 'refresh' },
|
|
381
|
-
{ keys: '/', label: 'filter' },
|
|
382
|
-
];
|
|
383
|
-
|
|
384
|
-
return this.renderList(width, height, {
|
|
385
|
-
title: 'Hooks Control Room',
|
|
386
|
-
header: headerLines,
|
|
387
|
-
footer: [
|
|
388
|
-
...detailLines,
|
|
389
|
-
buildPanelLine(width, [
|
|
390
|
-
[' Recent Activity', C.label],
|
|
391
|
-
[this._activityExpanded ? ' (a to collapse)' : ' (a to expand)', C.dim],
|
|
392
|
-
]),
|
|
393
|
-
...activityLines,
|
|
394
|
-
buildPanelLine(width, [[' Authoring', C.label]]),
|
|
395
|
-
...authoringLines,
|
|
396
|
-
...(this.confirmAction ? renderConfirmLines(width, this.confirmAction) : []),
|
|
397
|
-
buildKeyboardHints(width, hints, C),
|
|
398
|
-
],
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
}
|