gsd-pi 2.57.0-dev.f22a903 → 2.58.0-dev.778d6ac
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/README.md +1 -1
- package/dist/cli.js +49 -35
- package/dist/headless-ui.d.ts +17 -0
- package/dist/headless-ui.js +97 -3
- package/dist/headless.js +67 -6
- package/dist/help-text.js +1 -0
- package/dist/onboarding.js +44 -0
- package/dist/resource-loader.js +16 -1
- package/dist/resources/agents/researcher.md +1 -1
- package/dist/resources/extensions/ask-user-questions.js +16 -3
- package/dist/resources/extensions/async-jobs/extension-manifest.json +1 -1
- package/dist/resources/extensions/bg-shell/extension-manifest.json +1 -1
- package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/dist/resources/extensions/claude-code-cli/partial-builder.js +14 -6
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +59 -36
- package/dist/resources/extensions/context7/extension-manifest.json +1 -1
- package/dist/resources/extensions/get-secrets-from-user.js +8 -5
- package/dist/resources/extensions/google-search/extension-manifest.json +1 -1
- package/dist/resources/extensions/google-search/index.js +2 -1
- package/dist/resources/extensions/gsd/auto/phases.js +25 -21
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
- package/dist/resources/extensions/gsd/auto-dashboard.js +37 -20
- package/dist/resources/extensions/gsd/auto-dispatch.js +17 -2
- package/dist/resources/extensions/gsd/auto-model-selection.js +26 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +16 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +1 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +13 -5
- package/dist/resources/extensions/gsd/auto-start.js +35 -22
- package/dist/resources/extensions/gsd/auto-worktree.js +196 -12
- package/dist/resources/extensions/gsd/auto.js +4 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +80 -8
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -18
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +44 -11
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -0
- package/dist/resources/extensions/gsd/captures.js +56 -4
- package/dist/resources/extensions/gsd/db-writer.js +116 -8
- package/dist/resources/extensions/gsd/doctor-git-checks.js +28 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +2 -1
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +5 -4
- package/dist/resources/extensions/gsd/doctor.js +3 -1
- package/dist/resources/extensions/gsd/error-classifier.js +13 -10
- package/dist/resources/extensions/gsd/extension-manifest.json +16 -1
- package/dist/resources/extensions/gsd/forensics.js +123 -20
- package/dist/resources/extensions/gsd/git-service.js +23 -1
- package/dist/resources/extensions/gsd/gitignore.js +33 -0
- package/dist/resources/extensions/gsd/gsd-db.js +36 -9
- package/dist/resources/extensions/gsd/guided-flow.js +106 -44
- package/dist/resources/extensions/gsd/health-widget-core.js +31 -0
- package/dist/resources/extensions/gsd/health-widget.js +17 -0
- package/dist/resources/extensions/gsd/index.js +1 -1
- package/dist/resources/extensions/gsd/memory-extractor.js +7 -0
- package/dist/resources/extensions/gsd/migrate-external.js +8 -1
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +45 -0
- package/dist/resources/extensions/gsd/model-cost-table.js +18 -0
- package/dist/resources/extensions/gsd/model-router.js +35 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +17 -0
- package/dist/resources/extensions/gsd/notifications.js +16 -1
- package/dist/resources/extensions/gsd/parallel-eligibility.js +13 -2
- package/dist/resources/extensions/gsd/parallel-merge.js +78 -5
- package/dist/resources/extensions/gsd/parsers-legacy.js +20 -3
- package/dist/resources/extensions/gsd/paths.js +43 -0
- package/dist/resources/extensions/gsd/preferences-models.js +14 -1
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences.js +13 -16
- package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -2
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +2 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -0
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -0
- package/dist/resources/extensions/gsd/repo-identity.js +205 -11
- package/dist/resources/extensions/gsd/rethink.js +5 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +5 -4
- package/dist/resources/extensions/gsd/state.js +85 -27
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +20 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +34 -71
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +12 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +29 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +14 -3
- package/dist/resources/extensions/gsd/triage-resolution.js +22 -7
- package/dist/resources/extensions/gsd/undo.js +2 -2
- package/dist/resources/extensions/gsd/unit-ownership.js +164 -33
- package/dist/resources/extensions/gsd/verdict-parser.js +20 -8
- package/dist/resources/extensions/gsd/workflow-manifest.js +24 -5
- package/dist/resources/extensions/gsd/workflow-projections.js +95 -63
- package/dist/resources/extensions/gsd/workflow-reconcile.js +35 -5
- package/dist/resources/extensions/gsd/workspace-index.js +24 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +105 -1
- package/dist/resources/extensions/gsd/worktree-resolver.js +20 -3
- package/dist/resources/extensions/mcp-client/index.js +11 -7
- package/dist/resources/extensions/ollama/index.js +112 -0
- package/dist/resources/extensions/ollama/model-capabilities.js +115 -0
- package/dist/resources/extensions/ollama/ollama-client.js +168 -0
- package/dist/resources/extensions/ollama/ollama-commands.js +194 -0
- package/dist/resources/extensions/ollama/ollama-discovery.js +69 -0
- package/dist/resources/extensions/ollama/ollama-tool.js +184 -0
- package/dist/resources/extensions/ollama/types.js +2 -0
- package/dist/resources/extensions/search-the-web/extension-manifest.json +1 -1
- package/dist/resources/extensions/shared/interview-ui.js +11 -1
- package/dist/resources/skills/create-gsd-extension/SKILL.md +5 -3
- package/dist/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +5 -4
- package/dist/resources/skills/create-gsd-extension/workflows/add-capability.md +2 -2
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +4 -4
- package/dist/resources/skills/create-gsd-extension/workflows/debug-extension.md +5 -3
- package/dist/startup-model-validation.d.ts +39 -0
- package/dist/startup-model-validation.js +50 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- 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/react-loadable-manifest.json +1 -1
- 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 +2 -2
- 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_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 +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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 +2 -2
- 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_client-reference-manifest.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_client-reference-manifest.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_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_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_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_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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_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_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_client-reference-manifest.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.js +1 -1
- 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_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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +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 +16 -16
- package/dist/web/standalone/.next/server/chunks/2229.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-61d3afac6d0f0ce7.js → webpack-a1c1e452c6b32d04.js} +1 -1
- package/dist/web/standalone/.next/static/css/f6e8833d46e738d8.css +1 -0
- package/dist/web-mode.js +2 -1
- package/package.json +2 -2
- package/packages/daemon/src/cli.ts +49 -0
- package/packages/daemon/src/daemon.test.ts +104 -1
- package/packages/daemon/src/daemon.ts +24 -1
- package/packages/daemon/src/discord-bot.ts +73 -3
- package/packages/daemon/src/event-bridge.ts +15 -9
- package/packages/daemon/src/event-formatter.ts +30 -2
- package/packages/daemon/src/index.ts +9 -0
- package/packages/daemon/src/launchd.test.ts +356 -0
- package/packages/daemon/src/launchd.ts +242 -0
- package/packages/daemon/src/message-batcher.test.ts +2 -2
- package/packages/daemon/src/message-batcher.ts +9 -3
- package/packages/daemon/src/orchestrator.test.ts +1 -0
- package/packages/daemon/src/orchestrator.ts +106 -2
- package/packages/native/dist/ast/index.js +9 -5
- package/packages/native/dist/ast/types.js +2 -1
- package/packages/native/dist/clipboard/index.js +12 -7
- package/packages/native/dist/clipboard/types.js +2 -1
- package/packages/native/dist/diff/index.js +12 -7
- package/packages/native/dist/diff/types.js +2 -1
- package/packages/native/dist/fd/index.js +6 -3
- package/packages/native/dist/fd/types.js +2 -1
- package/packages/native/dist/glob/index.js +9 -5
- package/packages/native/dist/glob/types.js +2 -1
- package/packages/native/dist/grep/index.js +9 -5
- package/packages/native/dist/grep/types.js +2 -1
- package/packages/native/dist/gsd-parser/index.js +18 -11
- package/packages/native/dist/gsd-parser/types.js +2 -1
- package/packages/native/dist/highlight/index.js +12 -7
- package/packages/native/dist/highlight/types.js +2 -1
- package/packages/native/dist/html/index.js +6 -3
- package/packages/native/dist/html/types.js +2 -1
- package/packages/native/dist/image/index.js +10 -5
- package/packages/native/dist/image/types.js +7 -4
- package/packages/native/dist/index.js +70 -17
- package/packages/native/dist/json-parse/index.js +13 -8
- package/packages/native/dist/native.js +47 -10
- package/packages/native/dist/ps/index.js +15 -9
- package/packages/native/dist/ps/types.js +2 -1
- package/packages/native/dist/stream-process/index.js +12 -7
- package/packages/native/dist/text/index.js +24 -14
- package/packages/native/dist/text/types.js +5 -2
- package/packages/native/dist/truncate/index.js +12 -7
- package/packages/native/dist/ttsr/index.js +12 -7
- package/packages/native/dist/ttsr/types.js +2 -1
- package/packages/native/dist/xxhash/index.js +9 -5
- package/packages/native/package.json +19 -19
- package/packages/native/src/__tests__/module-compat.test.mjs +91 -0
- package/packages/native/src/native.ts +9 -8
- package/packages/pi-agent-core/dist/agent-loop.js +3 -2
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/proxy.d.ts +1 -1
- package/packages/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/proxy.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +45 -0
- package/packages/pi-agent-core/src/agent-loop.ts +3 -2
- package/packages/pi-agent-core/src/proxy.ts +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +1 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- 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/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +19 -2
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js +25 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -0
- package/packages/pi-ai/dist/types.d.ts +3 -3
- 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/dist/utils/json-parse.d.ts +3 -0
- package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/json-parse.js +24 -1
- package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts +37 -0
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/repair-tool-json.js +75 -0
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +73 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -0
- package/packages/pi-ai/src/env-api-keys.ts +1 -0
- package/packages/pi-ai/src/index.ts +1 -0
- package/packages/pi-ai/src/providers/anthropic-shared.test.ts +29 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +17 -2
- package/packages/pi-ai/src/types.ts +3 -2
- package/packages/pi-ai/src/utils/json-parse.ts +28 -1
- package/packages/pi-ai/src/utils/repair-tool-json.ts +88 -0
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +102 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +31 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +17 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +62 -2
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js +176 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/exec.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/exec.js +3 -1
- package/packages/pi-coding-agent/dist/core/exec.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.d.ts +28 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.js +37 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.test.js +63 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-manifest.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.d.ts +19 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.js +115 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.test.js +109 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-sort.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.d.ts +44 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.js +97 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.test.js +181 -0
- package/packages/pi-coding-agent/dist/core/image-overflow-recovery.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -1
- package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +3 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +3 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +31 -2
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/messages.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/messages.test.js +86 -0
- package/packages/pi-coding-agent/dist/core/messages.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +12 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +48 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +193 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +10 -3
- package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +13 -4
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.d.ts +16 -0
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +80 -0
- package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +2 -2
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- 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 +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/remote-terminal.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/remote-terminal.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/remote-terminal.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/remote-terminal.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +38 -1
- package/packages/pi-coding-agent/src/core/compaction/compaction.test.ts +236 -0
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +94 -1
- package/packages/pi-coding-agent/src/core/exec.ts +3 -1
- package/packages/pi-coding-agent/src/core/extensions/extension-manifest.test.ts +77 -0
- package/packages/pi-coding-agent/src/core/extensions/extension-manifest.ts +62 -0
- package/packages/pi-coding-agent/src/core/extensions/extension-sort.test.ts +134 -0
- package/packages/pi-coding-agent/src/core/extensions/extension-sort.ts +137 -0
- package/packages/pi-coding-agent/src/core/extensions/index.ts +4 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
- package/packages/pi-coding-agent/src/core/image-overflow-recovery.test.ts +228 -0
- package/packages/pi-coding-agent/src/core/image-overflow-recovery.ts +118 -0
- package/packages/pi-coding-agent/src/core/index.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +3 -0
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +3 -0
- package/packages/pi-coding-agent/src/core/messages.test.ts +114 -0
- package/packages/pi-coding-agent/src/core/messages.ts +29 -2
- package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
- package/packages/pi-coding-agent/src/core/resource-loader.ts +20 -1
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +255 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +52 -1
- package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +11 -3
- package/packages/pi-coding-agent/src/core/tools/read.ts +14 -4
- package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +92 -0
- package/packages/pi-coding-agent/src/index.ts +6 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -0
- package/packages/pi-coding-agent/src/modes/rpc/remote-terminal.ts +6 -0
- package/packages/pi-tui/dist/terminal.d.ts +2 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +9 -0
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +9 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/terminal.ts +14 -0
- package/packages/pi-tui/src/tui.ts +8 -0
- package/pkg/package.json +1 -1
- package/scripts/ensure-workspace-builds.cjs +45 -14
- package/src/resources/agents/researcher.md +1 -1
- package/src/resources/extensions/ask-user-questions.ts +21 -3
- package/src/resources/extensions/async-jobs/extension-manifest.json +1 -1
- package/src/resources/extensions/bg-shell/extension-manifest.json +1 -1
- package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
- package/src/resources/extensions/claude-code-cli/partial-builder.ts +13 -6
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +63 -35
- package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +28 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -1
- package/src/resources/extensions/context7/extension-manifest.json +1 -1
- package/src/resources/extensions/get-secrets-from-user.ts +8 -5
- package/src/resources/extensions/google-search/extension-manifest.json +1 -1
- package/src/resources/extensions/google-search/index.ts +2 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +43 -34
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
- package/src/resources/extensions/gsd/auto-dashboard.ts +37 -19
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
- package/src/resources/extensions/gsd/auto-model-selection.ts +26 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +1 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -5
- package/src/resources/extensions/gsd/auto-start.ts +35 -26
- package/src/resources/extensions/gsd/auto-worktree.ts +190 -9
- package/src/resources/extensions/gsd/auto.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +31 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +85 -8
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +38 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +31 -19
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +50 -11
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +75 -0
- package/src/resources/extensions/gsd/captures.ts +63 -3
- package/src/resources/extensions/gsd/db-writer.ts +140 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +26 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +2 -1
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +5 -4
- package/src/resources/extensions/gsd/doctor.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +14 -11
- package/src/resources/extensions/gsd/extension-manifest.json +16 -1
- package/src/resources/extensions/gsd/forensics.ts +144 -20
- package/src/resources/extensions/gsd/git-service.ts +26 -3
- package/src/resources/extensions/gsd/gitignore.ts +33 -0
- package/src/resources/extensions/gsd/gsd-db.ts +43 -7
- package/src/resources/extensions/gsd/guided-flow.ts +114 -45
- package/src/resources/extensions/gsd/health-widget-core.ts +34 -0
- package/src/resources/extensions/gsd/health-widget.ts +17 -0
- package/src/resources/extensions/gsd/index.ts +1 -0
- package/src/resources/extensions/gsd/memory-extractor.ts +8 -0
- package/src/resources/extensions/gsd/migrate-external.ts +9 -1
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +56 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +19 -0
- package/src/resources/extensions/gsd/model-router.ts +35 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +17 -0
- package/src/resources/extensions/gsd/notifications.ts +16 -0
- package/src/resources/extensions/gsd/parallel-eligibility.ts +15 -2
- package/src/resources/extensions/gsd/parallel-merge.ts +87 -4
- package/src/resources/extensions/gsd/parsers-legacy.ts +22 -3
- package/src/resources/extensions/gsd/paths.ts +42 -0
- package/src/resources/extensions/gsd/preferences-models.ts +14 -1
- package/src/resources/extensions/gsd/preferences-types.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +13 -15
- package/src/resources/extensions/gsd/prompt-loader.ts +4 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -2
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -1
- package/src/resources/extensions/gsd/prompts/forensics.md +2 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -0
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -0
- package/src/resources/extensions/gsd/repo-identity.ts +186 -11
- package/src/resources/extensions/gsd/rethink.ts +6 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +5 -4
- package/src/resources/extensions/gsd/state.ts +84 -32
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/auto-mode-interactive-guard.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/captures.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/cli-provider-rate-limit.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/completion-hierarchy-guards.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +131 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +7 -12
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +20 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/dynamic-routing-default.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +74 -0
- package/src/resources/extensions/gsd/tests/event-replay-idempotency.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +129 -0
- package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +125 -12
- package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +164 -0
- package/src/resources/extensions/gsd/tests/guided-flow-dynamic-routing.test.ts +135 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +111 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-false-positives.test.ts +243 -0
- package/src/resources/extensions/gsd/tests/integration/gitignore-staging-2570.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +959 -0
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +85 -2
- package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/milestone-status-authoritative.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +68 -3
- package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/notifications.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/parallel-commit-scope.test.ts +159 -0
- package/src/resources/extensions/gsd/tests/parallel-eligibility-ghost.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/project-relocation-recovery.test.ts +297 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-replacement.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/prompt-tool-names.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +157 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/reconciliation-edge-cases.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +97 -0
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +233 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +305 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +405 -0
- package/src/resources/extensions/gsd/tests/state-derivation-parity.test.ts +257 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +1628 -0
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +289 -0
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +100 -17
- package/src/resources/extensions/gsd/tests/vacuum-recovery.test.ts +154 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +4 -1
- package/src/resources/extensions/gsd/tests/verdict-parser.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +82 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +29 -5
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +95 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +36 -74
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +13 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +36 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +20 -2
- package/src/resources/extensions/gsd/triage-resolution.ts +23 -6
- package/src/resources/extensions/gsd/types.ts +4 -2
- package/src/resources/extensions/gsd/undo.ts +2 -2
- package/src/resources/extensions/gsd/unit-ownership.ts +206 -35
- package/src/resources/extensions/gsd/verdict-parser.ts +21 -6
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- package/src/resources/extensions/gsd/workflow-manifest.ts +22 -5
- package/src/resources/extensions/gsd/workflow-projections.ts +97 -64
- package/src/resources/extensions/gsd/workflow-reconcile.ts +39 -10
- package/src/resources/extensions/gsd/workspace-index.ts +30 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +120 -1
- package/src/resources/extensions/gsd/worktree-resolver.ts +22 -3
- package/src/resources/extensions/mcp-client/index.ts +13 -7
- package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +55 -0
- package/src/resources/extensions/ollama/index.ts +130 -0
- package/src/resources/extensions/ollama/model-capabilities.ts +145 -0
- package/src/resources/extensions/ollama/ollama-client.ts +196 -0
- package/src/resources/extensions/ollama/ollama-commands.ts +248 -0
- package/src/resources/extensions/ollama/ollama-discovery.ts +106 -0
- package/src/resources/extensions/ollama/ollama-tool.ts +218 -0
- package/src/resources/extensions/ollama/tests/model-capabilities.test.ts +162 -0
- package/src/resources/extensions/ollama/tests/ollama-client.test.ts +38 -0
- package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +28 -0
- package/src/resources/extensions/ollama/types.ts +130 -0
- package/src/resources/extensions/search-the-web/extension-manifest.json +1 -1
- package/src/resources/extensions/shared/interview-ui.ts +12 -1
- package/src/resources/extensions/shared/tests/ask-user-freetext.test.ts +156 -0
- package/src/resources/skills/create-gsd-extension/SKILL.md +5 -3
- package/src/resources/skills/create-gsd-extension/references/key-rules-gotchas.md +5 -4
- package/src/resources/skills/create-gsd-extension/workflows/add-capability.md +2 -2
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +4 -4
- package/src/resources/skills/create-gsd-extension/workflows/debug-extension.md +5 -3
- package/dist/web/standalone/.next/static/chunks/6502.8b732f67a11b11b4.js +0 -9
- package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +0 -1
- package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +0 -79
- /package/dist/web/standalone/.next/static/{OS7_z6QaL6uqp8q5pjHSJ → R0D4xaIPl5kg93edN7Oo0}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{OS7_z6QaL6uqp8q5pjHSJ → R0D4xaIPl5kg93edN7Oo0}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* uat-stuck-loop-orphaned-worktree.test.ts — Regression tests for #2821.
|
|
3
|
+
*
|
|
4
|
+
* Reproduces two cascading bugs:
|
|
5
|
+
*
|
|
6
|
+
* Bug 1 — UAT stuck-loop: syncProjectRootToWorktree uses force:false for
|
|
7
|
+
* milestone files. When the project root has an ASSESSMENT with a verdict
|
|
8
|
+
* but the worktree has a stale/empty ASSESSMENT (or none at all after DB
|
|
9
|
+
* rebuild), the verdict is NOT synced into the worktree. checkNeedsRunUat
|
|
10
|
+
* finds no verdict → re-dispatches run-uat indefinitely.
|
|
11
|
+
*
|
|
12
|
+
* Bug 2 — Orphaned worktree: removeWorktree silently swallows failures when
|
|
13
|
+
* git worktree remove fails (untracked files, CWD inside worktree, etc.).
|
|
14
|
+
* The worktree directory and branch persist on disk after teardown.
|
|
15
|
+
* teardownAutoWorktree has a fallback rmSync but it also fails when the
|
|
16
|
+
* git internal .git/worktrees/<name> directory holds a lock.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
20
|
+
import assert from "node:assert/strict";
|
|
21
|
+
import {
|
|
22
|
+
mkdtempSync,
|
|
23
|
+
mkdirSync,
|
|
24
|
+
writeFileSync,
|
|
25
|
+
rmSync,
|
|
26
|
+
existsSync,
|
|
27
|
+
readFileSync,
|
|
28
|
+
} from "node:fs";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { tmpdir } from "node:os";
|
|
31
|
+
import { execFileSync } from "node:child_process";
|
|
32
|
+
|
|
33
|
+
import { syncProjectRootToWorktree } from "../auto-worktree.ts";
|
|
34
|
+
import {
|
|
35
|
+
createWorktree,
|
|
36
|
+
removeWorktree,
|
|
37
|
+
worktreePath,
|
|
38
|
+
} from "../worktree-manager.ts";
|
|
39
|
+
|
|
40
|
+
function git(args: string[], cwd: string): string {
|
|
41
|
+
return execFileSync("git", args, {
|
|
42
|
+
cwd,
|
|
43
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44
|
+
encoding: "utf-8",
|
|
45
|
+
}).trim();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function makeBaseRepo(): string {
|
|
49
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-2821-"));
|
|
50
|
+
git(["init", "-b", "main"], base);
|
|
51
|
+
git(["config", "user.name", "Test"], base);
|
|
52
|
+
git(["config", "user.email", "test@test.com"], base);
|
|
53
|
+
writeFileSync(join(base, "README.md"), "# test\n");
|
|
54
|
+
mkdirSync(join(base, ".gsd", "milestones", "M011"), { recursive: true });
|
|
55
|
+
git(["add", "."], base);
|
|
56
|
+
git(["commit", "-m", "init"], base);
|
|
57
|
+
return base;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── Bug 1: ASSESSMENT force-sync ─────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
describe("#2821 Bug 1 — ASSESSMENT file force-synced on resume", () => {
|
|
63
|
+
let mainBase: string;
|
|
64
|
+
let wtBase: string;
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
mainBase = mkdtempSync(join(tmpdir(), "gsd-2821-main-"));
|
|
68
|
+
wtBase = mkdtempSync(join(tmpdir(), "gsd-2821-wt-"));
|
|
69
|
+
mkdirSync(join(mainBase, ".gsd", "milestones", "M011", "slices", "S01"), {
|
|
70
|
+
recursive: true,
|
|
71
|
+
});
|
|
72
|
+
mkdirSync(join(wtBase, ".gsd", "milestones", "M011", "slices", "S01"), {
|
|
73
|
+
recursive: true,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
rmSync(mainBase, { recursive: true, force: true });
|
|
79
|
+
rmSync(wtBase, { recursive: true, force: true });
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("force-syncs ASSESSMENT with verdict from project root into worktree when worktree copy has no verdict", () => {
|
|
83
|
+
// Project root has ASSESSMENT with a PASS verdict (written by run-uat, synced by post-unit)
|
|
84
|
+
const prAssessment = join(
|
|
85
|
+
mainBase,
|
|
86
|
+
".gsd",
|
|
87
|
+
"milestones",
|
|
88
|
+
"M011",
|
|
89
|
+
"slices",
|
|
90
|
+
"S01",
|
|
91
|
+
"S01-ASSESSMENT.md",
|
|
92
|
+
);
|
|
93
|
+
writeFileSync(
|
|
94
|
+
prAssessment,
|
|
95
|
+
"---\nverdict: pass\n---\n# S01 Assessment\nAll tests pass.\n",
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Worktree has a stale ASSESSMENT with FAIL verdict (from the initial run-uat execution)
|
|
99
|
+
const wtAssessment = join(
|
|
100
|
+
wtBase,
|
|
101
|
+
".gsd",
|
|
102
|
+
"milestones",
|
|
103
|
+
"M011",
|
|
104
|
+
"slices",
|
|
105
|
+
"S01",
|
|
106
|
+
"S01-ASSESSMENT.md",
|
|
107
|
+
);
|
|
108
|
+
writeFileSync(
|
|
109
|
+
wtAssessment,
|
|
110
|
+
"---\nverdict: fail\n---\n# S01 Assessment\nSome tests fail.\n",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
syncProjectRootToWorktree(mainBase, wtBase, "M011");
|
|
114
|
+
|
|
115
|
+
// The worktree ASSESSMENT must now have the project root's PASS verdict
|
|
116
|
+
const content = readFileSync(wtAssessment, "utf-8");
|
|
117
|
+
assert.ok(
|
|
118
|
+
content.includes("verdict: pass"),
|
|
119
|
+
`Expected worktree ASSESSMENT to have verdict:pass after sync, got: ${content.slice(0, 100)}`,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("force-syncs ASSESSMENT from project root when worktree has no ASSESSMENT at all", () => {
|
|
124
|
+
// Project root has ASSESSMENT with verdict
|
|
125
|
+
const prAssessment = join(
|
|
126
|
+
mainBase,
|
|
127
|
+
".gsd",
|
|
128
|
+
"milestones",
|
|
129
|
+
"M011",
|
|
130
|
+
"slices",
|
|
131
|
+
"S01",
|
|
132
|
+
"S01-ASSESSMENT.md",
|
|
133
|
+
);
|
|
134
|
+
writeFileSync(
|
|
135
|
+
prAssessment,
|
|
136
|
+
"---\nverdict: pass\n---\n# S01 Assessment\n",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Worktree has NO ASSESSMENT (deleted during DB rebuild)
|
|
140
|
+
// — file simply doesn't exist
|
|
141
|
+
|
|
142
|
+
syncProjectRootToWorktree(mainBase, wtBase, "M011");
|
|
143
|
+
|
|
144
|
+
const wtAssessment = join(
|
|
145
|
+
wtBase,
|
|
146
|
+
".gsd",
|
|
147
|
+
"milestones",
|
|
148
|
+
"M011",
|
|
149
|
+
"slices",
|
|
150
|
+
"S01",
|
|
151
|
+
"S01-ASSESSMENT.md",
|
|
152
|
+
);
|
|
153
|
+
assert.ok(
|
|
154
|
+
existsSync(wtAssessment),
|
|
155
|
+
"ASSESSMENT should be copied to worktree when missing",
|
|
156
|
+
);
|
|
157
|
+
const content = readFileSync(wtAssessment, "utf-8");
|
|
158
|
+
assert.ok(
|
|
159
|
+
content.includes("verdict: pass"),
|
|
160
|
+
`Synced ASSESSMENT should contain verdict:pass, got: ${content.slice(0, 100)}`,
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("does NOT overwrite worktree ASSESSMENT when project root has no verdict", () => {
|
|
165
|
+
// Project root has ASSESSMENT without verdict (incomplete)
|
|
166
|
+
const prAssessment = join(
|
|
167
|
+
mainBase,
|
|
168
|
+
".gsd",
|
|
169
|
+
"milestones",
|
|
170
|
+
"M011",
|
|
171
|
+
"slices",
|
|
172
|
+
"S01",
|
|
173
|
+
"S01-ASSESSMENT.md",
|
|
174
|
+
);
|
|
175
|
+
writeFileSync(prAssessment, "# S01 Assessment\nIn progress...\n");
|
|
176
|
+
|
|
177
|
+
// Worktree has ASSESSMENT with verdict:fail
|
|
178
|
+
const wtAssessment = join(
|
|
179
|
+
wtBase,
|
|
180
|
+
".gsd",
|
|
181
|
+
"milestones",
|
|
182
|
+
"M011",
|
|
183
|
+
"slices",
|
|
184
|
+
"S01",
|
|
185
|
+
"S01-ASSESSMENT.md",
|
|
186
|
+
);
|
|
187
|
+
writeFileSync(
|
|
188
|
+
wtAssessment,
|
|
189
|
+
"---\nverdict: fail\n---\n# S01 Assessment\nSome tests fail.\n",
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
syncProjectRootToWorktree(mainBase, wtBase, "M011");
|
|
193
|
+
|
|
194
|
+
// Worktree copy should NOT be overwritten by the verdictless project root copy
|
|
195
|
+
const content = readFileSync(wtAssessment, "utf-8");
|
|
196
|
+
assert.ok(
|
|
197
|
+
content.includes("verdict: fail"),
|
|
198
|
+
`Worktree ASSESSMENT should keep verdict:fail when project root has no verdict, got: ${content.slice(0, 100)}`,
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// ─── Bug 2: Orphaned worktree cleanup ─────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
describe("#2821 Bug 2 — removeWorktree cleans up despite untracked files", () => {
|
|
206
|
+
let base: string;
|
|
207
|
+
|
|
208
|
+
beforeEach(() => {
|
|
209
|
+
base = makeBaseRepo();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
afterEach(() => {
|
|
213
|
+
rmSync(base, { recursive: true, force: true });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("removes worktree directory even when it contains untracked files", () => {
|
|
217
|
+
const info = createWorktree(base, "M011", {
|
|
218
|
+
branch: "milestone/M011",
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// Simulate run-uat writing untracked files (S01-UAT-RESULT.md, ASSESSMENT)
|
|
222
|
+
mkdirSync(
|
|
223
|
+
join(info.path, ".gsd", "milestones", "M011", "slices", "S01"),
|
|
224
|
+
{ recursive: true },
|
|
225
|
+
);
|
|
226
|
+
writeFileSync(
|
|
227
|
+
join(
|
|
228
|
+
info.path,
|
|
229
|
+
".gsd",
|
|
230
|
+
"milestones",
|
|
231
|
+
"M011",
|
|
232
|
+
"slices",
|
|
233
|
+
"S01",
|
|
234
|
+
"S01-UAT-RESULT.md",
|
|
235
|
+
),
|
|
236
|
+
"# UAT Result\nverdict: fail\n",
|
|
237
|
+
);
|
|
238
|
+
writeFileSync(
|
|
239
|
+
join(
|
|
240
|
+
info.path,
|
|
241
|
+
".gsd",
|
|
242
|
+
"milestones",
|
|
243
|
+
"M011",
|
|
244
|
+
"slices",
|
|
245
|
+
"S01",
|
|
246
|
+
"S01-ASSESSMENT.md",
|
|
247
|
+
),
|
|
248
|
+
"---\nverdict: fail\n---\n# Assessment\n",
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
removeWorktree(base, "M011", {
|
|
252
|
+
branch: "milestone/M011",
|
|
253
|
+
deleteBranch: true,
|
|
254
|
+
force: true,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const wtDir = worktreePath(base, "M011");
|
|
258
|
+
assert.ok(
|
|
259
|
+
!existsSync(wtDir),
|
|
260
|
+
`Worktree directory should be removed after teardown, but still exists at ${wtDir}`,
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("removes git internal worktree metadata after filesystem removal", () => {
|
|
265
|
+
createWorktree(base, "M011", {
|
|
266
|
+
branch: "milestone/M011",
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
removeWorktree(base, "M011", {
|
|
270
|
+
branch: "milestone/M011",
|
|
271
|
+
deleteBranch: true,
|
|
272
|
+
force: true,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// The git internal worktree directory should be cleaned up
|
|
276
|
+
const gitInternalWorktreeDir = join(base, ".git", "worktrees", "M011");
|
|
277
|
+
assert.ok(
|
|
278
|
+
!existsSync(gitInternalWorktreeDir),
|
|
279
|
+
`Git internal worktree dir should be removed: ${gitInternalWorktreeDir}`,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
// The branch should be deleted
|
|
283
|
+
const branches = git(["branch"], base);
|
|
284
|
+
assert.ok(
|
|
285
|
+
!branches.includes("milestone/M011"),
|
|
286
|
+
"milestone/M011 branch should be deleted after removeWorktree",
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import test from 'node:test';
|
|
5
5
|
import assert from 'node:assert/strict';
|
|
6
|
-
import { mkdtempSync, rmSync
|
|
6
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { tmpdir } from 'node:os';
|
|
9
9
|
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
checkOwnership,
|
|
15
15
|
taskUnitKey,
|
|
16
16
|
sliceUnitKey,
|
|
17
|
+
initOwnershipTable,
|
|
18
|
+
closeOwnershipDb,
|
|
17
19
|
} from '../unit-ownership.ts';
|
|
18
20
|
|
|
19
21
|
function makeTmpBase(): string {
|
|
@@ -34,28 +36,51 @@ test('sliceUnitKey: builds correct key', () => {
|
|
|
34
36
|
assert.equal(sliceUnitKey('M001', 'S01'), 'M001/S01');
|
|
35
37
|
});
|
|
36
38
|
|
|
37
|
-
// ─── Claim / get / release
|
|
39
|
+
// ─── Claim / get / release (SQLite-backed) ──────────────────────────────
|
|
38
40
|
|
|
39
|
-
test('claimUnit: creates
|
|
41
|
+
test('claimUnit: creates DB and records agent', () => {
|
|
40
42
|
const base = makeTmpBase();
|
|
41
43
|
try {
|
|
42
|
-
|
|
44
|
+
initOwnershipTable(base);
|
|
45
|
+
const claimed = claimUnit(base, 'M001/S01/T01', 'executor-01');
|
|
43
46
|
|
|
44
|
-
assert.
|
|
47
|
+
assert.equal(claimed, true, 'first claim should succeed');
|
|
45
48
|
assert.equal(getOwner(base, 'M001/S01/T01'), 'executor-01');
|
|
46
49
|
} finally {
|
|
50
|
+
closeOwnershipDb(base);
|
|
47
51
|
cleanup(base);
|
|
48
52
|
}
|
|
49
53
|
});
|
|
50
54
|
|
|
51
|
-
test('claimUnit:
|
|
55
|
+
test('claimUnit: rejects second claim on same unit (first-writer-wins)', () => {
|
|
52
56
|
const base = makeTmpBase();
|
|
53
57
|
try {
|
|
54
|
-
|
|
55
|
-
claimUnit(base, 'M001/S01/T01', 'executor-
|
|
58
|
+
initOwnershipTable(base);
|
|
59
|
+
const first = claimUnit(base, 'M001/S01/T01', 'executor-01');
|
|
60
|
+
const second = claimUnit(base, 'M001/S01/T01', 'executor-02');
|
|
56
61
|
|
|
57
|
-
assert.equal(
|
|
62
|
+
assert.equal(first, true, 'first claim should succeed');
|
|
63
|
+
assert.equal(second, false, 'second claim should fail (first-writer-wins)');
|
|
64
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'executor-01',
|
|
65
|
+
'original owner must be preserved');
|
|
58
66
|
} finally {
|
|
67
|
+
closeOwnershipDb(base);
|
|
68
|
+
cleanup(base);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('claimUnit: same agent re-claiming same unit succeeds', () => {
|
|
73
|
+
const base = makeTmpBase();
|
|
74
|
+
try {
|
|
75
|
+
initOwnershipTable(base);
|
|
76
|
+
const first = claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
77
|
+
const second = claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
78
|
+
|
|
79
|
+
assert.equal(first, true);
|
|
80
|
+
assert.equal(second, true, 're-claim by same agent should succeed');
|
|
81
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'agent-a');
|
|
82
|
+
} finally {
|
|
83
|
+
closeOwnershipDb(base);
|
|
59
84
|
cleanup(base);
|
|
60
85
|
}
|
|
61
86
|
});
|
|
@@ -63,21 +88,25 @@ test('claimUnit: overwrites existing claim (last writer wins)', () => {
|
|
|
63
88
|
test('claimUnit: multiple units can be claimed independently', () => {
|
|
64
89
|
const base = makeTmpBase();
|
|
65
90
|
try {
|
|
91
|
+
initOwnershipTable(base);
|
|
66
92
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
67
93
|
claimUnit(base, 'M001/S01/T02', 'agent-b');
|
|
68
94
|
|
|
69
95
|
assert.equal(getOwner(base, 'M001/S01/T01'), 'agent-a');
|
|
70
96
|
assert.equal(getOwner(base, 'M001/S01/T02'), 'agent-b');
|
|
71
97
|
} finally {
|
|
98
|
+
closeOwnershipDb(base);
|
|
72
99
|
cleanup(base);
|
|
73
100
|
}
|
|
74
101
|
});
|
|
75
102
|
|
|
76
|
-
test('getOwner: returns null when no
|
|
103
|
+
test('getOwner: returns null when no DB initialized', () => {
|
|
77
104
|
const base = makeTmpBase();
|
|
78
105
|
try {
|
|
106
|
+
initOwnershipTable(base);
|
|
79
107
|
assert.equal(getOwner(base, 'M001/S01/T01'), null);
|
|
80
108
|
} finally {
|
|
109
|
+
closeOwnershipDb(base);
|
|
81
110
|
cleanup(base);
|
|
82
111
|
}
|
|
83
112
|
});
|
|
@@ -85,9 +114,11 @@ test('getOwner: returns null when no claim file exists', () => {
|
|
|
85
114
|
test('getOwner: returns null for unclaimed unit', () => {
|
|
86
115
|
const base = makeTmpBase();
|
|
87
116
|
try {
|
|
117
|
+
initOwnershipTable(base);
|
|
88
118
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
89
119
|
assert.equal(getOwner(base, 'M001/S01/T99'), null);
|
|
90
120
|
} finally {
|
|
121
|
+
closeOwnershipDb(base);
|
|
91
122
|
cleanup(base);
|
|
92
123
|
}
|
|
93
124
|
});
|
|
@@ -95,11 +126,13 @@ test('getOwner: returns null for unclaimed unit', () => {
|
|
|
95
126
|
test('releaseUnit: removes claim', () => {
|
|
96
127
|
const base = makeTmpBase();
|
|
97
128
|
try {
|
|
129
|
+
initOwnershipTable(base);
|
|
98
130
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
99
131
|
releaseUnit(base, 'M001/S01/T01');
|
|
100
132
|
|
|
101
133
|
assert.equal(getOwner(base, 'M001/S01/T01'), null);
|
|
102
134
|
} finally {
|
|
135
|
+
closeOwnershipDb(base);
|
|
103
136
|
cleanup(base);
|
|
104
137
|
}
|
|
105
138
|
});
|
|
@@ -107,32 +140,43 @@ test('releaseUnit: removes claim', () => {
|
|
|
107
140
|
test('releaseUnit: no-op for non-existent claim', () => {
|
|
108
141
|
const base = makeTmpBase();
|
|
109
142
|
try {
|
|
143
|
+
initOwnershipTable(base);
|
|
110
144
|
// Should not throw
|
|
111
145
|
releaseUnit(base, 'M001/S01/T01');
|
|
112
146
|
} finally {
|
|
147
|
+
closeOwnershipDb(base);
|
|
113
148
|
cleanup(base);
|
|
114
149
|
}
|
|
115
150
|
});
|
|
116
151
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
test('checkOwnership: returns null when no actorName provided (opt-in)', () => {
|
|
152
|
+
test('releaseUnit: allows reclaim after release', () => {
|
|
120
153
|
const base = makeTmpBase();
|
|
121
154
|
try {
|
|
155
|
+
initOwnershipTable(base);
|
|
122
156
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
157
|
+
releaseUnit(base, 'M001/S01/T01');
|
|
123
158
|
|
|
124
|
-
|
|
125
|
-
assert.equal(
|
|
159
|
+
const reclaimed = claimUnit(base, 'M001/S01/T01', 'agent-b');
|
|
160
|
+
assert.equal(reclaimed, true, 'reclaim after release should succeed');
|
|
161
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'agent-b');
|
|
126
162
|
} finally {
|
|
163
|
+
closeOwnershipDb(base);
|
|
127
164
|
cleanup(base);
|
|
128
165
|
}
|
|
129
166
|
});
|
|
130
167
|
|
|
131
|
-
|
|
168
|
+
// ─── checkOwnership ──────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
test('checkOwnership: returns null when no actorName provided (opt-in)', () => {
|
|
132
171
|
const base = makeTmpBase();
|
|
133
172
|
try {
|
|
134
|
-
|
|
173
|
+
initOwnershipTable(base);
|
|
174
|
+
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
175
|
+
|
|
176
|
+
// No actorName → ownership not enforced
|
|
177
|
+
assert.equal(checkOwnership(base, 'M001/S01/T01', undefined), null);
|
|
135
178
|
} finally {
|
|
179
|
+
closeOwnershipDb(base);
|
|
136
180
|
cleanup(base);
|
|
137
181
|
}
|
|
138
182
|
});
|
|
@@ -140,11 +184,13 @@ test('checkOwnership: returns null when no claim file exists', () => {
|
|
|
140
184
|
test('checkOwnership: returns null when unit is unclaimed', () => {
|
|
141
185
|
const base = makeTmpBase();
|
|
142
186
|
try {
|
|
187
|
+
initOwnershipTable(base);
|
|
143
188
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
144
189
|
|
|
145
190
|
// Different unit, unclaimed
|
|
146
191
|
assert.equal(checkOwnership(base, 'M001/S01/T99', 'agent-b'), null);
|
|
147
192
|
} finally {
|
|
193
|
+
closeOwnershipDb(base);
|
|
148
194
|
cleanup(base);
|
|
149
195
|
}
|
|
150
196
|
});
|
|
@@ -152,10 +198,12 @@ test('checkOwnership: returns null when unit is unclaimed', () => {
|
|
|
152
198
|
test('checkOwnership: returns null when actor matches owner', () => {
|
|
153
199
|
const base = makeTmpBase();
|
|
154
200
|
try {
|
|
201
|
+
initOwnershipTable(base);
|
|
155
202
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
156
203
|
|
|
157
204
|
assert.equal(checkOwnership(base, 'M001/S01/T01', 'agent-a'), null);
|
|
158
205
|
} finally {
|
|
206
|
+
closeOwnershipDb(base);
|
|
159
207
|
cleanup(base);
|
|
160
208
|
}
|
|
161
209
|
});
|
|
@@ -163,6 +211,7 @@ test('checkOwnership: returns null when actor matches owner', () => {
|
|
|
163
211
|
test('checkOwnership: returns error string when actor does not match owner', () => {
|
|
164
212
|
const base = makeTmpBase();
|
|
165
213
|
try {
|
|
214
|
+
initOwnershipTable(base);
|
|
166
215
|
claimUnit(base, 'M001/S01/T01', 'agent-a');
|
|
167
216
|
|
|
168
217
|
const err = checkOwnership(base, 'M001/S01/T01', 'agent-b');
|
|
@@ -170,6 +219,40 @@ test('checkOwnership: returns error string when actor does not match owner', ()
|
|
|
170
219
|
assert.match(err!, /owned by agent-a/);
|
|
171
220
|
assert.match(err!, /not agent-b/);
|
|
172
221
|
} finally {
|
|
222
|
+
closeOwnershipDb(base);
|
|
223
|
+
cleanup(base);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// ─── Race condition: first-writer-wins atomicity ─────────────────────────
|
|
228
|
+
|
|
229
|
+
test('claimUnit: concurrent claims — only first writer wins (no lost update)', () => {
|
|
230
|
+
const base = makeTmpBase();
|
|
231
|
+
try {
|
|
232
|
+
initOwnershipTable(base);
|
|
233
|
+
|
|
234
|
+
// Simulate the race described in #2728:
|
|
235
|
+
// Two agents both try to claim the same unit.
|
|
236
|
+
// With SQLite INSERT OR IGNORE, only the first succeeds.
|
|
237
|
+
const results: boolean[] = [];
|
|
238
|
+
const agents = ['agent-alpha', 'agent-beta', 'agent-gamma'];
|
|
239
|
+
for (const agent of agents) {
|
|
240
|
+
results.push(claimUnit(base, 'M001/S01/T01', agent));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Exactly one agent should have won
|
|
244
|
+
const wins = results.filter(r => r === true);
|
|
245
|
+
assert.equal(wins.length, 1, 'exactly one agent should win the claim');
|
|
246
|
+
|
|
247
|
+
// The winner is the first agent (deterministic in single-threaded)
|
|
248
|
+
assert.equal(results[0], true);
|
|
249
|
+
assert.equal(results[1], false);
|
|
250
|
+
assert.equal(results[2], false);
|
|
251
|
+
|
|
252
|
+
// The owner must be the first agent
|
|
253
|
+
assert.equal(getOwner(base, 'M001/S01/T01'), 'agent-alpha');
|
|
254
|
+
} finally {
|
|
255
|
+
closeOwnershipDb(base);
|
|
173
256
|
cleanup(base);
|
|
174
257
|
}
|
|
175
258
|
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import {
|
|
8
|
+
openDatabase,
|
|
9
|
+
closeDatabase,
|
|
10
|
+
isDbAvailable,
|
|
11
|
+
_getAdapter,
|
|
12
|
+
} from '../gsd-db.ts';
|
|
13
|
+
|
|
14
|
+
const _require = createRequire(import.meta.url);
|
|
15
|
+
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
// Helpers
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
|
|
20
|
+
function tempDbPath(): string {
|
|
21
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-vacuum-test-'));
|
|
22
|
+
return path.join(dir, 'test.db');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cleanup(dbPath: string): void {
|
|
26
|
+
closeDatabase();
|
|
27
|
+
try {
|
|
28
|
+
const dir = path.dirname(dbPath);
|
|
29
|
+
for (const f of fs.readdirSync(dir)) {
|
|
30
|
+
fs.unlinkSync(path.join(dir, f));
|
|
31
|
+
}
|
|
32
|
+
fs.rmdirSync(dir);
|
|
33
|
+
} catch { /* best effort */ }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Create a SQLite DB with a corrupt freelist that causes DDL to fail
|
|
38
|
+
* with "database disk image is malformed" but is recoverable via VACUUM.
|
|
39
|
+
*
|
|
40
|
+
* Strategy:
|
|
41
|
+
* 1. Create a DB with schema_version at v0 (so initSchema needs to run DDL)
|
|
42
|
+
* 2. Add padding rows to create many pages, then delete + drop to free them
|
|
43
|
+
* 3. Corrupt the freelist trunk pointer to point at a B-tree page
|
|
44
|
+
*
|
|
45
|
+
* This simulates the real-world scenario described in #2519: an interrupted
|
|
46
|
+
* WAL checkpoint leaves the freelist in an inconsistent state.
|
|
47
|
+
*/
|
|
48
|
+
function createCorruptFreelistDb(dbPath: string): void {
|
|
49
|
+
// Use node:sqlite directly to build the minimal corrupt DB
|
|
50
|
+
const sqlite = _require('node:sqlite');
|
|
51
|
+
const db = new sqlite.DatabaseSync(dbPath);
|
|
52
|
+
db.exec('PRAGMA journal_mode=WAL');
|
|
53
|
+
db.exec('CREATE TABLE schema_version (version INTEGER NOT NULL, applied_at TEXT NOT NULL)');
|
|
54
|
+
db.exec("INSERT INTO schema_version VALUES (0, '2024-01-01')");
|
|
55
|
+
// Pad with data to create many pages, then free them
|
|
56
|
+
db.exec('CREATE TABLE _padding (id INTEGER PRIMARY KEY, data TEXT)');
|
|
57
|
+
for (let i = 0; i < 30; i++) {
|
|
58
|
+
db.exec(`INSERT INTO _padding (data) VALUES ('${'x'.repeat(4000)}')`);
|
|
59
|
+
}
|
|
60
|
+
db.exec('DELETE FROM _padding');
|
|
61
|
+
db.exec('DROP TABLE _padding');
|
|
62
|
+
db.exec('PRAGMA wal_checkpoint(TRUNCATE)');
|
|
63
|
+
db.close();
|
|
64
|
+
|
|
65
|
+
// Remove WAL/SHM files to ensure clean file-only state
|
|
66
|
+
try { fs.unlinkSync(dbPath + '-wal'); } catch { /* may not exist */ }
|
|
67
|
+
try { fs.unlinkSync(dbPath + '-shm'); } catch { /* may not exist */ }
|
|
68
|
+
|
|
69
|
+
// Corrupt: point freelist trunk (offset 32-35) to page 2 (a B-tree page),
|
|
70
|
+
// and claim 10 free pages (offset 36-39)
|
|
71
|
+
const fd = fs.openSync(dbPath, 'r+');
|
|
72
|
+
try {
|
|
73
|
+
const buf = Buffer.alloc(8);
|
|
74
|
+
buf.writeUInt32BE(2, 0); // trunk page = page 2 (actually a B-tree page)
|
|
75
|
+
buf.writeUInt32BE(10, 4); // freelist count = 10
|
|
76
|
+
fs.writeSync(fd, buf, 0, 8, 32);
|
|
77
|
+
} finally {
|
|
78
|
+
fs.closeSync(fd);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
+
// Tests
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
86
|
+
describe('openDatabase VACUUM recovery on corrupt freelist', () => {
|
|
87
|
+
|
|
88
|
+
test('recovers a file-backed DB with corrupt freelist via VACUUM', () => {
|
|
89
|
+
const dbPath = tempDbPath();
|
|
90
|
+
|
|
91
|
+
// Create a DB with corrupt freelist (schema at v0 so initSchema runs DDL)
|
|
92
|
+
createCorruptFreelistDb(dbPath);
|
|
93
|
+
|
|
94
|
+
// Without the fix, this throws "database disk image is malformed".
|
|
95
|
+
// With the fix, openDatabase detects "malformed", runs VACUUM, retries.
|
|
96
|
+
const ok = openDatabase(dbPath);
|
|
97
|
+
assert.ok(ok, 'openDatabase should succeed after VACUUM recovery');
|
|
98
|
+
assert.ok(isDbAvailable(), 'DB should be available after recovery');
|
|
99
|
+
|
|
100
|
+
// Verify full schema was applied
|
|
101
|
+
const adapter = _getAdapter()!;
|
|
102
|
+
const row = adapter.prepare(
|
|
103
|
+
'SELECT MAX(version) as version FROM schema_version',
|
|
104
|
+
).get();
|
|
105
|
+
assert.ok(
|
|
106
|
+
typeof row?.['version'] === 'number' && (row['version'] as number) > 0,
|
|
107
|
+
'schema_version should have a positive version after recovery',
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
cleanup(dbPath);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('does not attempt VACUUM for non-malformed errors', () => {
|
|
114
|
+
// openDatabase with :memory: never hits the fileBacked VACUUM path,
|
|
115
|
+
// so non-malformed errors propagate directly. We verify by checking
|
|
116
|
+
// that a non-file error from an in-memory DB propagates unchanged.
|
|
117
|
+
// (In-memory DBs always succeed for initSchema, so this is a design
|
|
118
|
+
// check — the VACUUM path is only for fileBacked = true.)
|
|
119
|
+
const ok = openDatabase(':memory:');
|
|
120
|
+
assert.ok(ok, 'in-memory DB should open fine');
|
|
121
|
+
closeDatabase();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('throws if VACUUM itself fails on unrecoverable corruption', () => {
|
|
125
|
+
const dbPath = tempDbPath();
|
|
126
|
+
|
|
127
|
+
// Create a file with valid SQLite header but thoroughly corrupt content
|
|
128
|
+
const page = Buffer.alloc(4096);
|
|
129
|
+
// SQLite magic: "SQLite format 3\0"
|
|
130
|
+
page.write('SQLite format 3\0', 0, 'utf8');
|
|
131
|
+
// Page size: 4096 (big-endian at offset 16)
|
|
132
|
+
page.writeUInt16BE(4096, 16);
|
|
133
|
+
page[18] = 1; // write version
|
|
134
|
+
page[19] = 1; // read version
|
|
135
|
+
page[20] = 0; // reserved space
|
|
136
|
+
page[21] = 64; // max embedded payload fraction
|
|
137
|
+
page[22] = 32; // min embedded payload fraction
|
|
138
|
+
page[23] = 32; // leaf payload fraction
|
|
139
|
+
page.writeUInt32BE(1, 28); // page_count = 1
|
|
140
|
+
page.writeUInt32BE(999, 32); // corrupt freelist trunk
|
|
141
|
+
page.writeUInt32BE(5, 36); // freelist count = 5
|
|
142
|
+
|
|
143
|
+
fs.writeFileSync(dbPath, page);
|
|
144
|
+
|
|
145
|
+
// Should throw — VACUUM cannot save a thoroughly corrupt file
|
|
146
|
+
assert.throws(
|
|
147
|
+
() => openDatabase(dbPath),
|
|
148
|
+
/./,
|
|
149
|
+
'should throw for unrecoverable corruption',
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
cleanup(dbPath);
|
|
153
|
+
});
|
|
154
|
+
});
|