gsd-pi 2.38.0-dev.96dc7fb → 2.38.0-dev.98b44dc

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 (217) hide show
  1. package/README.md +15 -11
  2. package/dist/app-paths.js +1 -1
  3. package/dist/extension-registry.js +2 -2
  4. package/dist/remote-questions-config.js +2 -2
  5. package/dist/resource-loader.js +34 -1
  6. package/dist/resources/extensions/browser-tools/index.js +3 -1
  7. package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
  8. package/dist/resources/extensions/env-utils.js +29 -0
  9. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  10. package/dist/resources/extensions/github-sync/cli.js +284 -0
  11. package/dist/resources/extensions/github-sync/index.js +73 -0
  12. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  13. package/dist/resources/extensions/github-sync/sync.js +424 -0
  14. package/dist/resources/extensions/github-sync/templates.js +118 -0
  15. package/dist/resources/extensions/github-sync/types.js +7 -0
  16. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
  18. package/dist/resources/extensions/gsd/auto-loop.js +636 -594
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +99 -70
  20. package/dist/resources/extensions/gsd/auto-prompts.js +202 -48
  21. package/dist/resources/extensions/gsd/auto-start.js +7 -1
  22. package/dist/resources/extensions/gsd/auto-worktree-sync.js +2 -1
  23. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  24. package/dist/resources/extensions/gsd/auto.js +143 -96
  25. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  26. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  27. package/dist/resources/extensions/gsd/commands.js +4 -2
  28. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  29. package/dist/resources/extensions/gsd/detection.js +1 -2
  30. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  31. package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
  32. package/dist/resources/extensions/gsd/doctor.js +20 -1
  33. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  34. package/dist/resources/extensions/gsd/export.js +1 -1
  35. package/dist/resources/extensions/gsd/files.js +48 -9
  36. package/dist/resources/extensions/gsd/forensics.js +1 -1
  37. package/dist/resources/extensions/gsd/git-service.js +30 -12
  38. package/dist/resources/extensions/gsd/gitignore.js +16 -3
  39. package/dist/resources/extensions/gsd/guided-flow.js +149 -38
  40. package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
  41. package/dist/resources/extensions/gsd/health-widget.js +3 -86
  42. package/dist/resources/extensions/gsd/index.js +24 -20
  43. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  44. package/dist/resources/extensions/gsd/migrate-external.js +18 -1
  45. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  46. package/dist/resources/extensions/gsd/paths.js +3 -0
  47. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  48. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  49. package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
  50. package/dist/resources/extensions/gsd/preferences.js +22 -11
  51. package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
  52. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  55. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
  56. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  59. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  67. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  68. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  69. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  71. package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
  72. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  73. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  74. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  75. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  76. package/dist/resources/extensions/gsd/state.js +42 -23
  77. package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
  78. package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
  79. package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
  80. package/dist/resources/extensions/mcp-client/index.js +14 -1
  81. package/dist/resources/extensions/remote-questions/status.js +4 -1
  82. package/dist/resources/extensions/remote-questions/store.js +4 -1
  83. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  84. package/dist/resources/extensions/shared/frontmatter.js +1 -1
  85. package/dist/resources/extensions/subagent/isolation.js +2 -1
  86. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  87. package/package.json +1 -1
  88. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  89. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  90. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  91. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  93. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
  95. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/skills.js +6 -1
  97. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  99. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/index.js +1 -1
  101. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  102. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  103. package/packages/pi-coding-agent/src/core/skills.ts +9 -1
  104. package/packages/pi-coding-agent/src/index.ts +1 -0
  105. package/src/resources/extensions/browser-tools/index.ts +3 -0
  106. package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
  107. package/src/resources/extensions/env-utils.ts +31 -0
  108. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  109. package/src/resources/extensions/github-sync/cli.ts +364 -0
  110. package/src/resources/extensions/github-sync/index.ts +93 -0
  111. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  112. package/src/resources/extensions/github-sync/sync.ts +556 -0
  113. package/src/resources/extensions/github-sync/templates.ts +183 -0
  114. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  115. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  116. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  117. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  118. package/src/resources/extensions/github-sync/types.ts +47 -0
  119. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  120. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
  121. package/src/resources/extensions/gsd/auto-loop.ts +526 -545
  122. package/src/resources/extensions/gsd/auto-post-unit.ts +80 -44
  123. package/src/resources/extensions/gsd/auto-prompts.ts +247 -50
  124. package/src/resources/extensions/gsd/auto-start.ts +11 -1
  125. package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -1
  126. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  127. package/src/resources/extensions/gsd/auto.ts +139 -101
  128. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  129. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  130. package/src/resources/extensions/gsd/commands.ts +5 -3
  131. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  132. package/src/resources/extensions/gsd/detection.ts +2 -2
  133. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  134. package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
  135. package/src/resources/extensions/gsd/doctor.ts +22 -1
  136. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  137. package/src/resources/extensions/gsd/export.ts +1 -1
  138. package/src/resources/extensions/gsd/files.ts +51 -11
  139. package/src/resources/extensions/gsd/forensics.ts +1 -1
  140. package/src/resources/extensions/gsd/git-service.ts +44 -10
  141. package/src/resources/extensions/gsd/gitignore.ts +17 -3
  142. package/src/resources/extensions/gsd/guided-flow.ts +177 -44
  143. package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
  144. package/src/resources/extensions/gsd/health-widget.ts +3 -89
  145. package/src/resources/extensions/gsd/index.ts +24 -17
  146. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  147. package/src/resources/extensions/gsd/migrate-external.ts +18 -1
  148. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  149. package/src/resources/extensions/gsd/paths.ts +4 -0
  150. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  151. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  152. package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
  153. package/src/resources/extensions/gsd/preferences.ts +25 -11
  154. package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
  155. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  156. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  157. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  158. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
  159. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  160. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  161. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  162. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  163. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  164. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  165. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  166. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  167. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  168. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  169. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  170. package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  171. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  172. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  174. package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
  175. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  176. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  177. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  178. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  179. package/src/resources/extensions/gsd/state.ts +39 -21
  180. package/src/resources/extensions/gsd/templates/runtime.md +21 -0
  181. package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
  182. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  183. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
  184. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
  185. package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
  186. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  187. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
  188. package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
  189. package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
  190. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
  191. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  192. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  193. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  194. package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
  195. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
  196. package/src/resources/extensions/gsd/types.ts +18 -1
  197. package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
  198. package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
  199. package/src/resources/extensions/mcp-client/index.ts +17 -1
  200. package/src/resources/extensions/remote-questions/status.ts +5 -1
  201. package/src/resources/extensions/remote-questions/store.ts +5 -1
  202. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  203. package/src/resources/extensions/shared/frontmatter.ts +1 -1
  204. package/src/resources/extensions/subagent/isolation.ts +3 -1
  205. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  206. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  207. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  208. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  209. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  210. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  211. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  212. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  213. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  214. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  215. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  216. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  217. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -10,7 +10,7 @@ All relevant context has been preloaded below. Start working immediately without
