gsd-pi 2.41.0-dev.cac69f9 → 2.42.0-dev.1df898f

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.
Files changed (263) hide show
  1. package/README.md +23 -0
  2. package/dist/cli.js +18 -3
  3. package/dist/loader.js +3 -1
  4. package/dist/resource-loader.js +39 -6
  5. package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
  6. package/dist/resources/extensions/async-jobs/await-tool.js +5 -0
  7. package/dist/resources/extensions/async-jobs/index.js +2 -0
  8. package/dist/resources/extensions/gsd/auto/loop.js +80 -0
  9. package/dist/resources/extensions/gsd/auto/phases.js +3 -5
  10. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -0
  12. package/dist/resources/extensions/gsd/auto-prompts.js +3 -16
  13. package/dist/resources/extensions/gsd/auto-start.js +8 -11
  14. package/dist/resources/extensions/gsd/auto.js +28 -1
  15. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -5
  16. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +7 -2
  17. package/dist/resources/extensions/gsd/commands/catalog.js +32 -0
  18. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +146 -0
  19. package/dist/resources/extensions/gsd/context-injector.js +74 -0
  20. package/dist/resources/extensions/gsd/custom-execution-policy.js +47 -0
  21. package/dist/resources/extensions/gsd/custom-verification.js +145 -0
  22. package/dist/resources/extensions/gsd/custom-workflow-engine.js +164 -0
  23. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -0
  24. package/dist/resources/extensions/gsd/definition-loader.js +352 -0
  25. package/dist/resources/extensions/gsd/detection.js +19 -0
  26. package/dist/resources/extensions/gsd/dev-execution-policy.js +24 -0
  27. package/dist/resources/extensions/gsd/dev-workflow-engine.js +82 -0
  28. package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
  29. package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
  30. package/dist/resources/extensions/gsd/engine-resolver.js +40 -0
  31. package/dist/resources/extensions/gsd/engine-types.js +8 -0
  32. package/dist/resources/extensions/gsd/execution-policy.js +8 -0
  33. package/dist/resources/extensions/gsd/forensics.js +84 -0
  34. package/dist/resources/extensions/gsd/git-constants.js +1 -0
  35. package/dist/resources/extensions/gsd/git-service.js +1 -1
  36. package/dist/resources/extensions/gsd/graph.js +225 -0
  37. package/dist/resources/extensions/gsd/native-git-bridge.js +1 -0
  38. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  39. package/dist/resources/extensions/gsd/preferences.js +59 -8
  40. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  41. package/dist/resources/extensions/gsd/repo-identity.js +46 -5
  42. package/dist/resources/extensions/gsd/run-manager.js +134 -0
  43. package/dist/resources/extensions/gsd/service-tier.js +13 -4
  44. package/dist/resources/extensions/gsd/session-lock.js +2 -2
  45. package/dist/resources/extensions/gsd/workflow-engine.js +7 -0
  46. package/dist/resources/extensions/gsd/worktree-resolver.js +2 -2
  47. package/dist/resources/extensions/gsd/worktree.js +2 -2
  48. package/dist/resources/extensions/mcp-client/index.js +2 -1
  49. package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
  50. package/dist/resources/skills/create-workflow/SKILL.md +103 -0
  51. package/dist/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  52. package/dist/resources/skills/create-workflow/references/verification-policies.md +76 -0
  53. package/dist/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  54. package/dist/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  55. package/dist/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  56. package/dist/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  57. package/dist/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  58. package/dist/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  59. package/dist/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  60. package/dist/web/standalone/.next/BUILD_ID +1 -1
  61. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  62. package/dist/web/standalone/.next/build-manifest.json +2 -2
  63. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  64. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  65. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/index.html +1 -1
  82. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  89. package/dist/web/standalone/.next/server/chunks/229.js +2 -2
  90. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  91. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  92. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  93. package/dist/web-mode.d.ts +2 -0
  94. package/dist/web-mode.js +40 -4
  95. package/package.json +1 -1
  96. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  97. package/packages/pi-agent-core/dist/agent.js +2 -0
  98. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  99. package/packages/pi-agent-core/dist/types.d.ts +6 -0
  100. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  101. package/packages/pi-agent-core/dist/types.js.map +1 -1
  102. package/packages/pi-agent-core/src/agent.test.ts +53 -0
  103. package/packages/pi-agent-core/src/agent.ts +3 -0
  104. package/packages/pi-agent-core/src/types.ts +6 -0
  105. package/packages/pi-agent-core/tsconfig.json +1 -1
  106. package/packages/pi-ai/dist/models.d.ts +5 -3
  107. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  108. package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
  109. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  110. package/packages/pi-ai/dist/models.generated.js +1135 -1588
  111. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  112. package/packages/pi-ai/dist/models.js.map +1 -1
  113. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  114. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
  115. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  116. package/packages/pi-ai/scripts/generate-models.ts +1543 -0
  117. package/packages/pi-ai/src/models.generated.ts +1140 -1593
  118. package/packages/pi-ai/src/models.ts +7 -4
  119. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
  120. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
  122. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
  124. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
  126. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
  128. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/extensions/loader.js +18 -0
  131. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
  134. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
  137. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
  139. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
  141. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
  143. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
  145. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
  148. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -10
  157. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  158. package/packages/pi-coding-agent/package.json +1 -1
  159. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
  160. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
  161. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
  162. package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
  163. package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
  164. package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
  165. package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
  166. package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
  167. package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
  168. package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
  169. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
  170. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -11
  171. package/pkg/package.json +1 -1
  172. package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
  173. package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
  174. package/src/resources/extensions/async-jobs/await-tool.test.ts +47 -0
  175. package/src/resources/extensions/async-jobs/await-tool.ts +5 -0
  176. package/src/resources/extensions/async-jobs/index.ts +1 -0
  177. package/src/resources/extensions/async-jobs/job-manager.ts +2 -0
  178. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -1
  179. package/src/resources/extensions/gsd/auto/loop.ts +91 -0
  180. package/src/resources/extensions/gsd/auto/phases.ts +3 -5
  181. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  182. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -0
  183. package/src/resources/extensions/gsd/auto-prompts.ts +2 -18
  184. package/src/resources/extensions/gsd/auto-start.ts +7 -10
  185. package/src/resources/extensions/gsd/auto.ts +31 -1
  186. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -5
  187. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +9 -2
  188. package/src/resources/extensions/gsd/commands/catalog.ts +32 -0
  189. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +164 -0
  190. package/src/resources/extensions/gsd/context-injector.ts +100 -0
  191. package/src/resources/extensions/gsd/custom-execution-policy.ts +73 -0
  192. package/src/resources/extensions/gsd/custom-verification.ts +180 -0
  193. package/src/resources/extensions/gsd/custom-workflow-engine.ts +216 -0
  194. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -0
  195. package/src/resources/extensions/gsd/definition-loader.ts +462 -0
  196. package/src/resources/extensions/gsd/detection.ts +19 -0
  197. package/src/resources/extensions/gsd/dev-execution-policy.ts +51 -0
  198. package/src/resources/extensions/gsd/dev-workflow-engine.ts +110 -0
  199. package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
  200. package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
  201. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  202. package/src/resources/extensions/gsd/engine-resolver.ts +57 -0
  203. package/src/resources/extensions/gsd/engine-types.ts +71 -0
  204. package/src/resources/extensions/gsd/execution-policy.ts +43 -0
  205. package/src/resources/extensions/gsd/forensics.ts +92 -0
  206. package/src/resources/extensions/gsd/git-constants.ts +1 -0
  207. package/src/resources/extensions/gsd/git-service.ts +0 -1
  208. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  209. package/src/resources/extensions/gsd/graph.ts +312 -0
  210. package/src/resources/extensions/gsd/native-git-bridge.ts +1 -0
  211. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  212. package/src/resources/extensions/gsd/preferences.ts +62 -6
  213. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  214. package/src/resources/extensions/gsd/repo-identity.ts +48 -5
  215. package/src/resources/extensions/gsd/run-manager.ts +180 -0
  216. package/src/resources/extensions/gsd/service-tier.ts +17 -4
  217. package/src/resources/extensions/gsd/session-lock.ts +2 -2
  218. package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
  219. package/src/resources/extensions/gsd/tests/bundled-workflow-defs.test.ts +180 -0
  220. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +283 -0
  221. package/src/resources/extensions/gsd/tests/context-injector.test.ts +313 -0
  222. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +540 -0
  223. package/src/resources/extensions/gsd/tests/custom-verification.test.ts +382 -0
  224. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +339 -0
  225. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +87 -0
  226. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +778 -0
  227. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +318 -0
  228. package/src/resources/extensions/gsd/tests/e2e-workflow-pipeline-integration.test.ts +476 -0
  229. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +271 -0
  230. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
  231. package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
  233. package/src/resources/extensions/gsd/tests/git-service.test.ts +44 -0
  234. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +599 -0
  235. package/src/resources/extensions/gsd/tests/iterate-engine-integration.test.ts +429 -0
  236. package/src/resources/extensions/gsd/tests/journal.test.ts +82 -127
  237. package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
  238. package/src/resources/extensions/gsd/tests/run-manager.test.ts +229 -0
  239. package/src/resources/extensions/gsd/tests/service-tier.test.ts +30 -1
  240. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +56 -3
  241. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
  242. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +45 -0
  243. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
  244. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -78
  245. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +81 -74
  246. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +1 -2
  247. package/src/resources/extensions/gsd/workflow-engine.ts +38 -0
  248. package/src/resources/extensions/gsd/worktree-resolver.ts +2 -3
  249. package/src/resources/extensions/gsd/worktree.ts +2 -2
  250. package/src/resources/extensions/mcp-client/index.ts +5 -1
  251. package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
  252. package/src/resources/skills/create-workflow/SKILL.md +103 -0
  253. package/src/resources/skills/create-workflow/references/feature-patterns.md +128 -0
  254. package/src/resources/skills/create-workflow/references/verification-policies.md +76 -0
  255. package/src/resources/skills/create-workflow/references/yaml-schema-v1.md +46 -0
  256. package/src/resources/skills/create-workflow/templates/blog-post-pipeline.yaml +60 -0
  257. package/src/resources/skills/create-workflow/templates/code-audit.yaml +60 -0
  258. package/src/resources/skills/create-workflow/templates/release-checklist.yaml +66 -0
  259. package/src/resources/skills/create-workflow/templates/workflow-definition.yaml +32 -0
  260. package/src/resources/skills/create-workflow/workflows/create-from-scratch.md +104 -0
  261. package/src/resources/skills/create-workflow/workflows/create-from-template.md +72 -0
  262. /package/dist/web/standalone/.next/static/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_buildManifest.js +0 -0
  263. /package/dist/web/standalone/.next/static/{EnGUNqHeGbE0tuuUkTJVA → qw8qDHXOTLUXBq1vEknSz}/_ssgManifest.js +0 -0
