gsd-pi 2.16.0 → 2.18.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 (225) hide show
  1. package/README.md +39 -0
  2. package/dist/onboarding.js +2 -2
  3. package/dist/remote-questions-config.d.ts +10 -0
  4. package/dist/remote-questions-config.js +36 -0
  5. package/dist/resources/extensions/gsd/activity-log.ts +37 -7
  6. package/dist/resources/extensions/gsd/auto-dashboard.ts +4 -0
  7. package/dist/resources/extensions/gsd/auto-dispatch.ts +9 -3
  8. package/dist/resources/extensions/gsd/auto-prompts.ts +91 -42
  9. package/dist/resources/extensions/gsd/auto-recovery.ts +7 -2
  10. package/dist/resources/extensions/gsd/auto-worktree.ts +33 -4
  11. package/dist/resources/extensions/gsd/auto.ts +177 -25
  12. package/dist/resources/extensions/gsd/commands.ts +264 -23
  13. package/dist/resources/extensions/gsd/complexity.ts +236 -0
  14. package/dist/resources/extensions/gsd/dispatch-guard.ts +7 -19
  15. package/dist/resources/extensions/gsd/docs/preferences-reference.md +202 -2
  16. package/dist/resources/extensions/gsd/files.ts +129 -3
  17. package/dist/resources/extensions/gsd/git-service.ts +19 -8
  18. package/dist/resources/extensions/gsd/gitignore.ts +41 -2
  19. package/dist/resources/extensions/gsd/guided-flow.ts +247 -10
  20. package/dist/resources/extensions/gsd/index.ts +47 -3
  21. package/dist/resources/extensions/gsd/metrics.ts +44 -0
  22. package/dist/resources/extensions/gsd/native-git-bridge.ts +5 -0
  23. package/dist/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  24. package/dist/resources/extensions/gsd/paths.ts +9 -0
  25. package/dist/resources/extensions/gsd/preferences.ts +181 -2
  26. package/dist/resources/extensions/gsd/prompts/execute-task.md +6 -5
  27. package/dist/resources/extensions/gsd/prompts/system.md +2 -0
  28. package/dist/resources/extensions/gsd/queue-order.ts +231 -0
  29. package/dist/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
  30. package/dist/resources/extensions/gsd/routing-history.ts +290 -0
  31. package/dist/resources/extensions/gsd/state.ts +15 -3
  32. package/dist/resources/extensions/gsd/templates/knowledge.md +19 -0
  33. package/dist/resources/extensions/gsd/templates/preferences.md +14 -0
  34. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  35. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
  36. package/dist/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  37. package/dist/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  38. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  39. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
  40. package/dist/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  41. package/dist/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
  42. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
  43. package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
  44. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  45. package/dist/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
  46. package/dist/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
  47. package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
  48. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  49. package/dist/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
  50. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  51. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  52. package/dist/resources/extensions/gsd/types.ts +28 -0
  53. package/dist/resources/extensions/gsd/worktree-manager.ts +8 -5
  54. package/dist/resources/extensions/gsd/worktree.ts +24 -2
  55. package/dist/resources/extensions/shared/next-action-ui.ts +16 -1
  56. package/package.json +1 -1
  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/cli/args.d.ts +5 -0
  73. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  74. package/packages/pi-coding-agent/dist/cli/args.js +21 -0
  75. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/cli/list-models.d.ts +14 -3
  77. package/packages/pi-coding-agent/dist/cli/list-models.d.ts.map +1 -1
  78. package/packages/pi-coding-agent/dist/cli/list-models.js +52 -17
  79. package/packages/pi-coding-agent/dist/cli/list-models.js.map +1 -1
  80. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts +27 -0
  81. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -0
  82. package/packages/pi-coding-agent/dist/core/discovery-cache.js +79 -0
  83. package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -0
  84. package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts +2 -0
  85. package/packages/pi-coding-agent/dist/core/discovery-cache.test.d.ts.map +1 -0
  86. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +140 -0
  87. package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -0
  88. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +35 -0
  89. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -0
  90. package/packages/pi-coding-agent/dist/core/model-discovery.js +162 -0
  91. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -0
  92. package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts +2 -0
  93. package/packages/pi-coding-agent/dist/core/model-discovery.test.d.ts.map +1 -0
  94. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +100 -0
  95. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -0
  96. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts +2 -0
  97. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.d.ts.map +1 -0
  98. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +113 -0
  99. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -0
  100. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +26 -0
  101. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/model-registry.js +98 -0
  103. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts +62 -0
  105. package/packages/pi-coding-agent/dist/core/models-json-writer.d.ts.map +1 -0
  106. package/packages/pi-coding-agent/dist/core/models-json-writer.js +145 -0
  107. package/packages/pi-coding-agent/dist/core/models-json-writer.js.map +1 -0
  108. package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts +2 -0
  109. package/packages/pi-coding-agent/dist/core/models-json-writer.test.d.ts.map +1 -0
  110. package/packages/pi-coding-agent/dist/core/models-json-writer.test.js +118 -0
  111. package/packages/pi-coding-agent/dist/core/models-json-writer.test.js.map +1 -0
  112. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
  113. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  114. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  115. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
  118. package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts +7 -7
  120. package/packages/pi-coding-agent/dist/core/tools/edit-diff.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js +209 -13
  122. package/packages/pi-coding-agent/dist/core/tools/edit-diff.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts +2 -0
  124. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.d.ts.map +1 -0
  125. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +67 -0
  126. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -0
  127. package/packages/pi-coding-agent/dist/index.d.ts +5 -1
  128. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  129. package/packages/pi-coding-agent/dist/index.js +4 -1
  130. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  131. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/main.js +17 -2
  133. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts +1 -0
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/index.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/index.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +1 -1
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +25 -0
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -0
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +121 -0
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -0
  144. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +32 -0
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +10 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  151. package/packages/pi-coding-agent/src/cli/args.ts +21 -0
  152. package/packages/pi-coding-agent/src/cli/list-models.ts +70 -17
  153. package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +170 -0
  154. package/packages/pi-coding-agent/src/core/discovery-cache.ts +97 -0
  155. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +125 -0
  156. package/packages/pi-coding-agent/src/core/model-discovery.ts +231 -0
  157. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +135 -0
  158. package/packages/pi-coding-agent/src/core/model-registry.ts +107 -0
  159. package/packages/pi-coding-agent/src/core/models-json-writer.test.ts +145 -0
  160. package/packages/pi-coding-agent/src/core/models-json-writer.ts +188 -0
  161. package/packages/pi-coding-agent/src/core/settings-manager.ts +21 -0
  162. package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
  163. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +85 -0
  164. package/packages/pi-coding-agent/src/core/tools/edit-diff.ts +245 -17
  165. package/packages/pi-coding-agent/src/index.ts +5 -0
  166. package/packages/pi-coding-agent/src/main.ts +19 -2
  167. package/packages/pi-coding-agent/src/modes/interactive/components/index.ts +1 -0
  168. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +1 -1
  169. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +163 -0
  170. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +37 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +13 -0
  172. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  173. package/pkg/dist/modes/interactive/theme/theme.js +10 -0
  174. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  175. package/src/resources/extensions/gsd/activity-log.ts +37 -7
  176. package/src/resources/extensions/gsd/auto-dashboard.ts +4 -0
  177. package/src/resources/extensions/gsd/auto-dispatch.ts +9 -3
  178. package/src/resources/extensions/gsd/auto-prompts.ts +91 -42
  179. package/src/resources/extensions/gsd/auto-recovery.ts +7 -2
  180. package/src/resources/extensions/gsd/auto-worktree.ts +33 -4
  181. package/src/resources/extensions/gsd/auto.ts +177 -25
  182. package/src/resources/extensions/gsd/commands.ts +264 -23
  183. package/src/resources/extensions/gsd/complexity.ts +236 -0
  184. package/src/resources/extensions/gsd/dispatch-guard.ts +7 -19
  185. package/src/resources/extensions/gsd/docs/preferences-reference.md +202 -2
  186. package/src/resources/extensions/gsd/files.ts +129 -3
  187. package/src/resources/extensions/gsd/git-service.ts +19 -8
  188. package/src/resources/extensions/gsd/gitignore.ts +41 -2
  189. package/src/resources/extensions/gsd/guided-flow.ts +247 -10
  190. package/src/resources/extensions/gsd/index.ts +47 -3
  191. package/src/resources/extensions/gsd/metrics.ts +44 -0
  192. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -0
  193. package/src/resources/extensions/gsd/native-parser-bridge.ts +5 -0
  194. package/src/resources/extensions/gsd/paths.ts +9 -0
  195. package/src/resources/extensions/gsd/preferences.ts +181 -2
  196. package/src/resources/extensions/gsd/prompts/execute-task.md +6 -5
  197. package/src/resources/extensions/gsd/prompts/system.md +2 -0
  198. package/src/resources/extensions/gsd/queue-order.ts +231 -0
  199. package/src/resources/extensions/gsd/queue-reorder-ui.ts +263 -0
  200. package/src/resources/extensions/gsd/routing-history.ts +290 -0
  201. package/src/resources/extensions/gsd/state.ts +15 -3
  202. package/src/resources/extensions/gsd/templates/knowledge.md +19 -0
  203. package/src/resources/extensions/gsd/templates/preferences.md +14 -0
  204. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +50 -0
  205. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +20 -0
  206. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +220 -0
  207. package/src/resources/extensions/gsd/tests/complexity-routing.test.ts +294 -0
  208. package/src/resources/extensions/gsd/tests/context-compression.test.ts +180 -0
  209. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +99 -0
  210. package/src/resources/extensions/gsd/tests/git-service.test.ts +132 -0
  211. package/src/resources/extensions/gsd/tests/in-flight-tool-tracking.test.ts +79 -0
  212. package/src/resources/extensions/gsd/tests/knowledge.test.ts +161 -0
  213. package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +87 -0
  214. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +28 -0
  215. package/src/resources/extensions/gsd/tests/preferences-wizard-fields.test.ts +168 -0
  216. package/src/resources/extensions/gsd/tests/queue-order.test.ts +204 -0
  217. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +281 -0
  218. package/src/resources/extensions/gsd/tests/routing-history.test.ts +87 -0
  219. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +139 -0
  220. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +130 -0
  221. package/src/resources/extensions/gsd/tests/token-profile.test.ts +263 -0
  222. package/src/resources/extensions/gsd/types.ts +28 -0
  223. package/src/resources/extensions/gsd/worktree-manager.ts +8 -5
  224. package/src/resources/extensions/gsd/worktree.ts +24 -2
  225. package/src/resources/extensions/shared/next-action-ui.ts +16 -1
