gsd-pi 2.60.0-dev.2580e65 → 2.60.0-dev.d9052f5

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 (196) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +4 -7
  2. package/dist/resources/extensions/gsd/auto/phases.js +7 -15
  3. package/dist/resources/extensions/gsd/auto-dashboard.js +8 -21
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +3 -6
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +9 -58
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +2 -3
  7. package/dist/resources/extensions/gsd/auto-prompts.js +20 -36
  8. package/dist/resources/extensions/gsd/auto-recovery.js +18 -37
  9. package/dist/resources/extensions/gsd/auto-start.js +5 -9
  10. package/dist/resources/extensions/gsd/auto-timers.js +5 -11
  11. package/dist/resources/extensions/gsd/auto-unit-closeout.js +3 -5
  12. package/dist/resources/extensions/gsd/auto-verification.js +2 -3
  13. package/dist/resources/extensions/gsd/auto-worktree.js +55 -120
  14. package/dist/resources/extensions/gsd/auto.js +17 -39
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -6
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
  17. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +10 -4
  18. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +1 -2
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +0 -7
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -11
  21. package/dist/resources/extensions/gsd/commands/catalog.js +0 -2
  22. package/dist/resources/extensions/gsd/commands-codebase.js +21 -48
  23. package/dist/resources/extensions/gsd/commands-inspect.js +1 -2
  24. package/dist/resources/extensions/gsd/commands-maintenance.js +19 -32
  25. package/dist/resources/extensions/gsd/complexity-classifier.js +4 -8
  26. package/dist/resources/extensions/gsd/custom-verification.js +2 -3
  27. package/dist/resources/extensions/gsd/gsd-db.js +13 -33
  28. package/dist/resources/extensions/gsd/guided-flow.js +9 -19
  29. package/dist/resources/extensions/gsd/init-wizard.js +0 -12
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +9 -11
  31. package/dist/resources/extensions/gsd/md-importer.js +4 -5
  32. package/dist/resources/extensions/gsd/milestone-actions.js +2 -3
  33. package/dist/resources/extensions/gsd/milestone-ids.js +1 -2
  34. package/dist/resources/extensions/gsd/model-router.js +121 -156
  35. package/dist/resources/extensions/gsd/parallel-merge.js +3 -5
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +14 -26
  37. package/dist/resources/extensions/gsd/preferences-types.js +0 -1
  38. package/dist/resources/extensions/gsd/preferences-validation.js +0 -45
  39. package/dist/resources/extensions/gsd/preferences.js +3 -15
  40. package/dist/resources/extensions/gsd/prompt-loader.js +2 -3
  41. package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
  42. package/dist/resources/extensions/gsd/rule-registry.js +6 -7
  43. package/dist/resources/extensions/gsd/safe-fs.js +8 -6
  44. package/dist/resources/extensions/gsd/tools/complete-milestone.js +2 -3
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -3
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +2 -3
  47. package/dist/resources/extensions/gsd/tools/plan-milestone.js +2 -3
  48. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -3
  49. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -2
  50. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  51. package/dist/resources/extensions/gsd/tools/reopen-slice.js +1 -2
  52. package/dist/resources/extensions/gsd/tools/reopen-task.js +1 -2
  53. package/dist/resources/extensions/gsd/tools/replan-slice.js +1 -2
  54. package/dist/resources/extensions/gsd/tools/validate-milestone.js +1 -2
  55. package/dist/resources/extensions/gsd/triage-resolution.js +4 -11
  56. package/dist/resources/extensions/gsd/workflow-events.js +1 -2
  57. package/dist/resources/extensions/gsd/workflow-logger.js +4 -37
  58. package/dist/resources/extensions/gsd/workflow-migration.js +12 -14
  59. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  60. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  61. package/dist/resources/extensions/gsd/worktree-manager.js +14 -26
  62. package/dist/resources/extensions/shared/interview-ui.js +1 -3
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  68. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.html +1 -1
  84. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  91. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  92. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  93. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  94. package/package.json +1 -1
  95. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  96. package/packages/pi-coding-agent/dist/core/extensions/loader.js +0 -5
  97. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  98. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -2
  99. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/core/extensions/runner.js +0 -16
  101. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +0 -26
  103. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  104. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  105. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  106. package/packages/pi-coding-agent/dist/core/lsp/config.js +1 -6
  107. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  109. package/packages/pi-coding-agent/src/core/extensions/loader.ts +0 -6
  110. package/packages/pi-coding-agent/src/core/extensions/runner.ts +0 -19
  111. package/packages/pi-coding-agent/src/core/extensions/types.ts +0 -26
  112. package/packages/pi-coding-agent/src/core/lsp/config.ts +1 -7
  113. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  114. package/src/resources/extensions/ask-user-questions.ts +3 -7
  115. package/src/resources/extensions/gsd/auto/phases.ts +7 -17
  116. package/src/resources/extensions/gsd/auto-dashboard.ts +8 -22
  117. package/src/resources/extensions/gsd/auto-dispatch.ts +3 -7
  118. package/src/resources/extensions/gsd/auto-model-selection.ts +15 -77
  119. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
  120. package/src/resources/extensions/gsd/auto-prompts.ts +20 -37
  121. package/src/resources/extensions/gsd/auto-recovery.ts +18 -38
  122. package/src/resources/extensions/gsd/auto-start.ts +9 -10
  123. package/src/resources/extensions/gsd/auto-timers.ts +5 -12
  124. package/src/resources/extensions/gsd/auto-unit-closeout.ts +2 -6
  125. package/src/resources/extensions/gsd/auto-verification.ts +6 -3
  126. package/src/resources/extensions/gsd/auto-worktree.ts +55 -121
  127. package/src/resources/extensions/gsd/auto.ts +17 -40
  128. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -4
  129. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
  130. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -4
  131. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +1 -2
  132. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +0 -8
  133. package/src/resources/extensions/gsd/bootstrap/system-context.ts +10 -11
  134. package/src/resources/extensions/gsd/commands/catalog.ts +0 -2
  135. package/src/resources/extensions/gsd/commands-codebase.ts +20 -52
  136. package/src/resources/extensions/gsd/commands-inspect.ts +1 -2
  137. package/src/resources/extensions/gsd/commands-maintenance.ts +19 -28
  138. package/src/resources/extensions/gsd/complexity-classifier.ts +4 -9
  139. package/src/resources/extensions/gsd/custom-verification.ts +2 -3
  140. package/src/resources/extensions/gsd/gsd-db.ts +14 -12
  141. package/src/resources/extensions/gsd/guided-flow.ts +8 -9
  142. package/src/resources/extensions/gsd/init-wizard.ts +0 -12
  143. package/src/resources/extensions/gsd/markdown-renderer.ts +17 -11
  144. package/src/resources/extensions/gsd/md-importer.ts +4 -5
  145. package/src/resources/extensions/gsd/milestone-actions.ts +2 -3
  146. package/src/resources/extensions/gsd/milestone-ids.ts +1 -2
  147. package/src/resources/extensions/gsd/model-router.ts +173 -199
  148. package/src/resources/extensions/gsd/parallel-merge.ts +3 -5
  149. package/src/resources/extensions/gsd/parallel-orchestrator.ts +14 -18
  150. package/src/resources/extensions/gsd/preferences-types.ts +0 -13
  151. package/src/resources/extensions/gsd/preferences-validation.ts +0 -45
  152. package/src/resources/extensions/gsd/preferences.ts +3 -16
  153. package/src/resources/extensions/gsd/prompt-loader.ts +2 -3
  154. package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
  155. package/src/resources/extensions/gsd/rule-registry.ts +6 -7
  156. package/src/resources/extensions/gsd/safe-fs.ts +5 -6
  157. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +0 -63
  158. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +2 -27
  159. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  160. package/src/resources/extensions/gsd/tests/model-router.test.ts +3 -403
  161. package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -62
  162. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +0 -21
  163. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  164. package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -3
  165. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -3
  166. package/src/resources/extensions/gsd/tools/complete-task.ts +6 -3
  167. package/src/resources/extensions/gsd/tools/plan-milestone.ts +6 -3
  168. package/src/resources/extensions/gsd/tools/plan-slice.ts +6 -3
  169. package/src/resources/extensions/gsd/tools/plan-task.ts +3 -2
  170. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -4
  171. package/src/resources/extensions/gsd/tools/reopen-slice.ts +3 -2
  172. package/src/resources/extensions/gsd/tools/reopen-task.ts +3 -2
  173. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -2
  174. package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -2
  175. package/src/resources/extensions/gsd/triage-resolution.ts +4 -11
  176. package/src/resources/extensions/gsd/types.ts +0 -1
  177. package/src/resources/extensions/gsd/workflow-events.ts +1 -2
  178. package/src/resources/extensions/gsd/workflow-logger.ts +5 -52
  179. package/src/resources/extensions/gsd/workflow-migration.ts +12 -14
  180. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  181. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  182. package/src/resources/extensions/gsd/worktree-manager.ts +14 -16
  183. package/src/resources/extensions/shared/interview-ui.ts +1 -3
  184. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +0 -2
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +0 -1
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +0 -47
  187. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +0 -1
  188. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +0 -70
  189. package/src/resources/extensions/gsd/tests/capability-router.test.ts +0 -347
  190. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +0 -1188
  191. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +0 -841
  192. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +0 -284
  193. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +0 -120
  194. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +0 -144
  195. /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_buildManifest.js +0 -0
  196. /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_ssgManifest.js +0 -0
