gsd-pi 2.37.1 → 2.38.0-dev.29edcdc

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 (239) hide show
  1. package/README.md +1 -1
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/onboarding.js +1 -0
  8. package/dist/remote-questions-config.js +2 -2
  9. package/dist/resource-loader.js +34 -1
  10. package/dist/resources/extensions/browser-tools/package.json +3 -1
  11. package/dist/resources/extensions/cmux/index.js +55 -1
  12. package/dist/resources/extensions/context7/package.json +1 -1
  13. package/dist/resources/extensions/env-utils.js +29 -0
  14. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  15. package/dist/resources/extensions/github-sync/cli.js +284 -0
  16. package/dist/resources/extensions/github-sync/index.js +73 -0
  17. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  18. package/dist/resources/extensions/github-sync/sync.js +424 -0
  19. package/dist/resources/extensions/github-sync/templates.js +118 -0
  20. package/dist/resources/extensions/github-sync/types.js +7 -0
  21. package/dist/resources/extensions/google-search/package.json +3 -1
  22. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  23. package/dist/resources/extensions/gsd/auto-dispatch.js +75 -10
  24. package/dist/resources/extensions/gsd/auto-loop.js +597 -588
  25. package/dist/resources/extensions/gsd/auto-post-unit.js +111 -68
  26. package/dist/resources/extensions/gsd/auto-prompts.js +114 -45
  27. package/dist/resources/extensions/gsd/auto-recovery.js +37 -1
  28. package/dist/resources/extensions/gsd/auto-start.js +13 -2
  29. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  30. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  31. package/dist/resources/extensions/gsd/auto.js +143 -96
  32. package/dist/resources/extensions/gsd/captures.js +9 -1
  33. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  34. package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
  35. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  36. package/dist/resources/extensions/gsd/commands.js +24 -3
  37. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  38. package/dist/resources/extensions/gsd/detection.js +1 -2
  39. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  40. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  41. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  42. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  43. package/dist/resources/extensions/gsd/doctor-providers.js +62 -12
  44. package/dist/resources/extensions/gsd/doctor.js +204 -12
  45. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  46. package/dist/resources/extensions/gsd/export.js +1 -1
  47. package/dist/resources/extensions/gsd/files.js +47 -2
  48. package/dist/resources/extensions/gsd/forensics.js +1 -1
  49. package/dist/resources/extensions/gsd/git-service.js +15 -12
  50. package/dist/resources/extensions/gsd/guided-flow.js +82 -32
  51. package/dist/resources/extensions/gsd/index.js +24 -20
  52. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  53. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  54. package/dist/resources/extensions/gsd/observability-validator.js +24 -0
  55. package/dist/resources/extensions/gsd/package.json +1 -1
  56. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  57. package/dist/resources/extensions/gsd/preferences-types.js +3 -2
  58. package/dist/resources/extensions/gsd/preferences-validation.js +101 -11
  59. package/dist/resources/extensions/gsd/preferences.js +8 -5
  60. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  61. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
  62. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  63. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  64. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  66. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  67. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  68. package/dist/resources/extensions/gsd/prompts/run-uat.md +27 -10
  69. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  70. package/dist/resources/extensions/gsd/reactive-graph.js +227 -0
  71. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  72. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  73. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  74. package/dist/resources/extensions/gsd/state.js +1 -1
  75. package/dist/resources/extensions/gsd/templates/task-plan.md +11 -3
  76. package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
  77. package/dist/resources/extensions/gsd/worktree.js +35 -16
  78. package/dist/resources/extensions/mcp-client/index.js +14 -1
  79. package/dist/resources/extensions/remote-questions/status.js +2 -1
  80. package/dist/resources/extensions/remote-questions/store.js +2 -1
  81. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  82. package/dist/resources/extensions/subagent/index.js +12 -3
  83. package/dist/resources/extensions/subagent/isolation.js +2 -1
  84. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  85. package/dist/resources/extensions/universal-config/package.json +1 -1
  86. package/dist/welcome-screen.d.ts +12 -0
  87. package/dist/welcome-screen.js +53 -0
  88. package/package.json +2 -1
  89. package/packages/pi-ai/dist/env-api-keys.js +13 -0
  90. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  91. package/packages/pi-ai/dist/models.generated.d.ts +172 -0
  92. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  93. package/packages/pi-ai/dist/models.generated.js +172 -0
  94. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +64 -0
  96. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -0
  97. package/packages/pi-ai/dist/providers/anthropic-shared.js +668 -0
  98. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -0
  99. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts +5 -0
  100. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts.map +1 -0
  101. package/packages/pi-ai/dist/providers/anthropic-vertex.js +85 -0
  102. package/packages/pi-ai/dist/providers/anthropic-vertex.js.map +1 -0
  103. package/packages/pi-ai/dist/providers/anthropic.d.ts +4 -30
  104. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  105. package/packages/pi-ai/dist/providers/anthropic.js +47 -764
  106. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  107. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  108. package/packages/pi-ai/dist/providers/register-builtins.js +6 -0
  109. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  110. package/packages/pi-ai/dist/types.d.ts +2 -2
  111. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  112. package/packages/pi-ai/dist/types.js.map +1 -1
  113. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  114. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  115. package/packages/pi-ai/package.json +1 -0
  116. package/packages/pi-ai/src/env-api-keys.ts +14 -0
  117. package/packages/pi-ai/src/models.generated.ts +172 -0
  118. package/packages/pi-ai/src/providers/anthropic-shared.ts +761 -0
  119. package/packages/pi-ai/src/providers/anthropic-vertex.ts +130 -0
  120. package/packages/pi-ai/src/providers/anthropic.ts +76 -868
  121. package/packages/pi-ai/src/providers/register-builtins.ts +7 -0
  122. package/packages/pi-ai/src/types.ts +2 -0
  123. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  124. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  126. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  129. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  132. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  133. package/packages/pi-coding-agent/package.json +1 -1
  134. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  135. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  136. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  137. package/pkg/package.json +1 -1
  138. package/src/resources/extensions/cmux/index.ts +57 -1
  139. package/src/resources/extensions/env-utils.ts +31 -0
  140. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  141. package/src/resources/extensions/github-sync/cli.ts +364 -0
  142. package/src/resources/extensions/github-sync/index.ts +93 -0
  143. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  144. package/src/resources/extensions/github-sync/sync.ts +556 -0
  145. package/src/resources/extensions/github-sync/templates.ts +183 -0
  146. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  147. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  148. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  149. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  150. package/src/resources/extensions/github-sync/types.ts +47 -0
  151. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  152. package/src/resources/extensions/gsd/auto-dispatch.ts +100 -9
  153. package/src/resources/extensions/gsd/auto-loop.ts +484 -546
  154. package/src/resources/extensions/gsd/auto-post-unit.ts +92 -42
  155. package/src/resources/extensions/gsd/auto-prompts.ts +150 -48
  156. package/src/resources/extensions/gsd/auto-recovery.ts +42 -0
  157. package/src/resources/extensions/gsd/auto-start.ts +18 -2
  158. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  159. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  160. package/src/resources/extensions/gsd/auto.ts +139 -101
  161. package/src/resources/extensions/gsd/captures.ts +10 -1
  162. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  163. package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
  164. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  165. package/src/resources/extensions/gsd/commands.ts +26 -4
  166. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  167. package/src/resources/extensions/gsd/detection.ts +2 -2
  168. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  169. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  170. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  171. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  172. package/src/resources/extensions/gsd/doctor-providers.ts +64 -10
  173. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  174. package/src/resources/extensions/gsd/doctor.ts +199 -14
  175. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  176. package/src/resources/extensions/gsd/export.ts +1 -1
  177. package/src/resources/extensions/gsd/files.ts +50 -3
  178. package/src/resources/extensions/gsd/forensics.ts +1 -1
  179. package/src/resources/extensions/gsd/git-service.ts +20 -10
  180. package/src/resources/extensions/gsd/guided-flow.ts +110 -38
  181. package/src/resources/extensions/gsd/index.ts +24 -17
  182. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  183. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  184. package/src/resources/extensions/gsd/observability-validator.ts +27 -0
  185. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  186. package/src/resources/extensions/gsd/preferences-types.ts +9 -5
  187. package/src/resources/extensions/gsd/preferences-validation.ts +92 -11
  188. package/src/resources/extensions/gsd/preferences.ts +8 -5
  189. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  190. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
  191. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  192. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  193. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  194. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  195. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  196. package/src/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  197. package/src/resources/extensions/gsd/prompts/run-uat.md +27 -10
  198. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  199. package/src/resources/extensions/gsd/reactive-graph.ts +289 -0
  200. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  201. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  202. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  203. package/src/resources/extensions/gsd/state.ts +1 -1
  204. package/src/resources/extensions/gsd/templates/task-plan.md +11 -3
  205. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  206. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
  207. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  208. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  209. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +191 -3
  210. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +111 -0
  211. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  212. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  213. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +511 -0
  214. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +299 -0
  215. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  216. package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
  217. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  218. package/src/resources/extensions/gsd/types.ts +43 -1
  219. package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
  220. package/src/resources/extensions/gsd/worktree.ts +35 -15
  221. package/src/resources/extensions/mcp-client/index.ts +17 -1
  222. package/src/resources/extensions/remote-questions/status.ts +3 -1
  223. package/src/resources/extensions/remote-questions/store.ts +3 -1
  224. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  225. package/src/resources/extensions/subagent/index.ts +12 -3
  226. package/src/resources/extensions/subagent/isolation.ts +3 -1
  227. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  228. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  229. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  230. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  231. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  232. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  233. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  234. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  235. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  236. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  237. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  238. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  239. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -34,6 +34,7 @@ import { showConfirm } from "../shared/mod.js";