package/README.md CHANGED
@@ -21,6 +21,25 @@ One command. Walk away. Come back to a built project with clean git history.
21
21
 
22
22
  ---
23
23
 
24
+ ## Documentation
25
+
26
+ Full documentation is available in the [`docs/`](./docs/) directory:
27
+
28
+ - **[Getting Started](./docs/getting-started.md)** — install, first run, basic usage
29
+ - **[Auto Mode](./docs/auto-mode.md)** — autonomous execution deep-dive
30
+ - **[Configuration](./docs/configuration.md)** — all preferences, models, git, and hooks
31
+ - **[Token Optimization](./docs/token-optimization.md)** — profiles, context compression, complexity routing (v2.17)
32
+ - **[Cost Management](./docs/cost-management.md)** — budgets, tracking, projections
33
+ - **[Git Strategy](./docs/git-strategy.md)** — worktree isolation, branching, merge behavior
34
+ - **[Working in Teams](./docs/working-in-teams.md)** — unique IDs, shared artifacts
35
+ - **[Skills](./docs/skills.md)** — bundled skills, discovery, custom authoring
36
+ - **[Commands Reference](./docs/commands.md)** — all commands and keyboard shortcuts
37
+ - **[Architecture](./docs/architecture.md)** — system design and dispatch pipeline
38
+ - **[Troubleshooting](./docs/troubleshooting.md)** — common issues, doctor, recovery
39
+ - **[Migration from v1](./docs/migration.md)** — `.planning` → `.gsd` migration
40
+
41
+ ---
42
+
24
43
  ## What Changed From v1