@@ -15,7 +15,6 @@ import { renderRoadmapFromDb } from "../markdown-renderer.js";
15
15
  import { renderAllProjections } from "../workflow-projections.js";
16
16
  import { writeManifest } from "../workflow-manifest.js";
17
17
  import { appendEvent } from "../workflow-events.js";
18
- import { logWarning } from "../workflow-logger.js";
19
18
 
20
19
  export interface PlanMilestoneSliceInput {
21
20
  sliceId: string;
@@ -270,7 +269,9 @@ export async function handlePlanMilestone(
270
269
  const renderResult = await renderRoadmapFromDb(basePath, params.milestoneId);
271
270
  roadmapPath = renderResult.roadmapPath;
272
271
  } catch (renderErr) {
273
- logWarning("tool", `plan_milestone — render failed (DB rows preserved for debugging): ${(renderErr as Error).message}`);
272
+ process.stderr.write(
273
+ `gsd-db: plan_milestone — render failed (DB rows preserved for debugging): ${(renderErr as Error).message}\n`,
274
+ );
274
275
  invalidateStateCache();
275
276
  return { error: `render failed: ${(renderErr as Error).message}` };
276
277
  }
@@ -291,7 +292,9 @@ export async function handlePlanMilestone(
291
292
  trigger_reason: params.triggerReason,
292
293
  });
