@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
package/src/cli/types.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export type GoodVibesCliCommand =
|
|
2
|
+
| 'tui'
|
|
3
|
+
| 'run'
|
|
4
|
+
| 'serve'
|
|
5
|
+
| 'web'
|
|
6
|
+
| 'service'
|
|
7
|
+
| 'status'
|
|
8
|
+
| 'doctor'
|
|
9
|
+
| 'onboarding'
|
|
10
|
+
| 'models'
|
|
11
|
+
| 'providers'
|
|
12
|
+
| 'auth'
|
|
13
|
+
| 'subscription'
|
|
14
|
+
| 'secrets'
|
|
15
|
+
| 'sessions'
|
|
16
|
+
| 'tasks'
|
|
17
|
+
| 'pair'
|
|
18
|
+
| 'surfaces'
|
|
19
|
+
| 'listener'
|
|
20
|
+
| 'control-plane'
|
|
21
|
+
| 'bundle'
|
|
22
|
+
| 'remote'
|
|
23
|
+
| 'bridge'
|
|
24
|
+
| 'completion'
|
|
25
|
+
| 'help'
|
|
26
|
+
| 'version'
|
|
27
|
+
| 'unknown';
|
|
28
|
+
|
|
29
|
+
export type GoodVibesCliOutputFormat = 'text' | 'json' | 'stream-json';
|
|
30
|
+
|
|
31
|
+
export interface CliCommandOutput {
|
|
32
|
+
readonly output: string;
|
|
33
|
+
readonly exitCode: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface GoodVibesCliFlags {
|
|
37
|
+
readonly provider: string | undefined;
|
|
38
|
+
readonly model: string | undefined;
|
|
39
|
+
readonly daemonHome: string | undefined;
|
|
40
|
+
readonly workingDir: string | undefined;
|
|
41
|
+
readonly help: boolean;
|
|
42
|
+
readonly version: boolean;
|
|
43
|
+
readonly prompt: string | undefined;
|
|
44
|
+
readonly print: boolean;
|
|
45
|
+
readonly outputFormat: GoodVibesCliOutputFormat;
|
|
46
|
+
readonly configOverrides: readonly string[];
|
|
47
|
+
readonly enableFeatures: readonly string[];
|
|
48
|
+
readonly disableFeatures: readonly string[];
|
|
49
|
+
readonly noAltScreen: boolean;
|
|
50
|
+
readonly port: number | undefined;
|
|
51
|
+
readonly hostname: string | undefined;
|
|
52
|
+
readonly open: boolean;
|
|
53
|
+
readonly continueLast: boolean;
|
|
54
|
+
readonly resume: string | undefined;
|
|
55
|
+
readonly session: string | undefined;
|
|
56
|
+
readonly fork: boolean;
|
|
57
|
+
readonly rawOutput: boolean;
|
|
58
|
+
readonly acceptRawOutputRisk: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface GoodVibesCliParseResult {
|
|
62
|
+
readonly binary: string;
|
|
63
|
+
readonly command: GoodVibesCliCommand;
|
|
64
|
+
readonly rawCommand: string | undefined;
|
|
65
|
+
readonly commandArgs: readonly string[];
|
|
66
|
+
readonly positionals: readonly string[];
|
|
67
|
+
readonly flags: GoodVibesCliFlags;
|
|
68
|
+
readonly errors: readonly string[];
|
|
69
|
+
}
|
package/src/cli-flags.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Compatibility wrapper for older imports. New CLI code lives in src/cli.
|
|
2
|
+
export type { GoodVibesCliFlags as CliFlags } from './cli/types.ts';
|
|
3
|
+
export {
|
|
4
|
+
applyRuntimeConfigOverrides,
|
|
5
|
+
applyRuntimeConfigValue,
|
|
6
|
+
applyRuntimeCommandEndpointFlagOverrides,
|
|
7
|
+
applyRuntimeEndpointFlagOverrides,
|
|
8
|
+
applyRuntimeFeatureFlagOverrides,
|
|
9
|
+
handleGoodVibesCliCommand,
|
|
10
|
+
parseGoodVibesCli,
|
|
11
|
+
renderGoodVibesCommandHelp,
|
|
12
|
+
renderGoodVibesHelp,
|
|
13
|
+
renderGoodVibesVersion,
|
|
14
|
+
} from './cli/index.ts';
|
|
15
|
+
|
|
16
|
+
import { parseGoodVibesCli } from './cli/parser.ts';
|
|
17
|
+
import type { GoodVibesCliFlags } from './cli/types.ts';
|
|
18
|
+
|
|
19
|
+
export function parseCliFlags(argv: readonly string[], binary = 'goodvibes'): GoodVibesCliFlags {
|
|
20
|
+
return parseGoodVibesCli(argv, binary).flags;
|
|
21
|
+
}
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
readdirSync,
|
|
7
|
+
statSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'node:fs';
|
|
10
|
+
import { createReadStream } from 'node:fs';
|
|
11
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
12
|
+
import { CONFIG_SCHEMA, DEFAULT_CONFIG } from '@pellux/goodvibes-sdk/platform/config';
|
|
13
|
+
|
|
14
|
+
export type HomeFileOwner = 'tui' | 'daemon' | 'foreign-goodvibes-product' | 'unknown-root';
|
|
15
|
+
|
|
16
|
+
export type SettingsKeyClassification =
|
|
17
|
+
| 'current-schema'
|
|
18
|
+
| 'known-dynamic'
|
|
19
|
+
| 'default-config-dynamic'
|
|
20
|
+
| 'unknown-stale-candidate';
|
|
21
|
+
|
|
22
|
+
export interface HomeAuditOptions {
|
|
23
|
+
readonly homeDir: string;
|
|
24
|
+
readonly includeHashes?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface HomeFileSummary {
|
|
28
|
+
readonly owner: HomeFileOwner;
|
|
29
|
+
readonly files: number;
|
|
30
|
+
readonly directories: number;
|
|
31
|
+
readonly bytes: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface HomeFileRecord {
|
|
35
|
+
readonly relativePath: string;
|
|
36
|
+
readonly owner: HomeFileOwner;
|
|
37
|
+
readonly bytes: number;
|
|
38
|
+
readonly mode: string;
|
|
39
|
+
readonly mtimeIso: string;
|
|
40
|
+
readonly sha256?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface SettingsKeyAudit {
|
|
44
|
+
readonly key: string;
|
|
45
|
+
readonly classification: SettingsKeyClassification;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SettingsAudit {
|
|
49
|
+
readonly path: string;
|
|
50
|
+
readonly exists: boolean;
|
|
51
|
+
readonly schemaKeyCount: number;
|
|
52
|
+
readonly leafKeyCount: number;
|
|
53
|
+
readonly recognizedKeyCount: number;
|
|
54
|
+
readonly missingSchemaKeys: readonly string[];
|
|
55
|
+
readonly keys: readonly SettingsKeyAudit[];
|
|
56
|
+
readonly staleCandidates: readonly string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface HomeAuditFinding {
|
|
60
|
+
readonly severity: 'info' | 'warn' | 'error';
|
|
61
|
+
readonly code: string;
|
|
62
|
+
readonly path?: string;
|
|
63
|
+
readonly message: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface DuplicateProfilePattern {
|
|
67
|
+
readonly normalizedName: string;
|
|
68
|
+
readonly count: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface GoodVibesHomeAudit {
|
|
72
|
+
readonly homeDir: string;
|
|
73
|
+
readonly generatedAt: string;
|
|
74
|
+
readonly summaries: readonly HomeFileSummary[];
|
|
75
|
+
readonly files: readonly HomeFileRecord[];
|
|
76
|
+
readonly settings: SettingsAudit;
|
|
77
|
+
readonly duplicateProfilePatterns: readonly DuplicateProfilePattern[];
|
|
78
|
+
readonly findings: readonly HomeAuditFinding[];
|
|
79
|
+
readonly allowedWriteRoots: readonly string[];
|
|
80
|
+
readonly readOnlyRoots: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface HomeSnapshotEntry {
|
|
84
|
+
readonly relativePath: string;
|
|
85
|
+
readonly bytes: number;
|
|
86
|
+
readonly mode: string;
|
|
87
|
+
readonly sha256: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type HomeSnapshot = Readonly<Record<string, HomeSnapshotEntry>>;
|
|
91
|
+
|
|
92
|
+
export interface HomeSnapshotDiff {
|
|
93
|
+
readonly added: readonly string[];
|
|
94
|
+
readonly removed: readonly string[];
|
|
95
|
+
readonly changed: readonly string[];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const KNOWN_FOREIGN_ROOTS = new Set([
|
|
99
|
+
'.backups',
|
|
100
|
+
'.exec-output',
|
|
101
|
+
'archive',
|
|
102
|
+
'companion-chat',
|
|
103
|
+
'events',
|
|
104
|
+
'full-suite',
|
|
105
|
+
'hooks',
|
|
106
|
+
'logs',
|
|
107
|
+
'memory',
|
|
108
|
+
'providers',
|
|
109
|
+
'scripts',
|
|
110
|
+
'sdk',
|
|
111
|
+
'skills',
|
|
112
|
+
'state',
|
|
113
|
+
'telemetry',
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
const KNOWN_DYNAMIC_KEYS = [
|
|
117
|
+
/^featureFlags(?:\.|$)/,
|
|
118
|
+
/^notifications\.webhookUrls$/,
|
|
119
|
+
/^wrfc\.gates$/,
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
export const GOODVIBES_ALLOWED_WRITE_ROOTS = ['tui/', 'daemon/'] as const;
|
|
123
|
+
export const GOODVIBES_READ_ONLY_ROOTS = [
|
|
124
|
+
'*.api.json',
|
|
125
|
+
'archive/',
|
|
126
|
+
'sdk/',
|
|
127
|
+
'state/',
|
|
128
|
+
'events/',
|
|
129
|
+
'companion-chat/',
|
|
130
|
+
'full-suite/',
|
|
131
|
+
] as const;
|
|
132
|
+
|
|
133
|
+
function toPosixRelative(path: string): string {
|
|
134
|
+
return path.split(sep).join('/');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function flattenObject(value: unknown, prefix = '', out: Record<string, unknown> = {}): Record<string, unknown> {
|
|
138
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
139
|
+
if (prefix) out[prefix] = value;
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const [key, entry] of Object.entries(value as Record<string, unknown>)) {
|
|
144
|
+
const nextPrefix = prefix ? `${prefix}.${key}` : key;
|
|
145
|
+
if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
|
|
146
|
+
flattenObject(entry, nextPrefix, out);
|
|
147
|
+
} else {
|
|
148
|
+
out[nextPrefix] = entry;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function classifyFile(relativePath: string): HomeFileOwner {
|
|
155
|
+
if (relativePath.startsWith('tui/')) return 'tui';
|
|
156
|
+
if (relativePath.startsWith('daemon/')) return 'daemon';
|
|
157
|
+
if (!relativePath.includes('/')) return 'foreign-goodvibes-product';
|
|
158
|
+
if (basename(relativePath).endsWith('.api.json')) return 'foreign-goodvibes-product';
|
|
159
|
+
const firstSegment = relativePath.split('/')[0] ?? '';
|
|
160
|
+
if (KNOWN_FOREIGN_ROOTS.has(firstSegment)) return 'foreign-goodvibes-product';
|
|
161
|
+
return 'unknown-root';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isKnownDynamicKey(key: string): boolean {
|
|
165
|
+
return KNOWN_DYNAMIC_KEYS.some((pattern) => pattern.test(key));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function classifySettingsKey(
|
|
169
|
+
key: string,
|
|
170
|
+
schemaKeys: ReadonlySet<string>,
|
|
171
|
+
defaultKeys: ReadonlySet<string>,
|
|
172
|
+
): SettingsKeyClassification {
|
|
173
|
+
if (schemaKeys.has(key)) return 'current-schema';
|
|
174
|
+
if (isKnownDynamicKey(key)) return 'known-dynamic';
|
|
175
|
+
if (defaultKeys.has(key)) return 'default-config-dynamic';
|
|
176
|
+
return 'unknown-stale-candidate';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function walkFiles(root: string): { files: string[]; directoryCountByOwner: Map<HomeFileOwner, number> } {
|
|
180
|
+
const files: string[] = [];
|
|
181
|
+
const directoryCountByOwner = new Map<HomeFileOwner, number>();
|
|
182
|
+
if (!existsSync(root)) return { files, directoryCountByOwner };
|
|
183
|
+
|
|
184
|
+
const visit = (dir: string): void => {
|
|
185
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
186
|
+
const absolute = join(dir, entry.name);
|
|
187
|
+
const rel = toPosixRelative(relative(root, absolute));
|
|
188
|
+
if (entry.isDirectory()) {
|
|
189
|
+
const owner = classifyFile(`${rel}/`);
|
|
190
|
+
directoryCountByOwner.set(owner, (directoryCountByOwner.get(owner) ?? 0) + 1);
|
|
191
|
+
visit(absolute);
|
|
192
|
+
} else if (entry.isFile()) {
|
|
193
|
+
files.push(absolute);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
visit(root);
|
|
199
|
+
files.sort((a, b) => a.localeCompare(b));
|
|
200
|
+
return { files, directoryCountByOwner };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function hashFile(path: string): Promise<string> {
|
|
204
|
+
const hash = createHash('sha256');
|
|
205
|
+
await new Promise<void>((resolvePromise, reject) => {
|
|
206
|
+
const stream = createReadStream(path);
|
|
207
|
+
stream.on('data', (chunk) => hash.update(chunk));
|
|
208
|
+
stream.on('error', reject);
|
|
209
|
+
stream.on('end', resolvePromise);
|
|
210
|
+
});
|
|
211
|
+
return hash.digest('hex');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function readJsonObject(path: string): Record<string, unknown> | null {
|
|
215
|
+
if (!existsSync(path)) return null;
|
|
216
|
+
try {
|
|
217
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
218
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
219
|
+
? parsed as Record<string, unknown>
|
|
220
|
+
: null;
|
|
221
|
+
} catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function auditSettings(homeDir: string): SettingsAudit {
|
|
227
|
+
const path = join(homeDir, 'tui', 'settings.json');
|
|
228
|
+
const settings = readJsonObject(path);
|
|
229
|
+
const flatSettings = flattenObject(settings ?? {});
|
|
230
|
+
const schemaKeys = new Set(CONFIG_SCHEMA.map((entry) => entry.key));
|
|
231
|
+
const defaultKeys = new Set(Object.keys(flattenObject(DEFAULT_CONFIG)));
|
|
232
|
+
const keys = Object.keys(flatSettings)
|
|
233
|
+
.sort((a, b) => a.localeCompare(b))
|
|
234
|
+
.map((key): SettingsKeyAudit => ({
|
|
235
|
+
key,
|
|
236
|
+
classification: classifySettingsKey(key, schemaKeys, defaultKeys),
|
|
237
|
+
}));
|
|
238
|
+
const missingSchemaKeys = [...schemaKeys]
|
|
239
|
+
.filter((key) => !(key in flatSettings))
|
|
240
|
+
.sort((a, b) => a.localeCompare(b));
|
|
241
|
+
const staleCandidates = keys
|
|
242
|
+
.filter((entry) => entry.classification === 'unknown-stale-candidate')
|
|
243
|
+
.map((entry) => entry.key);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
path,
|
|
247
|
+
exists: settings !== null,
|
|
248
|
+
schemaKeyCount: schemaKeys.size,
|
|
249
|
+
leafKeyCount: keys.length,
|
|
250
|
+
recognizedKeyCount: keys.filter((entry) => entry.classification === 'current-schema').length,
|
|
251
|
+
missingSchemaKeys,
|
|
252
|
+
keys,
|
|
253
|
+
staleCandidates,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function modeString(path: string): string {
|
|
258
|
+
return (statSync(path).mode & 0o777).toString(8).padStart(3, '0');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function collectPermissionFindings(homeDir: string): HomeAuditFinding[] {
|
|
262
|
+
const findings: HomeAuditFinding[] = [];
|
|
263
|
+
const sensitiveFiles = [
|
|
264
|
+
join(homeDir, 'tui', 'secrets.enc'),
|
|
265
|
+
join(homeDir, 'tui', 'auth-users.json'),
|
|
266
|
+
join(homeDir, 'daemon', 'operator-tokens.json'),
|
|
267
|
+
join(homeDir, 'daemon', '.goodvibes', 'daemon', 'operator-tokens.json'),
|
|
268
|
+
join(homeDir, 'daemon', '.goodvibes', 'tui', 'auth-users.json'),
|
|
269
|
+
join(homeDir, 'daemon', '.goodvibes', 'tui', 'auth-bootstrap.txt'),
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
for (const path of sensitiveFiles) {
|
|
273
|
+
if (!existsSync(path)) continue;
|
|
274
|
+
const mode = modeString(path);
|
|
275
|
+
if (mode !== '600') {
|
|
276
|
+
findings.push({
|
|
277
|
+
severity: 'warn',
|
|
278
|
+
code: 'sensitive-file-permissions',
|
|
279
|
+
path,
|
|
280
|
+
message: `Sensitive file mode is ${mode}; expected 600.`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return findings;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function collectDuplicateProfilePatterns(homeDir: string): DuplicateProfilePattern[] {
|
|
289
|
+
const profilesDir = join(homeDir, 'tui', 'profiles');
|
|
290
|
+
if (!existsSync(profilesDir)) return [];
|
|
291
|
+
|
|
292
|
+
const counts = new Map<string, number>();
|
|
293
|
+
for (const entry of readdirSync(profilesDir, { withFileTypes: true })) {
|
|
294
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
|
|
295
|
+
const normalizedName = entry.name.replace(/^(team-)+/, 'team-');
|
|
296
|
+
counts.set(normalizedName, (counts.get(normalizedName) ?? 0) + 1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return [...counts.entries()]
|
|
300
|
+
.filter(([, count]) => count > 1)
|
|
301
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
302
|
+
.map(([normalizedName, count]) => ({ normalizedName, count }));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export async function auditGoodVibesHome(options: HomeAuditOptions): Promise<GoodVibesHomeAudit> {
|
|
306
|
+
const homeDir = resolve(options.homeDir);
|
|
307
|
+
const { files, directoryCountByOwner } = walkFiles(homeDir);
|
|
308
|
+
const summaries = new Map<HomeFileOwner, { files: number; directories: number; bytes: number }>();
|
|
309
|
+
const records: HomeFileRecord[] = [];
|
|
310
|
+
|
|
311
|
+
for (const file of files) {
|
|
312
|
+
const stats = statSync(file);
|
|
313
|
+
const relativePath = toPosixRelative(relative(homeDir, file));
|
|
314
|
+
const owner = classifyFile(relativePath);
|
|
315
|
+
const summary = summaries.get(owner) ?? { files: 0, directories: directoryCountByOwner.get(owner) ?? 0, bytes: 0 };
|
|
316
|
+
summary.files += 1;
|
|
317
|
+
summary.bytes += stats.size;
|
|
318
|
+
summaries.set(owner, summary);
|
|
319
|
+
records.push({
|
|
320
|
+
relativePath,
|
|
321
|
+
owner,
|
|
322
|
+
bytes: stats.size,
|
|
323
|
+
mode: (stats.mode & 0o777).toString(8).padStart(3, '0'),
|
|
324
|
+
mtimeIso: stats.mtime.toISOString(),
|
|
325
|
+
sha256: options.includeHashes ? await hashFile(file) : undefined,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const settings = auditSettings(homeDir);
|
|
330
|
+
const findings = [
|
|
331
|
+
...collectPermissionFindings(homeDir),
|
|
332
|
+
...settings.staleCandidates.map((key): HomeAuditFinding => ({
|
|
333
|
+
severity: 'warn',
|
|
334
|
+
code: 'stale-settings-key',
|
|
335
|
+
path: settings.path,
|
|
336
|
+
message: `Setting '${key}' is not in CONFIG_SCHEMA, DEFAULT_CONFIG, or known dynamic config.`,
|
|
337
|
+
})),
|
|
338
|
+
];
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
homeDir,
|
|
342
|
+
generatedAt: new Date().toISOString(),
|
|
343
|
+
summaries: [...summaries.entries()]
|
|
344
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
345
|
+
.map(([owner, summary]) => ({ owner, ...summary })),
|
|
346
|
+
files: records,
|
|
347
|
+
settings,
|
|
348
|
+
duplicateProfilePatterns: collectDuplicateProfilePatterns(homeDir),
|
|
349
|
+
findings,
|
|
350
|
+
allowedWriteRoots: [...GOODVIBES_ALLOWED_WRITE_ROOTS],
|
|
351
|
+
readOnlyRoots: [...GOODVIBES_READ_ONLY_ROOTS],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export async function snapshotGoodVibesHome(homeDir: string): Promise<HomeSnapshot> {
|
|
356
|
+
const root = resolve(homeDir);
|
|
357
|
+
const { files } = walkFiles(root);
|
|
358
|
+
const entries: Record<string, HomeSnapshotEntry> = {};
|
|
359
|
+
for (const file of files) {
|
|
360
|
+
const stats = statSync(file);
|
|
361
|
+
const relativePath = toPosixRelative(relative(root, file));
|
|
362
|
+
entries[relativePath] = {
|
|
363
|
+
relativePath,
|
|
364
|
+
bytes: stats.size,
|
|
365
|
+
mode: (stats.mode & 0o777).toString(8).padStart(3, '0'),
|
|
366
|
+
sha256: await hashFile(file),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
return entries;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function diffHomeSnapshots(before: HomeSnapshot, after: HomeSnapshot): HomeSnapshotDiff {
|
|
373
|
+
const beforeKeys = new Set(Object.keys(before));
|
|
374
|
+
const afterKeys = new Set(Object.keys(after));
|
|
375
|
+
const added = [...afterKeys].filter((key) => !beforeKeys.has(key)).sort((a, b) => a.localeCompare(b));
|
|
376
|
+
const removed = [...beforeKeys].filter((key) => !afterKeys.has(key)).sort((a, b) => a.localeCompare(b));
|
|
377
|
+
const changed = [...afterKeys]
|
|
378
|
+
.filter((key) => beforeKeys.has(key))
|
|
379
|
+
.filter((key) => {
|
|
380
|
+
const prior = before[key];
|
|
381
|
+
const next = after[key];
|
|
382
|
+
return prior.sha256 !== next.sha256 || prior.mode !== next.mode || prior.bytes !== next.bytes;
|
|
383
|
+
})
|
|
384
|
+
.sort((a, b) => a.localeCompare(b));
|
|
385
|
+
return { added, removed, changed };
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export function findDisallowedHomeMutations(
|
|
389
|
+
diff: HomeSnapshotDiff,
|
|
390
|
+
allowedRoots: readonly string[] = GOODVIBES_ALLOWED_WRITE_ROOTS,
|
|
391
|
+
): string[] {
|
|
392
|
+
const touched = [...diff.added, ...diff.removed, ...diff.changed];
|
|
393
|
+
return touched
|
|
394
|
+
.filter((relativePath) => !allowedRoots.some((root) => relativePath.startsWith(root)))
|
|
395
|
+
.sort((a, b) => a.localeCompare(b));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function renderGoodVibesHomeAuditMarkdown(audit: GoodVibesHomeAudit): string {
|
|
399
|
+
const lines: string[] = [
|
|
400
|
+
'# GoodVibes Home Audit',
|
|
401
|
+
'',
|
|
402
|
+
`Generated: ${audit.generatedAt}`,
|
|
403
|
+
`Home: \`${audit.homeDir}\``,
|
|
404
|
+
'',
|
|
405
|
+
'## Ownership Summary',
|
|
406
|
+
'',
|
|
407
|
+
'| Owner | Files | Directories | Bytes |',
|
|
408
|
+
'|---|---:|---:|---:|',
|
|
409
|
+
...audit.summaries.map((summary) => (
|
|
410
|
+
`| ${summary.owner} | ${summary.files} | ${summary.directories} | ${summary.bytes} |`
|
|
411
|
+
)),
|
|
412
|
+
'',
|
|
413
|
+
'## Settings',
|
|
414
|
+
'',
|
|
415
|
+
`Path: \`${audit.settings.path}\``,
|
|
416
|
+
'',
|
|
417
|
+
'| Metric | Count |',
|
|
418
|
+
'|---|---:|',
|
|
419
|
+
`| Schema keys | ${audit.settings.schemaKeyCount} |`,
|
|
420
|
+
`| Persisted leaf keys | ${audit.settings.leafKeyCount} |`,
|
|
421
|
+
`| Current-schema keys | ${audit.settings.recognizedKeyCount} |`,
|
|
422
|
+
`| Missing schema keys | ${audit.settings.missingSchemaKeys.length} |`,
|
|
423
|
+
`| Stale candidates | ${audit.settings.staleCandidates.length} |`,
|
|
424
|
+
'',
|
|
425
|
+
];
|
|
426
|
+
|
|
427
|
+
if (audit.settings.missingSchemaKeys.length > 0) {
|
|
428
|
+
lines.push('### Missing Schema Keys', '');
|
|
429
|
+
for (const key of audit.settings.missingSchemaKeys) lines.push(`- \`${key}\``);
|
|
430
|
+
lines.push('');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (audit.settings.staleCandidates.length > 0) {
|
|
434
|
+
lines.push('### Stale Setting Candidates', '');
|
|
435
|
+
for (const key of audit.settings.staleCandidates) lines.push(`- \`${key}\``);
|
|
436
|
+
lines.push('');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (audit.duplicateProfilePatterns.length > 0) {
|
|
440
|
+
lines.push('## Duplicate Profile Patterns', '');
|
|
441
|
+
for (const pattern of audit.duplicateProfilePatterns) {
|
|
442
|
+
lines.push(`- \`${pattern.normalizedName}\`: ${pattern.count}`);
|
|
443
|
+
}
|
|
444
|
+
lines.push('');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
lines.push('## Findings', '');
|
|
448
|
+
if (audit.findings.length === 0) {
|
|
449
|
+
lines.push('No findings.');
|
|
450
|
+
} else {
|
|
451
|
+
for (const finding of audit.findings) {
|
|
452
|
+
const path = finding.path ? ` \`${finding.path}\`` : '';
|
|
453
|
+
lines.push(`- ${finding.severity.toUpperCase()} ${finding.code}:${path} ${finding.message}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
lines.push('');
|
|
457
|
+
|
|
458
|
+
return `${lines.join('\n')}\n`;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function writeAuditReportFiles(audit: GoodVibesHomeAudit, outputDir: string): void {
|
|
462
|
+
mkdirSync(outputDir, { recursive: true });
|
|
463
|
+
writeFileSync(join(outputDir, 'goodvibes-home-audit.json'), `${JSON.stringify(audit, null, 2)}\n`, 'utf8');
|
|
464
|
+
writeFileSync(join(outputDir, 'goodvibes-home-audit.md'), renderGoodVibesHomeAuditMarkdown(audit), 'utf8');
|
|
465
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config system barrel export.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - ConfigManager class and all schema types
|
|
6
|
+
* - Pure helpers that derive values from an explicit ConfigManager instance
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
10
|
+
export type { DeepReadonly } from '@pellux/goodvibes-sdk/platform/config';
|
|
11
|
+
export type { GoodVibesConfig, ConfigKey, ConfigValue, ConfigSetting, PermissionMode, PermissionAction, PermissionsToolConfig, NotificationsConfig, PersistedFlagState } from '@pellux/goodvibes-sdk/platform/config';
|
|
12
|
+
export { DEFAULT_CONFIG, CONFIG_SCHEMA } from '@pellux/goodvibes-sdk/platform/config';
|
|
13
|
+
export { ConfigError } from '@pellux/goodvibes-sdk/platform/types';
|
|
14
|
+
|
|
15
|
+
import { readFileSync } from 'fs';
|
|
16
|
+
import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
17
|
+
import type { GoodVibesConfig } from '@pellux/goodvibes-sdk/platform/config';
|
|
18
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
19
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
20
|
+
import { getProviderIdFromModel } from './provider-model.ts';
|
|
21
|
+
|
|
22
|
+
export function getConfigSnapshot(configManager: Pick<ConfigManager, 'getRaw'>): Readonly<GoodVibesConfig> {
|
|
23
|
+
return configManager.getRaw();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getConfiguredModelId(configManager: Pick<ConfigManager, 'get'>): string {
|
|
27
|
+
return configManager.get('provider.model');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getConfiguredProviderId(configManager: Pick<ConfigManager, 'get'>): string {
|
|
31
|
+
return getProviderIdFromModel(configManager.get('provider.model'));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getConfiguredEmbeddingProviderId(configManager: Pick<ConfigManager, 'get'>): string {
|
|
35
|
+
return configManager.get('provider.embeddingProvider');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isAutoApproveEnabled(configManager: Pick<ConfigManager, 'get'>): boolean {
|
|
39
|
+
return configManager.get('behavior.autoApprove');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getWorkingDirectory(configManager: Pick<ConfigManager, 'getWorkingDirectory'>): string | null {
|
|
43
|
+
return configManager.getWorkingDirectory();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getConfiguredSystemPrompt(configManager: Pick<ConfigManager, 'get'>): string | undefined {
|
|
47
|
+
const file = configManager.get('provider.systemPromptFile');
|
|
48
|
+
if (!file) return undefined;
|
|
49
|
+
try {
|
|
50
|
+
return readFileSync(file, 'utf-8');
|
|
51
|
+
} catch (err) {
|
|
52
|
+
logger.debug('systemPrompt file read failed (non-fatal)', { file, error: summarizeError(err) });
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export { getConfiguredApiKeys, resolveApiKeys } from '@pellux/goodvibes-sdk/platform/config';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DEFAULT_CONFIG } from '@pellux/goodvibes-sdk/platform/config';
|
|
2
|
+
|
|
3
|
+
export function getProviderIdFromModel(model: unknown): string {
|
|
4
|
+
const raw = String(model ?? '').trim();
|
|
5
|
+
if (!raw) return getProviderIdFromModel(DEFAULT_CONFIG.provider.model);
|
|
6
|
+
const separator = raw.indexOf(':');
|
|
7
|
+
return separator > 0 ? raw.slice(0, separator) : raw;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getModelIdFromProviderModel(model: unknown): string {
|
|
11
|
+
const raw = String(model ?? '').trim();
|
|
12
|
+
if (!raw) return String(DEFAULT_CONFIG.provider.model);
|
|
13
|
+
const separator = raw.indexOf(':');
|
|
14
|
+
return separator > 0 ? raw.slice(separator + 1) : raw;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function formatProviderModel(providerId: string, modelId: string): string {
|
|
18
|
+
const provider = providerId.trim();
|
|
19
|
+
const model = modelId.trim();
|
|
20
|
+
if (!provider) return model;
|
|
21
|
+
if (!model) return `${provider}:`;
|
|
22
|
+
return model.includes(':') ? model : `${provider}:${model}`;
|
|
23
|
+
}
|