25
44
 
26
45
  The original GSD was a collection of markdown prompts installed into `~/.claude/commands/`. It relied entirely on the LLM reading those prompts and doing the right thing. That worked surprisingly well — but it had hard limits:
@@ -334,6 +353,26 @@ unique_milestone_ids: true
334
353
  | `skill_rules` | Situational rules for skill routing |
335
354
  | `unique_milestone_ids` | Uses unique milestone names to avoid clashes when working in teams of people |
336
355
 
356
+ ### Token Optimization (v2.17)
357
+
358
+ GSD 2.17 introduced a coordinated token optimization system that reduces usage by 40-60% on cost-sensitive workloads. Set a single preference to coordinate model selection, phase skipping, and context compression:
359
+
360
+ ```yaml
361
+ token_profile: budget # or balanced (default), quality
362
+ ```
363
+
364
+ | Profile | Savings | What It Does |
365
+ |---------|---------|-------------|
366
+ | `budget` | 40-60% | Cheap models, skip research/reassess, minimal context inlining |
367
+ | `balanced` | 10-20% | Default models, skip slice research, standard context |
368
+ | `quality` | 0% | All phases, all context, full model power |
369
+
370
+ **Complexity-based routing** automatically classifies tasks as simple/standard/complex and routes to appropriate models. Simple docs tasks get Haiku; complex architectural work gets Opus. The classification is heuristic (sub-millisecond, no LLM calls) and learns from outcomes via a persistent routing history.
371
+
372
+ **Budget pressure** graduates model downgrading as you approach your budget ceiling — 50%, 75%, and 90% thresholds progressively shift work to cheaper tiers.
373
+
374
+ See the full [Token Optimization Guide](./docs/token-optimization.md) for details.
375
+
337
376
  ### Bundled Tools
338
377
 
339
378
  GSD ships with 14 extensions, all loaded automatically:
@@ -633,7 +633,7 @@ async function runRemoteQuestionsStep(p, pc, authStorage) {
633
633
  });
634
634
  if (p.isCancel(channelId) || !channelId)
635
635
  return null;
636
- const { saveRemoteQuestionsConfig } = await import('./resources/extensions/remote-questions/remote-command.js');
636
+ const { saveRemoteQuestionsConfig } = await import('./remote-questions-config.js');
637
637
  saveRemoteQuestionsConfig('slack', channelId.trim());