293
294
  } catch (hookErr) {
294
- logWarning("tool", `plan-milestone post-mutation hook warning: ${(hookErr as Error).message}`);
295
+ process.stderr.write(
296
+ `gsd: plan-milestone post-mutation hook warning: ${(hookErr as Error).message}\n`,
297
+ );
295
298
  }
296
299
 
297
300
  return {
@@ -16,7 +16,6 @@ import { renderPlanFromDb } from "../markdown-renderer.js";
16
16
  import { renderAllProjections } from "../workflow-projections.js";
17
17
  import { writeManifest } from "../workflow-manifest.js";
18
18
  import { appendEvent } from "../workflow-events.js";
19
- import { logWarning } from "../workflow-logger.js";
20
19
 
21
20
  export interface PlanSliceTaskInput {
22
21
  taskId: string;
@@ -230,7 +229,9 @@ export async function handlePlanSlice(
230
229
  trigger_reason: params.triggerReason,
231
230
  });
232
231
  } catch (hookErr) {
233
- logWarning("tool", `plan-slice post-mutation hook warning: ${(hookErr as Error).message}`);
232
+ process.stderr.write(
233
+ `gsd: plan-slice post-mutation hook warning: ${(hookErr as Error).message}\n`,
234
+ );
234
235
  }
235
236
 
236
237
  return {
@@ -240,7 +241,9 @@ export async function handlePlanSlice(
240
241
  taskPlanPaths: renderResult.taskPlanPaths,
241
242
  };
242
243
  } catch (renderErr) {
243
- logWarning("tool", `plan_slice — render failed (DB rows preserved for debugging): ${(renderErr as Error).message}`);
244
+ process.stderr.write(
245
+ `gsd-db: plan_slice — render failed (DB rows preserved for debugging): ${(renderErr as Error).message}\n`,
246
+ );
244
247
  invalidateStateCache();
245
248
  return { error: `render failed: ${(renderErr as Error).message}` };
246
249
  }
@@ -7,7 +7,6 @@ import { renderTaskPlanFromDb } from "../markdown-renderer.js";
7
7
  import { renderAllProjections } from "../workflow-projections.js";
8
8
  import { writeManifest } from "../workflow-manifest.js";
9
9
  import { appendEvent } from "../workflow-events.js";
10
- import { logWarning } from "../workflow-logger.js";
11
10
 
12
11
  export interface PlanTaskParams {
13
12
  milestoneId: string;
@@ -136,7 +135,9 @@ export async function handlePlanTask(
136
135
  trigger_reason: params.triggerReason,
137
136
  });
138
137
  } catch (hookErr) {
139
- logWarning("tool", `plan-task post-mutation hook warning: ${(hookErr as Error).message}`);
138
+ process.stderr.write(
139
+ `gsd: plan-task post-mutation hook warning: ${(hookErr as Error).message}\n`,
140
+ );
140
141
  }
141
142
 
142
143
  return {
@@ -19,7 +19,6 @@ import { renderRoadmapFromDb, renderAssessmentFromDb } from "../markdown-rendere
19
19
  import { renderAllProjections } from "../workflow-projections.js";
20
20
  import { writeManifest } from "../workflow-manifest.js";
21
21
  import { appendEvent } from "../workflow-events.js";
22
- import { logWarning } from "../workflow-logger.js";
23
22
 
24
23
  export interface SliceChangeInput {
25
24
  sliceId: string;
@@ -249,8 +248,9 @@ export async function handleReassessRoadmap(
249
248
  );
250
249
  try {
251
250
  if (existsSync(validationFile)) unlinkSync(validationFile);
252
- } catch (e) {
253
- logWarning("tool", `validation file cleanup failed: ${(e as Error).message}`);
251
+ } catch {
252
+ // Best-effort: DB row is already deleted, so state derivation
253
+ // will not see the file-based verdict as authoritative.
254
254
  }
255
255
  }
256
256
 
@@ -271,7 +271,9 @@ export async function handleReassessRoadmap(
271
271
  trigger_reason: params.triggerReason,
272
272
  });
273
273
  } catch (hookErr) {
274
- logWarning("tool", `reassess-roadmap post-mutation hook warning: ${(hookErr as Error).message}`);
274
+ process.stderr.write(
275
+ `gsd: reassess-roadmap post-mutation hook warning: ${(hookErr as Error).message}\n`,
276
+ );
275
277
  }
276
278
 
277
279
  return {
@@ -24,7 +24,6 @@ import { isClosedStatus } from "../status-guards.js";
24
24
  import { renderAllProjections } from "../workflow-projections.js";
25
25
  import { writeManifest } from "../workflow-manifest.js";
26
26
  import { appendEvent } from "../workflow-events.js";
27
- import { logWarning } from "../workflow-logger.js";
28
27
 
29
28
  export interface ReopenSliceParams {
30
29
  milestoneId: string;
@@ -114,7 +113,9 @@ export async function handleReopenSlice(
114
113
  trigger_reason: params.triggerReason,
115
114
  });
116
115
  } catch (hookErr) {
117
- logWarning("tool", `reopen-slice post-mutation hook warning: ${(hookErr as Error).message}`);
116
+ process.stderr.write(
117
+ `gsd: reopen-slice post-mutation hook warning: ${(hookErr as Error).message}\n`,
118
+ );
118
119
  }
119
120
 
