gsd-pi 2.15.1 → 2.17.0

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 (161) hide show
  1. package/README.md +1 -0
  2. package/dist/resources/extensions/async-jobs/async-bash-tool.ts +2 -1
  3. package/dist/resources/extensions/async-jobs/await-tool.ts +5 -3
  4. package/dist/resources/extensions/async-jobs/cancel-job-tool.ts +2 -1
  5. package/dist/resources/extensions/async-jobs/index.ts +3 -3
  6. package/dist/resources/extensions/gsd/auto-dashboard.ts +7 -0
  7. package/dist/resources/extensions/gsd/auto-dispatch.ts +40 -4
  8. package/dist/resources/extensions/gsd/auto-prompts.ts +156 -43
  9. package/dist/resources/extensions/gsd/auto-recovery.ts +52 -28
  10. package/dist/resources/extensions/gsd/auto-supervisor.ts +2 -7
  11. package/dist/resources/extensions/gsd/auto-worktree.ts +53 -106
  12. package/dist/resources/extensions/gsd/auto.ts +80 -25
  13. package/dist/resources/extensions/gsd/commands.ts +113 -29
  14. package/dist/resources/extensions/gsd/complexity.ts +236 -0
  15. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  16. package/dist/resources/extensions/gsd/doctor.ts +12 -22
  17. package/dist/resources/extensions/gsd/files.ts +160 -4
  18. package/dist/resources/extensions/gsd/git-self-heal.ts +4 -4
  19. package/dist/resources/extensions/gsd/git-service.ts +40 -29
  20. package/dist/resources/extensions/gsd/gitignore.ts +43 -7
  21. package/dist/resources/extensions/gsd/guided-flow.ts +19 -15
  22. package/dist/resources/extensions/gsd/index.ts +10 -3
  23. package/dist/resources/extensions/gsd/metrics.ts +44 -0
  24. package/dist/resources/extensions/gsd/native-git-bridge.ts +848 -12
  25. package/dist/resources/extensions/gsd/native-parser-bridge.ts +133 -1
  26. package/dist/resources/extensions/gsd/paths.ts +89 -0
  27. package/dist/resources/extensions/gsd/preferences.ts +122 -1
  28. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  29. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -0
  30. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +32 -0
  31. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  32. package/dist/resources/extensions/gsd/provider-error-pause.ts +1 -1
  33. package/dist/resources/extensions/gsd/routing-history.ts +290 -0
  34. package/dist/resources/extensions/gsd/session-forensics.ts +29 -12
  35. package/dist/resources/extensions/gsd/state.ts +1 -38
  36. package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +2 -2
  37. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  38. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +1 -1
  39. package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  40. package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  41. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  42. package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  43. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -1
  44. package/dist/resources/extensions/gsd/tests/overrides.test.ts +131 -0
  45. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +33 -5
  46. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +7 -6
  47. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  48. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  49. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  50. package/dist/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +1 -1
  51. package/dist/resources/extensions/gsd/types.ts +28 -0
  52. package/dist/resources/extensions/gsd/undo.ts +24 -18
  53. package/dist/resources/extensions/gsd/worktree-command.ts +2 -2
  54. package/dist/resources/extensions/gsd/worktree-manager.ts +81 -134
  55. package/dist/resources/extensions/gsd/worktree.ts +2 -2
  56. package/package.json +3 -2
  57. package/packages/pi-ai/dist/models.generated.d.ts +493 -13
  58. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  59. package/packages/pi-ai/dist/models.generated.js +422 -62
  60. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  61. package/packages/pi-ai/dist/providers/google-shared.d.ts +12 -0
  62. package/packages/pi-ai/dist/providers/google-shared.d.ts.map +1 -1
  63. package/packages/pi-ai/dist/providers/google-shared.js +9 -22
  64. package/packages/pi-ai/dist/providers/google-shared.js.map +1 -1
  65. package/packages/pi-ai/dist/providers/google-shared.test.d.ts +2 -0
  66. package/packages/pi-ai/dist/providers/google-shared.test.d.ts.map +1 -0
  67. package/packages/pi-ai/dist/providers/google-shared.test.js +125 -0
  68. package/packages/pi-ai/dist/providers/google-shared.test.js.map +1 -0
  69. package/packages/pi-ai/src/models.generated.ts +422 -62
  70. package/packages/pi-ai/src/providers/google-shared.test.ts +137 -0
  71. package/packages/pi-ai/src/providers/google-shared.ts +10 -19
  72. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +1 -1
  73. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  74. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  75. package/packages/pi-coding-agent/dist/core/model-resolver.js +2 -2
  76. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
  78. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
  80. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
  82. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
  83. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
  84. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.js +8 -1
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/custom-editor.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  89. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  90. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
  91. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  92. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  93. package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
  94. package/packages/pi-coding-agent/src/core/model-resolver.ts +2 -2
  95. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
  96. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
  97. package/packages/pi-coding-agent/src/modes/interactive/components/custom-editor.ts +8 -1
  98. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +1 -1
  99. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
  100. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
  101. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  102. package/packages/pi-tui/dist/components/editor.js +4 -2
  103. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  104. package/packages/pi-tui/src/components/editor.ts +5 -3
  105. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  106. package/pkg/dist/modes/interactive/theme/theme.js +10 -0
  107. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  108. package/src/resources/extensions/async-jobs/async-bash-tool.ts +2 -1
  109. package/src/resources/extensions/async-jobs/await-tool.ts +5 -3
  110. package/src/resources/extensions/async-jobs/cancel-job-tool.ts +2 -1
  111. package/src/resources/extensions/async-jobs/index.ts +3 -3
  112. package/src/resources/extensions/gsd/auto-dashboard.ts +7 -0
  113. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -4
  114. package/src/resources/extensions/gsd/auto-prompts.ts +156 -43
  115. package/src/resources/extensions/gsd/auto-recovery.ts +52 -28
  116. package/src/resources/extensions/gsd/auto-supervisor.ts +2 -7
  117. package/src/resources/extensions/gsd/auto-worktree.ts +53 -106
  118. package/src/resources/extensions/gsd/auto.ts +80 -25
  119. package/src/resources/extensions/gsd/commands.ts +113 -29
  120. package/src/resources/extensions/gsd/complexity.ts +236 -0
  121. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  122. package/src/resources/extensions/gsd/doctor.ts +12 -22
  123. package/src/resources/extensions/gsd/files.ts +160 -4
  124. package/src/resources/extensions/gsd/git-self-heal.ts +4 -4
  125. package/src/resources/extensions/gsd/git-service.ts +40 -29
  126. package/src/resources/extensions/gsd/gitignore.ts +43 -7
  127. package/src/resources/extensions/gsd/guided-flow.ts +19 -15
  128. package/src/resources/extensions/gsd/index.ts +10 -3
  129. package/src/resources/extensions/gsd/metrics.ts +44 -0
  130. package/src/resources/extensions/gsd/native-git-bridge.ts +848 -12
  131. package/src/resources/extensions/gsd/native-parser-bridge.ts +133 -1
  132. package/src/resources/extensions/gsd/paths.ts +89 -0
  133. package/src/resources/extensions/gsd/preferences.ts +122 -1
  134. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  135. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -0
  136. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +32 -0
  137. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  138. package/src/resources/extensions/gsd/provider-error-pause.ts +1 -1
  139. package/src/resources/extensions/gsd/routing-history.ts +290 -0
  140. package/src/resources/extensions/gsd/session-forensics.ts +29 -12
  141. package/src/resources/extensions/gsd/state.ts +1 -38
  142. package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +2 -2
  143. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  144. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +1 -1
  145. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  146. package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  147. package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  148. package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  149. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -1
  150. package/src/resources/extensions/gsd/tests/overrides.test.ts +131 -0
  151. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +33 -5
  152. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +7 -6
  153. package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  154. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  155. package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  156. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +1 -1
  157. package/src/resources/extensions/gsd/types.ts +28 -0
  158. package/src/resources/extensions/gsd/undo.ts +24 -18
  159. package/src/resources/extensions/gsd/worktree-command.ts +2 -2
  160. package/src/resources/extensions/gsd/worktree-manager.ts +81 -134
  161. package/src/resources/extensions/gsd/worktree.ts +2 -2