638
638
  p.log.success(`Slack channel: ${pc.green(channelId.trim())}`);
639
639
  return 'Slack';
@@ -736,7 +736,7 @@ async function runDiscordChannelStep(p, pc, token) {
736
736
  channelId = channelChoice;
737
737
  }
738
738
  // Save remote questions config
739
- const { saveRemoteQuestionsConfig } = await import('./resources/extensions/remote-questions/remote-command.js');
739
+ const { saveRemoteQuestionsConfig } = await import('./remote-questions-config.js');
740
740
  saveRemoteQuestionsConfig('discord', channelId);
741
741
  const channelName = channels.find(ch => ch.id === channelId)?.name;
742
742
  p.log.success(`Discord channel: ${pc.green(channelName ? `#${channelName}` : channelId)}`);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Remote Questions Config Helper
3
+ *
4
+ * Extracted from remote-questions extension so onboarding.ts can import
5
+ * it without crossing the compiled/uncompiled boundary. The extension
6
+ * files in src/resources/ are shipped as raw .ts and loaded via jiti,
7
+ * but onboarding.ts is compiled by tsc — dynamic imports from compiled
8
+ * JS to uncompiled .ts fail at runtime (#592).
9
+ */
10
+ export declare function saveRemoteQuestionsConfig(channel: "slack" | "discord", channelId: string): void;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Remote Questions Config Helper
3
+ *
4
+ * Extracted from remote-questions extension so onboarding.ts can import
5
+ * it without crossing the compiled/uncompiled boundary. The extension
6
+ * files in src/resources/ are shipped as raw .ts and loaded via jiti,
7
+ * but onboarding.ts is compiled by tsc — dynamic imports from compiled
8
+ * JS to uncompiled .ts fail at runtime (#592).
9
+ */
10
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
+ import { dirname } from "node:path";
12
+ import { getGlobalGSDPreferencesPath } from "./resources/extensions/gsd/preferences.js";
13
+ export function saveRemoteQuestionsConfig(channel, channelId) {
14
+ const prefsPath = getGlobalGSDPreferencesPath();
15
+ const block = [
16
+ "remote_questions:",
17
+ ` channel: ${channel}`,
18
+ ` channel_id: "${channelId}"`,
19
+ " timeout_minutes: 5",
20
+ " poll_interval_seconds: 5",
21
+ ].join("\n");
22
+ const content = existsSync(prefsPath) ? readFileSync(prefsPath, "utf-8") : "";
23
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
24
+ let next = content;
25
+ if (fmMatch) {
26
+ let frontmatter = fmMatch[1];
27
+ const regex = /remote_questions:[\s\S]*?(?=\n[a-zA-Z_]|\n---|$)/;
28
+ frontmatter = regex.test(frontmatter) ? frontmatter.replace(regex, block) : `${frontmatter.trimEnd()}\n${block}`;
29
+ next = `---\n${frontmatter}\n---${content.slice(fmMatch[0].length)}`;
30
+ }
31
+ else {
32
+ next = `---\n${block}\n---\n\n${content}`;
33
+ }
34
+ mkdirSync(dirname(prefsPath), { recursive: true });
35
+ writeFileSync(prefsPath, next, "utf-8");
36
+ }
@@ -8,7 +8,7 @@
8
8
  * Diagnostic extraction is handled by session-forensics.ts.
9
9
  */
10
10
 
11
- import { writeFileSync, mkdirSync, readdirSync, unlinkSync, statSync, openSync, closeSync, constants } from "node:fs";
11
+ import { writeFileSync, writeSync, mkdirSync, readdirSync, unlinkSync, statSync, openSync, closeSync, constants } from "node:fs";
12
12
  import { createHash } from "node:crypto";
13
13
  import { join } from "node:path";
14
14
 
@@ -23,6 +23,15 @@ interface ActivityLogState {
23
23
 
24
24
  const activityLogState = new Map<string, ActivityLogState>();
25
25
 
26
+ /**
27
+ * Clear accumulated activity log state (#611).
28
+ * Call when auto-mode stops to prevent unbounded memory growth
29
+ * from lastSnapshotKeyByUnit maps accumulating across units.
30
+ */
31
+ export function clearActivityLogState(): void {
32
+ activityLogState.clear();
33
+ }
34
+
26
35
  function scanNextSequence(activityDir: string): number {
27
36
  let maxSeq = 0;
28
37
  try {
@@ -46,9 +55,21 @@ function getActivityState(activityDir: string): ActivityLogState {
46
55
  return state;
47
56
  }
48
57
 
49
- function snapshotKey(unitType: string, unitId: string, content: string): string {
50
- const digest = createHash("sha1").update(content).digest("hex");
51
- return `${unitType}\0${unitId}\0${digest}`;
58
+ /**
59
+ * Build a lightweight dedup key from session entries without serializing
60
+ * the entire content to a string (#611). Uses entry count + hash of
61
+ * the last few entries as a fingerprint instead of hashing megabytes.
62
+ */
63
+ function snapshotKey(unitType: string, unitId: string, entries: unknown[]): string {
64
+ const hash = createHash("sha1");
65
+ hash.update(`${unitType}\0${unitId}\0${entries.length}\0`);
66
+ // Hash only the last 3 entries as a fingerprint — if the session grew,
67
+ // the count change alone detects it; if content changed, the tail hash catches it.
68
+ const tail = entries.slice(-3);
69
+ for (const entry of tail) {
70
+ hash.update(JSON.stringify(entry));
71
+ }
72
+ return hash.digest("hex");
52
73
  }
53
74
 
54
75
  function nextActivityFilePath(
@@ -91,14 +112,23 @@ export function saveActivityLog(
91
112
  mkdirSync(activityDir, { recursive: true });
92
113
 
93
114
  const safeUnitId = unitId.replace(/\//g, "-");
94
- const content = `${entries.map(entry => JSON.stringify(entry)).join("\n")}\n`;
95
115
  const state = getActivityState(activityDir);
96
116
  const unitKey = `${unitType}\0${safeUnitId}`;
97
- const key = snapshotKey(unitType, safeUnitId, content);
117
+ // Use lightweight fingerprint instead of serializing all entries (#611)
118
+ const key = snapshotKey(unitType, safeUnitId, entries);
98
119
  if (state.lastSnapshotKeyByUnit.get(unitKey) === key) return;
99
120
 
100
121
  const filePath = nextActivityFilePath(activityDir, state, unitType, safeUnitId);
101
- writeFileSync(filePath, content, "utf-8");
122
+ // Stream entries to disk line-by-line instead of building one massive string (#611).
123
+ // For large sessions, the single-string approach allocated hundreds of MB.
124
+ const fd = openSync(filePath, "w");
125
+ try {
126
+ for (const entry of entries) {
127
+ writeSync(fd, JSON.stringify(entry) + "\n");
128
+ }
129
+ } finally {
130
+ closeSync(fd);
131
+ }
102
132
  state.nextSeq += 1;
103
133
  state.lastSnapshotKeyByUnit.set(unitKey, key);
104
134
  } catch (e) {
@@ -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 ─────────────────────────────────────────────────
@@ -122,7 +122,9 @@ const DISPATCH_RULES: DispatchRule[] = [
122
122
  },
123
123
  {
124
124
  name: "reassess-roadmap (post-completion)",
125
- 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;
126
128
  const needsReassess = await checkNeedsReassessment(basePath, mid, state);
127
129
  if (!needsReassess) return null;
128
130
  return {
@@ -160,8 +162,10 @@ const DISPATCH_RULES: DispatchRule[] = [
160
162
  },
161
163
  {
162
164
  name: "pre-planning (no research) → research-milestone",
163
- match: async ({ state, mid, midTitle, basePath }) => {
165
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
164
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;
165
169
  const researchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
166
170
  if (researchFile) return null; // has research, fall through
167
171
  return {
@@ -186,8 +190,10 @@ const DISPATCH_RULES: DispatchRule[] = [
186
190
  },
187
191
  {
188
192
  name: "planning (no research, not S01) → research-slice",
189
- match: async ({ state, mid, midTitle, basePath }) => {
193
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
190
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;
191
197
  const sid = state.activeSlice!.id;
192
198
  const sTitle = state.activeSlice!.title;
193
199
  const researchFile = resolveSliceFile(basePath, mid, sid, "RESEARCH");
@@ -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";
@@ -89,7 +89,7 @@ export async function inlineDependencySummaries(
89
89
  export async function inlineGsdRootFile(
90
90
  base: string, filename: string, label: string,
91
91
  ): Promise<string | null> {
92
- const key = filename.replace(/\.md$/i, "").toUpperCase() as "PROJECT" | "DECISIONS" | "QUEUE" | "STATE" | "REQUIREMENTS";
92
+ const key = filename.replace(/\.md$/i, "").toUpperCase() as "PROJECT" | "DECISIONS" | "QUEUE" | "STATE" | "REQUIREMENTS" | "KNOWLEDGE";
93
93
  const absPath = resolveGsdRootFile(base, key);
94
94
  if (!existsSync(absPath)) return null;
95
95
  return inlineFileOptional(absPath, relGsdRootFile(key), label);
@@ -377,6 +377,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
377
377
  if (requirementsInline) inlined.push(requirementsInline);
378
378
  const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
379
379
  if (decisionsInline) inlined.push(decisionsInline);
380
+ const knowledgeInlineRM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
381
+ if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
380
382
  inlined.push(inlineTemplate("research", "Research"));
381
383
 
382
384
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
@@ -393,7 +395,8 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
393
395
  });
394
396
  }
395
397
 
396
- export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string): Promise<string> {
398
+ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, base: string, level?: InlineLevel): Promise<string> {
399
+ const inlineLevel = level ?? resolveInlineLevel();
397
400
  const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
398
401
  const contextRel = relMilestoneFile(base, mid, "CONTEXT");
399
402
  const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
@@ -406,17 +409,25 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
406
409
  const { inlinePriorMilestoneSummary } = await import("./files.js");
407
410
  const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
408
411
  if (priorSummaryInline) inlined.push(priorSummaryInline);
409
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
412
+ const projectInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "project.md", "Project") : null;
410
413
  if (projectInline) inlined.push(projectInline);
411
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
414
+ const requirementsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "requirements.md", "Requirements") : null;
412
415
  if (requirementsInline) inlined.push(requirementsInline);
413
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
416
+ const decisionsInline = inlineLevel !== "minimal" ? await inlineGsdRootFile(base, "decisions.md", "Decisions") : null;
414
417
  if (decisionsInline) inlined.push(decisionsInline);
418
+ const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
419
+ if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
415
420
  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"));
421
+ if (inlineLevel === "full") {
422
+ inlined.push(inlineTemplate("decisions", "Decisions"));
423
+ inlined.push(inlineTemplate("plan", "Slice Plan"));
424
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
425
+ inlined.push(inlineTemplate("secrets-manifest", "Secrets Manifest"));
426
+ } else if (inlineLevel === "standard") {
427
+ inlined.push(inlineTemplate("decisions", "Decisions"));
428
+ inlined.push(inlineTemplate("plan", "Slice Plan"));
429
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
430
+ }
420
431
 
421
432
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
422
433
 
@@ -454,6 +465,8 @@ export async function buildResearchSlicePrompt(
454
465
  if (decisionsInline) inlined.push(decisionsInline);
455
466
  const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
456
467
  if (requirementsInline) inlined.push(requirementsInline);
468
+ const knowledgeInlineRS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
469
+ if (knowledgeInlineRS) inlined.push(knowledgeInlineRS);
457
470
  inlined.push(inlineTemplate("research", "Research"));
458
471
 
459
472
  const depContent = await inlineDependencySummaries(mid, sid, base);
@@ -479,8 +492,9 @@ export async function buildResearchSlicePrompt(
479
492
  }
480
493
 
481
494
  export async function buildPlanSlicePrompt(
482
- mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
495
+ mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
483
496
  ): Promise<string> {
497
+ const inlineLevel = level ?? resolveInlineLevel();
484
498
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
485
499
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
486
500
  const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
@@ -490,12 +504,18 @@ export async function buildPlanSlicePrompt(
490
504
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
491
505
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
492
506
  if (researchInline) inlined.push(researchInline);
493
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
494
- if (decisionsInline) inlined.push(decisionsInline);
495
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
496
- if (requirementsInline) inlined.push(requirementsInline);
507
+ if (inlineLevel !== "minimal") {
508
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
509
+ if (decisionsInline) inlined.push(decisionsInline);
510
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
511
+ if (requirementsInline) inlined.push(requirementsInline);
512
+ }
513
+ const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
514
+ if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
497
515
  inlined.push(inlineTemplate("plan", "Slice Plan"));
498
- inlined.push(inlineTemplate("task-plan", "Task Plan"));
516
+ if (inlineLevel === "full") {
517
+ inlined.push(inlineTemplate("task-plan", "Task Plan"));
518
+ }
499
519
 
500
520
  const depContent = await inlineDependencySummaries(mid, sid, base);
501
521
  const planActiveOverrides = await loadActiveOverrides(base);
@@ -519,8 +539,9 @@ export async function buildPlanSlicePrompt(
519
539
 
520
540
  export async function buildExecuteTaskPrompt(
521
541
  mid: string, sid: string, sTitle: string,
522
- tid: string, tTitle: string, base: string,
542
+ tid: string, tTitle: string, base: string, level?: InlineLevel,
523
543
  ): Promise<string> {
544
+ const inlineLevel = level ?? resolveInlineLevel();
524
545
 
525
546
  const priorSummaries = await getPriorTaskSummaryPaths(mid, sid, tid, base);
526
547
  const priorLines = priorSummaries.length > 0
@@ -560,11 +581,22 @@ export async function buildExecuteTaskPrompt(
560
581
  legacyContinuePath ? `${relSlicePath(base, mid, sid)}/continue.md` : null,
561
582
  );
562
583
 
563
- const carryForwardSection = await buildCarryForwardSection(priorSummaries, base);
564
- const inlinedTemplates = [
565
- inlineTemplate("task-summary", "Task Summary"),
566
- inlineTemplate("decisions", "Decisions"),
567
- ].join("\n\n---\n\n");
584
+ // For minimal inline level, only carry forward the most recent prior summary
585
+ const effectivePriorSummaries = inlineLevel === "minimal" && priorSummaries.length > 1
586
+ ? priorSummaries.slice(-1)
587
+ : priorSummaries;
588
+ const carryForwardSection = await buildCarryForwardSection(effectivePriorSummaries, base);
589
+
590
+ // Inline project knowledge if available
591
+ const knowledgeInlineET = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
592
+
593
+ const inlinedTemplates = inlineLevel === "minimal"
594
+ ? inlineTemplate("task-summary", "Task Summary")
595
+ : [
596
+ inlineTemplate("task-summary", "Task Summary"),
597
+ inlineTemplate("decisions", "Decisions"),
598
+ ...(knowledgeInlineET ? [knowledgeInlineET] : []),
599
+ ].join("\n\n---\n\n");
568
600
 
569
601
  const taskSummaryPath = `${relSlicePath(base, mid, sid)}/tasks/${tid}-SUMMARY.md`;
570
602
 
@@ -589,8 +621,9 @@ export async function buildExecuteTaskPrompt(
589
621
  }
590
622
 
591
623
  export async function buildCompleteSlicePrompt(
592
- mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
624
+ mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
593
625
  ): Promise<string> {
626
+ const inlineLevel = level ?? resolveInlineLevel();
594
627
 
595
628
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
596
629
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
@@ -600,8 +633,12 @@ export async function buildCompleteSlicePrompt(
600
633
  const inlined: string[] = [];
601
634
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
602
635
  inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
603
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
604
- if (requirementsInline) inlined.push(requirementsInline);
636
+ if (inlineLevel !== "minimal") {
637
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
638
+ if (requirementsInline) inlined.push(requirementsInline);
639
+ }
640
+ const knowledgeInlineCS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
641
+ if (knowledgeInlineCS) inlined.push(knowledgeInlineCS);
605
642
 
606
643
  // Inline all task summaries for this slice
607
644
  const tDir = resolveTasksDir(base, mid, sid);
@@ -618,7 +655,9 @@ export async function buildCompleteSlicePrompt(
618
655
  }
619
656
  }
620
657
  inlined.push(inlineTemplate("slice-summary", "Slice Summary"));
621
- inlined.push(inlineTemplate("uat", "UAT"));
658
+ if (inlineLevel !== "minimal") {
659
+ inlined.push(inlineTemplate("uat", "UAT"));
660
+ }
622
661
  const completeActiveOverrides = await loadActiveOverrides(base);
623
662
  const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
624
663
  if (completeOverridesInline) inlined.unshift(completeOverridesInline);
@@ -641,8 +680,9 @@ export async function buildCompleteSlicePrompt(
641
680
  }
642
681
 
643
682
  export async function buildCompleteMilestonePrompt(
644
- mid: string, midTitle: string, base: string,
683
+ mid: string, midTitle: string, base: string, level?: InlineLevel,
645
684
  ): Promise<string> {
685
+ const inlineLevel = level ?? resolveInlineLevel();
646
686
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
647
687
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
648
688
 
@@ -663,13 +703,17 @@ export async function buildCompleteMilestonePrompt(
663
703
  }
664
704
  }
665
705
 
666
- // Inline root GSD files
667
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
668
- if (requirementsInline) inlined.push(requirementsInline);
669
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
670
- if (decisionsInline) inlined.push(decisionsInline);
671
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
672
- if (projectInline) inlined.push(projectInline);
706
+ // Inline root GSD files (skip for minimal — completion can read these if needed)
707
+ if (inlineLevel !== "minimal") {
708
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
709
+ if (requirementsInline) inlined.push(requirementsInline);
710
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
711
+ if (decisionsInline) inlined.push(decisionsInline);
712
+ const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
713
+ if (projectInline) inlined.push(projectInline);
714
+ }
715
+ const knowledgeInlineCM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
716
+ if (knowledgeInlineCM) inlined.push(knowledgeInlineCM);
673
717
  // Inline milestone context file (milestone-level, not GSD root)
674
718
  const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
675
719
  const contextRel = relMilestoneFile(base, mid, "CONTEXT");
@@ -779,8 +823,9 @@ export async function buildRunUatPrompt(
779
823
  }
780
824
 
781
825
  export async function buildReassessRoadmapPrompt(
782
- mid: string, midTitle: string, completedSliceId: string, base: string,
826
+ mid: string, midTitle: string, completedSliceId: string, base: string, level?: InlineLevel,
783
827
  ): Promise<string> {
828
+ const inlineLevel = level ?? resolveInlineLevel();
784
829
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
785
830
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
786
831
  const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
@@ -789,12 +834,16 @@ export async function buildReassessRoadmapPrompt(
789
834
  const inlined: string[] = [];
790
835
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
791
836
  inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
792
- const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
793
- if (projectInline) inlined.push(projectInline);
794
- const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
795
- if (requirementsInline) inlined.push(requirementsInline);
796
- const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
797
- if (decisionsInline) inlined.push(decisionsInline);
837
+ if (inlineLevel !== "minimal") {
838
+ const projectInline = await inlineGsdRootFile(base, "project.md", "Project");
839
+ if (projectInline) inlined.push(projectInline);
840
+ const requirementsInline = await inlineGsdRootFile(base, "requirements.md", "Requirements");
841
+ if (requirementsInline) inlined.push(requirementsInline);
842
+ const decisionsInline = await inlineGsdRootFile(base, "decisions.md", "Decisions");
843
+ if (decisionsInline) inlined.push(decisionsInline);
844
+ }
845
+ const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
846
+ if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
798
847
 
799
848
  const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
800
849
 
@@ -11,6 +11,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
11
11
  import {
12
12
  clearUnitRuntimeRecord,
13
13
  } from "./unit-runtime.js";
14
+ import { clearParseCache } from "./files.js";
14
15
  import {
15
16
  nativeConflictFiles,
16
17
  nativeCommit,
@@ -107,9 +108,13 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
107
108
  // is managed by the hook engine, not the artifact verification system.
108
109
  if (unitType.startsWith("hook/")) return true;
109
110
 
110
- // Clear stale directory listing cache so artifact checks see fresh disk state (#431).
111
- // Moved after hook check to avoid unnecessary cache clears for hook units.
111
+ // Clear stale directory listing cache AND parse cache so artifact checks see
112
+ // fresh disk state (#431). The parse cache must also be cleared because
113
+ // cacheKey() uses length + first/last 100 chars — when a checkbox changes
114
+ // from [ ] to [x], the key collides with the pre-edit version, returning
115
+ // stale parsed results (e.g., slice.done = false when it's actually true).
112
116
  clearPathCache();
117
+ clearParseCache();
113
118
 
114
119
  if (unitType === "rewrite-docs") {
115
120
  const overridesPath = resolveGsdRootFile(base, "OVERRIDES");
@@ -14,8 +14,10 @@ import {
14
14
  removeWorktree,
15
15
  worktreePath,
16
16
  } from "./worktree-manager.js";
17
+ import { detectWorktreeName } from "./worktree.js";
17
18
  import {
18
19
  MergeConflictError,
20
+ readIntegrationBranch,
19
21
  } from "./git-service.js";
20
22
  import { parseRoadmap } from "./files.js";
21
23
  import { loadEffectiveGSDPreferences } from "./preferences.js";
@@ -90,7 +92,12 @@ export function autoWorktreeBranch(milestoneId: string): string {
90
92
  */
91
93
  export function createAutoWorktree(basePath: string, milestoneId: string): string {
92
94
  const branch = autoWorktreeBranch(milestoneId);
93
- const info = createWorktree(basePath, milestoneId, { branch });
95
+
96
+ // Use the integration branch recorded in META.json as the start point.
97
+ // This ensures the worktree branch is created from the branch the user
98
+ // was on when they started the milestone (e.g. f-setup-gsd-2), not main.
99
+ const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
100
+ const info = createWorktree(basePath, milestoneId, { branch, startPoint: integrationBranch });
94
101
 
95
102
  // Copy .gsd/ planning artifacts from the source repo into the new worktree.
96
103
  // Worktrees are fresh git checkouts — untracked files don't carry over.
@@ -224,6 +231,27 @@ export function getAutoWorktreeOriginalBase(): string | null {
224
231
  return originalBase;
225
232
  }
226
233
 
234
+ export function getActiveAutoWorktreeContext(): {
235
+ originalBase: string;
236
+ worktreeName: string;
237
+ branch: string;
238
+ } | null {
239
+ if (!originalBase) return null;
240
+ const cwd = process.cwd();
241
+ const resolvedBase = existsSync(originalBase) ? realpathSync(originalBase) : originalBase;
242
+ const wtDir = join(resolvedBase, ".gsd", "worktrees");
243
+ if (!cwd.startsWith(wtDir)) return null;
244
+ const worktreeName = detectWorktreeName(cwd);
245
+ if (!worktreeName) return null;
246
+ const branch = nativeGetCurrentBranch(cwd);
247
+ if (!branch.startsWith("milestone/")) return null;
248
+ return {
249
+ originalBase,
250
+ worktreeName,
251
+ branch,
252
+ };
253
+ }
254
+
227
255
  // ─── Merge Milestone -> Main ───────────────────────────────────────────────
228
256
 
229
257
  /**
@@ -279,11 +307,12 @@ export function mergeMilestoneToMain(
279
307
  const previousCwd = process.cwd();
280
308
  process.chdir(originalBasePath_);
281
309
 
282
- // 4. Resolve main branch from preferences
310
+ // 4. Resolve integration branch prefer milestone metadata, fall back to preferences / "main"
283
311
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
284
- const mainBranch = prefs.main_branch || "main";
312
+ const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
313
+ const mainBranch = integrationBranch ?? prefs.main_branch ?? "main";
285
314
 
286
- // 5. Checkout main
315
+ // 5. Checkout integration branch
287
316
  nativeCheckoutBranch(originalBasePath_, mainBranch);
288
317
 
289
318
  // 6. Build rich commit message