gsd-pi 2.45.0-dev.6b9da3e → 2.45.0-dev.e0ee972

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 (104) hide show
  1. package/dist/help-text.js +1 -1
  2. package/dist/loader.js +34 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +16 -10
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
  5. package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
  6. package/dist/resources/extensions/gsd/auto.js +4 -5
  7. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +13 -12
  8. package/dist/resources/extensions/gsd/db-writer.js +9 -9
  9. package/dist/resources/extensions/gsd/doctor-checks.js +1 -1
  10. package/dist/resources/extensions/gsd/doctor.js +2 -2
  11. package/dist/resources/extensions/gsd/gsd-db.js +5 -1
  12. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  13. package/dist/resources/extensions/gsd/preferences.js +8 -4
  14. package/dist/resources/extensions/gsd/workflow-logger.js +138 -0
  15. package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
  16. package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
  17. package/dist/resources/extensions/voice/index.js +11 -16
  18. package/dist/resources/extensions/voice/linux-ready.js +67 -0
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  21. package/dist/web/standalone/.next/build-manifest.json +2 -2
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  24. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.html +1 -1
  40. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  47. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  48. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  49. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  50. package/package.json +1 -1
  51. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
  52. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
  53. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  54. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  55. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  57. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +4 -0
  58. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
  59. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
  60. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
  61. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
  62. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
  63. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
  64. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
  65. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
  66. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
  68. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
  70. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
  72. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
  73. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
  74. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  75. package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
  76. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
  77. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
  78. package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
  79. package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
  80. package/src/resources/extensions/gsd/auto/phases.ts +16 -12
  81. package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
  82. package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
  83. package/src/resources/extensions/gsd/auto.ts +3 -3
  84. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +13 -12
  85. package/src/resources/extensions/gsd/db-writer.ts +9 -17
  86. package/src/resources/extensions/gsd/doctor-checks.ts +1 -1
  87. package/src/resources/extensions/gsd/doctor.ts +2 -2
  88. package/src/resources/extensions/gsd/gsd-db.ts +5 -1
  89. package/src/resources/extensions/gsd/journal.ts +6 -1
  90. package/src/resources/extensions/gsd/preferences-types.ts +2 -2
  91. package/src/resources/extensions/gsd/preferences.ts +7 -3
  92. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
  93. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
  94. package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
  95. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
  96. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
  97. package/src/resources/extensions/gsd/workflow-logger.ts +193 -0
  98. package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
  99. package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
  100. package/src/resources/extensions/voice/index.ts +11 -21
  101. package/src/resources/extensions/voice/linux-ready.ts +87 -0
  102. package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
  103. /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → dFMji9G1LZ-Tv36el9pRT}/_buildManifest.js +0 -0
  104. /package/dist/web/standalone/.next/static/{rzO54ZboyINyEt7cVM_uS → dFMji9G1LZ-Tv36el9pRT}/_ssgManifest.js +0 -0