120
121
  return {
@@ -22,7 +22,6 @@ import { isClosedStatus } from "../status-guards.js";
22
22
  import { renderAllProjections } from "../workflow-projections.js";
23
23
  import { writeManifest } from "../workflow-manifest.js";
24
24
  import { appendEvent } from "../workflow-events.js";
25
- import { logWarning } from "../workflow-logger.js";
26
25
 
27
26
  export interface ReopenTaskParams {
28
27
  milestoneId: string;
@@ -118,7 +117,9 @@ export async function handleReopenTask(
118
117
  trigger_reason: params.triggerReason,
119
118
  });
120
119
  } catch (hookErr) {
121
- logWarning("tool", `reopen-task post-mutation hook warning: ${(hookErr as Error).message}`);
120
+ process.stderr.write(
121
+ `gsd: reopen-task post-mutation hook warning: ${(hookErr as Error).message}\n`,
122
+ );
122
123
  }
123
124
 
124
125
  return {
@@ -16,7 +16,6 @@ import { renderPlanFromDb, renderReplanFromDb } from "../markdown-renderer.js";
16
16
  import { renderAllProjections } from "../workflow-projections.js";
17
17
  import { writeManifest } from "../workflow-manifest.js";
18
18
  import { appendEvent } from "../workflow-events.js";
19
- import { logWarning } from "../workflow-logger.js";
20
19
 
21
20
  export interface ReplanSliceTaskInput {
22
21
  taskId: string;
@@ -227,7 +226,9 @@ export async function handleReplanSlice(
227
226
  trigger_reason: params.triggerReason,
228
227
  });
229
228
  } catch (hookErr) {
230
- logWarning("tool", `replan-slice post-mutation hook warning: ${(hookErr as Error).message}`);
229
+ process.stderr.write(
230
+ `gsd: replan-slice post-mutation hook warning: ${(hookErr as Error).message}\n`,
231
+ );
231
232
  }
232
233
 
