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
@@ -20,7 +20,7 @@ import {
20
20
  resolveSkillDiscoveryMode,
21
21
  getIsolationMode,
22
22
  } from "./preferences.js";
23
- import { ensureGsdSymlink } from "./repo-identity.js";
23
+ import { ensureGsdSymlink, validateProjectId } from "./repo-identity.js";
24
24
  import { migrateToExternalState, recoverFailedMigration } from "./migrate-external.js";
25
25
  import { collectSecretsFromManifest } from "../get-secrets-from-user.js";
26
26
  import { gsdRoot, resolveMilestoneFile, milestonesDir } from "./paths.js";
@@ -130,6 +130,16 @@ export async function bootstrapAutoSession(
130
130
  }
131
131
 
132
132
  try {
133
+ // Validate GSD_PROJECT_ID early so the user gets immediate feedback
134
+ const customProjectId = process.env.GSD_PROJECT_ID;
135
+ if (customProjectId && !validateProjectId(customProjectId)) {
136
+ ctx.ui.notify(
137
+ `GSD_PROJECT_ID must contain only alphanumeric characters, hyphens, and underscores. Got: "${customProjectId}"`,
138
+ "error",
139
+ );
140
+ return releaseLockAndReturn();
141
+ }
142
+
133
143
  // Ensure git repo exists
134
144
  if (!nativeIsRepo(base)) {
135
145
  const mainBranch =
@@ -429,10 +439,16 @@ export async function bootstrapAutoSession(
429
439
  s.originalBasePath = base;
430
440
 
431
441
  const isUnderGsdWorktrees = (p: string): boolean => {
442
+ // Direct layout: /.gsd/worktrees/
432
443
  const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
433
444
  if (p.includes(marker)) return true;
434
445
  const worktreesSuffix = `${pathSep}.gsd${pathSep}worktrees`;
435
- return p.endsWith(worktreesSuffix);
446
+ if (p.endsWith(worktreesSuffix)) return true;
447
+ // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
448
+ const symlinkRe = new RegExp(
449
+ `\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees(?:\\${pathSep}|$)`,
450
+ );
451
+ return symlinkRe.test(p);
436
452
  };
437
453
 
438
454
  if (
@@ -22,6 +22,8 @@ import { join, sep as pathSep } from "node:path";
22
22
  import { homedir } from "node:os";
23
23
  import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
24
24
 
25
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
26
+
25
27
  // ─── Project Root → Worktree Sync ─────────────────────────────────────────
26
28
 
27
29
  /**
@@ -111,7 +113,7 @@ export function syncStateToProjectRoot(
111
113
  */
112
114
  export function readResourceVersion(): string | null {
113
115
  const agentDir =
114
- process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent");
116
+ process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
115
117
  const manifestPath = join(agentDir, "managed-resources.json");
116
118
  try {
117
119
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
@@ -153,9 +155,18 @@ export function checkResourcesStale(
153
155
  * Returns the corrected base path.
154
156
  */
155
157
  export function escapeStaleWorktree(base: string): string {
156
- const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
157
- const idx = base.indexOf(marker);
158
- if (idx === -1) return base;
158
+ // Direct layout: /.gsd/worktrees/
159
+ const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
160
+ let idx = base.indexOf(directMarker);
161
+ if (idx === -1) {
162
+ // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
163
+ const symlinkRe = new RegExp(
164
+ `\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`,
165
+ );
166
+ const match = base.match(symlinkRe);
167
+ if (!match || match.index === undefined) return base;
168
+ idx = match.index;
169
+ }
159
170
 
160
171
  // base is inside .gsd/worktrees/<something> — extract the project root
161
172
  const projectRoot = base.slice(0, idx);
@@ -37,13 +37,13 @@ import {
37
37
  resolveGitHeadPath,
38
38
  nudgeGitBranchCache,
39
39
  } from "./worktree.js";
40
- import { MergeConflictError, readIntegrationBranch } from "./git-service.js";
40
+ import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
41
41
  import { parseRoadmap } from "./files.js";
42
42
  import { loadEffectiveGSDPreferences } from "./preferences.js";
43
43
  import {
44
44
  nativeGetCurrentBranch,
45
45
  nativeWorkingTreeStatus,
46
- nativeAddAll,
46
+ nativeAddAllWithExclusions,
47
47
  nativeCommit,
48
48
  nativeCheckoutBranch,
49
49
  nativeMergeSquash,
@@ -768,7 +768,7 @@ function autoCommitDirtyState(cwd: string): boolean {
768
768
  try {
769
769
  const status = nativeWorkingTreeStatus(cwd);
770
770
  if (!status) return false;
771
- nativeAddAll(cwd);
771
+ nativeAddAllWithExclusions(cwd, RUNTIME_EXCLUSION_PATHS);
772
772
  const result = nativeCommit(
773
773
  cwd,
774
774
  "chore: auto-commit before milestone merge",
@@ -536,129 +536,167 @@ export async function stopAuto(
536
536
  if (!s.active && !s.paused) return;
537
537
  const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
538
538
  const reasonSuffix = reason ? ` — ${reason}` : "";
539
- clearUnitTimeout();
540
- if (lockBase()) clearLock(lockBase());
541
- if (lockBase()) releaseSessionLock(lockBase());
542
- clearSkillSnapshot();
543
- resetSkillTelemetry();
544
539
 
545
- // Remove SIGTERM handler registered at auto-mode start
546
- deregisterSigtermHandler();
540
+ try {
541
+ // ── Step 1: Timers and locks ──
542
+ try {
543
+ clearUnitTimeout();
544
+ if (lockBase()) clearLock(lockBase());
545
+ if (lockBase()) releaseSessionLock(lockBase());
546
+ } catch (e) {
547
+ debugLog("stop-cleanup-locks", { error: e instanceof Error ? e.message : String(e) });
548
+ }
547
549
 
548
- // ── Auto-worktree: exit worktree and reset s.basePath on stop ──
549
- if (s.currentMilestoneId) {
550
- const notifyCtx = ctx
551
- ? { notify: ctx.ui.notify.bind(ctx.ui) }
552
- : { notify: () => {} };
553
- buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
554
- preserveBranch: true,
555
- });
556
- }
550
+ // ── Step 2: Skill state ──
551
+ try {
552
+ clearSkillSnapshot();
553
+ resetSkillTelemetry();
554
+ } catch (e) {
555
+ debugLog("stop-cleanup-skills", { error: e instanceof Error ? e.message : String(e) });
556
+ }
557
557
 
558
- // ── DB cleanup: close the SQLite connection ──
559
- if (isDbAvailable()) {
558
+ // ── Step 3: SIGTERM handler ──
560
559
  try {
561
- const { closeDatabase } = await import("./gsd-db.js");
562
- closeDatabase();
560
+ deregisterSigtermHandler();
563
561
  } catch (e) {
564
- debugLog("db-close-failed", {
565
- error: e instanceof Error ? e.message : String(e),
566
- });
562
+ debugLog("stop-cleanup-sigterm", { error: e instanceof Error ? e.message : String(e) });
567
563
  }
568
- }
569
564
 
570
- if (s.originalBasePath) {
571
- s.basePath = s.originalBasePath;
565
+ // ── Step 4: Auto-worktree exit ──
572
566
  try {
573
- process.chdir(s.basePath);
574
- } catch {
575
- /* best-effort */
567
+ if (s.currentMilestoneId) {
568
+ const notifyCtx = ctx
569
+ ? { notify: ctx.ui.notify.bind(ctx.ui) }
570
+ : { notify: () => {} };
571
+ buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
572
+ preserveBranch: true,
573
+ });
574
+ }
575
+ } catch (e) {
576
+ debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
576
577
  }
577
- }
578
578
 
579
- const ledger = getLedger();
580
- if (ledger && ledger.units.length > 0) {
581
- const totals = getProjectTotals(ledger.units);
582
- ctx?.ui.notify(
583
- `Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`,
584
- "info",
585
- );
586
- } else {
587
- ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
588
- }
579
+ // ── Step 5: DB cleanup ──
580
+ if (isDbAvailable()) {
581
+ try {
582
+ const { closeDatabase } = await import("./gsd-db.js");
583
+ closeDatabase();
584
+ } catch (e) {
585
+ debugLog("db-close-failed", {
586
+ error: e instanceof Error ? e.message : String(e),
587
+ });
588
+ }
589
+ }
589
590
 
590
- if (s.basePath) {
591
+ // ── Step 6: Restore basePath and chdir ──
591
592
  try {
592
- await rebuildState(s.basePath);
593
+ if (s.originalBasePath) {
594
+ s.basePath = s.originalBasePath;
595
+ try {
596
+ process.chdir(s.basePath);
597
+ } catch {
598
+ /* best-effort */
599
+ }
600
+ }
593
601
  } catch (e) {
594
- debugLog("stop-rebuild-state-failed", {
595
- error: e instanceof Error ? e.message : String(e),
596
- });
602
+ debugLog("stop-cleanup-basepath", { error: e instanceof Error ? e.message : String(e) });
597
603
  }
598
- }
599
604
 
600
- clearCmuxSidebar(loadedPreferences);
601
- logCmuxEvent(
602
- loadedPreferences,
603
- `Auto-mode stopped${reasonSuffix || ""}.`,
604
- reason?.startsWith("Blocked:") ? "warning" : "info",
605
- );
605
+ // ── Step 7: Ledger notification ──
606
+ try {
607
+ const ledger = getLedger();
608
+ if (ledger && ledger.units.length > 0) {
609
+ const totals = getProjectTotals(ledger.units);
610
+ ctx?.ui.notify(
611
+ `Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`,
612
+ "info",
613
+ );
614
+ } else {
615
+ ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
616
+ }
617
+ } catch (e) {
618
+ debugLog("stop-cleanup-ledger", { error: e instanceof Error ? e.message : String(e) });
619
+ }
606
620
 
607
- if (isDebugEnabled()) {
608
- const logPath = writeDebugSummary();
609
- if (logPath) {
610
- ctx?.ui.notify(`Debug log written → ${logPath}`, "info");
621
+ // ── Step 8: Rebuild state ──
622
+ if (s.basePath) {
623
+ try {
624
+ await rebuildState(s.basePath);
625
+ } catch (e) {
626
+ debugLog("stop-rebuild-state-failed", {
627
+ error: e instanceof Error ? e.message : String(e),
628
+ });
629
+ }
611
630
  }
612
- }
613
631
 
614
- resetMetrics();
615
- resetRoutingHistory();
616
- resetHookState();
617
- if (s.basePath) clearPersistedHookState(s.basePath);
632
+ // ── Step 9: Cmux sidebar / event log ──
633
+ try {
634
+ clearCmuxSidebar(loadedPreferences);
635
+ logCmuxEvent(
636
+ loadedPreferences,
637
+ `Auto-mode stopped${reasonSuffix || ""}.`,
638
+ reason?.startsWith("Blocked:") ? "warning" : "info",
639
+ );
640
+ } catch (e) {
641
+ debugLog("stop-cleanup-cmux", { error: e instanceof Error ? e.message : String(e) });
642
+ }
618
643
 
619
- // Remove paused-session metadata if present (#1383)
620
- try {
621
- const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
622
- if (existsSync(pausedPath)) unlinkSync(pausedPath);
623
- } catch { /* non-fatal */ }
644
+ // ── Step 10: Debug summary ──
645
+ try {
646
+ if (isDebugEnabled()) {
647
+ const logPath = writeDebugSummary();
648
+ if (logPath) {
649
+ ctx?.ui.notify(`Debug log written → ${logPath}`, "info");
650
+ }
651
+ }
652
+ } catch (e) {
653
+ debugLog("stop-cleanup-debug", { error: e instanceof Error ? e.message : String(e) });
654
+ }
624
655
 
625
- s.active = false;
626
- s.paused = false;
627
- s.stepMode = false;
628
- s.unitDispatchCount.clear();
629
- s.unitRecoveryCount.clear();
630
- clearInFlightTools();
631
- s.lastBudgetAlertLevel = 0;
632
- s.lastStateRebuildAt = 0;
633
- s.unitLifetimeDispatches.clear();
634
- s.currentUnit = null;
635
- s.autoModeStartModel = null;
636
- s.currentMilestoneId = null;
637
- s.originalBasePath = "";
638
- s.completedUnits = [];
639
- s.pendingQuickTasks = [];
640
- clearSliceProgressCache();
641
- clearActivityLogState();
642
- resetProactiveHealing();
643
- s.pendingCrashRecovery = null;
644
- s.pendingVerificationRetry = null;
645
- s.verificationRetryCount.clear();
646
- s.pausedSessionFile = null;
647
- ctx?.ui.setStatus("gsd-auto", undefined);
648
- ctx?.ui.setWidget("gsd-progress", undefined);
649
- ctx?.ui.setFooter(undefined);
656
+ // ── Step 11: Reset metrics, routing, hooks ──
657
+ try {
658
+ resetMetrics();
659
+ resetRoutingHistory();
660
+ resetHookState();
661
+ if (s.basePath) clearPersistedHookState(s.basePath);
662
+ } catch (e) {
663
+ debugLog("stop-cleanup-metrics", { error: e instanceof Error ? e.message : String(e) });
664
+ }
650
665
 
651
- if (pi && ctx && s.originalModelId && s.originalModelProvider) {
652
- const original = ctx.modelRegistry.find(
653
- s.originalModelProvider,
654
- s.originalModelId,
655
- );
656
- if (original) await pi.setModel(original);
657
- s.originalModelId = null;
658
- s.originalModelProvider = null;
659
- }
666
+ // ── Step 12: Remove paused-session metadata (#1383) ──
667
+ try {
668
+ const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
669
+ if (existsSync(pausedPath)) unlinkSync(pausedPath);
670
+ } catch { /* non-fatal */ }
660
671
 
661
- s.cmdCtx = null;
672
+ // ── Step 13: Restore original model (before reset clears IDs) ──
673
+ try {
674
+ if (pi && ctx && s.originalModelId && s.originalModelProvider) {
675
+ const original = ctx.modelRegistry.find(
676
+ s.originalModelProvider,
677
+ s.originalModelId,
678
+ );
679
+ if (original) await pi.setModel(original);
680
+ }
681
+ } catch (e) {
682
+ debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
683
+ }
684
+ } finally {
685
+ // ── Critical invariants: these MUST execute regardless of errors ──
686
+ // External cleanup (not covered by session reset)
687
+ clearInFlightTools();
688
+ clearSliceProgressCache();
689
+ clearActivityLogState();
690
+ resetProactiveHealing();
691
+
692
+ // UI cleanup
693
+ ctx?.ui.setStatus("gsd-auto", undefined);
694
+ ctx?.ui.setWidget("gsd-progress", undefined);
695
+ ctx?.ui.setFooter(undefined);
696
+
697
+ // Reset all session state in one call
698
+ s.reset();
699
+ }
662
700
  }
663
701
 
664
702
  /**
@@ -59,8 +59,17 @@ const VALID_CLASSIFICATIONS: readonly string[] = [
59
59
  */
60
60
  export function resolveCapturesPath(basePath: string): string {
61
61
  const resolved = resolve(basePath);
62
+ // Direct layout: /.gsd/worktrees/
62
63
  const worktreeMarker = `${sep}.gsd${sep}worktrees${sep}`;
63
- const idx = resolved.indexOf(worktreeMarker);
64
+ let idx = resolved.indexOf(worktreeMarker);
65
+ if (idx === -1) {
66
+ // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
67
+ const symlinkRe = new RegExp(
68
+ `\\${sep}\\.gsd\\${sep}projects\\${sep}[a-f0-9]+\\${sep}worktrees\\${sep}`,
69
+ );
70
+ const match = resolved.match(symlinkRe);
71
+ if (match && match.index !== undefined) idx = match.index;
72
+ }
64
73
  if (idx !== -1) {
65
74
  // basePath is inside a worktree — resolve to project root
66
75
  const projectRoot = resolved.slice(0, idx);
@@ -11,6 +11,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFile
11
11
  import { dirname, join } from "node:path";
12
12
  import { homedir } from "node:os";
13
13
 
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
15
+
14
16
  // ─── Types (mirrored from extension-registry.ts) ────────────────────────────
15
17
 
16
18
  interface ExtensionManifest {
@@ -48,11 +50,11 @@ interface ExtensionRegistry {
48
50
  // ─── Registry I/O ───────────────────────────────────────────────────────────
49
51
 
50
52
  function getRegistryPath(): string {
51
- return join(homedir(), ".gsd", "extensions", "registry.json");
53
+ return join(gsdHome, "extensions", "registry.json");
52
54
  }
53
55
 
54
56
  function getAgentExtensionsDir(): string {
55
- return join(homedir(), ".gsd", "agent", "extensions");
57
+ return join(gsdHome, "agent", "extensions");
56
58
  }
57
59
 
58
60
  function loadRegistry(): ExtensionRegistry {
@@ -15,6 +15,7 @@ import { appendOverride, appendKnowledge } from "./files.js";
15
15
  import {
16
16
  formatDoctorIssuesForPrompt,
17
17
  formatDoctorReport,
18
+ formatDoctorReportJson,
18
19
  runGSDDoctor,
19
20
  selectDoctorScope,
20
21
  filterDoctorIssues,
@@ -43,16 +44,30 @@ export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined,
43
44
 
44
45
  export async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
45
46
  const trimmed = args.trim();
46
- const parts = trimmed ? trimmed.split(/\s+/) : [];
47
+ // Extract flags before positional parsing
48
+ const jsonMode = trimmed.includes("--json");
49
+ const dryRun = trimmed.includes("--dry-run");
50
+ const includeBuild = trimmed.includes("--build");
51
+ const includeTests = trimmed.includes("--test");
52
+ const stripped = trimmed.replace(/--json|--dry-run|--build|--test/g, "").trim();
53
+ const parts = stripped ? stripped.split(/\s+/) : [];
47
54
  const mode = parts[0] === "fix" || parts[0] === "heal" || parts[0] === "audit" ? parts[0] : "doctor";
48
55
  const requestedScope = mode === "doctor" ? parts[0] : parts[1];
49
56
  const scope = await selectDoctorScope(projectRoot(), requestedScope);
50
57
  const effectiveScope = mode === "audit" ? requestedScope : scope;
51
58
  const report = await runGSDDoctor(projectRoot(), {
52
- fix: mode === "fix" || mode === "heal",
59
+ fix: mode === "fix" || mode === "heal" || dryRun,
60
+ dryRun,
53
61
  scope: effectiveScope,
62
+ includeBuild,
63
+ includeTests,
54
64
  });
55
65
 
66
+ if (jsonMode) {
67
+ ctx.ui.notify(formatDoctorReportJson(report), "info");
68
+ return;
69
+ }
70
+
56
71
  const reportText = formatDoctorReport(report, {
57
72
  scope: effectiveScope,
58
73
  includeWarnings: mode === "audit",
@@ -745,7 +745,7 @@ export function serializePreferencesToFrontmatter(prefs: Record<string, unknown>
745
745
  "dynamic_routing", "token_profile", "phases", "parallel",
746
746
  "auto_visualize", "auto_report",
747
747
  "verification_commands", "verification_auto_fix", "verification_max_retries",
748
- "search_provider", "compression_strategy", "context_selection",
748
+ "search_provider", "context_selection",
749
749
  ];
750
750
 
751
751
  const seen = new Set<string>();
@@ -4,12 +4,14 @@
4
4
  * One command, one wizard. Routes to smart entry or status.
5
5
  */
6
6
 
7
- import type { ExtensionAPI, ExtensionCommandContext } from "@gsd/pi-coding-agent";
7
+ import { importExtensionModule, type ExtensionAPI, type ExtensionCommandContext } from "@gsd/pi-coding-agent";
8
8
  import type { GSDState } from "./types.js";
9
9
  import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
10
10
  import { homedir } from "node:os";
11
11
  import { join } from "node:path";
12
12
  import { gsdRoot } from "./paths.js";
13
+
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
13
15
  import { enableDebug } from "./debug-logger.js";
14
16
  import { deriveState } from "./state.js";
15
17
  import { GSDDashboardOverlay } from "./dashboard-overlay.js";
@@ -159,7 +161,7 @@ async function guardRemoteSession(
159
161
 
160
162
  export function registerGSDCommand(pi: ExtensionAPI): void {
161
163
  pi.registerCommand("gsd", {
162
- description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|update",
164
+ description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update",
163
165
  getArgumentCompletions: (prefix: string) => {
164
166
  const subcommands = [
165
167
  { cmd: "help", desc: "Categorized command reference with descriptions" },
@@ -210,7 +212,11 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
210
212
  { cmd: "templates", desc: "List available workflow templates" },
211
213
  { cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
212
214
  ];
215
+ const hasTrailingSpace = prefix.endsWith(" ");
213
216
  const parts = prefix.trim().split(/\s+/);
217
+ if (hasTrailingSpace && parts.length >= 1) {
218
+ parts.push("");
219
+ }
214
220
 
215
221
  if (parts.length <= 1) {
216
222
  return subcommands
@@ -478,7 +484,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
478
484
  if (parts.length === 3 && ["enable", "disable", "info"].includes(parts[1])) {
479
485
  const idPrefix = parts[2] ?? "";
480
486
  try {
481
- const extDir = join(homedir(), ".gsd", "agent", "extensions");
487
+ const extDir = join(gsdHome, "agent", "extensions");
482
488
  const ids: { id: string; name: string }[] = [];
483
489
  for (const entry of readdirSync(extDir, { withFileTypes: true })) {
484
490
  if (!entry.isDirectory()) continue;
@@ -509,6 +515,10 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
509
515
  { cmd: "fix", desc: "Auto-fix detected issues" },
510
516
  { cmd: "heal", desc: "AI-driven deep healing" },
511
517
  { cmd: "audit", desc: "Run health audit without fixing" },
518
+ { cmd: "--dry-run", desc: "Show what --fix would change without applying" },
519
+ { cmd: "--json", desc: "Output report as JSON (CI/tooling friendly)" },
520
+ { cmd: "--build", desc: "Include slow build health check (npm run build)" },
521
+ { cmd: "--test", desc: "Include slow test health check (npm test)" },
512
522
  ];
513
523
 
514
524
  if (parts.length <= 2) {
@@ -536,6 +546,18 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
536
546
  .map((p) => ({ value: `dispatch ${p.cmd}`, label: p.cmd, description: p.desc }));
537
547
  }
538
548
 
549
+ if (parts[0] === "rate" && parts.length <= 2) {
550
+ const tierPrefix = parts[1] ?? "";
551
+ const tiers = [
552
+ { cmd: "over", desc: "Model was overqualified for this task" },
553
+ { cmd: "ok", desc: "Model was appropriate for this task" },
554
+ { cmd: "under", desc: "Model was underqualified for this task" },
555
+ ];
556
+ return tiers
557
+ .filter((t) => t.cmd.startsWith(tierPrefix))
558
+ .map((t) => ({ value: `rate ${t.cmd}`, label: t.cmd, description: t.desc }));
559
+ }
560
+
539
561
  return [];
540
562
  },
541
563
 
@@ -563,7 +585,7 @@ export async function handleGSDCommand(
563
585
  }
564
586
 
565
587
  if (trimmed === "widget" || trimmed.startsWith("widget ")) {
566
- const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await import("./auto-dashboard.js");
588
+ const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await importExtensionModule<typeof import("./auto-dashboard.js")>(import.meta.url, "./auto-dashboard.js");
567
589
  const arg = trimmed.replace(/^widget\s*/, "").trim();
568
590
  if (arg === "full" || arg === "small" || arg === "min" || arg === "off") {
569
591
  setWidgetMode(arg);
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import { type TokenProvider, getCharsPerToken } from "./token-counter.js";
12
- import { compressToTarget } from "./prompt-compressor.js";
13
12
 
14
13
  // ─── Budget ratio constants ──────────────────────────────────────────────────
15
14
  // Percentages of total context window allocated to each budget category.
@@ -202,22 +201,13 @@ export function resolveExecutorContextWindow(
202
201
  }
203
202
 
204
203
  /**
205
- * Smart context reduction: compress first, then truncate if still over budget.
206
- * Returns the content within budget with maximum information preservation.
204
+ * Reduce content to fit within budget using section-boundary truncation.
207
205
  */
208
206
  export function reduceToFit(content: string, budgetChars: number): TruncationResult {
209
207
  if (!content || content.length <= budgetChars) {
210
208
  return { content, droppedSections: 0 };
211
209
  }
212
-
213
- // Step 1: Try compression
214
- const compressed = compressToTarget(content, budgetChars);
215
- if (compressed.compressedChars <= budgetChars) {
216
- return { content: compressed.content, droppedSections: 0 };
217
- }
218
-
219
- // Step 2: Truncate the compressed content at section boundaries
220
- return truncateAtSectionBoundary(compressed.content, budgetChars);
210
+ return truncateAtSectionBoundary(content, budgetChars);
221
211
  }
222
212
 
223
213
  // ─── Internal helpers ────────────────────────────────────────────────────────
@@ -11,6 +11,8 @@ import { join } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
  import { gsdRoot } from "./paths.js";
13
13
 
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
15
+
14
16
  // ─── Types ──────────────────────────────────────────────────────────────────────
15
17
 
16
18
  export interface ProjectDetection {
@@ -400,7 +402,6 @@ function detectVerificationCommands(
400
402
  * Check if global GSD setup exists (has ~/.gsd/ with preferences).
401
403
  */
402
404
  export function hasGlobalSetup(): boolean {
403
- const gsdHome = join(homedir(), ".gsd");
404
405
  return (
405
406
  existsSync(join(gsdHome, "preferences.md")) ||
406
407
  existsSync(join(gsdHome, "PREFERENCES.md"))
@@ -412,7 +413,6 @@ export function hasGlobalSetup(): boolean {
412
413
  * Returns true if ~/.gsd/ doesn't exist or has no preferences or auth.
413
414
  */
414
415
  export function isFirstEverLaunch(): boolean {
415
- const gsdHome = join(homedir(), ".gsd");
416
416
  if (!existsSync(gsdHome)) return true;
417
417
 
418
418
  // If we have preferences, not first launch
@@ -194,8 +194,6 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
194
194
 
195
195
  - `search_provider`: `"brave"`, `"tavily"`, `"ollama"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.
196
196
 
197
- - `compression_strategy`: `"truncate"` or `"compress"` — controls how context that exceeds the budget is reduced. `"truncate"` (default) drops sections from the end. `"compress"` applies heuristic compression before truncating, preserving more content at the cost of some fidelity. Default: `"truncate"`.
198
-
199
197
  - `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.
200
198
 
201
199
  - `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys: