@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
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D7a Layer 1 (TUI wiring) — `goodvibes-daemon install-service | uninstall-service
|
|
3
|
+
* | service-status`.
|
|
4
|
+
*
|
|
5
|
+
* The daemon is a SYSTEM SERVICE. These subcommands install it as a durable host
|
|
6
|
+
* service (systemd user unit on Linux, launchd agent on macOS, a Scheduled Task
|
|
7
|
+
* on Windows) so N surfaces share ONE daemon that survives reboots, instead of
|
|
8
|
+
* every surface spawning a session-scoped daemon.
|
|
9
|
+
*
|
|
10
|
+
* W3 drift note: the SDK's D7a-era `systemd-user-service.ts` (a Linux-only,
|
|
11
|
+
* bespoke systemd shim) was dead code and got deleted in SDK W3-S5. The SDK's
|
|
12
|
+
* REAL wired service machinery — reached in production by the daemon's own HTTP
|
|
13
|
+
* `/api/service/*` routes via facade-composition.ts — is
|
|
14
|
+
* `PlatformServiceManager` (`@pellux/goodvibes-sdk/platform/daemon`): a single
|
|
15
|
+
* systemd/launchd/windows-aware manager with install/uninstall/status/start/
|
|
16
|
+
* stop/restart and a `suggestedCommands` hint list. This module now rewires the
|
|
17
|
+
* three CLI subcommands onto that manager instead of the deleted shim.
|
|
18
|
+
*
|
|
19
|
+
* Two real behavioral differences from the old shim, called out honestly:
|
|
20
|
+
* - `install()` only WRITES the unit/plist/task; it does not enable/start it.
|
|
21
|
+
* So `install-service` here calls `install()` then `start()` to preserve the
|
|
22
|
+
* old "install implies enabled + running" behavior.
|
|
23
|
+
* - `uninstall()` only REMOVES the unit file; there is no manager-level
|
|
24
|
+
* `disable` verb (only start/stop/restart exist). So `uninstall-service`
|
|
25
|
+
* calls `stop()` then `uninstall()`, and honestly tells the caller that a
|
|
26
|
+
* stray "enabled" symlink may remain until `systemctl --user daemon-reload`
|
|
27
|
+
* (offered back as a suggested follow-up) or the next login cleans it up.
|
|
28
|
+
*
|
|
29
|
+
* W4-D1: a fourth subcommand, `migrate-service`, closes the Wave-3 "detect and
|
|
30
|
+
* disclose only" inheritance (W3 Finding 4, below) with a GUIDED, CONSENTED
|
|
31
|
+
* takeover of the legacy `goodvibes-daemon.service` unit. Design constraints,
|
|
32
|
+
* all load-bearing:
|
|
33
|
+
* - NEVER auto-migrate. Without explicit consent (`confirmMigration`, wired
|
|
34
|
+
* from the CLI's existing `-y`/`--yes` flag) this prints the exact plan
|
|
35
|
+
* and touches nothing.
|
|
36
|
+
* - NEW-UP-THEN-OLD-DOWN. The new `goodvibes.service` unit is installed,
|
|
37
|
+
* started, and verified healthy (a fresh `status().running` read, which
|
|
38
|
+
* honestly queries systemd via the injected actionRunner) BEFORE the
|
|
39
|
+
* legacy unit is stopped, disabled, or removed. A failed or unhealthy new
|
|
40
|
+
* unit rolls itself back (uninstalled) and never touches the legacy one
|
|
41
|
+
* — a botched takeover must never cost the user their working daemon.
|
|
42
|
+
* - ADOPT-OR-WARN, NEVER KILL. If the legacy unit file is simply absent but
|
|
43
|
+
* something is already listening on the configured host:port (Mike's real
|
|
44
|
+
* dev-host case: a manual `nohup`'d daemon with no unit at all), this is
|
|
45
|
+
* an unidentified process, not a managed unit — there is nothing to stop
|
|
46
|
+
* or disable, and this module will not attempt to kill it. It warns and
|
|
47
|
+
* leaves the decision to the operator.
|
|
48
|
+
* - Every action (legacy stop/disable, unit-file removal, daemon-reload)
|
|
49
|
+
* goes through the SAME injectable `actionRunner`/`legacyUnitFileRemove`
|
|
50
|
+
* seams tests already use — this module never has a code path that bypasses
|
|
51
|
+
* them, so the migration is exercised deterministically via fakes and never
|
|
52
|
+
* touches a real running service in tests.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
56
|
+
import { PlatformServiceManager, type ManagedServiceStatus } from '@pellux/goodvibes-sdk/platform/daemon';
|
|
57
|
+
import {
|
|
58
|
+
buildManagedDaemonServiceManager,
|
|
59
|
+
detectLegacyUnit,
|
|
60
|
+
legacyUnitNote,
|
|
61
|
+
resolveManagedUnitName,
|
|
62
|
+
runLegacyDaemonMigration,
|
|
63
|
+
LEGACY_SERVICE_UNIT_NAME,
|
|
64
|
+
MANAGED_SERVICE_NAME as SERVICE_NAME,
|
|
65
|
+
MANAGED_SERVICE_DESCRIPTION as SERVICE_DESCRIPTION,
|
|
66
|
+
type LegacyUnitInfo,
|
|
67
|
+
type ManagedServiceActionRunner,
|
|
68
|
+
} from '../runtime/legacy-daemon-migration.ts';
|
|
69
|
+
|
|
70
|
+
// `resolveInstalledDaemonBinary` lives in the runtime module (shared with the
|
|
71
|
+
// onboarding guided UX) — re-exported here so this module stays the CLI's
|
|
72
|
+
// stable public surface (and so existing test imports keep working).
|
|
73
|
+
export {
|
|
74
|
+
resolveInstalledDaemonBinary,
|
|
75
|
+
type ResolveDaemonBinaryOptions,
|
|
76
|
+
} from '../runtime/legacy-daemon-migration.ts';
|
|
77
|
+
export type { ManagedServiceActionRunner } from '../runtime/legacy-daemon-migration.ts';
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// W3 Finding 4: legacy-unit detection. W4-D1: guided migration.
|
|
81
|
+
//
|
|
82
|
+
// The prior D7a-era command installed the daemon's systemd unit under the
|
|
83
|
+
// literal name `goodvibes-daemon.service`. This module (rewired onto
|
|
84
|
+
// PlatformServiceManager, see the file banner above) manages a DIFFERENT
|
|
85
|
+
// unit name (`goodvibes`, SERVICE_NAME). A host that still has the legacy
|
|
86
|
+
// unit installed — this dev machine included — would otherwise see
|
|
87
|
+
// service-status honestly report installed:false/running:false for the
|
|
88
|
+
// tracked name while the legacy unit keeps running untouched underneath it,
|
|
89
|
+
// uninstall-service would silently leave the legacy unit orphaned with no
|
|
90
|
+
// mention, and install-service could start a SECOND daemon competing for the
|
|
91
|
+
// same port.
|
|
92
|
+
//
|
|
93
|
+
// The detection (`detectLegacyUnit`/`legacyUnitNote`) and the guided
|
|
94
|
+
// migration engine (`runLegacyDaemonMigration`) both live in
|
|
95
|
+
// `../runtime/legacy-daemon-migration.ts` rather than here, so the TUI's
|
|
96
|
+
// onboarding UX (`src/input/handler-onboarding-daemon-adopt.ts`) can reuse
|
|
97
|
+
// them directly — the architecture gate forbids `src/input/**` from
|
|
98
|
+
// importing `src/daemon/**` (input must stay entrypoint-agnostic), so the
|
|
99
|
+
// shared engine lives in the entrypoint-agnostic `runtime` layer instead and
|
|
100
|
+
// this CLI module is just one of its two consumers.
|
|
101
|
+
//
|
|
102
|
+
// This detection is entirely independent of PlatformServiceManager's own
|
|
103
|
+
// status() — it does not rely on (or get invalidated by) the parallel SDK
|
|
104
|
+
// fix that makes status().running itself query systemd honestly for the
|
|
105
|
+
// TRACKED unit name.
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
export type DaemonServiceSubcommand = 'install-service' | 'uninstall-service' | 'service-status' | 'migrate-service';
|
|
109
|
+
|
|
110
|
+
export function isDaemonServiceSubcommand(value: string | undefined): value is DaemonServiceSubcommand {
|
|
111
|
+
return value === 'install-service' || value === 'uninstall-service' || value === 'service-status' || value === 'migrate-service';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface DaemonServiceCliInput {
|
|
115
|
+
readonly subcommand: DaemonServiceSubcommand;
|
|
116
|
+
readonly binaryPath: string;
|
|
117
|
+
readonly homeDir: string;
|
|
118
|
+
readonly host: string;
|
|
119
|
+
readonly port: number;
|
|
120
|
+
/** Defaults to `homeDir` — overridable so tests can scope both to one tempdir. */
|
|
121
|
+
readonly workingDirectory?: string | undefined;
|
|
122
|
+
/** Injected in tests; a real `ConfigManager` rooted at `homeDir` otherwise. */
|
|
123
|
+
readonly configManager?: ConfigManager | undefined;
|
|
124
|
+
/** Injectable systemctl/launchctl/schtasks runner so tests never touch the host. */
|
|
125
|
+
readonly actionRunner?: ManagedServiceActionRunner | undefined;
|
|
126
|
+
/** Injectable existsSync for the legacy-unit file check (W3 Finding 4) so tests never touch the host filesystem. */
|
|
127
|
+
readonly legacyUnitFileExists?: ((path: string) => boolean) | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* `migrate-service` only: explicit consent to actually execute the
|
|
130
|
+
* migration (wired from the CLI's `-y`/`--yes` flag). Without it, the
|
|
131
|
+
* subcommand prints the exact plan and changes nothing — never auto-migrate.
|
|
132
|
+
*/
|
|
133
|
+
readonly confirmMigration?: boolean | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* `migrate-service` only: injectable port-liveness check for the
|
|
136
|
+
* legacy-absent branch (never a raw network call in tests). Defaults to a
|
|
137
|
+
* real, read-only TCP connect attempt.
|
|
138
|
+
*/
|
|
139
|
+
readonly portProbe?: ((host: string, port: number) => boolean | Promise<boolean>) | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* `migrate-service` only: injectable removal of the legacy unit file, so
|
|
142
|
+
* tests never call a raw fs op against a real path. Defaults to a real
|
|
143
|
+
* `rmSync`.
|
|
144
|
+
*/
|
|
145
|
+
readonly legacyUnitFileRemove?: ((path: string) => void) | undefined;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface DaemonServiceCliResult {
|
|
149
|
+
readonly ok: boolean;
|
|
150
|
+
readonly exitCode: number;
|
|
151
|
+
/** Honest, human-readable stdout lines describing exactly what happened. */
|
|
152
|
+
readonly lines: readonly string[];
|
|
153
|
+
readonly status: ManagedServiceStatus;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The manager's definition (`ExecStart` command/args, name, description) is
|
|
158
|
+
* built once, in `../runtime/legacy-daemon-migration.ts`, and shared with the
|
|
159
|
+
* onboarding guided UX — see that module's doc comment for why.
|
|
160
|
+
*/
|
|
161
|
+
function buildManager(input: DaemonServiceCliInput): PlatformServiceManager {
|
|
162
|
+
return buildManagedDaemonServiceManager({
|
|
163
|
+
binaryPath: input.binaryPath,
|
|
164
|
+
homeDir: input.homeDir,
|
|
165
|
+
host: input.host,
|
|
166
|
+
port: input.port,
|
|
167
|
+
workingDirectory: input.workingDirectory,
|
|
168
|
+
configManager: input.configManager,
|
|
169
|
+
actionRunner: input.actionRunner,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function statusLines(status: ManagedServiceStatus): string[] {
|
|
174
|
+
const lines = [
|
|
175
|
+
`platform: ${status.platform}`,
|
|
176
|
+
`installed: ${status.installed}`,
|
|
177
|
+
`running: ${status.running}`,
|
|
178
|
+
`unit path: ${status.path}`,
|
|
179
|
+
];
|
|
180
|
+
if (status.pid !== undefined) lines.push(`pid: ${status.pid}`);
|
|
181
|
+
if (status.platform !== 'manual' && status.installed && !status.running) {
|
|
182
|
+
// W3 Finding 4: this used to assert "'running' here only reflects
|
|
183
|
+
// processes this tool started directly" — true for the pid-file-only
|
|
184
|
+
// check the (currently linked) SDK still uses, but the parallel SDK
|
|
185
|
+
// batch is making status().running query systemd honestly via
|
|
186
|
+
// `is-active`, which would make that specific claim stale. Drop the
|
|
187
|
+
// claim about HOW running was computed and just offer the escape
|
|
188
|
+
// hatch — true and useful under either SDK version.
|
|
189
|
+
lines.push(
|
|
190
|
+
`note: if this looks wrong, verify directly: ${status.suggestedCommands[status.suggestedCommands.length - 1] ?? 'the platform service-status command'}`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return lines;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Build the install-branch's result lines. Exported for direct unit
|
|
198
|
+
* coverage: the "suggested follow-ups" block is gated on the actual
|
|
199
|
+
* not-started case (W3 Finding 4 friction fix) rather than printed
|
|
200
|
+
* unconditionally, and driving that through the full
|
|
201
|
+
* PlatformServiceManager/systemd integration can't reliably produce
|
|
202
|
+
* `running: true` under the (currently linked) SDK build, whose systemd
|
|
203
|
+
* status check is still pid-file-only (see the honesty note above).
|
|
204
|
+
*/
|
|
205
|
+
export function buildInstallResultLines(status: ManagedServiceStatus): string[] {
|
|
206
|
+
const lines = [`installed the ${status.platform} service at ${status.path}`];
|
|
207
|
+
if (status.running) {
|
|
208
|
+
lines.push('service is enabled and running');
|
|
209
|
+
} else {
|
|
210
|
+
lines.push('suggested follow-ups if it did not start automatically:');
|
|
211
|
+
for (const cmd of status.suggestedCommands) lines.push(` ${cmd}`);
|
|
212
|
+
}
|
|
213
|
+
return lines;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function ok(action: 'install' | 'uninstall' | 'status', status: ManagedServiceStatus, extra: string[] = []): DaemonServiceCliResult {
|
|
217
|
+
const lines: string[] = [];
|
|
218
|
+
if (action === 'install') {
|
|
219
|
+
lines.push(...buildInstallResultLines(status));
|
|
220
|
+
} else if (action === 'uninstall') {
|
|
221
|
+
lines.push(`removed the ${status.platform} service at ${status.path}`);
|
|
222
|
+
if (status.platform === 'systemd') {
|
|
223
|
+
lines.push(
|
|
224
|
+
"note: this removes the unit file but does not run `disable` — run " +
|
|
225
|
+
"`systemctl --user daemon-reload` to clear any stale enablement symlink.",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
lines.push(...statusLines(status));
|
|
230
|
+
}
|
|
231
|
+
lines.push(...extra);
|
|
232
|
+
return { ok: true, exitCode: 0, lines, status };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function failed(action: 'install' | 'uninstall' | 'status', status: ManagedServiceStatus): DaemonServiceCliResult {
|
|
236
|
+
return {
|
|
237
|
+
ok: false,
|
|
238
|
+
exitCode: 1,
|
|
239
|
+
lines: [`service ${action} failed: ${status.actionError ?? 'unknown error'}`],
|
|
240
|
+
status,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* `migrate-service`: the guided, consented takeover of the legacy
|
|
246
|
+
* `goodvibes-daemon.service` unit (W4-D1). Thin wrapper over
|
|
247
|
+
* `runLegacyDaemonMigration` (`../runtime/legacy-daemon-migration.ts`) — see
|
|
248
|
+
* that module for the design constraints (never auto-migrate,
|
|
249
|
+
* new-up-then-old-down, adopt-or-warn/never kill an unrecognized process,
|
|
250
|
+
* every action through an injectable seam) and the onboarding UX consumer.
|
|
251
|
+
*/
|
|
252
|
+
function runMigrateService(
|
|
253
|
+
input: DaemonServiceCliInput,
|
|
254
|
+
manager: PlatformServiceManager,
|
|
255
|
+
legacy: LegacyUnitInfo,
|
|
256
|
+
): Promise<DaemonServiceCliResult> {
|
|
257
|
+
return runLegacyDaemonMigration(
|
|
258
|
+
{
|
|
259
|
+
host: input.host,
|
|
260
|
+
port: input.port,
|
|
261
|
+
trackedServiceName: SERVICE_NAME,
|
|
262
|
+
confirmMigration: input.confirmMigration,
|
|
263
|
+
portProbe: input.portProbe,
|
|
264
|
+
legacyUnitFileRemove: input.legacyUnitFileRemove,
|
|
265
|
+
actionRunner: input.actionRunner,
|
|
266
|
+
},
|
|
267
|
+
manager,
|
|
268
|
+
legacy,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Dispatch a daemon service subcommand to the SDK's `PlatformServiceManager`. */
|
|
273
|
+
export async function runDaemonServiceCli(input: DaemonServiceCliInput): Promise<DaemonServiceCliResult> {
|
|
274
|
+
const manager = buildManager(input);
|
|
275
|
+
const legacy = detectLegacyUnit(input);
|
|
276
|
+
switch (input.subcommand) {
|
|
277
|
+
case 'migrate-service':
|
|
278
|
+
return runMigrateService(input, manager, legacy);
|
|
279
|
+
case 'install-service': {
|
|
280
|
+
// W3 Finding 4: refuse rather than risk starting a second daemon
|
|
281
|
+
// alongside an already-installed legacy unit. Refuses whenever the
|
|
282
|
+
// legacy unit is present at all (not just when currently active) —
|
|
283
|
+
// an installed-but-inactive legacy unit can still be enabled and
|
|
284
|
+
// start competing for the same port later, and "never silently
|
|
285
|
+
// start a second daemon" is the bar here, not "never right now."
|
|
286
|
+
if (legacy.present) {
|
|
287
|
+
const status = manager.status();
|
|
288
|
+
const resolvedName = resolveManagedUnitName(status);
|
|
289
|
+
return {
|
|
290
|
+
ok: false,
|
|
291
|
+
exitCode: 1,
|
|
292
|
+
lines: [
|
|
293
|
+
`service install refused: a service is already installed under the legacy name ${LEGACY_SERVICE_UNIT_NAME}.service.`,
|
|
294
|
+
legacyUnitNote(legacy, resolvedName),
|
|
295
|
+
`Installing this tool's ${resolvedName}.service alongside it risks two daemons competing for the same port.`,
|
|
296
|
+
],
|
|
297
|
+
status,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const installed = manager.install();
|
|
301
|
+
if (installed.actionError) return failed('install', installed);
|
|
302
|
+
const started = manager.start();
|
|
303
|
+
// start()'s actionError (e.g. a platform this manager can't dispatch
|
|
304
|
+
// actions for) doesn't undo the write — report install as ok, but surface
|
|
305
|
+
// the follow-up problem honestly instead of claiming it is running.
|
|
306
|
+
return started.actionError
|
|
307
|
+
? ok('install', { ...started, running: false }, [`could not start it automatically: ${started.actionError}`])
|
|
308
|
+
: ok('install', started);
|
|
309
|
+
}
|
|
310
|
+
case 'uninstall-service': {
|
|
311
|
+
const stopped = manager.stop();
|
|
312
|
+
const uninstalled = manager.uninstall();
|
|
313
|
+
if (uninstalled.actionError) return failed('uninstall', uninstalled);
|
|
314
|
+
const extra: string[] = [];
|
|
315
|
+
if (stopped.actionError) extra.push(`(it may not have been running: ${stopped.actionError})`);
|
|
316
|
+
// W3 Finding 4: this command only ever touches the TRACKED unit
|
|
317
|
+
// (whatever name/path actually resolved, per F2 — not necessarily the
|
|
318
|
+
// SERVICE_NAME constant) above — say so explicitly when a legacy unit
|
|
319
|
+
// also exists, so its continued presence is never a silent surprise.
|
|
320
|
+
if (legacy.present) extra.push(legacyUnitNote(legacy, resolveManagedUnitName(uninstalled)));
|
|
321
|
+
return ok('uninstall', uninstalled, extra);
|
|
322
|
+
}
|
|
323
|
+
case 'service-status': {
|
|
324
|
+
const status = manager.status();
|
|
325
|
+
if (status.actionError) return failed('status', status);
|
|
326
|
+
return ok('status', status, legacy.present ? [legacyUnitNote(legacy, resolveManagedUnitName(status))] : []);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
package/src/export/cost-utils.ts
CHANGED
|
@@ -151,3 +151,39 @@ export function calcSessionCost(
|
|
|
151
151
|
const billableInput = inputTokens + cacheRead + cacheWrite;
|
|
152
152
|
return (billableInput * pricing.input + outputTokens * pricing.output) / 1_000_000;
|
|
153
153
|
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* computeBudgetBreach — pure predicate shared by CostTrackerPanel's render-time
|
|
157
|
+
* "OVER BUDGET" flag and the background budget-breach notifier
|
|
158
|
+
* (core/budget-breach-notifier.ts), so both agree on exactly one definition
|
|
159
|
+
* of "over budget". `budgetThreshold <= 0` means no budget is configured
|
|
160
|
+
* (disabled), which can never be breached.
|
|
161
|
+
*/
|
|
162
|
+
export function computeBudgetBreach(sessionCost: number, budgetThreshold: number): boolean {
|
|
163
|
+
return budgetThreshold > 0 && sessionCost > budgetThreshold;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Read/write access to the `behavior.budgetAlertUsd` TUI-local config key. Plain
|
|
167
|
+
* callbacks (rather than a ConfigManager reference) so callers can pass a
|
|
168
|
+
* generic-key-cast wrapper without this module depending on the SDK config types. */
|
|
169
|
+
export interface BudgetAlertConfigAccess {
|
|
170
|
+
readonly get: (key: string) => unknown;
|
|
171
|
+
readonly set: (key: string, value: unknown) => void;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Default for the `behavior.budgetAlertUsd` synthetic setting: 0 = no budget configured. */
|
|
175
|
+
export const BUDGET_ALERT_USD_DEFAULT = 0;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* readBudgetAlertUsd — read the session cost-budget alert threshold (USD) from
|
|
179
|
+
* config. This is the single source of truth CostTrackerPanel and the
|
|
180
|
+
* background budget-breach notifier (core/budget-breach-notifier.ts) both
|
|
181
|
+
* read, so a threshold set via the panel's 'b' key is immediately visible to
|
|
182
|
+
* the notifier and vice versa. Falls back to BUDGET_ALERT_USD_DEFAULT (off)
|
|
183
|
+
* when the key is absent or invalid.
|
|
184
|
+
*/
|
|
185
|
+
export function readBudgetAlertUsd(configGet: (key: string) => unknown): number {
|
|
186
|
+
const raw = configGet('behavior.budgetAlertUsd');
|
|
187
|
+
const parsed = typeof raw === 'number' ? raw : Number(raw);
|
|
188
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : BUDGET_ALERT_USD_DEFAULT;
|
|
189
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { COMMON_COMMAND_NAMES, type SlashCommand } from './command-registry.ts';
|
|
2
2
|
import type { CommandRegistry } from './command-registry.ts';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -18,6 +18,14 @@ export interface AutocompleteState {
|
|
|
18
18
|
query: string;
|
|
19
19
|
results: AutocompleteResult[];
|
|
20
20
|
selectedIndex: number;
|
|
21
|
+
/**
|
|
22
|
+
* UX-C (item 4): how many leading entries of `results` belong to the
|
|
23
|
+
* curated "common" tier — only meaningful when `query === ''` (0
|
|
24
|
+
* otherwise, since a typed filter searches everything and the common/rest
|
|
25
|
+
* split no longer applies). The renderer uses this to draw a separator
|
|
26
|
+
* before the alphabetical rest.
|
|
27
|
+
*/
|
|
28
|
+
commonCount: number;
|
|
21
29
|
}
|
|
22
30
|
|
|
23
31
|
/**
|
|
@@ -35,6 +43,7 @@ export class AutocompleteEngine {
|
|
|
35
43
|
query: '',
|
|
36
44
|
results: [],
|
|
37
45
|
selectedIndex: 0,
|
|
46
|
+
commonCount: 0,
|
|
38
47
|
};
|
|
39
48
|
|
|
40
49
|
constructor(private registry: CommandRegistry) {}
|
|
@@ -47,6 +56,22 @@ export class AutocompleteEngine {
|
|
|
47
56
|
this.state.query = query;
|
|
48
57
|
this.state.results = this.registry.fuzzyMatch(query);
|
|
49
58
|
this.state.active = true;
|
|
59
|
+
// UX-C: fuzzyMatch('') sorts the curated common tier first (score 2),
|
|
60
|
+
// then the alphabetical rest (score 1) — both tiers alphabetical within
|
|
61
|
+
// themselves. Count the leading common run to tell the renderer where to
|
|
62
|
+
// draw the separator. A non-empty query means every command was scored on
|
|
63
|
+
// its actual match quality, not tier, so the common/rest split does not
|
|
64
|
+
// apply — the resulting 0 is not a real answer to "how many are common"
|
|
65
|
+
// and is unused because renderAutocompleteOverlay itself gates on the
|
|
66
|
+
// query string too, but zeroing here keeps the field internally honest.
|
|
67
|
+
let commonCount = 0;
|
|
68
|
+
if (query === '') {
|
|
69
|
+
for (const result of this.state.results) {
|
|
70
|
+
if (!COMMON_COMMAND_NAMES.has(result.command.name)) break;
|
|
71
|
+
commonCount++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.state.commonCount = commonCount;
|
|
50
75
|
// Clamp selection to new result count
|
|
51
76
|
if (this.state.selectedIndex >= this.state.results.length) {
|
|
52
77
|
this.state.selectedIndex = Math.max(0, this.state.results.length - 1);
|
|
@@ -79,6 +104,7 @@ export class AutocompleteEngine {
|
|
|
79
104
|
query: '',
|
|
80
105
|
results: [],
|
|
81
106
|
selectedIndex: 0,
|
|
107
|
+
commonCount: 0,
|
|
82
108
|
};
|
|
83
109
|
}
|
|
84
110
|
|
|
@@ -7,7 +7,8 @@ import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
|
7
7
|
import type { PermissionRequestHandler } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
8
8
|
import type { SelectionItem, SelectionResult, SelectionAction } from './selection-modal.ts';
|
|
9
9
|
import type { FileUndoManager } from '@pellux/goodvibes-sdk/platform/state';
|
|
10
|
-
import type {
|
|
10
|
+
import type { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
|
|
11
|
+
import type { PanelManager, PanelDeepLinkTarget } from '../panels/panel-manager.ts';
|
|
11
12
|
import type { KeybindingsManager } from './keybindings.ts';
|
|
12
13
|
import type { OnboardingWizardMode } from './onboarding/onboarding-wizard.ts';
|
|
13
14
|
import type { OpenOnboardingWizardOptions } from './handler-ui-state.ts';
|
|
@@ -47,6 +48,13 @@ export interface CommandRuntimeState {
|
|
|
47
48
|
systemPrompt: string;
|
|
48
49
|
reasoningEffort: string;
|
|
49
50
|
sessionId: string;
|
|
51
|
+
/**
|
|
52
|
+
* Cumulative count of direct terminal writes the output guard intercepted
|
|
53
|
+
* this session — surfaced by /debug. Optional so the SDK's MutableRuntimeState
|
|
54
|
+
* (which has no such field) stays assignable; the guard sets it lazily on the
|
|
55
|
+
* shared runtime object. (UX-B item 1a.)
|
|
56
|
+
*/
|
|
57
|
+
terminalWritesIntercepted?: number;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/**
|
|
@@ -68,6 +76,9 @@ export interface CommandUiActions {
|
|
|
68
76
|
};
|
|
69
77
|
executeCommand?: (name: string, args: string[]) => Promise<boolean>;
|
|
70
78
|
cancelGeneration?: () => void;
|
|
79
|
+
/** True while an LLM turn is actively streaming. Used to give Escape
|
|
80
|
+
* cancel-turn precedence over a focused panel's own escape handling. */
|
|
81
|
+
isGenerating?: () => boolean;
|
|
71
82
|
completeModelSelection?: (selection: {
|
|
72
83
|
model: { id: string; provider: string; displayName: string; registryKey: string };
|
|
73
84
|
effort: string;
|
|
@@ -75,6 +86,14 @@ export interface CommandUiActions {
|
|
|
75
86
|
/** Which config target to write the selected model to. Defaults to 'main'. */
|
|
76
87
|
target?: import('./model-picker.ts').ModelPickerTarget;
|
|
77
88
|
}) => void;
|
|
89
|
+
/**
|
|
90
|
+
* Commit an embedding-provider selection from the model picker's
|
|
91
|
+
* 'embeddingProvider' mode (the 'embeddings' target). Deliberately separate
|
|
92
|
+
* from completeModelSelection — embedding providers are not
|
|
93
|
+
* ModelDefinition-shaped, so routing them through the same commit path
|
|
94
|
+
* would require fabricating a fake model object.
|
|
95
|
+
*/
|
|
96
|
+
completeEmbeddingProviderSelection?: (providerId: string) => void;
|
|
78
97
|
clearScreen?: () => void;
|
|
79
98
|
activatePlan?: (planId: string, task: string) => void;
|
|
80
99
|
requestPermission?: PermissionRequestHandler;
|
|
@@ -103,16 +122,32 @@ export interface CommandShellUiOpeners {
|
|
|
103
122
|
openSelection?: (
|
|
104
123
|
title: string,
|
|
105
124
|
items: SelectionItem[],
|
|
106
|
-
opts: { preSelectId?: string; allowSearch?: boolean; customActions?: Map<string, SelectionAction
|
|
125
|
+
opts: { preSelectId?: string; allowSearch?: boolean; customActions?: Map<string, SelectionAction>; primaryVerbLabel?: string } | undefined,
|
|
107
126
|
callback: (result: SelectionResult | null) => void,
|
|
108
127
|
) => void;
|
|
109
128
|
openSettingsModal?: (target?: string) => void;
|
|
129
|
+
/**
|
|
130
|
+
* Open a MIGRATE-TO-MODAL surface by name (W6.1 purge skeleton — WO-A/B
|
|
131
|
+
* command runtimes call this instead of openCommandPanel once their panel
|
|
132
|
+
* is converted to a ModalFactory config). Threaded from ui-openers.ts the
|
|
133
|
+
* same way openSettingsModal is.
|
|
134
|
+
*/
|
|
135
|
+
openModal?: (name: string) => void;
|
|
110
136
|
openSessionPicker?: () => void;
|
|
111
137
|
openProfilePicker?: () => void;
|
|
112
138
|
openShortcutsOverlay?: () => void;
|
|
113
139
|
getScrollTop?: () => number;
|
|
114
140
|
openPanelPicker?: () => void;
|
|
115
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Open (and optionally focus) a panel. UX-C focus rule: the command path is
|
|
143
|
+
* "the user is mid-command-flow" — opening a panel this way leaves keyboard
|
|
144
|
+
* focus in the composer by default. Pass `{ focus: true }` for a caller that
|
|
145
|
+
* genuinely wants to grab focus (chords use panelManager.focusPanels()
|
|
146
|
+
* directly instead of this method, so no current call site needs it — but
|
|
147
|
+
* the intent is explicit rather than implicit here). `target` (DEBT-5) is a
|
|
148
|
+
* fleet deep-link jump target forwarded to PanelManager.open.
|
|
149
|
+
*/
|
|
150
|
+
showPanel?: (panelId: string, pane?: 'top' | 'bottom', target?: PanelDeepLinkTarget, opts?: { focus?: boolean }) => void;
|
|
116
151
|
focusPanels?: () => void;
|
|
117
152
|
focusPrompt?: () => void;
|
|
118
153
|
openOpsPanel?: () => void;
|
|
@@ -157,6 +192,22 @@ export interface CommandSessionServices {
|
|
|
157
192
|
* fresh Orchestrator's zeroed default (W0.9).
|
|
158
193
|
*/
|
|
159
194
|
readonly hydrateSessionUsage?: () => void;
|
|
195
|
+
/** Wave 4 (wo703): the orchestration engine's command-facing facade — see runtime/workstream-services.ts. */
|
|
196
|
+
readonly workstreamEngine?: import('../runtime/workstream-services.ts').WorkstreamCommandService;
|
|
197
|
+
/** Wave 5 (wo804): the repo source-tree code index — see runtime/code-index-services.ts. */
|
|
198
|
+
readonly codeIndexStore?: import('@pellux/goodvibes-sdk/platform/state').CodeIndexStore;
|
|
199
|
+
/** Wave-5 Stage B: tool-site reindex scheduler — `/codebase status` reports its last activity. */
|
|
200
|
+
readonly codeIndexReindexScheduler?: import('@pellux/goodvibes-sdk/platform/state').CodeIndexReindexScheduler;
|
|
201
|
+
/** Wave-5 Stage B: whether the (default-off) `agent-passive-code-injection` flag is on — for `/codebase status`. */
|
|
202
|
+
readonly isPassiveCodeInjectionFlagEnabled?: () => boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Wave 5 (wo805): the MAIN interactive session's per-turn passive-injection
|
|
205
|
+
* honesty ring — `Orchestrator.getTurnInjections()`, the main-session
|
|
206
|
+
* counterpart to `AgentRecord.turnInjections` (wo801). `/recall injections`
|
|
207
|
+
* with no agent id reads this. Optional so command contexts built without an
|
|
208
|
+
* orchestrator (headless, tests) fall back to the honest empty state.
|
|
209
|
+
*/
|
|
210
|
+
readonly getMainSessionTurnInjections?: () => readonly import('../renderer/turn-injection.ts').TurnInjectionEntry[];
|
|
160
211
|
}
|
|
161
212
|
|
|
162
213
|
export interface CommandProviderServices {
|
|
@@ -173,6 +224,7 @@ export interface CommandProviderServices {
|
|
|
173
224
|
export interface CommandWorkspaceUiServices {
|
|
174
225
|
keybindingsManager?: KeybindingsManager;
|
|
175
226
|
fileUndoManager?: FileUndoManager;
|
|
227
|
+
workspaceCheckpointManager?: WorkspaceCheckpointManager;
|
|
176
228
|
panelManager?: PanelManager;
|
|
177
229
|
profileManager?: import('@pellux/goodvibes-sdk/platform/profiles').ProfileManager;
|
|
178
230
|
bookmarkManager?: import('@pellux/goodvibes-sdk/platform/bookmarks').BookmarkManager;
|
|
@@ -190,6 +242,11 @@ export interface CommandPlatformConfigServices {
|
|
|
190
242
|
readonly configManager: ConfigManager;
|
|
191
243
|
readonly voiceProviderRegistry?: VoiceProviderRegistry;
|
|
192
244
|
readonly voiceService?: VoiceService;
|
|
245
|
+
/** Direct-command consumer (`/search`) alongside the existing agent-tool consumer. */
|
|
246
|
+
readonly webSearchService?: import('@pellux/goodvibes-sdk/platform/web-search').WebSearchService;
|
|
247
|
+
/** Direct-command consumer (`/image`) — first production caller of `.generate()`. */
|
|
248
|
+
readonly mediaProviders?: import('@pellux/goodvibes-sdk/platform/media').MediaProviderRegistry;
|
|
249
|
+
readonly artifactStore?: import('@pellux/goodvibes-sdk/platform/artifacts').ArtifactStore;
|
|
193
250
|
}
|
|
194
251
|
|
|
195
252
|
export interface CommandPlatformServices
|
|
@@ -202,7 +259,7 @@ export interface CommandOpsServices
|
|
|
202
259
|
export interface CommandExtensionRegistryServices {
|
|
203
260
|
readonly toolRegistry: ToolRegistry;
|
|
204
261
|
readonly mcpRegistry: McpRegistry;
|
|
205
|
-
readonly evalRegistry?: import('../panels/eval-
|
|
262
|
+
readonly evalRegistry?: import('../panels/eval-registry.ts').EvalRegistry;
|
|
206
263
|
}
|
|
207
264
|
|
|
208
265
|
export interface CommandExtensionServices
|
|
@@ -235,6 +292,23 @@ export interface CommandContext
|
|
|
235
292
|
};
|
|
236
293
|
}
|
|
237
294
|
|
|
295
|
+
/**
|
|
296
|
+
* UX-C palette curation (item 4): the "common" first tier the slash-command
|
|
297
|
+
* autocomplete dropdown shows before the alphabetical rest when it opens with
|
|
298
|
+
* no filter typed yet (bare '/') — the "132-command palette unranked"
|
|
299
|
+
* evaluator finding. Typed filtering (any non-empty query) is completely
|
|
300
|
+
* unaffected — fuzzyMatch still searches every registered command exactly as
|
|
301
|
+
* before; this only reorders the empty-query case. Curated for breadth across
|
|
302
|
+
* the product's main workflows rather than raw usage frequency: help/config
|
|
303
|
+
* (orientation), panel/model (workspace + provider), recall/codebase/search
|
|
304
|
+
* (knowledge), workstream/checkpoint (control-plane), imagine (generation),
|
|
305
|
+
* sessions (continuity), quit (exit).
|
|
306
|
+
*/
|
|
307
|
+
export const COMMON_COMMAND_NAMES: ReadonlySet<string> = new Set([
|
|
308
|
+
'help', 'config', 'panel', 'model', 'recall', 'codebase',
|
|
309
|
+
'workstream', 'checkpoint', 'search', 'imagine', 'sessions', 'quit',
|
|
310
|
+
]);
|
|
311
|
+
|
|
238
312
|
/**
|
|
239
313
|
* SlashCommand - A single slash command definition.
|
|
240
314
|
*/
|
|
@@ -337,7 +411,12 @@ export class CommandRegistry {
|
|
|
337
411
|
}
|
|
338
412
|
|
|
339
413
|
if (bestScore > 0 || q === '') {
|
|
340
|
-
|
|
414
|
+
// UX-C: with no query yet, rank the curated common tier (score 2)
|
|
415
|
+
// ahead of everything else (score 1) — the tie-break below then sorts
|
|
416
|
+
// each tier alphabetically, so the result is "common tier, then the
|
|
417
|
+
// alphabetical rest" rather than one flat alphabetical list.
|
|
418
|
+
const score = q === '' ? (COMMON_COMMAND_NAMES.has(cmd.name) ? 2 : 1) : bestScore;
|
|
419
|
+
results.push({ command: cmd, score });
|
|
341
420
|
}
|
|
342
421
|
}
|
|
343
422
|
|