@@ -7,7 +7,7 @@
7
7
  * rather than hard-coding package.json / src/ only.
8
8
  */
9
9
 
10
- import test from "node:test";
10
+ import { describe, test, beforeEach, afterEach } from "node:test";
11
11
  import assert from "node:assert/strict";
12
12
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
13
13
  import { join } from "node:path";
@@ -67,112 +67,69 @@ test("PROJECT_FILES is exported and contains expected multi-ecosystem entries",
67
67
  assert.ok(PROJECT_FILES.includes("Package.swift"), "includes Swift marker");
68
68
  });
69
69
 
70
- test("health check passes for Rust project (Cargo.toml, no package.json)", () => {
71
- const dir = createGitRepo();
72
- try {
70
+ describe("health check with git repo", () => {
71
+ let dir: string;
72
+ beforeEach(() => { dir = createGitRepo(); });
73
+ afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
74
+
75
+ test("health check passes for Rust project (Cargo.toml, no package.json)", () => {
73
76
  writeFileSync(join(dir, "Cargo.toml"), "[package]\nname = \"test\"\n");
74
77
  mkdirSync(join(dir, "crates"), { recursive: true });
75
78
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Rust project should pass health check");
76
- } finally {
77
- rmSync(dir, { recursive: true, force: true });
78
- }
79
- });
79
+ });
80
80
 
81
- test("health check passes for Go project (go.mod, no package.json)", () => {
82
- const dir = createGitRepo();
83
- try {
81
+ test("health check passes for Go project (go.mod, no package.json)", () => {
84
82
  writeFileSync(join(dir, "go.mod"), "module example.com/test\n\ngo 1.21\n");
85
83
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Go project should pass health check");
86
- } finally {
87
- rmSync(dir, { recursive: true, force: true });
88
- }
89
- });
84
+ });
90
85
 
91
- test("health check passes for Python project (pyproject.toml, no package.json)", () => {
92
- const dir = createGitRepo();
93
- try {
86
+ test("health check passes for Python project (pyproject.toml, no package.json)", () => {
94
87
  writeFileSync(join(dir, "pyproject.toml"), "[project]\nname = \"test\"\n");
95
88
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Python project should pass health check");
96
- } finally {
97
- rmSync(dir, { recursive: true, force: true });
98
- }
99
- });
89
+ });
100
90
 
101
- test("health check passes for Java project (pom.xml, no package.json)", () => {
102
- const dir = createGitRepo();
103
- try {
91
+ test("health check passes for Java project (pom.xml, no package.json)", () => {
104
92
  writeFileSync(join(dir, "pom.xml"), "<project></project>\n");
105
93
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Java project should pass health check");
106
- } finally {
107
- rmSync(dir, { recursive: true, force: true });
108
- }
109
- });
94
+ });
110
95
 
111
- test("health check passes for Swift project (Package.swift, no package.json)", () => {
112
- const dir = createGitRepo();
113
- try {
96
+ test("health check passes for Swift project (Package.swift, no package.json)", () => {
114
97
  writeFileSync(join(dir, "Package.swift"), "// swift-tools-version:5.7\n");
115
98
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Swift project should pass health check");
116
- } finally {
117
- rmSync(dir, { recursive: true, force: true });
118
- }
119
- });
99
+ });
120
100
 
121
- test("health check passes for C/C++ project (CMakeLists.txt, no package.json)", () => {
122
- const dir = createGitRepo();
123
- try {
101
+ test("health check passes for C/C++ project (CMakeLists.txt, no package.json)", () => {
124
102
  writeFileSync(join(dir, "CMakeLists.txt"), "cmake_minimum_required(VERSION 3.20)\n");
125
103
  assert.ok(wouldPassHealthCheck(dir, existsSync), "C/C++ project should pass health check");
126
- } finally {
127
- rmSync(dir, { recursive: true, force: true });
128
- }
129
- });
104
+ });
130
105
 
131
- test("health check passes for Elixir project (mix.exs, no package.json)", () => {
132
- const dir = createGitRepo();
133
- try {
106
+ test("health check passes for Elixir project (mix.exs, no package.json)", () => {
134
107
  writeFileSync(join(dir, "mix.exs"), "defmodule Test.MixProject do\nend\n");
135
108
  assert.ok(wouldPassHealthCheck(dir, existsSync), "Elixir project should pass health check");
136
- } finally {
137
- rmSync(dir, { recursive: true, force: true });
138
- }
139
- });
109
+ });
140
110
 
141
- test("health check passes for JS project (package.json, backward compat)", () => {
142
- const dir = createGitRepo();
143
- try {
111
+ test("health check passes for JS project (package.json, backward compat)", () => {
144
112
  writeFileSync(join(dir, "package.json"), '{"name":"test"}\n');
145
113
  assert.ok(wouldPassHealthCheck(dir, existsSync), "JS project should pass health check");
146
- } finally {
147
- rmSync(dir, { recursive: true, force: true });
148
- }
149
- });
114
+ });
150
115
 
151
- test("health check passes for src/-only project (backward compat)", () => {
152
- const dir = createGitRepo();
153
- try {
116
+ test("health check passes for src/-only project (backward compat)", () => {
154
117
  mkdirSync(join(dir, "src"), { recursive: true });
155
118
  assert.ok(wouldPassHealthCheck(dir, existsSync), "src/-only project should pass health check");
156
- } finally {
157
- rmSync(dir, { recursive: true, force: true });
158
- }
119
+ });
120
+
121
+ test("health check fails for empty git repo with no project files", () => {
122
+ assert.ok(!wouldPassHealthCheck(dir, existsSync), "empty git repo should fail health check");
123
+ });
159
124
  });
160
125
 
161
- test("health check fails for directory with no .git", () => {
162
- const dir = mkdtempSync(join(tmpdir(), "wt-dispatch-test-nogit-"));
163
- try {
126
+ describe("health check without git repo", () => {
127
+ let dir: string;
128
+ beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "wt-dispatch-test-nogit-")); });
129
+ afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
130
+
131
+ test("health check fails for directory with no .git", () => {
164
132
  writeFileSync(join(dir, "Cargo.toml"), "[package]\nname = \"test\"\n");
165
133
  assert.ok(!wouldPassHealthCheck(dir, existsSync), "no-git directory should fail health check");
166
- } finally {
167
- rmSync(dir, { recursive: true, force: true });
168
- }
169
- });
170
-
171
- test("health check fails for empty git repo with no project files", () => {
172
- const dir = createGitRepo();
173
- try {
174
- assert.ok(!wouldPassHealthCheck(dir, existsSync), "empty git repo should fail health check");
175
- } finally {
176
- rmSync(dir, { recursive: true, force: true });
177
- }
134
+ });
178
135
  });