package/README.md CHANGED
@@ -213,6 +213,7 @@ On first run, GSD launches a branded setup wizard that walks you through LLM pro
213
213
  | `/gsd next` | Explicit step mode (same as bare `/gsd`) |
214
214
  | `/gsd auto` | Autonomous mode — researches, plans, executes, commits, repeats |
215
215
  | `/gsd stop` | Stop auto mode gracefully |
216
+ | `/gsd steer` | Hard-steer plan documents during execution |
216
217
  | `/gsd discuss` | Discuss architecture and decisions (works alongside auto mode) |
217
218
  | `/gsd status` | Progress dashboard |
218
219
  | `/gsd queue` | Queue future milestones (safe during auto mode) |
@@ -71,7 +71,7 @@ export function createAsyncBashTool(
71
71
  "Check /jobs to see all running and recent background jobs.",
72
72
  ],
73
73
  parameters: schema,
74
- async execute(_toolCallId, params) {
74
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
75
75
  const manager = getManager();
76
76
  const cwd = getCwd();
77
77
  const { command, timeout, label } = params;
@@ -91,6 +91,7 @@ export function createAsyncBashTool(
91
91
  "Use `await_job` to get results when ready, or `cancel_job` to stop.",
92
92
  ].join("\n"),
93
93
  }],
