gsd-pi 2.8.1 → 2.8.2

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 (83) hide show
  1. package/dist/loader.js +5 -0
  2. package/node_modules/@gsd/pi-coding-agent/dist/core/bash-executor.js +2 -2
  3. package/node_modules/@gsd/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  4. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js +2 -2
  5. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  6. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.d.ts +1 -1
  7. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  8. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.js +13 -2
  9. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  10. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.d.ts +2 -0
  11. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.d.ts.map +1 -0
  12. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.js +57 -0
  13. package/node_modules/@gsd/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -0
  14. package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts +1 -1
  15. package/node_modules/@gsd/pi-coding-agent/dist/index.d.ts.map +1 -1
  16. package/node_modules/@gsd/pi-coding-agent/dist/index.js +1 -1
  17. package/node_modules/@gsd/pi-coding-agent/dist/index.js.map +1 -1
  18. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  19. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -5
  20. package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  21. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.d.ts +7 -0
  22. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  23. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.js +11 -0
  24. package/node_modules/@gsd/pi-coding-agent/dist/utils/shell.js.map +1 -1
  25. package/node_modules/@gsd/pi-coding-agent/src/core/bash-executor.ts +2 -2
  26. package/node_modules/@gsd/pi-coding-agent/src/core/tools/bash.ts +2 -2
  27. package/node_modules/@gsd/pi-coding-agent/src/core/tools/path-utils.test.ts +66 -0
  28. package/node_modules/@gsd/pi-coding-agent/src/core/tools/path-utils.ts +14 -2
  29. package/node_modules/@gsd/pi-coding-agent/src/index.ts +1 -1
  30. package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +6 -4
  31. package/node_modules/@gsd/pi-coding-agent/src/utils/shell.ts +11 -0
  32. package/package.json +1 -1
  33. package/packages/pi-coding-agent/dist/core/bash-executor.js +2 -2
  34. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  35. package/packages/pi-coding-agent/dist/core/tools/bash.js +2 -2
  36. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  37. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts +1 -1
  38. package/packages/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  39. package/packages/pi-coding-agent/dist/core/tools/path-utils.js +13 -2
  40. package/packages/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  41. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.d.ts +2 -0
  42. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.d.ts.map +1 -0
  43. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js +57 -0
  44. package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -0
  45. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  46. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  47. package/packages/pi-coding-agent/dist/index.js +1 -1
  48. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  49. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  50. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -5
  51. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  52. package/packages/pi-coding-agent/dist/utils/shell.d.ts +7 -0
  53. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  54. package/packages/pi-coding-agent/dist/utils/shell.js +11 -0
  55. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  56. package/packages/pi-coding-agent/src/core/bash-executor.ts +2 -2
  57. package/packages/pi-coding-agent/src/core/tools/bash.ts +2 -2
  58. package/packages/pi-coding-agent/src/core/tools/path-utils.test.ts +66 -0
  59. package/packages/pi-coding-agent/src/core/tools/path-utils.ts +14 -2
  60. package/packages/pi-coding-agent/src/index.ts +1 -1
  61. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +6 -4
  62. package/packages/pi-coding-agent/src/utils/shell.ts +11 -0
  63. package/src/resources/extensions/bg-shell/index.ts +2 -1
  64. package/src/resources/extensions/gsd/auto.ts +18 -36
  65. package/src/resources/extensions/gsd/docs/preferences-reference.md +76 -0
  66. package/src/resources/extensions/gsd/gitignore.ts +27 -0
  67. package/src/resources/extensions/gsd/guided-flow.ts +10 -9
  68. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -2
  69. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  70. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  71. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
  72. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -3
  73. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  74. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -2
  75. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -3
  76. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  77. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  78. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  79. package/src/resources/extensions/gsd/prompts/run-uat.md +4 -4
  80. package/src/resources/extensions/gsd/tests/git-service.test.ts +53 -1
  81. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +5 -5
  82. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
  83. package/src/resources/extensions/gsd/tests/run-uat.test.ts +2 -4
