@opengsd/gsd-pi 1.2.0-dev.e8563f58 → 1.2.0-dev.fbdca60b
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-model-override.d.ts +15 -0
- package/dist/cli-model-override.js +21 -0
- package/dist/cli.js +1 -18
- package/dist/loader.js +6 -4
- package/dist/register-agent-bundles.d.ts +11 -2
- package/dist/register-agent-bundles.js +18 -4
- package/dist/resource-loader.d.ts +10 -5
- package/dist/resource-loader.js +121 -6
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +3 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
- package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
- package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
- package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
- package/dist/resources/extensions/gsd/auto/loop.js +4 -1
- package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
- package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
- package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
- package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
- package/dist/resources/extensions/gsd/auto-start.js +23 -3
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
- package/dist/resources/extensions/gsd/auto-verification.js +14 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
- package/dist/resources/extensions/gsd/auto.js +45 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
- package/dist/resources/extensions/gsd/commands/context.js +16 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
- package/dist/resources/extensions/gsd/db/engine.js +24 -6
- package/dist/resources/extensions/gsd/db/queries.js +30 -0
- package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
- package/dist/resources/extensions/gsd/db-transaction.js +27 -23
- package/dist/resources/extensions/gsd/db-writer.js +8 -17
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
- package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
- package/dist/resources/extensions/gsd/gsd-db.js +15 -20
- package/dist/resources/extensions/gsd/guided-flow.js +93 -4
- package/dist/resources/extensions/gsd/health-widget.js +87 -28
- package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
- package/dist/resources/extensions/gsd/memory-relations.js +1 -1
- package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
- package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
- package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
- package/dist/resources/extensions/gsd/notifications.js +12 -7
- package/dist/resources/extensions/gsd/projection-flush.js +7 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
- package/dist/resources/extensions/gsd/session-lock.js +1 -1
- package/dist/resources/extensions/gsd/skill-activation.js +3 -6
- package/dist/resources/extensions/gsd/state.js +6 -2
- package/dist/resources/extensions/gsd/tool-contract.js +14 -3
- package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
- package/dist/resources/extensions/gsd/unit-registry.js +34 -4
- package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
- package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
- package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
- package/dist/resources/extensions/gsd/workflow-events.js +6 -18
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
- package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
- package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
- package/dist/resources/extensions/gsd/worktree.js +8 -1
- package/dist/resources/extensions/mcp-client/manager.js +6 -1
- package/dist/resources/skills/create-skill/SKILL.md +3 -0
- package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
- package/dist/runtime-checks.d.ts +10 -0
- package/dist/runtime-checks.js +27 -0
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +3 -3
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +6 -4
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +12 -3
- package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
- package/packages/mcp-server/dist/cli-runner.js +137 -0
- package/packages/mcp-server/dist/cli-runner.js.map +1 -0
- package/packages/mcp-server/dist/cli.js +2 -53
- package/packages/mcp-server/dist/cli.js.map +1 -1
- package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
- package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
- package/packages/mcp-server/dist/pid-registry.js +459 -0
- package/packages/mcp-server/dist/pid-registry.js.map +1 -0
- package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
- package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
- package/packages/mcp-server/dist/probe-mode.js +10 -0
- package/packages/mcp-server/dist/probe-mode.js.map +1 -0
- package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
- package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
- package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
- package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +62 -43
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -5
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +43 -2
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/README.md +15 -0
- package/packages/pi-tui/dist/index.d.ts +2 -2
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +2 -2
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
- package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal-image.js +54 -2
- package/packages/pi-tui/dist/terminal-image.js.map +1 -1
- package/packages/pi-tui/dist/terminal.d.ts +12 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +70 -25
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +15 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +106 -21
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/dist/utils.d.ts.map +1 -1
- package/packages/pi-tui/dist/utils.js +110 -36
- package/packages/pi-tui/dist/utils.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +45 -17
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -2
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
- package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
- package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
- package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
- package/src/resources/extensions/gsd/auto/loop.ts +4 -1
- package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
- package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
- package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
- package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
- package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
- package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
- package/src/resources/extensions/gsd/auto-start.ts +24 -4
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
- package/src/resources/extensions/gsd/auto-verification.ts +18 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
- package/src/resources/extensions/gsd/auto.ts +56 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
- package/src/resources/extensions/gsd/commands/context.ts +16 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
- package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
- package/src/resources/extensions/gsd/db/engine.ts +26 -6
- package/src/resources/extensions/gsd/db/queries.ts +29 -0
- package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
- package/src/resources/extensions/gsd/db-transaction.ts +37 -20
- package/src/resources/extensions/gsd/db-writer.ts +11 -19
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
- package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
- package/src/resources/extensions/gsd/gsd-db.ts +15 -19
- package/src/resources/extensions/gsd/guided-flow.ts +145 -24
- package/src/resources/extensions/gsd/health-widget.ts +91 -27
- package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
- package/src/resources/extensions/gsd/memory-relations.ts +1 -1
- package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
- package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
- package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
- package/src/resources/extensions/gsd/notifications.ts +13 -6
- package/src/resources/extensions/gsd/projection-flush.ts +20 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
- package/src/resources/extensions/gsd/session-lock.ts +1 -1
- package/src/resources/extensions/gsd/skill-activation.ts +3 -6
- package/src/resources/extensions/gsd/state.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
- package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
- package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
- package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
- package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
- package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
- package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
- package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
- package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
- package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
- package/src/resources/extensions/gsd/tool-contract.ts +38 -3
- package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
- package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
- package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
- package/src/resources/extensions/gsd/unit-registry.ts +34 -4
- package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
- package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
- package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
- package/src/resources/extensions/gsd/workflow-events.ts +12 -20
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
- package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
- package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
- package/src/resources/extensions/gsd/worktree.ts +7 -1
- package/src/resources/extensions/mcp-client/manager.ts +7 -1
- package/src/resources/skills/create-skill/SKILL.md +3 -0
- package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
- package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
- package/src/resources/skills/gsd-browser/SKILL.md +0 -41
- /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { atomicWriteSync } from "./atomic-write.js";
|
|
5
|
+
import { resolveGsdPathContract } from "./paths.js";
|
|
6
|
+
import { buildAuditEnvelope, emitUokAuditEvent } from "./uok/audit.js";
|
|
7
|
+
import { isUnifiedAuditEnabled } from "./uok/audit-toggle.js";
|
|
8
|
+
import { normalizeWorkflowEventCommand } from "./workflow-event-vocabulary.js";
|
|
9
|
+
export const WORKFLOW_EVENT_LOG_FILENAME = "event-log.jsonl";
|
|
10
|
+
export function resolveWorkflowEventLedgerLocation(basePath, originalProjectRoot) {
|
|
11
|
+
const contract = resolveGsdPathContract(basePath, originalProjectRoot);
|
|
12
|
+
return {
|
|
13
|
+
projectRoot: contract.projectRoot,
|
|
14
|
+
workRoot: contract.workRoot,
|
|
15
|
+
projectGsd: contract.projectGsd,
|
|
16
|
+
worktreeGsd: contract.worktreeGsd,
|
|
17
|
+
projectLogPath: join(contract.projectGsd, WORKFLOW_EVENT_LOG_FILENAME),
|
|
18
|
+
worktreeLogPath: contract.worktreeGsd
|
|
19
|
+
? join(contract.worktreeGsd, WORKFLOW_EVENT_LOG_FILENAME)
|
|
20
|
+
: null,
|
|
21
|
+
isWorktree: contract.isWorktree,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function workflowEventLogPath(basePath) {
|
|
25
|
+
return resolveWorkflowEventLedgerLocation(basePath).projectLogPath;
|
|
26
|
+
}
|
|
27
|
+
export function workflowEventArchivePath(basePath, milestoneId) {
|
|
28
|
+
const location = resolveWorkflowEventLedgerLocation(basePath);
|
|
29
|
+
return join(location.projectGsd, `event-log-${milestoneId}.jsonl.archived`);
|
|
30
|
+
}
|
|
31
|
+
export function readWorktreeEventLogPath(worktreeBasePath) {
|
|
32
|
+
const location = resolveWorkflowEventLedgerLocation(worktreeBasePath);
|
|
33
|
+
return location.worktreeLogPath ?? location.projectLogPath;
|
|
34
|
+
}
|
|
35
|
+
export function buildWorkflowEvent(event, sessionId) {
|
|
36
|
+
const hash = createHash("sha256")
|
|
37
|
+
.update(JSON.stringify({ cmd: event.cmd, params: event.params }))
|
|
38
|
+
.digest("hex")
|
|
39
|
+
.slice(0, 16);
|
|
40
|
+
return {
|
|
41
|
+
v: 2,
|
|
42
|
+
...event,
|
|
43
|
+
hash,
|
|
44
|
+
session_id: sessionId,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function appendWorkflowEvent(basePath, event, sessionId) {
|
|
48
|
+
const fullEvent = buildWorkflowEvent(event, sessionId);
|
|
49
|
+
const location = resolveWorkflowEventLedgerLocation(basePath);
|
|
50
|
+
mkdirSync(location.projectGsd, { recursive: true });
|
|
51
|
+
appendFileSync(location.projectLogPath, `${JSON.stringify(fullEvent)}\n`, "utf-8");
|
|
52
|
+
emitWorkflowEventAudit(location.projectRoot, fullEvent);
|
|
53
|
+
return fullEvent;
|
|
54
|
+
}
|
|
55
|
+
export function writeWorkflowEventLog(basePath, events) {
|
|
56
|
+
const location = resolveWorkflowEventLedgerLocation(basePath);
|
|
57
|
+
mkdirSync(location.projectGsd, { recursive: true });
|
|
58
|
+
const content = events.map((event) => JSON.stringify(event)).join("\n") + (events.length > 0 ? "\n" : "");
|
|
59
|
+
atomicWriteSync(location.projectLogPath, content);
|
|
60
|
+
}
|
|
61
|
+
export function writeWorktreeEventLog(worktreeBasePath, events) {
|
|
62
|
+
const location = resolveWorkflowEventLedgerLocation(worktreeBasePath);
|
|
63
|
+
const logPath = location.worktreeLogPath ?? location.projectLogPath;
|
|
64
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
65
|
+
const content = events.map((event) => JSON.stringify(event)).join("\n") + (events.length > 0 ? "\n" : "");
|
|
66
|
+
atomicWriteSync(logPath, content);
|
|
67
|
+
}
|
|
68
|
+
function emitWorkflowEventAudit(basePath, event) {
|
|
69
|
+
if (!isUnifiedAuditEnabled())
|
|
70
|
+
return;
|
|
71
|
+
try {
|
|
72
|
+
const normalized = normalizeWorkflowEventCommand(event.cmd) ?? "unknown";
|
|
73
|
+
emitUokAuditEvent(basePath, buildAuditEnvelope({
|
|
74
|
+
traceId: event.session_id,
|
|
75
|
+
category: "orchestration",
|
|
76
|
+
type: `workflow-event-${normalized}`,
|
|
77
|
+
payload: {
|
|
78
|
+
cmd: event.cmd,
|
|
79
|
+
params: event.params,
|
|
80
|
+
actor: event.actor,
|
|
81
|
+
actorName: event.actor_name,
|
|
82
|
+
triggerReason: event.trigger_reason,
|
|
83
|
+
eventTs: event.ts,
|
|
84
|
+
hash: event.hash,
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Best-effort: audit projection must never block the workflow event ledger.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export function normalizeWorkflowEventCommand(cmd) {
|
|
2
|
+
return typeof cmd === "string" ? cmd.replace(/-/g, "_") : null;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Workflow progress events are keyed by the domain entity they mutate.
|
|
6
|
+
* Keep command aliases and conflict identity in this module so replay,
|
|
7
|
+
* conflict detection, and tests do not each grow their own vocabulary.
|
|
8
|
+
*/
|
|
9
|
+
export function workflowEventEntityKey(event) {
|
|
10
|
+
const p = event.params;
|
|
11
|
+
const cmd = normalizeWorkflowEventCommand(event.cmd);
|
|
12
|
+
if (!cmd)
|
|
13
|
+
return null;
|
|
14
|
+
switch (cmd) {
|
|
15
|
+
case "complete_task":
|
|
16
|
+
case "start_task":
|
|
17
|
+
case "skip_task":
|
|
18
|
+
case "report_blocker":
|
|
19
|
+
case "record_verification":
|
|
20
|
+
case "plan_task":
|
|
21
|
+
return typeof p["taskId"] === "string"
|
|
22
|
+
? { type: "task", id: p["taskId"] }
|
|
23
|
+
: null;
|
|
24
|
+
case "complete_slice":
|
|
25
|
+
case "replan_slice":
|
|
26
|
+
return typeof p["sliceId"] === "string"
|
|
27
|
+
? { type: "slice", id: p["sliceId"] }
|
|
28
|
+
: null;
|
|
29
|
+
case "plan_slice":
|
|
30
|
+
return typeof p["sliceId"] === "string"
|
|
31
|
+
? { type: "slice_plan", id: p["sliceId"] }
|
|
32
|
+
: null;
|
|
33
|
+
case "complete_milestone":
|
|
34
|
+
case "plan_milestone":
|
|
35
|
+
return typeof p["milestoneId"] === "string"
|
|
36
|
+
? { type: "milestone", id: p["milestoneId"] }
|
|
37
|
+
: null;
|
|
38
|
+
case "save_decision":
|
|
39
|
+
if (typeof p["scope"] === "string" && typeof p["decision"] === "string") {
|
|
40
|
+
return { type: "decision", id: `${p["scope"]}:${p["decision"]}` };
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
default:
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { join } from "node:path";
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
4
3
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
5
4
|
import { withFileLockSync } from "./file-lock.js";
|
|
5
|
+
import { appendWorkflowEvent, workflowEventArchivePath, workflowEventLogPath, } from "./workflow-event-ledger.js";
|
|
6
6
|
import { logWarning } from "./workflow-logger.js";
|
|
7
7
|
// ─── Session ID ───────────────────────────────────────────────────────────
|
|
8
8
|
/**
|
|
@@ -20,19 +20,7 @@ export function getSessionId() {
|
|
|
20
20
|
* Creates .gsd directory if needed.
|
|
21
21
|
*/
|
|
22
22
|
export function appendEvent(basePath, event) {
|
|
23
|
-
|
|
24
|
-
.update(JSON.stringify({ cmd: event.cmd, params: event.params }))
|
|
25
|
-
.digest("hex")
|
|
26
|
-
.slice(0, 16);
|
|
27
|
-
const fullEvent = {
|
|
28
|
-
v: 2,
|
|
29
|
-
...event,
|
|
30
|
-
hash,
|
|
31
|
-
session_id: ENGINE_SESSION_ID,
|
|
32
|
-
};
|
|
33
|
-
const dir = join(basePath, ".gsd");
|
|
34
|
-
mkdirSync(dir, { recursive: true });
|
|
35
|
-
appendFileSync(join(dir, "event-log.jsonl"), JSON.stringify(fullEvent) + "\n", "utf-8");
|
|
23
|
+
appendWorkflowEvent(basePath, event, ENGINE_SESSION_ID);
|
|
36
24
|
}
|
|
37
25
|
// ─── readEvents ──────────────────────────────────────────────────────────
|
|
38
26
|
/**
|
|
@@ -87,8 +75,8 @@ export function findForkPoint(logA, logB) {
|
|
|
87
75
|
* @returns { archived: number } — count of events moved to archive
|
|
88
76
|
*/
|
|
89
77
|
export function compactMilestoneEvents(basePath, milestoneId) {
|
|
90
|
-
const logPath =
|
|
91
|
-
const archivePath =
|
|
78
|
+
const logPath = workflowEventLogPath(basePath);
|
|
79
|
+
const archivePath = workflowEventArchivePath(basePath, milestoneId);
|
|
92
80
|
return withFileLockSync(logPath, () => {
|
|
93
81
|
const allEvents = readEvents(logPath);
|
|
94
82
|
// Single-pass partition to halve the work (per reviewer agent)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureProjectWorkflowMcpConfig, } from "./mcp-project-config.js";
|
|
2
|
+
import { warmWorkflowMcpProbeInBackground } from "./workflow-mcp-readiness-cache.js";
|
|
2
3
|
import { usesWorkflowMcpTransport } from "./workflow-mcp.js";
|
|
3
4
|
function withModelOverride(ctx, modelOverride) {
|
|
4
5
|
if (!modelOverride)
|
|
@@ -46,6 +47,7 @@ export function prepareWorkflowMcpForProject(ctx, projectRoot, modelOverride) {
|
|
|
46
47
|
if (result.status !== "unchanged") {
|
|
47
48
|
prepCtx.ui?.notify?.(`GSD MCP Server Prepared at ${result.configPath}`, "info");
|
|
48
49
|
}
|
|
50
|
+
warmWorkflowMcpProbeInBackground(projectRoot);
|
|
49
51
|
return result;
|
|
50
52
|
}
|
|
51
53
|
catch (err) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Share recent workflow MCP probe results across guided-flow and Claude Code SDK preflight.
|
|
3
|
+
import { testMcpServerConnection } from "../mcp-client/manager.js";
|
|
4
|
+
import { mcpToolMatchesBaseName } from "./mcp-tool-name.js";
|
|
5
|
+
import { detectWorkflowMcpLaunchConfig, resolveWorkflowMcpProjectRoot, } from "./workflow-mcp.js";
|
|
6
|
+
import { isWorkflowToolSurfaceName } from "./workflow-tool-surface.js";
|
|
7
|
+
/** Reuse a recent successful probe instead of spawning another stdio server. */
|
|
8
|
+
export const WORKFLOW_MCP_PROBE_CACHE_TTL_MS = 120_000;
|
|
9
|
+
/** Per-probe connect + tools/list budget (server typically ready in ~1–2s). */
|
|
10
|
+
export const WORKFLOW_MCP_PROBE_TIMEOUT_MS = 5_000;
|
|
11
|
+
const cacheByProject = new Map();
|
|
12
|
+
const probeInFlightByProject = new Map();
|
|
13
|
+
let activeWorkflowMcpSdkSessions = 0;
|
|
14
|
+
function cacheKey(projectRoot) {
|
|
15
|
+
return resolveWorkflowMcpProjectRoot(projectRoot);
|
|
16
|
+
}
|
|
17
|
+
export function beginWorkflowMcpSdkSession() {
|
|
18
|
+
activeWorkflowMcpSdkSessions++;
|
|
19
|
+
}
|
|
20
|
+
export function endWorkflowMcpSdkSession() {
|
|
21
|
+
activeWorkflowMcpSdkSessions = Math.max(0, activeWorkflowMcpSdkSessions - 1);
|
|
22
|
+
}
|
|
23
|
+
export function getCachedWorkflowMcpProbe(projectRoot) {
|
|
24
|
+
const entry = cacheByProject.get(cacheKey(projectRoot));
|
|
25
|
+
if (!entry)
|
|
26
|
+
return null;
|
|
27
|
+
if (Date.now() - entry.probedAt > WORKFLOW_MCP_PROBE_CACHE_TTL_MS) {
|
|
28
|
+
cacheByProject.delete(cacheKey(projectRoot));
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return entry;
|
|
32
|
+
}
|
|
33
|
+
export function recordWorkflowMcpProbe(projectRoot, serverName, tools) {
|
|
34
|
+
cacheByProject.set(cacheKey(projectRoot), {
|
|
35
|
+
serverName,
|
|
36
|
+
tools: [...tools],
|
|
37
|
+
probedAt: Date.now(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export function clearWorkflowMcpProbeCache(projectRoot) {
|
|
41
|
+
if (projectRoot === undefined) {
|
|
42
|
+
cacheByProject.clear();
|
|
43
|
+
probeInFlightByProject.clear();
|
|
44
|
+
activeWorkflowMcpSdkSessions = 0;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const key = cacheKey(projectRoot);
|
|
48
|
+
cacheByProject.delete(key);
|
|
49
|
+
probeInFlightByProject.delete(key);
|
|
50
|
+
}
|
|
51
|
+
export function workflowMcpProbeAdvertisesSurface(tools) {
|
|
52
|
+
return tools.some((tool) => isWorkflowToolSurfaceName(tool));
|
|
53
|
+
}
|
|
54
|
+
export function cachedWorkflowMcpCoversRequired(projectRoot, serverName, required) {
|
|
55
|
+
const entry = getCachedWorkflowMcpProbe(projectRoot);
|
|
56
|
+
if (!entry || entry.serverName !== serverName)
|
|
57
|
+
return false;
|
|
58
|
+
return required.every((tool) => entry.tools.some((name) => name === tool || mcpToolMatchesBaseName(name, tool)));
|
|
59
|
+
}
|
|
60
|
+
async function runProbeAndCacheWorkflowMcp(projectRoot, options = {}) {
|
|
61
|
+
const root = cacheKey(projectRoot);
|
|
62
|
+
const launch = detectWorkflowMcpLaunchConfig(root);
|
|
63
|
+
const serverName = launch?.name ?? "gsd-workflow";
|
|
64
|
+
const cached = getCachedWorkflowMcpProbe(projectRoot);
|
|
65
|
+
if (cached && workflowMcpProbeAdvertisesSurface(cached.tools)) {
|
|
66
|
+
return { ok: true, tools: cached.tools, serverName: cached.serverName };
|
|
67
|
+
}
|
|
68
|
+
const result = await testMcpServerConnection(serverName, {
|
|
69
|
+
projectDir: root,
|
|
70
|
+
timeoutMs: options.timeoutMs ?? WORKFLOW_MCP_PROBE_TIMEOUT_MS,
|
|
71
|
+
});
|
|
72
|
+
if (result.ok && workflowMcpProbeAdvertisesSurface(result.tools)) {
|
|
73
|
+
recordWorkflowMcpProbe(projectRoot, serverName, result.tools);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
ok: result.ok,
|
|
77
|
+
tools: result.tools,
|
|
78
|
+
serverName,
|
|
79
|
+
error: result.error,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export async function probeAndCacheWorkflowMcp(projectRoot, options = {}) {
|
|
83
|
+
const key = cacheKey(projectRoot);
|
|
84
|
+
const inFlight = probeInFlightByProject.get(key);
|
|
85
|
+
if (inFlight)
|
|
86
|
+
return inFlight;
|
|
87
|
+
const promise = runProbeAndCacheWorkflowMcp(projectRoot, options).finally(() => {
|
|
88
|
+
probeInFlightByProject.delete(key);
|
|
89
|
+
});
|
|
90
|
+
probeInFlightByProject.set(key, promise);
|
|
91
|
+
return promise;
|
|
92
|
+
}
|
|
93
|
+
/** Fire-and-forget probe so /gsd dispatch often hits a warm cache. */
|
|
94
|
+
export function warmWorkflowMcpProbeInBackground(projectRoot) {
|
|
95
|
+
if (activeWorkflowMcpSdkSessions > 0)
|
|
96
|
+
return;
|
|
97
|
+
const key = cacheKey(projectRoot);
|
|
98
|
+
if (getCachedWorkflowMcpProbe(projectRoot))
|
|
99
|
+
return;
|
|
100
|
+
if (probeInFlightByProject.has(key))
|
|
101
|
+
return;
|
|
102
|
+
void probeAndCacheWorkflowMcp(projectRoot).catch(() => {
|
|
103
|
+
// Background warm is best-effort.
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -2,6 +2,8 @@ import { join } from "node:path";
|
|
|
2
2
|
import { mkdirSync, existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
3
|
import { logWarning, logError } from "./workflow-logger.js";
|
|
4
4
|
import { readEvents, findForkPoint, getSessionId } from "./workflow-events.js";
|
|
5
|
+
import { normalizeWorkflowEventCommand, workflowEventEntityKey, } from "./workflow-event-vocabulary.js";
|
|
6
|
+
import { readWorktreeEventLogPath, workflowEventLogPath, writeWorkflowEventLog, writeWorktreeEventLog, } from "./workflow-event-ledger.js";
|
|
5
7
|
import { transaction, updateTaskStatus, updateSliceStatus, updateMilestoneStatus, getSliceTasks, insertMilestone, getMilestoneSlices, insertVerificationEvidence, upsertDecision, setTaskBlockerDiscovered, insertOrIgnoreSlice, insertOrIgnoreTask, } from "./gsd-db.js";
|
|
6
8
|
import { openWorkflowDatabasePath } from "./db-workspace.js";
|
|
7
9
|
import { isClosedStatus } from "./status-guards.js";
|
|
@@ -48,14 +50,11 @@ function replayEvents(events) {
|
|
|
48
50
|
transaction(() => {
|
|
49
51
|
for (const event of events) {
|
|
50
52
|
const p = event.params;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Type guard: malformed event lines with non-string cmd are skipped.
|
|
54
|
-
if (typeof event.cmd !== "string") {
|
|
53
|
+
const cmd = normalizeWorkflowEventCommand(event.cmd);
|
|
54
|
+
if (!cmd) {
|
|
55
55
|
logWarning("reconcile", `Event with non-string cmd skipped: ${JSON.stringify(event.cmd)}`);
|
|
56
56
|
continue;
|
|
57
57
|
}
|
|
58
|
-
const cmd = event.cmd.replace(/-/g, "_");
|
|
59
58
|
switch (cmd) {
|
|
60
59
|
case "complete_task": {
|
|
61
60
|
const milestoneId = p["milestoneId"];
|
|
@@ -199,43 +198,7 @@ function replayEvents(events) {
|
|
|
199
198
|
* (e.g. unknown or future cmds).
|
|
200
199
|
*/
|
|
201
200
|
export function extractEntityKey(event) {
|
|
202
|
-
|
|
203
|
-
// Normalize cmd format: accept both hyphens and underscores
|
|
204
|
-
if (typeof event.cmd !== "string")
|
|
205
|
-
return null;
|
|
206
|
-
const cmd = event.cmd.replace(/-/g, "_");
|
|
207
|
-
switch (cmd) {
|
|
208
|
-
case "complete_task":
|
|
209
|
-
case "start_task":
|
|
210
|
-
case "skip_task":
|
|
211
|
-
case "report_blocker":
|
|
212
|
-
case "record_verification":
|
|
213
|
-
case "plan_task":
|
|
214
|
-
return typeof p["taskId"] === "string"
|
|
215
|
-
? { type: "task", id: p["taskId"] }
|
|
216
|
-
: null;
|
|
217
|
-
case "complete_slice":
|
|
218
|
-
case "replan_slice":
|
|
219
|
-
return typeof p["sliceId"] === "string"
|
|
220
|
-
? { type: "slice", id: p["sliceId"] }
|
|
221
|
-
: null;
|
|
222
|
-
case "plan_slice":
|
|
223
|
-
return typeof p["sliceId"] === "string"
|
|
224
|
-
? { type: "slice_plan", id: p["sliceId"] }
|
|
225
|
-
: null;
|
|
226
|
-
case "complete_milestone":
|
|
227
|
-
case "plan_milestone":
|
|
228
|
-
return typeof p["milestoneId"] === "string"
|
|
229
|
-
? { type: "milestone", id: p["milestoneId"] }
|
|
230
|
-
: null;
|
|
231
|
-
case "save_decision":
|
|
232
|
-
if (typeof p["scope"] === "string" && typeof p["decision"] === "string") {
|
|
233
|
-
return { type: "decision", id: `${p["scope"]}:${p["decision"]}` };
|
|
234
|
-
}
|
|
235
|
-
return null;
|
|
236
|
-
default:
|
|
237
|
-
return null;
|
|
238
|
-
}
|
|
201
|
+
return workflowEventEntityKey(event);
|
|
239
202
|
}
|
|
240
203
|
// ─── detectConflicts ──────────────────────────────────────────────────────────
|
|
241
204
|
/**
|
|
@@ -302,12 +265,6 @@ function rewriteDivergedEventsForEntity(divergedEvents, entityType, entityId, re
|
|
|
302
265
|
}
|
|
303
266
|
return rewritten;
|
|
304
267
|
}
|
|
305
|
-
function writeEventLog(basePath, events) {
|
|
306
|
-
const dir = join(basePath, ".gsd");
|
|
307
|
-
mkdirSync(dir, { recursive: true });
|
|
308
|
-
const content = events.map((e) => JSON.stringify(e)).join("\n") + (events.length > 0 ? "\n" : "");
|
|
309
|
-
atomicWriteSync(join(dir, "event-log.jsonl"), content);
|
|
310
|
-
}
|
|
311
268
|
// ─── writeConflictsFile ───────────────────────────────────────────────────────
|
|
312
269
|
/**
|
|
313
270
|
* Write a human-readable CONFLICTS.md to basePath/.gsd/CONFLICTS.md.
|
|
@@ -375,10 +332,15 @@ export function reconcileWorktreeLogs(mainBasePath, worktreeBasePath) {
|
|
|
375
332
|
}
|
|
376
333
|
function _reconcileWorktreeLogsInner(mainBasePath, worktreeBasePath) {
|
|
377
334
|
// Step 1: Read both logs
|
|
378
|
-
const mainLogPath =
|
|
379
|
-
const wtLogPath =
|
|
335
|
+
const mainLogPath = workflowEventLogPath(mainBasePath);
|
|
336
|
+
const wtLogPath = readWorktreeEventLogPath(worktreeBasePath);
|
|
380
337
|
const mainEvents = readEvents(mainLogPath);
|
|
381
338
|
const wtEvents = readEvents(wtLogPath);
|
|
339
|
+
// Canonical worktree appends are already durable in the project ledger.
|
|
340
|
+
// Empty/missing worktree shards are legacy-only absence, not divergence.
|
|
341
|
+
if (wtEvents.length === 0) {
|
|
342
|
+
return { autoMerged: 0, conflicts: [] };
|
|
343
|
+
}
|
|
382
344
|
// Step 2: Find fork point
|
|
383
345
|
const forkPoint = findForkPoint(mainEvents, wtEvents);
|
|
384
346
|
// Step 3: Slice diverged sets
|
|
@@ -412,9 +374,7 @@ function _reconcileWorktreeLogsInner(mainBasePath, worktreeBasePath) {
|
|
|
412
374
|
}
|
|
413
375
|
const baseEvents = mainEvents.slice(0, forkPoint + 1);
|
|
414
376
|
const mergedLog = baseEvents.concat(merged);
|
|
415
|
-
|
|
416
|
-
mkdirSync(join(mainBasePath, ".gsd"), { recursive: true });
|
|
417
|
-
atomicWriteSync(join(mainBasePath, ".gsd", "event-log.jsonl"), logContent);
|
|
377
|
+
writeWorkflowEventLog(mainBasePath, mergedLog);
|
|
418
378
|
// Step 8: Replay into DB (wrapped in a transaction by replayEvents)
|
|
419
379
|
openWorkflowDatabasePath(resolveGsdPathContract(mainBasePath).projectDb);
|
|
420
380
|
replayEvents(merged);
|
|
@@ -533,8 +493,8 @@ pick) {
|
|
|
533
493
|
throw new Error(`No conflict found for entity ${entityKey}`);
|
|
534
494
|
const conflict = conflicts[idx];
|
|
535
495
|
const eventsToReplay = pick === "main" ? conflict.mainSideEvents : conflict.worktreeSideEvents;
|
|
536
|
-
const mainLogPath =
|
|
537
|
-
const wtLogPath =
|
|
496
|
+
const mainLogPath = workflowEventLogPath(basePath);
|
|
497
|
+
const wtLogPath = readWorktreeEventLogPath(worktreeBasePath);
|
|
538
498
|
const mainEvents = readEvents(mainLogPath);
|
|
539
499
|
const wtEvents = readEvents(wtLogPath);
|
|
540
500
|
const forkPoint = findForkPoint(mainEvents, wtEvents);
|
|
@@ -547,7 +507,12 @@ pick) {
|
|
|
547
507
|
: rewriteDivergedEventsForEntity(mainDiverged, entityType, entityId, eventsToReplay);
|
|
548
508
|
const targetBasePath = pick === "main" ? worktreeBasePath : basePath;
|
|
549
509
|
const targetBaseEvents = pick === "main" ? wtBaseEvents : mainBaseEvents;
|
|
550
|
-
|
|
510
|
+
if (pick === "main") {
|
|
511
|
+
writeWorktreeEventLog(targetBasePath, targetBaseEvents.concat(rewrittenTargetEvents));
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
writeWorkflowEventLog(targetBasePath, targetBaseEvents.concat(rewrittenTargetEvents));
|
|
515
|
+
}
|
|
551
516
|
// Replay resolved events through the DB (updates DB state)
|
|
552
517
|
openWorkflowDatabasePath(resolveGsdPathContract(basePath).projectDb);
|
|
553
518
|
replayEvents(eventsToReplay);
|
|
@@ -528,7 +528,13 @@ export function removeWorktree(basePath, name, opts = {}) {
|
|
|
528
528
|
// inside .gsd/worktrees/ — a symlink inside the directory could point out.
|
|
529
529
|
const resolvedPathSafe = isInsideWorktreesDir(basePath, resolvedWtPath);
|
|
530
530
|
// If we're inside the worktree, move out first — git can't remove an in-use directory
|
|
531
|
-
|
|
531
|
+
let cwd;
|
|
532
|
+
try {
|
|
533
|
+
cwd = process.cwd();
|
|
534
|
+
}
|
|
535
|
+
catch {
|
|
536
|
+
cwd = basePath;
|
|
537
|
+
}
|
|
532
538
|
const resolvedCwd = existsSync(cwd) ? realpathSync(cwd) : cwd;
|
|
533
539
|
if (resolvedCwd === resolvedWtPath || resolvedCwd.startsWith(resolvedWtPath + sep)) {
|
|
534
540
|
process.chdir(basePath);
|
|
@@ -90,38 +90,40 @@ export function createWorktreeSafetyModule(deps = defaultDeps) {
|
|
|
90
90
|
return failure("worktree-git-marker-not-file", `Worktree root ${unitRoot} has a .git directory, not a registered worktree .git file.`, "Use a registered GSD worktree instead of a copied or nested repository.", { gitMarker });
|
|
91
91
|
}
|
|
92
92
|
let registered;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
93
|
+
if (isolationMode === "worktree") {
|
|
94
|
+
try {
|
|
95
|
+
registered = deps.listRegisteredWorktrees?.(projectRoot);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return failure("worktree-git-probe-failed", `Unable to list registered worktrees for project root ${projectRoot}.`, "Recover or recreate the milestone worktree before dispatching the source-writing Unit.", { projectRoot, error: errorMessage(error) });
|
|
99
|
+
}
|
|
100
|
+
if (registered && !registered.some((worktree) => samePath(worktree.path, unitRoot))) {
|
|
101
|
+
const wasPreviouslyTracked = unregisteredRecoveryFailed.has(unitRoot);
|
|
102
|
+
let attemptedPrune = false;
|
|
103
|
+
if (!wasPreviouslyTracked && deps.pruneRegisteredWorktrees) {
|
|
104
|
+
attemptedPrune = true;
|
|
105
|
+
try {
|
|
106
|
+
deps.pruneRegisteredWorktrees(projectRoot);
|
|
107
|
+
const rechecked = deps.listRegisteredWorktrees?.(projectRoot);
|
|
108
|
+
if (rechecked?.some((worktree) => samePath(worktree.path, unitRoot))) {
|
|
109
|
+
unregisteredRecoveryFailed.delete(unitRoot);
|
|
110
|
+
registered = rechecked;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
unregisteredRecoveryFailed.add(unitRoot);
|
|
114
|
+
}
|
|
110
115
|
}
|
|
111
|
-
|
|
116
|
+
catch (error) {
|
|
112
117
|
unregisteredRecoveryFailed.add(unitRoot);
|
|
118
|
+
return failure("worktree-git-probe-failed", `Unable to recover unregistered worktree root ${unitRoot}.`, "Run 'git worktree prune', then recreate or re-register the milestone worktree before dispatching the source-writing Unit.", { projectRoot, unitRoot, error: errorMessage(error) });
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
if (!registered?.some((worktree) => samePath(worktree.path, unitRoot))) {
|
|
122
|
+
return failure("worktree-unregistered", `Worktree root ${unitRoot} is not registered with git worktree list.`, attemptedPrune || wasPreviouslyTracked
|
|
123
|
+
? "Worktree recovery was attempted but the root is still unregistered. Recreate or re-register the milestone worktree before dispatching the source-writing Unit."
|
|
124
|
+
: "Run 'git worktree prune'. If still unregistered, recreate or re-register the milestone worktree before dispatching the source-writing Unit.", { unitRoot, attemptedPrune, trackedAsFailed: unregisteredRecoveryFailed.has(unitRoot) });
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
|
-
if (!registered?.some((worktree) => samePath(worktree.path, unitRoot))) {
|
|
121
|
-
return failure("worktree-unregistered", `Worktree root ${unitRoot} is not registered with git worktree list.`, attemptedPrune || wasPreviouslyTracked
|
|
122
|
-
? "Worktree recovery was attempted but the root is still unregistered. Recreate or re-register the milestone worktree before dispatching the source-writing Unit."
|
|
123
|
-
: "Run 'git worktree prune'. If still unregistered, recreate or re-register the milestone worktree before dispatching the source-writing Unit.", { unitRoot, attemptedPrune, trackedAsFailed: unregisteredRecoveryFailed.has(unitRoot) });
|
|
124
|
-
}
|
|
125
127
|
}
|
|
126
128
|
if (input.emptyWorktreeWithProjectContent) {
|
|
127
129
|
return failure("empty-worktree-with-project-content", `Worktree root ${unitRoot} has no project content, but the project root does.`, "Resolve untracked project-root content or recreate the worktree so source writes stay isolated.", { unitRoot, projectRoot });
|
|
@@ -195,7 +195,14 @@ export function resolveGitHeadPath(dir) {
|
|
|
195
195
|
*/
|
|
196
196
|
export function nudgeGitBranchCache(previousCwd) {
|
|
197
197
|
const now = new Date();
|
|
198
|
-
|
|
198
|
+
let currentCwd = null;
|
|
199
|
+
try {
|
|
200
|
+
currentCwd = process.cwd();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
currentCwd = null;
|
|
204
|
+
}
|
|
205
|
+
for (const dir of [previousCwd, currentCwd].filter((dir) => Boolean(dir))) {
|
|
199
206
|
try {
|
|
200
207
|
const headPath = resolveGitHeadPath(dir);
|
|
201
208
|
if (headPath)
|
|
@@ -30,6 +30,8 @@ const CHILD_ENV_ALLOWLIST = new Set([
|
|
|
30
30
|
"XDG_CACHE_HOME",
|
|
31
31
|
]);
|
|
32
32
|
const MCP_STDERR_MAX_BYTES = 4096;
|
|
33
|
+
/** Short-lived stdio probes must not register/kill production MCP PIDs (see probe-mode.ts). */
|
|
34
|
+
export const GSD_MCP_PROBE_ENV = "GSD_MCP_PROBE";
|
|
33
35
|
let cachedStatus = null;
|
|
34
36
|
let cachedStatusKey = "";
|
|
35
37
|
export function clearMcpConfigCache() {
|
|
@@ -259,7 +261,10 @@ export async function testMcpServerConnection(nameOrConfig, options = {}) {
|
|
|
259
261
|
transport = new StdioClientTransport({
|
|
260
262
|
command: config.command ?? "",
|
|
261
263
|
args: config.args,
|
|
262
|
-
env:
|
|
264
|
+
env: {
|
|
265
|
+
...buildMcpChildEnv(config.env),
|
|
266
|
+
[GSD_MCP_PROBE_ENV]: "1",
|
|
267
|
+
},
|
|
263
268
|
cwd: config.cwd,
|
|
264
269
|
stderr: "pipe",
|
|
265
270
|
});
|
|
@@ -174,6 +174,9 @@ description: ... # What it does AND when to use it (third person)
|
|
|
174
174
|
---
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
Frontmatter must parse as YAML. Quote description values containing `:` or use
|
|
178
|
+
a folded block (`description: >`) for longer trigger descriptions.
|
|
179
|
+
|
|
177
180
|
Name conventions: `create-*`, `manage-*`, `setup-*`, `generate-*`, `build-*`
|
|
178
181
|
</yaml_requirements>
|
|
179
182
|
|
|
@@ -109,6 +109,7 @@ description: What it does and when to use it (third person, specific triggers)
|
|
|
109
109
|
- No XML tags
|
|
110
110
|
- Third person (never first or second person)
|
|
111
111
|
- Include what it does AND when to use it
|
|
112
|
+
- Must parse as YAML; quote values containing `:` or use `description: >`
|
|
112
113
|
|
|
113
114
|
**Critical rule**: Always write in third person.
|
|
114
115
|
- ✅ "Processes Excel files and generates reports"
|
package/dist/runtime-checks.d.ts
CHANGED
|
@@ -25,3 +25,13 @@ export declare function checkNodeVersion(versionString: string, min?: number): {
|
|
|
25
25
|
* real subprocess.
|
|
26
26
|
*/
|
|
27
27
|
export declare function requireGit(execFn: (cmd: string, args: ReadonlyArray<string>) => unknown): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Fast presence check for `git`: scan the directories on `$PATH` for a `git`
|
|
30
|
+
* executable (plus Windows `.exe`/`.cmd` variants) instead of spawning
|
|
31
|
+
* `git --version`. The subprocess form costs ~15ms on every startup and showed
|
|
32
|
+
* up as ~5% of cold-start CPU; a filesystem `existsSync` scan is far cheaper and
|
|
33
|
+
* answers the same gate ("is git installed"). Returns true if a candidate path
|
|
34
|
+
* exists. `env`/`platform` are injectable so this can be unit-tested without a
|
|
35
|
+
* real $PATH.
|
|
36
|
+
*/
|
|
37
|
+
export declare function gitAvailableOnPath(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
|
package/dist/runtime-checks.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Runtime dependency checks — pure helpers used by loader.ts.
|
|
2
2
|
// Extracted so they can be unit-tested without spawning the full loader.
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { delimiter, join } from 'path';
|
|
3
5
|
/**
|
|
4
6
|
* Minimum supported Node.js major version. Kept in sync with
|
|
5
7
|
* `engines.node` in package.json — see test
|
|
@@ -36,3 +38,28 @@ export function requireGit(execFn) {
|
|
|
36
38
|
return false;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Fast presence check for `git`: scan the directories on `$PATH` for a `git`
|
|
43
|
+
* executable (plus Windows `.exe`/`.cmd` variants) instead of spawning
|
|
44
|
+
* `git --version`. The subprocess form costs ~15ms on every startup and showed
|
|
45
|
+
* up as ~5% of cold-start CPU; a filesystem `existsSync` scan is far cheaper and
|
|
46
|
+
* answers the same gate ("is git installed"). Returns true if a candidate path
|
|
47
|
+
* exists. `env`/`platform` are injectable so this can be unit-tested without a
|
|
48
|
+
* real $PATH.
|
|
49
|
+
*/
|
|
50
|
+
export function gitAvailableOnPath(env = process.env, platform = process.platform) {
|
|
51
|
+
const pathValue = env.PATH ?? env.Path ?? env.path ?? '';
|
|
52
|
+
if (pathValue.length === 0)
|
|
53
|
+
return false;
|
|
54
|
+
const dirs = pathValue.split(delimiter).filter((d) => d.length > 0);
|
|
55
|
+
const names = platform === 'win32'
|
|
56
|
+
? (env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').map((ext) => `git${ext.toLowerCase()}`).concat('git')
|
|
57
|
+
: ['git'];
|
|
58
|
+
for (const dir of dirs) {
|
|
59
|
+
for (const name of names) {
|
|
60
|
+
if (existsSync(join(dir, name)))
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|