gsd-pi 2.44.0-dev.0b97ffd → 2.44.0-dev.73f2fd5

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 (173) hide show
  1. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  2. package/dist/resources/extensions/gsd/auto/phases.js +36 -36
  3. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  4. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  5. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  6. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  7. package/dist/resources/extensions/gsd/auto.js +30 -3
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  9. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  10. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  11. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  12. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  13. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  14. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  15. package/dist/resources/extensions/gsd/doctor.js +8 -0
  16. package/dist/resources/extensions/gsd/git-service.js +8 -3
  17. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  18. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  19. package/dist/resources/extensions/gsd/preferences.js +9 -1
  20. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  21. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  22. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  23. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  24. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  25. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  26. package/dist/resources/extensions/gsd/state.js +19 -2
  27. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  28. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  29. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  30. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  31. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  32. package/dist/resources/extensions/mcp-client/index.js +14 -0
  33. package/dist/web/standalone/.next/BUILD_ID +1 -1
  34. package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
  35. package/dist/web/standalone/.next/build-manifest.json +2 -2
  36. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  37. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  38. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.html +1 -1
  54. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
  61. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  62. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  63. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  64. package/package.json +1 -1
  65. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  66. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  68. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  70. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  71. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  72. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  73. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  74. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  75. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  76. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  78. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  80. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  82. package/packages/pi-coding-agent/dist/main.js +17 -0
  83. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  88. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  89. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  91. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  93. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  94. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  95. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  97. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  109. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  112. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  113. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  114. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  115. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  116. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  117. package/packages/pi-coding-agent/src/main.ts +19 -0
  118. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  119. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  120. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  121. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  122. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  123. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  124. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  125. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  126. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  127. package/src/resources/extensions/gsd/auto/phases.ts +45 -48
  128. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  129. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  130. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  131. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  132. package/src/resources/extensions/gsd/auto.ts +37 -3
  133. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  134. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  135. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  136. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  137. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  138. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  139. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  140. package/src/resources/extensions/gsd/doctor.ts +7 -1
  141. package/src/resources/extensions/gsd/git-service.ts +6 -2
  142. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  143. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  144. package/src/resources/extensions/gsd/preferences.ts +11 -1
  145. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  146. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  147. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  148. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  149. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  150. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  151. package/src/resources/extensions/gsd/state.ts +19 -1
  152. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  153. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  154. package/src/resources/extensions/gsd/tests/db-writer.test.ts +79 -0
  155. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +60 -0
  156. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  157. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  158. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  159. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  160. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  161. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  162. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  163. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  164. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  165. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +2 -1
  166. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  167. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  168. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  169. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  170. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  171. package/src/resources/extensions/mcp-client/index.ts +20 -0
  172. /package/dist/web/standalone/.next/static/{alS4hoANx0TK4UVZY27da → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
  173. /package/dist/web/standalone/.next/static/{alS4hoANx0TK4UVZY27da → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
@@ -45,18 +45,9 @@ export async function buildBeforeAgentStartResult(event, ctx) {
45
45
  ctx.ui.notify(`GSD skill preferences: ${report.warnings.length} unresolved skill${report.warnings.length === 1 ? "" : "s"}: ${report.warnings.join(", ")}`, "warning");
46
46
  }
47
47
  }
48
- let knowledgeBlock = "";
49
- const knowledgePath = resolveGsdRootFile(process.cwd(), "KNOWLEDGE");
50
- if (existsSync(knowledgePath)) {
51
- try {
52
- const content = readFileSync(knowledgePath, "utf-8").trim();
53
- if (content) {
54
- knowledgeBlock = `\n\n[PROJECT KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${content}`;
55
- }
56
- }
57
- catch {
58
- // skip
59
- }
48
+ const { block: knowledgeBlock, globalSizeKb } = loadKnowledgeBlock(gsdHome, process.cwd());
49
+ if (globalSizeKb > 4) {
50
+ ctx.ui.notify(`GSD: ~/.gsd/agent/KNOWLEDGE.md is ${globalSizeKb.toFixed(1)}KB — consider trimming to keep system prompt lean.`, "warning");
60
51
  }
61
52
  let memoryBlock = "";
62
53
  try {
@@ -102,6 +93,49 @@ export async function buildBeforeAgentStartResult(event, ctx) {
102
93
  : {}),
103
94
  };
104
95
  }
96
+ export function loadKnowledgeBlock(gsdHomeDir, cwd) {
97
+ // 1. Global knowledge (~/.gsd/agent/KNOWLEDGE.md) — cross-project, user-maintained
98
+ let globalKnowledge = "";
99
+ let globalSizeKb = 0;
100
+ const globalKnowledgePath = join(gsdHomeDir, "agent", "KNOWLEDGE.md");
101
+ if (existsSync(globalKnowledgePath)) {
102
+ try {
103
+ const content = readFileSync(globalKnowledgePath, "utf-8").trim();
104
+ if (content) {
105
+ globalSizeKb = Buffer.byteLength(content, "utf-8") / 1024;
106
+ globalKnowledge = content;
107
+ }
108
+ }
109
+ catch {
110
+ // skip
111
+ }
112
+ }
113
+ // 2. Project knowledge (.gsd/KNOWLEDGE.md) — project-specific
114
+ let projectKnowledge = "";
115
+ const knowledgePath = resolveGsdRootFile(cwd, "KNOWLEDGE");
116
+ if (existsSync(knowledgePath)) {
117
+ try {
118
+ const content = readFileSync(knowledgePath, "utf-8").trim();
119
+ if (content)
120
+ projectKnowledge = content;
121
+ }
122
+ catch {
123
+ // skip
124
+ }
125
+ }
126
+ if (!globalKnowledge && !projectKnowledge) {
127
+ return { block: "", globalSizeKb: 0 };
128
+ }
129
+ const parts = [];
130
+ if (globalKnowledge)
131
+ parts.push(`## Global Knowledge\n\n${globalKnowledge}`);
132
+ if (projectKnowledge)
133
+ parts.push(`## Project Knowledge\n\n${projectKnowledge}`);
134
+ return {
135
+ block: `\n\n[KNOWLEDGE — Rules, patterns, and lessons learned]\n\n${parts.join("\n\n")}`,
136
+ globalSizeKb,
137
+ };
138
+ }
105
139
  function buildWorktreeContextBlock() {
106
140
  const worktreeName = getActiveWorktreeName();
107
141
  const worktreeMainCwd = getWorktreeOriginalCwd();
@@ -4,7 +4,7 @@ import { join } from "node:path";
4
4
  import { loadRegistry } from "../workflow-templates.js";
5
5
  import { resolveProjectRoot } from "../worktree.js";
6
6
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
7
- export const GSD_COMMAND_DESCRIPTION = "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast";
7
+ export const GSD_COMMAND_DESCRIPTION = "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp";
8
8
  export const TOP_LEVEL_SUBCOMMANDS = [
9
9
  { cmd: "help", desc: "Categorized command reference with descriptions" },
10
10
  { cmd: "next", desc: "Explicit step mode (same as /gsd)" },
@@ -56,6 +56,7 @@ export const TOP_LEVEL_SUBCOMMANDS = [
56
56
  { cmd: "templates", desc: "List available workflow templates" },
57
57
  { cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
58
58
  { cmd: "fast", desc: "Toggle OpenAI service tier (on/off/flex/status)" },
59
+ { cmd: "mcp", desc: "MCP server status and connectivity check (status, check <server>)" },
59
60
  { cmd: "workflow", desc: "Custom workflow lifecycle (new, run, list, validate, pause, resume)" },
60
61
  ];
61
62
  const NESTED_COMPLETIONS = {
@@ -174,6 +175,10 @@ const NESTED_COMPLETIONS = {
174
175
  { cmd: "flex", desc: "Flex tier (0.5x cost, slower)" },
175
176
  { cmd: "status", desc: "Show current service tier setting" },
176
177
  ],
178
+ mcp: [
179
+ { cmd: "status", desc: "Show all MCP server statuses (default)" },
180
+ { cmd: "check", desc: "Detailed status for a specific server" },
181
+ ],
177
182
  doctor: [
178
183
  { cmd: "fix", desc: "Auto-fix detected issues" },
179
184
  { cmd: "heal", desc: "AI-driven deep healing" },
@@ -49,6 +49,7 @@ export function showHelp(ctx) {
49
49
  " /gsd hooks Show post-unit hook configuration",
50
50
  " /gsd extensions Manage extensions [list|enable|disable|info]",
51
51
  " /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
52
+ " /gsd mcp MCP server status and connectivity [status|check <server>]",
52
53
  "",
53
54
  "MAINTENANCE",
54
55
  " /gsd doctor Diagnose and repair .gsd/ state [audit|fix|heal] [scope]",
@@ -188,6 +188,11 @@ Examples:
188
188
  await handleFast(trimmed.replace(/^fast\s*/, "").trim(), ctx);
189
189
  return true;
190
190
  }
191
+ if (trimmed === "mcp" || trimmed.startsWith("mcp ")) {
192
+ const { handleMcpStatus } = await import("../../commands-mcp-status.js");
193
+ await handleMcpStatus(trimmed.replace(/^mcp\s*/, "").trim(), ctx);
194
+ return true;
195
+ }
191
196
  if (trimmed === "extensions" || trimmed.startsWith("extensions ")) {
192
197
  const { handleExtensions } = await import("../../commands-extensions.js");
193
198
  await handleExtensions(trimmed.replace(/^extensions\s*/, "").trim(), ctx);
@@ -0,0 +1,187 @@
1
+ /**
2
+ * MCP Status — `/gsd mcp` command handler.
3
+ *
4
+ * Shows configured MCP servers, their connection status, and available tools.
5
+ *
6
+ * Subcommands:
7
+ * /gsd mcp — Overview of all servers (alias: /gsd mcp status)
8
+ * /gsd mcp status — Same as bare /gsd mcp
9
+ * /gsd mcp check <srv> — Detailed status for a specific server
10
+ */
11
+ import { existsSync, readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ function readMcpConfigs() {
14
+ const servers = [];
15
+ const seen = new Set();
16
+ const configPaths = [
17
+ join(process.cwd(), ".mcp.json"),
18
+ join(process.cwd(), ".gsd", "mcp.json"),
19
+ ];
20
+ for (const configPath of configPaths) {
21
+ try {
22
+ if (!existsSync(configPath))
23
+ continue;
24
+ const raw = readFileSync(configPath, "utf-8");
25
+ const data = JSON.parse(raw);
26
+ const mcpServers = (data.mcpServers ?? data.servers);
27
+ if (!mcpServers || typeof mcpServers !== "object")
28
+ continue;
29
+ for (const [name, config] of Object.entries(mcpServers)) {
30
+ if (seen.has(name))
31
+ continue;
32
+ seen.add(name);
33
+ const hasCommand = typeof config.command === "string";
34
+ const hasUrl = typeof config.url === "string";
35
+ const transport = hasCommand
36
+ ? "stdio"
37
+ : hasUrl
38
+ ? "http"
39
+ : "unknown";
40
+ servers.push({
41
+ name,
42
+ transport,
43
+ ...(hasCommand && {
44
+ command: config.command,
45
+ args: Array.isArray(config.args) ? config.args : undefined,
46
+ }),
47
+ ...(hasUrl && { url: config.url }),
48
+ });
49
+ }
50
+ }
51
+ catch {
52
+ // Non-fatal — config file may not exist or be malformed
53
+ }
54
+ }
55
+ return servers;
56
+ }
57
+ // ─── Formatters (exported for testing) ──────────────────────────────────────
58
+ export function formatMcpStatusReport(servers) {
59
+ if (servers.length === 0) {
60
+ return [
61
+ "No MCP servers configured.",
62
+ "",
63
+ "Add servers to .mcp.json or .gsd/mcp.json to enable MCP integrations.",
64
+ "See: https://modelcontextprotocol.io/quickstart",
65
+ ].join("\n");
66
+ }
67
+ const lines = [`MCP Server Status — ${servers.length} server(s)\n`];
68
+ for (const s of servers) {
69
+ const icon = s.error ? "✗" : s.connected ? "✓" : "○";
70
+ const status = s.error
71
+ ? `error: ${s.error}`
72
+ : s.connected
73
+ ? `connected — ${s.toolCount} tools`
74
+ : "disconnected";
75
+ lines.push(` ${icon} ${s.name} (${s.transport}) — ${status}`);
76
+ }
77
+ lines.push("");
78
+ lines.push("Use /gsd mcp check <server> for details on a specific server.");
79
+ lines.push("Use mcp_discover to connect and list tools for a server.");
80
+ return lines.join("\n");
81
+ }
82
+ export function formatMcpServerDetail(server) {
83
+ const lines = [`MCP Server: ${server.name}\n`];
84
+ lines.push(` Transport: ${server.transport}`);
85
+ if (server.error) {
86
+ lines.push(` Status: error`);
87
+ lines.push(` Error: ${server.error}`);
88
+ }
89
+ else if (server.connected) {
90
+ lines.push(` Status: connected`);
91
+ lines.push(` Tools: ${server.toolCount}`);
92
+ if (server.tools.length > 0) {
93
+ lines.push("");
94
+ lines.push(" Available tools:");
95
+ for (const tool of server.tools) {
96
+ lines.push(` - ${tool}`);
97
+ }
98
+ }
99
+ }
100
+ else {
101
+ lines.push(` Status: disconnected`);
102
+ lines.push("");
103
+ lines.push(` Run mcp_discover("${server.name}") to connect and list tools.`);
104
+ }
105
+ return lines.join("\n");
106
+ }
107
+ // ─── Command handler ────────────────────────────────────────────────────────
108
+ /**
109
+ * Handle `/gsd mcp [status|check <server>]`.
110
+ */
111
+ export async function handleMcpStatus(args, ctx) {
112
+ const trimmed = args.trim().toLowerCase();
113
+ const configs = readMcpConfigs();
114
+ // /gsd mcp check <server>
115
+ if (trimmed.startsWith("check ")) {
116
+ const serverName = args.trim().slice("check ".length).trim();
117
+ const config = configs.find((c) => c.name === serverName);
118
+ if (!config) {
119
+ const available = configs.map((c) => c.name).join(", ") || "(none)";
120
+ ctx.ui.notify(`Unknown MCP server: "${serverName}"\n\nAvailable: ${available}`, "warning");
121
+ return;
122
+ }
123
+ // Try to get connection/tool info from the mcp-client module if available
124
+ let connected = false;
125
+ let toolNames = [];
126
+ let error;
127
+ try {
128
+ const mcpClient = await import("../mcp-client/index.js");
129
+ // Access the module's connection state if exported; fall back gracefully
130
+ const mod = mcpClient;
131
+ if (typeof mod.getConnectionStatus === "function") {
132
+ const status = mod.getConnectionStatus(serverName);
133
+ connected = status.connected;
134
+ toolNames = status.tools;
135
+ error = status.error;
136
+ }
137
+ }
138
+ catch {
139
+ // mcp-client may not expose status helpers — that's fine
140
+ }
141
+ ctx.ui.notify(formatMcpServerDetail({
142
+ name: config.name,
143
+ transport: config.transport,
144
+ connected,
145
+ toolCount: toolNames.length,
146
+ tools: toolNames,
147
+ error,
148
+ }), "info");
149
+ return;
150
+ }
151
+ // /gsd mcp or /gsd mcp status
152
+ if (!trimmed || trimmed === "status") {
153
+ // Build status for each server
154
+ const statuses = [];
155
+ for (const config of configs) {
156
+ let connected = false;
157
+ let toolCount = 0;
158
+ let error;
159
+ try {
160
+ const mcpClient = await import("../mcp-client/index.js");
161
+ const mod = mcpClient;
162
+ if (typeof mod.getConnectionStatus === "function") {
163
+ const status = mod.getConnectionStatus(config.name);
164
+ connected = status.connected;
165
+ toolCount = status.tools.length;
166
+ error = status.error;
167
+ }
168
+ }
169
+ catch {
170
+ // Fall back to unknown state
171
+ }
172
+ statuses.push({
173
+ name: config.name,
174
+ transport: config.transport,
175
+ connected,
176
+ toolCount,
177
+ error,
178
+ });
179
+ }
180
+ ctx.ui.notify(formatMcpStatusReport(statuses), "info");
181
+ return;
182
+ }
183
+ // Unknown subcommand
184
+ ctx.ui.notify("Usage: /gsd mcp [status|check <server>]\n\n" +
185
+ " status Show all MCP server statuses (default)\n" +
186
+ " check <server> Detailed status for a specific server", "warning");
187
+ }
@@ -8,7 +8,7 @@
8
8
  // Critical invariant: generated markdown must round-trip through
9
9
  // parseDecisionsTable() and parseRequirementsSections() with field fidelity.
10
10
  import { resolve } from 'node:path';
11
- import { readFileSync, existsSync } from 'node:fs';
11
+ import { readFileSync, existsSync, statSync } from 'node:fs';
12
12
  import { resolveGsdRootFile } from './paths.js';
13
13
  import { saveFile } from './files.js';
14
14
  import { GSDError, GSD_STALE_STATE, GSD_IO_ERROR } from './errors.js';
@@ -356,28 +356,46 @@ export async function updateRequirementInDb(id, updates, basePath) {
356
356
  export async function saveArtifactToDb(opts, basePath) {
357
357
  try {
358
358
  const db = await import('./gsd-db.js');
359
+ // Guard against path traversal before any reads/writes
360
+ const gsdDir = resolve(basePath, '.gsd');
361
+ const fullPath = resolve(basePath, '.gsd', opts.path);
362
+ if (!fullPath.startsWith(gsdDir)) {
363
+ throw new GSDError(GSD_IO_ERROR, `saveArtifactToDb: path escapes .gsd/ directory: ${opts.path}`);
364
+ }
365
+ // Shrinkage guard: if the file already exists and the new content is
366
+ // significantly smaller (<50%), preserve the richer file on disk and
367
+ // store its content in the DB instead of the abbreviated version.
368
+ let dbContent = opts.content;
369
+ let skipDiskWrite = false;
370
+ if (existsSync(fullPath)) {
371
+ const existingSize = statSync(fullPath).size;
372
+ const newSize = Buffer.byteLength(opts.content, 'utf-8');
373
+ if (existingSize > 0 && newSize < existingSize * 0.5) {
374
+ process.stderr.write(`gsd-db: saveArtifactToDb — new content (${newSize}B) is <50% of existing file ` +
375
+ `(${existingSize}B) at ${opts.path}. Preserving disk file to prevent data loss.\n`);
376
+ dbContent = readFileSync(fullPath, 'utf-8');
377
+ skipDiskWrite = true;
378
+ }
379
+ }
359
380
  db.insertArtifact({
360
381
  path: opts.path,
361
382
  artifact_type: opts.artifact_type,
362
383
  milestone_id: opts.milestone_id ?? null,
363
384
  slice_id: opts.slice_id ?? null,
364
385
  task_id: opts.task_id ?? null,
365
- full_content: opts.content,
386
+ full_content: dbContent,
366
387
  });
367
- // Write the file to disk (guard against path traversal)
368
- const gsdDir = resolve(basePath, '.gsd');
369
- const fullPath = resolve(basePath, '.gsd', opts.path);
370
- if (!fullPath.startsWith(gsdDir)) {
371
- throw new GSDError(GSD_IO_ERROR, `saveArtifactToDb: path escapes .gsd/ directory: ${opts.path}`);
372
- }
373
- try {
374
- await saveFile(fullPath, opts.content);
375
- }
376
- catch (diskErr) {
377
- process.stderr.write(`gsd-db: saveArtifactToDb — disk write failed, rolling back DB row: ${diskErr.message}\n`);
378
- const rollbackAdapter = db._getAdapter();
379
- rollbackAdapter?.prepare('DELETE FROM artifacts WHERE path = :path').run({ ':path': opts.path });
380
- throw diskErr;
388
+ // Write the file to disk (only if we're not preserving a richer existing file)
389
+ if (!skipDiskWrite) {
390
+ try {
391
+ await saveFile(fullPath, opts.content);
392
+ }
393
+ catch (diskErr) {
394
+ process.stderr.write(`gsd-db: saveArtifactToDb — disk write failed, rolling back DB row: ${diskErr.message}\n`);
395
+ const rollbackAdapter = db._getAdapter();
396
+ rollbackAdapter?.prepare('DELETE FROM artifacts WHERE path = :path').run({ ':path': opts.path });
397
+ throw diskErr;
398
+ }
381
399
  }
382
400
  // Invalidate file-read caches so deriveState() sees the updated markdown.
383
401
  // Do NOT clear the artifacts table — we just wrote to it intentionally.
@@ -441,6 +441,7 @@ export async function runGSDDoctor(basePath, options) {
441
441
  id: s.id,
442
442
  title: s.title,
443
443
  done: s.status === "complete",
444
+ pending: s.status === "pending",
444
445
  risk: (s.risk || "medium"),
445
446
  depends: s.depends,
446
447
  demo: s.demo,
@@ -528,6 +529,10 @@ export async function runGSDDoctor(basePath, options) {
528
529
  }
529
530
  const slicePath = resolveSlicePath(basePath, milestoneId, slice.id);
530
531
  if (!slicePath) {
532
+ // Pending slices haven't been planned yet — directories are created
533
+ // lazily by ensurePreconditions() at dispatch time. Skip them.
534
+ if (slice.pending)
535
+ continue;
531
536
  const expectedPath = relSlicePath(basePath, milestoneId, slice.id);
532
537
  issues.push({
533
538
  severity: slice.done ? "warning" : "error",
@@ -549,6 +554,9 @@ export async function runGSDDoctor(basePath, options) {
549
554
  }
550
555
  const tasksDir = resolveTasksDir(basePath, milestoneId, slice.id);
551
556
  if (!tasksDir) {
557
+ // Pending slices haven't been planned yet — tasks/ is created on demand.
558
+ if (slice.pending)
559
+ continue;
552
560
  issues.push({
553
561
  severity: slice.done ? "warning" : "error",
554
562
  code: "missing_tasks_dir",
@@ -510,13 +510,18 @@ export class GitServiceImpl {
510
510
  * Returns the PR URL on success, or null on failure.
511
511
  * Non-fatal: callers should treat failure as best-effort.
512
512
  */
513
- export function createDraftPR(basePath, milestoneId, title, body) {
513
+ export function createDraftPR(basePath, milestoneId, title, body, opts) {
514
514
  try {
515
- const result = execFileSync("gh", [
515
+ const args = [
516
516
  "pr", "create", "--draft",
517
517
  "--title", title,
518
518
  "--body", body,
519
- ], { cwd: basePath, encoding: "utf8", timeout: 30000, env: GIT_NO_PROMPT_ENV });
519
+ ];
520
+ if (opts?.head)
521
+ args.push("--head", opts.head);
522
+ if (opts?.base)
523
+ args.push("--base", opts.base);
524
+ const result = execFileSync("gh", args, { cwd: basePath, encoding: "utf8", timeout: 30000, env: GIT_NO_PROMPT_ENV });
520
525
  return result.trim();
521
526
  }
522
527
  catch {
@@ -253,6 +253,7 @@ function initSchema(db, fileBacked) {
253
253
  inputs TEXT NOT NULL DEFAULT '[]',
254
254
  expected_output TEXT NOT NULL DEFAULT '[]',
255
255
  observability_impact TEXT NOT NULL DEFAULT '',
256
+ full_plan_md TEXT NOT NULL DEFAULT '',
256
257
  sequence INTEGER DEFAULT 0, -- DEAD CODE: no tool exposes sequence — always 0
257
258
  PRIMARY KEY (milestone_id, slice_id, id),
258
259
  FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
@@ -542,6 +543,13 @@ function migrateSchema(db) {
542
543
  ":applied_at": new Date().toISOString(),
543
544
  });
544
545
  }
546
+ if (currentVersion < 11) {
547
+ ensureColumn(db, "tasks", "full_plan_md", `ALTER TABLE tasks ADD COLUMN full_plan_md TEXT NOT NULL DEFAULT ''`);
548
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
549
+ ":version": 11,
550
+ ":applied_at": new Date().toISOString(),
551
+ });
552
+ }
545
553
  db.exec("COMMIT");
546
554
  }
547
555
  catch (err) {
@@ -997,7 +1005,8 @@ export function upsertTaskPlanning(milestoneId, sliceId, taskId, planning) {
997
1005
  verify = COALESCE(:verify, verify),
998
1006
  inputs = COALESCE(:inputs, inputs),
999
1007
  expected_output = COALESCE(:expected_output, expected_output),
1000
- observability_impact = COALESCE(:observability_impact, observability_impact)
1008
+ observability_impact = COALESCE(:observability_impact, observability_impact),
1009
+ full_plan_md = COALESCE(:full_plan_md, full_plan_md)
1001
1010
  WHERE milestone_id = :milestone_id AND slice_id = :slice_id AND id = :id`).run({
1002
1011
  ":milestone_id": milestoneId,
1003
1012
  ":slice_id": sliceId,
@@ -1010,6 +1019,7 @@ export function upsertTaskPlanning(milestoneId, sliceId, taskId, planning) {
1010
1019
  ":inputs": planning.inputs ? JSON.stringify(planning.inputs) : null,
1011
1020
  ":expected_output": planning.expectedOutput ? JSON.stringify(planning.expectedOutput) : null,
1012
1021
  ":observability_impact": planning.observabilityImpact ?? null,
1022
+ ":full_plan_md": planning.fullPlanMd ?? null,
1013
1023
  });
1014
1024
  }
1015
1025
  function rowToSlice(row) {
@@ -1078,6 +1088,7 @@ function rowToTask(row) {
1078
1088
  inputs: JSON.parse(row["inputs"] || "[]"),
1079
1089
  expected_output: JSON.parse(row["expected_output"] || "[]"),
1080
1090
  observability_impact: row["observability_impact"] ?? "",
1091
+ full_plan_md: row["full_plan_md"] ?? "",
1081
1092
  sequence: row["sequence"] ?? 0,
1082
1093
  };
1083
1094
  }
@@ -298,7 +298,7 @@ export async function renderTaskPlanFromDb(basePath, milestoneId, sliceId, taskI
298
298
  mkdirSync(tasksDir, { recursive: true });
299
299
  const absPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
300
300
  const artifactPath = toArtifactPath(absPath, basePath);
301
- const content = renderTaskPlanMarkdown(task);
301
+ const content = task.full_plan_md.trim() ? task.full_plan_md : renderTaskPlanMarkdown(task);
302
302
  await writeAndStore(absPath, artifactPath, content, {
303
303
  artifact_type: "PLAN",
304
304
  milestone_id: milestoneId,
@@ -125,6 +125,11 @@ function loadPreferencesFile(path, scope) {
125
125
  ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}),
126
126
  };
127
127
  }
128
+ let _warnedUnrecognizedFormat = false;
129
+ /** @internal Reset the warn-once flag — exported for testing only. */
130
+ export function _resetParseWarningFlag() {
131
+ _warnedUnrecognizedFormat = false;
132
+ }
128
133
  /** @internal Exported for testing only */
129
134
  export function parsePreferencesMarkdown(content) {
130
135
  // Use indexOf instead of [\s\S]*? regex to avoid backtracking (#468)
@@ -142,7 +147,10 @@ export function parsePreferencesMarkdown(content) {
142
147
  if (/^##\s+\w/m.test(content)) {
143
148
  return parseHeadingListFormat(content);
144
149
  }
145
- console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
150
+ if (!_warnedUnrecognizedFormat) {
151
+ _warnedUnrecognizedFormat = true;
152
+ console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
153
+ }
146
154
  return null;
147
155
  }
148
156
  function parseFrontmatterBlock(frontmatter) {
@@ -21,8 +21,8 @@ Then:
21
21
  4. 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.
22
22
  5. Verify the milestone's **definition of done** — all slices are `[x]`, all slice summaries exist, and any cross-slice integration points work correctly.
23
23
  6. 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.
24
- 7. 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`.
25
- 8. Update `.gsd/REQUIREMENTS.md` if any requirement status transitions were validated in step 5.
24
+ 7. **Persist completion through `gsd_complete_milestone`.** Call it with: `milestoneId`, `title`, `oneLiner`, `narrative`, `successCriteriaResults`, `definitionOfDoneResults`, `requirementOutcomes`, `keyDecisions`, `keyFiles`, `lessonsLearned`, `followUps`, `deviations`. The tool updates the milestone status in the DB, renders `{{milestoneSummaryPath}}`, and validates all slices are complete before proceeding.
25
+ 8. Update `.gsd/REQUIREMENTS.md` if any requirement status transitions were validated in step 6.
26
26
  9. Update `.gsd/PROJECT.md` to reflect milestone completion and current project state.
27
27
  10. Review all slice summaries for cross-cutting lessons, patterns, or gotchas that emerged during this milestone. Append any non-obvious, reusable insights to `.gsd/KNOWLEDGE.md`.
28
28
  11. Do not commit manually — the system auto-commits your changes after this unit completes.
@@ -31,6 +31,4 @@ Then:
31
31
 
32
32
  **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
33
33
 
34
- **You MUST write `{{milestoneSummaryPath}}` AND update PROJECT.md before finishing.**
35
-
36
34
  When done, say: "Milestone {{milestoneId}} complete."
@@ -63,7 +63,7 @@ Then:
63
63
  - a matching task plan file with description, steps, must-haves, verification, inputs, and expected output
64
64
  - **Inputs and Expected Output must list concrete backtick-wrapped file paths** (e.g. `` `src/types.ts` ``). These are machine-parsed to derive task dependencies — vague prose without paths breaks parallel execution. Every task must have at least one output file path.
65
65
  - Observability Impact section **only if the task touches runtime boundaries, async flows, or error paths** — omit it otherwise
66
- 6. **Persist planning state through DB-backed tools.** Call `gsd_plan_slice` with the full slice planning payload (goal, demo, must-haves, verification, tasks, and metadata). Then call `gsd_plan_task` for each task to persist its planning fields. These tools write to the DB and render `{{outputPath}}` and `{{slicePath}}/tasks/T##-PLAN.md` files automatically. Do **not** rely on direct `PLAN.md` writes as the source of truth; the DB-backed tools are the canonical write path for slice and task planning state.
66
+ 6. **Persist planning state through `gsd_plan_slice`.** Call it with the full slice planning payload (goal, demo, must-haves, verification, tasks, and metadata). The tool inserts all tasks in the same transaction, writes to the DB, and renders `{{outputPath}}` and `{{slicePath}}/tasks/T##-PLAN.md` files automatically. Do **not** call `gsd_plan_task` separately — `gsd_plan_slice` handles task persistence. Do **not** rely on direct `PLAN.md` writes as the source of truth; the DB-backed tool is the canonical write path for slice and task planning state.
67
67
  7. **Self-audit the plan.** Walk through each check — if any fail, fix the plan files before moving on:
68
68
  - **Completion semantics:** If every task were completed exactly as written, the slice goal/demo should actually be true.
69
69
  - **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.
@@ -50,14 +50,14 @@ If all criteria have at least one remaining owning slice, the coverage check pas
50
50
 
51
51
  **If the roadmap is still good:**
52
52
 
53
- Write `{{assessmentPath}}` with a brief confirmation that roadmap coverage still holds after {{completedSliceId}}. If requirements exist, explicitly note whether requirement coverage remains sound. If `gsd_reassess_roadmap` is available, use it with `verdict: "roadmap-confirmed"`, an empty `sliceChanges` object, and the assessment text — the tool writes the assessment to the DB and renders ASSESSMENT.md.
53
+ Use `gsd_reassess_roadmap` with `verdict: "roadmap-confirmed"`, an empty `sliceChanges` object, and the assessment text — the tool writes the assessment to the DB and renders `{{assessmentPath}}`. If requirements exist, explicitly note whether requirement coverage remains sound.
54
54
 
55
55
  **If changes are needed:**
56
56
 
57
- 1. **Persist changes through `gsd_reassess_roadmap`.** Pass: `milestoneId`, `completedSliceId`, `verdict` (e.g. "roadmap-adjusted"), `assessment` (text explaining the decision), and `sliceChanges` with `modified` (array of sliceId, title, risk, depends, demo), `added` (same shape), `removed` (array of slice ID strings). The tool structurally enforces preservation of completed slices, writes the assessment to the DB, re-renders ROADMAP.md, and renders ASSESSMENT.md. Skip step 2 when this tool succeeds.
58
- 2. **Degraded fallback — direct file writes:** If `gsd_reassess_roadmap` is not available, rewrite the remaining (unchecked) slices in `{{roadmapPath}}` directly. 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.
59
- 3. Write `{{assessmentPath}}` explaining what changed and why keep it brief and concrete.
60
- 4. If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, update it.
61
- 5. {{commitInstruction}}
57
+ **Persist changes through `gsd_reassess_roadmap`.** Pass: `milestoneId`, `completedSliceId`, `verdict` (e.g. "roadmap-adjusted"), `assessment` (text explaining the decision), and `sliceChanges` with `modified` (array of sliceId, title, risk, depends, demo), `added` (same shape), `removed` (array of slice ID strings). The tool structurally enforces preservation of completed slices, writes the assessment to the DB, re-renders `{{roadmapPath}}`, and renders `{{assessmentPath}}`.
58
+
59
+ If `.gsd/REQUIREMENTS.md` exists and requirement ownership or status changed, update it.
60
+
61
+ {{commitInstruction}}
62
62
 
63
63
  When done, say: "Roadmap reassessed."
@@ -32,19 +32,8 @@ Consider these captures when rewriting the remaining tasks — they represent th
32
32
 
33
33
  1. Read the blocker task summary carefully. Understand exactly what was discovered and why it blocks the current plan.
34
34
  2. Analyze the remaining `[ ]` tasks in the slice plan. Determine which are still valid, which need modification, and which should be replaced.
35
- 3. **Persist replan state through `gsd_replan_slice`.** Call it with the following parameters: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders PLAN.md, and renders REPLAN.md. Skip steps 4–5 when this tool succeeds.
36
- 4. **Degraded fallback direct file writes:** If `gsd_replan_slice` is not available, fall back to writing files directly. Write `{{replanPath}}` documenting:
37
- - What blocker was discovered and in which task
38
- - What changed in the plan and why
39
- - Which incomplete tasks were modified, added, or removed
40
- - Any new risks or considerations introduced by the replan
41
- 5. If using the degraded fallback, rewrite `{{planPath}}` with the updated slice plan:
42
- - Keep all `[x]` tasks exactly as they were (same IDs, same descriptions, same checkmarks)
43
- - Update the `[ ]` tasks to address the blocker
44
- - Ensure the slice Goal and Demo sections are still achievable with the new tasks, or update them if the blocker fundamentally changes what the slice can deliver
45
- - Update the Files Likely Touched section if the replan changes which files are affected
46
- - If a DB-backed planning tool exists for this phase, use it as the source of truth and make any rewritten `PLAN.md` reflect that persisted state rather than bypassing it
47
- 6. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
48
- 7. Do not commit manually — the system auto-commits your changes after this unit completes.
35
+ 3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`.
36
+ 4. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
37
+ 5. Do not commit manually the system auto-commits your changes after this unit completes.
49
38
 
50
39
  When done, say: "Slice {{sliceId}} replanned."