gsd-pi 2.70.0-dev.55a1c68 → 2.70.0-dev.7ebda5e

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 (105) hide show
  1. package/dist/loader.js +4 -0
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +150 -2
  3. package/dist/resources/extensions/gsd/auto-model-selection.js +33 -19
  4. package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
  5. package/dist/resources/extensions/gsd/auto-start.js +25 -1
  6. package/dist/resources/extensions/gsd/auto.js +12 -8
  7. package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
  8. package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
  9. package/dist/resources/extensions/gsd/doctor-format.js +2 -0
  10. package/dist/resources/extensions/gsd/guided-flow.js +21 -10
  11. package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
  12. package/dist/resources/extensions/gsd/validate-directory.js +30 -12
  13. package/dist/resources/extensions/gsd/workflow-mcp.js +11 -0
  14. package/dist/resources/extensions/slash-commands/audit.js +2 -1
  15. package/dist/resources/extensions/subagent/isolation.js +4 -2
  16. package/dist/update-check.d.ts +1 -0
  17. package/dist/update-check.js +30 -27
  18. package/dist/update-cmd.js +3 -11
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  21. package/dist/web/standalone/.next/build-manifest.json +2 -2
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/required-server-files.json +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  41. package/dist/web/standalone/.next/server/app/index.html +1 -1
  42. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  49. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  50. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  51. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  52. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  53. package/dist/web/standalone/server.js +1 -1
  54. package/dist/web-mode.js +4 -0
  55. package/package.json +11 -11
  56. package/packages/mcp-server/dist/workflow-tools.d.ts +2 -0
  57. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  58. package/packages/mcp-server/dist/workflow-tools.js +35 -3
  59. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  60. package/packages/mcp-server/src/import-candidates.test.ts +48 -0
  61. package/packages/mcp-server/src/workflow-tools.ts +34 -1
  62. package/packages/pi-agent-core/dist/agent.d.ts +8 -0
  63. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  64. package/packages/pi-agent-core/dist/agent.js +3 -0
  65. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  66. package/packages/pi-agent-core/src/agent.test.ts +82 -0
  67. package/packages/pi-agent-core/src/agent.ts +12 -0
  68. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +1 -0
  69. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/lsp/config.js +38 -15
  71. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  73. package/packages/pi-coding-agent/dist/core/sdk.js +10 -0
  74. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  75. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  76. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +3 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  78. package/packages/pi-coding-agent/src/core/lsp/config.ts +43 -17
  79. package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
  80. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -5
  81. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +227 -2
  82. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +172 -0
  83. package/src/resources/extensions/gsd/auto-model-selection.ts +39 -25
  84. package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
  85. package/src/resources/extensions/gsd/auto-start.ts +34 -1
  86. package/src/resources/extensions/gsd/auto.ts +12 -8
  87. package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
  88. package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
  89. package/src/resources/extensions/gsd/doctor-format.ts +1 -0
  90. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  91. package/src/resources/extensions/gsd/guided-flow.ts +24 -8
  92. package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
  93. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
  94. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
  95. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
  96. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
  97. package/src/resources/extensions/gsd/tests/validate-directory.test.ts +33 -1
  98. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
  99. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +25 -0
  100. package/src/resources/extensions/gsd/validate-directory.ts +33 -11
  101. package/src/resources/extensions/gsd/workflow-mcp.ts +15 -0
  102. package/src/resources/extensions/slash-commands/audit.ts +2 -1
  103. package/src/resources/extensions/subagent/isolation.ts +4 -3
  104. /package/dist/web/standalone/.next/static/{0CnmwCBOy-QNRFzdWLB7Q → yvFbuOJuph5517lR7HBt2}/_buildManifest.js +0 -0
  105. /package/dist/web/standalone/.next/static/{0CnmwCBOy-QNRFzdWLB7Q → yvFbuOJuph5517lR7HBt2}/_ssgManifest.js +0 -0
@@ -9,10 +9,11 @@ import { deriveState, isValidationTerminal } from "../state.ts";
9
9
  import { resolveExpectedArtifactPath, diagnoseExpectedArtifact } from "../auto-artifact-paths.ts";