@@ -6,6 +6,7 @@ import { findMilestoneIds, nextMilestoneId, claimReservedId, getReservedMileston
6
6
  import { loadEffectiveGSDPreferences } from "../preferences.js";
7
7
  import { ensureDbOpen } from "./dynamic-tools.js";
8
8
  import { StringEnum } from "@gsd/pi-ai";
9
+ import { logError } from "../workflow-logger.js";
9
10
 
10
11
  /**
11
12
  * Register an alias tool that shares the same execute function as its canonical counterpart.
@@ -52,7 +53,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
52
53
  };
53
54
  } catch (err) {
54
55
  const msg = err instanceof Error ? err.message : String(err);
55
- process.stderr.write(`gsd-db: gsd_decision_save tool failed: ${msg}\n`);
56
+ logError("tool", `gsd_decision_save tool failed: ${msg}`, { tool: "gsd_decision_save", error: String(err) });
56
57
  return {
57
58
  content: [{ type: "text" as const, text: `Error saving decision: ${msg}` }],
58
59
  details: { operation: "save_decision", error: msg } as any,
@@ -143,7 +144,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
143
144
  };
144
145
  } catch (err) {
145
146
  const msg = err instanceof Error ? err.message : String(err);
146
- process.stderr.write(`gsd-db: gsd_requirement_update tool failed: ${msg}\n`);
147
+ logError("tool", `gsd_requirement_update tool failed: ${msg}`, { tool: "gsd_requirement_update", error: String(err) });
147
148
  return {
148
149
  content: [{ type: "text" as const, text: `Error updating requirement: ${msg}` }],
149
150
  details: { operation: "update_requirement", id: params.id, error: msg } as any,
@@ -239,7 +240,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
239
240
  };
240
241
  } catch (err) {
241
242
  const msg = err instanceof Error ? err.message : String(err);
242
- process.stderr.write(`gsd-db: gsd_summary_save tool failed: ${msg}\n`);
243
+ logError("tool", `gsd_summary_save tool failed: ${msg}`, { tool: "gsd_summary_save", error: String(err) });
243
244
  return {
244
245
  content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
245
246
  details: { operation: "save_summary", error: msg } as any,
@@ -402,7 +403,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
402
403
  };
403
404
  } catch (err) {
404
405
  const msg = err instanceof Error ? err.message : String(err);
405
- process.stderr.write(`gsd-db: plan_milestone tool failed: ${msg}\n`);
406
+ logError("tool", `plan_milestone tool failed: ${msg}`, { tool: "gsd_plan_milestone", error: String(err) });
406
407
  return {
407
408
  content: [{ type: "text" as const, text: `Error planning milestone: ${msg}` }],
408
409
  details: { operation: "plan_milestone", error: msg } as any,
@@ -495,7 +496,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
495
496
  };
496
497
  } catch (err) {
497
498
  const msg = err instanceof Error ? err.message : String(err);
498
- process.stderr.write(`gsd-db: plan_slice tool failed: ${msg}\n`);
499
+ logError("tool", `plan_slice tool failed: ${msg}`, { tool: "gsd_plan_slice", error: String(err) });
499
500
  return {
500
501
  content: [{ type: "text" as const, text: `Error planning slice: ${msg}` }],
501
502
  details: { operation: "plan_slice", error: msg } as any,
@@ -572,7 +573,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
572
573
  };
573
574
  } catch (err) {
574
575
  const msg = err instanceof Error ? err.message : String(err);
575
- process.stderr.write(`gsd-db: plan_task tool failed: ${msg}\n`);
576
+ logError("tool", `plan_task tool failed: ${msg}`, { tool: "gsd_plan_task", error: String(err) });
576
577
  return {
577
578
  content: [{ type: "text" as const, text: `Error planning task: ${msg}` }],
578
579
  details: { operation: "plan_task", error: msg } as any,
@@ -642,7 +643,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
642
643
  };
643
644
  } catch (err) {
644
645
  const msg = err instanceof Error ? err.message : String(err);
645
- process.stderr.write(`gsd-db: complete_task tool failed: ${msg}\n`);
646
+ logError("tool", `complete_task tool failed: ${msg}`, { tool: "gsd_task_complete", error: String(err) });
646
647
  return {
647
648
  content: [{ type: "text" as const, text: `Error completing task: ${msg}` }],
648
649
  details: { operation: "complete_task", error: msg } as any,
@@ -723,7 +724,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
723
724
  };
724
725
  } catch (err) {
725
726
  const msg = err instanceof Error ? err.message : String(err);
726
- process.stderr.write(`gsd-db: complete_slice tool failed: ${msg}\n`);
727
+ logError("tool", `complete_slice tool failed: ${msg}`, { tool: "gsd_slice_complete", error: String(err) });
727
728
  return {
728
729
  content: [{ type: "text" as const, text: `Error completing slice: ${msg}` }],
729
730
  details: { operation: "complete_slice", error: msg } as any,
@@ -834,7 +835,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
834
835
  };
835
836
  } catch (err) {
836
837
  const msg = err instanceof Error ? err.message : String(err);
837
- process.stderr.write(`gsd-db: complete_milestone tool failed: ${msg}\n`);
838
+ logError("tool", `complete_milestone tool failed: ${msg}`, { tool: "gsd_complete_milestone", error: String(err) });
838
839
  return {
839
840
  content: [{ type: "text" as const, text: `Error completing milestone: ${msg}` }],
840
841
  details: { operation: "complete_milestone", error: msg } as any,
@@ -904,7 +905,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
904
905
  };
905
906
  } catch (err) {
906
907
  const msg = err instanceof Error ? err.message : String(err);
907
- process.stderr.write(`gsd-db: validate_milestone tool failed: ${msg}\n`);
908
+ logError("tool", `validate_milestone tool failed: ${msg}`, { tool: "gsd_validate_milestone", error: String(err) });
908
909
  return {
909
910
  content: [{ type: "text" as const, text: `Error validating milestone: ${msg}` }],
910
911
  details: { operation: "validate_milestone", error: msg } as any,
@@ -973,7 +974,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
973
974
  };
974
975
  } catch (err) {
975
976
  const msg = err instanceof Error ? err.message : String(err);
976
- process.stderr.write(`gsd-db: replan_slice tool failed: ${msg}\n`);
977
+ logError("tool", `replan_slice tool failed: ${msg}`, { tool: "gsd_replan_slice", error: String(err) });
977
978
  return {
978
979
  content: [{ type: "text" as const, text: `Error replanning slice: ${msg}` }],
979
980
  details: { operation: "replan_slice", error: msg } as any,
@@ -1053,7 +1054,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1053
1054
  };
1054
1055
  } catch (err) {
1055
1056
  const msg = err instanceof Error ? err.message : String(err);
1056
- process.stderr.write(`gsd-db: reassess_roadmap tool failed: ${msg}\n`);
1057
+ logError("tool", `reassess_roadmap tool failed: ${msg}`, { tool: "gsd_reassess_roadmap", error: String(err) });
1057
1058
  return {
1058
1059
  content: [{ type: "text" as const, text: `Error reassessing roadmap: ${msg}` }],
1059
1060
  details: { operation: "reassess_roadmap", error: msg } as any,
@@ -14,6 +14,7 @@ import type { Decision, Requirement } from './types.js';
14
14
  import { resolveGsdRootFile } from './paths.js';
15
15
  import { saveFile } from './files.js';
16
16
  import { GSDError, GSD_STALE_STATE, GSD_IO_ERROR } from './errors.js';
17
+ import { logWarning, logError } from './workflow-logger.js';
17
18
  import { invalidateStateCache } from './state.js';
18
19
  import { clearPathCache } from './paths.js';
19
20
  import { clearParseCache } from './files.js';
@@ -221,7 +222,7 @@ export async function nextDecisionId(): Promise<string> {
221
222
  const next = maxNum + 1;
222
223
  return `D${String(next).padStart(3, '0')}`;
223
224
  } catch (err) {
224
- process.stderr.write(`gsd-db: nextDecisionId failed: ${(err as Error).message}\n`);
225
+ logError('manifest', 'nextDecisionId failed', { fn: 'nextDecisionId', error: String((err as Error).message) });
225
226
  return 'D001';
226
227
  }
227
228
  }
@@ -311,9 +312,7 @@ export async function saveDecisionToDb(
311
312
  try {
312
313
  await saveFile(filePath, md);
313
314
  } catch (diskErr) {
314
- process.stderr.write(
315
- `gsd-db: saveDecisionToDb — disk write failed, rolling back DB row: ${(diskErr as Error).message}\n`,
316
- );
315
+ logError('manifest', 'disk write failed, rolling back DB row', { fn: 'saveDecisionToDb', error: String((diskErr as Error).message) });
317
316
  adapter?.prepare('DELETE FROM decisions WHERE id = :id').run({ ':id': id });
318
317
  throw diskErr;
319
318
  }
@@ -325,7 +324,7 @@ export async function saveDecisionToDb(
325
324
 
326
325
  return { id };
327
326
  } catch (err) {
328
- process.stderr.write(`gsd-db: saveDecisionToDb failed: ${(err as Error).message}\n`);
327
+ logError('manifest', 'saveDecisionToDb failed', { fn: 'saveDecisionToDb', error: String((err as Error).message) });
329
328
  throw err;
330
329
  }
331
330
  }
@@ -388,9 +387,7 @@ export async function updateRequirementInDb(
388
387
  try {
389
388
  await saveFile(filePath, md);
390
389
  } catch (diskErr) {
391
- process.stderr.write(
392
- `gsd-db: updateRequirementInDb — disk write failed, reverting DB row: ${(diskErr as Error).message}\n`,
393
- );
390
+ logError('manifest', 'disk write failed, reverting DB row', { fn: 'updateRequirementInDb', error: String((diskErr as Error).message) });
394
391
  db.upsertRequirement(existing);
395
392
  throw diskErr;
396
393
  }
@@ -400,7 +397,7 @@ export async function updateRequirementInDb(
400
397
  clearPathCache();
401
398
  clearParseCache();
402
399
  } catch (err) {
403
- process.stderr.write(`gsd-db: updateRequirementInDb failed: ${(err as Error).message}\n`);
400
+ logError('manifest', 'updateRequirementInDb failed', { fn: 'updateRequirementInDb', error: String((err as Error).message) });
404
401
  throw err;
405
402
  }
406
403
  }
@@ -444,10 +441,7 @@ export async function saveArtifactToDb(
444
441
  const existingSize = statSync(fullPath).size;
445
442
  const newSize = Buffer.byteLength(opts.content, 'utf-8');
446
443
  if (existingSize > 0 && newSize < existingSize * 0.5) {
447
- process.stderr.write(
448
- `gsd-db: saveArtifactToDb — new content (${newSize}B) is <50% of existing file ` +
449
- `(${existingSize}B) at ${opts.path}. Preserving disk file to prevent data loss.\n`,
450
- );
444
+ logWarning('manifest', `new content (${newSize}B) is <50% of existing file (${existingSize}B), preserving disk file`, { fn: 'saveArtifactToDb', path: opts.path });
451
445
  dbContent = readFileSync(fullPath, 'utf-8');
452
446
  skipDiskWrite = true;
453
447
  }
@@ -467,9 +461,7 @@ export async function saveArtifactToDb(
467
461
  try {
468
462
  await saveFile(fullPath, opts.content);
469
463
  } catch (diskErr) {
470
- process.stderr.write(
471
- `gsd-db: saveArtifactToDb — disk write failed, rolling back DB row: ${(diskErr as Error).message}\n`,
472
- );
464
+ logError('manifest', 'disk write failed, rolling back DB row', { fn: 'saveArtifactToDb', error: String((diskErr as Error).message) });
473
465
  const rollbackAdapter = db._getAdapter();
474
466
  rollbackAdapter?.prepare('DELETE FROM artifacts WHERE path = :path').run({ ':path': opts.path });
475
467
  throw diskErr;
@@ -481,7 +473,7 @@ export async function saveArtifactToDb(
481
473
  clearPathCache();
482
474
  clearParseCache();
483
475
  } catch (err) {
484
- process.stderr.write(`gsd-db: saveArtifactToDb failed: ${(err as Error).message}\n`);
476
+ logError('manifest', 'saveArtifactToDb failed', { fn: 'saveArtifactToDb', error: String((err as Error).message) });
485
477
  throw err;
486
478
  }
487
479
  }
@@ -25,7 +25,7 @@ export async function checkGitHealth(
25
25
  issues: DoctorIssue[],
26
26
  fixesApplied: string[],
27
27
  shouldFix: (code: DoctorIssueCode) => boolean,
28
- isolationMode: "none" | "worktree" | "branch" = "worktree",
28
+ isolationMode: "none" | "worktree" | "branch" = "none",
29
29
  ): Promise<void> {
30
30
  // Degrade gracefully if not a git repo
31
31
  if (!nativeIsRepo(basePath)) {
@@ -360,8 +360,8 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
360
360
  // Git health checks — timed
361
361
  const t0git = Date.now();
362
362
  const isolationMode: "none" | "worktree" | "branch" = options?.isolationMode ??
363
- (prefs?.preferences?.git?.isolation === "none" ? "none" :
364
- prefs?.preferences?.git?.isolation === "branch" ? "branch" : "worktree");
363
+ (prefs?.preferences?.git?.isolation === "worktree" ? "worktree" :
364
+ prefs?.preferences?.git?.isolation === "branch" ? "branch" : "none");
365
365
  await checkGitHealth(basePath, issues, fixesApplied, shouldFix, isolationMode);
366
366
  const gitMs = Date.now() - t0git;
367
367
 
@@ -78,8 +78,12 @@ function loadProvider(): void {
78
78
  // unavailable
79
79
  }
80
80
 
81
+ const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
82
+ const versionHint = nodeMajor < 22
83
+ ? ` GSD requires Node >= 22.0.0 (current: v${process.versions.node}). Upgrade Node to fix this.`
84
+ : "";
81
85
  process.stderr.write(
82
- "gsd-db: No SQLite provider available (tried node:sqlite, better-sqlite3)\n",
86
+ `gsd-db: No SQLite provider available (tried node:sqlite, better-sqlite3).${versionHint}\n`,
83
87
  );
84
88
  }
85
89
 
@@ -32,7 +32,12 @@ export type JournalEventType =
32
32
  | "milestone-transition"
33
33
  | "stuck-detected"
34
34
  | "sidecar-dequeue"
35
- | "iteration-end";
35
+ | "iteration-end"
36
+ | "worktree-enter"
37
+ | "worktree-create-failed"
38
+ | "worktree-skip"
39
+ | "worktree-merge-start"
40
+ | "worktree-merge-failed";
36
41
 
37
42
  /** A single structured event in the journal. */
38
43
  export interface JournalEntry {
@@ -34,7 +34,7 @@ export const MODE_DEFAULTS: Record<WorkflowMode, Partial<GSDPreferences>> = {
34
34
  push_branches: false,
35
35
  pre_merge_check: false,
36
36
  merge_strategy: "squash",
37
- isolation: "worktree",
37
+ isolation: "none",
38
38
  },
39
39
  unique_milestone_ids: false,
40
40
  },
@@ -44,7 +44,7 @@ export const MODE_DEFAULTS: Record<WorkflowMode, Partial<GSDPreferences>> = {
44
44
  push_branches: true,
45
45
  pre_merge_check: true,
46
46
  merge_strategy: "squash",
47
- isolation: "worktree",
47
+ isolation: "none",
48
48
  },
49
49
  unique_milestone_ids: true,
50
50
  },