10
10
 
11
11
  {{inlinedContext}}
12
12
 
13
- If a `GSD Skill Preferences` block is present in system context, use it to decide which skills to load and follow during UAT execution, without relaxing required verification or artifact rules.
13
+ {{skillActivation}}
14
14
 
15
15
  ---
16
16
 
@@ -18,32 +18,49 @@ If a `GSD Skill Preferences` block is present in system context, use it to decid
18
18
 
19
19
  **UAT file:** `{{uatPath}}`
20
20
  **Result file to write:** `{{uatResultPath}}`
21
+ **Detected UAT mode:** `{{uatType}}`
21
22
 
22
- You are the test runner. Execute every check defined in `{{uatPath}}` directly:
23
+ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply as this mode truthfully allows. Do not collapse live or subjective checks into cheap artifact checks just to get a PASS.
24
+
25
+ ### Automation rules by mode
26
+
27
+ - `artifact-driven` — verify with shell commands, scripts, file reads, and artifact structure checks.
28
+ - `browser-executable` — use browser tools to navigate to the target URL and verify expected behavior. Capture screenshots as evidence. Record pass/fail with specific assertions.
29
+ - `runtime-executable` — execute the specified command or script. Capture stdout/stderr as evidence. Record pass/fail based on exit code and output.
30
+ - `live-runtime` — exercise the real runtime path. Start or connect to the app/service if needed, use browser/runtime/network checks, and verify observable behavior.
31
+ - `mixed` — run all automatable artifact-driven and live-runtime checks. Separate any remaining human-only checks explicitly.
32
+ - `human-experience` — automate setup, preconditions, screenshots, logs, and objective checks, but do **not** invent subjective PASS results. Mark taste-based, experiential, or purely human-judgment checks as `NEEDS-HUMAN` and use an overall verdict of `PARTIAL` unless every required check was objective and passed.
33
+
34
+ ### Evidence tools
35
+
36
+ Choose the lightest tool that proves the check honestly:
23
37
 