@@ -1,4 +1,4 @@
1
- import test from "node:test";
1
+ import { describe, test, beforeEach, afterEach } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -73,9 +73,12 @@ test("worktreeBranchName formats branch name", () => {
73
73
 
74
74
  // ─── createWorktree ───────────────────────────────────────────────────────────
75
75
 
76
- test("createWorktree creates worktree with correct metadata", () => {
77
- const base = makeBaseRepo();
78
- try {
76
+ describe("createWorktree", () => {
77
+ let base: string;
78
+ beforeEach(() => { base = makeBaseRepo(); });
79
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
80
+
81
+ test("creates worktree with correct metadata", () => {
79
82
  const info = createWorktree(base, "feature-x");
80
83
  assert.strictEqual(info.name, "feature-x", "name should match");
81
84
  assert.strictEqual(info.branch, "worktree/feature-x", "branch should be prefixed");
@@ -88,80 +91,82 @@ test("createWorktree creates worktree with correct metadata", () => {
88
91
  );
89
92
  const branches = run("git branch", base);
90
93
  assert.ok(branches.includes("worktree/feature-x"), "branch should be created in base repo");
91
- } finally {
92
- rmSync(base, { recursive: true, force: true });
93
- }
94
- });
94
+ });
95
95
 
96
- test("createWorktree rejects duplicate name", () => {
97
- const { base } = makeRepoWithWorktree("feature-x");
98
- try {
96
+ test("rejects invalid name", () => {
99
97
  assert.throws(
100
- () => createWorktree(base, "feature-x"),
98
+ () => createWorktree(base, "bad name!"),
101
99
  (err: Error) => {
102
100
  assert.ok(
103
- err.message.includes("already exists"),
104
- `expected "already exists" in error, got: ${err.message}`,
101
+ err.message.includes("Invalid worktree name"),
102
+ `expected "Invalid worktree name" in error, got: ${err.message}`,
105
103
  );
106
104
  return true;
107
105
  },
108
- "should throw on duplicate worktree name",
106
+ "should throw on invalid worktree name",
109
107
  );
110
- } finally {
111
- rmSync(base, { recursive: true, force: true });
112
- }
108
+ });
113
109
  });
114
110
 
115
- test("createWorktree rejects invalid name", () => {
116
- const base = makeBaseRepo();
117
- try {
111
+ describe("createWorktree duplicate rejection", () => {
112
+ let base: string;
113
+ beforeEach(() => {
114
+ const repo = makeRepoWithWorktree("feature-x");
115
+ base = repo.base;
116
+ });
117
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
118
+
119
+ test("rejects duplicate name", () => {
118
120
  assert.throws(
119
- () => createWorktree(base, "bad name!"),
121
+ () => createWorktree(base, "feature-x"),
120
122
  (err: Error) => {
121
123
  assert.ok(
122
- err.message.includes("Invalid worktree name"),
123
- `expected "Invalid worktree name" in error, got: ${err.message}`,
124
+ err.message.includes("already exists"),
125
+ `expected "already exists" in error, got: ${err.message}`,
124
126
  );
125
127
  return true;
126
128
  },
127
- "should throw on invalid worktree name",
129
+ "should throw on duplicate worktree name",
128
130
  );
129
- } finally {
130
- rmSync(base, { recursive: true, force: true });
131
- }
131
+ });
132
132
  });
133
133
 
134
134
  // ─── listWorktrees ────────────────────────────────────────────────────────────
135
135
 
136
- test("listWorktrees returns active worktrees", () => {
137
- const { base } = makeRepoWithWorktree("feature-x");
138
- try {
136
+ describe("listWorktrees", () => {
137
+ let base: string;
138
+ beforeEach(() => {
139
+ const repo = makeRepoWithWorktree("feature-x");
140
+ base = repo.base;
141
+ });
142
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
143
+
144
+ test("returns active worktrees", () => {
139
145
  const list = listWorktrees(base);
140
146
  assert.strictEqual(list.length, 1, "should list exactly one worktree");
141
147
  assert.strictEqual(list[0]!.name, "feature-x", "name should match");
142
148
  assert.strictEqual(list[0]!.branch, "worktree/feature-x", "branch should match");
143
149
  assert.ok(list[0]!.exists, "exists flag should be true");
144
- } finally {
145
- rmSync(base, { recursive: true, force: true });
146
- }
147
- });
150
+ });
148
151
 
149
- test("listWorktrees returns empty after removal", () => {
150
- const { base } = makeRepoWithWorktree("feature-x");
151
- try {
152
+ test("returns empty after removal", () => {
152
153
  removeWorktree(base, "feature-x");
153
154
  const list = listWorktrees(base);
154
155
  assert.strictEqual(list.length, 0, "should have no worktrees after removal");
155
- } finally {
156
- rmSync(base, { recursive: true, force: true });
157
- }
156
+ });
158
157
  });
159
158
 
160
159
  // ─── diffWorktreeGSD ─────────────────────────────────────────────────────────
161
160
 
162
- test("diffWorktreeGSD detects added and modified GSD files", () => {
163
- const { base } = makeRepoWithChanges("feature-x");
164
- try {
161
+ describe("diffWorktreeGSD and getWorktreeGSDDiff", () => {
162
+ let base: string;
163
+ beforeEach(() => {
164
+ const repo = makeRepoWithChanges("feature-x");
165
+ base = repo.base;
166
+ });
167
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
168
+
169
+ test("detects added and modified GSD files", () => {
165
170
  const diff = diffWorktreeGSD(base, "feature-x");
166
171
  assert.ok(diff.added.length > 0, "should have added files");
167
172
  assert.ok(
@@ -174,58 +179,60 @@ test("diffWorktreeGSD detects added and modified GSD files", () => {
174
179
  "M001 roadmap should be in modified files",
175
180
  );
176
181
  assert.strictEqual(diff.removed.length, 0, "should have no removed files");
177
- } finally {
178
- rmSync(base, { recursive: true, force: true });
179
- }
180
- });
181
-
182
- // ─── getWorktreeGSDDiff ───────────────────────────────────────────────────────
182
+ });
183
183
 
184
- test("getWorktreeGSDDiff returns patch content", () => {
185
- const { base } = makeRepoWithChanges("feature-x");
186
- try {
184
+ test("returns patch content", () => {
187
185
  const fullDiff = getWorktreeGSDDiff(base, "feature-x");
188
186
  assert.ok(fullDiff.includes("M002"), "diff should mention M002");
189
187
  assert.ok(fullDiff.includes("updated"), "diff should mention the update");
190
- } finally {
191
- rmSync(base, { recursive: true, force: true });
192
- }
188
+ });
193
189
  });
194
190
 
195
191
  // ─── getWorktreeLog ───────────────────────────────────────────────────────────
196
192
 
197
- test("getWorktreeLog shows commits", () => {
198
- const { base } = makeRepoWithChanges("feature-x");
199
- try {
193
+ describe("getWorktreeLog", () => {
194
+ let base: string;
195
+ beforeEach(() => {
196
+ const repo = makeRepoWithChanges("feature-x");
197
+ base = repo.base;
198
+ });
199
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
200
+
201
+ test("shows commits", () => {
200
202
  const log = getWorktreeLog(base, "feature-x");
201
203
  assert.ok(log.includes("add M002"), "log should include the commit message");
202
- } finally {
203
- rmSync(base, { recursive: true, force: true });
204
- }
204
+ });
205
205
  });
206
206
 
207
207
  // ─── removeWorktree ───────────────────────────────────────────────────────────
208
208
 
209
- test("removeWorktree removes directory and branch", () => {
210
- const { base, wtPath } = makeRepoWithWorktree("feature-x");
211
- try {
209
+ describe("removeWorktree", () => {
210
+ let base: string;
211
+ let wtPath: string;
212
+ beforeEach(() => {
213
+ const repo = makeRepoWithWorktree("feature-x");
214
+ base = repo.base;
215
+ wtPath = repo.wtPath;
216
+ });
217
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
218
+
219
+ test("removes directory and branch", () => {
212
220
  removeWorktree(base, "feature-x", { deleteBranch: true });
213
221
  assert.ok(!existsSync(wtPath), "worktree directory should be gone");
214
222
  const branches = run("git branch", base);
215
223
  assert.ok(!branches.includes("worktree/feature-x"), "branch should be deleted");
216
- } finally {
217
- rmSync(base, { recursive: true, force: true });
218
- }
224
+ });
219
225
  });
220
226
 
221
- test("removeWorktree on missing worktree does not throw", () => {
222
- const base = makeBaseRepo();
223
- try {
227
+ describe("removeWorktree missing worktree", () => {
228
+ let base: string;
229
+ beforeEach(() => { base = makeBaseRepo(); });
230
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
231
+
232
+ test("on missing worktree does not throw", () => {
224
233
  assert.doesNotThrow(
225
234
  () => removeWorktree(base, "nonexistent"),
226
235
  "should not throw when worktree does not exist",
227
236
  );
228
- } finally {
229
- rmSync(base, { recursive: true, force: true });
230
- }
237
+ });
231
238
  });
@@ -139,11 +139,10 @@ function makeDeps(
139
139
  captureIntegrationBranch: (
140
140
  basePath: string,
141
141
  mid: string | undefined,
142
- opts?: { commitDocs?: boolean },
143
142
  ) => {
144
143
  calls.push({
145
144
  fn: "captureIntegrationBranch",
146
- args: [basePath, mid, opts],
145
+ args: [basePath, mid],
147
146
  });
148
147
  },
149
148
  ...overrides,
@@ -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
+ }
@@ -63,7 +63,6 @@ export interface WorktreeResolverDeps {
63
63
  captureIntegrationBranch: (
64
64
  basePath: string,
65
65
  mid: string,
66
- opts?: { commitDocs?: boolean },
67
66
  ) => void;
68
67
  }
69
68
 
@@ -410,10 +409,10 @@ export class WorktreeResolver {
410
409
  });
411
410
  // Surface a clear, actionable error. The worktree and milestone branch are
412
411
  // intentionally preserved — nothing has been deleted. The user can retry
413
- // /complete-milestone or merge manually once the underlying issue is fixed
412
+ // /gsd dispatch complete-milestone or merge manually once the underlying issue is fixed
414
413
  // (e.g. checkout to wrong branch, unresolved conflicts). (#1668)
415
414
  ctx.notify(
416
- `Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /complete-milestone or merge manually.`,
415
+ `Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /gsd dispatch complete-milestone or merge manually.`,
417
416
  "warning",
418
417
  );
419
418
 
@@ -57,13 +57,13 @@ export function setActiveMilestoneId(basePath: string, milestoneId: string | nul
57
57
  * record when the user starts from a different branch (#300). Always a no-op
58
58
  * if on a GSD slice branch.
59
59
  */
60
- export function captureIntegrationBranch(basePath: string, milestoneId: string, options?: { commitDocs?: boolean }): void {
60
+ export function captureIntegrationBranch(basePath: string, milestoneId: string): void {
61
61
  // In a worktree, the base branch is implicit (worktree/<name>).
62
62
  // Writing it to META.json would leave stale metadata after merge back to main.
63
63
  if (detectWorktreeName(basePath)) return;
64
64
  const svc = getService(basePath);
65
65
  const current = svc.getCurrentBranch();
66
- writeIntegrationBranch(basePath, milestoneId, current, options);
66
+ writeIntegrationBranch(basePath, milestoneId, current);
67
67
  }
68
68
 
69
69
  // ─── Pure Utility Functions (unchanged) ────────────────────────────────────
@@ -149,7 +149,11 @@ async function getOrConnect(name: string, signal?: AbortSignal): Promise<Client>
149
149
  stderr: "pipe",
150
150
  });
151
151
  } else if (config.transport === "http" && config.url) {
152
- transport = new StreamableHTTPClientTransport(new URL(config.url));
152
+ const resolvedUrl = config.url.replace(
153
+ /\$\{([^}]+)\}/g,
154
+ (_, name) => process.env[name] ?? "",
155
+ );
156
+ transport = new StreamableHTTPClientTransport(new URL(resolvedUrl));
153
157
  } else {
154
158
  throw new Error(`Server "${name}" has unsupported transport: ${config.transport}`);
155
159
  }
@@ -398,16 +398,16 @@ export function registerSearchTool(pi: ExtensionAPI) {
398
398
  // with brief interruptions every MAX_CONSECUTIVE_DUPES+1 calls.
399
399
  if (cacheKey === lastSearchKey) {
400
400
  consecutiveDupeCount++;
401
- if (consecutiveDupeCount >= MAX_CONSECUTIVE_DUPES) {
401
+ if (consecutiveDupeCount > MAX_CONSECUTIVE_DUPES) {
402
402
  return {
403
- content: [{ type: "text" as const, text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount + 1} times consecutively with identical results. The information you need is already in the previous search results above. Stop searching and use those results to proceed with your task.` }],
403
+ content: [{ type: "text" as const, text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount} times consecutively with identical results. The information you need is already in the previous search results above. Stop searching and use those results to proceed with your task.` }],
404
404
  isError: true,
405
405
  details: { errorKind: "search_loop", error: "Consecutive duplicate search detected" } satisfies Partial<SearchDetails>,
406
406
  };
407
407
  }
408
408
  } else {
409
409
  lastSearchKey = cacheKey;
410
- consecutiveDupeCount = 0;
410
+ consecutiveDupeCount = 1;
411
411
  }
412
412
 
413
413
  const cached = searchCache.get(cacheKey);
@@ -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>