233
234
  return {
@@ -22,7 +22,6 @@ import { saveFile, clearParseCache } from "../files.js";
22
22
  import { invalidateStateCache } from "../state.js";
23
23
  import { VALIDATION_VERDICTS, isValidMilestoneVerdict } from "../verdict-parser.js";
24
24
  import { insertMilestoneValidationGates } from "../milestone-validation-gates.js";
25
- import { logWarning } from "../workflow-logger.js";
26
25
 
27
26
  export interface ValidateMilestoneParams {
28
27
  milestoneId: string;
@@ -138,7 +137,9 @@ export async function handleValidateMilestone(
138
137
  try {
139
138
  await saveFile(validationPath, validationMd);
140
139
  } catch (renderErr) {
141
- logWarning("tool", `validate_milestone — disk render failed, rolling back DB row: ${(renderErr as Error).message}`);
140
+ process.stderr.write(
141
+ `gsd-db: validate_milestone — disk render failed, rolling back DB row: ${(renderErr as Error).message}\n`,
142
+ );
142
143
  deleteAssessmentByScope(params.milestoneId, 'milestone-validation');
143
144
  return { error: `disk render failed: ${(renderErr as Error).message}` };
144
145
  }
@@ -148,17 +148,10 @@ export function executeBacktrack(
148
148
  capture: CaptureEntry,
149
149
  ): string | null {
150
150
  try {
151
- // Extract target milestone from capture text or resolution.
152
- // Filter out the current milestone ID to avoid picking it as the backtrack target
153
- // when the text mentions both current and target milestones (e.g. "backtrack from M004 to M003").
154
- const sourceText = capture.resolution ?? capture.text;
155
- const allMatches = [...sourceText.matchAll(/\b(M\d{3}(?:-[a-z0-9]{6})?)\b/g)]
156
- .map(m => m[1])
157
- .filter(id => id !== currentMilestoneId);
158
- // Reject ambiguous multi-target strings — if more than one distinct target remains,
159
- // don't guess; let the user clarify.
160
- const uniqueTargets = [...new Set(allMatches)];
161
- const targetMilestoneId = uniqueTargets.length === 1 ? uniqueTargets[0] : null;
151
+ // Extract target milestone from capture text or resolution
152
+ const targetMatch = (capture.resolution ?? capture.text)
153
+ .match(/\b(M\d{3}(?:-[a-z0-9]{6})?)\b/);
154
+ const targetMilestoneId = targetMatch?.[1] ?? null;
162
155
 
163
156
  const ts = new Date().toISOString();
164
157
  const triggerPath = join(gsdRoot(basePath), "BACKTRACK-TRIGGER.md");
@@ -316,7 +316,6 @@ export interface ClassificationResult {
316
316
  tier: ComplexityTier;
317
317
  reason: string;
318
318
  downgraded: boolean;
319
- taskMetadata?: TaskMetadata;
320
319
  }
321
320
 
322
321
  export interface TaskMetadata {
@@ -2,7 +2,6 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { appendFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { atomicWriteSync } from "./atomic-write.js";
5
- import { logWarning } from "./workflow-logger.js";
6
5
 
7
6
  // ─── Session ID ───────────────────────────────────────────────────────────
8
7
 
@@ -75,7 +74,7 @@ export function readEvents(logPath: string): WorkflowEvent[] {
75
74
  try {
76
75
  events.push(JSON.parse(line) as WorkflowEvent);
77
76
  } catch {
78
- logWarning("event-log", `skipping corrupted event line (${line.length} bytes)`);
77
+ process.stderr.write(`workflow-events: skipping corrupted event line: ${line.slice(0, 80)}\n`);
79
78
  }
80
79
  }
81
80
 
@@ -2,9 +2,7 @@
2
2
  // Centralized warning/error accumulator for the workflow engine pipeline.
3
3
  // Captures structured entries that the auto-loop can drain after each unit
4
4
  // to surface root causes for stuck loops, silent degradation, and blocked writes.
5
- // Error-severity entries are persisted to .gsd/audit-log.jsonl (sanitized) for
6
- // post-mortem analysis. Warnings are ephemeral (stderr + buffer only) to avoid
7
- // log amplification from expected-control-flow catch paths.
5
+ // All entries are also persisted to .gsd/audit-log.jsonl for post-mortem analysis.
8
6
  //
9
7
  // Stderr policy: every logWarning/logError call writes immediately to stderr
10
8
  // for terminal visibility. This is intentional — unlike debug-logger (which is
@@ -35,20 +33,7 @@ export type LogComponent =
35
33
  | "compaction" // Event compaction
36
34
  | "reconcile" // Worktree reconciliation
37
35
  | "db" // Database operations (gsd-db)
38
- | "dispatch" // Auto-dispatch rule evaluation
39
- | "recovery" // Auto-recovery and timeout recovery
40
- | "session" // Session lock and session state I/O
41
- | "prompt" // Prompt construction and context injection
42
- | "dashboard" // Auto-dashboard rendering
43
- | "timer" // Auto-timers (idle watchdog, hard timeout)
44
- | "worktree" // Worktree lifecycle (create, sync, merge)
45
- | "command" // Slash command execution and maintenance
46
- | "parallel" // Parallel orchestrator and merge
47
- | "fs" // Safe filesystem operations
48
- | "bootstrap" // Extension bootstrap (system-context, agent-end)
49
- | "guided" // Guided flow (discuss, plan wizards)
50
- | "registry" // Rule registry hook state
51
- | "renderer"; // Markdown renderer and projections
36
+ | "dispatch"; // Auto-dispatch rule evaluation
52
37
 
53
38
  export interface LogEntry {
54
39
  ts: string;
@@ -245,47 +230,15 @@ function _push(
245
230
  _buffer.shift();
246
231
  }
247
232
 
248
- // Persist errors to .gsd/audit-log.jsonl so they survive context resets.
249
- // Only error-severity entries are persisted — warnings are ephemeral (stderr + buffer)
250
- // to avoid log amplification from expected-control-flow catch paths.
251
- if (_auditBasePath && severity === "error") {
233
+ // Persist to .gsd/audit-log.jsonl so entries survive context resets
234
+ if (_auditBasePath) {
252
235
  try {
253
236
  const auditDir = join(_auditBasePath, ".gsd");
254
237
  mkdirSync(auditDir, { recursive: true });
255
- const sanitized = _sanitizeForAudit(entry);
256
- appendFileSync(join(auditDir, "audit-log.jsonl"), JSON.stringify(sanitized) + "\n", "utf-8");
238
+ appendFileSync(join(auditDir, "audit-log.jsonl"), JSON.stringify(entry) + "\n", "utf-8");
257
239
  } catch (auditErr) {
258
240
  // Best-effort — never let audit write failures bubble up
259
241
  process.stderr.write(`[gsd:audit] failed to persist log entry: ${(auditErr as Error).message}\n`);
260
242
  }
261
243
  }
262
244
  }
263
-
264
- /**
265
- * Sanitize a log entry before persisting to the audit JSONL file.
266
- * Strips potentially sensitive context (raw paths, cwd, full error text)
267
- * to avoid leaking local environment details into durable telemetry.
268
- */
269
- function _sanitizeForAudit(entry: LogEntry): LogEntry {
270
- const sanitized: LogEntry = {
271
- ts: entry.ts,
272
- severity: entry.severity,
273
- component: entry.component,
274
- // Truncate message to avoid persisting oversized raw error dumps
275
- message: entry.message.length > 200 ? entry.message.slice(0, 200) + "…[truncated]" : entry.message,
276
- };
277
- if (entry.context) {
278
- // Allowlist: only persist known-safe structured keys
279
- const SAFE_KEYS = new Set(["fn", "tool", "mid", "sid", "tid", "worktree"]);
280
- const filtered: Record<string, string> = {};
281
- for (const [k, v] of Object.entries(entry.context)) {
282
- if (SAFE_KEYS.has(k)) {
283
- filtered[k] = v;
284
- }
285
- }
286
- if (Object.keys(filtered).length > 0) {
287
- sanitized.context = filtered;
288
- }
289
- }
290
- return sanitized;
291
- }
@@ -7,7 +7,6 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
8
  import { _getAdapter, transaction } from "./gsd-db.js";
9
9
  import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
10
- import { logWarning } from "./workflow-logger.js";
11
10
 
12
11
  // ─── needsAutoMigration ───────────────────────────────────────────────────
13
12
 
@@ -24,8 +23,8 @@ export function needsAutoMigration(basePath: string): boolean {
24
23
  try {
25
24
  const row = db.prepare("SELECT COUNT(*) as cnt FROM milestones").get();
26
25
  if (row && (row["cnt"] as number) > 0) return false;
27
- } catch (e) {
28
- logWarning("migration", `DB probe failed: ${(e as Error).message}`);
26
+ } catch {
27
+ // Table might not exist yet — that's fine, we can still migrate
29
28
  return false;
30
29
  }
31
30
 
@@ -72,7 +71,7 @@ export function migrateFromMarkdown(basePath: string): void {
72
71
  .filter(e => e.isDirectory())
73
72
  .map(e => e.name);
74
73
  } catch {
75
- logWarning("migration", "failed to read milestones directory");
74
+ process.stderr.write("workflow-migration: failed to read milestones directory\n");
76
75
  return;
77
76
  }
78
77
 
@@ -142,7 +141,7 @@ export function migrateFromMarkdown(basePath: string): void {
142
141
  risk: s.risk || "low",
143
142
  }));
144
143
  } catch (err) {
145
- logWarning("migration", `failed to parse ROADMAP.md for ${mId}: ${(err as Error).message}`);
144
+ process.stderr.write(`workflow-migration: failed to parse ROADMAP.md for ${mId}: ${(err as Error).message}\n`);
146
145
  // Still add milestone with ID as title
147
146
  milestoneInserts.push({ id: mId, title: mId, status: milestoneStatus });
148
147
  }
@@ -192,7 +191,7 @@ export function migrateFromMarkdown(basePath: string): void {
192
191
  });
193
192
  }
194
193
  } catch (err) {
195
- logWarning("migration", `failed to parse ${slice.id}-PLAN.md for ${mId}: ${(err as Error).message}`);
194
+ process.stderr.write(`workflow-migration: failed to parse ${slice.id}-PLAN.md for ${mId}: ${(err as Error).message}\n`);
196
195
  }
197
196
  }
198
197
  }
@@ -207,8 +206,8 @@ export function migrateFromMarkdown(basePath: string): void {
207
206
  process.stderr.write(`workflow-migration: orphaned summary file ${summaryFile} in ${mId} (slice not found in ROADMAP.md), skipping\n`);
208
207
  }
209
208
  }
