gsd-pi 2.41.0-dev.0acbce9 → 2.41.0-dev.5a170d0
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-web-branch.d.ts +6 -0
- package/dist/cli-web-branch.js +17 -0
- package/dist/onboarding.js +2 -1
- package/dist/resources/extensions/gsd/auto/loop.js +89 -1
- package/dist/resources/extensions/gsd/auto/phases.js +28 -10
- package/dist/resources/extensions/gsd/auto/session.js +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +19 -2
- package/dist/resources/extensions/gsd/auto-post-unit.js +7 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +12 -4
- package/dist/resources/extensions/gsd/auto-start.js +8 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +147 -13
- package/dist/resources/extensions/gsd/auto.js +64 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +199 -164
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +62 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +40 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
- package/dist/resources/extensions/gsd/context-injector.js +74 -0
- package/dist/resources/extensions/gsd/context-store.js +4 -3
- package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
- package/dist/resources/extensions/gsd/custom-verification.js +145 -0
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
- package/dist/resources/extensions/gsd/db-writer.js +5 -2
- package/dist/resources/extensions/gsd/definition-loader.js +352 -0
- package/dist/resources/extensions/gsd/detection.js +1 -1
- package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
- package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
- package/dist/resources/extensions/gsd/doctor.js +11 -1
- package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
- package/dist/resources/extensions/gsd/engine-types.js +8 -0
- package/dist/resources/extensions/gsd/execution-policy.js +8 -0
- package/dist/resources/extensions/gsd/exit-command.js +12 -2
- package/dist/resources/extensions/gsd/export.js +9 -13
- package/dist/resources/extensions/gsd/extension-manifest.json +2 -2
- package/dist/resources/extensions/gsd/files.js +28 -11
- package/dist/resources/extensions/gsd/forensics.js +10 -3
- package/dist/resources/extensions/gsd/git-service.js +5 -1
- package/dist/resources/extensions/gsd/graph.js +225 -0
- package/dist/resources/extensions/gsd/gsd-db.js +25 -8
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +7 -3
- package/dist/resources/extensions/gsd/journal.js +85 -0
- package/dist/resources/extensions/gsd/md-importer.js +5 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +2 -2
- package/dist/resources/extensions/gsd/post-unit-hooks.js +24 -412
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +1 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +34 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +46 -2
- package/dist/resources/extensions/gsd/rule-registry.js +489 -0
- package/dist/resources/extensions/gsd/rule-types.js +6 -0
- package/dist/resources/extensions/gsd/run-manager.js +134 -0
- package/dist/resources/extensions/gsd/service-tier.js +138 -0
- package/dist/resources/extensions/gsd/structured-data-formatter.js +2 -1
- package/dist/resources/extensions/gsd/templates/decisions.md +2 -2
- package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
- package/dist/resources/extensions/gsd/workflow-templates.js +13 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +20 -6
- package/dist/resources/extensions/gsd/worktree-resolver.js +19 -2
- package/dist/resources/extensions/subagent/index.js +7 -3
- package/dist/resources/extensions/voice/index.js +4 -4
- package/dist/resources/skills/create-workflow/SKILL.md +103 -0
- package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -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.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.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 +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.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/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.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/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +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 +16 -16
- package/dist/web/standalone/.next/server/chunks/229.js +3 -3
- 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/4024.c195dc1fdd2adbea.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9afaaebf6042a1d7.js → webpack-fa307370fcf9fb2c.js} +1 -1
- package/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +29 -7
- package/package.json +1 -1
- package/packages/native/src/__tests__/text.test.mjs +33 -0
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +3 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +10 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +4 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +11 -7
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -1
- package/src/resources/extensions/gsd/auto/loop.ts +101 -1
- package/src/resources/extensions/gsd/auto/phases.ts +30 -10
- package/src/resources/extensions/gsd/auto/session.ts +6 -0
- package/src/resources/extensions/gsd/auto/types.ts +4 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +25 -5
- package/src/resources/extensions/gsd/auto-post-unit.ts +8 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +12 -4
- package/src/resources/extensions/gsd/auto-start.ts +8 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +162 -18
- package/src/resources/extensions/gsd/auto.ts +71 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +209 -162
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +62 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -0
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
- package/src/resources/extensions/gsd/commands/catalog.ts +40 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
- package/src/resources/extensions/gsd/context-injector.ts +100 -0
- package/src/resources/extensions/gsd/context-store.ts +4 -3
- package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
- package/src/resources/extensions/gsd/custom-verification.ts +180 -0
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
- package/src/resources/extensions/gsd/db-writer.ts +6 -2
- package/src/resources/extensions/gsd/definition-loader.ts +462 -0
- package/src/resources/extensions/gsd/detection.ts +1 -1
- package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
- package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
- package/src/resources/extensions/gsd/doctor.ts +12 -1
- package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
- package/src/resources/extensions/gsd/engine-types.ts +71 -0
- package/src/resources/extensions/gsd/execution-policy.ts +43 -0
- package/src/resources/extensions/gsd/exit-command.ts +14 -2
- package/src/resources/extensions/gsd/export.ts +8 -15
- package/src/resources/extensions/gsd/extension-manifest.json +2 -2
- package/src/resources/extensions/gsd/files.ts +29 -12
- package/src/resources/extensions/gsd/forensics.ts +9 -3
- package/src/resources/extensions/gsd/git-service.ts +5 -4
- package/src/resources/extensions/gsd/graph.ts +312 -0
- package/src/resources/extensions/gsd/gsd-db.ts +37 -8
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +7 -3
- package/src/resources/extensions/gsd/journal.ts +134 -0
- package/src/resources/extensions/gsd/md-importer.ts +6 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -2
- package/src/resources/extensions/gsd/post-unit-hooks.ts +24 -462
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +1 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +35 -4
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +11 -10
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +2 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +47 -2
- package/src/resources/extensions/gsd/rule-registry.ts +599 -0
- package/src/resources/extensions/gsd/rule-types.ts +68 -0
- package/src/resources/extensions/gsd/run-manager.ts +180 -0
- package/src/resources/extensions/gsd/service-tier.ts +171 -0
- package/src/resources/extensions/gsd/structured-data-formatter.ts +3 -1
- package/src/resources/extensions/gsd/templates/decisions.md +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +103 -120
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +12 -1
- package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/context-store.test.ts +10 -5
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +20 -20
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
- package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
- package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +15 -10
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
- package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +513 -0
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +147 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-id-reservation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parsers.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -25
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +11 -22
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +195 -105
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -3
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +74 -0
- package/src/resources/extensions/gsd/types.ts +3 -0
- package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
- package/src/resources/extensions/gsd/workflow-templates.ts +12 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +21 -6
- package/src/resources/extensions/gsd/worktree-resolver.ts +30 -9
- package/src/resources/extensions/subagent/index.ts +7 -3
- package/src/resources/extensions/voice/index.ts +4 -4
- package/src/resources/skills/create-workflow/SKILL.md +103 -0
- package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
- package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
- package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
- package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
- package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
- package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
- package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
- package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
- package/dist/web/standalone/.next/static/chunks/4024.279c423e4661ece1.js +0 -9
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{SwbKZ7JPNFlEmU4f8pKEv → K7GYOOPvQWX6TKYEKhODM}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for #1852: removeWorktree targets wrong path when .gsd/ is a symlink.
|
|
3
|
+
*
|
|
4
|
+
* When .gsd/ is a symlink to an external state directory, git registers
|
|
5
|
+
* the worktree at the resolved (real) path. But removeWorktree recomputes
|
|
6
|
+
* the path via worktreePath() which uses the unresolved symlink, causing
|
|
7
|
+
* a mismatch — the removal silently fails.
|
|
8
|
+
*
|
|
9
|
+
* Fix: removeWorktree should query `git worktree list` to find the actual
|
|
10
|
+
* registered path when the computed path doesn't match.
|
|
11
|
+
*/
|
|
12
|
+
import { mkdtempSync, mkdirSync, rmSync, symlinkSync, unlinkSync, writeFileSync, existsSync, realpathSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { execSync } from "node:child_process";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
createWorktree,
|
|
19
|
+
removeWorktree,
|
|
20
|
+
listWorktrees,
|
|
21
|
+
worktreePath,
|
|
22
|
+
} from "../worktree-manager.ts";
|
|
23
|
+
import { createTestContext } from './test-helpers.ts';
|
|
24
|
+
|
|
25
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
26
|
+
|
|
27
|
+
function run(command: string, cwd: string): string {
|
|
28
|
+
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Set up a test repo with .gsd/ as a symlink to an external directory,
|
|
32
|
+
// mimicking the external state directory layout (~/.gsd/projects/<hash>/).
|
|
33
|
+
// Resolve tmpdir to handle macOS /tmp -> /private/var/... symlink.
|
|
34
|
+
const realTmp = realpathSync(tmpdir());
|
|
35
|
+
const base = mkdtempSync(join(realTmp, "gsd-wt-symlink-test-"));
|
|
36
|
+
const externalState = mkdtempSync(join(realTmp, "gsd-wt-symlink-ext-"));
|
|
37
|
+
|
|
38
|
+
run("git init -b main", base);
|
|
39
|
+
run('git config user.name "Test"', base);
|
|
40
|
+
run('git config user.email "test@example.com"', base);
|
|
41
|
+
|
|
42
|
+
// Create external state directory structure
|
|
43
|
+
mkdirSync(join(externalState, "worktrees"), { recursive: true });
|
|
44
|
+
|
|
45
|
+
// Create .gsd as a symlink to the external state directory
|
|
46
|
+
symlinkSync(externalState, join(base, ".gsd"));
|
|
47
|
+
|
|
48
|
+
// Verify the symlink is in place
|
|
49
|
+
assertTrue(existsSync(join(base, ".gsd")), ".gsd symlink exists");
|
|
50
|
+
assertTrue(
|
|
51
|
+
realpathSync(join(base, ".gsd")) === externalState,
|
|
52
|
+
".gsd resolves to external state dir",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Create initial commit so we have a valid repo
|
|
56
|
+
writeFileSync(join(base, "README.md"), "# Test\n", "utf-8");
|
|
57
|
+
run("git add .", base);
|
|
58
|
+
run('git commit -m "init"', base);
|
|
59
|
+
|
|
60
|
+
async function main(): Promise<void> {
|
|
61
|
+
console.log("\n=== #1852: removeWorktree with symlinked .gsd/ ===");
|
|
62
|
+
|
|
63
|
+
// Create a worktree — git will resolve the symlink and register
|
|
64
|
+
// the worktree at the external path
|
|
65
|
+
const info = createWorktree(base, "M002", { branch: "milestone/M002" });
|
|
66
|
+
assertTrue(info.exists, "worktree created");
|
|
67
|
+
|
|
68
|
+
// Verify worktree was created at the resolved (external) path
|
|
69
|
+
const realWtPath = realpathSync(info.path);
|
|
70
|
+
assertTrue(
|
|
71
|
+
realWtPath.startsWith(externalState),
|
|
72
|
+
`worktree real path (${realWtPath}) is under external state dir`,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Verify git registered the worktree
|
|
76
|
+
const gitList = run("git worktree list", base);
|
|
77
|
+
assertTrue(gitList.includes("M002"), "git worktree list shows M002");
|
|
78
|
+
|
|
79
|
+
// The computed path via worktreePath uses the symlink path
|
|
80
|
+
const computedPath = worktreePath(base, "M002");
|
|
81
|
+
assertTrue(existsSync(computedPath), "computed path exists (via symlink)");
|
|
82
|
+
|
|
83
|
+
// Simulate what syncStateToProjectRoot does: replace the .gsd symlink with
|
|
84
|
+
// a real directory containing stale worktree data. This causes worktreePath()
|
|
85
|
+
// to compute a LOCAL path that differs from git's REGISTERED path (the
|
|
86
|
+
// resolved external path). The stale local dir passes existsSync but is not
|
|
87
|
+
// a real git worktree, so nativeWorktreeRemove fails silently.
|
|
88
|
+
unlinkSync(join(base, ".gsd")); // remove the symlink
|
|
89
|
+
mkdirSync(join(base, ".gsd", "worktrees", "M002"), { recursive: true });
|
|
90
|
+
// Write a dummy file so the stale directory is non-empty
|
|
91
|
+
writeFileSync(join(base, ".gsd", "worktrees", "M002", "stale.txt"), "stale sync artifact", "utf-8");
|
|
92
|
+
|
|
93
|
+
// Now worktreePath(base, "M002") points to the LOCAL stale dir, not the
|
|
94
|
+
// external path where git actually registered the worktree.
|
|
95
|
+
const stalePath = worktreePath(base, "M002");
|
|
96
|
+
assertTrue(existsSync(stalePath), "stale local worktree dir exists");
|
|
97
|
+
assertTrue(
|
|
98
|
+
stalePath !== realWtPath,
|
|
99
|
+
`computed path (${stalePath}) differs from git-registered path (${realWtPath})`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// THE ACTUAL TEST: removeWorktree must find the git-registered path and
|
|
103
|
+
// remove the real worktree, not just operate on the stale local directory.
|
|
104
|
+
removeWorktree(base, "M002", { branch: "milestone/M002", deleteBranch: true });
|
|
105
|
+
|
|
106
|
+
// After removal, the worktree should be gone from git's list
|
|
107
|
+
const gitListAfter = run("git worktree list", base);
|
|
108
|
+
assertTrue(
|
|
109
|
+
!gitListAfter.includes("M002"),
|
|
110
|
+
"worktree removed from git worktree list after removeWorktree",
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// The branch should be deleted
|
|
114
|
+
const branches = run("git branch", base);
|
|
115
|
+
assertTrue(
|
|
116
|
+
!branches.includes("milestone/M002"),
|
|
117
|
+
"milestone/M002 branch deleted after removeWorktree",
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// The worktree directory should be gone
|
|
121
|
+
assertTrue(
|
|
122
|
+
!existsSync(realWtPath),
|
|
123
|
+
"worktree directory removed from disk",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// List should be empty
|
|
127
|
+
const listed = listWorktrees(base);
|
|
128
|
+
assertEq(listed.length, 0, "no worktrees listed after removal");
|
|
129
|
+
|
|
130
|
+
// Cleanup
|
|
131
|
+
rmSync(base, { recursive: true, force: true });
|
|
132
|
+
rmSync(externalState, { recursive: true, force: true });
|
|
133
|
+
|
|
134
|
+
report();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main().catch((error) => {
|
|
138
|
+
console.error(error);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* - syncWorktreeStateBack syncs root-level .gsd/ files (REQUIREMENTS, PROJECT, etc.)
|
|
20
20
|
* - syncWorktreeStateBack syncs ALL milestone directories, not just the current one
|
|
21
21
|
* - syncWorktreeStateBack handles next-milestone artifacts created during completion
|
|
22
|
+
* - syncGsdStateToWorktree syncs non-standard milestone dir names (#1547)
|
|
23
|
+
* - syncWorktreeStateBack syncs non-standard milestone dir names (#1547)
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
26
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
|
|
@@ -517,6 +519,78 @@ async function main(): Promise<void> {
|
|
|
517
519
|
}
|
|
518
520
|
}
|
|
519
521
|
|
|
522
|
+
// ─── 14. syncGsdStateToWorktree syncs non-standard milestone dir names (#1547) ──
|
|
523
|
+
console.log('\n=== 14. syncGsdStateToWorktree syncs non-standard milestone dir names (#1547) ===');
|
|
524
|
+
{
|
|
525
|
+
const mainBase = createBase('main');
|
|
526
|
+
const wtBase = createBase('wt');
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
// Main has milestone dirs with non-standard names
|
|
530
|
+
const customDir = join(mainBase, '.gsd', 'milestones', 'sprint-alpha');
|
|
531
|
+
mkdirSync(customDir, { recursive: true });
|
|
532
|
+
writeFileSync(join(customDir, 'CONTEXT.md'), '# Sprint Alpha Context');
|
|
533
|
+
|
|
534
|
+
const suffixDir = join(mainBase, '.gsd', 'milestones', 'M001-abc123');
|
|
535
|
+
mkdirSync(suffixDir, { recursive: true });
|
|
536
|
+
writeFileSync(join(suffixDir, 'M001-abc123-CONTEXT.md'), '# M001 Context');
|
|
537
|
+
|
|
538
|
+
assertTrue(!existsSync(join(wtBase, '.gsd', 'milestones', 'sprint-alpha')), 'sprint-alpha missing before sync');
|
|
539
|
+
assertTrue(!existsSync(join(wtBase, '.gsd', 'milestones', 'M001-abc123')), 'M001-abc123 missing before sync');
|
|
540
|
+
|
|
541
|
+
const result = syncGsdStateToWorktree(mainBase, wtBase);
|
|
542
|
+
|
|
543
|
+
assertTrue(
|
|
544
|
+
existsSync(join(wtBase, '.gsd', 'milestones', 'sprint-alpha', 'CONTEXT.md')),
|
|
545
|
+
'#1547: non-standard milestone dir "sprint-alpha" synced to worktree',
|
|
546
|
+
);
|
|
547
|
+
assertTrue(
|
|
548
|
+
existsSync(join(wtBase, '.gsd', 'milestones', 'M001-abc123', 'M001-abc123-CONTEXT.md')),
|
|
549
|
+
'#1547: suffixed milestone dir "M001-abc123" synced to worktree',
|
|
550
|
+
);
|
|
551
|
+
assertTrue(result.synced.length > 0, 'sync reported files');
|
|
552
|
+
} finally {
|
|
553
|
+
cleanup(mainBase);
|
|
554
|
+
cleanup(wtBase);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ─── 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ──
|
|
559
|
+
console.log('\n=== 15. syncWorktreeStateBack syncs non-standard milestone dir names (#1547) ===');
|
|
560
|
+
{
|
|
561
|
+
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-main-'));
|
|
562
|
+
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-back-custom-wt-'));
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
mkdirSync(join(mainBase, '.gsd', 'milestones'), { recursive: true });
|
|
566
|
+
mkdirSync(join(wtBase, '.gsd', 'milestones'), { recursive: true });
|
|
567
|
+
|
|
568
|
+
// Worktree has a non-standard milestone dir
|
|
569
|
+
const wtCustomDir = join(wtBase, '.gsd', 'milestones', 'sprint-beta');
|
|
570
|
+
mkdirSync(wtCustomDir, { recursive: true });
|
|
571
|
+
writeFileSync(join(wtCustomDir, 'SUMMARY.md'), '# Sprint Beta Summary');
|
|
572
|
+
|
|
573
|
+
assertTrue(
|
|
574
|
+
!existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta')),
|
|
575
|
+
'sprint-beta missing in main before sync',
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
|
|
579
|
+
|
|
580
|
+
assertTrue(
|
|
581
|
+
existsSync(join(mainBase, '.gsd', 'milestones', 'sprint-beta', 'SUMMARY.md')),
|
|
582
|
+
'#1547: non-standard milestone dir "sprint-beta" synced back to main',
|
|
583
|
+
);
|
|
584
|
+
assertTrue(
|
|
585
|
+
synced.some((p) => p.includes('sprint-beta')),
|
|
586
|
+
'#1547: sprint-beta appears in synced list',
|
|
587
|
+
);
|
|
588
|
+
} finally {
|
|
589
|
+
rmSync(mainBase, { recursive: true, force: true });
|
|
590
|
+
rmSync(wtBase, { recursive: true, force: true });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
520
594
|
report();
|
|
521
595
|
}
|
|
522
596
|
|
|
@@ -404,6 +404,8 @@ export interface HookStatusEntry {
|
|
|
404
404
|
|
|
405
405
|
// ─── Database Types (Decisions & Requirements) ────────────────────────────
|
|
406
406
|
|
|
407
|
+
export type DecisionMadeBy = "human" | "agent" | "collaborative";
|
|
408
|
+
|
|
407
409
|
export interface Decision {
|
|
408
410
|
seq: number; // auto-increment primary key
|
|
409
411
|
id: string; // e.g. "D001"
|
|
@@ -413,6 +415,7 @@ export interface Decision {
|
|
|
413
415
|
choice: string; // the specific choice made
|
|
414
416
|
rationale: string; // why this choice
|
|
415
417
|
revisable: string; // whether/when revisable
|
|
418
|
+
made_by: DecisionMadeBy; // who made the decision: human, agent, or collaborative
|
|
416
419
|
superseded_by: string | null; // ID of superseding decision, or null
|
|
417
420
|
}
|
|
418
421
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* workflow-engine.ts — WorkflowEngine interface.
|
|
3
|
+
*
|
|
4
|
+
* Defines the contract every engine implementation must satisfy.
|
|
5
|
+
* Imports only from the leaf-node engine-types.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
EngineState,
|
|
10
|
+
EngineDispatchAction,
|
|
11
|
+
CompletedStep,
|
|
12
|
+
ReconcileResult,
|
|
13
|
+
DisplayMetadata,
|
|
14
|
+
} from "./engine-types.js";
|
|
15
|
+
|
|
16
|
+
/** A pluggable workflow engine that drives the auto-loop. */
|
|
17
|
+
export interface WorkflowEngine {
|
|
18
|
+
/** Unique identifier for this engine (e.g. "dev", "custom"). */
|
|
19
|
+
readonly engineId: string;
|
|
20
|
+
|
|
21
|
+
/** Derive the current engine state from the project on disk. */
|
|
22
|
+
deriveState(basePath: string): Promise<EngineState>;
|
|
23
|
+
|
|
24
|
+
/** Decide what the loop should do next given current state. */
|
|
25
|
+
resolveDispatch(
|
|
26
|
+
state: EngineState,
|
|
27
|
+
context: { basePath: string },
|
|
28
|
+
): Promise<EngineDispatchAction>;
|
|
29
|
+
|
|
30
|
+
/** Reconcile state after a step has been executed. */
|
|
31
|
+
reconcile(
|
|
32
|
+
state: EngineState,
|
|
33
|
+
completedStep: CompletedStep,
|
|
34
|
+
): Promise<ReconcileResult>;
|
|
35
|
+
|
|
36
|
+
/** Return UI-facing metadata for progress display. */
|
|
37
|
+
getDisplayMetadata(state: EngineState): DisplayMetadata;
|
|
38
|
+
}
|
|
@@ -8,10 +8,21 @@
|
|
|
8
8
|
import { readFileSync, existsSync } from "node:fs";
|
|
9
9
|
import { join, dirname } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { homedir } from "node:os";
|
|
11
12
|
|
|
12
|
-
const __extensionDir =
|
|
13
|
+
const __extensionDir = resolveGsdExtensionDir();
|
|
13
14
|
const registryPath = join(__extensionDir, "workflow-templates", "registry.json");
|
|
14
15
|
|
|
16
|
+
/** Resolve the GSD extension dir with fallback to ~/.gsd/agent/extensions/gsd/. */
|
|
17
|
+
function resolveGsdExtensionDir(): string {
|
|
18
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
if (existsSync(join(moduleDir, "workflow-templates"))) return moduleDir;
|
|
20
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
21
|
+
const agentGsdDir = join(gsdHome, "agent", "extensions", "gsd");
|
|
22
|
+
if (existsSync(join(agentGsdDir, "workflow-templates"))) return agentGsdDir;
|
|
23
|
+
return moduleDir;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
16
27
|
|
|
17
28
|
export interface TemplateEntry {
|
|
@@ -286,11 +286,26 @@ export function removeWorktree(
|
|
|
286
286
|
name: string,
|
|
287
287
|
opts: { deleteBranch?: boolean; force?: boolean; branch?: string } = {},
|
|
288
288
|
): void {
|
|
289
|
-
|
|
290
|
-
const resolvedWtPath = existsSync(wtPath) ? realpathSync(wtPath) : wtPath;
|
|
289
|
+
let wtPath = worktreePath(basePath, name);
|
|
291
290
|
const branch = opts.branch ?? worktreeBranchName(name);
|
|
292
291
|
const { deleteBranch = true, force = true } = opts;
|
|
293
292
|
|
|
293
|
+
// Resolve the ACTUAL worktree path from git's worktree list.
|
|
294
|
+
// The computed path may differ when .gsd/ is (or was) a symlink to an
|
|
295
|
+
// external state directory — git resolves symlinks at worktree creation
|
|
296
|
+
// time, so its registered path points to the resolved external location.
|
|
297
|
+
// If syncStateToProjectRoot later creates a real .gsd/ directory that
|
|
298
|
+
// shadows the symlink, the computed path diverges from git's record.
|
|
299
|
+
try {
|
|
300
|
+
const entries = nativeWorktreeList(basePath);
|
|
301
|
+
const entry = entries.find(e => e.branch === branch);
|
|
302
|
+
if (entry?.path) {
|
|
303
|
+
wtPath = entry.path;
|
|
304
|
+
}
|
|
305
|
+
} catch { /* fall back to computed path */ }
|
|
306
|
+
|
|
307
|
+
const resolvedWtPath = existsSync(wtPath) ? realpathSync(wtPath) : wtPath;
|
|
308
|
+
|
|
294
309
|
// If we're inside the worktree, move out first — git can't remove an in-use directory
|
|
295
310
|
const cwd = process.cwd();
|
|
296
311
|
const resolvedCwd = existsSync(cwd) ? realpathSync(cwd) : cwd;
|
|
@@ -306,12 +321,12 @@ export function removeWorktree(
|
|
|
306
321
|
return;
|
|
307
322
|
}
|
|
308
323
|
|
|
309
|
-
// Remove worktree (force if requested, to handle dirty worktrees)
|
|
310
|
-
try { nativeWorktreeRemove(basePath,
|
|
324
|
+
// Remove worktree using the resolved path (force if requested, to handle dirty worktrees)
|
|
325
|
+
try { nativeWorktreeRemove(basePath, resolvedWtPath, force); } catch { /* may fail */ }
|
|
311
326
|
|
|
312
327
|
// If the directory is still there (e.g. locked), try harder with force
|
|
313
|
-
if (existsSync(
|
|
314
|
-
try { nativeWorktreeRemove(basePath,
|
|
328
|
+
if (existsSync(resolvedWtPath)) {
|
|
329
|
+
try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch { /* may fail */ }
|
|
315
330
|
}
|
|
316
331
|
|
|
317
332
|
// Prune stale entries so git knows the worktree is gone
|
|
@@ -28,7 +28,7 @@ export interface WorktreeResolverDeps {
|
|
|
28
28
|
basePath: string,
|
|
29
29
|
milestoneId: string,
|
|
30
30
|
roadmapContent: string,
|
|
31
|
-
) => { pushed: boolean };
|
|
31
|
+
) => { pushed: boolean; codeFilesChanged: boolean };
|
|
32
32
|
syncWorktreeStateBack: (
|
|
33
33
|
mainBasePath: string,
|
|
34
34
|
worktreePath: string,
|
|
@@ -371,10 +371,23 @@ export class WorktreeResolver {
|
|
|
371
371
|
milestoneId,
|
|
372
372
|
roadmapContent,
|
|
373
373
|
);
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
374
|
+
if (mergeResult.codeFilesChanged) {
|
|
375
|
+
ctx.notify(
|
|
376
|
+
`Milestone ${milestoneId} merged to main.${mergeResult.pushed ? " Pushed to remote." : ""}`,
|
|
377
|
+
"info",
|
|
378
|
+
);
|
|
379
|
+
} else {
|
|
380
|
+
// (#1906) Milestone produced only .gsd/ metadata — no actual code was
|
|
381
|
+
// merged. This typically means the LLM wrote planning artifacts
|
|
382
|
+
// (summaries, roadmaps) but never implemented the code. Surface this
|
|
383
|
+
// clearly so the user knows the milestone is not truly complete.
|
|
384
|
+
ctx.notify(
|
|
385
|
+
`WARNING: Milestone ${milestoneId} merged to main but contained NO code changes — only .gsd/ metadata files. ` +
|
|
386
|
+
`The milestone summary may describe planned work that was never implemented. ` +
|
|
387
|
+
`Review the milestone output and re-run if code is missing.`,
|
|
388
|
+
"warning",
|
|
389
|
+
);
|
|
390
|
+
}
|
|
378
391
|
} else {
|
|
379
392
|
// No roadmap at either location — teardown but PRESERVE the branch so
|
|
380
393
|
// commits are not orphaned. The user can merge manually later (#1573).
|
|
@@ -478,10 +491,18 @@ export class WorktreeResolver {
|
|
|
478
491
|
// Rebuild GitService after merge (branch HEAD changed)
|
|
479
492
|
this.rebuildGitService();
|
|
480
493
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
494
|
+
if (mergeResult.codeFilesChanged) {
|
|
495
|
+
ctx.notify(
|
|
496
|
+
`Milestone ${milestoneId} merged (branch mode).${mergeResult.pushed ? " Pushed to remote." : ""}`,
|
|
497
|
+
"info",
|
|
498
|
+
);
|
|
499
|
+
} else {
|
|
500
|
+
ctx.notify(
|
|
501
|
+
`WARNING: Milestone ${milestoneId} merged (branch mode) but contained NO code changes — only .gsd/ metadata. ` +
|
|
502
|
+
`Review the milestone output and re-run if code is missing.`,
|
|
503
|
+
"warning",
|
|
504
|
+
);
|
|
505
|
+
}
|
|
485
506
|
debugLog("WorktreeResolver", {
|
|
486
507
|
action: "mergeAndExit",
|
|
487
508
|
milestoneId,
|
|
@@ -516,12 +516,16 @@ async function runSingleAgentInCmuxSplit(
|
|
|
516
516
|
const bundledPaths = (process.env.GSD_BUNDLED_EXTENSION_PATHS ?? "").split(path.delimiter).map((s) => s.trim()).filter(Boolean);
|
|
517
517
|
const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
|
|
518
518
|
const processArgs = [process.env.GSD_BIN_PATH!, ...extensionArgs, ...buildSubagentProcessArgs(agent, task, tmpPromptPath)];
|
|
519
|
+
// Normalize all paths to forward slashes before embedding in bash strings.
|
|
520
|
+
// On Windows, backslashes are interpreted as escape characters by bash,
|
|
521
|
+
// mangling paths like C:\Users\user into C:Useruser (#1436).
|
|
522
|
+
const bashPath = (p: string) => shellEscape(p.replaceAll("\\", "/"));
|
|
519
523
|
const innerScript = [
|
|
520
|
-
`cd ${
|
|
524
|
+
`cd ${bashPath(cwd ?? defaultCwd)}`,
|
|
521
525
|
"set -o pipefail",
|
|
522
|
-
`${
|
|
526
|
+
`${bashPath(process.execPath)} ${processArgs.map(a => bashPath(a)).join(" ")} 2> >(tee ${bashPath(stderrPath)} >&2) | tee ${bashPath(stdoutPath)}`,
|
|
523
527
|
"status=${PIPESTATUS[0]}",
|
|
524
|
-
`printf '%s' "$status" > ${
|
|
528
|
+
`printf '%s' "$status" > ${bashPath(exitPath)}`,
|
|
525
529
|
].join("; ");
|
|
526
530
|
|
|
527
531
|
const sent = await cmuxClient.sendSurface(cmuxSurfaceId, `bash -lc ${shellEscape(innerScript)}`);
|
|
@@ -2,7 +2,7 @@ import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
2
2
|
import { shortcutDesc } from "../shared/mod.js";
|
|
3
3
|
import type { AssistantMessage } from "@gsd/pi-ai";
|
|
4
4
|
import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@gsd/pi-tui";
|
|
5
|
-
import { spawn,
|
|
5
|
+
import { spawn, execFileSync, type ChildProcess } from "node:child_process";
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
@@ -32,7 +32,7 @@ function linuxPython(): string {
|
|
|
32
32
|
function ensureBinary(): boolean {
|
|
33
33
|
if (fs.existsSync(RECOGNIZER_BIN)) return true;
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
execFileSync("swiftc", [SWIFT_SRC, "-o", RECOGNIZER_BIN, "-framework", "Speech", "-framework", "AVFoundation"], {
|
|
36
36
|
timeout: 60000,
|
|
37
37
|
});
|
|
38
38
|
return true;
|
|
@@ -54,7 +54,7 @@ function ensureLinuxReady(ctx: ExtensionContext): boolean {
|
|
|
54
54
|
|
|
55
55
|
// Check python3 exists
|
|
56
56
|
try {
|
|
57
|
-
|
|
57
|
+
execFileSync("which", ["python3"], { stdio: "pipe" });
|
|
58
58
|
} catch {
|
|
59
59
|
ctx.ui.notify("Voice: python3 not found — install with: sudo apt install python3", "error");
|
|
60
60
|
return false;
|
|
@@ -63,7 +63,7 @@ function ensureLinuxReady(ctx: ExtensionContext): boolean {
|
|
|
63
63
|
// Check that sounddevice is importable
|
|
64
64
|
const py = linuxPython();
|
|
65
65
|
try {
|
|
66
|
-
|
|
66
|
+
execFileSync(py, ["-c", "import sounddevice"], {
|
|
67
67
|
stdio: "pipe",
|
|
68
68
|
timeout: 10000,
|
|
69
69
|
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-workflow
|
|
3
|
+
description: Conversational guide for creating valid YAML workflow definitions. Use when asked to "create a workflow", "new workflow definition", "build a workflow", "workflow YAML", "define workflow steps", or "workflow from template".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<essential_principles>
|
|
7
|
+
You are a workflow definition author. You help users create valid V1 YAML workflow definitions that the GSD workflow engine can execute.
|
|
8
|
+
|
|
9
|
+
**V1 Schema Basics:**
|
|
10
|
+
|
|
11
|
+
- Every definition requires `version: 1`, a non-empty `name`, and at least one step in `steps[]`.
|
|
12
|
+
- Optional top-level fields: `description` (string), `params` (key-value defaults for `{{ key }}` substitution).
|
|
13
|
+
- Each step requires: `id` (unique string), `name` (non-empty string), `prompt` (non-empty string).
|
|
14
|
+
- Each step optionally has: `requires` or `depends_on` (array of step IDs), `produces` (array of artifact paths), `context_from` (array of step IDs), `verify` (verification policy object), `iterate` (fan-out config object).
|
|
15
|
+
- YAML uses **snake_case** keys: `depends_on`, `context_from`. The engine converts to camelCase internally.
|
|
16
|
+
|
|
17
|
+
**Validation Rules:**
|
|
18
|
+
|
|
19
|
+
- Step IDs must be unique across the workflow.
|
|
20
|
+
- Dependencies (`requires`/`depends_on`) must reference existing step IDs — no dangling refs.
|
|
21
|
+
- A step cannot depend on itself.
|
|
22
|
+
- The dependency graph must be acyclic (no circular dependencies).
|
|
23
|
+
- `produces` paths must not contain `..` (path traversal rejected).
|
|
24
|
+
- `iterate.source` must not contain `..` (path traversal rejected).
|
|
25
|
+
- `iterate.pattern` must be a valid regex with at least one capture group.
|
|
26
|
+
|
|
27
|
+
**Four Verification Policies:**
|
|
28
|
+
|
|
29
|
+
1. `content-heuristic` — Checks artifact content. Optional: `minSize` (number), `pattern` (string).
|
|
30
|
+
2. `shell-command` — Runs a shell command. Required: `command` (non-empty string).
|
|
31
|
+
3. `prompt-verify` — Asks an LLM to verify. Required: `prompt` (non-empty string).
|
|
32
|
+
4. `human-review` — Pauses for human approval. No extra fields required.
|
|
33
|
+
|
|
34
|
+
**Parameter Substitution:**
|
|
35
|
+
|
|
36
|
+
- Define defaults in top-level `params: { key: "default_value" }`.
|
|
37
|
+
- Use `{{ key }}` placeholders in step prompts — the engine replaces them at runtime.
|
|
38
|
+
- CLI overrides take precedence over definition defaults.
|
|
39
|
+
- Parameter values must not contain `..` (path traversal guard).
|
|
40
|
+
- Any unresolved `{{ key }}` after substitution causes an error.
|
|
41
|
+
|
|
42
|
+
**Path Traversal Guard:**
|
|
43
|
+
|
|
44
|
+
- The engine rejects any `produces` path or `iterate.source` containing `..`.
|
|
45
|
+
- Parameter values are also checked for `..` during substitution.
|
|
46
|
+
|
|
47
|
+
**Output Location:**
|
|
48
|
+
|
|
49
|
+
- Finished definitions go in `.gsd/workflow-defs/<name>.yaml`.
|
|
50
|
+
- After writing, tell the user to validate with `/gsd workflow validate <name>`.
|
|
51
|
+
</essential_principles>
|
|
52
|
+
|
|
53
|
+
<routing>
|
|
54
|
+
Determine the user's intent and route to the appropriate workflow:
|
|
55
|
+
|
|
56
|
+
**"I want to create a workflow from scratch" / "new workflow" / "build a workflow":**
|
|
57
|
+
→ Read `workflows/create-from-scratch.md` and follow it.
|
|
58
|
+
|
|
59
|
+
**"I want to start from a template" / "from an example" / "customize a template":**
|
|
60
|
+
→ Read `workflows/create-from-template.md` and follow it.
|
|
61
|
+
|
|
62
|
+
**"Help me understand the schema" / "what fields are available?":**
|
|
63
|
+
→ Read `references/yaml-schema-v1.md` and explain the relevant parts.
|
|
64
|
+
|
|
65
|
+
**"How does verification work?" / "verify policies":**
|
|
66
|
+
→ Read `references/verification-policies.md` and explain.
|
|
67
|
+
|
|
68
|
+
**"How do I use context_from / iterate / params?":**
|
|
69
|
+
→ Read `references/feature-patterns.md` and explain the relevant feature.
|
|
70
|
+
|
|
71
|
+
**If intent is unclear, ask one clarifying question:**
|
|
72
|
+
- "Do you want to create a workflow from scratch, or start from an existing template?"
|
|
73
|
+
- Then route based on the answer.
|
|
74
|
+
</routing>
|
|
75
|
+
|
|
76
|
+
<reference_index>
|
|
77
|
+
Read these files when you need detailed schema knowledge during workflow authoring:
|
|
78
|
+
|
|
79
|
+
- `references/yaml-schema-v1.md` — Complete field-by-field V1 schema reference. Read when you need to explain any field's type, constraints, or defaults.
|
|
80
|
+
- `references/verification-policies.md` — All four verify policies with complete YAML examples. Read when helping the user choose or configure verification for a step.
|
|
81
|
+
- `references/feature-patterns.md` — Usage patterns for `context_from`, `iterate`, and `params` with complete YAML examples. Read when the user wants context chaining, fan-out iteration, or parameterized workflows.
|
|
82
|
+
</reference_index>
|
|
83
|
+
|
|
84
|
+
<templates_index>
|
|
85
|
+
Available templates in `templates/`:
|
|
86
|
+
|
|
87
|
+
- `workflow-definition.yaml` — Blank scaffold with all fields shown as comments. Copy and fill for a quick start.
|
|
88
|
+
- `blog-post-pipeline.yaml` — Linear chain with params and content-heuristic verification.
|
|
89
|
+
- `code-audit.yaml` — Iterate-based fan-out with shell-command verification.
|
|
90
|
+
- `release-checklist.yaml` — Diamond dependency graph with human-review verification.
|
|
91
|
+
</templates_index>
|
|
92
|
+
|
|
93
|
+
<output_conventions>
|
|
94
|
+
When assembling the final YAML:
|
|
95
|
+
|
|
96
|
+
1. Use 2-space indentation consistently.
|
|
97
|
+
2. Quote string values that contain special YAML characters (`:`, `{`, `}`, `[`, `]`, `#`).
|
|
98
|
+
3. Always include `version: 1` as the first field.
|
|
99
|
+
4. Order top-level fields: `version`, `name`, `description`, `params`, `steps`.
|
|
100
|
+
5. Order step fields: `id`, `name`, `prompt`, `requires`, `produces`, `context_from`, `verify`, `iterate`.
|
|
101
|
+
6. Write the file to `.gsd/workflow-defs/<name>.yaml`.
|
|
102
|
+
7. After writing, tell the user: "Run `/gsd workflow validate <name>` to check the definition."
|
|
103
|
+
</output_conventions>
|