@@ -21,7 +21,7 @@ import {
21
21
  import { join } from "node:path";
22
22
  import { readFileSync, existsSync, mkdirSync, readdirSync, rmSync } from "node:fs";
23
23
  import { execSync, execFileSync } from "node:child_process";
24
- import { ensureGitignore, ensurePreferences } from "./gitignore.js";
24
+ import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
25
25
  import { loadEffectiveGSDPreferences } from "./preferences.js";
26
26
  import { showConfirm } from "../shared/confirm-ui.js";
27
27
 
@@ -87,13 +87,13 @@ function dispatchWorkflow(pi: ExtensionAPI, note: string, customType = "gsd-run"
87
87
  * Build the discuss-and-plan prompt for a new milestone.
88
88
  * Used by all three "new milestone" paths (first ever, no active, all complete).
89
89
  */
90
- function buildDiscussPrompt(nextId: string, preamble: string, basePath: string): string {
91
- const milestoneDirAbs = join(basePath, ".gsd", "milestones", nextId);
90
+ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string): string {
91
+ const milestoneRel = `.gsd/milestones/${nextId}`;
92
92
  return loadPrompt("discuss", {
93
93
  milestoneId: nextId,
94
94
  preamble,
95
- contextAbsPath: join(milestoneDirAbs, `${nextId}-CONTEXT.md`),
96
- roadmapAbsPath: join(milestoneDirAbs, `${nextId}-ROADMAP.md`),
95
+ contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
96
+ roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
97
97
  });
98
98
  }
99
99
 
@@ -345,16 +345,16 @@ async function buildDiscussSlicePrompt(
345
345
  ? `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`
346
346
  : `## Inlined Context\n\n_(no context files found yet — go in blind and ask broad questions)_`;
347
347
 
348
- const sliceDirAbsPath = join(base, ".gsd", "milestones", mid, "slices", sid);
349
- const contextAbsPath = join(sliceDirAbsPath, `${sid}-CONTEXT.md`);
348
+ const sliceDirPath = `.gsd/milestones/${mid}/slices/${sid}`;
349
+ const sliceContextPath = `${sliceDirPath}/${sid}-CONTEXT.md`;
350
350
 
351
351
  return loadPrompt("guided-discuss-slice", {
352
352
  milestoneId: mid,
353
353
  sliceId: sid,
354
354
  sliceTitle: sTitle,
355
355
  inlinedContext,
356
- sliceDirAbsPath,
357
- contextAbsPath,
356
+ sliceDirPath,
357
+ contextPath: sliceContextPath,
358
358
  projectRoot: base,
359
359
  });
360
360
  }
@@ -457,6 +457,7 @@ export async function showSmartEntry(
457
457
 
458
458
  // ── Ensure .gitignore has baseline patterns ──────────────────────────
459
459
  ensureGitignore(basePath);
460
+ untrackRuntimeFiles(basePath);
460
461
 
461
462
  // ── No GSD project OR no milestone → Create first/next milestone ────
462
463
  if (!existsSync(join(basePath, ".gsd"))) {
@@ -12,7 +12,7 @@ Then:
12
12
  3. Verify each **success criterion** from the milestone definition in `{{roadmapPath}}`. For each criterion, confirm it was met with specific evidence from slice summaries, test results, or observable behavior. List any criterion that was NOT met.
13
13
  4. Verify the milestone's **definition of done** — all slices are `[x]`, all slice summaries exist, and any cross-slice integration points work correctly.
14
14
  5. Validate **requirement status transitions**. For each requirement that changed status during this milestone, confirm the transition is supported by evidence. Requirements can move between Active, Validated, Deferred, Blocked, or Out of Scope — but only with proof.
15
- 6. Write `{{milestoneSummaryAbsPath}}` using the milestone-summary template. Fill all frontmatter fields and narrative sections. The `requirement_outcomes` field must list every requirement that changed status with `from_status`, `to_status`, and `proof`.
15
+ 6. Write `{{milestoneSummaryPath}}` using the milestone-summary template. Fill all frontmatter fields and narrative sections. The `requirement_outcomes` field must list every requirement that changed status with `from_status`, `to_status`, and `proof`.
16
16
  7. Update `.gsd/REQUIREMENTS.md` if any requirement status transitions were validated in step 5.
17
17
  8. Update `.gsd/PROJECT.md` to reflect milestone completion and current project state.
18
18
  9. Do not commit manually — the system auto-commits your changes after this unit completes.
@@ -20,6 +20,6 @@ Then:
20
20
 
21
21
  **Important:** Do NOT skip the success criteria and definition of done verification (steps 3-4). The milestone summary must reflect actual verified outcomes, not assumed success. If any criterion was not met, document it clearly in the summary and do not mark the milestone as passing verification.
22
22
 
23
- **You MUST write `{{milestoneSummaryAbsPath}}` AND update PROJECT.md before finishing.**
23
+ **You MUST write `{{milestoneSummaryPath}}` AND update PROJECT.md before finishing.**
24
24
 
25
25
  When done, say: "Milestone {{milestoneId}} complete."
@@ -16,14 +16,14 @@ Then:
16
16
  3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
17
17
  4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
18
18
  5. If `.gsd/REQUIREMENTS.md` exists, update it based on what this slice actually proved. Move requirements between Active, Validated, Deferred, Blocked, or Out of Scope only when the evidence from execution supports that change.
19
- 6. Write `{{sliceSummaryAbsPath}}` (compress all task summaries).
20
- 7. Write `{{sliceUatAbsPath}}` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
19
+ 6. Write `{{sliceSummaryPath}}` (compress all task summaries).
20
+ 7. Write `{{sliceUatPath}}` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
21
21
  8. Review task summaries for `key_decisions`. Append any significant decisions to `.gsd/DECISIONS.md` if missing.
22
22
  9. Mark {{sliceId}} done in `{{roadmapPath}}` (change `[ ]` to `[x]`)
23
23
  10. Do not commit or squash-merge manually — the system auto-commits your changes and handles the merge after this unit succeeds.
24
24
  11. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed.
25
25
  12. Update `.gsd/STATE.md`
26
26
 
27
- **You MUST do ALL THREE before finishing: (1) write `{{sliceSummaryAbsPath}}`, (2) write `{{sliceUatAbsPath}}`, (3) mark {{sliceId}} as `[x]` in `{{roadmapPath}}`. The unit will not be marked complete if any of these files are missing.**
27
+ **You MUST do ALL THREE before finishing: (1) write `{{sliceSummaryPath}}`, (2) write `{{sliceUatPath}}`, (3) mark {{sliceId}} as `[x]` in `{{roadmapPath}}`. The unit will not be marked complete if any of these files are missing.**
28
28
 
29
29
  When done, say: "Slice {{sliceId}} complete."
@@ -191,8 +191,8 @@ Once the user is satisfied, in a single pass:
191
191
  **Depth-Preservation Guidance for context.md:**
192
192
  When writing context.md, preserve the user's exact terminology, emphasis, and specific framing from the discussion. Do not paraphrase user nuance into generic summaries. If the user said "craft feel," write "craft feel" — not "high-quality user experience." If they emphasized a specific constraint or negative requirement, carry that emphasis through verbatim. The context file is downstream agents' only window into this conversation — flattening specifics into generics loses the signal that shaped every decision.
193
193
 
194
- 4. Write `{{contextAbsPath}}` — read the template at `~/.gsd/agent/extensions/gsd/templates/context.md` first. Preserve key risks, unknowns, existing codebase constraints, integration points, and relevant requirements surfaced during discussion.
195
- 5. Write `{{roadmapAbsPath}}` — read the template at `~/.gsd/agent/extensions/gsd/templates/roadmap.md` first. Decompose into demoable vertical slices with checkboxes, risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, requirement coverage, and a boundary map. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment.
194
+ 4. Write `{{contextPath}}` — read the template at `~/.gsd/agent/extensions/gsd/templates/context.md` first. Preserve key risks, unknowns, existing codebase constraints, integration points, and relevant requirements surfaced during discussion.
195
+ 5. Write `{{roadmapPath}}` — read the template at `~/.gsd/agent/extensions/gsd/templates/roadmap.md` first. Decompose into demoable vertical slices with checkboxes, risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, requirement coverage, and a boundary map. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment.
196
196
  6. Seed `.gsd/DECISIONS.md` — read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` first. Append rows for any architectural or pattern decisions made during discussion.
197
197
  7. Update `.gsd/STATE.md`
198
198
  8. Commit: `docs({{milestoneId}}): context, requirements, and roadmap`
@@ -49,13 +49,13 @@ Then:
49
49
  11. **Blocker discovery:** If execution reveals that the remaining slice plan is fundamentally invalid — not just a bug or minor deviation, but a plan-invalidating finding like a wrong API, missing capability, or architectural mismatch — set `blocker_discovered: true` in the task summary frontmatter and describe the blocker clearly in the summary narrative. Do NOT set `blocker_discovered: true` for ordinary debugging, minor deviations, or issues that can be fixed within the current task or the remaining plan. This flag triggers an automatic replan of the slice.
50
50
  12. If you made an architectural, pattern, library, or observability decision during this task that downstream work should know about, append it to `.gsd/DECISIONS.md` (read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` if the file doesn't exist yet). Not every task produces decisions — only append when a meaningful choice was made.
51
51
  13. Read the template at `~/.gsd/agent/extensions/gsd/templates/task-summary.md`
52
- 14. Write `{{taskSummaryAbsPath}}`
52
+ 14. Write `{{taskSummaryPath}}`
53
53
  15. Mark {{taskId}} done in `{{planPath}}` (change `[ ]` to `[x]`)
54
54
  16. Do not commit manually — the system auto-commits your changes after this unit completes.
55
55
  17. Update `.gsd/STATE.md`
56
56
 
57
57
  You are on the slice branch. All work stays here.
58
58
 
59
- **You MUST mark {{taskId}} as `[x]` in `{{planPath}}` AND write `{{taskSummaryAbsPath}}` before finishing.**
59
+ **You MUST mark {{taskId}} as `[x]` in `{{planPath}}` AND write `{{taskSummaryPath}}` before finishing.**
60
60
 
61
61
  When done, say: "Task {{taskId}} complete."
@@ -46,8 +46,8 @@ If the user wants to keep going, keep asking. Stop when they say wrap up.
46
46
  Once the user is ready to wrap up:
47
47
 
48
48
  1. Read the slice context template at `~/.gsd/agent/extensions/gsd/templates/slice-context.md`
49
- 2. `mkdir -p {{sliceDirAbsPath}}`
50
- 3. Write `{{contextAbsPath}}` — use the template structure, filling in:
49
+ 2. `mkdir -p {{sliceDirPath}}`
50
+ 3. Write `{{contextPath}}` — use the template structure, filling in:
51
51
  - **Goal** — one sentence: what this slice delivers
52
52
  - **Why this Slice** — why now, what it unblocks
53
53
  - **Scope / In Scope** — what was confirmed in scope during the interview
@@ -55,5 +55,5 @@ Once the user is ready to wrap up:
55
55
  - **Constraints** — anything the user flagged as a hard constraint
56
56
  - **Integration Points** — what this slice consumes and produces
57
57
  - **Open Questions** — anything still unresolved, with current thinking
58
- 4. Commit: `git -C {{projectRoot}} add {{contextAbsPath}} && git -C {{projectRoot}} commit -m "docs({{milestoneId}}/{{sliceId}}): slice context from discuss"`
58
+ 4. Commit: `git -C {{projectRoot}} add {{contextPath}} && git -C {{projectRoot}} commit -m "docs({{milestoneId}}/{{sliceId}}): slice context from discuss"`
59
59
  5. Say exactly: `"{{sliceId}} context written."` — nothing else.
@@ -76,6 +76,6 @@ If this milestone requires any external API keys or secrets:
76
76
 
77
77
  If this milestone does not require any external API keys or secrets, skip this step entirely — do not create an empty manifest.
78
78
 
79
- **You MUST write the file `{{outputAbsPath}}` before finishing.**
79
+ **You MUST write the file `{{outputPath}}` before finishing.**
80
80
 
81
81
  When done, say: "Milestone {{milestoneId}} planned."
@@ -37,7 +37,7 @@ Then:
37
37
  - a matching task plan file with description, steps, must-haves, verification, inputs, and expected output
38
38
  - Observability Impact section **only if the task touches runtime boundaries, async flows, or error paths** — omit it otherwise
39
39
  6. Write `{{outputPath}}`
40
- 7. Write individual task plans in `{{sliceAbsPath}}/tasks/`: `T01-PLAN.md`, `T02-PLAN.md`, etc.
40
+ 7. Write individual task plans in `{{slicePath}}/tasks/`: `T01-PLAN.md`, `T02-PLAN.md`, etc.
41
41
  8. **Self-audit the plan.** Walk through each check — if any fail, fix the plan files before moving on:
42
42
  - **Completion semantics:** If every task were completed exactly as written, the slice goal/demo should actually be true.
43
43
  - **Requirement coverage:** Every must-have in the slice maps to at least one task. No must-have is orphaned. If `REQUIREMENTS.md` exists, every Active requirement this slice owns maps to at least one task.
@@ -52,6 +52,6 @@ Then:
52
52
 
53
53
  The slice directory and tasks/ subdirectory already exist. Do NOT mkdir. You are on the slice branch; all work stays here.
54
54
 
55
- **You MUST write the file `{{outputAbsPath}}` before finishing.**
55
+ **You MUST write the file `{{outputPath}}` before finishing.**
56
56
 
57
57
  When done, say: "Slice {{sliceId}} planned."
@@ -34,15 +34,15 @@ If all criteria have at least one remaining owning slice, the coverage check pas
34
34
 
35
35
  **If the roadmap is still good:**
36
36
 
37
- Write `{{assessmentAbsPath}}` with a brief confirmation that roadmap coverage still holds after {{completedSliceId}}. If requirements exist, explicitly note whether requirement coverage remains sound.
37
+ Write `{{assessmentPath}}` with a brief confirmation that roadmap coverage still holds after {{completedSliceId}}. If requirements exist, explicitly note whether requirement coverage remains sound.
38
38
 
39
39
  **If changes are needed:**
40
40
 
41
41
  1. Rewrite the remaining (unchecked) slices in `{{roadmapPath}}`. Keep completed slices exactly as they are (`[x]`). Update the boundary map for changed slices. Update the proof strategy if risks changed. Update requirement coverage if ownership or scope changed.
42
- 2. Write `{{assessmentAbsPath}}` explaining what changed and why — keep it brief and concrete.
42
+ 2. Write `{{assessmentPath}}` explaining what changed and why — keep it brief and concrete.
43
43
  3. If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, update it.
44
44
  4. Commit: `docs({{milestoneId}}): reassess roadmap after {{completedSliceId}}`
45
45
 
46
- **You MUST write the file `{{assessmentAbsPath}}` before finishing.**
46
+ **You MUST write the file `{{assessmentPath}}` before finishing.**
47
47
 
48
48
  When done, say: "Roadmap reassessed."
@@ -20,7 +20,7 @@ All relevant context has been preloaded below — the roadmap, current slice pla
20
20
 
21
21
  1. Read the blocker task summary carefully. Understand exactly what was discovered and why it blocks the current plan.
22
22
  2. Analyze the remaining `[ ]` tasks in the slice plan. Determine which are still valid, which need modification, and which should be replaced.
23
- 3. Write `{{replanAbsPath}}` documenting:
23
+ 3. Write `{{replanPath}}` documenting:
24
24
  - What blocker was discovered and in which task
25
25
  - What changed in the plan and why
26
26
  - Which incomplete tasks were modified, added, or removed
@@ -34,6 +34,6 @@ All relevant context has been preloaded below — the roadmap, current slice pla
34
34
  6. Do not commit manually — the system auto-commits your changes after this unit completes.
35
35
  7. Update `.gsd/STATE.md`
36
36
 
37
- **You MUST write `{{replanAbsPath}}` and the updated slice plan before finishing.**
37
+ **You MUST write `{{replanPath}}` and the updated slice plan before finishing.**
38
38
 
39
39
  When done, say: "Slice {{sliceId}} replanned."
@@ -32,6 +32,6 @@ Then research the codebase and relevant technologies. Narrate key findings and s
32
32
 
33
33
  **Research is advisory, not auto-binding.** Surface candidate requirements clearly instead of silently expanding scope.
34
34
 
35
- **You MUST write the file `{{outputAbsPath}}` before finishing.**
35
+ **You MUST write the file `{{outputPath}}` before finishing.**
36
36
 
37
37
  When done, say: "Milestone {{milestoneId}} researched."
@@ -23,6 +23,6 @@ Then research what this slice needs. Narrate key findings and surprises as you g
23
23
 
24
24
  The slice directory already exists at `{{slicePath}}/`. Do NOT mkdir — just write the file.
25
25
 
26
- **You MUST write the file `{{outputAbsPath}}` before finishing.**
26
+ **You MUST write the file `{{outputPath}}` before finishing.**
27
27
 
28
28
  When done, say: "Slice {{sliceId}} researched."
@@ -14,7 +14,7 @@ If a `GSD Skill Preferences` block is present in system context, use it to decid
14
14
 
15
15
  **UAT file:** `{{uatPath}}`
16
16
  **UAT type:** `{{uatType}}`
17
- **Result file to write:** `{{uatResultAbsPath}}` (relative: `{{uatResultPath}}`)
17
+ **Result file to write:** `{{uatResultPath}}`
18
18
 
19
19
  ### If UAT type is `artifact-driven`
20
20
 
@@ -37,7 +37,7 @@ After running all checks, compute the **overall verdict**:
37
37
  - `FAIL` — one or more checks failed
38
38
  - `PARTIAL` — some checks passed, some failed or were skipped
39
39
 
40
- Write `{{uatResultAbsPath}}` with:
40
+ Write `{{uatResultPath}}` with:
41
41
 
42
42
  ```markdown
43
43
  ---
@@ -68,7 +68,7 @@ date: <ISO 8601 timestamp>
68
68
 
69
69
  This UAT type requires human execution or live-runtime observation that you cannot perform mechanically. Your role is to surface it clearly for review.
70
70
 
71
- Write `{{uatResultAbsPath}}` with:
71
+ Write `{{uatResultPath}}` with:
72
72
 
73
73
  ```markdown
74
74
  ---
@@ -104,6 +104,6 @@ Once updated, run `/gsd auto` to resume auto-mode.
104
104
 
105
105
  ---
106
106
 
107
- **You MUST write `{{uatResultAbsPath}}` before finishing.**
107
+ **You MUST write `{{uatResultPath}}` before finishing.**
108
108
 
109
109
  When done, say: "UAT {{sliceId}} complete."
@@ -1,4 +1,4 @@
1
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
1
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs";
2
2
  import { join, dirname } from "node:path";
3
3
  import { tmpdir } from "node:os";
4
4
  import { execSync } from "node:child_process";
@@ -1370,6 +1370,58 @@ async function main(): Promise<void> {
1370
1370
  assert(true, "PreMergeCheckResult type exported and usable");
1371
1371
  }
1372
1372
 
1373
+ // ─── untrackRuntimeFiles: removes tracked runtime files from index ───
1374
+
1375
+ console.log("\n=== untrackRuntimeFiles ===");
1376
+
1377
+ {
1378
+ const { untrackRuntimeFiles } = await import("../gitignore.ts");
1379
+ const repo = mkdtempSync(join(tmpdir(), "gsd-untrack-"));
1380
+ run("git init -b main", repo);
1381
+ run("git config user.email test@test.com", repo);
1382
+ run("git config user.name Test", repo);
1383
+
1384
+ // Create and track runtime files (simulates pre-.gitignore state)
1385
+ mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
1386
+ mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
1387
+ writeFileSync(join(repo, ".gsd", "completed-units.json"), '["u1"]');
1388
+ writeFileSync(join(repo, ".gsd", "metrics.json"), '{}');
1389
+ writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
1390
+ writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
1391
+ writeFileSync(join(repo, ".gsd", "runtime", "data.json"), "{}");
1392
+ writeFileSync(join(repo, "src.ts"), "code");
1393
+ run("git add -A", repo);
1394
+ run("git commit -m init", repo);
1395
+
1396
+ // Precondition: runtime files are tracked
1397
+ const trackedBefore = run("git ls-files .gsd/", repo);
1398
+ assert(trackedBefore.includes("completed-units.json"), "untrack: precondition — completed-units tracked");
1399
+ assert(trackedBefore.includes("metrics.json"), "untrack: precondition — metrics tracked");
1400
+
1401
+ // Run untrackRuntimeFiles
1402
+ untrackRuntimeFiles(repo);
1403
+
1404
+ // Runtime files should be removed from the index
1405
+ const trackedAfter = run("git ls-files .gsd/", repo);
1406
+ assertEq(trackedAfter, "", "untrack: all runtime files removed from index");
1407
+
1408
+ // Non-runtime files remain tracked
1409
+ const srcTracked = run("git ls-files src.ts", repo);
1410
+ assert(srcTracked.includes("src.ts"), "untrack: non-runtime files remain tracked");
1411
+
1412
+ // Files still exist on disk
1413
+ assert(existsSync(join(repo, ".gsd", "completed-units.json")),
1414
+ "untrack: completed-units.json still on disk");
1415
+ assert(existsSync(join(repo, ".gsd", "metrics.json")),
1416
+ "untrack: metrics.json still on disk");
1417
+
1418
+ // Idempotent — running again doesn't error
1419
+ untrackRuntimeFiles(repo);
1420
+ assert(true, "untrack: second call is idempotent (no error)");
1421
+
1422
+ rmSync(repo, { recursive: true, force: true });
1423
+ }
1424
+
1373
1425
  console.log(`\nResults: ${passed} passed, ${failed} failed`);
1374
1426
  if (failed > 0) process.exit(1);
1375
1427
  console.log("All tests passed ✓");
@@ -54,7 +54,7 @@ async function main(): Promise<void> {
54
54
  const testVars = {
55
55
  milestoneId: "M099",
56
56
  completedSliceId: "S03",
57
- assessmentAbsPath: ".gsd/milestones/M099/slices/S03/S03-ASSESSMENT.md",
57
+ assessmentPath: ".gsd/milestones/M099/slices/S03/S03-ASSESSMENT.md",
58
58
  roadmapPath: ".gsd/milestones/M099/M099-ROADMAP.md",
59
59
  inlinedContext: "--- test inlined context block ---",
60
60
  };
@@ -75,14 +75,14 @@ async function main(): Promise<void> {
75
75
  // Verify all test variables were substituted into the output
76
76
  assert(result.includes("M099"), "prompt contains milestoneId 'M099'");
77
77
  assert(result.includes("S03"), "prompt contains completedSliceId 'S03'");
78
- assert(result.includes(".gsd/milestones/M099/slices/S03/S03-ASSESSMENT.md"), "prompt contains assessmentAbsPath");
78
+ assert(result.includes(".gsd/milestones/M099/slices/S03/S03-ASSESSMENT.md"), "prompt contains assessmentPath");
79
79
  assert(result.includes(".gsd/milestones/M099/M099-ROADMAP.md"), "prompt contains roadmapPath");
80
80
  assert(result.includes("--- test inlined context block ---"), "prompt contains inlinedContext");
81
81
 
82
82
  // Verify no un-substituted variables remain
83
83
  assert(!result.includes("{{milestoneId}}"), "no un-substituted {{milestoneId}}");
84
84
  assert(!result.includes("{{completedSliceId}}"), "no un-substituted {{completedSliceId}}");
85
- assert(!result.includes("{{assessmentAbsPath}}"), "no un-substituted {{assessmentAbsPath}}");
85
+ assert(!result.includes("{{assessmentPath}}"), "no un-substituted {{assessmentPath}}");
86
86
  assert(!result.includes("{{roadmapPath}}"), "no un-substituted {{roadmapPath}}");
87
87
  assert(!result.includes("{{inlinedContext}}"), "no un-substituted {{inlinedContext}}");
88
88
  }
@@ -93,7 +93,7 @@ async function main(): Promise<void> {
93
93
  const prompt = loadPromptFromWorktree("reassess-roadmap", {
94
94
  milestoneId: "M001",
95
95
  completedSliceId: "S01",
96
- assessmentAbsPath: ".gsd/milestones/M001/slices/S01/S01-ASSESSMENT.md",
96
+ assessmentPath: ".gsd/milestones/M001/slices/S01/S01-ASSESSMENT.md",
97
97
  roadmapPath: ".gsd/milestones/M001/M001-ROADMAP.md",
98
98
  inlinedContext: "context",
99
99
  });
@@ -132,7 +132,7 @@ async function main(): Promise<void> {
132
132
  const prompt = loadPromptFromWorktree("reassess-roadmap", {
133
133
  milestoneId: "M001",
134
134
  completedSliceId: "S01",
135
- assessmentAbsPath: ".gsd/milestones/M001/slices/S01/S01-ASSESSMENT.md",
135
+ assessmentPath: ".gsd/milestones/M001/slices/S01/S01-ASSESSMENT.md",
136
136
  roadmapPath: ".gsd/milestones/M001/M001-ROADMAP.md",
137
137
  inlinedContext: "context",
138
138
  });
@@ -404,12 +404,13 @@ console.log('\n=== prompt: replan-slice contains preserve-completed-tasks instru
404
404
  slicePath: '.gsd/milestones/M001/slices/S01',
405
405
  planPath: '.gsd/milestones/M001/slices/S01/S01-PLAN.md',
406
406
  blockerTaskId: 'T01',
407
+ replanPath: '.gsd/milestones/M001/slices/S01/S01-REPLAN.md',
407
408
  inlinedContext: '',
408
409
  });
409
410
 
410
411
  assert(prompt.includes('Do NOT renumber or remove completed tasks'), 'prompt contains preserve-completed-tasks instruction');
411
412
  assert(prompt.includes('[x]'), 'prompt mentions [x] checkmarks');
412
- assert(prompt.includes('replanAbsPath') || prompt.includes('REPLAN'), 'prompt references replan output path');
413
+ assert(prompt.includes('REPLAN'), 'prompt references replan output path');
413
414
  assert(prompt.includes('blocker_discovered'), 'prompt mentions blocker_discovered');
414
415
  }
415
416
 
@@ -226,7 +226,6 @@ async function main(): Promise<void> {
226
226
  const milestoneId = 'M001';
227
227
  const sliceId = 'S01';
228
228
  const uatPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
229
- const uatResultAbsPath = '/tmp/gsd-test/S01-UAT-RESULT.md';
230
229
  const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT-RESULT.md';
231
230
  const uatType = 'artifact-driven';
232
231
  const inlinedContext = '<!-- no context -->';
@@ -238,7 +237,6 @@ async function main(): Promise<void> {
238
237
  milestoneId,
239
238
  sliceId,
240
239
  uatPath,
241
- uatResultAbsPath,
242
240
  uatResultPath,
243
241
  uatType,
244
242
  inlinedContext,
@@ -261,8 +259,8 @@ async function main(): Promise<void> {
261
259
  `prompt contains sliceId value "${sliceId}" after substitution`,
262
260
  );
263
261
  assert(
264
- promptResult?.includes(uatResultAbsPath) ?? false,
265
- `prompt contains uatResultAbsPath value after substitution`,
262
+ promptResult?.includes(uatResultPath) ?? false,
263
+ `prompt contains uatResultPath value after substitution`,
266
264
  );
267
265
  assert(
268
266
  !/\{\{[^}]+\}\}/.test(promptResult ?? ''),