210
- } catch (e) {
211
- logWarning("migration", `Orphaned summary check failed for ${mId}: ${(e as Error).message}`);
209
+ } catch {
210
+ // Non-fatal
212
211
  }
213
212
  }
214
213
 
@@ -309,18 +308,17 @@ export function validateMigration(basePath: string): { discrepancies: string[] }
309
308
  const planContent = readFileSync(planPath, "utf-8");
310
309
  const plan = parsePlan(planContent);
311
310
  mdTaskCount += plan.tasks.length;
312
- } catch (e) {
313
- logWarning("migration", `Failed to read plan ${slice.id}-PLAN.md: ${(e as Error).message}`);
311
+ } catch {
312
+ // Skip unreadable plan
314
313
  }
315
314
  }
316
315
  }
317
- } catch (e) {
318
- logWarning("migration", `Failed to read roadmap for ${mId}: ${(e as Error).message}`);
316
+ } catch {
317
+ // Skip unreadable roadmap
319
318
  }
320
319
  }
321
320
  }
322
- } catch (e) {
323
- logWarning("migration", `Validation failed to read markdown: ${(e as Error).message}`);
321
+ } catch {
324
322
  return { discrepancies: ["Failed to read markdown for validation"] };
325
323
  }
326
324
 
@@ -423,7 +423,7 @@ export function regenerateIfMissing(
423
423
  renderSummaryProjection(basePath, milestoneId, sliceId, task.id);
424
424
  regenerated++;
425
425
  } catch (err) {
426
- logWarning("projection", `regenerateIfMissing SUMMARY failed for ${task.id}: ${(err as Error).message}`);
426
+ console.error(`[projections] regenerateIfMissing SUMMARY failed for ${task.id}:`, err);
427
427
  }
428
428
  }
429
429
  }
@@ -452,7 +452,7 @@ export function regenerateIfMissing(
452
452
  }
453
453
  return true;
454
454
  } catch (err) {
455
- logWarning("projection", `regenerateIfMissing ${fileType} failed: ${(err as Error).message}`);
455
+ console.error(`[projections] regenerateIfMissing ${fileType} failed:`, err);
456
456
  return false;
457
457
  }
458
458
  }
@@ -455,8 +455,8 @@ function parseEventBlock(block: string): WorkflowEvent[] {
455
455
  if (paramsMatch) {
456
456
  try {
457
457
  params = JSON.parse(paramsMatch[1]!) as Record<string, unknown>;
458
- } catch (e) {
459
- logWarning("reconcile", `tool call params parse failed: ${(e as Error).message}`);
458
+ } catch {
459
+ // Keep empty params on parse error
460
460
  }
461
461
  i++; // consume params line
462
462
  }
@@ -95,8 +95,8 @@ export function resolveGitDir(basePath: string): string {
95
95
  if (content.startsWith("gitdir: ")) {
96
96
  return resolve(basePath, content.slice(8));
97
97
  }
98
- } catch (e) {
99
- logWarning("worktree", `.git file read failed: ${(e as Error).message}`);
98
+ } catch {
99
+ // Not a file or unreadable fall through to default
100
100
  }
101
101
  return join(basePath, ".git");
102
102
  }