34
34
  import { debugLog } from "./debug-logger.js";
35
35
  import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
36
36
  import { parkMilestone, discardMilestone } from "./milestone-actions.js";
37
+ import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
37
38
 
38
39
  // ─── Re-exports (preserve public API for existing importers) ────────────────
39
40
  export {
@@ -190,8 +191,40 @@ type UIContext = ExtensionContext;
190
191
  /**
191
192
  * Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
192
193
  * This is the only way the wizard triggers work — everything else is the LLM's job.
194
+ *
195
+ * When a unitType is provided, resolves the user's model preference for that
196
+ * phase (e.g., models.planning → "plan-milestone") and applies it before
197
+ * dispatching. This ensures guided-flow dispatches respect the same
198
+ * per-phase model preferences that auto-mode uses.
193
199
  */
194
- function dispatchWorkflow(pi: ExtensionAPI, note: string, customType = "gsd-run"): void {
200
+ async function dispatchWorkflow(
201
+ pi: ExtensionAPI,
202
+ note: string,
203
+ customType = "gsd-run",
204
+ ctx?: ExtensionContext,
205
+ unitType?: string,
206
+ ): Promise<void> {
207
+ // Apply model preference for this unit type (if configured)
208
+ if (ctx && unitType) {
209
+ const modelConfig = resolveModelWithFallbacksForUnit(unitType);
210
+ if (modelConfig) {
211
+ const availableModels = ctx.modelRegistry.getAvailable();
212
+ const modelsToTry = [modelConfig.primary, ...modelConfig.fallbacks];
213
+
214
+ for (const modelId of modelsToTry) {
215
+ // Resolve model from available models (same logic as auto-model-selection)
216
+ const model = resolveAvailableModel(modelId, availableModels, ctx.model?.provider);
217
+ if (!model) continue;
218
+
219
+ const ok = await pi.setModel(model, { persist: false });
220
+ if (ok) {
221
+ debugLog("guided-flow-model-applied", { unitType, model: `${model.provider}/${model.id}` });
222
+ break;
223
+ }
224
+ }
225
+ }
226
+ }
227
+
195
228
  const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(process.env.HOME ?? "~", ".gsd", "agent", "GSD-WORKFLOW.md");
196
229
  const workflow = readFileSync(workflowPath, "utf-8");
197
230
 
@@ -205,6 +238,45 @@ function dispatchWorkflow(pi: ExtensionAPI, note: string, customType = "gsd-run"
205
238
  );
206
239
  }
207
240
 
241
+ /**
242
+ * Resolve a model ID string to a model object from available models.
243
+ * Handles "provider/model" and bare ID formats.
244
+ */
245
+ function resolveAvailableModel<T extends { id: string; provider: string }>(
246
+ modelId: string,
247
+ availableModels: T[],
248
+ currentProvider: string | undefined,
249
+ ): T | undefined {
250
+ const slashIdx = modelId.indexOf("/");
251
+
252
+ if (slashIdx !== -1) {
253
+ const maybeProvider = modelId.substring(0, slashIdx);
254
+ const id = modelId.substring(slashIdx + 1);
255
+
256
+ const knownProviders = new Set(availableModels.map(m => m.provider.toLowerCase()));
257
+ if (knownProviders.has(maybeProvider.toLowerCase())) {
258
+ const match = availableModels.find(
259
+ m => m.provider.toLowerCase() === maybeProvider.toLowerCase()
260
+ && m.id.toLowerCase() === id.toLowerCase(),
261
+ );
262
+ if (match) return match;
263
+ }
264
+
265
+ // Try matching the full string as a model ID (OpenRouter-style)
266
+ const lower = modelId.toLowerCase();
267
+ return availableModels.find(
268
+ m => m.id.toLowerCase() === lower
269
+ || `${m.provider}/${m.id}`.toLowerCase() === lower,
270
+ );
271
+ }
272
+
273
+ // Bare ID — prefer current provider, then first available
274
+ const exactProviderMatch = availableModels.find(
275
+ m => m.id === modelId && m.provider === currentProvider,
276
+ );
277
+ return exactProviderMatch ?? availableModels.find(m => m.id === modelId);
278
+ }
279
+
208
280
  /**
209
281
  * Build the discuss-and-plan prompt for a new milestone.
210
282
  * Used by all three "new milestone" paths (first ever, no active, all complete).
@@ -301,8 +373,8 @@ export async function showHeadlessMilestoneCreation(
301
373
  // Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
302
374
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId };
303
375
 
304
- // Dispatch
305
- dispatchWorkflow(pi, prompt);
376
+ // Dispatch — headless milestone creation is a planning activity
377
+ await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "plan-milestone");
306
378
  }
307
379
 
308
380
 
@@ -467,21 +539,21 @@ export async function showDiscuss(
467
539
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
468
540
  : basePrompt;
469
541
  pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
470
- dispatchWorkflow(pi, seed, "gsd-discuss");
542
+ await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
471
543
  } else if (choice === "discuss_fresh") {
472
544
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
473
545
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
474
546
  pendingAutoStart = { ctx, pi, basePath, milestoneId: mid, step: false };
475
- dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
547
+ await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
476
548
  milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
477
549
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
478
- }), "gsd-discuss");
550
+ }), "gsd-discuss", ctx, "plan-milestone");
479
551
  } else if (choice === "skip_milestone") {
480
552
  const milestoneIds = findMilestoneIds(basePath);
481
553
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
482
554
  const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
483
555
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: false };
484
- dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath));
556
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "plan-milestone");
485
557
  }
486
558
  return;
487
559
  }
@@ -580,7 +652,7 @@ export async function showDiscuss(
580
652
  }
581
653
 
582
654
  const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss });
583
- dispatchWorkflow(pi, prompt, "gsd-discuss");
655
+ await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "plan-slice");
584
656
 
585
657
  // Wait for the discuss session to finish, then loop back to the picker
586
658
  await ctx.waitForIdle();
@@ -722,10 +794,10 @@ async function handleMilestoneActions(
722
794
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
723
795
  const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
724
796
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
725
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
797
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
726
798
  `New milestone ${nextId}.`,
727
799
  basePath
728
- ));
800
+ ), "gsd-run", ctx, "plan-milestone");
729
801
  return true;
730
802
  }
731
803
 
@@ -866,10 +938,10 @@ export async function showSmartEntry(
866
938
  if (isFirst) {
867
939
  // First ever — skip wizard, just ask directly
868
940
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
869
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
941
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
870
942
  `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`,
871
943
  basePath
872
- ));
944
+ ), "gsd-run", ctx, "plan-milestone");
873
945
  } else {
874
946
  const choice = await showNextAction(ctx, {
875
947
  title: "GSD — Get Shit Done",
@@ -887,10 +959,10 @@ export async function showSmartEntry(
887
959
 
888
960
  if (choice === "new_milestone") {
889
961
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
890
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
962
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
891
963
  `New milestone ${nextId}.`,
892
964
  basePath
893
- ));
965
+ ), "gsd-run", ctx, "plan-milestone");
894
966
  }
895
967
  }
896
968
  return;
@@ -926,10 +998,10 @@ export async function showSmartEntry(
926
998
  const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
927
999
 
928
1000
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
929
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
1001
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
930
1002
  `New milestone ${nextId}.`,
931
1003
  basePath
932
- ));
1004
+ ), "gsd-run", ctx, "plan-milestone");
933
1005
  } else if (choice === "status") {
934
1006
  const { fireStatusViaCommand } = await import("./commands.js");
935
1007
  await fireStatusViaCommand(ctx);
@@ -977,24 +1049,24 @@ export async function showSmartEntry(
977
1049
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
978
1050
  : basePrompt;
979
1051
  pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
980
- dispatchWorkflow(pi, seed, "gsd-discuss");
1052
+ await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "plan-milestone");
981
1053
  } else if (choice === "discuss_fresh") {
982
1054
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
983
1055
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
984
1056
  pendingAutoStart = { ctx, pi, basePath, milestoneId, step: stepMode };
985
- dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1057
+ await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
986
1058
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
987
1059
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
988
- }), "gsd-discuss");
1060
+ }), "gsd-discuss", ctx, "plan-milestone");
989
1061
  } else if (choice === "skip_milestone") {
990
1062
  const milestoneIds = findMilestoneIds(basePath);
991
1063
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
992
1064
  const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
993
1065
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
994
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
1066
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
995
1067
  `New milestone ${nextId}.`,
996
1068
  basePath
997
- ));
1069
+ ), "gsd-run", ctx, "plan-milestone");
998
1070
  }
999
1071
  return;
1000
1072
  }
@@ -1051,25 +1123,25 @@ export async function showSmartEntry(
1051
1123
  inlineTemplate("secrets-manifest", "Secrets Manifest"),
1052
1124
  ].join("\n\n---\n\n");
1053
1125
  const secretsOutputPath = relMilestoneFile(basePath, milestoneId, "SECRETS");
1054
- dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
1126
+ await dispatchWorkflow(pi, loadPrompt("guided-plan-milestone", {
1055
1127
  milestoneId, milestoneTitle, secretsOutputPath, inlinedTemplates: planMilestoneTemplates,
1056
- }));
1128
+ }), "gsd-run", ctx, "plan-milestone");
1057
1129
  } else if (choice === "discuss") {
1058
1130
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
1059
1131
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
1060
- dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1132
+ await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1061
1133
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1062
1134
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1063
- }));
1135
+ }), "gsd-run", ctx, "plan-milestone");
1064
1136
  } else if (choice === "skip_milestone") {
1065
1137
  const milestoneIds = findMilestoneIds(basePath);
1066
1138
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1067
1139
  const nextId = nextMilestoneId(milestoneIds, uniqueMilestoneIds);
1068
1140
  pendingAutoStart = { ctx, pi, basePath, milestoneId: nextId, step: stepMode };
1069
- dispatchWorkflow(pi, buildDiscussPrompt(nextId,
1141
+ await dispatchWorkflow(pi, buildDiscussPrompt(nextId,
1070
1142
  `New milestone ${nextId}.`,
1071
1143
  basePath
1072
- ));
1144
+ ), "gsd-run", ctx, "plan-milestone");
1073
1145
  } else if (choice === "discard_milestone") {
1074
1146
  const confirmed = await showConfirm(ctx, {
1075
1147
  title: "Discard milestone?",
@@ -1181,16 +1253,16 @@ export async function showSmartEntry(
1181
1253
  inlineTemplate("plan", "Slice Plan"),
1182
1254
  inlineTemplate("task-plan", "Task Plan"),
1183
1255
  ].join("\n\n---\n\n");
1184
- dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
1256
+ await dispatchWorkflow(pi, loadPrompt("guided-plan-slice", {
1185
1257
  milestoneId, sliceId, sliceTitle, inlinedTemplates: planSliceTemplates,
1186
- }));
1258
+ }), "gsd-run", ctx, "plan-slice");
1187
1259
  } else if (choice === "discuss") {
1188
- dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }));
1260
+ await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }), "gsd-run", ctx, "plan-slice");
1189
1261
  } else if (choice === "research") {
1190
1262
  const researchTemplates = inlineTemplate("research", "Research");
1191
- dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
1263
+ await dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
1192
1264
  milestoneId, sliceId, sliceTitle, inlinedTemplates: researchTemplates,
1193
- }));
1265
+ }), "gsd-run", ctx, "research-slice");
1194
1266
  } else if (choice === "status") {
1195
1267
  const { fireStatusViaCommand } = await import("./commands.js");
1196
1268
  await fireStatusViaCommand(ctx);
@@ -1232,9 +1304,9 @@ export async function showSmartEntry(
1232
1304
  inlineTemplate("slice-summary", "Slice Summary"),
1233
1305
  inlineTemplate("uat", "UAT"),
1234
1306
  ].join("\n\n---\n\n");
1235
- dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
1307
+ await dispatchWorkflow(pi, loadPrompt("guided-complete-slice", {
1236
1308
  workingDirectory: basePath, milestoneId, sliceId, sliceTitle, inlinedTemplates: completeSliceTemplates,
1237
- }));
1309
+ }), "gsd-run", ctx, "complete-slice");
1238
1310
  } else if (choice === "status") {
1239
1311
  const { fireStatusViaCommand } = await import("./commands.js");
1240
1312
  await fireStatusViaCommand(ctx);
@@ -1297,14 +1369,14 @@ export async function showSmartEntry(
1297
1369
 
1298
1370
  if (choice === "execute") {
1299
1371
  if (hasInterrupted) {
1300
- dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
1372
+ await dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
1301
1373
  milestoneId, sliceId,
1302
- }));
1374
+ }), "gsd-run", ctx, "execute-task");
1303
1375
  } else {
1304
1376
  const executeTaskTemplates = inlineTemplate("task-summary", "Task Summary");
1305
- dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
1377
+ await dispatchWorkflow(pi, loadPrompt("guided-execute-task", {
1306
1378
  milestoneId, sliceId, taskId, taskTitle, inlinedTemplates: executeTaskTemplates,
1307
- }));
1379
+ }), "gsd-run", ctx, "execute-task");
1308
1380
  }
1309
1381
  } else if (choice === "status") {
1310
1382
  const { fireStatusViaCommand } = await import("./commands.js");
@@ -60,6 +60,8 @@ import { join } from "node:path";
60
60
  import { existsSync, readFileSync } from "node:fs";
61
61
  import { homedir } from "node:os";
62
62
  import { shortcutDesc } from "../shared/mod.js";
63
+
64
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
63
65
  import { Text } from "@gsd/pi-tui";
64
66
  import { pauseAutoForProviderError, classifyProviderError } from "./provider-error-pause.js";
65
67
  import { toPosixPath } from "../shared/mod.js";
@@ -73,7 +75,7 @@ import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../cmux/index.js"
73
75
 
74
76
  function warnDeprecatedAgentInstructions(): void {
75
77
  const paths = [
76
- join(homedir(), ".gsd", "agent-instructions.md"),
78
+ join(gsdHome, "agent-instructions.md"),
77
79
  join(process.cwd(), ".gsd", "agent-instructions.md"),
78
80
  ];
79
81
  for (const p of paths) {
@@ -90,6 +92,23 @@ function warnDeprecatedAgentInstructions(): void {
90
92
  // ── Depth verification state ──────────────────────────────────────────────
91
93
  let depthVerificationDone = false;
92
94
 
95
+ // ── DB lazy-open helper ───────────────────────────────────────────────────
96
+ // In manual sessions (no auto-mode), the DB is never opened by bootstrapAutoSession.
97
+ // This helper ensures the DB is lazily opened on first tool call that needs it.
98
+ async function ensureDbOpen(): Promise<boolean> {
99
+ try {
100
+ const db = await import("./gsd-db.js");
101
+ if (db.isDbAvailable()) return true;
102
+ const dbPath = join(process.cwd(), ".gsd", "gsd.db");
103
+ if (existsSync(dbPath)) {
104
+ return db.openDatabase(dbPath);
105
+ }
106
+ return false;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+
93
112
  // ── Queue phase tracking ──────────────────────────────────────────────────
94
113
  // When true, the LLM is in a queue flow writing CONTEXT.md files.
95
114
  // The write-gate applies during queue flows just like discussion flows.
@@ -298,12 +317,8 @@ export default function (pi: ExtensionAPI) {
298
317
  when_context: Type.Optional(Type.String({ description: "When/context for the decision (e.g. milestone ID)" })),
299
318
  }),
300
319
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
301
- // Check DB availability
302
- let dbAvailable = false;
303
- try {
304
- const db = await import("./gsd-db.js");
305
- dbAvailable = db.isDbAvailable();
306
- } catch { /* dynamic import failed */ }
320
+ // Ensure DB is open (lazy-open on first tool call in manual sessions)
321
+ const dbAvailable = await ensureDbOpen();
307
322
 
308
323
  if (!dbAvailable) {
309
324
  return {
@@ -365,11 +380,7 @@ export default function (pi: ExtensionAPI) {
365
380
  supporting_slices: Type.Optional(Type.String({ description: "Supporting slices" })),
366
381
  }),
367
382
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
368
- let dbAvailable = false;
369
- try {
370
- const db = await import("./gsd-db.js");
371
- dbAvailable = db.isDbAvailable();
372
- } catch { /* dynamic import failed */ }
383
+ const dbAvailable = await ensureDbOpen();
373
384
 
374
385
  if (!dbAvailable) {
375
386
  return {
@@ -439,11 +450,7 @@ export default function (pi: ExtensionAPI) {
439
450
  content: Type.String({ description: "The full markdown content of the artifact" }),
440
451
  }),
441
452
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
442
- let dbAvailable = false;
443
- try {
444
- const db = await import("./gsd-db.js");
445
- dbAvailable = db.isDbAvailable();
446
- } catch { /* dynamic import failed */ }
453
+ const dbAvailable = await ensureDbOpen();
447
454
 
448
455
  if (!dbAvailable) {
449
456
  return {
@@ -3,7 +3,7 @@
3
3
  // Zero Pi dependencies — uses only exported helpers from files.ts.
4
4
 
5
5
  import { splitFrontmatter, parseFrontmatterMap, extractBoldField } from '../files.js';
6
- import { normalizeStringArray } from '../../shared/mod.js';
6
+ import { normalizeStringArray } from '../../shared/format-utils.js';
7
7
 
8
8
  import type {
9
9
  PlanningRoadmap,
@@ -671,6 +671,43 @@ export function nativeAddAll(basePath: string): void {
671
671
  gitFileExec(basePath, ["add", "-A"]);
672
672
  }
673
673
 
674
+ /**
675
+ * Stage all files with pathspec exclusions (git add -A -- ':!pattern' ...).
676
+ * Excluded paths are never hashed by git, preventing hangs on large
677
+ * untracked artifact trees (57GB+, 11K+ files). See #1605.
678
+ *
679
+ * Falls back to plain `git add -A` when no exclusions are provided.
680
+ * Always uses the CLI path (not libgit2) because libgit2's add_all
681
+ * does not support pathspec exclusion syntax.
682
+ *
683
+ * When excluded paths are already covered by .gitignore, git may exit
684
+ * with code 1 and an "ignored by .gitignore" warning. This is harmless
685
+ * (the staging succeeds for all non-ignored files) and is suppressed.
686
+ */
687
+ export function nativeAddAllWithExclusions(basePath: string, exclusions: readonly string[]): void {
688
+ if (exclusions.length === 0) {
689
+ nativeAddAll(basePath);
690
+ return;
691
+ }
692
+ const pathspecs = exclusions.map(e => `:!${e}`);
693
+ try {
694
+ execFileSync("git", ["add", "-A", "--", ...pathspecs], {
695
+ cwd: basePath,
696
+ stdio: ["ignore", "pipe", "pipe"],
697
+ encoding: "utf-8",
698
+ env: GIT_NO_PROMPT_ENV,
699
+ });
700
+ } catch (err: unknown) {
701
+ // git exits 1 when pathspec exclusions reference paths already covered
702
+ // by .gitignore. The staging itself succeeds — only suppress that case.
703
+ const stderr = (err as { stderr?: string })?.stderr ?? "";
704
+ if (stderr.includes("ignored by one of your .gitignore files")) {
705
+ return;
706
+ }
707
+ throw new GSDError(GSD_GIT_ERROR, `git add -A with exclusions failed in ${basePath}: ${getErrorMessage(err)}`);
708
+ }
709
+ }
710
+
674
711
  /**
675
712
  * Stage specific files.
676
713
  * Native: libgit2 index add.
@@ -235,6 +235,33 @@ export function validateTaskPlanContent(file: string, content: string): Validati
235
235
  }
236
236
  }
237
237
 
238
+ // Rule: Inputs and Expected Output should contain backtick-wrapped file paths
239
+ const inputsSection = getSection(content, "Inputs", 2);
240
+ const outputSection = getSection(content, "Expected Output", 2);
241
+ const backtickPathPattern = /`[^`]*[./][^`]*`/;
242
+
243
+ if (outputSection === null || !backtickPathPattern.test(outputSection)) {
244
+ issues.push({
245
+ severity: "warning",
246
+ scope: "task-plan",
247
+ file,
248
+ ruleId: "missing_output_file_paths",
249
+ message: "Task plan `## Expected Output` is missing or has no backtick-wrapped file paths.",
250
+ suggestion: "List concrete output file paths in backticks (e.g. `src/types.ts`). These are machine-parsed to derive task dependencies.",
251
+ });
252
+ }
253
+
254
+ if (inputsSection !== null && inputsSection.trim().length > 0 && !backtickPathPattern.test(inputsSection)) {
255
+ issues.push({
256
+ severity: "info",
257
+ scope: "task-plan",
258
+ file,
259
+ ruleId: "missing_input_file_paths",
260
+ message: "Task plan `## Inputs` has content but no backtick-wrapped file paths.",
261
+ suggestion: "List input file paths in backticks (e.g. `src/config.json`). These are machine-parsed to derive task dependencies.",
262
+ });
263
+ }
264
+
238
265
  // ── Observability rules (gated by runtime relevance) ──
239
266
 
240
267
  const relevant = textSuggestsObservabilityRelevant(content);
@@ -295,18 +295,6 @@ export function resolveInlineLevel(): InlineLevel {
295
295
  }
296
296
  }
297
297
 
298
- /**
299
- * Resolve the compression strategy from the active token profile.
300
- * budget/balanced -> "compress", quality -> "truncate".
301
- * Explicit preference always wins.
302
- */
303
- export function resolveCompressionStrategy(): import("./types.js").CompressionStrategy {
304
- const prefs = loadEffectiveGSDPreferences();
305
- if (prefs?.preferences.compression_strategy) return prefs.preferences.compression_strategy;
306
- const profile = resolveEffectiveProfile();
307
- return profile === "quality" ? "truncate" : "compress";
308
- }
309
-
310
298
  /**
311
299
  * Resolve the context selection mode from the active token profile.
312
300
  * budget -> "smart", balanced/quality -> "full".
@@ -16,10 +16,11 @@ import type {
16
16
  InlineLevel,
17
17
  PhaseSkipPreferences,
18
18
  ParallelConfig,
19
- CompressionStrategy,
20
19
  ContextSelectionMode,
20
+ ReactiveExecutionConfig,
21
21
  } from "./types.js";
22
22
  import type { DynamicRoutingConfig } from "./model-router.js";
23
+ import type { GitHubSyncConfig } from "../github-sync/types.js";
23
24
 
24
25
  // ─── Workflow Modes ──────────────────────────────────────────────────────────
25
26
 
@@ -83,15 +84,16 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
83
84
  "verification_auto_fix",
84
85
  "verification_max_retries",
85
86
  "search_provider",
86
- "compression_strategy",
87
87
  "context_selection",
88
88
  "widget_mode",
89
+ "reactive_execution",
90
+ "github",
89
91
  ]);
90
92
 
91
93
  /** Canonical list of all dispatch unit types. */
92
94
  export const KNOWN_UNIT_TYPES = [
93
95
  "research-milestone", "plan-milestone", "research-slice", "plan-slice",
94
- "execute-task", "complete-slice", "replan-slice", "reassess-roadmap",
96
+ "execute-task", "reactive-execute", "complete-slice", "replan-slice", "reassess-roadmap",
95
97
  "run-uat", "complete-milestone",
96
98
  ] as const;
97
99
  export type UnitType = (typeof KNOWN_UNIT_TYPES)[number];
@@ -209,12 +211,14 @@ export interface GSDPreferences {
209
211
  verification_max_retries?: number;
210
212
  /** Search provider preference. "brave"/"tavily"/"ollama" force that backend and disable native Anthropic search. "native" forces native only. "auto" = current default behavior. */
211
213
  search_provider?: "brave" | "tavily" | "ollama" | "native" | "auto";
212
- /** Compression strategy for context that exceeds budget. "truncate" (default) drops sections, "compress" applies heuristic compression first. */
213
- compression_strategy?: CompressionStrategy;
214
214
  /** Context selection mode for file inlining. "full" inlines entire files, "smart" uses semantic chunking. Default derived from token profile. */
215
215
  context_selection?: ContextSelectionMode;
216
216
  /** Default widget display mode for auto-mode dashboard. "full" | "small" | "min" | "off". Default: "full". */
217
217
  widget_mode?: "full" | "small" | "min" | "off";
218
+ /** Reactive (graph-derived parallel) task execution within slices. Disabled by default. */
219
+ reactive_execution?: ReactiveExecutionConfig;
220
+ /** GitHub sync configuration. Opt-in: syncs GSD events to GitHub Issues, Milestones, and PRs. */
221
+ github?: GitHubSyncConfig;
218
222
  }
219
223
 
220
224
  export interface LoadedGSDPreferences {