10
10
  import { verifyExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.ts";
11
11
  import { resolveDispatch, type DispatchContext } from "../auto-dispatch.ts";
12
- import { buildValidateMilestonePrompt } from "../auto-prompts.ts";
12
+ import { buildCompleteMilestonePrompt, buildValidateMilestonePrompt } from "../auto-prompts.ts";
13
13
  import type { GSDState } from "../types.ts";
14
14
  import { clearPathCache } from "../paths.ts";
15
15
  import { clearParseCache } from "../files.ts";
16
+ import { closeDatabase, insertMilestone, insertSlice, openDatabase } from "../gsd-db.ts";
16
17
 
17
18
  // ─── Helpers ──────────────────────────────────────────────────────────────
18
19
 
@@ -25,9 +26,15 @@ function makeTmpBase(): string {
25
26
  function cleanup(base: string): void {
26
27
  clearPathCache();
27
28
  clearParseCache();
29
+ closeDatabase();
28
30
  try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
29
31
  }
30
32
 
33
+ function openTestDb(base: string): void {
34
+ const dbPath = join(base, ".gsd", "gsd.db");
35
+ assert.equal(openDatabase(dbPath), true, "test DB should open");
36
+ }
37
+
31
38
  function writeRoadmap(base: string, mid: string, content: string): void {
32
39
  const dir = join(base, ".gsd", "milestones", mid);
33
40
  mkdirSync(dir, { recursive: true });
@@ -218,6 +225,85 @@ test("buildValidateMilestonePrompt inlines ASSESSMENT evidence instead of UAT sp
218
225
  }
219
226
  });
220
227
 
228
+ test("buildCompleteMilestonePrompt skips skipped slices from DB-backed summary inlining", async () => {
229
+ const base = makeTmpBase();
230
+ try {
231
+ writeRoadmap(base, "M001", `# M001: Test Milestone
232
+
233
+ ## Vision
234
+ Test
235
+
236
+ ## Success Criteria
237
+ - It works
238
+
239
+ ## Slices
240
+
241
+ - [x] **S01: First slice** \`risk:low\` \`depends:[]\`
242
+ > Done
243
+ - [ ] **S02: Skipped slice** \`risk:low\` \`depends:[]\`
244
+ > Intentionally skipped
245
+
246
+ ## Boundary Map
247
+
248
+ | From | To | Produces | Consumes |
249
+ |------|-----|----------|----------|
250
+ | S01 | terminal | output | nothing |
251
+ `);
252
+ openTestDb(base);
253
+ insertMilestone({ id: "M001", title: "Test Milestone", status: "active" });
254
+ insertSlice({ id: "S01", milestoneId: "M001", title: "First slice", status: "complete", depends: [], sequence: 1 });
255
+ insertSlice({ id: "S02", milestoneId: "M001", title: "Skipped slice", status: "skipped", depends: [], sequence: 2 });
256
+ writeSliceSummary(base, "M001", "S01", "# S01 Summary\nDelivered.");
257
+
258
+ const prompt = await buildCompleteMilestonePrompt("M001", "Test Milestone", base);
259
+ assert.match(prompt, /S01 Summary/i, "prompt should inline non-skipped slice summaries");
260
+ assert.doesNotMatch(prompt, /### S02 Summary/i, "prompt should not inline skipped slice summaries");
261
+ assert.doesNotMatch(prompt, /not found — file does not exist yet/i, "prompt should not emit skipped-slice missing-file placeholders");
262
+ } finally {
263
+ cleanup(base);
264
+ }
265
+ });
266
+
267
+ test("buildValidateMilestonePrompt skips skipped slices from DB-backed summary inlining", async () => {
268
+ const base = makeTmpBase();
269
+ try {
270
+ writeRoadmap(base, "M001", `# M001: Test Milestone
271
+
272
+ ## Vision
273
+ Test
274
+
275
+ ## Success Criteria
276
+ - It works
277
+
278
+ ## Slices
279
+
280
+ - [x] **S01: First slice** \`risk:low\` \`depends:[]\`
281
+ > Done
282
+ - [ ] **S02: Skipped slice** \`risk:low\` \`depends:[]\`
283
+ > Intentionally skipped
284
+
285
+ ## Boundary Map
286
+
287
+ | From | To | Produces | Consumes |
288
+ |------|-----|----------|----------|
289
+ | S01 | terminal | output | nothing |
290
+ `);
291
+ openTestDb(base);
292
+ insertMilestone({ id: "M001", title: "Test Milestone", status: "active" });
293
+ insertSlice({ id: "S01", milestoneId: "M001", title: "First slice", status: "complete", depends: [], sequence: 1 });
294
+ insertSlice({ id: "S02", milestoneId: "M001", title: "Skipped slice", status: "skipped", depends: [], sequence: 2 });
295
+ writeSliceSummary(base, "M001", "S01", "# S01 Summary\nDelivered.");
296
+ writeSliceAssessment(base, "M001", "S01", "---\nverdict: PASS\n---\n# Assessment\nEvidence captured.");
297
+
298
+ const prompt = await buildValidateMilestonePrompt("M001", "Test Milestone", base);
299
+ assert.match(prompt, /S01 Summary/i, "prompt should inline non-skipped slice summaries");
300
+ assert.doesNotMatch(prompt, /### S02 Summary/i, "prompt should not inline skipped slice summaries");
301
+ assert.doesNotMatch(prompt, /not found — file does not exist yet/i, "prompt should not emit skipped-slice missing-file placeholders");
302
+ } finally {
303
+ cleanup(base);
304
+ }
305
+ });
306
+
221
307
  // ─── Dispatch rule ────────────────────────────────────────────────────────
222
308
 
223
309
  test("dispatch rule matches validating-milestone phase", async () => {
@@ -13,6 +13,7 @@ import {
13
13
  getWorkflowTransportSupportError,
14
14
  getRequiredWorkflowToolsForAutoUnit,
15
15
  getRequiredWorkflowToolsForGuidedUnit,
16
+ supportsStructuredQuestions,
16
17
  usesWorkflowMcpTransport,
17
18
  } from "../workflow-mcp.ts";
18
19
 
@@ -291,6 +292,30 @@ test("usesWorkflowMcpTransport matches local externalCli providers", () => {
291
292
  assert.equal(usesWorkflowMcpTransport("oauth", "local://custom"), false);
292
293
  });
293
294
 
295
+ test("supportsStructuredQuestions disables structured ask flow on workflow MCP transports", () => {
296
+ assert.equal(
297
+ supportsStructuredQuestions(["ask_user_questions"], {
298
+ authMode: "externalCli",
299
+ baseUrl: "local://claude-code",
300
+ }),
301
+ false,
302
+ );
303
+ assert.equal(
304
+ supportsStructuredQuestions(["ask_user_questions"], {
305
+ authMode: "oauth",
306
+ baseUrl: "https://api.anthropic.com",
307
+ }),
308
+ true,
309
+ );
310
+ assert.equal(
311
+ supportsStructuredQuestions([], {
312
+ authMode: "oauth",
313
+ baseUrl: "https://api.anthropic.com",
314
+ }),
315
+ false,
316
+ );
317
+ });
318
+
294
319
  test("transport compatibility passes when required tools fit current MCP surface", () => {
295
320
  const error = getWorkflowTransportSupportError(
296
321
  "claude-code",
@@ -61,6 +61,33 @@ const WINDOWS_BLOCKED_PATHS = new Set([
61
61
  "C:\\Program Files (x86)",
62
62
  ]);
63
63
 
64
+ const WINDOWS_BLOCKED_SUFFIXES = new Set([
65
+ "\\",
66
+ "\\windows",
67
+ "\\windows\\system32",
68
+ "\\program files",
69
+ "\\program files (x86)",
70
+ ]);
71
+
72
+ function normalizePathForComparison(dirPath: string): string {
73
+ let normalized = dirPath.replace(/[/\\]+$/, "");
74
+ if (normalized === "") {
75
+ normalized = "/";
76
+ } else if (/^[A-Za-z]:$/.test(normalized)) {
77
+ normalized += "\\";
78
+ }
79
+ return platform() === "win32" ? normalized.toLowerCase() : normalized;
80
+ }
81
+
82
+ function isBlockedWindowsPath(normalized: string): boolean {
83
+ if (!/^[a-z]:\\/.test(normalized)) {
84
+ return false;
85
+ }
86
+
87
+ const suffix = normalized.slice(2);
88
+ return WINDOWS_BLOCKED_SUFFIXES.has(suffix);
89
+ }
90
+
64
91
  // ─── Core Validation ────────────────────────────────────────────────────────────
65
92
 
66
93
  /**
@@ -84,16 +111,11 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
84
111
 
85
112
  // Normalize trailing slashes for consistent comparison.
86
113
  // Special cases: "/" → "/" (not ""), "C:\" → "C:\" (not "C:")
87
- let normalized = resolved.replace(/[/\\]+$/, "");
88
- if (normalized === "") {
89
- normalized = "/";
90
- } else if (/^[A-Za-z]:$/.test(normalized)) {
91
- normalized = normalized + "\\";
92
- }
114
+ const normalized = normalizePathForComparison(resolved);
93
115
 
94
116
  // ── Check 1: Blocked system paths ──────────────────────────────────────
95
117
  const blockedPaths = platform() === "win32" ? WINDOWS_BLOCKED_PATHS : UNIX_BLOCKED_PATHS;
96
- if (blockedPaths.has(normalized)) {
118
+ if (platform() === "win32" ? isBlockedWindowsPath(normalized) : blockedPaths.has(normalized)) {
97
119
  return {
98
120
  safe: false,
99
121
  severity: "blocked",
@@ -104,9 +126,9 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
104
126
  // ── Check 2: Home directory itself (not subdirs) ───────────────────────
105
127
  let resolvedHome: string;
106
128
  try {
107
- resolvedHome = realpathSync(resolve(homedir())).replace(/[/\\]+$/, "");
129
+ resolvedHome = normalizePathForComparison(realpathSync(resolve(homedir())));
108
130
  } catch {
109
- resolvedHome = resolve(homedir()).replace(/[/\\]+$/, "");
131
+ resolvedHome = normalizePathForComparison(resolve(homedir()));
110
132
  }
111
133
 
112
134
  if (normalized === resolvedHome) {
@@ -120,9 +142,9 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
120
142
  // ── Check 3: Temp directory root ───────────────────────────────────────
121
143
  let resolvedTmp: string;
122
144
  try {
123
- resolvedTmp = realpathSync(resolve(tmpdir())).replace(/[/\\]+$/, "");
145
+ resolvedTmp = normalizePathForComparison(realpathSync(resolve(tmpdir())));
124
146
  } catch {
125
- resolvedTmp = resolve(tmpdir()).replace(/[/\\]+$/, "");
147
+ resolvedTmp = normalizePathForComparison(resolve(tmpdir()));
126
148
  }
127
149
 
128
150
  if (normalized === resolvedTmp) {
@@ -348,6 +348,21 @@ export function usesWorkflowMcpTransport(
348
348
  return authMode === "externalCli" && typeof baseUrl === "string" && baseUrl.startsWith("local://");
349
349
  }
350
350
 
351
+ export function supportsStructuredQuestions(
352
+ activeTools: string[],
353
+ options: Pick<WorkflowCapabilityOptions, "authMode" | "baseUrl"> = {},
354
+ ): boolean {
355
+ if (!activeTools.includes("ask_user_questions")) return false;
356
+
357
+ // Workflow MCP currently exposes ask_user_questions via MCP form elicitation.
358
+ // Local external CLI transports such as Claude Code can invoke the tool, but
359
+ // do not reliably complete that elicitation round-trip yet, so guided discuss
360
+ // prompts must fall back to plain-text questioning.
361
+ if (usesWorkflowMcpTransport(options.authMode, options.baseUrl)) return false;
362
+
363
+ return true;
364
+ }
365
+
351
366
  export function getWorkflowTransportSupportError(
352
367
  provider: string | undefined,
353
368
  requiredTools: string[],
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
2
+ import { mkdirSync } from "node:fs";
2
3
 
3
4
  export default function auditCommand(pi: ExtensionAPI) {
4
5
  pi.registerCommand("audit", {
@@ -39,7 +40,7 @@ export default function auditCommand(pi: ExtensionAPI) {
39
40
 
40
41
  // ── Step 3: Ensure the output directory exists ───────────────────────
41
42
 
42
- await pi.exec("mkdir", ["-p", ".gsd/audits"]);
43
+ mkdirSync(".gsd/audits", { recursive: true });
43
44
 
44
45
  // ── Step 4: Send the audit prompt to the agent ───────────────────────
45
46
 
@@ -53,8 +53,10 @@ interface Baseline {
53
53
  // Directory helpers
54
54
  // ============================================================================
55
55
 
56
- function encodeCwd(cwd: string): string {
57
- return cwd.replace(/\//g, "--");
56
+ export function encodeCwd(cwd: string): string {
57
+ // Encode the entire cwd so Windows drive letters, separators, and UNC
58
+ // prefixes cannot leak into the isolation path.
59
+ return Buffer.from(cwd, "utf8").toString("base64url");
58
60
  }
59
61
 
60
62
  const gsdHome = process.env.GSD_HOME || path.join(os.homedir(), ".gsd");
@@ -500,4 +502,3 @@ export function readIsolationMode(): IsolationMode {
500
502
  return "none";
501
503
  }
502
504
  }
503
-