@@ -308,9 +308,8 @@ export function findNestedGitDirs(rootPath: string): string[] {
308
308
  let entries: string[];
309
309
  try {
310
310
  entries = readdirSync(dir);
311
- } catch (e) {
312
- logWarning("worktree", `readdirSync failed: ${(e as Error).message}`);
313
- return;
311
+ } catch {
312
+ return; // Permission denied, broken symlink, etc.
314
313
  }
315
314
 
316
315
  for (const entry of entries) {
@@ -322,8 +321,7 @@ export function findNestedGitDirs(rootPath: string): string[] {
322
321
  let stat;
323
322
  try {
324
323
  stat = lstatSync(fullPath);
325
- } catch (e) {
326
- logWarning("worktree", `lstatSync failed for ${fullPath}: ${(e as Error).message}`);
324
+ } catch {
327
325
  continue;
328
326
  }
329
327
  if (!stat.isDirectory()) continue;
@@ -339,8 +337,8 @@ export function findNestedGitDirs(rootPath: string): string[] {
339
337
  // Don't recurse into the nested repo — we found what we need
340
338
  continue;
341
339
  }
342
- } catch (e) {
343
- logWarning("worktree", `existsSync/.git check failed for ${fullPath}: ${(e as Error).message}`);
340
+ } catch {
341
+ // No .git here continue scanning
344
342
  }
345
343
 
346
344
  walk(fullPath, depth + 1);
@@ -376,7 +374,7 @@ export function removeWorktree(
376
374
  if (entry?.path) {
377
375
  wtPath = entry.path;
378
376
  }
379
- } catch (e) { logWarning("worktree", `nativeWorktreeList parse failed: ${(e as Error).message}`); }
377
+ } catch { /* fall back to computed path */ }
380
378
 
381
379
  const resolvedWtPath = existsSync(wtPath) ? realpathSync(wtPath) : wtPath;
382
380
 
@@ -390,7 +388,7 @@ export function removeWorktree(
390
388
  if (!existsSync(wtPath)) {
391
389
  nativeWorktreePrune(basePath);
392
390
  if (deleteBranch) {
393
- try { nativeBranchDelete(basePath, branch, true); } catch (e) { logWarning("worktree", `nativeBranchDelete failed: ${(e as Error).message}`); }
391
+ try { nativeBranchDelete(basePath, branch, true); } catch { /* branch may not exist */ }
394
392
  }
395
393
  return;
396
394
  }
@@ -424,8 +422,8 @@ export function removeWorktree(
424
422
  logWarning("reconcile", `Submodule changes detected — stash failed, changes may be lost during force removal`, { worktree: name, path: resolvedWtPath });
425
423
  }
426
424
  }
427
- } catch (e) {
428
- logWarning("worktree", `submodule status check failed: ${(e as Error).message}`);
425
+ } catch {
426
+ // submodule status failed proceed with normal removal
429
427
  }
430
428
  }
431
429
 
@@ -456,11 +454,11 @@ export function removeWorktree(
456
454
  // Remove worktree: try non-force first when submodules have changes,
457
455
  // falling back to force only after submodule state has been preserved.
458
456
  const useForce = hasSubmoduleChanges ? false : force;
459
- try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch (e) { logWarning("worktree", `nativeWorktreeRemove failed: ${(e as Error).message}`); }
457
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, useForce); } catch { /* may fail */ }
460
458
 
461
459
  // If the directory is still there (e.g. locked), try harder with force
462
460
  if (existsSync(resolvedWtPath)) {
463
- try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch (e) { logWarning("worktree", `nativeWorktreeRemove (force) failed: ${(e as Error).message}`); }
461
+ try { nativeWorktreeRemove(basePath, resolvedWtPath, true); } catch { /* may fail */ }
464
462
  }
465
463
 
466
464
  // (#2821) If the worktree directory STILL exists after both native removal
@@ -490,7 +488,7 @@ export function removeWorktree(
490
488
  nativeWorktreePrune(basePath);
491
489
 
492
490
  if (deleteBranch) {
493
- try { nativeBranchDelete(basePath, branch, true); } catch (e) { logWarning("worktree", `final branch delete failed: ${(e as Error).message}`); }
491
+ try { nativeBranchDelete(basePath, branch, true); } catch { /* branch may not exist */ }
494
492
  }
495
493
  }
496
494
 