@@ -497,13 +497,17 @@ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
497
497
 
498
498
  /**
499
499
  * Resolve the effective git isolation mode from preferences.
500
- * Returns "worktree" (default), "branch", or "none".
500
+ * Returns "none" (default), "worktree", or "branch".
501
+ *
502
+ * Default is "none" so GSD works out of the box without preferences.md.
503
+ * Worktree isolation requires explicit opt-in because it depends on git
504
+ * branch infrastructure that must be set up before use.
501
505
  */
502
506
  export function getIsolationMode(): "none" | "worktree" | "branch" {
503
507
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
504
- if (prefs?.isolation === "none") return "none";
508
+ if (prefs?.isolation === "worktree") return "worktree";
505
509
  if (prefs?.isolation === "branch") return "branch";
506
- return "worktree"; // default
510
+ return "none"; // default — no isolation, work on current branch
507
511
  }
508
512
 
509
513
  export function resolveParallelConfig(prefs: GSDPreferences | undefined): import("./types.js").ParallelConfig {
@@ -27,7 +27,7 @@ console.log("\n=== #2330: Merge conflict stops auto loop ===");
27
27
  const methodStart = resolverSrc.indexOf("Worktree-mode merge:");
28
28
  assertTrue(methodStart > 0, "worktree-resolver has _mergeWorktreeMode method");
29
29
 
30
- const methodBody = resolverSrc.slice(methodStart, methodStart + 5000);
30
+ const methodBody = resolverSrc.slice(methodStart, methodStart + 6000);
31
31
  const rethrowsConflict =
32
32
  methodBody.includes("MergeConflictError") &&
33
33
  methodBody.includes("throw err");
@@ -70,18 +70,20 @@ try {
70
70
  }
71
71
  });
