pi-gsd 1.12.4 → 2.0.1

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 (182) hide show
  1. package/.gsd/extensions/pi-gsd-hooks.ts +202 -40
  2. package/.gsd/harnesses/pi/get-shit-done/workflows/add-phase.md +66 -11
  3. package/.gsd/harnesses/pi/get-shit-done/workflows/add-tests.md +69 -4
  4. package/.gsd/harnesses/pi/get-shit-done/workflows/add-todo.md +30 -4
  5. package/.gsd/harnesses/pi/get-shit-done/workflows/audit-milestone.md +75 -17
  6. package/.gsd/harnesses/pi/get-shit-done/workflows/audit-uat.md +38 -0
  7. package/.gsd/harnesses/pi/get-shit-done/workflows/autonomous.md +95 -286
  8. package/.gsd/harnesses/pi/get-shit-done/workflows/check-todos.md +67 -4
  9. package/.gsd/harnesses/pi/get-shit-done/workflows/cleanup.md +25 -0
  10. package/.gsd/harnesses/pi/get-shit-done/workflows/complete-milestone.md +51 -529
  11. package/.gsd/harnesses/pi/get-shit-done/workflows/diagnose-issues.md +39 -0
  12. package/.gsd/harnesses/pi/get-shit-done/workflows/discovery-phase.md +2 -0
  13. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase-assumptions.md +80 -5
  14. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md +43 -5
  15. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md.bak +1049 -0
  16. package/.gsd/harnesses/pi/get-shit-done/workflows/do.md +30 -3
  17. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-milestone.md +64 -0
  18. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md +78 -20
  19. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md.bak +846 -0
  20. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-plan.md +56 -19
  21. package/.gsd/harnesses/pi/get-shit-done/workflows/fast.md +2 -0
  22. package/.gsd/harnesses/pi/get-shit-done/workflows/forensics.md +40 -0
  23. package/.gsd/harnesses/pi/get-shit-done/workflows/health.md +25 -0
  24. package/.gsd/harnesses/pi/get-shit-done/workflows/help.md +2 -0
  25. package/.gsd/harnesses/pi/get-shit-done/workflows/insert-phase.md +69 -11
  26. package/.gsd/harnesses/pi/get-shit-done/workflows/list-phase-assumptions.md +2 -0
  27. package/.gsd/harnesses/pi/get-shit-done/workflows/list-workspaces.md +51 -4
  28. package/.gsd/harnesses/pi/get-shit-done/workflows/manager.md +81 -8
  29. package/.gsd/harnesses/pi/get-shit-done/workflows/map-codebase.md +40 -5
  30. package/.gsd/harnesses/pi/get-shit-done/workflows/milestone-summary.md +66 -48
  31. package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md +41 -13
  32. package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md.bak +486 -0
  33. package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md +43 -7
  34. package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md.bak +1250 -0
  35. package/.gsd/harnesses/pi/get-shit-done/workflows/new-workspace.md +55 -4
  36. package/.gsd/harnesses/pi/get-shit-done/workflows/next.md +39 -0
  37. package/.gsd/harnesses/pi/get-shit-done/workflows/node-repair.md +2 -0
  38. package/.gsd/harnesses/pi/get-shit-done/workflows/note.md +2 -0
  39. package/.gsd/harnesses/pi/get-shit-done/workflows/pause-work.md +46 -0
  40. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-milestone-gaps.md +39 -0
  41. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-milestone.md +40 -0
  42. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md +57 -7
  43. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md.bak +859 -0
  44. package/.gsd/harnesses/pi/get-shit-done/workflows/plant-seed.md +28 -0
  45. package/.gsd/harnesses/pi/get-shit-done/workflows/pr-branch.md +2 -0
  46. package/.gsd/harnesses/pi/get-shit-done/workflows/profile-user.md +51 -0
  47. package/.gsd/harnesses/pi/get-shit-done/workflows/progress.md +52 -4
  48. package/.gsd/harnesses/pi/get-shit-done/workflows/quick.md +99 -32
  49. package/.gsd/harnesses/pi/get-shit-done/workflows/remove-phase.md +66 -4
  50. package/.gsd/harnesses/pi/get-shit-done/workflows/remove-workspace.md +55 -4
  51. package/.gsd/harnesses/pi/get-shit-done/workflows/research-phase.md +79 -22
  52. package/.gsd/harnesses/pi/get-shit-done/workflows/resume-project.md +66 -4
  53. package/.gsd/harnesses/pi/get-shit-done/workflows/review.md +66 -36
  54. package/.gsd/harnesses/pi/get-shit-done/workflows/session-report.md +2 -0
  55. package/.gsd/harnesses/pi/get-shit-done/workflows/settings.md +27 -5
  56. package/.gsd/harnesses/pi/get-shit-done/workflows/ship.md +41 -4
  57. package/.gsd/harnesses/pi/get-shit-done/workflows/stats.md +24 -0
  58. package/.gsd/harnesses/pi/get-shit-done/workflows/transition.md +54 -0
  59. package/.gsd/harnesses/pi/get-shit-done/workflows/ui-phase.md +91 -6
  60. package/.gsd/harnesses/pi/get-shit-done/workflows/ui-review.md +80 -5
  61. package/.gsd/harnesses/pi/get-shit-done/workflows/update.md +2 -0
  62. package/.gsd/harnesses/pi/get-shit-done/workflows/validate-phase.md +90 -17
  63. package/.gsd/harnesses/pi/get-shit-done/workflows/verify-phase.md +79 -4
  64. package/.gsd/harnesses/pi/get-shit-done/workflows/verify-work.md +87 -31
  65. package/README.md +146 -112
  66. package/dist/pi-gsd-tools.js +166 -163
  67. package/package.json +14 -5
  68. package/prompts/gsd-add-backlog.md +2 -3
  69. package/prompts/gsd-add-phase.md +3 -2
  70. package/prompts/gsd-add-tests.md +3 -2
  71. package/prompts/gsd-add-todo.md +3 -2
  72. package/prompts/gsd-audit-milestone.md +3 -2
  73. package/prompts/gsd-audit-uat.md +3 -2
  74. package/prompts/gsd-autonomous.md +3 -2
  75. package/prompts/gsd-check-todos.md +3 -2
  76. package/prompts/gsd-cleanup.md +3 -2
  77. package/prompts/gsd-complete-milestone.md +2 -3
  78. package/prompts/gsd-debug.md +2 -3
  79. package/prompts/gsd-discuss-phase.md +4 -3
  80. package/prompts/gsd-do.md +3 -2
  81. package/prompts/gsd-execute-milestone.md +3 -2
  82. package/prompts/gsd-execute-phase.md +3 -2
  83. package/prompts/gsd-fast.md +2 -1
  84. package/prompts/gsd-forensics.md +3 -2
  85. package/prompts/gsd-insert-phase.md +3 -2
  86. package/prompts/gsd-join-discord.md +2 -3
  87. package/prompts/gsd-list-phase-assumptions.md +2 -1
  88. package/prompts/gsd-list-workspaces.md +3 -2
  89. package/prompts/gsd-manager.md +3 -2
  90. package/prompts/gsd-map-codebase.md +3 -2
  91. package/prompts/gsd-milestone-summary.md +3 -2
  92. package/prompts/gsd-new-milestone.md +3 -2
  93. package/prompts/gsd-new-project.md +3 -2
  94. package/prompts/gsd-new-workspace.md +3 -2
  95. package/prompts/gsd-note.md +2 -1
  96. package/prompts/gsd-pause-work.md +3 -2
  97. package/prompts/gsd-plan-milestone-gaps.md +3 -2
  98. package/prompts/gsd-plan-milestone.md +3 -2
  99. package/prompts/gsd-plan-phase.md +3 -2
  100. package/prompts/gsd-plant-seed.md +3 -2
  101. package/prompts/gsd-pr-branch.md +2 -1
  102. package/prompts/gsd-profile-user.md +3 -2
  103. package/prompts/gsd-quick.md +3 -2
  104. package/prompts/gsd-reapply-patches.md +2 -3
  105. package/prompts/gsd-remove-phase.md +3 -2
  106. package/prompts/gsd-remove-workspace.md +3 -2
  107. package/prompts/gsd-research-phase.md +2 -3
  108. package/prompts/gsd-resume-work.md +3 -2
  109. package/prompts/gsd-review-backlog.md +2 -3
  110. package/prompts/gsd-review.md +3 -2
  111. package/prompts/gsd-session-report.md +2 -1
  112. package/prompts/gsd-set-profile.md +2 -3
  113. package/prompts/gsd-settings.md +3 -2
  114. package/prompts/gsd-ship.md +3 -2
  115. package/prompts/gsd-thread.md +2 -3
  116. package/prompts/gsd-ui-phase.md +3 -2
  117. package/prompts/gsd-ui-review.md +3 -2
  118. package/prompts/gsd-validate-phase.md +3 -2
  119. package/prompts/gsd-verify-work.md +3 -2
  120. package/prompts/gsd-workstreams.md +2 -3
  121. package/src/cli.ts +644 -0
  122. package/src/commands/base.ts +67 -0
  123. package/src/commands/commit.ts +22 -0
  124. package/src/commands/config.ts +71 -0
  125. package/src/commands/frontmatter.ts +51 -0
  126. package/src/commands/index.ts +76 -0
  127. package/src/commands/init.ts +43 -0
  128. package/src/commands/milestone.ts +37 -0
  129. package/src/commands/phase.ts +92 -0
  130. package/src/commands/progress.ts +71 -0
  131. package/src/commands/roadmap.ts +40 -0
  132. package/src/commands/scaffold.ts +19 -0
  133. package/src/commands/state.ts +102 -0
  134. package/src/commands/template.ts +52 -0
  135. package/src/commands/verify.ts +70 -0
  136. package/src/commands/workstream.ts +98 -0
  137. package/src/commands/wxp.ts +65 -0
  138. package/src/lib/commands.ts +1040 -0
  139. package/src/lib/config.ts +385 -0
  140. package/src/lib/core.ts +1167 -0
  141. package/src/lib/frontmatter.ts +462 -0
  142. package/src/lib/init.ts +517 -0
  143. package/src/lib/milestone.ts +290 -0
  144. package/src/lib/model-profiles.ts +272 -0
  145. package/src/lib/phase.ts +1012 -0
  146. package/src/lib/profile-output.ts +237 -0
  147. package/src/lib/profile-pipeline.ts +556 -0
  148. package/src/lib/roadmap.ts +378 -0
  149. package/src/lib/schemas.ts +290 -0
  150. package/src/lib/security.ts +176 -0
  151. package/src/lib/state.ts +1175 -0
  152. package/src/lib/template.ts +246 -0
  153. package/src/lib/uat.ts +289 -0
  154. package/src/lib/verify.ts +879 -0
  155. package/src/lib/workstream.ts +524 -0
  156. package/src/output.ts +45 -0
  157. package/src/schemas/pi-gsd-settings.schema.json +80 -0
  158. package/src/schemas/wxp.xsd +619 -0
  159. package/src/schemas/wxp.zod.ts +318 -0
  160. package/src/wxp/__tests__/arguments.test.ts +86 -0
  161. package/src/wxp/__tests__/conditions.test.ts +106 -0
  162. package/src/wxp/__tests__/executor.test.ts +95 -0
  163. package/src/wxp/__tests__/helpers.ts +26 -0
  164. package/src/wxp/__tests__/integration.test.ts +166 -0
  165. package/src/wxp/__tests__/new-features.test.ts +222 -0
  166. package/src/wxp/__tests__/parser.test.ts +159 -0
  167. package/src/wxp/__tests__/paste.test.ts +66 -0
  168. package/src/wxp/__tests__/schema.test.ts +120 -0
  169. package/src/wxp/__tests__/security.test.ts +87 -0
  170. package/src/wxp/__tests__/shell.test.ts +85 -0
  171. package/src/wxp/__tests__/string-ops.test.ts +25 -0
  172. package/src/wxp/__tests__/variables.test.ts +65 -0
  173. package/src/wxp/arguments.ts +89 -0
  174. package/src/wxp/conditions.ts +78 -0
  175. package/src/wxp/executor.ts +191 -0
  176. package/src/wxp/index.ts +191 -0
  177. package/src/wxp/parser.ts +198 -0
  178. package/src/wxp/paste.ts +51 -0
  179. package/src/wxp/security.ts +102 -0
  180. package/src/wxp/shell.ts +81 -0
  181. package/src/wxp/string-ops.ts +44 -0
  182. package/src/wxp/variables.ts +109 -0