24
38
  - Run shell commands with `bash`
25
39
  - Run `grep` / `rg` checks against files
26
- - Run `node` / script invocations
40
+ - Run `node` / other script invocations
27
41
  - Read files and verify their contents
28
42
  - Check that expected artifacts exist and have correct structure
43
+ - For live/runtime/UI checks, exercise the real flow in the browser when applicable and inspect runtime/network/console state
44
+ - When a check cannot be honestly automated, gather the best objective evidence you can and mark it `NEEDS-HUMAN`
29
45
 
30
46
  For each check, record:
31
47
  - The check description (from the UAT file)
48
+ - The evidence mode used: `artifact`, `runtime`, or `human-follow-up`
32
49
  - The command or action taken
33
50
  - The actual result observed
34
- - PASS or FAIL verdict
51
+ - `PASS`, `FAIL`, or `NEEDS-HUMAN`
35
52
 
36
53
  After running all checks, compute the **overall verdict**:
37
- - `PASS` — all checks passed
54
+ - `PASS` — all required checks passed and no human-only checks remain
38
55
  - `FAIL` — one or more checks failed
39
- - `PARTIAL` — some checks passed, some failed or were skipped
56
+ - `PARTIAL` — some checks passed, but one or more checks were skipped, inconclusive, or still require human judgment
40
57
 
41
58
  Write `{{uatResultPath}}` with:
42
59
 
43
60
  ```markdown
44
61
  ---
45
62
  sliceId: {{sliceId}}
46
- uatType: artifact-driven
63
+ uatType: {{uatType}}
47
64
  verdict: PASS | FAIL | PARTIAL
48
65
  date: <ISO 8601 timestamp>
49
66
  ---
@@ -52,9 +69,9 @@ date: <ISO 8601 timestamp>
52
69
 
53
70
  ## Checks
54
71
 
55
- | Check | Result | Notes |
56
- |-------|--------|-------|
57
- | <check description> | PASS / FAIL | <observed output or reason> |
72
+ | Check | Mode | Result | Notes |
73
+ |-------|------|--------|-------|
74
+ | <check description> | artifact / runtime / human-follow-up | PASS / FAIL / NEEDS-HUMAN | <observed output, evidence, or reason> |
58
75
 
59
76
  ## Overall Verdict
60
77
 
@@ -62,7 +79,7 @@ date: <ISO 8601 timestamp>
62
79
 
63
80
  ## Notes
64
81
 
65
- <any additional context, errors encountered, or follow-up items>
82
+ <any additional context, errors encountered, screenshots/logs gathered, or manual follow-up still required>
66
83
  ```
67
84
 
68
85
  ---
