@opengsd/gsd-pi 1.0.2-dev.867e002 → 1.0.2-dev.aed3486
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/dist/onboarding.js +22 -3
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/context7/index.js +12 -2
- package/dist/resources/extensions/get-secrets-from-user.js +16 -16
- package/dist/resources/extensions/google-cli/index.js +30 -0
- package/dist/resources/extensions/google-cli/models.js +55 -0
- package/dist/resources/extensions/google-cli/package.json +11 -0
- package/dist/resources/extensions/google-cli/readiness.js +12 -0
- package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
- package/dist/resources/extensions/gsd/auto/loop.js +62 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
- package/dist/resources/extensions/gsd/auto/phases.js +37 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
- package/dist/resources/extensions/gsd/auto-post-unit.js +21 -11
- package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
- package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
- package/dist/resources/extensions/gsd/auto-start.js +232 -49
- package/dist/resources/extensions/gsd/auto.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
- package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
- package/dist/resources/extensions/gsd/commands-usage.js +105 -1
- package/dist/resources/extensions/gsd/config-overlay.js +20 -14
- package/dist/resources/extensions/gsd/context-overlay.js +22 -16
- package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
- package/dist/resources/extensions/gsd/doctor.js +6 -1
- package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
- package/dist/resources/extensions/gsd/guided-flow.js +5 -6
- package/dist/resources/extensions/gsd/key-manager.js +45 -13
- package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
- package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
- package/dist/resources/extensions/gsd/preferences.js +14 -2
- package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/dist/resources/extensions/gsd/prompts/system.md +1 -3
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
- package/dist/resources/extensions/gsd/repository-registry.js +3 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +11 -4
- package/dist/resources/extensions/gsd/skill-activation.js +233 -0
- package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
- package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
- package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
- package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
- package/dist/resources/extensions/gsd/skill-scope.js +52 -0
- package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
- package/dist/resources/extensions/gsd/skills.js +60 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
- package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
- package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
- package/dist/resources/extensions/gsd/vision-ask.js +22 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
- package/dist/resources/extensions/search-the-web/native-search.js +57 -8
- package/dist/resources/extensions/shared/confirm-ui.js +9 -6
- package/dist/resources/extensions/shared/dialog-frame.js +42 -0
- package/dist/resources/extensions/shared/interview-ui.js +42 -30
- package/dist/resources/extensions/shared/next-action-ui.js +6 -6
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
- package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
- package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
- package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +10 -5
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/skills.js +6 -0
- package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
- package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +8 -59
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +21 -72
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +50 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
- package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/README.md +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +3 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/context7/index.ts +15 -2
- package/src/resources/extensions/get-secrets-from-user.ts +17 -16
- package/src/resources/extensions/google-cli/index.ts +34 -0
- package/src/resources/extensions/google-cli/models.ts +57 -0
- package/src/resources/extensions/google-cli/package.json +11 -0
- package/src/resources/extensions/google-cli/readiness.ts +15 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
- package/src/resources/extensions/gsd/auto/loop.ts +74 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
- package/src/resources/extensions/gsd/auto/phases.ts +46 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
- package/src/resources/extensions/gsd/auto-post-unit.ts +43 -14
- package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
- package/src/resources/extensions/gsd/auto-start.ts +307 -56
- package/src/resources/extensions/gsd/auto.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
- package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
- package/src/resources/extensions/gsd/commands-usage.ts +110 -5
- package/src/resources/extensions/gsd/config-overlay.ts +19 -16
- package/src/resources/extensions/gsd/context-overlay.ts +24 -19
- package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
- package/src/resources/extensions/gsd/doctor-types.ts +2 -0
- package/src/resources/extensions/gsd/doctor.ts +6 -1
- package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
- package/src/resources/extensions/gsd/guided-flow.ts +5 -6
- package/src/resources/extensions/gsd/key-manager.ts +57 -14
- package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
- package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
- package/src/resources/extensions/gsd/preferences.ts +17 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
- package/src/resources/extensions/gsd/prompts/system.md +1 -3
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
- package/src/resources/extensions/gsd/repository-registry.ts +3 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -4
- package/src/resources/extensions/gsd/skill-activation.ts +292 -0
- package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
- package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
- package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
- package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
- package/src/resources/extensions/gsd/skill-scope.ts +63 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
- package/src/resources/extensions/gsd/skills.ts +75 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
- package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
- package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
- package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
- package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
- package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
- package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
- package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
- package/src/resources/extensions/gsd/vision-ask.ts +28 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
- package/src/resources/extensions/search-the-web/native-search.ts +60 -8
- package/src/resources/extensions/shared/confirm-ui.ts +8 -12
- package/src/resources/extensions/shared/dialog-frame.ts +71 -0
- package/src/resources/extensions/shared/interview-ui.ts +43 -42
- package/src/resources/extensions/shared/next-action-ui.ts +6 -6
- package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
- package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
- /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → _LIEWYP8ISVAPKAEEXxFr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → _LIEWYP8ISVAPKAEEXxFr}/_ssgManifest.js +0 -0
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
truncateToWidth,
|
|
37
37
|
type TUI,
|
|
38
38
|
} from "@gsd/pi-tui";
|
|
39
|
+
import { renderSharedDialogFrame } from "./dialog-frame.js";
|
|
39
40
|
import { mergeSideBySide } from "./layout-utils.js";
|
|
40
41
|
import { makeUI, INDENT } from "./ui.js";
|
|
41
42
|
|
|
@@ -126,6 +127,10 @@ const DIVIDER_CHARS = " │ ";
|
|
|
126
127
|
const DIVIDER_WIDTH = 3;
|
|
127
128
|
const PREVIEW_MAX_LINES = 20; // hard cap — keeps total ≤ 24 rows for single-question
|
|
128
129
|
|
|
130
|
+
function dialogContentWidth(width: number): number {
|
|
131
|
+
return width < 4 ? Math.max(1, width) : Math.max(1, width - 4);
|
|
132
|
+
}
|
|
133
|
+
|
|
129
134
|
// ─── Wrap-up screen ───────────────────────────────────────────────────────────
|
|
130
135
|
|
|
131
136
|
export async function showWrapUpScreen(
|
|
@@ -157,11 +162,12 @@ export async function showWrapUpScreen(
|
|
|
157
162
|
|
|
158
163
|
function render(width: number): string[] {
|
|
159
164
|
if (cachedLines) return cachedLines;
|
|
160
|
-
const
|
|
165
|
+
const contentWidth = dialogContentWidth(width);
|
|
166
|
+
const ui = makeUI(theme, contentWidth);
|
|
161
167
|
const lines: string[] = [];
|
|
162
168
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
163
169
|
|
|
164
|
-
push(ui.
|
|
170
|
+
push(ui.blank());
|
|
165
171
|
if (opts.progress) push(ui.meta(` ${opts.progress}`), ui.blank());
|
|
166
172
|
|
|
167
173
|
if (cursorIdx === 1) {
|
|
@@ -175,14 +181,11 @@ export async function showWrapUpScreen(
|
|
|
175
181
|
} else {
|
|
176
182
|
push(ui.actionUnselected(2, opts.keepGoingLabel, "Continue with another batch of questions."));
|
|
177
183
|
}
|
|
178
|
-
push(
|
|
179
|
-
ui.blank(),
|
|
180
|
-
ui.hints(["↑/↓ to choose", "1/2 to quick-select", "enter to confirm"]),
|
|
181
|
-
ui.bar(),
|
|
182
|
-
);
|
|
184
|
+
push(ui.blank());
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
const footer = ui.hints(["↑/↓ to choose", "1/2 to quick-select", "enter to confirm"])[0] ?? "";
|
|
187
|
+
cachedLines = renderSharedDialogFrame(theme, opts.headline, lines, width, { footer });
|
|
188
|
+
return cachedLines;
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
return {
|
|
@@ -469,11 +472,13 @@ export async function showInterviewRound(
|
|
|
469
472
|
// ── Review screen ────────────────────────────────────────────────
|
|
470
473
|
|
|
471
474
|
function renderReviewScreen(width: number): string[] {
|
|
472
|
-
const
|
|
475
|
+
const contentWidth = dialogContentWidth(width);
|
|
476
|
+
const title = opts.reviewHeadline ?? "Review your answers";
|
|
477
|
+
const ui = makeUI(theme, contentWidth);
|
|
473
478
|
const lines: string[] = [];
|
|
474
479
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
475
480
|
|
|
476
|
-
push(ui.
|
|
481
|
+
push(ui.blank());
|
|
477
482
|
|
|
478
483
|
for (let i = 0; i < questions.length; i++) {
|
|
479
484
|
const q = questions[i];
|
|
@@ -500,24 +505,22 @@ export async function showInterviewRound(
|
|
|
500
505
|
push(
|
|
501
506
|
ui.actionSelected(0, "Submit answers"),
|
|
502
507
|
ui.blank(),
|
|
503
|
-
ui.hints(["← to go back and edit", "enter to submit", `esc to ${opts.exitLabel ?? "end interview"}`]),
|
|
504
|
-
ui.bar(),
|
|
505
508
|
);
|
|
506
509
|
|
|
507
|
-
|
|
510
|
+
const footer = ui.hints(["← to go back and edit", "enter to submit", `esc to ${opts.exitLabel ?? "end interview"}`])[0] ?? "";
|
|
511
|
+
return renderSharedDialogFrame(theme, title, lines, width, { footer });
|
|
508
512
|
}
|
|
509
513
|
|
|
510
514
|
// ── Exit confirm screen ──────────────────────────────────────────
|
|
511
515
|
|
|
512
516
|
function renderExitConfirm(width: number): string[] {
|
|
513
|
-
const
|
|
517
|
+
const contentWidth = dialogContentWidth(width);
|
|
518
|
+
const title = opts.exitHeadline ?? "End interview?";
|
|
519
|
+
const ui = makeUI(theme, contentWidth);
|
|
514
520
|
const lines: string[] = [];
|
|
515
521
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
516
522
|
|
|
517
523
|
push(
|
|
518
|
-
ui.bar(),
|
|
519
|
-
ui.blank(),
|
|
520
|
-
ui.header(` ${opts.exitHeadline ?? "End interview?"}`),
|
|
521
524
|
ui.blank(),
|
|
522
525
|
ui.subtitle(" Answers from this batch won't be saved."),
|
|
523
526
|
ui.blank(),
|
|
@@ -538,13 +541,10 @@ export async function showInterviewRound(
|
|
|
538
541
|
} else {
|
|
539
542
|
push(ui.actionUnselected(2, exitActionLabel, "Exit and discard this batch of answers."));
|
|
540
543
|
}
|
|
541
|
-
push(
|
|
542
|
-
ui.blank(),
|
|
543
|
-
ui.hints(["↑/↓ to choose", "1/2 to quick-select", "enter to confirm"]),
|
|
544
|
-
ui.bar(),
|
|
545
|
-
);
|
|
544
|
+
push(ui.blank());
|
|
546
545
|
|
|
547
|
-
|
|
546
|
+
const footer = ui.hints(["↑/↓ to choose", "1/2 to quick-select", "enter to confirm"])[0] ?? "";
|
|
547
|
+
return renderSharedDialogFrame(theme, title, lines, width, { footer });
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
// ── Preview helpers ──────────────────────────────────────────────
|
|
@@ -648,17 +648,17 @@ export async function showInterviewRound(
|
|
|
648
648
|
if (showingExitConfirm) { cachedLines = renderExitConfirm(width); return cachedLines; }
|
|
649
649
|
if (showingReview) { cachedLines = renderReviewScreen(width); return cachedLines; }
|
|
650
650
|
|
|
651
|
+
const contentWidth = dialogContentWidth(width);
|
|
652
|
+
const title = questions[currentIdx]?.header || "GSD Interview";
|
|
651
653
|
const useSideBySide = questionHasAnyPreview()
|
|
652
|
-
&&
|
|
654
|
+
&& contentWidth >= (MIN_OPTIONS_WIDTH + MIN_PREVIEW_WIDTH + DIVIDER_WIDTH);
|
|
653
655
|
|
|
654
656
|
if (useSideBySide) {
|
|
655
657
|
// ── Preview path ──────────────────────────────────────
|
|
656
|
-
const ui = makeUI(theme,
|
|
658
|
+
const ui = makeUI(theme, contentWidth);
|
|
657
659
|
const lines: string[] = [];
|
|
658
660
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
659
661
|
|
|
660
|
-
push(ui.bar());
|
|
661
|
-
|
|
662
662
|
if (isMultiQuestion) {
|
|
663
663
|
const unanswered = questions.filter((_, i) => !isQuestionAnswered(i)).length;
|
|
664
664
|
const answeredSet = new Set(questions.map((_, i) => i).filter(i => isQuestionAnswered(i)));
|
|
@@ -680,12 +680,15 @@ export async function showInterviewRound(
|
|
|
680
680
|
// component: spinner/loader (1-2), status line (1), tool header (1),
|
|
681
681
|
// plus a safety margin for future additions.
|
|
682
682
|
const termRows = (typeof process !== "undefined" && process.stdout?.rows) || 24;
|
|
683
|
-
const footerLines =
|
|
683
|
+
const footerLines = 5; // body spacer + frame top/footer/bottom chrome
|
|
684
684
|
const tuiChrome = 5;
|
|
685
685
|
const maxBody = Math.min(PREVIEW_MAX_LINES, Math.max(6, termRows - lines.length - footerLines - tuiChrome));
|
|
686
686
|
|
|
687
|
-
const previewWidth = Math.max(
|
|
688
|
-
|
|
687
|
+
const previewWidth = Math.max(
|
|
688
|
+
MIN_PREVIEW_WIDTH,
|
|
689
|
+
Math.min(contentWidth - MIN_OPTIONS_WIDTH - DIVIDER_WIDTH, Math.floor(contentWidth * PREVIEW_RATIO)),
|
|
690
|
+
);
|
|
691
|
+
const leftWidth = Math.max(MIN_OPTIONS_WIDTH, contentWidth - previewWidth - DIVIDER_WIDTH);
|
|
689
692
|
|
|
690
693
|
const fullLeft = renderOptionsColumn(leftWidth);
|
|
691
694
|
const leftLines = fullLeft.slice(0, maxBody);
|
|
@@ -709,7 +712,7 @@ export async function showInterviewRound(
|
|
|
709
712
|
while (leftLines.length < maxBody) leftLines.push("");
|
|
710
713
|
while (rightLines.length < maxBody) rightLines.push("");
|
|
711
714
|
const divider = theme.fg("dim", DIVIDER_CHARS);
|
|
712
|
-
lines.push(...mergeSideBySide(leftLines, rightLines, leftWidth, divider,
|
|
715
|
+
lines.push(...mergeSideBySide(leftLines, rightLines, leftWidth, divider, contentWidth));
|
|
713
716
|
|
|
714
717
|
// Footer
|
|
715
718
|
push(ui.blank());
|
|
@@ -729,15 +732,15 @@ export async function showInterviewRound(
|
|
|
729
732
|
hints.push(isLast && allAnswered() ? "enter to review" : "enter to next");
|
|
730
733
|
}
|
|
731
734
|
hints.push("esc to exit");
|
|
732
|
-
|
|
735
|
+
const footer = ui.hints(hints)[0] ?? "";
|
|
733
736
|
|
|
734
|
-
cachedLines = lines;
|
|
735
|
-
return
|
|
737
|
+
cachedLines = renderSharedDialogFrame(theme, title, lines, width, { footer });
|
|
738
|
+
return cachedLines;
|
|
736
739
|
}
|
|
737
740
|
|
|
738
741
|
// ── Original path — no preview, untouched ────────────────
|
|
739
742
|
|
|
740
|
-
const ui = makeUI(theme,
|
|
743
|
+
const ui = makeUI(theme, contentWidth);
|
|
741
744
|
const lines: string[] = [];
|
|
742
745
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
743
746
|
|
|
@@ -745,8 +748,6 @@ export async function showInterviewRound(
|
|
|
745
748
|
const st = states[currentIdx];
|
|
746
749
|
const multiSel = isMultiSelect(currentIdx);
|
|
747
750
|
|
|
748
|
-
push(ui.bar());
|
|
749
|
-
|
|
750
751
|
// ── Progress header ────────────────────────────────────────────
|
|
751
752
|
if (isMultiQuestion) {
|
|
752
753
|
const unanswered = questions.filter((_, i) => !isQuestionAnswered(i)).length;
|
|
@@ -809,7 +810,7 @@ export async function showInterviewRound(
|
|
|
809
810
|
if (st.notesVisible || focusNotes) {
|
|
810
811
|
push(ui.blank(), ui.notesLabel(focusNotes));
|
|
811
812
|
if (focusNotes) {
|
|
812
|
-
for (const line of getEditor().render(
|
|
813
|
+
for (const line of getEditor().render(contentWidth - 2)) lines.push(truncateToWidth(` ${line}`, contentWidth));
|
|
813
814
|
} else if (st.notes) {
|
|
814
815
|
push(ui.notesText(st.notes));
|
|
815
816
|
}
|
|
@@ -833,10 +834,10 @@ export async function showInterviewRound(
|
|
|
833
834
|
hints.push(isLast && allAnswered() ? "enter to review" : "enter to next");
|
|
834
835
|
}
|
|
835
836
|
hints.push("esc to exit");
|
|
836
|
-
|
|
837
|
+
const footer = ui.hints(hints)[0] ?? "";
|
|
837
838
|
|
|
838
|
-
cachedLines = lines;
|
|
839
|
-
return
|
|
839
|
+
cachedLines = renderSharedDialogFrame(theme, title, lines, width, { footer });
|
|
840
|
+
return cachedLines;
|
|
840
841
|
}
|
|
841
842
|
|
|
842
843
|
return {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
45
45
|
import { type Theme } from "@gsd/pi-coding-agent";
|
|
46
46
|
import { Key, matchesKey, type TUI } from "@gsd/pi-tui";
|
|
47
|
+
import { renderSharedDialogFrame } from "./dialog-frame.js";
|
|
47
48
|
import { makeUI } from "./ui.js";
|
|
48
49
|
|
|
49
50
|
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
@@ -142,14 +143,14 @@ export async function showNextAction(
|
|
|
142
143
|
|
|
143
144
|
function render(width: number): string[] {
|
|
144
145
|
if (cachedLines) return cachedLines;
|
|
145
|
-
const
|
|
146
|
+
const contentWidth = Math.max(1, width - 4);
|
|
147
|
+
const ui = makeUI(theme, contentWidth);
|
|
146
148
|
const lines: string[] = [];
|
|
147
149
|
const push = (...rows: string[][]) => { for (const r of rows) lines.push(...r); };
|
|
148
150
|
|
|
149
151
|
// ── Header — uses success colour to signal completion ────────────
|
|
150
152
|
// Note: next-action intentionally uses "success" for its bar/title
|
|
151
153
|
// to distinguish it from regular accent-coloured screens.
|
|
152
|
-
push(ui.bar());
|
|
153
154
|
push(ui.blank());
|
|
154
155
|
push(ui.header(` ✓ ${opts.title}`));
|
|
155
156
|
|
|
@@ -192,11 +193,10 @@ export async function showNextAction(
|
|
|
192
193
|
|
|
193
194
|
// ── Footer ────────────────────────────────────────────────────────
|
|
194
195
|
const numHint = allActions.map((_, i) => `${i + 1}`).join("/");
|
|
195
|
-
|
|
196
|
-
push(ui.bar());
|
|
196
|
+
const footer = ui.hints([`↑/↓ to choose`, `${numHint} to quick-select`, `enter to confirm`])[0] ?? "";
|
|
197
197
|
|
|
198
|
-
cachedLines = lines;
|
|
199
|
-
return
|
|
198
|
+
cachedLines = renderSharedDialogFrame(theme, "GSD Next Action", lines, width, { footer });
|
|
199
|
+
return cachedLines;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
return { render, invalidate: () => { cachedLines = undefined; }, handleInput };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { stripVTControlCharacters } from "node:util";
|
|
4
|
+
|
|
5
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
6
|
+
|
|
7
|
+
import { showConfirm } from "../confirm-ui.js";
|
|
8
|
+
|
|
9
|
+
function assertFullOuterBorder(lines: string[], width: number): void {
|
|
10
|
+
assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
|
|
11
|
+
for (const [index, line] of lines.entries()) {
|
|
12
|
+
assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
|
|
13
|
+
}
|
|
14
|
+
const top = stripVTControlCharacters(lines[0] ?? "");
|
|
15
|
+
const bottom = stripVTControlCharacters(lines.at(-1) ?? "");
|
|
16
|
+
assert.match(top, /^[╭┌].*[╮┐]$/);
|
|
17
|
+
assert.match(bottom, /^[╰└].*[╯┘]$/);
|
|
18
|
+
for (let index = 1; index < lines.length - 1; index++) {
|
|
19
|
+
const line = stripVTControlCharacters(lines[index] ?? "");
|
|
20
|
+
assert.match(line, /^[│┃├]/, `line ${index} missing left border: ${line}`);
|
|
21
|
+
assert.match(line, /[│┃┤]$/, `line ${index} missing right border: ${line}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("showConfirm", () => {
|
|
26
|
+
it("renders the confirmation prompt inside a full border", async () => {
|
|
27
|
+
let rendered: string[] = [];
|
|
28
|
+
const theme = {
|
|
29
|
+
fg: (_color: string, text: string) => text,
|
|
30
|
+
bold: (text: string) => text,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const ctx = {
|
|
34
|
+
hasUI: true,
|
|
35
|
+
ui: {
|
|
36
|
+
custom: async (factory: any) => {
|
|
37
|
+
let resolved: boolean | undefined;
|
|
38
|
+
const component = factory({ requestRender() {} }, theme, null, (value: boolean) => {
|
|
39
|
+
resolved = value;
|
|
40
|
+
});
|
|
41
|
+
rendered = component.render(80);
|
|
42
|
+
component.handleInput("\r");
|
|
43
|
+
return resolved as never;
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const result = await showConfirm(ctx as any, {
|
|
49
|
+
title: "Confirm action",
|
|
50
|
+
message: "Proceed with the change?",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
assert.equal(result, true);
|
|
54
|
+
assertFullOuterBorder(rendered, 80);
|
|
55
|
+
assert.match(stripVTControlCharacters(rendered[0] ?? ""), /Confirm action/);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// gsd-pi — Shared interview UI dialog border contract
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { before, describe, it } from "node:test";
|
|
5
|
+
|
|
6
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
7
|
+
import { initTheme } from "@gsd/pi-coding-agent";
|
|
8
|
+
import {
|
|
9
|
+
showInterviewRound,
|
|
10
|
+
showWrapUpScreen,
|
|
11
|
+
type Question,
|
|
12
|
+
type RoundResult,
|
|
13
|
+
type WrapUpResult,
|
|
14
|
+
} from "../interview-ui.js";
|
|
15
|
+
|
|
16
|
+
const ANSI_PATTERN = /\x1b\[[0-9;?]*[ -/]*[@-~]/g;
|
|
17
|
+
const ENTER = "\r";
|
|
18
|
+
const ESC = "\x1b";
|
|
19
|
+
|
|
20
|
+
before(() => { initTheme(); });
|
|
21
|
+
|
|
22
|
+
type RenderWidget = {
|
|
23
|
+
render(width: number): string[];
|
|
24
|
+
handleInput(input: string): void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function stripAnsi(text: string): string {
|
|
28
|
+
return text.replace(ANSI_PATTERN, "");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function assertFullOuterBorder(lines: string[], width: number): void {
|
|
32
|
+
assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
|
|
33
|
+
|
|
34
|
+
for (const [index, line] of lines.entries()) {
|
|
35
|
+
assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const top = stripAnsi(lines[0] ?? "");
|
|
39
|
+
const bottom = stripAnsi(lines.at(-1) ?? "");
|
|
40
|
+
assert.match(top, /^╭.*╮$/, `top border missing full corners: ${top}`);
|
|
41
|
+
assert.match(bottom, /^╰.*╯$/, `bottom border missing full corners: ${bottom}`);
|
|
42
|
+
|
|
43
|
+
for (let index = 1; index < lines.length - 1; index++) {
|
|
44
|
+
const line = stripAnsi(lines[index] ?? "");
|
|
45
|
+
assert.match(line, /^[│├]/, `line ${index} missing left border: ${line}`);
|
|
46
|
+
assert.match(line, /[│┤]$/, `line ${index} missing right border: ${line}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mockTheme() {
|
|
51
|
+
return {
|
|
52
|
+
fg: (_color: string, text: string) => text,
|
|
53
|
+
bold: (text: string) => text,
|
|
54
|
+
dim: (text: string) => text,
|
|
55
|
+
italic: (text: string) => text,
|
|
56
|
+
strikethrough: (text: string) => text,
|
|
57
|
+
accent: (text: string) => text,
|
|
58
|
+
success: (text: string) => text,
|
|
59
|
+
warning: (text: string) => text,
|
|
60
|
+
error: (text: string) => text,
|
|
61
|
+
info: (text: string) => text,
|
|
62
|
+
muted: (text: string) => text,
|
|
63
|
+
dimmed: (text: string) => text,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function captureInterviewWidget(
|
|
68
|
+
questions: Question[],
|
|
69
|
+
): Promise<RenderWidget> {
|
|
70
|
+
let widget: RenderWidget | undefined;
|
|
71
|
+
|
|
72
|
+
await showInterviewRound(questions, {}, {
|
|
73
|
+
ui: {
|
|
74
|
+
custom: (factory: any) => {
|
|
75
|
+
widget = factory(
|
|
76
|
+
{ requestRender: () => {} },
|
|
77
|
+
mockTheme(),
|
|
78
|
+
{},
|
|
79
|
+
(_result: RoundResult) => {},
|
|
80
|
+
);
|
|
81
|
+
return Promise.resolve({ endInterview: false, answers: {} });
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
} as any);
|
|
85
|
+
|
|
86
|
+
assert.ok(widget, "interview widget should be created");
|
|
87
|
+
return widget;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function captureWrapUpWidget(): Promise<RenderWidget> {
|
|
91
|
+
let widget: RenderWidget | undefined;
|
|
92
|
+
|
|
93
|
+
await showWrapUpScreen({
|
|
94
|
+
headline: "Ready to wrap up?",
|
|
95
|
+
progress: "4 questions answered",
|
|
96
|
+
keepGoingLabel: "Keep going",
|
|
97
|
+
satisfiedLabel: "I'm satisfied",
|
|
98
|
+
}, {
|
|
99
|
+
ui: {
|
|
100
|
+
custom: (factory: any) => {
|
|
101
|
+
widget = factory(
|
|
102
|
+
{ requestRender: () => {} },
|
|
103
|
+
mockTheme(),
|
|
104
|
+
{},
|
|
105
|
+
(_result: WrapUpResult) => {},
|
|
106
|
+
);
|
|
107
|
+
return Promise.resolve({ satisfied: false });
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
} as any);
|
|
111
|
+
|
|
112
|
+
assert.ok(widget, "wrap-up widget should be created");
|
|
113
|
+
return widget;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
describe("interview-ui dialog borders", () => {
|
|
117
|
+
const questions: Question[] = [
|
|
118
|
+
{
|
|
119
|
+
id: "project_type",
|
|
120
|
+
header: "Project Type",
|
|
121
|
+
question: "What type of project?",
|
|
122
|
+
options: [
|
|
123
|
+
{ label: "Web App", description: "Frontend or full-stack" },
|
|
124
|
+
{ label: "CLI Tool", description: "Command-line utility" },
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
it("renders the main question screen with a full border", async () => {
|
|
130
|
+
const widget = await captureInterviewWidget(questions);
|
|
131
|
+
assertFullOuterBorder(widget.render(80), 80);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("renders the preview split screen with a full border", async () => {
|
|
135
|
+
const widget = await captureInterviewWidget([{
|
|
136
|
+
...questions[0],
|
|
137
|
+
options: [
|
|
138
|
+
{
|
|
139
|
+
label: "Web App",
|
|
140
|
+
description: "Frontend or full-stack",
|
|
141
|
+
preview: "### Stack\n\nUse React with a typed API boundary.",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
}]);
|
|
145
|
+
assertFullOuterBorder(widget.render(100), 100);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("renders review and exit confirmation screens with full borders", async () => {
|
|
149
|
+
const widget = await captureInterviewWidget(questions);
|
|
150
|
+
|
|
151
|
+
widget.handleInput(ENTER);
|
|
152
|
+
assertFullOuterBorder(widget.render(80), 80);
|
|
153
|
+
|
|
154
|
+
const exitWidget = await captureInterviewWidget(questions);
|
|
155
|
+
exitWidget.handleInput(ESC);
|
|
156
|
+
assertFullOuterBorder(exitWidget.render(80), 80);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("renders the wrap-up screen with a full border", async () => {
|
|
160
|
+
const widget = await captureWrapUpWidget();
|
|
161
|
+
assertFullOuterBorder(widget.render(80), 80);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -15,9 +15,27 @@
|
|
|
15
15
|
|
|
16
16
|
import { describe, it } from "node:test";
|
|
17
17
|
import assert from "node:assert/strict";
|
|
18
|
+
import { stripVTControlCharacters } from "node:util";
|
|
19
|
+
import { visibleWidth } from "@gsd/pi-tui";
|
|
18
20
|
|
|
19
21
|
import { showNextAction } from "../next-action-ui.js";
|
|
20
22
|
|
|
23
|
+
function assertFullOuterBorder(lines: string[], width: number): void {
|
|
24
|
+
assert.ok(lines.length >= 2, "dialog must include top and bottom borders");
|
|
25
|
+
for (const [index, line] of lines.entries()) {
|
|
26
|
+
assert.equal(visibleWidth(line), width, `line ${index} must fill dialog width`);
|
|
27
|
+
}
|
|
28
|
+
const top = stripVTControlCharacters(lines[0] ?? "");
|
|
29
|
+
const bottom = stripVTControlCharacters(lines.at(-1) ?? "");
|
|
30
|
+
assert.match(top, /^[╭┌].*[╮┐]$/);
|
|
31
|
+
assert.match(bottom, /^[╰└].*[╯┘]$/);
|
|
32
|
+
for (let index = 1; index < lines.length - 1; index++) {
|
|
33
|
+
const line = stripVTControlCharacters(lines[index] ?? "");
|
|
34
|
+
assert.match(line, /^[│┃├]/, `line ${index} missing left border: ${line}`);
|
|
35
|
+
assert.match(line, /[│┃┤]$/, `line ${index} missing right border: ${line}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
describe("showNextAction ctx.hasUI guard (#5125 lockup root protection)", () => {
|
|
22
40
|
it("returns 'not_yet' immediately when ctx.hasUI is false (no UI calls)", async () => {
|
|
23
41
|
let customCalled = 0;
|
|
@@ -152,4 +170,41 @@ describe("showNextAction ctx.hasUI guard (#5125 lockup root protection)", () =>
|
|
|
152
170
|
assert.equal(result, "beta", "TUI selection should be returned verbatim");
|
|
153
171
|
assert.equal(selectCalled, 0, "ctx.ui.select fallback must NOT fire when custom returns a value");
|
|
154
172
|
});
|
|
173
|
+
|
|
174
|
+
it("renders the interactive next-action menu inside a full border", async () => {
|
|
175
|
+
let rendered: string[] = [];
|
|
176
|
+
const theme = {
|
|
177
|
+
fg: (_color: string, text: string) => text,
|
|
178
|
+
bold: (text: string) => text,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const ctx = {
|
|
182
|
+
hasUI: true,
|
|
183
|
+
ui: {
|
|
184
|
+
custom: async (factory: any) => {
|
|
185
|
+
let resolved: string | undefined;
|
|
186
|
+
const component = factory({ requestRender() {} }, theme, null, (value: string) => {
|
|
187
|
+
resolved = value;
|
|
188
|
+
});
|
|
189
|
+
rendered = component.render(80);
|
|
190
|
+
component.handleInput("\r");
|
|
191
|
+
return resolved as never;
|
|
192
|
+
},
|
|
193
|
+
select: async () => undefined,
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const result = await showNextAction(ctx as any, {
|
|
198
|
+
title: "GSD — test",
|
|
199
|
+
summary: ["summary"],
|
|
200
|
+
actions: [
|
|
201
|
+
{ id: "alpha", label: "Alpha", description: "first", recommended: true },
|
|
202
|
+
{ id: "beta", label: "Beta", description: "second" },
|
|
203
|
+
],
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
assert.equal(result, "alpha");
|
|
207
|
+
assertFullOuterBorder(rendered, 80);
|
|
208
|
+
assert.match(stripVTControlCharacters(rendered[0] ?? ""), /GSD Next Action/);
|
|
209
|
+
});
|
|
155
210
|
});
|
|
@@ -31,7 +31,7 @@ GSD auto-discovers skills at session start and during auto-mode:
|
|
|
31
31
|
|
|
32
32
|
**Session start:** All discovered skills are enumerated and their names + descriptions are injected into the system prompt as `<available_skills>`.
|
|
33
33
|
|
|
34
|
-
**Auto-mode discovery:** `skill-discovery.ts`
|
|
34
|
+
**Auto-mode discovery:** `skill-discovery.ts` snapshots installed skill names at auto-mode start. When new skills appear mid-run, GSD reloads the resource catalog so they appear in the standard `<available_skills>` block (no separate XML injection).
|
|
35
35
|
|
|
36
36
|
**Manual reload:** Running `/reload` re-scans the skill directories and updates the available skills list mid-session.
|
|
37
37
|
</skill_discovery>
|
|
File without changes
|
|
File without changes
|