@@ -298,9 +298,7 @@ export async function showInterviewRound(
298
298
  // Auto-open the notes field when "None of the above" is selected
299
299
  // so the user can immediately provide a free-text explanation
300
300
  // instead of being trapped in a re-asking loop (bug #2715).
301
- // Only auto-open if the user hasn't already provided notes —
302
- // otherwise Enter from notes mode loops back here endlessly.
303
- if (!isMultiSelect(currentIdx) && states[currentIdx].cursorIndex === noneOrDoneIdx(currentIdx) && !states[currentIdx].notes) {
301
+ if (!isMultiSelect(currentIdx) && states[currentIdx].cursorIndex === noneOrDoneIdx(currentIdx)) {
304
302
  states[currentIdx].notesVisible = true;
305
303
  focusNotes = true;
306
304
  loadStateToEditor();
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=lsp-legacy-alias.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lsp-legacy-alias.test.d.ts","sourceRoot":"","sources":["../../../src/core/lsp/lsp-legacy-alias.test.ts"],"names":[],"mappings":""}
@@ -1,47 +0,0 @@
1
- // GSD2 — Regression test for LSP legacy server key aliases
2
- // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
- /**
4
- * When a default server key is renamed (e.g., kotlin-language-server → kotlin-lsp),
5
- * user overrides referencing the old key must still merge correctly via LEGACY_ALIASES.
6
- *
7
- * This test exercises the merge path through loadConfig() with a temp project
8
- * containing an lsp.json that uses the legacy key.
9
- */
10
- import { describe, it, beforeEach, afterEach } from "node:test";
11
- import assert from "node:assert/strict";
12
- import * as fs from "node:fs";
13
- import * as path from "node:path";
14
- import * as os from "node:os";
15
- import { loadConfig } from "./config.js";
16
- describe("LSP legacy server key aliases", () => {
17
- let tmpDir;
18
- beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "lsp-alias-test-"));
20
- });
21
- afterEach(() => {
22
- fs.rmSync(tmpDir, { recursive: true, force: true });
23
- });
24
- it("merges user override with legacy key 'kotlin-language-server' into 'kotlin-lsp'", () => {
25
- // Write an lsp.json that uses the old key name with a command that exists (node)
26
- // so resolveCommand doesn't filter it out.
27
- const overrideConfig = {
28
- servers: {
29
- "kotlin-language-server": {
30
- command: "node",
31
- },
32
- },
33
- };
34
- fs.writeFileSync(path.join(tmpDir, "lsp.json"), JSON.stringify(overrideConfig));
35
- // Also add root markers so the server is detected
36
- fs.writeFileSync(path.join(tmpDir, "build.gradle.kts"), "");
37
- const config = loadConfig(tmpDir);
38
- // The merged config should have kotlin-lsp (new key) with the user's command override
39
- const kotlinServer = config.servers["kotlin-lsp"];
40
- assert.ok(kotlinServer, "kotlin-lsp should exist in merged config");
41
- assert.equal(kotlinServer.command, "node", "command should be overridden from user config via legacy alias");
42
- assert.ok(kotlinServer.fileTypes.includes(".kt"), "fileTypes should be inherited from defaults");
43
- // The old key should NOT appear as a separate entry
44
- assert.equal(config.servers["kotlin-language-server"], undefined, "legacy key should not appear as separate server");
45
- });
46
- });
47
- //# sourceMappingURL=lsp-legacy-alias.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lsp-legacy-alias.test.js","sourceRoot":"","sources":["../../../src/core/lsp/lsp-legacy-alias.test.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,4DAA4D;AAE5D;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC9C,IAAI,MAAc,CAAC;IAEnB,UAAU,CAAC,GAAG,EAAE;QACf,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QAC1F,iFAAiF;QACjF,2CAA2C;QAC3C,MAAM,cAAc,GAAG;YACtB,OAAO,EAAE;gBACR,wBAAwB,EAAE;oBACzB,OAAO,EAAE,MAAM;iBACf;aACD;SACD,CAAC;QACF,EAAE,CAAC,aAAa,CACf,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7B,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAC9B,CAAC;QAEF,kDAAkD;QAClD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,EAAE,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAElC,sFAAsF;QACtF,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAClD,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,0CAA0C,CAAC,CAAC;QACpE,MAAM,CAAC,KAAK,CACX,YAAY,CAAC,OAAO,EACpB,MAAM,EACN,gEAAgE,CAChE,CAAC;QACF,MAAM,CAAC,EAAE,CACR,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EACtC,6CAA6C,CAC7C,CAAC;QAEF,oDAAoD;QACpD,MAAM,CAAC,KAAK,CACX,MAAM,CAAC,OAAO,CAAC,wBAAwB,CAAC,EACxC,SAAS,EACT,iDAAiD,CACjD,CAAC;IACH,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["// GSD2 — Regression test for LSP legacy server key aliases\n// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>\n\n/**\n * When a default server key is renamed (e.g., kotlin-language-server → kotlin-lsp),\n * user overrides referencing the old key must still merge correctly via LEGACY_ALIASES.\n *\n * This test exercises the merge path through loadConfig() with a temp project\n * containing an lsp.json that uses the legacy key.\n */\n\nimport { describe, it, beforeEach, afterEach } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { loadConfig } from \"./config.js\";\n\ndescribe(\"LSP legacy server key aliases\", () => {\n\tlet tmpDir: string;\n\n\tbeforeEach(() => {\n\t\ttmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"lsp-alias-test-\"));\n\t});\n\n\tafterEach(() => {\n\t\tfs.rmSync(tmpDir, { recursive: true, force: true });\n\t});\n\n\tit(\"merges user override with legacy key 'kotlin-language-server' into 'kotlin-lsp'\", () => {\n\t\t// Write an lsp.json that uses the old key name with a command that exists (node)\n\t\t// so resolveCommand doesn't filter it out.\n\t\tconst overrideConfig = {\n\t\t\tservers: {\n\t\t\t\t\"kotlin-language-server\": {\n\t\t\t\t\tcommand: \"node\",\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tfs.writeFileSync(\n\t\t\tpath.join(tmpDir, \"lsp.json\"),\n\t\t\tJSON.stringify(overrideConfig),\n\t\t);\n\n\t\t// Also add root markers so the server is detected\n\t\tfs.writeFileSync(path.join(tmpDir, \"build.gradle.kts\"), \"\");\n\n\t\tconst config = loadConfig(tmpDir);\n\n\t\t// The merged config should have kotlin-lsp (new key) with the user's command override\n\t\tconst kotlinServer = config.servers[\"kotlin-lsp\"];\n\t\tassert.ok(kotlinServer, \"kotlin-lsp should exist in merged config\");\n\t\tassert.equal(\n\t\t\tkotlinServer.command,\n\t\t\t\"node\",\n\t\t\t\"command should be overridden from user config via legacy alias\",\n\t\t);\n\t\tassert.ok(\n\t\t\tkotlinServer.fileTypes.includes(\".kt\"),\n\t\t\t\"fileTypes should be inherited from defaults\",\n\t\t);\n\n\t\t// The old key should NOT appear as a separate entry\n\t\tassert.equal(\n\t\t\tconfig.servers[\"kotlin-language-server\"],\n\t\t\tundefined,\n\t\t\t\"legacy key should not appear as separate server\",\n\t\t);\n\t});\n});\n"]}