@@ -18,18 +18,20 @@
18
18
 
19
19
  import { execSync } from "node:child_process";
20
20
  import {
21
+ copyFileSync,
21
22
  existsSync,
22
23
  lstatSync,
23
24
  mkdirSync,
24
25
  readFileSync,
25
- rmSync,
26
- statSync,
27
- symlinkSync,
26
+ readdirSync,
28
27
  writeFileSync,
29
28
  } from "node:fs";
30
29
  import { homedir } from "node:os";
31
- import { dirname, join } from "node:path";
30
+ import { dirname, join, relative } from "node:path";
32
31
  import type { ContextUsage, ExtensionAPI } from "@mariozechner/pi-coding-agent";
32
+ import { processWxpTrustedContent, WxpProcessingError, readWorkflowVersionTag } from "../../src/wxp/index.js";
33
+ import { DEFAULT_SHELL_ALLOWLIST } from "../../src/wxp/security.js";
34
+ import type { WxpSecurityConfig } from "../../src/schemas/wxp.zod.js";
33
35
 
34
36
  /**
35
37
  * Ensures .pi/gsd/ in the project is a symlink to the harness files
@@ -37,45 +39,92 @@ import type { ContextUsage, ExtensionAPI } from "@mariozechner/pi-coding-agent";
37
39
  * if already present. Never overwrites a real directory (user may have
38
40
  * customised it).
39
41
  */