94
+ details: undefined,
94
95
  };
95
96
  },
96
97
  };
@@ -24,7 +24,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
24
24
  description:
25
25
  "Wait for background jobs to complete. Provide specific job IDs or omit to wait for the next job that finishes. Returns results of completed jobs.",
26
26
  parameters: schema,
27
- async execute(_toolCallId, params) {
27
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
28
28
  const manager = getManager();
29
29
  const { jobs: jobIds } = params;
30
30
 
@@ -43,6 +43,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
43
43
  if (notFound.length > 0 && watched.length === 0) {
44
44
  return {
45
45
  content: [{ type: "text", text: `No jobs found: ${notFound.join(", ")}` }],
46
+ details: undefined,
46
47
  };
47
48
  }
48
49
  } else {
@@ -50,6 +51,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
50
51
  if (watched.length === 0) {
51
52
  return {
52
53
  content: [{ type: "text", text: "No running background jobs." }],
54
+ details: undefined,
53
55
  };
54
56
  }
55
57
  }
@@ -59,7 +61,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
59
61
  if (running.length === 0) {
60
62
  const result = formatResults(watched);
61
63
  manager.acknowledgeDeliveries(watched.map((j) => j.id));
62
- return { content: [{ type: "text", text: result }] };
64
+ return { content: [{ type: "text", text: result }], details: undefined };
63
65
  }
64
66
 
65
67
  // Wait for at least one to complete
@@ -75,7 +77,7 @@ export function createAwaitTool(getManager: () => AsyncJobManager): ToolDefiniti
75
77
  result += `\n\n**Still running:** ${stillRunning.map((j) => `${j.id} (${j.label})`).join(", ")}`;
76
78
  }
77
79
 
78
- return { content: [{ type: "text", text: result }] };
80
+ return { content: [{ type: "text", text: result }], details: undefined };
79
81
  },
80
82
  };
81
83
  }
@@ -16,7 +16,7 @@ export function createCancelJobTool(getManager: () => AsyncJobManager): ToolDefi
16
16
  label: "Cancel Background Job",
17
17
  description: "Cancel a running background job by its ID.",
18
18
  parameters: schema,
19
- async execute(_toolCallId, params) {
19
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
20
20
  const manager = getManager();
21
21
  const result = manager.cancel(params.job_id);
22
22
 
@@ -28,6 +28,7 @@ export function createCancelJobTool(getManager: () => AsyncJobManager): ToolDefi
28
28
 
29
29
  return {
30
30
  content: [{ type: "text", text: messages[result] ?? `Unknown result: ${result}` }],
31
+ details: undefined,
31
32
  };
32
33
  },
33
34
  };
@@ -62,7 +62,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
62
62
  "",
63
63
  truncatedOutput,
64
64
  ].join("\n"),
65
- display: `Background job ${job.id} ${job.status}`,
65
+ display: true,
66
66
  },
67
67
  { deliverAs: "followUp", triggerTurn: true },
68
68
  );
@@ -92,7 +92,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
92
92
  pi.sendMessage({
93
93
  customType: "async_jobs_list",
94
94
  content: "No async job manager active.",
95
- display: "No jobs",
95
+ display: true,
96
96
  });
97
97
  return;
98
98
  }
@@ -126,7 +126,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
126
126
  pi.sendMessage({
127
127
  customType: "async_jobs_list",
128
128
  content: lines.join("\n"),
129
- display: `${running.length} running, ${completed.length} recent`,
129
+ display: true,
130
130
  });
