gsd-pi 2.73.1 → 2.74.0-dev.b741afb
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/cli-web-branch.d.ts +4 -3
- package/dist/cli-web-branch.js +10 -7
- package/dist/cli.js +184 -206
- package/dist/headless-query.js +4 -1
- package/dist/help-text.js +23 -0
- package/dist/logo.d.ts +1 -1
- package/dist/logo.js +1 -1
- package/dist/onboarding.js +59 -53
- package/dist/resource-loader.js +2 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
- package/dist/resources/extensions/gsd/auto/phases.js +60 -10
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +93 -57
- package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
- package/dist/resources/extensions/gsd/auto-start.js +23 -6
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-verification.js +88 -3
- package/dist/resources/extensions/gsd/auto.js +37 -10
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
- package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
- package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
- package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
- package/dist/resources/extensions/gsd/commands-do.js +79 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
- package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
- package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
- package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
- package/dist/resources/extensions/gsd/commands-ship.js +187 -0
- package/dist/resources/extensions/gsd/db-writer.js +3 -5
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/graph-context.js +66 -0
- package/dist/resources/extensions/gsd/gsd-db.js +321 -0
- package/dist/resources/extensions/gsd/index.js +15 -2
- package/dist/resources/extensions/gsd/md-importer.js +3 -4
- package/dist/resources/extensions/gsd/memory-store.js +19 -51
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
- package/dist/resources/extensions/gsd/notification-widget.js +2 -2
- package/dist/resources/extensions/gsd/preferences-models.js +43 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
- package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/dist/resources/extensions/gsd/state.js +66 -15
- package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
- package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
- package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
- package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
- package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
- package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
- package/dist/tsconfig.extensions.tsbuildinfo +1 -0
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +13 -5
- package/dist/update-cmd.js +4 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +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/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +3 -3
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/index.d.ts +3 -0
- package/packages/mcp-server/dist/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/index.js +3 -0
- package/packages/mcp-server/dist/index.js.map +1 -1
- package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
- package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
- package/packages/mcp-server/dist/readers/graph.js +548 -0
- package/packages/mcp-server/dist/readers/graph.js.map +1 -0
- package/packages/mcp-server/dist/readers/index.d.ts +2 -0
- package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
- package/packages/mcp-server/dist/readers/index.js +1 -0
- package/packages/mcp-server/dist/readers/index.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +65 -0
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/index.ts +15 -0
- package/packages/mcp-server/src/readers/graph.test.ts +426 -0
- package/packages/mcp-server/src/readers/graph.ts +708 -0
- package/packages/mcp-server/src/readers/index.ts +12 -0
- package/packages/mcp-server/src/server.ts +83 -0
- package/packages/mcp-server/tsconfig.json +1 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
- package/packages/native/package.json +2 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.json +1 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.js +12 -0
- package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/index.ts +4 -0
- package/packages/pi-ai/src/utils/overflow.ts +14 -1
- package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
- package/packages/pi-ai/tsconfig.json +1 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +9 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +94 -16
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +11 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
- package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +113 -21
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
- package/packages/pi-coding-agent/tsconfig.json +1 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +8 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +32 -3
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
- package/packages/pi-tui/src/tui.ts +31 -3
- package/packages/pi-tui/tsconfig.json +1 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
- package/packages/rpc-client/package.json +1 -1
- package/packages/rpc-client/tsconfig.json +1 -0
- package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
- package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
- package/src/resources/extensions/gsd/auto/phases.ts +90 -10
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -4
- package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
- package/src/resources/extensions/gsd/auto-post-unit.ts +107 -58
- package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
- package/src/resources/extensions/gsd/auto-start.ts +30 -6
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
- package/src/resources/extensions/gsd/auto-verification.ts +98 -3
- package/src/resources/extensions/gsd/auto.ts +38 -14
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
- package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
- package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
- package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
- package/src/resources/extensions/gsd/commands-do.ts +109 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
- package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
- package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
- package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
- package/src/resources/extensions/gsd/commands-ship.ts +219 -0
- package/src/resources/extensions/gsd/db-writer.ts +3 -5
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/graph-context.ts +85 -0
- package/src/resources/extensions/gsd/gsd-db.ts +467 -0
- package/src/resources/extensions/gsd/index.ts +18 -2
- package/src/resources/extensions/gsd/md-importer.ts +3 -5
- package/src/resources/extensions/gsd/memory-store.ts +31 -62
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
- package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
- package/src/resources/extensions/gsd/notification-widget.ts +2 -2
- package/src/resources/extensions/gsd/preferences-models.ts +41 -0
- package/src/resources/extensions/gsd/preferences-types.ts +12 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
- package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
- package/src/resources/extensions/gsd/state.ts +80 -17
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
- package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
- package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
- package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
- package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
- package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
- package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
- package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
- package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Qr27MOHx0lxRGnJvlhxxu → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
|
@@ -33,8 +33,12 @@ test("bootstrapAutoSession checks manual session override before preferences", (
|
|
|
33
33
|
assert.ok(manualIdx > -1, "auto-start.ts should read session model override first");
|
|
34
34
|
|
|
35
35
|
// resolveDefaultSessionModel() should still be called for fallback behavior
|
|
36
|
-
const preferredIdx = source.indexOf("const preferredModel =
|
|
37
|
-
assert.ok(preferredIdx > -1, "auto-start.ts should
|
|
36
|
+
const preferredIdx = source.indexOf("const preferredModel = ");
|
|
37
|
+
assert.ok(preferredIdx > -1, "auto-start.ts should build preferredModel");
|
|
38
|
+
assert.ok(
|
|
39
|
+
source.indexOf("resolveDefaultSessionModel(") > -1,
|
|
40
|
+
"auto-start.ts should call resolveDefaultSessionModel()",
|
|
41
|
+
);
|
|
38
42
|
|
|
39
43
|
// Session provider should be passed for bare model ID resolution
|
|
40
44
|
const withProviderIdx = source.indexOf("resolveDefaultSessionModel(ctx.model?.provider)");
|
|
@@ -47,6 +51,51 @@ test("bootstrapAutoSession checks manual session override before preferences", (
|
|
|
47
51
|
manualIdx < snapshotIdx && preferredIdx < snapshotIdx,
|
|
48
52
|
"manual override and preference fallback must be resolved before building startModelSnapshot",
|
|
49
53
|
);
|
|
54
|
+
|
|
55
|
+
// The validated preferred model must still appear as one of the snapshot
|
|
56
|
+
// sources so PREFERENCES.md continues to win over a stale settings.json
|
|
57
|
+
// default for built-in providers.
|
|
58
|
+
const snapshotBlock = source.slice(snapshotIdx, snapshotIdx + 400);
|
|
59
|
+
assert.ok(
|
|
60
|
+
snapshotBlock.includes("validatedPreferredModel") || snapshotBlock.includes("preferredModel"),
|
|
61
|
+
"startModelSnapshot must still consider preferredModel for built-in providers",
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("bootstrapAutoSession prefers session model over PREFERENCES.md when provider is custom (#4122)", () => {
|
|
66
|
+
// Custom providers (Ollama, vLLM, OpenAI-compatible proxies) live in
|
|
67
|
+
// ~/.gsd/agent/models.json, not PREFERENCES.md. When the user picks one
|
|
68
|
+
// via /gsd model, that selection must win over any preferredModel from
|
|
69
|
+
// PREFERENCES.md, otherwise auto-mode tries to start a built-in provider
|
|
70
|
+
// the user is not logged into and pauses with "Not logged in".
|
|
71
|
+
const customCheckIdx = source.indexOf("isCustomProvider(ctx.model?.provider)");
|
|
72
|
+
assert.ok(
|
|
73
|
+
customCheckIdx > -1,
|
|
74
|
+
"auto-start.ts should call isCustomProvider() to detect custom-model sessions",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// sessionProviderIsCustom must gate preferredModel resolution so that when the
|
|
78
|
+
// session provider is custom, preferredModel is null and PREFERENCES.md is
|
|
79
|
+
// skipped entirely — the snapshot then falls through to ctx.model.
|
|
80
|
+
const gateIdx = source.indexOf("sessionProviderIsCustom");
|
|
81
|
+
assert.ok(gateIdx > -1, "auto-start.ts should bind sessionProviderIsCustom");
|
|
82
|
+
|
|
83
|
+
const preferredIdx = source.indexOf("const preferredModel = ");
|
|
84
|
+
assert.ok(preferredIdx > -1, "auto-start.ts should build preferredModel");
|
|
85
|
+
|
|
86
|
+
const preferredBlock = source.slice(preferredIdx, preferredIdx + 200);
|
|
87
|
+
assert.ok(
|
|
88
|
+
preferredBlock.includes("sessionProviderIsCustom"),
|
|
89
|
+
"preferredModel must be gated on sessionProviderIsCustom so PREFERENCES.md is skipped for custom providers",
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const snapshotIdx = source.indexOf("const startModelSnapshot = ");
|
|
93
|
+
assert.ok(snapshotIdx > -1, "auto-start.ts should build startModelSnapshot");
|
|
94
|
+
|
|
95
|
+
assert.ok(
|
|
96
|
+
customCheckIdx < preferredIdx && preferredIdx < snapshotIdx,
|
|
97
|
+
"isCustomProvider() must be evaluated before preferredModel, which must be resolved before startModelSnapshot",
|
|
98
|
+
);
|
|
50
99
|
});
|
|
51
100
|
|
|
52
101
|
test("bootstrapAutoSession validates preferred model against live registry auth (#unconfigured-models)", () => {
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
|
|
8
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
function makeTmpBase(): string {
|
|
11
|
+
const base = join(tmpdir(), `gsd-backlog-test-${randomUUID()}`);
|
|
12
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
13
|
+
return base;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function cleanup(base: string): void {
|
|
17
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function backlogPath(base: string): string {
|
|
21
|
+
return join(base, ".gsd", "BACKLOG.md");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function writeBacklog(base: string, content: string): void {
|
|
25
|
+
writeFileSync(backlogPath(base), content, "utf-8");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readBacklog(base: string): string {
|
|
29
|
+
return readFileSync(backlogPath(base), "utf-8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Test the parsing/writing logic inline since the handler requires runtime context
|
|
33
|
+
|
|
34
|
+
interface BacklogItem {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
done: boolean;
|
|
38
|
+
note: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseBacklog(content: string): BacklogItem[] {
|
|
42
|
+
const items: BacklogItem[] = [];
|
|
43
|
+
for (const line of content.split("\n")) {
|
|
44
|
+
const match = line.match(/^- \[([ x])\] (999\.\d+) — (.+?)(?:\s*\((.+)\))?$/);
|
|
45
|
+
if (match) {
|
|
46
|
+
items.push({
|
|
47
|
+
id: match[2],
|
|
48
|
+
title: match[3].trim(),
|
|
49
|
+
done: match[1] === "x",
|
|
50
|
+
note: match[4] ?? "",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return items;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function formatBacklog(items: BacklogItem[]): string {
|
|
58
|
+
const lines = ["# Backlog\n"];
|
|
59
|
+
for (const item of items) {
|
|
60
|
+
const check = item.done ? "x" : " ";
|
|
61
|
+
const note = item.note ? ` (${item.note})` : "";
|
|
62
|
+
lines.push(`- [${check}] ${item.id} — ${item.title}${note}`);
|
|
63
|
+
}
|
|
64
|
+
lines.push("");
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Tests ──────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
test("backlog: parse empty file returns empty array", () => {
|
|
71
|
+
const items = parseBacklog("");
|
|
72
|
+
assert.equal(items.length, 0);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("backlog: parse valid entries", () => {
|
|
76
|
+
const content = `# Backlog
|
|
77
|
+
|
|
78
|
+
- [ ] 999.1 — OAuth support (added 2026-03-23)
|
|
79
|
+
- [x] 999.2 — Rate limiting (promoted 2026-03-24)
|
|
80
|
+
- [ ] 999.3 — Dark mode`;
|
|
81
|
+
|
|
82
|
+
const items = parseBacklog(content);
|
|
83
|
+
assert.equal(items.length, 3);
|
|
84
|
+
assert.equal(items[0].id, "999.1");
|
|
85
|
+
assert.equal(items[0].title, "OAuth support");
|
|
86
|
+
assert.equal(items[0].done, false);
|
|
87
|
+
assert.equal(items[0].note, "added 2026-03-23");
|
|
88
|
+
|
|
89
|
+
assert.equal(items[1].id, "999.2");
|
|
90
|
+
assert.equal(items[1].done, true);
|
|
91
|
+
assert.equal(items[1].note, "promoted 2026-03-24");
|
|
92
|
+
|
|
93
|
+
assert.equal(items[2].id, "999.3");
|
|
94
|
+
assert.equal(items[2].title, "Dark mode");
|
|
95
|
+
assert.equal(items[2].note, "");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("backlog: format roundtrips correctly", () => {
|
|
99
|
+
const items: BacklogItem[] = [
|
|
100
|
+
{ id: "999.1", title: "OAuth support", done: false, note: "added 2026-03-23" },
|
|
101
|
+
{ id: "999.2", title: "Rate limiting", done: true, note: "promoted 2026-03-24" },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
const formatted = formatBacklog(items);
|
|
105
|
+
const parsed = parseBacklog(formatted);
|
|
106
|
+
|
|
107
|
+
assert.equal(parsed.length, 2);
|
|
108
|
+
assert.equal(parsed[0].id, "999.1");
|
|
109
|
+
assert.equal(parsed[0].title, "OAuth support");
|
|
110
|
+
assert.equal(parsed[1].done, true);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("backlog: write and read from disk", () => {
|
|
114
|
+
const base = makeTmpBase();
|
|
115
|
+
try {
|
|
116
|
+
const items: BacklogItem[] = [
|
|
117
|
+
{ id: "999.1", title: "Test item", done: false, note: "added 2026-03-23" },
|
|
118
|
+
];
|
|
119
|
+
writeBacklog(base, formatBacklog(items));
|
|
120
|
+
|
|
121
|
+
assert.ok(existsSync(backlogPath(base)));
|
|
122
|
+
const content = readBacklog(base);
|
|
123
|
+
assert.ok(content.includes("999.1"));
|
|
124
|
+
assert.ok(content.includes("Test item"));
|
|
125
|
+
} finally {
|
|
126
|
+
cleanup(base);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("backlog: next ID increments correctly", () => {
|
|
131
|
+
const items: BacklogItem[] = [
|
|
132
|
+
{ id: "999.1", title: "First", done: false, note: "" },
|
|
133
|
+
{ id: "999.2", title: "Second", done: false, note: "" },
|
|
134
|
+
{ id: "999.5", title: "Fifth", done: false, note: "" },
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
let maxNum = 0;
|
|
138
|
+
for (const item of items) {
|
|
139
|
+
const match = item.id.match(/^999\.(\d+)$/);
|
|
140
|
+
if (match) {
|
|
141
|
+
const num = parseInt(match[1], 10);
|
|
142
|
+
if (num > maxNum) maxNum = num;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const nextId = `999.${maxNum + 1}`;
|
|
146
|
+
assert.equal(nextId, "999.6");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("backlog: empty backlog returns no items", () => {
|
|
150
|
+
const base = makeTmpBase();
|
|
151
|
+
try {
|
|
152
|
+
// No BACKLOG.md exists
|
|
153
|
+
assert.ok(!existsSync(backlogPath(base)));
|
|
154
|
+
// Would return empty array
|
|
155
|
+
} finally {
|
|
156
|
+
cleanup(base);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
// ─── Mock dispatcher to capture routed commands ─────────────────────────
|
|
5
|
+
|
|
6
|
+
let lastRouted: string | null = null;
|
|
7
|
+
let lastQuick: string | null = null;
|
|
8
|
+
|
|
9
|
+
const mockCtx = {
|
|
10
|
+
ui: {
|
|
11
|
+
notify: (_msg: string, _level: string) => {},
|
|
12
|
+
},
|
|
13
|
+
} as any;
|
|
14
|
+
|
|
15
|
+
// We test the keyword matching logic directly since the handler imports
|
|
16
|
+
// the dispatcher dynamically (which requires the full extension runtime).
|
|
17
|
+
|
|
18
|
+
// Inline the route-matching logic from commands-do.ts for unit testing.
|
|
19
|
+
interface Route {
|
|
20
|
+
keywords: string[];
|
|
21
|
+
command: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const ROUTES: Route[] = [
|
|
25
|
+
{ keywords: ["progress", "status", "dashboard", "how far", "where are we"], command: "status" },
|
|
26
|
+
{ keywords: ["auto", "autonomous", "run all", "keep going", "start auto"], command: "auto" },
|
|
27
|
+
{ keywords: ["stop", "halt", "abort"], command: "stop" },
|
|
28
|
+
{ keywords: ["pause", "break", "take a break"], command: "pause" },
|
|
29
|
+
{ keywords: ["history", "past", "what happened", "previous"], command: "history" },
|
|
30
|
+
{ keywords: ["doctor", "health", "diagnose", "check health"], command: "doctor" },
|
|
31
|
+
{ keywords: ["clean up", "cleanup", "remove old", "prune", "tidy"], command: "cleanup" },
|
|
32
|
+
{ keywords: ["ship", "pull request", "create pr", "open pr", "merge"], command: "ship" },
|
|
33
|
+
{ keywords: ["discuss", "talk about", "architecture", "design"], command: "discuss" },
|
|
34
|
+
{ keywords: ["undo", "revert", "rollback", "take back"], command: "undo" },
|
|
35
|
+
{ keywords: ["skip", "skip task", "skip this"], command: "skip" },
|
|
36
|
+
{ keywords: ["visualize", "viz", "graph", "chart", "show graph"], command: "visualize" },
|
|
37
|
+
{ keywords: ["capture", "note", "idea", "thought", "remember"], command: "capture" },
|
|
38
|
+
{ keywords: ["inspect", "database", "sqlite", "db state"], command: "inspect" },
|
|
39
|
+
{ keywords: ["session report", "session summary", "cost summary", "how much"], command: "session-report" },
|
|
40
|
+
{ keywords: ["backlog", "parking lot", "later", "someday"], command: "backlog" },
|
|
41
|
+
{ keywords: ["add tests", "write tests", "generate tests", "test coverage"], command: "add-tests" },
|
|
42
|
+
{ keywords: ["next", "step", "next step", "what's next"], command: "next" },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
interface MatchResult {
|
|
46
|
+
command: string;
|
|
47
|
+
remainingArgs: string;
|
|
48
|
+
score: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function matchRoute(input: string): MatchResult | null {
|
|
52
|
+
const lower = input.toLowerCase();
|
|
53
|
+
let bestMatch: MatchResult | null = null;
|
|
54
|
+
|
|
55
|
+
for (const route of ROUTES) {
|
|
56
|
+
for (const keyword of route.keywords) {
|
|
57
|
+
if (lower.includes(keyword)) {
|
|
58
|
+
const score = keyword.length;
|
|
59
|
+
if (!bestMatch || score > bestMatch.score) {
|
|
60
|
+
const idx = lower.indexOf(keyword);
|
|
61
|
+
const remaining = (input.slice(0, idx) + input.slice(idx + keyword.length)).trim();
|
|
62
|
+
bestMatch = { command: route.command, remainingArgs: remaining, score };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return bestMatch;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Tests ──────────────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
test("/gsd do: routes 'show me progress' to status", () => {
|
|
74
|
+
const match = matchRoute("show me progress");
|
|
75
|
+
assert.ok(match);
|
|
76
|
+
assert.equal(match.command, "status");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("/gsd do: routes 'run autonomously' to auto", () => {
|
|
80
|
+
const match = matchRoute("run autonomously");
|
|
81
|
+
assert.ok(match);
|
|
82
|
+
assert.equal(match.command, "auto");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("/gsd do: routes 'clean up old branches' to cleanup", () => {
|
|
86
|
+
const match = matchRoute("clean up old branches");
|
|
87
|
+
assert.ok(match);
|
|
88
|
+
assert.equal(match.command, "cleanup");
|
|
89
|
+
assert.equal(match.remainingArgs, "old branches");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("/gsd do: routes 'create pr for milestone' to ship", () => {
|
|
93
|
+
const match = matchRoute("create pr for milestone");
|
|
94
|
+
assert.ok(match);
|
|
95
|
+
assert.equal(match.command, "ship");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("/gsd do: routes 'add tests for S03' to add-tests", () => {
|
|
99
|
+
const match = matchRoute("add tests for S03");
|
|
100
|
+
assert.ok(match);
|
|
101
|
+
assert.equal(match.command, "add-tests");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("/gsd do: routes 'what is next' to next", () => {
|
|
105
|
+
const match = matchRoute("what's next");
|
|
106
|
+
assert.ok(match);
|
|
107
|
+
assert.equal(match.command, "next");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("/gsd do: returns null for unrecognized input", () => {
|
|
111
|
+
const match = matchRoute("florbinate the gizmo");
|
|
112
|
+
assert.equal(match, null);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("/gsd do: prefers longer keyword match", () => {
|
|
116
|
+
// "check health" (12 chars) should beat "health" (6 chars)
|
|
117
|
+
const match = matchRoute("check health of the system");
|
|
118
|
+
assert.ok(match);
|
|
119
|
+
assert.equal(match.command, "doctor");
|
|
120
|
+
assert.ok(match.score >= 12);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("/gsd do: routes 'session report' to session-report", () => {
|
|
124
|
+
const match = matchRoute("show me the session report");
|
|
125
|
+
assert.ok(match);
|
|
126
|
+
assert.equal(match.command, "session-report");
|
|
127
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
// Test the filtering logic used by /gsd pr-branch.
|
|
5
|
+
// Full integration requires git operations, so we test the path filtering.
|
|
6
|
+
|
|
7
|
+
test("pr-branch: identifies .gsd/ paths", () => {
|
|
8
|
+
const files = [
|
|
9
|
+
".gsd/milestones/M001/ROADMAP.md",
|
|
10
|
+
".gsd/metrics.json",
|
|
11
|
+
"src/main.ts",
|
|
12
|
+
"package.json",
|
|
13
|
+
".planning/PLAN.md",
|
|
14
|
+
"PLAN.md",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const codeFiles = files.filter(
|
|
18
|
+
(f) => !f.startsWith(".gsd/") && !f.startsWith(".planning/") && f !== "PLAN.md",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
assert.deepEqual(codeFiles, ["src/main.ts", "package.json"]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("pr-branch: all .gsd/ files returns empty", () => {
|
|
25
|
+
const files = [
|
|
26
|
+
".gsd/milestones/M001/ROADMAP.md",
|
|
27
|
+
".gsd/metrics.json",
|
|
28
|
+
".gsd/BACKLOG.md",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const codeFiles = files.filter(
|
|
32
|
+
(f) => !f.startsWith(".gsd/") && !f.startsWith(".planning/") && f !== "PLAN.md",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
assert.equal(codeFiles.length, 0);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("pr-branch: mixed commits with code changes", () => {
|
|
39
|
+
const files = [
|
|
40
|
+
".gsd/milestones/M001/ROADMAP.md",
|
|
41
|
+
"src/auth.ts",
|
|
42
|
+
"src/auth.test.ts",
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const hasCodeChanges = files.some(
|
|
46
|
+
(f) => !f.startsWith(".gsd/") && !f.startsWith(".planning/") && f !== "PLAN.md",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
assert.ok(hasCodeChanges);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("pr-branch: --dry-run flag", () => {
|
|
53
|
+
assert.ok("--dry-run".includes("--dry-run"));
|
|
54
|
+
assert.ok(!"--name my-branch".includes("--dry-run"));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("pr-branch: --name flag parsing", () => {
|
|
58
|
+
const args = "--name my-clean-pr";
|
|
59
|
+
const nameMatch = args.match(/--name\s+(\S+)/);
|
|
60
|
+
assert.ok(nameMatch);
|
|
61
|
+
assert.equal(nameMatch[1], "my-clean-pr");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("pr-branch: default branch name", () => {
|
|
65
|
+
const currentBranch = "feat/add-auth";
|
|
66
|
+
const prBranch = `pr/${currentBranch}`;
|
|
67
|
+
assert.equal(prBranch, "pr/feat/add-auth");
|
|
68
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
// Test the formatting logic used by session-report.
|
|
5
|
+
// The actual handler requires runtime context (metrics module), so we
|
|
6
|
+
// test the core formatting and aggregation patterns.
|
|
7
|
+
|
|
8
|
+
test("session-report: format cost correctly", () => {
|
|
9
|
+
// Simple cost formatting test
|
|
10
|
+
const formatCost = (cost: number): string => {
|
|
11
|
+
if (cost < 0.01) return "<$0.01";
|
|
12
|
+
return `$${cost.toFixed(2)}`;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
assert.equal(formatCost(0), "<$0.01");
|
|
16
|
+
assert.equal(formatCost(0.005), "<$0.01");
|
|
17
|
+
assert.equal(formatCost(1.5), "$1.50");
|
|
18
|
+
assert.equal(formatCost(10.999), "$11.00");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("session-report: format token count", () => {
|
|
22
|
+
const formatTokenCount = (count: number): string => {
|
|
23
|
+
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M`;
|
|
24
|
+
if (count >= 1_000) return `${(count / 1_000).toFixed(1)}K`;
|
|
25
|
+
return String(count);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
assert.equal(formatTokenCount(500), "500");
|
|
29
|
+
assert.equal(formatTokenCount(1500), "1.5K");
|
|
30
|
+
assert.equal(formatTokenCount(1_200_000), "1.2M");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("session-report: aggregate by model", () => {
|
|
34
|
+
interface UnitMetric {
|
|
35
|
+
model: string;
|
|
36
|
+
cost: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const units: UnitMetric[] = [
|
|
40
|
+
{ model: "opus", cost: 1.0 },
|
|
41
|
+
{ model: "opus", cost: 0.8 },
|
|
42
|
+
{ model: "sonnet", cost: 0.3 },
|
|
43
|
+
{ model: "sonnet", cost: 0.5 },
|
|
44
|
+
{ model: "sonnet", cost: 0.2 },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const byModel = new Map<string, { count: number; cost: number }>();
|
|
48
|
+
for (const u of units) {
|
|
49
|
+
const existing = byModel.get(u.model) ?? { count: 0, cost: 0 };
|
|
50
|
+
existing.count++;
|
|
51
|
+
existing.cost += u.cost;
|
|
52
|
+
byModel.set(u.model, existing);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const opus = byModel.get("opus")!;
|
|
56
|
+
assert.equal(opus.count, 2);
|
|
57
|
+
assert.ok(Math.abs(opus.cost - 1.8) < 0.01);
|
|
58
|
+
|
|
59
|
+
const sonnet = byModel.get("sonnet")!;
|
|
60
|
+
assert.equal(sonnet.count, 3);
|
|
61
|
+
assert.ok(Math.abs(sonnet.cost - 1.0) < 0.01);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("session-report: --json flag detection", () => {
|
|
65
|
+
const args1 = "--json";
|
|
66
|
+
const args2 = "--save --json";
|
|
67
|
+
const args3 = "something else";
|
|
68
|
+
|
|
69
|
+
assert.ok(args1.includes("--json"));
|
|
70
|
+
assert.ok(args2.includes("--json"));
|
|
71
|
+
assert.ok(!args3.includes("--json"));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("session-report: --save flag detection", () => {
|
|
75
|
+
const args1 = "--save";
|
|
76
|
+
const args2 = "--save --json";
|
|
77
|
+
const args3 = "";
|
|
78
|
+
|
|
79
|
+
assert.ok(args1.includes("--save"));
|
|
80
|
+
assert.ok(args2.includes("--save"));
|
|
81
|
+
assert.ok(!args3.includes("--save"));
|
|
82
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
// Test the PR content generation logic used by /gsd ship.
|
|
5
|
+
// Full integration requires gh CLI + git, so we test the text generation.
|
|
6
|
+
|
|
7
|
+
test("ship: generates TL;DR format", () => {
|
|
8
|
+
// Simulate generatePRContent output structure
|
|
9
|
+
const milestoneId = "M001";
|
|
10
|
+
const milestoneTitle = "User authentication system";
|
|
11
|
+
|
|
12
|
+
const title = `feat: ${milestoneTitle}`;
|
|
13
|
+
assert.equal(title, "feat: User authentication system");
|
|
14
|
+
assert.ok(title.length < 80); // PR title should be short
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("ship: --dry-run flag detection", () => {
|
|
18
|
+
const args1 = "--dry-run";
|
|
19
|
+
const args2 = "--draft --dry-run";
|
|
20
|
+
const args3 = "--draft";
|
|
21
|
+
|
|
22
|
+
assert.ok(args1.includes("--dry-run"));
|
|
23
|
+
assert.ok(args2.includes("--dry-run"));
|
|
24
|
+
assert.ok(!args3.includes("--dry-run"));
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("ship: --base flag parsing", () => {
|
|
28
|
+
const args = "--base develop --draft";
|
|
29
|
+
const baseMatch = args.match(/--base\s+(\S+)/);
|
|
30
|
+
assert.ok(baseMatch);
|
|
31
|
+
assert.equal(baseMatch[1], "develop");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("ship: --base flag absent defaults", () => {
|
|
35
|
+
const args = "--draft";
|
|
36
|
+
const baseMatch = args.match(/--base\s+(\S+)/);
|
|
37
|
+
assert.equal(baseMatch, null);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("ship: --force flag detection", () => {
|
|
41
|
+
const args1 = "--force";
|
|
42
|
+
const args2 = "";
|
|
43
|
+
|
|
44
|
+
assert.ok(args1.includes("--force"));
|
|
45
|
+
assert.ok(!args2.includes("--force"));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("ship: change type checklist format", () => {
|
|
49
|
+
const checklist = [
|
|
50
|
+
"- [x] `feat` — New feature or capability",
|
|
51
|
+
"- [ ] `fix` — Bug fix",
|
|
52
|
+
"- [ ] `refactor` — Code restructuring",
|
|
53
|
+
"- [ ] `test` — Adding or updating tests",
|
|
54
|
+
"- [ ] `docs` — Documentation only",
|
|
55
|
+
"- [ ] `chore` — Build, CI, or tooling changes",
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
// Verify format matches CONTRIBUTING.md expectations
|
|
59
|
+
for (const line of checklist) {
|
|
60
|
+
assert.match(line, /^- \[[ x]\] `\w+` — .+$/);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("ship: PR body contains required sections", () => {
|
|
65
|
+
const requiredSections = ["## TL;DR", "## Change type"];
|
|
66
|
+
const body = "## TL;DR\n\n**What:** Ship M001\n\n## Change type\n\n- [x] `feat`";
|
|
67
|
+
|
|
68
|
+
for (const section of requiredSections) {
|
|
69
|
+
assert.ok(body.includes(section), `Missing section: ${section}`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
@@ -217,6 +217,20 @@ describe("workflow command handler", () => {
|
|
|
217
217
|
);
|
|
218
218
|
});
|
|
219
219
|
|
|
220
|
+
it("preserves quoted workflow run overrides (#4130)", async () => {
|
|
221
|
+
const { parseWorkflowRunArgs } = await import("../commands/handlers/workflow.ts");
|
|
222
|
+
assert.deepStrictEqual(
|
|
223
|
+
parseWorkflowRunArgs('demo-workflow target="multi word target" region=\'us east\''),
|
|
224
|
+
{
|
|
225
|
+
defName: "demo-workflow",
|
|
226
|
+
overrides: {
|
|
227
|
+
target: "multi word target",
|
|
228
|
+
region: "us east",
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
|
|
220
234
|
it("'/gsd workflow run nonexistent' shows error for missing definition", async () => {
|
|
221
235
|
const { handled, notifications } = await callHandler("workflow run nonexistent-def-12345");
|
|
222
236
|
assert.ok(handled, "should be handled");
|