72
72
 
73
- // Test 4: shouldUseWorktreeIsolation returns true for no prefs (default)
73
+ // Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none)
74
+ // Worktree isolation requires explicit opt-in — default is "none" so GSD
75
+ // works out of the box without preferences.md (#2480).
74
76
  // Skip if global prefs exist — they override the default and this test
75
77
  // cannot control ~/.gsd/preferences.md.
76
78
 
77
- test('shouldUseWorktreeIsolation returns true for no prefs (default)', () => {
79
+ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => {
78
80
  const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
79
81
  || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
80
82
  if (!globalPrefsExist) {
81
83
  try {
82
84
  removeRunnerPreferences(); // ensure no prefs file
83
85
  invalidateAllCaches();
84
- assert.deepStrictEqual(shouldUseWorktreeIsolation(), true, "shouldUseWorktreeIsolation() with no prefs (default worktree)");
86
+ assert.deepStrictEqual(shouldUseWorktreeIsolation(), false, "shouldUseWorktreeIsolation() with no prefs (default none)");
85
87
  } finally {
86
88
  invalidateAllCaches();
87
89
  }
@@ -89,6 +91,21 @@ test('shouldUseWorktreeIsolation returns true for no prefs (default)', () => {
89
91
  }
90
92
  });
91
93
 
94
+ // Test 5: getIsolationMode returns "none" when no preferences.md exists (#2480)
95
+ test('getIsolationMode returns "none" with no prefs (default)', () => {
96
+ const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md"))
97
+ || existsSync(join(homedir(), ".gsd", "PREFERENCES.md"));
98
+ if (!globalPrefsExist) {
99
+ try {
100
+ removeRunnerPreferences();
101
+ invalidateAllCaches();
102
+ assert.deepStrictEqual(getIsolationMode(), "none", "getIsolationMode() with no prefs defaults to none");
103
+ } finally {
104
+ invalidateAllCaches();
105
+ }
106
+ }
107
+ });
108
+
92
109
  test('getIsolationMode returns "none" with none prefs', () => {
93
110
  try {
94
111
  writeRunnerPreferences("none");
@@ -100,6 +117,28 @@ try {
100
117
  }
101
118
  });
102
119
 
120
+ test('getIsolationMode returns "worktree" with worktree prefs', () => {
121
+ try {
122
+ writeRunnerPreferences("worktree");
123
+ invalidateAllCaches();
124
+ assert.deepStrictEqual(getIsolationMode(), "worktree", "getIsolationMode() with worktree prefs");
125
+ } finally {
126
+ removeRunnerPreferences();
127
+ invalidateAllCaches();
128
+ }
129
+ });
130
+
131
+ test('getIsolationMode returns "branch" with branch prefs', () => {
132
+ try {
133
+ writeRunnerPreferences("branch");
134
+ invalidateAllCaches();
135
+ assert.deepStrictEqual(getIsolationMode(), "branch", "getIsolationMode() with branch prefs");
136
+ } finally {
137
+ removeRunnerPreferences();
138
+ invalidateAllCaches();
139
+ }
140
+ });
141
+
103
142
  test('getActiveAutoWorktreeContext returns null at baseline', () => {
104
143
  assert.deepStrictEqual(getActiveAutoWorktreeContext(), null, "getActiveAutoWorktreeContext() returns null without enterAutoWorktree()");
105
144
  });
@@ -41,18 +41,16 @@ test("git.merge_to_main produces deprecation warning", () => {
41
41
  });
42
42
 
43
43
 
44
- test("getIsolationMode defaults to worktree when preferences have no isolation setting", () => {
44
+ test("getIsolationMode defaults to none when preferences have no isolation setting", () => {
45
45
  // Validate the default via validatePreferences: when no isolation is set,
46
- // preferences.git.isolation is undefined, and getIsolationMode returns "worktree".
47
- // We test the function's logic by verifying its documented default.
46
+ // preferences.git.isolation is undefined, and getIsolationMode returns "none".
47
+ // Default changed from "worktree" to "none" so GSD works out of the box
48
+ // without preferences.md (#2480).
48
49
  const { preferences } = validatePreferences({});
49
50
  assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs");
50
- // The function returns "worktree" when prefs?.git?.isolation is not "none" or "branch"
51
- // This is a compile-time-verifiable truth from the function body — test it directly
52
- // by constructing the same conditions getIsolationMode checks.
53
51
  const isolation = preferences.git?.isolation;
54
- const expected = isolation === "none" ? "none" : isolation === "branch" ? "branch" : "worktree";
55
- assert.equal(expected, "worktree", "default isolation mode is worktree");
52
+ const expected = isolation === "worktree" ? "worktree" : isolation === "branch" ? "branch" : "none";
53
+ assert.equal(expected, "none", "default isolation mode is none");
56
54
  });
57
55
 
58
56
  // ── Mode defaults ────────────────────────────────────────────────────────────
@@ -63,7 +61,7 @@ test("solo mode applies correct defaults", () => {
63
61
  assert.equal(result.git?.push_branches, false);
64
62
  assert.equal(result.git?.pre_merge_check, false);
65
63
  assert.equal(result.git?.merge_strategy, "squash");
66
- assert.equal(result.git?.isolation, "worktree");
64
+ assert.equal(result.git?.isolation, "none");
67
65
  assert.equal(result.unique_milestone_ids, false);
68
66
  });
69
67