131
131
  },
132
132
  });
@@ -35,6 +35,10 @@ export interface AutoDashboardData {
35
35
  /** Running cost and token totals from metrics ledger */
36
36
  totalCost: number;
37
37
  totalTokens: number;
38
+ /** Projected remaining cost based on unit-type averages (undefined if insufficient data) */
39
+ projectedRemainingCost?: number;
40
+ /** Whether token profile has been auto-downgraded due to budget prediction */
41
+ profileDowngraded?: boolean;
38
42
  }
39
43
 
40
44
  // ─── Unit Description Helpers ─────────────────────────────────────────────────
@@ -49,6 +53,7 @@ export function unitVerb(unitType: string): string {
49
53
  case "execute-task": return "executing";
50
54
  case "complete-slice": return "completing";
51
55
  case "replan-slice": return "replanning";
56
+ case "rewrite-docs": return "rewriting";
52
57
  case "reassess-roadmap": return "reassessing";
53
58
  case "run-uat": return "running UAT";
54
59
  default: return unitType;
@@ -65,6 +70,7 @@ export function unitPhaseLabel(unitType: string): string {
65
70
  case "execute-task": return "EXECUTE";
66
71
  case "complete-slice": return "COMPLETE";
67
72
  case "replan-slice": return "REPLAN";
73
+ case "rewrite-docs": return "REWRITE";
68
74
  case "reassess-roadmap": return "REASSESS";
69
75
  case "run-uat": return "UAT";
70
76
  default: return unitType.toUpperCase();
@@ -88,6 +94,7 @@ function peekNext(unitType: string, state: GSDState): string {
88
94
  case "execute-task": return `continue ${sid}`;
89
95
  case "complete-slice": return "reassess roadmap";
90
96
  case "replan-slice": return `re-execute ${sid}`;
97
+ case "rewrite-docs": return "continue execution";
91
98
  case "reassess-roadmap": return "advance to next slice";
92
99
  case "run-uat": return "reassess roadmap";
93
100
  default: return "";
@@ -12,7 +12,7 @@
12
12
  import type { GSDState } from "./types.js";
13
13
  import type { GSDPreferences } from "./preferences.js";
14
14
  import type { UatType } from "./files.js";
15
- import { loadFile, extractUatType } from "./files.js";
15
+ import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
16
16
  import {
17
17
  resolveMilestoneFile, resolveSliceFile,
18
18
  relSliceFile,
@@ -28,6 +28,7 @@ import {
28
28
  buildReplanSlicePrompt,
29
29
  buildRunUatPrompt,
30
30
  buildReassessRoadmapPrompt,
31
+ buildRewriteDocsPrompt,
31
32
  checkNeedsReassessment,
32
33
  checkNeedsRunUat,
33
34
  } from "./auto-prompts.js";
@@ -54,9 +55,38 @@ interface DispatchRule {
54
55
  match: (ctx: DispatchContext) => Promise<DispatchAction | null>;
55
56
  }
56
57
 
58
+ // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
59
+
60
+ const MAX_REWRITE_ATTEMPTS = 3;
61
+ let rewriteAttemptCount = 0;
62
+ export function resetRewriteCircuitBreaker(): void {
63
+ rewriteAttemptCount = 0;
64
+ }
65
+
57
66
  // ─── Rules ────────────────────────────────────────────────────────────────
58
67
 
59
68
  const DISPATCH_RULES: DispatchRule[] = [
69
+ {
70
+ name: "rewrite-docs (override gate)",
71
+ match: async ({ mid, midTitle, state, basePath }) => {
72
+ const pendingOverrides = await loadActiveOverrides(basePath);
73
+ if (pendingOverrides.length === 0) return null;
74
+ if (rewriteAttemptCount >= MAX_REWRITE_ATTEMPTS) {
75
+ const { resolveAllOverrides } = await import("./files.js");
76
+ await resolveAllOverrides(basePath);
77
+ rewriteAttemptCount = 0;
78
+ return null;
79
+ }
80
+ rewriteAttemptCount++;
81
+ const unitId = state.activeSlice ? `${mid}/${state.activeSlice.id}` : mid;
82
+ return {
83
+ action: "dispatch",
84
+ unitType: "rewrite-docs",
85
+ unitId,
86
+ prompt: await buildRewriteDocsPrompt(mid, midTitle, state.activeSlice, basePath, pendingOverrides),
87
+ };
88
+ },
89
+ },
60
90
  {
61
91
  name: "summarizing → complete-slice",
62
92
  match: async ({ state, mid, midTitle, basePath }) => {
@@ -92,7 +122,9 @@ const DISPATCH_RULES: DispatchRule[] = [
92
122
  },
93
123
  {
94
124
  name: "reassess-roadmap (post-completion)",
95
- match: async ({ state, mid, midTitle, basePath }) => {
125
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
126
+ // Phase skip: skip reassess when preference or profile says so
127
+ if (prefs?.phases?.skip_reassess) return null;
96
128
  const needsReassess = await checkNeedsReassessment(basePath, mid, state);
97
129
  if (!needsReassess) return null;
98
130
  return {
@@ -130,8 +162,10 @@ const DISPATCH_RULES: DispatchRule[] = [
130
162
  },
131
163
  {
132
164
  name: "pre-planning (no research) → research-milestone",
133
- match: async ({ state, mid, midTitle, basePath }) => {
165
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
134
166
  if (state.phase !== "pre-planning") return null;
167
+ // Phase skip: skip research when preference or profile says so
168
+ if (prefs?.phases?.skip_research) return null;
135
169
  const researchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
136
170
  if (researchFile) return null; // has research, fall through
137
171
  return {
@@ -156,8 +190,10 @@ const DISPATCH_RULES: DispatchRule[] = [
156
190
  },
157
191
  {
158
192
  name: "planning (no research, not S01) → research-slice",
159
- match: async ({ state, mid, midTitle, basePath }) => {
193
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
160
194
  if (state.phase !== "planning") return null;
195
+ // Phase skip: skip research when preference or profile says so
196
+ if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null;
161
197
  const sid = state.activeSlice!.id;
162
198
  const sTitle = state.activeSlice!.title;
163
199
  const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
@@ -6,8 +6,8 @@
6
6
  * utility.
7
7
  */
8
8
 
9
- import { loadFile, parseContinue, parseRoadmap, parseSummary, extractUatType } from "./files.js";
10
- import type { UatType } from "./files.js";
9
+ import { loadFile, parseContinue, parsePlan, parseRoadmap, parseSummary, extractUatType, loadActiveOverrides, formatOverridesSection } from "./files.js";
10
+ import type { Override, UatType } from "./files.js";
11
11
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
12
12
  import {
13
13
  resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
@@ -15,8 +15,8 @@ import {
15
15
  relMilestoneFile, relSliceFile, relSlicePath, relMilestonePath,
16
16
  resolveGsdRootFile, relGsdRootFile,
17
17
  } from "./paths.js";
18
- import { resolveSkillDiscoveryMode } from "./preferences.js";
19
- import type { GSDState } from "./types.js";
18
+ import { resolveSkillDiscoveryMode, resolveInlineLevel } from "./preferences.js";
19
+ import type { GSDState, InlineLevel } from "./types.js";
20
20
  import type { GSDPreferences } from "./preferences.js";
21
21
  import { join } from "node:path";
22
22
  import { existsSync } from "node:fs";
@@ -393,7 +393,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
393
393
  });
394
394
  }
395
395
 
396
- export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string): Promise<string> {
396
+ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string, level?: InlineLevel): Promise<string> {
397
+ const inlineLevel = level ?? resolveInlineLevel();
397
398
  const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
398
399
  const contextRel = relMilestoneFile(base, mid, "CONTEXT");
399
400
  const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
@@ -406,17 +407,23 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
406
407
  const { inlinePriorMilestoneSummary } = await import("./files.js");
407
408
  const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
408
409
  if (priorSummaryInline) inlined.push(priorSummaryInline);
409
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
410
+ const projectInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "project.md", "Project") : null;
410
411
  if (projectInline) inlined.push(projectInline);
411
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
412
+ const requirementsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "requirements.md", "Requirements") : null;
412
413
  if (requirementsInline) inlined.push(requirementsInline);
413
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
414
+ const decisionsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "decisions.md", "Decisions") : null;
414
415
  if (decisionsInline) inlined.push(decisionsInline);
415
416
  inlined.push(inlineTemplate("roadmap", "Roadmap"));
416
- inlined.push(inlineTemplate("decisions", "Decisions"));
417
- inlined.push(inlineTemplate("plan", "Slice Plan"));
418
- inlined.push(inlineTemplate("task-plan", "Task Plan"));
419
- inlined.push(inlineTemplate("secrets-manifest", "Secrets Manifest"));
417
+ if (inlineLevel === "full") {
418
+ inlined.push(inlineTemplate("decisions", "Decisions"));
419
+ inlined.push(inlineTemplate("plan", "Slice Plan"));
420
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
421
+ inlined.push(inlineTemplate("secrets-manifest", "Secrets Manifest"));
422
+ } else if (inlineLevel === "standard") {
423
+ inlined.push(inlineTemplate("decisions", "Decisions"));
424
+ inlined.push(inlineTemplate("plan", "Slice Plan"));
425
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
426
+ }
420
427
 
421
428
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
422
429
 
@@ -457,6 +464,9 @@ export async function buildResearchSlicePrompt(
457
464
  inlined.push(inlineTemplate("research", "Research"));
458
465
 
459
466
  const depContent = await inlineDependencySummaries(mid, sid, base);
467
+ const activeOverrides = await loadActiveOverrides(base);
468
+ const overridesInline = formatOverridesSection(activeOverrides);
469
+ if (overridesInline) inlined.unshift(overridesInline);
460
470
 
461
471
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
462
472
 
@@ -476,8 +486,9 @@ export async function buildResearchSlicePrompt(
476
486
  }
477
487
 
478
488
  export async function buildPlanSlicePrompt(
479
- mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
489
+ mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
480
490
  ): Promise<string> {
491
+ const inlineLevel = level ?? resolveInlineLevel();
481
492
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
482
493
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
483
494
  const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
@@ -487,14 +498,21 @@ export async function buildPlanSlicePrompt(
487
498
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
488
499
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
489
500
  if (researchInline) inlined.push(researchInline);
490
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
491
- if (decisionsInline) inlined.push(decisionsInline);
492
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
493
- if (requirementsInline) inlined.push(requirementsInline);
501
+ if (inlineLevel !== "minimal") {
502
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
503
+ if (decisionsInline) inlined.push(decisionsInline);
504
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
505
+ if (requirementsInline) inlined.push(requirementsInline);
506
+ }
494
507
  inlined.push(inlineTemplate("plan", "Slice Plan"));
495
- inlined.push(inlineTemplate("task-plan", "Task Plan"));
508
+ if (inlineLevel === "full") {
509
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
510
+ }
496
511
 
497
512
  const depContent = await inlineDependencySummaries(mid, sid, base);
513
+ const planActiveOverrides = await loadActiveOverrides(base);
514
+ const planOverridesInline = formatOverridesSection(planActiveOverrides);
515
+ if (planOverridesInline) inlined.unshift(planOverridesInline);
498
516
 
499
517
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
500
518
 
@@ -513,8 +531,9 @@ export async function buildPlanSlicePrompt(
513
531
 
514
532
  export async function buildExecuteTaskPrompt(
515
533
  mid: string, sid: string, sTitle: string,
516
- tid: string, tTitle: string, base: string,
534
+ tid: string, tTitle: string, base: string, level?: InlineLevel,
517
535
  ): Promise<string> {
536
+ const inlineLevel = level ?? resolveInlineLevel();
518
537
 
519
538
  const priorSummaries = await getPriorTaskSummaryPaths(mid, sid, tid, base);
520
539
  const priorLines = priorSummaries.length > 0
@@ -554,15 +573,25 @@ export async function buildExecuteTaskPrompt(
554
573
  legacyContinuePath ? `${relSlicePath(base, mid, sid)}/continue.md` : null,
555
574
  );
556
575
 
557
- const carryForwardSection = await buildCarryForwardSection(priorSummaries, base);
558
- const inlinedTemplates = [
559
- inlineTemplate("task-summary", "Task Summary"),
560
- inlineTemplate("decisions", "Decisions"),
561
- ].join("\n\n---\n\n");
576
+ // For minimal inline level, only carry forward the most recent prior summary
577
+ const effectivePriorSummaries = inlineLevel === "minimal" && priorSummaries.length > 1
578
+ ? priorSummaries.slice(-1)
579
+ : priorSummaries;
580
+ const carryForwardSection = await buildCarryForwardSection(effectivePriorSummaries, base);
581
+ const inlinedTemplates = inlineLevel === "minimal"
582
+ ? inlineTemplate("task-summary", "Task Summary")
583
+ : [
584
+ inlineTemplate("task-summary", "Task Summary"),
585
+ inlineTemplate("decisions", "Decisions"),
586
+ ].join("\n\n---\n\n");
562
587
 
563
588
  const taskSummaryPath = `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`;
564
589
 
590
+ const activeOverrides = await loadActiveOverrides(base);
591
+ const overridesSection = formatOverridesSection(activeOverrides);
592
+
565
593
  return loadPrompt("execute-task", {
594
+ overridesSection,
566
595
  workingDirectory: base,
567
596
  milestoneId: mid, sliceId: sid, sliceTitle: sTitle, taskId: tid, taskTitle: tTitle,
568
597
  planPath: relSliceFile(base, mid, sid, "PLAN"),
@@ -579,8 +608,9 @@ export async function buildExecuteTaskPrompt(
579
608
  }
580
609
 
581
610
  export async function buildCompleteSlicePrompt(
582
- mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
611
+ mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
583
612
  ): Promise<string> {
613
+ const inlineLevel = level ?? resolveInlineLevel();
584
614
 
585
615
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
586
616
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
@@ -590,8 +620,10 @@ export async function buildCompleteSlicePrompt(
590
620
  const inlined: string[] = [];
591
621
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
592
622
  inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
593
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
594
- if (requirementsInline) inlined.push(requirementsInline);
623
+ if (inlineLevel !== "minimal") {
624
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
625
+ if (requirementsInline) inlined.push(requirementsInline);
626
+ }
595
627
 
596
628
  // Inline all task summaries for this slice
597
629
  const tDir = resolveTasksDir(base, mid, sid);
@@ -608,7 +640,12 @@ export async function buildCompleteSlicePrompt(
608
640
  }
609
641
  }
610
642
  inlined.push(inlineTemplate("slice-summary", "Slice Summary"));
611
- inlined.push(inlineTemplate("uat", "UAT"));
643
+ if (inlineLevel !== "minimal") {
644
+ inlined.push(inlineTemplate("uat", "UAT"));
645
+ }
646
+ const completeActiveOverrides = await loadActiveOverrides(base);
647
+ const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
648
+ if (completeOverridesInline) inlined.unshift(completeOverridesInline);
612
649
 
613
650
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
614
651
 
@@ -628,8 +665,9 @@ export async function buildCompleteSlicePrompt(
628
665
  }
629
666
 
630
667
  export async function buildCompleteMilestonePrompt(
631
- mid: string, midTitle: string, base: string,
668
+ mid: string, midTitle: string, base: string, level?: InlineLevel,
632
669
  ): Promise<string> {
670
+ const inlineLevel = level ?? resolveInlineLevel();
633
671
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
634
672
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
635
673
 
@@ -650,13 +688,15 @@ export async function buildCompleteMilestonePrompt(
650
688
  }
651
689
  }
652
690
 
653
- // Inline root GSD files
654
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
655
- if (requirementsInline) inlined.push(requirementsInline);
656
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
657
- if (decisionsInline) inlined.push(decisionsInline);
658
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
659
- if (projectInline) inlined.push(projectInline);
691
+ // Inline root GSD files (skip for minimal — completion can read these if needed)
692
+ if (inlineLevel !== "minimal") {
693
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
694
+ if (requirementsInline) inlined.push(requirementsInline);
695
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
696
+ if (decisionsInline) inlined.push(decisionsInline);
697
+ const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
698
+ if (projectInline) inlined.push(projectInline);
699
+ }
660
700
  // Inline milestone context file (milestone-level, not GSD root)
661
701
  const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
662
702
  const contextRel = relMilestoneFile(base, mid, "CONTEXT");
@@ -712,6 +752,9 @@ export async function buildReplanSlicePrompt(
712
752
  // Inline decisions
713
753
  const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
714
754
  if (decisionsInline) inlined.push(decisionsInline);
755
+ const replanActiveOverrides = await loadActiveOverrides(base);
756
+ const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
757
+ if (replanOverridesInline) inlined.unshift(replanOverridesInline);
715
758
 
716
759
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
717
760
 
@@ -763,8 +806,9 @@ export async function buildRunUatPrompt(
763
806
  }
764
807
 
765
808
  export async function buildReassessRoadmapPrompt(
766
- mid: string, midTitle: string, completedSliceId: string, base: string,
809
+ mid: string, midTitle: string, completedSliceId: string, base: string, level?: InlineLevel,
767
810
  ): Promise<string> {
811
+ const inlineLevel = level ?? resolveInlineLevel();
768
812
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
769
813
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
770
814
  const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
@@ -773,12 +817,14 @@ export async function buildReassessRoadmapPrompt(
773
817
  const inlined: string[] = [];
774
818
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
775
819
  inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
776
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
777
- if (projectInline) inlined.push(projectInline);
778
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
779
- if (requirementsInline) inlined.push(requirementsInline);
780
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
781
- if (decisionsInline) inlined.push(decisionsInline);
820
+ if (inlineLevel !== "minimal") {
821
+ const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
822
+ if (projectInline) inlined.push(projectInline);
823
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
824
+ if (requirementsInline) inlined.push(requirementsInline);
825
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
826
+ if (decisionsInline) inlined.push(decisionsInline);
827
+ }
782
828
 
783
829
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
784
830
 
@@ -795,3 +841,70 @@ export async function buildReassessRoadmapPrompt(
795
841
  inlinedContext,
796
842
  });
797
843
  }
844
+
845
+ export async function buildRewriteDocsPrompt(
846
+ mid: string, midTitle: string,
847
+ activeSlice: { id: string; title: string } | null,
848
+ base: string,
849
+ overrides: Override[],
850
+ ): Promise<string> {
851
+ const sid = activeSlice?.id;
852
+ const sTitle = activeSlice?.title ?? "";
853
+ const docList: string[] = [];
854
+
855
+ if (sid) {
856
+ const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
857
+ const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
858
+ if (slicePlanPath) {
859
+ docList.push(`- Slice plan: \`${slicePlanRel}\``);
860
+ const tDir = resolveTasksDir(base, mid, sid);
861
+ if (tDir) {
862
+ const planContent = await loadFile(slicePlanPath);
863
+ if (planContent) {
864
+ const plan = parsePlan(planContent);
865
+ for (const task of plan.tasks) {
866
+ if (!task.done) {
867
+ const taskPlanPath = resolveTaskFile(base, mid, sid, task.id, "PLAN");
868
+ if (taskPlanPath) {
869
+ const taskRelPath = `${relSlicePath(base, mid, sid)}/tasks/${task.id}-PLAN.md`;
870
+ docList.push(`- Task plan: \`${taskRelPath}\``);
871
+ }
872
+ }
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+
879
+ const decisionsPath = resolveGsdRootFile(base, "DECISIONS");
880
+ if (existsSync(decisionsPath)) docList.push(`- Decisions: \`${relGsdRootFile("DECISIONS")}\``);
881
+ const requirementsPath = resolveGsdRootFile(base, "REQUIREMENTS");
882
+ if (existsSync(requirementsPath)) docList.push(`- Requirements: \`${relGsdRootFile("REQUIREMENTS")}\``);
883
+ const projectPath = resolveGsdRootFile(base, "PROJECT");
884
+ if (existsSync(projectPath)) docList.push(`- Project: \`${relGsdRootFile("PROJECT")}\``);
885
+ const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
886
+ const contextRel = relMilestoneFile(base, mid, "CONTEXT");
887
+ if (contextPath) docList.push(`- Milestone context (reference only): \`${contextRel}\``);
888
+ const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
889
+ const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
890
+ if (roadmapPath) docList.push(`- Roadmap: \`${roadmapRel}\``);
891
+
892
+ const overrideContent = overrides.map((o, i) => [
893
+ `### Override ${i + 1}`,
894
+ `**Change:** ${o.change}`,
895
+ `**Issued:** ${o.timestamp}`,
896
+ `**During:** ${o.appliedAt}`,
897
+ ].join("\n")).join("\n\n");
898
+
899
+ const documentList = docList.length > 0 ? docList.join("\n") : "- No active plan documents found.";
900
+
901
+ return loadPrompt("rewrite-docs", {
902
+ milestoneId: mid,
903
+ milestoneTitle: midTitle,
904
+ sliceId: sid ?? "none",
905
+ sliceTitle: sTitle,
906
+ overrideContent,
907
+ documentList,
908
+ overridesPath: relGsdRootFile("OVERRIDES"),
909
+ });
910
+ }