@pellux/goodvibes-agent 0.1.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/.goodvibes/GOODVIBES.md +35 -0
- package/.goodvibes/agents/reviewer.md +48 -0
- package/.goodvibes/skills/add-provider/SKILL.md +199 -0
- package/CHANGELOG.md +25 -0
- package/README.md +74 -0
- package/bin/goodvibes-agent.ts +2 -0
- package/docs/README.md +23 -0
- package/docs/deployment-and-services.md +57 -0
- package/docs/getting-started.md +53 -0
- package/docs/release-and-publishing.md +46 -0
- package/package.json +134 -0
- package/scripts/check-bun.sh +20 -0
- package/src/audio/player.ts +156 -0
- package/src/audio/spoken-turn-controller.ts +203 -0
- package/src/audio/spoken-turn-model-routing.ts +117 -0
- package/src/audio/spoken-turn-wiring.ts +44 -0
- package/src/audio/text-chunker.ts +110 -0
- package/src/cli/bundle-command.ts +227 -0
- package/src/cli/completion.ts +90 -0
- package/src/cli/config-overrides.ts +159 -0
- package/src/cli/endpoints.ts +63 -0
- package/src/cli/entrypoint.ts +172 -0
- package/src/cli/help.ts +299 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/management-commands.ts +426 -0
- package/src/cli/management.ts +744 -0
- package/src/cli/network-posture.ts +46 -0
- package/src/cli/package-verification.ts +123 -0
- package/src/cli/parser.ts +369 -0
- package/src/cli/provider-auth-routes.ts +22 -0
- package/src/cli/provider-classification.ts +107 -0
- package/src/cli/redaction.ts +105 -0
- package/src/cli/service-command.ts +26 -0
- package/src/cli/service-posture.ts +482 -0
- package/src/cli/status.ts +383 -0
- package/src/cli/surface-command.ts +247 -0
- package/src/cli/tui-startup.ts +32 -0
- package/src/cli/types.ts +69 -0
- package/src/cli-flags.ts +21 -0
- package/src/config/goodvibes-home-audit.ts +465 -0
- package/src/config/index.ts +57 -0
- package/src/config/provider-model.ts +23 -0
- package/src/config/secret-config.ts +119 -0
- package/src/config/secrets.ts +71 -0
- package/src/config/surface.ts +1 -0
- package/src/core/composer-state.ts +61 -0
- package/src/core/conversation-rendering.ts +359 -0
- package/src/core/conversation.ts +551 -0
- package/src/core/history.ts +45 -0
- package/src/core/orchestrator.ts +7 -0
- package/src/core/system-message-router.ts +171 -0
- package/src/daemon/cli.ts +55 -0
- package/src/daemon/safe-serve.ts +61 -0
- package/src/input/agent-workspace.ts +428 -0
- package/src/input/autocomplete.ts +96 -0
- package/src/input/bookmark-modal.ts +115 -0
- package/src/input/command-args-hint.ts +36 -0
- package/src/input/command-registry.ts +329 -0
- package/src/input/commands/agent-externalized-tui.ts +73 -0
- package/src/input/commands/agent-workspace-runtime.ts +17 -0
- package/src/input/commands/branch-runtime.ts +72 -0
- package/src/input/commands/cloudflare-runtime.ts +370 -0
- package/src/input/commands/config.ts +18 -0
- package/src/input/commands/control-room-runtime.ts +255 -0
- package/src/input/commands/conversation-runtime.ts +207 -0
- package/src/input/commands/discovery-runtime.ts +52 -0
- package/src/input/commands/eval.ts +204 -0
- package/src/input/commands/experience-runtime.ts +278 -0
- package/src/input/commands/guidance-runtime.ts +106 -0
- package/src/input/commands/health-runtime.ts +434 -0
- package/src/input/commands/hooks-runtime.ts +148 -0
- package/src/input/commands/incident-runtime.ts +95 -0
- package/src/input/commands/integration-runtime.ts +394 -0
- package/src/input/commands/intelligence-runtime.ts +223 -0
- package/src/input/commands/knowledge.ts +531 -0
- package/src/input/commands/local-auth-runtime.ts +105 -0
- package/src/input/commands/local-provider-runtime.ts +170 -0
- package/src/input/commands/local-runtime.ts +392 -0
- package/src/input/commands/local-setup-review.ts +199 -0
- package/src/input/commands/local-setup-transfer.ts +135 -0
- package/src/input/commands/local-setup.ts +282 -0
- package/src/input/commands/managed-runtime.ts +209 -0
- package/src/input/commands/marketplace-runtime.ts +290 -0
- package/src/input/commands/mcp-runtime.ts +432 -0
- package/src/input/commands/memory-product-runtime.ts +111 -0
- package/src/input/commands/memory.ts +151 -0
- package/src/input/commands/notify-runtime.ts +83 -0
- package/src/input/commands/onboarding-runtime.ts +14 -0
- package/src/input/commands/operator-panel-runtime.ts +146 -0
- package/src/input/commands/operator-runtime.ts +392 -0
- package/src/input/commands/planning-runtime.ts +205 -0
- package/src/input/commands/platform-access-runtime.ts +422 -0
- package/src/input/commands/platform-services-runtime.ts +246 -0
- package/src/input/commands/policy-dispatch.ts +339 -0
- package/src/input/commands/policy.ts +17 -0
- package/src/input/commands/product-runtime.ts +351 -0
- package/src/input/commands/profile-sync-runtime.ts +99 -0
- package/src/input/commands/provider-accounts-runtime.ts +113 -0
- package/src/input/commands/provider.ts +363 -0
- package/src/input/commands/qrcode-runtime.ts +20 -0
- package/src/input/commands/quit-shared.ts +162 -0
- package/src/input/commands/recall-bundle.ts +132 -0
- package/src/input/commands/recall-capture.ts +152 -0
- package/src/input/commands/recall-query.ts +229 -0
- package/src/input/commands/recall-review.ts +98 -0
- package/src/input/commands/recall-shared.ts +22 -0
- package/src/input/commands/remote-runtime-pool.ts +106 -0
- package/src/input/commands/remote-runtime-setup.ts +199 -0
- package/src/input/commands/remote-runtime.ts +431 -0
- package/src/input/commands/replay-runtime.ts +18 -0
- package/src/input/commands/runtime-services.ts +291 -0
- package/src/input/commands/schedule-runtime.ts +91 -0
- package/src/input/commands/services-runtime.ts +209 -0
- package/src/input/commands/session-content.ts +408 -0
- package/src/input/commands/session-workflow.ts +464 -0
- package/src/input/commands/session.ts +375 -0
- package/src/input/commands/settings-sync-runtime.ts +174 -0
- package/src/input/commands/share-runtime.ts +119 -0
- package/src/input/commands/shell-core.ts +307 -0
- package/src/input/commands/skills-runtime.ts +221 -0
- package/src/input/commands/subscription-runtime.ts +434 -0
- package/src/input/commands/tasks-runtime.ts +230 -0
- package/src/input/commands/teamwork-runtime.ts +339 -0
- package/src/input/commands/teleport-runtime.ts +57 -0
- package/src/input/commands/tts-runtime.ts +29 -0
- package/src/input/commands/work-plan-runtime.ts +169 -0
- package/src/input/commands.ts +131 -0
- package/src/input/feed-context-factory.ts +254 -0
- package/src/input/file-picker.ts +192 -0
- package/src/input/handler-command-route.ts +180 -0
- package/src/input/handler-content-actions.ts +497 -0
- package/src/input/handler-feed-routes.ts +648 -0
- package/src/input/handler-feed.ts +452 -0
- package/src/input/handler-interactions.ts +281 -0
- package/src/input/handler-modal-routes.ts +418 -0
- package/src/input/handler-modal-stack.ts +263 -0
- package/src/input/handler-modal-token-routes.ts +329 -0
- package/src/input/handler-onboarding-cloudflare.ts +391 -0
- package/src/input/handler-onboarding.ts +620 -0
- package/src/input/handler-picker-routes.ts +472 -0
- package/src/input/handler-prompt-buffer.ts +320 -0
- package/src/input/handler-shortcuts.ts +213 -0
- package/src/input/handler-ui-state.ts +372 -0
- package/src/input/handler.ts +729 -0
- package/src/input/input-history.ts +297 -0
- package/src/input/keybindings.ts +292 -0
- package/src/input/mcp-workspace.ts +554 -0
- package/src/input/model-picker-provider-filter.ts +28 -0
- package/src/input/model-picker-types.ts +137 -0
- package/src/input/model-picker.ts +797 -0
- package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
- package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
- package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
- package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
- package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
- package/src/input/onboarding/onboarding-wizard.ts +711 -0
- package/src/input/panel-integration-actions.ts +78 -0
- package/src/input/profile-picker-modal.ts +222 -0
- package/src/input/search.ts +100 -0
- package/src/input/selection-modal.ts +163 -0
- package/src/input/selection.ts +135 -0
- package/src/input/session-picker-modal.ts +136 -0
- package/src/input/settings-modal-behavior.ts +37 -0
- package/src/input/settings-modal-secrets.ts +41 -0
- package/src/input/settings-modal-subscriptions.ts +95 -0
- package/src/input/settings-modal-types.ts +91 -0
- package/src/input/settings-modal.ts +793 -0
- package/src/input/submission-intent.ts +17 -0
- package/src/input/submission-router.ts +59 -0
- package/src/input/tts-settings-actions.ts +100 -0
- package/src/main.ts +792 -0
- package/src/mcp/runtime-reload.ts +81 -0
- package/src/panels/agent-inspector-panel.ts +521 -0
- package/src/panels/agent-inspector-shared.ts +94 -0
- package/src/panels/agent-logs-panel.ts +559 -0
- package/src/panels/agent-logs-shared.ts +129 -0
- package/src/panels/approval-panel.ts +150 -0
- package/src/panels/automation-control-panel.ts +212 -0
- package/src/panels/base-panel.ts +254 -0
- package/src/panels/builtin/agent.ts +117 -0
- package/src/panels/builtin/development.ts +31 -0
- package/src/panels/builtin/knowledge.ts +26 -0
- package/src/panels/builtin/operations.ts +349 -0
- package/src/panels/builtin/session.ts +129 -0
- package/src/panels/builtin/shared.ts +274 -0
- package/src/panels/builtin-panels.ts +23 -0
- package/src/panels/cockpit-panel.ts +183 -0
- package/src/panels/communication-panel.ts +153 -0
- package/src/panels/confirm-state.ts +61 -0
- package/src/panels/context-visualizer-panel.ts +204 -0
- package/src/panels/control-plane-panel.ts +211 -0
- package/src/panels/cost-tracker-panel.ts +444 -0
- package/src/panels/debug-panel.ts +432 -0
- package/src/panels/diff-panel.ts +520 -0
- package/src/panels/docs-panel.ts +283 -0
- package/src/panels/eval-panel.ts +399 -0
- package/src/panels/file-explorer-panel.ts +584 -0
- package/src/panels/file-preview-panel.ts +434 -0
- package/src/panels/forensics-panel.ts +364 -0
- package/src/panels/git-panel.ts +638 -0
- package/src/panels/hooks-panel.ts +239 -0
- package/src/panels/incident-review-panel.ts +197 -0
- package/src/panels/index.ts +46 -0
- package/src/panels/intelligence-panel.ts +176 -0
- package/src/panels/knowledge-panel.ts +345 -0
- package/src/panels/local-auth-panel.ts +130 -0
- package/src/panels/marketplace-panel.ts +212 -0
- package/src/panels/memory-panel.ts +225 -0
- package/src/panels/ops-control-panel.ts +150 -0
- package/src/panels/ops-strategy-panel.ts +235 -0
- package/src/panels/orchestration-panel.ts +273 -0
- package/src/panels/panel-list-panel.ts +509 -0
- package/src/panels/panel-manager.ts +570 -0
- package/src/panels/panel-picker.ts +106 -0
- package/src/panels/plan-dashboard-panel.ts +274 -0
- package/src/panels/plugins-panel.ts +178 -0
- package/src/panels/policy-panel.ts +308 -0
- package/src/panels/polish.ts +717 -0
- package/src/panels/project-planning-panel.ts +711 -0
- package/src/panels/provider-account-snapshot.ts +259 -0
- package/src/panels/provider-accounts-panel.ts +218 -0
- package/src/panels/provider-health-domains.ts +215 -0
- package/src/panels/provider-health-panel.ts +727 -0
- package/src/panels/provider-health-tracker.ts +115 -0
- package/src/panels/provider-stats-panel.ts +366 -0
- package/src/panels/qr-panel.ts +182 -0
- package/src/panels/remote-panel.ts +449 -0
- package/src/panels/routes-panel.ts +178 -0
- package/src/panels/sandbox-panel.ts +283 -0
- package/src/panels/schedule-panel.ts +329 -0
- package/src/panels/scrollable-list-panel.ts +491 -0
- package/src/panels/search-focus.ts +32 -0
- package/src/panels/security-panel.ts +295 -0
- package/src/panels/services-panel.ts +231 -0
- package/src/panels/session-browser-panel.ts +400 -0
- package/src/panels/session-maintenance.ts +125 -0
- package/src/panels/settings-sync-panel.ts +120 -0
- package/src/panels/skills-panel.ts +431 -0
- package/src/panels/subscription-panel.ts +263 -0
- package/src/panels/symbol-outline-panel.ts +486 -0
- package/src/panels/system-messages-panel.ts +230 -0
- package/src/panels/tasks-panel.ts +399 -0
- package/src/panels/thinking-panel.ts +304 -0
- package/src/panels/token-budget-panel.ts +475 -0
- package/src/panels/tool-inspector-panel.ts +429 -0
- package/src/panels/types.ts +54 -0
- package/src/panels/watchers-panel.ts +193 -0
- package/src/panels/work-plan-panel.ts +175 -0
- package/src/panels/worktree-panel.ts +182 -0
- package/src/panels/wrfc-panel.ts +609 -0
- package/src/permissions/prompt.ts +165 -0
- package/src/planning/project-planning-coordinator.ts +543 -0
- package/src/plugins/loader.ts +15 -0
- package/src/renderer/agent-detail-modal.ts +331 -0
- package/src/renderer/agent-workspace.ts +238 -0
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/autocomplete-overlay.ts +154 -0
- package/src/renderer/block-actions.ts +76 -0
- package/src/renderer/bookmark-modal.ts +101 -0
- package/src/renderer/bottom-bar.ts +58 -0
- package/src/renderer/buffer.ts +113 -0
- package/src/renderer/code-block.ts +373 -0
- package/src/renderer/compositor.ts +283 -0
- package/src/renderer/context-inspector.ts +219 -0
- package/src/renderer/conversation-layout.ts +67 -0
- package/src/renderer/conversation-overlays.ts +140 -0
- package/src/renderer/conversation-surface.ts +260 -0
- package/src/renderer/diff-view.ts +132 -0
- package/src/renderer/diff.ts +130 -0
- package/src/renderer/file-picker-overlay.ts +101 -0
- package/src/renderer/file-tree.ts +153 -0
- package/src/renderer/fullscreen-primitives.ts +130 -0
- package/src/renderer/fullscreen-workspace.ts +199 -0
- package/src/renderer/git-status.ts +89 -0
- package/src/renderer/help-overlay.ts +267 -0
- package/src/renderer/history-search-overlay.ts +73 -0
- package/src/renderer/layout-engine.ts +97 -0
- package/src/renderer/layout.ts +32 -0
- package/src/renderer/live-tail-modal.ts +156 -0
- package/src/renderer/markdown.ts +635 -0
- package/src/renderer/mcp-workspace.ts +237 -0
- package/src/renderer/modal-factory.ts +467 -0
- package/src/renderer/modal-utils.ts +24 -0
- package/src/renderer/model-picker-overlay.ts +473 -0
- package/src/renderer/model-workspace.ts +488 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
- package/src/renderer/overlay-box.ts +146 -0
- package/src/renderer/overlay-viewport.ts +104 -0
- package/src/renderer/panel-composite.ts +158 -0
- package/src/renderer/panel-picker-overlay.ts +202 -0
- package/src/renderer/panel-tab-bar.ts +69 -0
- package/src/renderer/panel-workspace-bar.ts +42 -0
- package/src/renderer/process-indicator.ts +96 -0
- package/src/renderer/process-modal.ts +656 -0
- package/src/renderer/process-summary.ts +67 -0
- package/src/renderer/profile-picker-modal.ts +129 -0
- package/src/renderer/progress.ts +98 -0
- package/src/renderer/qr-renderer.ts +120 -0
- package/src/renderer/search-overlay.ts +54 -0
- package/src/renderer/selection-modal-overlay.ts +214 -0
- package/src/renderer/semantic-diff.ts +369 -0
- package/src/renderer/session-picker-modal.ts +127 -0
- package/src/renderer/settings-modal-helpers.ts +193 -0
- package/src/renderer/settings-modal.ts +537 -0
- package/src/renderer/shell-surface.ts +88 -0
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +67 -0
- package/src/renderer/surface-layout.ts +101 -0
- package/src/renderer/syntax-highlighter.ts +542 -0
- package/src/renderer/system-message.ts +83 -0
- package/src/renderer/tab-strip.ts +108 -0
- package/src/renderer/text-layout.ts +31 -0
- package/src/renderer/thinking.ts +17 -0
- package/src/renderer/tool-call.ts +234 -0
- package/src/renderer/ui-factory.ts +524 -0
- package/src/renderer/ui-primitives.ts +96 -0
- package/src/runtime/bootstrap-command-context.ts +278 -0
- package/src/runtime/bootstrap-command-parts.ts +386 -0
- package/src/runtime/bootstrap-core.ts +540 -0
- package/src/runtime/bootstrap-hook-bridge.ts +112 -0
- package/src/runtime/bootstrap-shell.ts +283 -0
- package/src/runtime/bootstrap.ts +575 -0
- package/src/runtime/cloudflare-control-plane.ts +349 -0
- package/src/runtime/context.ts +142 -0
- package/src/runtime/diagnostics/panels/index.ts +24 -0
- package/src/runtime/diagnostics/panels/ops.ts +156 -0
- package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
- package/src/runtime/diagnostics/panels/policy.ts +177 -0
- package/src/runtime/index.ts +662 -0
- package/src/runtime/onboarding/apply.ts +642 -0
- package/src/runtime/onboarding/derivation.ts +534 -0
- package/src/runtime/onboarding/index.ts +7 -0
- package/src/runtime/onboarding/markers.ts +148 -0
- package/src/runtime/onboarding/snapshot.ts +406 -0
- package/src/runtime/onboarding/state.ts +141 -0
- package/src/runtime/onboarding/types.ts +404 -0
- package/src/runtime/onboarding/verify.ts +171 -0
- package/src/runtime/operator-token-cleanup.ts +27 -0
- package/src/runtime/perf/panel-contracts.ts +32 -0
- package/src/runtime/perf/panel-health-monitor.ts +18 -0
- package/src/runtime/sandbox-public-gaps.ts +358 -0
- package/src/runtime/services.ts +670 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
- package/src/runtime/store/domains/index.ts +222 -0
- package/src/runtime/store/domains/panels.ts +117 -0
- package/src/runtime/store/domains/ui-perf.ts +103 -0
- package/src/runtime/store/index.ts +305 -0
- package/src/runtime/store/selectors/index.ts +359 -0
- package/src/runtime/store/state.ts +145 -0
- package/src/runtime/surface-feature-flags.ts +65 -0
- package/src/runtime/terminal-output-guard.ts +228 -0
- package/src/runtime/ui/index.ts +39 -0
- package/src/runtime/ui/model-picker/data-provider.ts +182 -0
- package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
- package/src/runtime/ui/model-picker/index.ts +59 -0
- package/src/runtime/ui/model-picker/types.ts +149 -0
- package/src/runtime/ui/provider-health/data-provider.ts +244 -0
- package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
- package/src/runtime/ui/provider-health/index.ts +46 -0
- package/src/runtime/ui/provider-health/types.ts +146 -0
- package/src/runtime/ui-events.ts +1 -0
- package/src/runtime/ui-read-model-helpers.ts +1 -0
- package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
- package/src/runtime/ui-read-models-observability-options.ts +1 -0
- package/src/runtime/ui-read-models-observability-remote.ts +1 -0
- package/src/runtime/ui-read-models-observability-security.ts +1 -0
- package/src/runtime/ui-read-models-observability-system.ts +1 -0
- package/src/runtime/ui-read-models-observability.ts +1 -0
- package/src/runtime/ui-read-models.ts +61 -0
- package/src/runtime/ui-service-queries.ts +1 -0
- package/src/runtime/ui-services.ts +190 -0
- package/src/scripts/process-messages.ts +42 -0
- package/src/shell/blocking-input.ts +98 -0
- package/src/shell/service-settings-sync.ts +273 -0
- package/src/shell/ui-openers.ts +352 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/wrfc-agent-guard.ts +49 -0
- package/src/types/grid.ts +48 -0
- package/src/types/sql-js.d.ts +15 -0
- package/src/utils/clipboard.ts +22 -0
- package/src/utils/splash-lines.ts +46 -0
- package/src/utils/terminal-width.ts +185 -0
- package/src/verification/live-verifier.ts +430 -0
- package/src/verification/verification-ledger.ts +242 -0
- package/src/version.ts +17 -0
- package/src/widget/index.ts +2 -0
- package/src/widget/types.ts +9 -0
- package/src/widget/widget.ts +8 -0
- package/src/work-plans/work-plan-store.ts +374 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
2
|
+
import type { CommandContext } from './command-registry.ts';
|
|
3
|
+
|
|
4
|
+
export const AGENT_WORKSPACE_MODAL_NAME = 'agentWorkspace';
|
|
5
|
+
|
|
6
|
+
export type AgentWorkspaceFocusPane = 'categories' | 'actions';
|
|
7
|
+
|
|
8
|
+
export type AgentWorkspaceActionKind = 'command' | 'guidance';
|
|
9
|
+
|
|
10
|
+
export interface AgentWorkspaceAction {
|
|
11
|
+
readonly id: string;
|
|
12
|
+
readonly label: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
readonly command?: string;
|
|
15
|
+
readonly kind: AgentWorkspaceActionKind;
|
|
16
|
+
readonly safety: 'safe' | 'read-only' | 'delegates' | 'blocked';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AgentWorkspaceCategory {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly group: string;
|
|
22
|
+
readonly label: string;
|
|
23
|
+
readonly summary: string;
|
|
24
|
+
readonly detail: string;
|
|
25
|
+
readonly actions: readonly AgentWorkspaceAction[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type AgentWorkspaceCommandDispatcher = (command: string) => void;
|
|
29
|
+
|
|
30
|
+
export type AgentWorkspaceActionResultKind = 'guidance' | 'blocked' | 'dispatched' | 'refreshed' | 'error';
|
|
31
|
+
|
|
32
|
+
export interface AgentWorkspaceActionResult {
|
|
33
|
+
readonly kind: AgentWorkspaceActionResultKind;
|
|
34
|
+
readonly title: string;
|
|
35
|
+
readonly detail: string;
|
|
36
|
+
readonly command?: string;
|
|
37
|
+
readonly safety?: AgentWorkspaceAction['safety'];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type AgentWorkspaceConfigReader = {
|
|
41
|
+
get(key: string): unknown;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface AgentWorkspaceRuntimeSnapshot {
|
|
45
|
+
readonly provider: string;
|
|
46
|
+
readonly model: string;
|
|
47
|
+
readonly modelDisplayName: string;
|
|
48
|
+
readonly sessionId: string;
|
|
49
|
+
readonly workingDirectory: string;
|
|
50
|
+
readonly homeDirectory: string;
|
|
51
|
+
readonly daemonBaseUrl: string;
|
|
52
|
+
readonly daemonOwnership: 'external';
|
|
53
|
+
readonly sessionMemoryCount: number;
|
|
54
|
+
readonly knowledgeRoute: '/api/goodvibes-agent/knowledge';
|
|
55
|
+
readonly knowledgeIsolation: 'agent-only';
|
|
56
|
+
readonly executionPolicy: 'serial-proactive';
|
|
57
|
+
readonly wrfcPolicy: 'explicit-build-delegation-only';
|
|
58
|
+
readonly warnings: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readConfigString(context: CommandContext, key: string, fallback: string): string {
|
|
62
|
+
try {
|
|
63
|
+
const configManager = context.platform?.configManager as unknown as AgentWorkspaceConfigReader | undefined;
|
|
64
|
+
const value = configManager?.get(key);
|
|
65
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
|
|
66
|
+
} catch {
|
|
67
|
+
return fallback;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readConfigNumber(context: CommandContext, key: string, fallback: number): number {
|
|
72
|
+
try {
|
|
73
|
+
const configManager = context.platform?.configManager as unknown as AgentWorkspaceConfigReader | undefined;
|
|
74
|
+
const value = configManager?.get(key);
|
|
75
|
+
const numberValue = typeof value === 'number' ? value : Number(value);
|
|
76
|
+
return Number.isFinite(numberValue) ? numberValue : fallback;
|
|
77
|
+
} catch {
|
|
78
|
+
return fallback;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function buildAgentWorkspaceRuntimeSnapshot(context: CommandContext): AgentWorkspaceRuntimeSnapshot {
|
|
83
|
+
const host = readConfigString(context, 'controlPlane.host', '127.0.0.1');
|
|
84
|
+
const port = readConfigNumber(context, 'controlPlane.port', 3421);
|
|
85
|
+
const model = context.session?.runtime?.model ?? 'unknown';
|
|
86
|
+
const provider = context.session?.runtime?.provider ?? 'unknown';
|
|
87
|
+
const currentModel = (() => {
|
|
88
|
+
try {
|
|
89
|
+
return context.provider?.providerRegistry?.getCurrentModel?.();
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
const sessionMemoryCount = (() => {
|
|
95
|
+
try {
|
|
96
|
+
return context.session?.sessionMemoryStore?.list?.().length ?? 0;
|
|
97
|
+
} catch {
|
|
98
|
+
return 0;
|
|
99
|
+
}
|
|
100
|
+
})();
|
|
101
|
+
const warnings: string[] = [];
|
|
102
|
+
if (provider === 'unknown' || model === 'unknown') warnings.push('Provider/model unavailable in this runtime context.');
|
|
103
|
+
if (!context.executeCommand) warnings.push('Command dispatch is unavailable; workspace actions will show guidance only.');
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
provider,
|
|
107
|
+
model,
|
|
108
|
+
modelDisplayName: currentModel?.displayName ?? model,
|
|
109
|
+
sessionId: context.session?.runtime?.sessionId ?? 'unknown',
|
|
110
|
+
workingDirectory: context.workspace?.shellPaths?.workingDirectory ?? 'unavailable',
|
|
111
|
+
homeDirectory: context.workspace?.shellPaths?.homeDirectory ?? 'unavailable',
|
|
112
|
+
daemonBaseUrl: `http://${host}:${port}`,
|
|
113
|
+
daemonOwnership: 'external',
|
|
114
|
+
sessionMemoryCount,
|
|
115
|
+
knowledgeRoute: '/api/goodvibes-agent/knowledge',
|
|
116
|
+
knowledgeIsolation: 'agent-only',
|
|
117
|
+
executionPolicy: 'serial-proactive',
|
|
118
|
+
wrfcPolicy: 'explicit-build-delegation-only',
|
|
119
|
+
warnings,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
124
|
+
{
|
|
125
|
+
id: 'home',
|
|
126
|
+
group: 'OPERATE',
|
|
127
|
+
label: 'Home',
|
|
128
|
+
summary: 'Main operator surface for normal assistant work.',
|
|
129
|
+
detail: 'Use this as the Agent front door: chat in the main conversation, inspect state, choose model/provider, and open setup surfaces without switching into coding-TUI behavior.',
|
|
130
|
+
actions: [
|
|
131
|
+
{ id: 'chat', label: 'Continue assistant chat', detail: 'Close this workspace and type a normal message. Agent work stays serial in the main conversation.', kind: 'guidance', safety: 'safe' },
|
|
132
|
+
{ id: 'model', label: 'Choose model', detail: 'Open the model/provider workspace for the Agent chat route.', command: '/model', kind: 'command', safety: 'safe' },
|
|
133
|
+
{ id: 'help', label: 'Browse commands', detail: 'Open registry-driven command help.', command: '/help', kind: 'command', safety: 'safe' },
|
|
134
|
+
{ id: 'health', label: 'Review health', detail: 'Show the local health review surface without starting or mutating daemon services.', command: '/health review', kind: 'command', safety: 'read-only' },
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'setup',
|
|
139
|
+
group: 'SETUP',
|
|
140
|
+
label: 'Setup',
|
|
141
|
+
summary: 'Configuration, auth, provider, and onboarding surfaces.',
|
|
142
|
+
detail: 'Agent connects to an external daemon and owns local assistant configuration only. Daemon lifecycle and listener posture remain external.',
|
|
143
|
+
actions: [
|
|
144
|
+
{ id: 'config', label: 'Open config workspace', detail: 'Use the TUI-derived fullscreen settings workspace.', command: '/config', kind: 'command', safety: 'safe' },
|
|
145
|
+
{ id: 'onboarding', label: 'Open setup wizard', detail: 'Review Agent runtime settings in the fullscreen setup flow.', command: '/onboarding', kind: 'command', safety: 'safe' },
|
|
146
|
+
{ id: 'provider', label: 'Provider status', detail: 'Review provider/model posture.', command: '/provider', kind: 'command', safety: 'read-only' },
|
|
147
|
+
{ id: 'auth', label: 'Auth review', detail: 'Review authentication posture without printing token values.', command: '/auth review', kind: 'command', safety: 'read-only' },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'knowledge',
|
|
152
|
+
group: 'KNOW',
|
|
153
|
+
label: 'Knowledge',
|
|
154
|
+
summary: 'Agent Knowledge/Wiki and source-backed lookup.',
|
|
155
|
+
detail: 'Agent knowledge calls must use the isolated /api/goodvibes-agent/knowledge routes. Default regular wiki and HomeGraph are not the Agent knowledge environment.',
|
|
156
|
+
actions: [
|
|
157
|
+
{ id: 'knowledge-status', label: 'Knowledge status', detail: 'Inspect Agent knowledge readiness and counts.', command: '/knowledge status', kind: 'command', safety: 'read-only' },
|
|
158
|
+
{ id: 'knowledge-open', label: 'Open knowledge surface', detail: 'Open the knowledge panel/surface when available.', command: '/knowledge', kind: 'command', safety: 'read-only' },
|
|
159
|
+
{ id: 'knowledge-ask', label: 'Ask Agent knowledge', detail: 'Close this workspace and run /knowledge ask <question> or ask normally in chat.', kind: 'guidance', safety: 'read-only' },
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'memory',
|
|
164
|
+
group: 'LEARN',
|
|
165
|
+
label: 'Memory & Skills',
|
|
166
|
+
summary: 'Local assistant memory, skills, and reusable behavior.',
|
|
167
|
+
detail: 'Memory, skills, and personas stay Agent-local until stable shared daemon registry contracts exist. Secrets must not be stored as memory.',
|
|
168
|
+
actions: [
|
|
169
|
+
{ id: 'memory', label: 'Open memory', detail: 'Inspect local/session memory commands and surfaces.', command: '/memory', kind: 'command', safety: 'read-only' },
|
|
170
|
+
{ id: 'skills', label: 'Open skills', detail: 'Inspect discovered skills and skill catalog state.', command: '/skills open', kind: 'command', safety: 'read-only' },
|
|
171
|
+
{ id: 'personas', label: 'Persona library', detail: 'Use local Agent personas to shape serial assistant behavior without spawning background agents.', kind: 'guidance', safety: 'safe' },
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'work',
|
|
176
|
+
group: 'TRACK',
|
|
177
|
+
label: 'Work & Approvals',
|
|
178
|
+
summary: 'Visible task state, work plan, and approval posture.',
|
|
179
|
+
detail: 'Use these surfaces to inspect active operator state. Side-effecting approval decisions require explicit commands and confirmation outside this workspace.',
|
|
180
|
+
actions: [
|
|
181
|
+
{ id: 'workplan', label: 'Open work plan', detail: 'Open the workspace-scoped work plan panel.', command: '/workplan panel', kind: 'command', safety: 'read-only' },
|
|
182
|
+
{ id: 'workplan-list', label: 'List work plan', detail: 'Print a concise work plan summary.', command: '/workplan list', kind: 'command', safety: 'read-only' },
|
|
183
|
+
{ id: 'approvals', label: 'Review approvals', detail: 'Open/read approval posture. This workspace does not approve or deny requests.', command: '/approval open', kind: 'command', safety: 'read-only' },
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'automation',
|
|
188
|
+
group: 'WATCH',
|
|
189
|
+
label: 'Automation',
|
|
190
|
+
summary: 'Read-only automation and schedule observability.',
|
|
191
|
+
detail: 'Agent does not create, run, enable, disable, or remove local automation jobs. Schedule mutations wait for an Agent-safe public route and explicit approval.',
|
|
192
|
+
actions: [
|
|
193
|
+
{ id: 'schedule-list', label: 'List schedules', detail: 'Inspect configured jobs and history without running or mutating them.', command: '/schedule list', kind: 'command', safety: 'read-only' },
|
|
194
|
+
{ id: 'schedule-policy', label: 'Mutation blocked', detail: 'Schedule add/run/remove/enable/disable are intentionally blocked in Agent.', kind: 'guidance', safety: 'blocked' },
|
|
195
|
+
{ id: 'health-services', label: 'Service health', detail: 'Inspect service readiness without starting, stopping, or restarting daemon services.', command: '/health services', kind: 'command', safety: 'read-only' },
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'delegate',
|
|
200
|
+
group: 'BUILD',
|
|
201
|
+
label: 'Build Delegation',
|
|
202
|
+
summary: 'Explicit handoff to GoodVibes TUI for code work.',
|
|
203
|
+
detail: 'Agent does not become the coding TUI. Build, implement, fix, patch, and review work must be handed to GoodVibes TUI with the full original ask and WRFC only when explicitly requested.',
|
|
204
|
+
actions: [
|
|
205
|
+
{ id: 'delegate-guidance', label: 'Delegation rule', detail: 'For build/fix/review work, delegate one request to GoodVibes TUI instead of spawning local Engineer/Reviewer/Tester roots.', kind: 'guidance', safety: 'delegates' },
|
|
206
|
+
{ id: 'review-command', label: 'Review delegation command', detail: 'Use /review or /wrfc only when the user explicitly asks for code review/build execution.', command: '/review', kind: 'command', safety: 'delegates' },
|
|
207
|
+
{ id: 'remote-policy', label: 'Remote runner policy', detail: 'Remote dispatch/rerun is blocked in Agent; TUI owns runner topology for delegated build work.', command: '/remote dispatch', kind: 'command', safety: 'blocked' },
|
|
208
|
+
],
|
|
209
|
+
},
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
function parseCommand(command: string): { readonly name: string; readonly args: readonly string[] } {
|
|
213
|
+
const trimmed = command.trim().replace(/^\//, '');
|
|
214
|
+
if (!trimmed) return { name: '', args: [] };
|
|
215
|
+
const parts = trimmed.split(/\s+/);
|
|
216
|
+
return { name: parts[0] ?? '', args: parts.slice(1) };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export class AgentWorkspace {
|
|
220
|
+
public active = false;
|
|
221
|
+
public focusPane: AgentWorkspaceFocusPane = 'actions';
|
|
222
|
+
public selectedCategoryIndex = 0;
|
|
223
|
+
public selectedActionIndex = 0;
|
|
224
|
+
public status = 'Ready. Choose an operator flow; ordinary assistant work stays in the main conversation.';
|
|
225
|
+
public runtimeSnapshot: AgentWorkspaceRuntimeSnapshot | null = null;
|
|
226
|
+
public lastActionResult: AgentWorkspaceActionResult | null = null;
|
|
227
|
+
private context: CommandContext | null = null;
|
|
228
|
+
private dispatchCommand: AgentWorkspaceCommandDispatcher | null = null;
|
|
229
|
+
|
|
230
|
+
open(context: CommandContext, dispatchCommand: AgentWorkspaceCommandDispatcher): void {
|
|
231
|
+
this.context = context;
|
|
232
|
+
this.dispatchCommand = dispatchCommand;
|
|
233
|
+
this.runtimeSnapshot = buildAgentWorkspaceRuntimeSnapshot(context);
|
|
234
|
+
this.active = true;
|
|
235
|
+
this.focusPane = 'actions';
|
|
236
|
+
this.status = 'Ready. Choose an operator flow; ordinary assistant work stays in the main conversation.';
|
|
237
|
+
this.lastActionResult = null;
|
|
238
|
+
this.clampSelection();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
reopen(): void {
|
|
242
|
+
this.active = true;
|
|
243
|
+
this.clampSelection();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
close(): void {
|
|
247
|
+
this.active = false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
get categories(): readonly AgentWorkspaceCategory[] {
|
|
251
|
+
return AGENT_WORKSPACE_CATEGORIES;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
get selectedCategory(): AgentWorkspaceCategory {
|
|
255
|
+
return this.categories[this.selectedCategoryIndex] ?? this.categories[0]!;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
get actions(): readonly AgentWorkspaceAction[] {
|
|
259
|
+
return this.selectedCategory.actions;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
get selectedAction(): AgentWorkspaceAction | null {
|
|
263
|
+
return this.actions[this.selectedActionIndex] ?? null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
focusCategories(): void {
|
|
267
|
+
this.focusPane = 'categories';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
focusActions(): void {
|
|
271
|
+
this.focusPane = 'actions';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
toggleFocusPane(): void {
|
|
275
|
+
this.focusPane = this.focusPane === 'categories' ? 'actions' : 'categories';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
moveUp(): void {
|
|
279
|
+
if (this.focusPane === 'categories') {
|
|
280
|
+
this.selectedCategoryIndex = Math.max(0, this.selectedCategoryIndex - 1);
|
|
281
|
+
this.selectedActionIndex = 0;
|
|
282
|
+
} else {
|
|
283
|
+
this.selectedActionIndex = Math.max(0, this.selectedActionIndex - 1);
|
|
284
|
+
}
|
|
285
|
+
this.clampSelection();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
moveDown(): void {
|
|
289
|
+
if (this.focusPane === 'categories') {
|
|
290
|
+
this.selectedCategoryIndex = Math.min(this.categories.length - 1, this.selectedCategoryIndex + 1);
|
|
291
|
+
this.selectedActionIndex = 0;
|
|
292
|
+
} else {
|
|
293
|
+
this.selectedActionIndex = Math.min(this.actions.length - 1, this.selectedActionIndex + 1);
|
|
294
|
+
}
|
|
295
|
+
this.clampSelection();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
jumpHome(): void {
|
|
299
|
+
if (this.focusPane === 'categories') this.selectedCategoryIndex = 0;
|
|
300
|
+
else this.selectedActionIndex = 0;
|
|
301
|
+
this.clampSelection();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
jumpEnd(): void {
|
|
305
|
+
if (this.focusPane === 'categories') this.selectedCategoryIndex = this.categories.length - 1;
|
|
306
|
+
else this.selectedActionIndex = this.actions.length - 1;
|
|
307
|
+
this.clampSelection();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
refreshRuntimeSnapshot(): void {
|
|
311
|
+
if (!this.context) {
|
|
312
|
+
this.status = 'Runtime context is unavailable.';
|
|
313
|
+
this.lastActionResult = {
|
|
314
|
+
kind: 'error',
|
|
315
|
+
title: 'Context refresh failed',
|
|
316
|
+
detail: 'The Agent workspace has no command context to inspect.',
|
|
317
|
+
};
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
this.runtimeSnapshot = buildAgentWorkspaceRuntimeSnapshot(this.context);
|
|
321
|
+
this.status = 'Runtime context refreshed.';
|
|
322
|
+
this.lastActionResult = {
|
|
323
|
+
kind: 'refreshed',
|
|
324
|
+
title: 'Runtime context refreshed',
|
|
325
|
+
detail: 'Provider, model, session, local memory, daemon URL, and Agent knowledge route posture were re-read from the live command context.',
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
activateSelected(): void {
|
|
330
|
+
if (this.focusPane === 'categories') {
|
|
331
|
+
this.focusActions();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const action = this.selectedAction;
|
|
335
|
+
if (!action) return;
|
|
336
|
+
if (action.kind === 'guidance' || !action.command) {
|
|
337
|
+
this.status = action.detail;
|
|
338
|
+
this.lastActionResult = {
|
|
339
|
+
kind: 'guidance',
|
|
340
|
+
title: action.label,
|
|
341
|
+
detail: action.detail,
|
|
342
|
+
safety: action.safety,
|
|
343
|
+
};
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (action.safety === 'blocked') {
|
|
347
|
+
this.status = `Blocked here: ${action.label}.`;
|
|
348
|
+
this.lastActionResult = {
|
|
349
|
+
kind: 'blocked',
|
|
350
|
+
title: `${action.label} is blocked in Agent`,
|
|
351
|
+
detail: action.detail,
|
|
352
|
+
command: action.command,
|
|
353
|
+
safety: action.safety,
|
|
354
|
+
};
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const parsed = parseCommand(action.command);
|
|
358
|
+
if (!parsed.name) {
|
|
359
|
+
this.status = `No command is configured for ${action.label}.`;
|
|
360
|
+
this.lastActionResult = {
|
|
361
|
+
kind: 'error',
|
|
362
|
+
title: 'Command unavailable',
|
|
363
|
+
detail: `No command is configured for ${action.label}.`,
|
|
364
|
+
safety: action.safety,
|
|
365
|
+
};
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (!this.context?.executeCommand || !this.dispatchCommand) {
|
|
369
|
+
this.status = `Command dispatch is not available for ${action.command}.`;
|
|
370
|
+
this.lastActionResult = {
|
|
371
|
+
kind: 'error',
|
|
372
|
+
title: 'Command dispatch unavailable',
|
|
373
|
+
detail: `The command ${action.command} cannot be opened from this runtime.`,
|
|
374
|
+
command: action.command,
|
|
375
|
+
safety: action.safety,
|
|
376
|
+
};
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
this.status = `Opening ${action.command}.`;
|
|
380
|
+
this.lastActionResult = {
|
|
381
|
+
kind: 'dispatched',
|
|
382
|
+
title: `Opening ${action.label}`,
|
|
383
|
+
detail: 'The workspace handed this safe or read-only command to the shell-owned command router.',
|
|
384
|
+
command: action.command,
|
|
385
|
+
safety: action.safety,
|
|
386
|
+
};
|
|
387
|
+
this.dispatchCommand(action.command);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private clampSelection(): void {
|
|
391
|
+
this.selectedCategoryIndex = Math.max(0, Math.min(this.selectedCategoryIndex, this.categories.length - 1));
|
|
392
|
+
this.selectedActionIndex = Math.max(0, Math.min(this.selectedActionIndex, this.actions.length - 1));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function handleAgentWorkspaceToken(
|
|
397
|
+
workspace: AgentWorkspace,
|
|
398
|
+
token: InputToken,
|
|
399
|
+
handleEscape: () => void,
|
|
400
|
+
requestRender: () => void,
|
|
401
|
+
): boolean {
|
|
402
|
+
if (!workspace.active) return false;
|
|
403
|
+
|
|
404
|
+
if (token.type === 'key') {
|
|
405
|
+
if (token.logicalName === 'escape') {
|
|
406
|
+
handleEscape();
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
if (token.logicalName === 'enter' || token.logicalName === 'space') workspace.activateSelected();
|
|
410
|
+
else if (token.logicalName === 'left') workspace.focusCategories();
|
|
411
|
+
else if (token.logicalName === 'right') workspace.focusActions();
|
|
412
|
+
else if (token.logicalName === 'up') workspace.moveUp();
|
|
413
|
+
else if (token.logicalName === 'down') workspace.moveDown();
|
|
414
|
+
else if (token.logicalName === 'tab') workspace.toggleFocusPane();
|
|
415
|
+
else if (token.logicalName === 'home') workspace.jumpHome();
|
|
416
|
+
else if (token.logicalName === 'end') workspace.jumpEnd();
|
|
417
|
+
} else if (token.type === 'text') {
|
|
418
|
+
if (token.value === 'h') workspace.focusCategories();
|
|
419
|
+
else if (token.value === 'l') workspace.focusActions();
|
|
420
|
+
else if (token.value === 'j') workspace.moveDown();
|
|
421
|
+
else if (token.value === 'k') workspace.moveUp();
|
|
422
|
+
else if (token.value === 'r' || token.value === 'R') workspace.refreshRuntimeSnapshot();
|
|
423
|
+
else if (token.value === ' ') workspace.activateSelected();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
requestRender();
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { SlashCommand } from './command-registry.ts';
|
|
2
|
+
import type { CommandRegistry } from './command-registry.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AutocompleteResult - A ranked match from the command registry.
|
|
6
|
+
*/
|
|
7
|
+
export interface AutocompleteResult {
|
|
8
|
+
command: SlashCommand;
|
|
9
|
+
score: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* AutocompleteState - All state for the autocomplete dropdown.
|
|
14
|
+
* When `active` is false the dropdown is hidden.
|
|
15
|
+
*/
|
|
16
|
+
export interface AutocompleteState {
|
|
17
|
+
active: boolean;
|
|
18
|
+
query: string;
|
|
19
|
+
results: AutocompleteResult[];
|
|
20
|
+
selectedIndex: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* AutocompleteEngine - Manages autocomplete state for the slash command prompt.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* - Call `update(query)` whenever the partial command text changes.
|
|
28
|
+
* - Call `moveDown()` / `moveUp()` for arrow key navigation.
|
|
29
|
+
* - Call `getSelected()` to retrieve the currently highlighted command.
|
|
30
|
+
* - Call `reset()` on Escape or after a command executes.
|
|
31
|
+
*/
|
|
32
|
+
export class AutocompleteEngine {
|
|
33
|
+
private state: AutocompleteState = {
|
|
34
|
+
active: false,
|
|
35
|
+
query: '',
|
|
36
|
+
results: [],
|
|
37
|
+
selectedIndex: 0,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
constructor(private registry: CommandRegistry) {}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* update - Refresh the autocomplete results for the given query.
|
|
44
|
+
* Activates the dropdown whenever query changes.
|
|
45
|
+
*/
|
|
46
|
+
update(query: string): void {
|
|
47
|
+
this.state.query = query;
|
|
48
|
+
this.state.results = this.registry.fuzzyMatch(query);
|
|
49
|
+
this.state.active = true;
|
|
50
|
+
// Clamp selection to new result count
|
|
51
|
+
if (this.state.selectedIndex >= this.state.results.length) {
|
|
52
|
+
this.state.selectedIndex = Math.max(0, this.state.results.length - 1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Move selection down one item (wraps around). */
|
|
57
|
+
moveDown(): void {
|
|
58
|
+
if (this.state.results.length === 0) return;
|
|
59
|
+
this.state.selectedIndex = (this.state.selectedIndex + 1) % this.state.results.length;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Move selection up one item (wraps around). */
|
|
63
|
+
moveUp(): void {
|
|
64
|
+
if (this.state.results.length === 0) return;
|
|
65
|
+
this.state.selectedIndex =
|
|
66
|
+
(this.state.selectedIndex - 1 + this.state.results.length) % this.state.results.length;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Return the currently selected command, if any. */
|
|
70
|
+
getSelected(): SlashCommand | undefined {
|
|
71
|
+
const { results, selectedIndex } = this.state;
|
|
72
|
+
return results[selectedIndex]?.command;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Reset autocomplete to inactive state. */
|
|
76
|
+
reset(): void {
|
|
77
|
+
this.state = {
|
|
78
|
+
active: false,
|
|
79
|
+
query: '',
|
|
80
|
+
results: [],
|
|
81
|
+
selectedIndex: 0,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Read-only snapshot of current state. */
|
|
86
|
+
getState(): Readonly<AutocompleteState> {
|
|
87
|
+
return this.state;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* isActive - True when the dropdown should be shown.
|
|
92
|
+
*/
|
|
93
|
+
get isActive(): boolean {
|
|
94
|
+
return this.state.active && this.state.results.length > 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BookmarkModal — state management for the /bookmarks command modal.
|
|
3
|
+
*
|
|
4
|
+
* Lists bookmarks from BookmarkManager and tracks UI state:
|
|
5
|
+
* selected index, scroll offset, and pending action.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BookmarkEntry, BookmarkManager } from '@pellux/goodvibes-sdk/platform/bookmarks';
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// BookmarkModal
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export class BookmarkModal {
|
|
15
|
+
public static readonly DEFAULT_VISIBLE_ROWS = 8;
|
|
16
|
+
public static readonly VISIBLE_ROWS = BookmarkModal.DEFAULT_VISIBLE_ROWS;
|
|
17
|
+
public active = false;
|
|
18
|
+
public entries: BookmarkEntry[] = [];
|
|
19
|
+
public selectedIndex = 0;
|
|
20
|
+
/** Scroll offset for the list (number of items scrolled past the top). */
|
|
21
|
+
public scrollOffset = 0;
|
|
22
|
+
/** Max visible list rows. */
|
|
23
|
+
public visibleRows = BookmarkModal.DEFAULT_VISIBLE_ROWS;
|
|
24
|
+
|
|
25
|
+
public constructor(private readonly bookmarkManager: BookmarkManager) {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* open - Load current bookmarks and show the modal.
|
|
29
|
+
*/
|
|
30
|
+
open(): void {
|
|
31
|
+
this.entries = this.bookmarkManager.list();
|
|
32
|
+
this.selectedIndex = 0;
|
|
33
|
+
this.scrollOffset = 0;
|
|
34
|
+
this.active = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
close(): void {
|
|
38
|
+
this.active = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setVisibleRows(rows: number): void {
|
|
42
|
+
this.visibleRows = Math.max(3, rows);
|
|
43
|
+
this._clampScroll();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
moveUp(): void {
|
|
47
|
+
if (this.entries.length === 0) return;
|
|
48
|
+
this.selectedIndex = (this.selectedIndex - 1 + this.entries.length) % this.entries.length;
|
|
49
|
+
this._clampScroll();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
moveDown(): void {
|
|
53
|
+
if (this.entries.length === 0) return;
|
|
54
|
+
this.selectedIndex = (this.selectedIndex + 1) % this.entries.length;
|
|
55
|
+
this._clampScroll();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getSelected(): BookmarkEntry | null {
|
|
59
|
+
return this.entries[this.selectedIndex] ?? null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* removeSelected - Remove the currently selected bookmark and refresh the list.
|
|
64
|
+
* Returns the removed entry, or null if nothing was selected.
|
|
65
|
+
*/
|
|
66
|
+
removeSelected(): BookmarkEntry | null {
|
|
67
|
+
const entry = this.getSelected();
|
|
68
|
+
if (!entry) return null;
|
|
69
|
+
this.bookmarkManager.toggle(entry.key); // toggle off = remove
|
|
70
|
+
this.entries = this.bookmarkManager.list();
|
|
71
|
+
// Clamp selectedIndex after removal
|
|
72
|
+
if (this.entries.length === 0) {
|
|
73
|
+
this.selectedIndex = 0;
|
|
74
|
+
} else if (this.selectedIndex >= this.entries.length) {
|
|
75
|
+
this.selectedIndex = this.entries.length - 1;
|
|
76
|
+
}
|
|
77
|
+
this._clampScroll();
|
|
78
|
+
return entry;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* openSelectedFile - Load the saved file content for the selected entry.
|
|
83
|
+
* Returns file content string, or null if no saved file exists for the key.
|
|
84
|
+
*/
|
|
85
|
+
openSelectedFile(): string | null {
|
|
86
|
+
const entry = this.getSelected();
|
|
87
|
+
if (!entry) return null;
|
|
88
|
+
// Derive the filename from the key (saved as <timestamp>-<label>.txt)
|
|
89
|
+
// We list all saved files and look for one containing the entry key in the name
|
|
90
|
+
const files = this.bookmarkManager.listSavedFiles();
|
|
91
|
+
const match = files.find((f) => {
|
|
92
|
+
const base = f.split('/').pop() ?? '';
|
|
93
|
+
return base.includes(entry.key) ||
|
|
94
|
+
base.includes(entry.label.toLowerCase().replace(/[^a-z0-9_-]/g, '-').replace(/-+/g, '-').slice(0, 40));
|
|
95
|
+
});
|
|
96
|
+
if (!match) return null;
|
|
97
|
+
const name = match.split('/').pop()!;
|
|
98
|
+
return this.bookmarkManager.loadSavedFile(name);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Private helpers
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
private _clampScroll(): void {
|
|
106
|
+
const visRows = Math.max(3, this.visibleRows);
|
|
107
|
+
if (this.selectedIndex < this.scrollOffset) {
|
|
108
|
+
this.scrollOffset = this.selectedIndex;
|
|
109
|
+
} else if (this.selectedIndex >= this.scrollOffset + visRows) {
|
|
110
|
+
this.scrollOffset = this.selectedIndex - visRows + 1;
|
|
111
|
+
}
|
|
112
|
+
const maxOffset = Math.max(0, this.entries.length - visRows);
|
|
113
|
+
this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxOffset));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { CommandRegistry } from './command-registry.ts';
|
|
2
|
+
|
|
3
|
+
const SUBCOMMAND_HINTS: Record<string, Record<string, string>> = {
|
|
4
|
+
session: { rename: '<name>', resume: '<id|name>', info: '<id>', export: '<id> [format]', search: '<query>', delete: '<id>' },
|
|
5
|
+
template: { save: '<name>', use: '<name> [args]', edit: '<name>', delete: '<name>' },
|
|
6
|
+
secrets: { set: '<KEY> <value>', get: '<KEY>', delete: '<KEY>' },
|
|
7
|
+
permissions: { tool: '<name> allow|prompt|deny' },
|
|
8
|
+
config: { reset: '<key>' },
|
|
9
|
+
danger: {},
|
|
10
|
+
plugin: { enable: '<name>', disable: '<name>', reload: '' },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function buildCommandArgsHint(
|
|
14
|
+
prompt: string,
|
|
15
|
+
commandRegistry: Pick<CommandRegistry, 'get'>,
|
|
16
|
+
): string | undefined {
|
|
17
|
+
if (!prompt.startsWith('/')) return undefined;
|
|
18
|
+
|
|
19
|
+
const spaceIdx = prompt.indexOf(' ');
|
|
20
|
+
if (spaceIdx === -1) {
|
|
21
|
+
const cmd = commandRegistry.get(prompt.slice(1));
|
|
22
|
+
return cmd?.argsHint ?? cmd?.usage;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const cmdName = prompt.slice(1, spaceIdx);
|
|
26
|
+
const cmd = commandRegistry.get(cmdName);
|
|
27
|
+
if (!cmd) return undefined;
|
|
28
|
+
|
|
29
|
+
const afterCmd = prompt.slice(spaceIdx + 1);
|
|
30
|
+
const subSpaceIdx = afterCmd.indexOf(' ');
|
|
31
|
+
if (subSpaceIdx !== -1) return undefined;
|
|
32
|
+
|
|
33
|
+
const subMap = SUBCOMMAND_HINTS[cmdName];
|
|
34
|
+
if (subMap && afterCmd in subMap) return subMap[afterCmd];
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|