40
- const ensureHarnessSymlink = (cwd: string): void => {
41
- try {
42
- const dest = join(cwd, ".pi", "gsd");
43
- // If dest exists, verify it's a valid symlink with files inside.
44
- // A stale real directory (from old build or worktree) must be replaced.
45
- if (existsSync(dest)) {
46
- try {
47
- const stat = statSync(dest);
48
- if (stat.isSymbolicLink?.() || lstatSync(dest).isSymbolicLink()) return; // valid symlink, done
49
- // Real directory — check if it has the expected files
50
- if (existsSync(join(dest, "workflows", "execute-phase.md"))) return; // looks complete
51
- // Stale/incomplete directory — remove and replace with symlink
52
- rmSync(dest, { recursive: true, force: true });
53
- } catch {
54
- return; // can't inspect, leave it
42
+
43
+ /**
44
+ * Copy-on-first-run harness distribution (HRN-01, HRN-03).
45
+ * - Detects symlinks and replaces with real file copies.
46
+ * - Copies missing files; never overwrites existing real files.
47
+ * - Silent on any failure (non-blocking).
48
+ */
49
+ function copyHarness(
50
+ src: string,
51
+ dest: string,
52
+ ): { symlinksReplaced: number; filesCopied: number } {
53
+ let symlinksReplaced = 0;
54
+ let filesCopied = 0;
55
+
56
+ const walk = (srcDir: string, destDir: string): void => {
57
+ mkdirSync(destDir, { recursive: true });
58
+ const entries = readdirSync(srcDir, { withFileTypes: true });
59
+ for (const entry of entries) {
60
+ const srcPath = join(srcDir, entry.name);
61
+ const destPath = join(destDir, entry.name);
62
+ if (entry.isDirectory()) {
63
+ walk(srcPath, destPath);
64
+ continue;
55
65
  }
66
+ if (existsSync(destPath)) {
67
+ try {
68
+ const st = lstatSync(destPath);
69
+ if (st.isSymbolicLink()) {
70
+ // Replace symlink with real copy (HRN-03)
71
+ try {
72
+ // unlinkSync removes the symlink without following it
73
+ const { unlinkSync } = require("node:fs") as typeof import("node:fs");
74
+ unlinkSync(destPath);
75
+ } catch { /* ignore */ }
76
+ copyFileSync(srcPath, destPath);
77
+ symlinksReplaced++;
78
+ }
79
+ // Real file exists → skip (HRN-01: never overwrite)
80
+ } catch { /* ignore */ }
81
+ continue;
82
+ }
83
+ try {
84
+ copyFileSync(srcPath, destPath);
85
+ filesCopied++;
86
+ } catch { /* ignore */ }
56
87
  }
88
+ };
57
89
 
58
- // Walk up from this extension file to the package root:
59
- // <pkg>/.gsd/extensions/pi-gsd-hooks.ts <pkg>
60
- const extFile = typeof __filename !== "undefined" ? __filename : "";
61
- const pkgRoot = join(dirname(extFile), "..", "..");
62
- const harnessSrc = join(
63
- pkgRoot,
64
- ".gsd",
65
- "harnesses",
66
- "pi",
67
- "get-shit-done",
68
- );
90
+ walk(src, dest);
91
+ return { symlinksReplaced, filesCopied };
92
+ }
69
93
 
70
- if (!existsSync(harnessSrc)) return; // package incomplete — skip silently
94
+ /**
95
+ * Extract the raw arguments string from a message that was produced by pi template expansion.
96
+ * Pi replaces $ARGUMENTS in prompt templates with the user's typed text.
97
+ * After <gsd-include> resolution, $ARGUMENTS text appears as trailing plain text
98
+ * at the end of the message — everything after the last WXP/include tag block.
99
+ *
100
+ * Example message after pi expansion + include resolution:
101
+ * [workflow content with <gsd-execute> blocks...]
102
+ * 16 --auto
103
+ *
104
+ * Returns: "16 --auto"
105
+ */
106
+ function extractRawArguments(content: string): string {
107
+ // Find the last <...> block (WXP tag or include) position
108
+ const lastTagEnd = (() => {
109
+ const tagPattern = /<\/(?:gsd-[a-zA-Z0-9_-]+|shell|if|then|else|condition|args|outs|string-op|settings)>/g;
110
+ let lastEnd = 0;
111
+ let m: RegExpExecArray | null;
112
+ while ((m = tagPattern.exec(content)) !== null) {
113
+ lastEnd = m.index + m[0].length;
114
+ }
115
+ return lastEnd;
116
+ })();
71
117
 
72
- mkdirSync(join(cwd, ".pi"), { recursive: true });
73
- symlinkSync(harnessSrc, dest, "dir");
74
- } catch {
75
- /* silent — never block session startup */
76
- }
77
- };
118
+ // Everything after the last closing tag is the trailing plain text ($ARGUMENTS expansion)
119
+ const trailing = content.slice(lastTagEnd).trim();
78
120
 
121
+ // Only return if it looks like user arguments (not a full document block)
122
+ // Reject if it contains markdown headings or is very long (probably included file content)
123
+ if (trailing.length === 0 || trailing.length > 500 || trailing.includes("\n\n\n")) {
124
+ return "";
125
+ }
126
+ return trailing;
127
+ }
79
128
 
80
129
  export default function (pi: ExtensionAPI) {
81
130
  /** Resolve a single <gsd-include> match: file lookup + selector extraction. */
@@ -227,15 +276,128 @@ function resolveGsdInclude(
227
276
  return { messages: [] }; // block LLM call
228
277
  }
229
278
 
279
+ // ── WXP post-processing: run after <gsd-include> resolution (WXP-14) ──
280
+ // Load global + project settings (HRN-06, HRN-07)
281
+ const extFile2 = typeof __filename !== "undefined" ? __filename : "";
282
+ const pkgRoot2 = join(dirname(extFile2), "..", "..");
283
+
284
+ type SettingsFile = {
285
+ shellAllowlist?: string[];
286
+ shellBanlist?: string[];
287
+ trustedPaths?: Array<{ position: "project" | "pkg" | "absolute"; path: string }>;
288
+ untrustedPaths?: Array<{ position: "project" | "pkg" | "absolute"; path: string }>;
289
+ shellTimeoutMs?: number;
290
+ };
291
+ const loadSettings = (settingsPath: string): SettingsFile => {
292
+ try {
293
+ if (existsSync(settingsPath)) {
294
+ return JSON.parse(readFileSync(settingsPath, "utf8")) as SettingsFile;
295
+ }
296
+ } catch { /* ignore */ }
297
+ return {};
298
+ };
299
+ const globalSettings = loadSettings(join(homedir(), ".gsd", "pi-gsd-settings.json"));
300
+ const projectSettings = loadSettings(join(ctx.cwd, ".pi", "gsd", "pi-gsd-settings.json"));
301
+ const mergedAllowlist = [
302
+ ...DEFAULT_SHELL_ALLOWLIST,
303
+ ...(globalSettings.shellAllowlist ?? []),
304
+ ...(projectSettings.shellAllowlist ?? []),
305
+ ];
306
+ const wxpSecurity: WxpSecurityConfig = {
307
+ trustedPaths: [
308
+ ...(globalSettings.trustedPaths ?? []),
309
+ ...(projectSettings.trustedPaths ?? []),
310
+ { position: "pkg", path: ".gsd/harnesses/pi/get-shit-done" },
311
+ { position: "project", path: ".pi/gsd" },
312
+ ],
313
+ untrustedPaths: [
314
+ ...(globalSettings.untrustedPaths ?? []),
315
+ ...(projectSettings.untrustedPaths ?? []),
316
+ ],
317
+ shellAllowlist: [...new Set(mergedAllowlist)],
318
+ shellBanlist: [
319
+ ...(globalSettings.shellBanlist ?? []),
320
+ ...(projectSettings.shellBanlist ?? []),
321
+ ],
322
+ shellTimeoutMs: projectSettings.shellTimeoutMs ?? globalSettings.shellTimeoutMs ?? 30_000,
323
+ };
324
+
325
+ try {
326
+ for (const msg of messages) {
327
+ if (msg.role !== "user") continue;
328
+ if (typeof msg.content === "string") {
329
+ if (!msg.content.includes("<gsd-")) continue;
330
+ const virtualPath = join(ctx.cwd, ".pi", "gsd", "workflows", "_message.md");
331
+ const rawArgs = extractRawArguments(msg.content);
332
+ msg.content = processWxpTrustedContent(msg.content, virtualPath, wxpSecurity, ctx.cwd, pkgRoot2, rawArgs, (m, lv) => ctx.ui.notify(m, lv === "error" ? "error" : "info"));
333
+ } else if (Array.isArray(msg.content)) {
334
+ for (const block of msg.content) {
335
+ if (block.type !== "text" || !block.text) continue;
336
+ if (!block.text.includes("<gsd-")) continue;
337
+ const virtualPath = join(ctx.cwd, ".pi", "gsd", "workflows", "_message.md");
338
+ const rawArgs = extractRawArguments(block.text);
339
+ block.text = processWxpTrustedContent(block.text, virtualPath, wxpSecurity, ctx.cwd, pkgRoot2, rawArgs, (m, lv) => ctx.ui.notify(m, lv === "error" ? "error" : "info"));
340
+ }
341
+ }
342
+ }
343
+ } catch (wxpErr) {
344
+ if (wxpErr instanceof WxpProcessingError) {
345
+ ctx.ui.notify(wxpErr.message, "error");
346
+ return { messages: [] }; // WXP-09: no partial content reaches LLM
347
+ }
348
+ // Non-WXP error: log but don't block
349
+ const errMsg = wxpErr instanceof Error ? wxpErr.message : String(wxpErr);
350
+ ctx.ui.notify(`GSD WXP: unexpected context error: ${errMsg}`, "info");
351
+ }
352
+
230
353
  return { messages };
231
354
  });
232
355
 
233
356
  // ── session_start: GSD update check ──────────────────────────────────────
234
357
  pi.on("session_start", async (_event, ctx) => {
235
- // Ensure harness files are reachable via .pi/gsd/ symlink
236
- ensureHarnessSymlink(ctx.cwd);
237
-
358
+ // Copy-on-first-run harness distribution (HRN-01, HRN-03)
359
+ try {
360
+ const extFile = typeof __filename !== "undefined" ? __filename : "";
361
+ const pkgRoot = join(dirname(extFile), "..", "..");
362
+ const pkgHarness = join(pkgRoot, ".gsd", "harnesses", "pi", "get-shit-done");
363
+ const projectHarness = join(ctx.cwd, ".pi", "gsd");
364
+ if (existsSync(pkgHarness)) {
365
+ const { symlinksReplaced } = copyHarness(pkgHarness, projectHarness);
366
+ if (symlinksReplaced > 0) {
367
+ ctx.ui.notify(
368
+ `ℹ️ GSD: Replaced ${symlinksReplaced} symlink(s) in .pi/gsd/ with real file copies.`,
369
+ "info",
370
+ );
371
+ }
238
372
 
373
+ // Version-aware update detection (HRN-02)
374
+ try {
375
+ const pkgJsonPath = join(pkgRoot, "package.json");
376
+ if (existsSync(pkgJsonPath)) {
377
+ const pkgVersion = (JSON.parse(readFileSync(pkgJsonPath, "utf8")) as { version?: string }).version ?? "0.0.0";
378
+ const outdated: string[] = [];
379
+ // Check a sample of key workflow files for version drift
380
+ const sampleFiles = ["workflows/execute-phase.md", "workflows/plan-phase.md"];
381
+ for (const rel of sampleFiles) {
382
+ const projFile = join(projectHarness, rel);
383
+ if (!existsSync(projFile)) continue;
384
+ const content = readFileSync(projFile, "utf8");
385
+ const vtag = readWorkflowVersionTag(content);
386
+ if (!vtag || vtag.doNotUpdate) continue;
387
+ if (vtag.version !== pkgVersion) outdated.push(rel);
388
+ }
389
+ if (outdated.length > 0) {
390
+ ctx.ui.notify(
391
+ `ℹ️ GSD harness update available (package v${pkgVersion}).\n` +
392
+ `Outdated files: ${outdated.join(", ")}\n` +
393
+ `Run: pi-gsd-tools harness update [y|n|pick|diff]`,
394
+ "info",
395
+ );
396
+ }
397
+ }
398
+ } catch { /* silent */ }
399
+ }
400
+ } catch { /* silent */ }
239
401
  try {
240
402
  const cacheDir = join(homedir(), ".pi", "cache");
241
403
  const cacheFile = join(cacheDir, "gsd-update-check.json");
@@ -1,10 +1,68 @@
1
- <purpose>
2
- Add a new integer phase to the end of the current milestone in the roadmap. Automatically calculates next phase number, creates phase directory, and updates roadmap structure.
3
- </purpose>
4
-
5
- <required_reading>
6
- Read all files referenced by the invoking prompt's execution_context before starting.
7
- </required_reading>
1
+ <gsd-version v="1.12.4" />
2
+
3
+ <gsd-arguments>
4
+ <settings>
5
+ <keep-extra-args />
6
+ </settings>
7
+ <arg name="description" type="string" optional />
8
+ </gsd-arguments>
9
+
10
+ <gsd-execute>
11
+ <shell command="pi-gsd-tools">
12
+ <args>
13
+ <arg string="init" />
14
+ <arg string="phase-op" />
15
+ <arg string="0" />
16
+ </args>
17
+ <outs>
18
+ <out type="string" name="init" />
19
+ </outs>
20
+ </shell>
21
+ <if>
22
+ <condition>
23
+ <starts-with>
24
+ <left name="init" />
25
+ <right type="string" value="@file:" />
26
+ </starts-with>
27
+ </condition>
28
+ <then>
29
+ <string-op op="split">
30
+ <args>
31
+ <arg name="init" />
32
+ <arg type="string" value="@file:" />
33
+ </args>
34
+ <outs>
35
+ <out type="string" name="init-file" />
36
+ </outs>
37
+ </string-op>
38
+ <shell command="cat">
39
+ <args>
40
+ <arg name="init-file" wrap='"' />
41
+ </args>
42
+ <outs>
43
+ <out type="string" name="init" />
44
+ </outs>
45
+ </shell>
46
+ </then>
47
+ </if>
48
+ <shell command="pi-gsd-tools">
49
+ <args>
50
+ <arg string="state" />
51
+ <arg string="json" />
52
+ <arg string="--raw" />
53
+ </args>
54
+ <outs>
55
+ <out type="string" name="state" />
56
+ </outs>
57
+ </shell>
58
+ </gsd-execute>
59
+
60
+ ## Context (pre-injected by WXP)
61
+
62
+ **Description:** <gsd-paste name="description" />
63
+
64
+ **Project State:**
65
+ <gsd-paste name="state" />
8
66
 
9
67
  <process>
10
68
 
@@ -28,10 +86,7 @@ Exit.
28
86
  <step name="init_context">
29
87
  Load phase operation context:
30
88
 
31
- ```bash
32
- INIT=$(pi-gsd-tools init phase-op "0")
33
- if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
34
- ```
89
+ <!-- Context pre-injected above via WXP — variables available via <gsd-paste name="..."> -->
35
90
 
36
91
  Check `roadmap_exists` from init JSON. If false:
37
92
  ```
@@ -1,3 +1,71 @@
1
+ <gsd-version v="1.12.4" />
2
+
3
+ <gsd-arguments>
4
+ <settings><keep-extra-args /></settings>
5
+ <arg name="phase" type="number" />
6
+ </gsd-arguments>
7
+
8
+ <gsd-execute>
9
+ <shell command="pi-gsd-tools">
10
+ <args>
11
+ <arg string="pi-gsd-tools" />
12
+ <arg string="init" />
13
+ <arg string="phase-op" />
14
+ </args>
15
+ <outs>
16
+ <out type="string" name="init" />
17
+ </outs>
18
+ </shell>
19
+ <if>
20
+ <condition>
21
+ <starts-with>
22
+ <left name="init" />
23
+ <right type="string" value="@file:" />
24
+ </starts-with>
25
+ </condition>
26
+ <then>
27
+ <string-op op="split">
28
+ <args>
29
+ <arg name="init" />
30
+ <arg type="string" value="@file:" />
31
+ </args>
32
+ <outs>
33
+ <out type="string" name="init-file" />
34
+ </outs>
35
+ </string-op>
36
+ <shell command="cat">
37
+ <args>
38
+ <arg name="init-file" wrap='"' />
39
+ </args>
40
+ <outs>
41
+ <out type="string" name="init" />
42
+ </outs>
43
+ </shell>
44
+ </then>
45
+ </if>
46
+ <shell command="pi-gsd-tools">
47
+ <args>
48
+ <arg string="pi-gsd-tools" />
49
+ <arg string="state" />
50
+ <arg string="json" />
51
+ <arg string="--raw" />
52
+ </args>
53
+ <outs>
54
+ <out type="string" name="state" />
55
+ </outs>
56
+ </shell>
57
+ </gsd-execute>
58
+
59
+ ## Context (pre-injected)
60
+
61
+ **Phase:** <gsd-paste name="phase" />
62
+
63
+ **Phase Data:**
64
+ <gsd-paste name="init" />
65
+
66
+ **State:**
67
+ <gsd-paste name="state" />
68
+
1
69
  <purpose>
2
70
  Generate unit and E2E tests for a completed phase based on its SUMMARY.md, CONTEXT.md, and implementation. Classifies each changed file into TDD (unit), E2E (browser), or Skip categories, presents a test plan for user approval, then generates tests following RED-GREEN conventions.
3
71
 
@@ -32,10 +100,7 @@ Exit.
32
100
  <step name="init_context">
33
101
  Load phase operation context:
34
102
 
35
- ```bash
36
- INIT=$(pi-gsd-tools init phase-op "${PHASE_ARG}")
37
- if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
38
- ```
103
+ <!-- Context pre-injected above via WXP — variables available via <gsd-paste name="..."> -->
39
104
 
40
105
  Extract from init JSON: `phase_dir`, `phase_number`, `phase_name`.
41
106
 
@@ -1,3 +1,32 @@
1
+ <gsd-version v="1.12.4" />
2
+
3
+ <gsd-arguments>
4
+ <settings><keep-extra-args /></settings>
5
+ <arg name="text" type="string" optional />
6
+ </gsd-arguments>
7
+
8
+ <gsd-execute>
9
+ <shell command="pi-gsd-tools">
10
+ <args>
11
+ <arg string="pi-gsd-tools" />
12
+ <arg string="state" />
13
+ <arg string="json" />
14
+ <arg string="--raw" />
15
+ </args>
16
+ <outs>
17
+ <suppress-errors />
18
+ <out type="string" name="state" />
19
+ </outs>
20
+ </shell>
21
+ </gsd-execute>
22
+
23
+ ## Context (pre-injected)
24
+
25
+ **Todo text:** <gsd-paste name="text" />
26
+
27
+ **State:**
28
+ <gsd-paste name="state" />
29
+
1
30
  <purpose>
2
31
  Capture an idea, task, or issue that surfaces during a GSD session as a structured todo for later work. Enables "thought → capture → continue" flow without losing context.
3
32
  </purpose>
@@ -11,10 +40,7 @@ Read all files referenced by the invoking prompt's execution_context before star
11
40
  <step name="init_context">
12
41
  Load todo context:
13
42
 
14
- ```bash
15
- INIT=$(pi-gsd-tools init todos)
16
- if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
17
- ```
43
+ <!-- Context pre-injected above via WXP — variables available via <gsd-paste name="..."> -->
18
44
 
19
45
  Extract from init JSON: `commit_docs`, `date`, `timestamp`, `todo_count`, `todos`, `pending_dir`, `todos_dir_exists`.
20
46
 
@@ -1,25 +1,83 @@
1
- <purpose>
2
- Verify milestone achieved its definition of done by aggregating phase verifications, checking cross-phase integration, and assessing requirements coverage. Reads existing VERIFICATION.md files (phases already verified during execute-phase), aggregates tech debt and deferred gaps, then spawns integration checker for cross-phase wiring.
3
- </purpose>
4
-
5
- <required_reading>
6
- Read all files referenced by the invoking prompt's execution_context before starting.
7
- </required_reading>
8
-
9
- <available_agent_types>
10
- Valid GSD subagent types (use exact names - do not fall back to 'general-purpose'):
11
- - gsd-integration-checker - Checks cross-phase integration
12
- </available_agent_types>
1
+ <gsd-version v="1.12.4" />
2
+
3
+ <gsd-arguments>
4
+ <settings>
5
+ <keep-extra-args />
6
+ </settings>
7
+ </gsd-arguments>
8
+
9
+ <gsd-execute>
10
+ <shell command="pi-gsd-tools">
11
+ <args>
12
+ <arg string="init" />
13
+ <arg string="milestone-op" />
14
+ </args>
15
+ <outs>
16
+ <out type="string" name="init" />
17
+ </outs>
18
+ </shell>
19
+ <if>
20
+ <condition>
21
+ <starts-with>
22
+ <left name="init" />
23
+ <right type="string" value="@file:" />
24
+ </starts-with>
25
+ </condition>
26
+ <then>
27
+ <string-op op="split">
28
+ <args>
29
+ <arg name="init" />
30
+ <arg type="string" value="@file:" />
31
+ </args>
32
+ <outs>
33
+ <out type="string" name="init-file" />
34
+ </outs>
35
+ </string-op>
36
+ <shell command="cat">
37
+ <args>
38
+ <arg name="init-file" wrap='"' />
39
+ </args>
40
+ <outs>
41
+ <out type="string" name="init" />
42
+ </outs>
43
+ </shell>
44
+ </then>
45
+ </if>
46
+ <shell command="pi-gsd-tools">
47
+ <args>
48
+ <arg string="agent-skills" />
49
+ <arg string="gsd-integration-checker" />
50
+ </args>
51
+ <outs>
52
+ <suppress-errors />
53
+ <out type="string" name="agent-skills-checker" />
54
+ </outs>
55
+ </shell>
56
+ <shell command="pi-gsd-tools">
57
+ <args>
58
+ <arg string="resolve-model" />
59
+ <arg string="gsd-integration-checker" />
60
+ <arg string="--raw" />
61
+ </args>
62
+ <outs>
63
+ <suppress-errors />
64
+ <out type="string" name="integration-checker-model" />
65
+ </outs>
66
+ </shell>
67
+ </gsd-execute>
68
+
69
+ ## Milestone Audit Context (pre-injected by WXP)
70
+
71
+ **Milestone Init Data:**
72
+ <gsd-paste name="init" />
73
+
74
+ **Integration Checker Model:** <gsd-paste name="integration-checker-model" />
13
75
 
14
76
  <process>
15
77
 
16
78
  ## 0. Initialize Milestone Context
17
79
 
18
- ```bash
19
- INIT=$(pi-gsd-tools init milestone-op)
20
- if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
21
- AGENT_SKILLS_CHECKER=$(pi-gsd-tools agent-skills gsd-integration-checker 2>/dev/null)
22
- ```
80
+ <!-- Context pre-injected above via WXP — variables available via <gsd-paste name="..."> -->
23
81
 
24
82
  Extract from init JSON: `milestone_version`, `milestone_name`, `phase_count`, `completed_phases`, `commit_docs`.
25
83
 
@@ -1,3 +1,41 @@
1
+ <gsd-version v="1.12.4" />
2
+
3
+ <gsd-arguments>
4
+ <settings><keep-extra-args /></settings>
5
+ </gsd-arguments>
6
+
7
+ <gsd-execute>
8
+ <shell command="pi-gsd-tools">
9
+ <args>
10
+ <arg string="pi-gsd-tools" />
11
+ <arg string="audit-uat" />
12
+ <arg string="--raw" />
13
+ </args>
14
+ <outs>
15
+ <out type="string" name="audit-data" />
16
+ </outs>
17
+ </shell>
18
+ <shell command="pi-gsd-tools">
19
+ <args>
20
+ <arg string="pi-gsd-tools" />
21
+ <arg string="state" />
22
+ <arg string="json" />
23
+ <arg string="--raw" />
24
+ </args>
25
+ <outs>
26
+ <out type="string" name="state" />
27
+ </outs>
28
+ </shell>
29
+ </gsd-execute>
30
+
31
+ ## Audit Data (pre-injected)
32
+
33
+ **UAT Audit:**
34
+ <gsd-paste name="audit-data" />
35
+
36
+ **State:**
37
+ <gsd-paste name="state" />
38
+
1
39
  <purpose>
2
40
  Cross-phase audit of all UAT and verification files. Finds every outstanding item (pending, skipped, blocked, human_needed), optionally verifies against the codebase to detect stale docs, and produces a prioritized human test plan.
3
41
  </purpose>