@@ -14,7 +14,7 @@ You are executing a **{{templateName}}** workflow (template: `{{templateId}}`).
14
14
 
15
15
  ## Workflow Definition
16
16
 
17
- Follow the workflow defined below. Execute each phase in order, completing one before moving to the next. At each phase gate, confirm with the user before proceeding.
17
+ Follow the workflow defined below. Execute each phase in order, completing one before moving to the next. For low and medium complexity workflows, keep moving by default — pause only at true decision gates (user must choose between materially different directions, outward-facing actions need approval, or the workflow explicitly requires a human checkpoint). For high complexity workflows, confirm at phase transitions unless the workflow explicitly marks a gate as skip-safe.
18
18
 
19
19
  {{workflowContent}}
20
20
 
@@ -24,5 +24,5 @@ Follow the workflow defined below. Execute each phase in order, completing one b
24
24
  2. **Artifact discipline.** If an artifact directory is specified, write all planning/summary documents there.
25
25
  3. **Atomic commits.** Commit working code after each meaningful change. Use conventional commit format: `<type>(<scope>): <description>`.
26
26
  4. **Verify before shipping.** Run the project's test suite and build before marking the workflow complete.
27
- 5. **Gate between phases.** After each phase, summarize what was done and ask the user to confirm before moving to the next phase.
27
+ 5. **Decision gates, not ceremony.** After each phase, summarize what changed. For low/medium complexity, ask for confirmation only when the next phase depends on a real user choice or external approval. For high complexity, confirm before proceeding to each new phase.
28
28
  6. **Stay focused.** This is a {{complexity}}-complexity workflow. Match your ceremony level to the task — don't over-engineer or under-deliver.
@@ -12,6 +12,8 @@ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, s
12
12
  import { homedir } from "node:os";
13
13
  import { join, resolve } from "node:path";
14
14
 
15
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
16
+
15
17
  // ─── Repo Identity ──────────────────────────────────────────────────────────
16
18
 
17
19
  /**
@@ -90,14 +92,31 @@ function resolveGitRoot(basePath: string): string {
90
92
  }
91
93
  }
92
94
 
95
+ /**
96
+ * Validate a GSD_PROJECT_ID value.
97
+ *
98
+ * Must contain only alphanumeric characters, hyphens, and underscores.
99
+ * Call this once at startup so the user gets immediate feedback on bad values.
100
+ */
101
+ export function validateProjectId(id: string): boolean {
102
+ return /^[a-zA-Z0-9_-]+$/.test(id);
103
+ }
104
+
93
105
  /**
94
106
  * Compute a stable identity for a repository.
95
107
  *
96
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
97
- * Deterministic: same repo always produces the same hash regardless of
98
- * which worktree the caller is inside.
108
+ * If `GSD_PROJECT_ID` is set, returns it directly (validation is expected
109
+ * to have already happened at startup via `validateProjectId`).
110
+ *
111
+ * Otherwise returns SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated
112
+ * to 12 hex chars. Deterministic: same repo always produces the same hash
113
+ * regardless of which worktree the caller is inside.
99
114
  */
100
115
  export function repoIdentity(basePath: string): string {
116
+ const projectId = process.env.GSD_PROJECT_ID;
117
+ if (projectId) {
118
+ return projectId;
119
+ }
101
120
  const remoteUrl = getRemoteUrl(basePath);
102
121
  const root = resolveGitRoot(basePath);
103
122
  const input = `${remoteUrl}\n${root}`;
@@ -113,7 +132,7 @@ export function repoIdentity(basePath: string): string {
113
132
  * otherwise `~/.gsd/projects/<hash>`.
114
133
  */
115
134
  export function externalGsdRoot(basePath: string): string {
116
- const base = process.env.GSD_STATE_DIR || join(homedir(), ".gsd");
135
+ const base = process.env.GSD_STATE_DIR || gsdHome;
117
136
  return join(base, "projects", repoIdentity(basePath));
118
137
  }
119
138
 
@@ -11,6 +11,8 @@ import { join } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
  import { resolveProjectRoot } from "./worktree.js";
13
13
 
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
15
+
14
16
  // ─── Resource Staleness ───────────────────────────────────────────────────
15
17
 
16
18
  /**
@@ -23,7 +25,7 @@ function isManifestWithVersion(data: unknown): data is { gsdVersion: string } {
23
25
  }
24
26
 
25
27
  export function readResourceVersion(): string | null {
26
- const agentDir = process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent");
28
+ const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
27
29
  const manifestPath = join(agentDir, "managed-resources.json");
28
30
  const manifest = loadJsonFileOrNull(manifestPath, isManifestWithVersion);
29
31
  return manifest?.gsdVersion ?? null;
@@ -39,6 +39,35 @@ export function markSliceDoneInRoadmap(basePath: string, mid: string, sid: strin
39
39
  return true;
40
40
  }
41
41
 
42
+ /**
43
+ * Mark a slice as not done ([ ]) in the milestone roadmap.
44
+ * Idempotent — no-op if already unchecked or if the slice isn't found.
45
+ *
46
+ * @returns true if the roadmap was modified, false if no change was needed
47
+ */
48
+ export function markSliceUndoneInRoadmap(basePath: string, mid: string, sid: string): boolean {
49
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
50
+ if (!roadmapFile) return false;
51
+
52
+ let content: string;
53
+ try {
54
+ content = readFileSync(roadmapFile, "utf-8");
55
+ } catch {
56
+ return false;
57
+ }
58
+
59
+ const updated = content.replace(
60
+ new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sid}:`, "m"),
61
+ `$1[ ] **${sid}:`,
62
+ );
63
+
64
+ if (updated === content) return false;
65
+
66
+ atomicWriteSync(roadmapFile, updated);
67
+ clearParseCache();
68
+ return true;
69
+ }
70
+
42
71
  /**
43
72
  * Mark a task as done ([x]) in the slice plan.
44
73
  * Idempotent — no-op if already checked or if the task isn't found.
@@ -31,7 +31,7 @@ import {
31
31
  gsdRoot,
32
32
  } from './paths.js';
33
33
 
34
- import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
34
+ import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js';
35
35
  import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
36
36
 
37
37
  import { join, resolve } from 'path';
@@ -126,7 +126,12 @@ export async function getActiveMilestoneId(basePath: string): Promise<string | n
126
126
  // A draft milestone is still "active" — this function only determines which milestone is current.
127
127
  }
128
128
  const roadmap = parseRoadmap(content);
129
- if (!isMilestoneComplete(roadmap)) return mid;
129
+ if (!isMilestoneComplete(roadmap)) {
130
+ // Summary is the terminal artifact — if it exists, the milestone is
131
+ // complete even when roadmap checkboxes weren't ticked (#864).
132
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
133
+ if (!summaryFile) return mid;
134
+ }
130
135
  }
131
136
  return null;
132
137
  }
@@ -258,7 +263,13 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
258
263
  }
259
264
  const rmap = parseRoadmap(rc);
260
265
  roadmapCache.set(mid, rmap);
261
- if (!isMilestoneComplete(rmap)) continue;
266
+ if (!isMilestoneComplete(rmap)) {
267
+ // Summary is the terminal artifact — if it exists, the milestone is
268
+ // complete even when roadmap checkboxes weren't ticked (#864).
269
+ const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
270
+ if (sf) completeMilestoneIds.add(mid);
271
+ continue;
272
+ }
262
273
  const sf = resolveMilestoneFile(basePath, mid, "SUMMARY");
263
274
  if (sf) completeMilestoneIds.add(mid);
264
275
  }
@@ -357,26 +368,33 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
357
368
  } else {
358
369
  registry.push({ id: mid, title, status: 'complete' });
359
370
  }
360
- } else if (!activeMilestoneFound) {
361
- // Check milestone-level dependencies before promoting to active
362
- const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
363
- const contextContent = contextFile ? await cachedLoadFile(contextFile) : null;
364
- const deps = parseContextDependsOn(contextContent);
365
- const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
366
- if (depsUnmet) {
367
- registry.push({ id: mid, title, status: 'pending', dependsOn: deps });
368
- // Do NOT set activeMilestoneFound — let the loop continue to the next milestone
371
+ } else {
372
+ // Roadmap slices not all checked but if a summary exists, the milestone
373
+ // is still complete. The summary is the terminal artifact (#864).
374
+ const summaryFile = resolveMilestoneFile(basePath, mid, "SUMMARY");
375
+ if (summaryFile) {
376
+ registry.push({ id: mid, title, status: 'complete' });
377
+ } else if (!activeMilestoneFound) {
378
+ // Check milestone-level dependencies before promoting to active
379
+ const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
380
+ const contextContent = contextFile ? await cachedLoadFile(contextFile) : null;
381
+ const deps = parseContextDependsOn(contextContent);
382
+ const depsUnmet = deps.some(dep => !completeMilestoneIds.has(dep));
383
+ if (depsUnmet) {
384
+ registry.push({ id: mid, title, status: 'pending', dependsOn: deps });
385
+ // Do NOT set activeMilestoneFound — let the loop continue to the next milestone
386
+ } else {
387
+ activeMilestone = { id: mid, title };
388
+ activeRoadmap = roadmap;
389
+ activeMilestoneFound = true;
390
+ registry.push({ id: mid, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
391
+ }
369
392
  } else {
370
- activeMilestone = { id: mid, title };
371
- activeRoadmap = roadmap;
372
- activeMilestoneFound = true;
373
- registry.push({ id: mid, title, status: 'active', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
393
+ const contextFile2 = resolveMilestoneFile(basePath, mid, "CONTEXT");
394
+ const contextContent2 = contextFile2 ? await cachedLoadFile(contextFile2) : null;
395
+ const deps2 = parseContextDependsOn(contextContent2);
396
+ registry.push({ id: mid, title, status: 'pending', ...(deps2.length > 0 ? { dependsOn: deps2 } : {}) });
374
397
  }
375
- } else {
376
- const contextFile2 = resolveMilestoneFile(basePath, mid, "CONTEXT");
377
- const contextContent2 = contextFile2 ? await cachedLoadFile(contextFile2) : null;
378
- const deps2 = parseContextDependsOn(contextContent2);
379
- registry.push({ id: mid, title, status: 'pending', ...(deps2.length > 0 ? { dependsOn: deps2 } : {}) });
380
398
  }
381
399
  }
382
400
 
@@ -0,0 +1,21 @@
1
+ # Runtime Context
2
+
3
+ ## Stack
4
+ - **Language:** (e.g., TypeScript, Python, Go)
5
+ - **Framework:** (e.g., Next.js, FastAPI, Gin)
6
+ - **Build:** (e.g., npm run build, cargo build)
7
+ - **Test:** (e.g., npm run test, pytest)
8
+ - **Lint:** (e.g., npm run lint, ruff check)
9
+
10
+ ## Environment
11
+ - **Node version:** (e.g., 20.x)
12
+ - **Package manager:** (e.g., npm, pnpm, yarn)
13
+ - **Required env vars:** (list any needed for local dev)
14
+
15
+ ## Dev Server
16
+ - **Start command:** (e.g., npm run dev)
17
+ - **Default port:** (e.g., 3000)
18
+ - **Health check:** (e.g., curl http://localhost:3000/health)
19
+
20
+ ## Notes
21
+ (Any runtime-specific context the executor needs to know)
@@ -3,6 +3,9 @@
3
3
  # Tasks with 10+ estimated steps or 12+ estimated files trigger a warning to consider splitting.
4
4
  estimated_steps: {{estimatedSteps}}
5
5
  estimated_files: {{estimatedFiles}}
6
+ # Installed skills the planner expects the executor to load before coding.
7
+ skills_used:
8
+ - {{skillName}}
6
9
  ---
7
10
 
8
11
  # {{taskId}}: {{taskTitle}}
@@ -1,9 +1,9 @@
1
1
  /**
2
- * agent-end-retry.test.ts — Regression checks for the post-#1419 agent_end model.
2
+ * agent-end-retry.test.ts — Regression checks for the agent_end model.
3
3
  *
4
- * The old recursive handleAgentEnd retry path is gone. The loop now keeps
5
- * pendingResolve + pendingAgentEndQueue on AutoSession, and handleAgentEnd is
6
- * only a thin compatibility wrapper around resolveAgentEnd().
4
+ * The per-unit one-shot resolve function lives at module level in auto-loop.ts
5
+ * (_currentResolve). handleAgentEnd is a thin compatibility wrapper around
6
+ * resolveAgentEnd().
7
7
  */
8
8
 
9
9
  import test from "node:test";
@@ -14,40 +14,43 @@ import { fileURLToPath } from "node:url";
14
14
 
15
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
16
16
  const AUTO_TS_PATH = join(__dirname, "..", "auto.ts");
17
+ const AUTO_LOOP_TS_PATH = join(__dirname, "..", "auto-loop.ts");
17
18
  const SESSION_TS_PATH = join(__dirname, "..", "auto", "session.ts");
18
19
 
19
20
  function getAutoTsSource(): string {
20
21
  return readFileSync(AUTO_TS_PATH, "utf-8");
21
22
  }
22
23
 
24
+ function getAutoLoopTsSource(): string {
25
+ return readFileSync(AUTO_LOOP_TS_PATH, "utf-8");
26
+ }
27
+
23
28
  function getSessionTsSource(): string {
24
29
  return readFileSync(SESSION_TS_PATH, "utf-8");
25
30
  }
26
31
 
27
- test("AutoSession declares pending agent_end queue state", () => {
28
- const source = getSessionTsSource();
32
+ test("auto-loop.ts declares _currentResolve for per-unit one-shot promises", () => {
33
+ const source = getAutoLoopTsSource();
29
34
  assert.ok(
30
- source.includes("pendingResolve"),
31
- "AutoSession must declare pendingResolve for the in-flight unit promise",
35
+ source.includes("_currentResolve"),
36
+ "auto-loop.ts must declare _currentResolve for the per-unit resolve function",
32
37
  );
33
38
  assert.ok(
34
- source.includes("pendingAgentEndQueue"),
35
- "AutoSession must declare pendingAgentEndQueue for between-iteration agent_end events",
39
+ source.includes("_sessionSwitchInFlight"),
40
+ "auto-loop.ts must declare _sessionSwitchInFlight guard",
36
41
  );
37
42
  });
38
43
 
39
- test("AutoSession reset clears pending agent_end queue state", () => {
44
+ test("AutoSession no longer holds promise state (moved to auto-loop.ts module scope)", () => {
40
45
  const source = getSessionTsSource();
41
- const resetIdx = source.indexOf("reset(): void");
42
- assert.ok(resetIdx > -1, "AutoSession must have a reset() method");
43
- const resetBlock = source.slice(resetIdx, resetIdx + 4000);
46
+ // Properties should NOT exist as class fields
44
47
  assert.ok(
45
- resetBlock.includes("this.pendingResolve = null"),
46
- "reset() must clear pendingResolve",
48
+ !source.includes("pendingResolve:"),
49
+ "AutoSession must not declare pendingResolve (moved to auto-loop.ts)",
47
50
  );
48
51
  assert.ok(
49
- resetBlock.includes("this.pendingAgentEndQueue = []"),
50
- "reset() must clear pendingAgentEndQueue",
52
+ !source.includes("pendingAgentEndQueue:"),
53
+ "AutoSession must not declare pendingAgentEndQueue (removed — events are dropped)",
51
54
  );
52
55
  });
53
56