gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.4c866b677

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 (117) hide show
  1. package/dist/resource-loader.d.ts +1 -1
  2. package/dist/resource-loader.js +2 -8
  3. package/dist/resources/extensions/gsd/auto/phases.js +4 -1
  4. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +13 -2
  6. package/dist/resources/extensions/gsd/auto-start.js +28 -10
  7. package/dist/resources/extensions/gsd/auto.js +4 -1
  8. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  9. package/dist/resources/extensions/gsd/gsd-db.js +59 -3
  10. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  11. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  12. package/dist/resources/extensions/gsd/safety/file-change-validator.js +1 -1
  13. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  14. package/dist/web/standalone/.next/BUILD_ID +1 -1
  15. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  16. package/dist/web/standalone/.next/build-manifest.json +2 -2
  17. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  18. package/dist/web/standalone/.next/required-server-files.json +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.html +1 -1
  36. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  43. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  44. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  45. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  46. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  47. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  48. package/dist/web/standalone/server.js +1 -1
  49. package/package.json +1 -1
  50. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  51. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  52. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  53. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  54. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  55. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  56. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  57. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  58. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  59. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  60. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  61. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  62. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  63. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  64. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  65. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  66. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  67. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  68. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  69. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  70. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  71. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  72. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  73. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  74. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  75. package/packages/pi-coding-agent/dist/core/model-registry.js +76 -10
  76. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  77. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  78. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  79. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  80. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  81. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  82. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  83. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  84. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  85. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  87. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  88. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  89. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  90. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  91. package/packages/pi-coding-agent/src/core/model-registry.ts +86 -10
  92. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  93. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  94. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  95. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  96. package/scripts/link-workspace-packages.cjs +1 -0
  97. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  98. package/src/resources/extensions/gsd/auto/phases.ts +4 -0
  99. package/src/resources/extensions/gsd/auto/session.ts +7 -1
  100. package/src/resources/extensions/gsd/auto-model-selection.ts +16 -1
  101. package/src/resources/extensions/gsd/auto-start.ts +28 -10
  102. package/src/resources/extensions/gsd/auto.ts +4 -1
  103. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  104. package/src/resources/extensions/gsd/gsd-db.ts +65 -3
  105. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  106. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  107. package/src/resources/extensions/gsd/safety/file-change-validator.ts +1 -1
  108. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
  109. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  110. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  111. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  112. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +20 -0
  113. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +87 -0
  114. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  115. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  116. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → jDqWYbuP_CG6Kjc-uKwkN}/_buildManifest.js +0 -0
  117. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → jDqWYbuP_CG6Kjc-uKwkN}/_ssgManifest.js +0 -0
@@ -21,6 +21,6 @@ export declare function getNewerManagedResourceVersion(agentDir: string, current
21
21
  *
22
22
  * Inspectable: `ls ~/.gsd/agent/extensions/`
23
23
  */
24
- export declare function initResources(agentDir: string): void;
24
+ export declare function initResources(agentDir: string, skillsDir?: string): void;
25
25
  export declare function hasStaleCompiledExtensionSiblings(extensionsDir: string, sourceDir?: string): boolean;
26
26
  export declare function buildResourceLoader(agentDir: string): DefaultResourceLoader;
@@ -506,7 +506,7 @@ function pruneRemovedBundledExtensions(manifest, agentDir) {
506
506
  *
507
507
  * Inspectable: `ls ~/.gsd/agent/extensions/`
508
508
  */
509
- export function initResources(agentDir) {
509
+ export function initResources(agentDir, skillsDir = join(homedir(), '.agents', 'skills')) {
510
510
  mkdirSync(agentDir, { recursive: true });
511
511
  const currentVersion = getBundledGsdVersion();
512
512
  const manifest = readManagedResourceManifest(agentDir);
@@ -538,13 +538,7 @@ export function initResources(agentDir) {
538
538
  // Sync bundled resources — overwrite so updates land on next launch.
539
539
  syncResourceDir(bundledExtensionsDir, join(agentDir, 'extensions'));
540
540
  syncResourceDir(join(resourcesDir, 'agents'), join(agentDir, 'agents'));
541
- // Skills are no longer force-synced here. Users install skills via the
542
- // skills.sh CLI (`npx skills add <repo>`) into ~/.agents/skills/ which
543
- // is the industry-standard Agent Skills ecosystem directory.
544
- //
545
- // Migration from the legacy ~/.gsd/agent/skills/ directory is handled
546
- // above the manifest check so it runs on every launch (including retries
547
- // after partial copy failures).
541
+ syncResourceDir(join(resourcesDir, 'skills'), skillsDir);
548
542
  // Sync GSD-WORKFLOW.md to agentDir as a fallback for when GSD_WORKFLOW_PATH
549
543
  // env var is not set (e.g. fork/dev builds, alternative entry points).
550
544
  const workflowSrc = join(resourcesDir, 'GSD-WORKFLOW.md');
@@ -1094,7 +1094,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1094
1094
  logWarning("engine", "Prompt reorder failed", { error: msg });
1095
1095
  }
1096
1096
  // Select and apply model (with tier escalation on retry — normal units only)
1097
- const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier }, undefined, s.manualSessionModelOverride);
1097
+ const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier }, undefined, s.manualSessionModelOverride, s.autoModeStartThinkingLevel);
1098
1098
  s.currentUnitRouting =
1099
1099
  modelResult.routing;
1100
1100
  s.currentUnitModel =
@@ -1107,6 +1107,9 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1107
1107
  if (match) {
1108
1108
  const ok = await pi.setModel(match, { persist: false });
1109
1109
  if (ok) {
1110
+ if (s.autoModeStartThinkingLevel) {
1111
+ pi.setThinkingLevel(s.autoModeStartThinkingLevel);
1112
+ }
1110
1113
  s.currentUnitModel = match;
1111
1114
  ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
1112
1115
  }
@@ -65,6 +65,8 @@ export class AutoSession {
65
65
  currentDispatchedModelId = null;
66
66
  originalModelId = null;
67
67
  originalModelProvider = null;
68
+ autoModeStartThinkingLevel = null;
69
+ originalThinkingLevel = null;
68
70
  lastBudgetAlertLevel = 0;
69
71
  // ── Recovery ─────────────────────────────────────────────────────────────
70
72
  pendingCrashRecovery = null;
@@ -177,6 +179,8 @@ export class AutoSession {
177
179
  this.currentDispatchedModelId = null;
178
180
  this.originalModelId = null;
179
181
  this.originalModelProvider = null;
182
+ this.autoModeStartThinkingLevel = null;
183
+ this.originalThinkingLevel = null;
180
184
  this.lastBudgetAlertLevel = 0;
181
185
  // Recovery
182
186
  this.pendingCrashRecovery = null;
@@ -13,6 +13,11 @@ import { logWarning } from "./workflow-logger.js";
13
13
  import { resolveUokFlags } from "./uok/flags.js";
14
14
  import { applyModelPolicyFilter } from "./uok/model-policy.js";
15
15
  import { isModelBlocked } from "./blocked-models.js";
16
+ function reapplyThinkingLevel(pi, level) {
17
+ if (!level)
18
+ return;
19
+ pi.setThinkingLevel(level);
20
+ }
16
21
  export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true) {
17
22
  const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
18
23
  if (explicitConfig) {
@@ -58,7 +63,9 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
58
63
  * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
59
64
  isAutoMode = true,
60
65
  /** Explicit /gsd model pin captured at bootstrap for long-running auto loops. */
61
- sessionModelOverride) {
66
+ sessionModelOverride,
67
+ /** Thinking level captured at auto-mode start and re-applied after model swaps. */
68
+ autoModeStartThinkingLevel) {
62
69
  const uokFlags = resolveUokFlags(prefs);
63
70
  const effectiveSessionModelOverride = sessionModelOverride === undefined
64
71
  ? getSessionModelOverride(ctx.sessionManager.getSessionId())
@@ -280,6 +287,7 @@ sessionModelOverride) {
280
287
  const ok = await pi.setModel(model, { persist: false });
281
288
  if (ok) {
282
289
  appliedModel = model;
290
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
283
291
  // ADR-005: Adjust active tool set for the selected model's provider capabilities.
284
292
  // Hard-filter incompatible tools, then let extensions override via adjust_tool_set hook.
285
293
  const activeToolNames = pi.getActiveTools();
@@ -346,12 +354,15 @@ sessionModelOverride) {
346
354
  const byId = availableModels.find(m => m.id === autoModeStartModel.id && !isModelBlocked(basePath, m.provider, m.id));
347
355
  if (byId) {
348
356
  const fallbackOk = await pi.setModel(byId, { persist: false });
349
- if (fallbackOk)
357
+ if (fallbackOk) {
350
358
  appliedModel = byId;
359
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
360
+ }
351
361
  }
352
362
  }
353
363
  else {
354
364
  appliedModel = startModel;
365
+ reapplyThinkingLevel(pi, autoModeStartThinkingLevel);
355
366
  }
356
367
  }
357
368
  }
@@ -30,7 +30,7 @@ import { initRoutingHistory } from "./routing-history.js";
30
30
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
31
31
  import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
32
32
  import { snapshotSkills } from "./skill-discovery.js";
33
- import { isDbAvailable, getMilestone, openDatabase } from "./gsd-db.js";
33
+ import { isDbAvailable, getMilestone, openDatabase, getDbStatus } from "./gsd-db.js";
34
34
  import { hideFooter } from "./auto-dashboard.js";
35
35
  import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
36
36
  import { logWarning, logError } from "./workflow-logger.js";
@@ -190,8 +190,8 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
190
190
  //
191
191
  // Precedence:
192
192
  // 1) Explicit session override via /gsd model (this session)
193
- // 2) GSD model preferences from PREFERENCES.md (validated against live auth)
194
- // 3) Current session model from settings/session restore (if provider ready)
193
+ // 2) Current session model from settings/session restore (if provider ready)
194
+ // 3) GSD model preferences from PREFERENCES.md (validated against live auth)
195
195
  //
196
196
  // This preserves #3517 defaults while honoring explicit runtime model
197
197
  // selection for subsequent /gsd runs in the same session.
@@ -224,11 +224,14 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
224
224
  }
225
225
  }
226
226
  const sessionModelReady = ctx.model && ctx.modelRegistry.isProviderRequestReady(ctx.model.provider);
227
+ const currentSessionModel = (sessionModelReady && ctx.model)
228
+ ? { provider: ctx.model.provider, id: ctx.model.id }
229
+ : null;
230
+ const startThinkingSnapshot = pi.getThinkingLevel();
227
231
  const startModelSnapshot = manualSessionOverride
232
+ ?? currentSessionModel
228
233
  ?? validatedPreferredModel
229
- ?? (sessionModelReady && ctx.model
230
- ? { provider: ctx.model.provider, id: ctx.model.id }
231
- : null);
234
+ ?? null;
232
235
  try {
233
236
  // Validate GSD_PROJECT_ID early so the user gets immediate feedback
234
237
  const customProjectId = process.env.GSD_PROJECT_ID;
@@ -506,8 +509,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
506
509
  s.pendingQuickTasks = [];
507
510
  s.currentUnit = null;
508
511
  s.currentMilestoneId = state.activeMilestone?.id ?? null;
509
- s.originalModelId = ctx.model?.id ?? null;
510
- s.originalModelProvider = ctx.model?.provider ?? null;
512
+ s.originalModelId = startModelSnapshot?.id ?? ctx.model?.id ?? null;
513
+ s.originalModelProvider = startModelSnapshot?.provider ?? ctx.model?.provider ?? null;
514
+ s.originalThinkingLevel = startThinkingSnapshot ?? null;
511
515
  // Register SIGTERM handler
512
516
  registerSigtermHandler(base);
513
517
  // Capture integration branch
@@ -593,8 +597,21 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
593
597
  // call returns "db_unavailable", triggering artifact-retry which
594
598
  // re-dispatches the same task — producing an infinite loop (#2419).
595
599
  if (existsSync(gsdDbPath) && !isDbAvailable()) {
596
- ctx.ui.notify("SQLite database exists but failed to open. Auto-mode cannot proceed without a working database provider. " +
597
- "Check for corrupt gsd.db or missing native SQLite bindings.", "error");
600
+ const dbStatus = getDbStatus();
601
+ const phaseHint = dbStatus.lastPhase === "open"
602
+ ? "The database file could not be opened"
603
+ : dbStatus.lastPhase === "initSchema"
604
+ ? "The database schema could not be initialized"
605
+ : dbStatus.lastPhase === "vacuum-recovery"
606
+ ? "Corruption recovery (VACUUM) failed"
607
+ : dbStatus.attempted
608
+ ? "The database could not be opened (phase unknown)"
609
+ : "The database provider could not be loaded";
610
+ const errorDetail = dbStatus.lastError ? ` (${dbStatus.lastError.message})` : "";
611
+ const providerHint = dbStatus.provider
612
+ ? ` Provider: ${dbStatus.provider}.`
613
+ : " No SQLite provider available — check Node >= 22 or install better-sqlite3.";
614
+ ctx.ui.notify(`SQLite database exists but failed to open: ${gsdDbPath}. ${phaseHint}${errorDetail}.${providerHint}`, "error");
598
615
  return releaseLockAndReturn();
599
616
  }
600
617
  // Initialize metrics
@@ -608,6 +625,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
608
625
  id: startModelSnapshot.id,
609
626
  };
610
627
  }
628
+ s.autoModeStartThinkingLevel = startThinkingSnapshot ?? null;
611
629
  s.manualSessionModelOverride = manualSessionOverride ?? null;
612
630
  // Apply worker model override from parallel orchestrator (#worker-model).
613
631
  // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
@@ -695,13 +695,16 @@ export async function stopAuto(ctx, pi, reason) {
695
695
  catch (err) { /* non-fatal */
696
696
  logWarning("engine", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
697
697
  }
698
- // ── Step 13: Restore original model (before reset clears IDs) ──
698
+ // ── Step 13: Restore original model + thinking (before reset clears IDs) ──
699
699
  try {
700
700
  if (pi && ctx && s.originalModelId && s.originalModelProvider) {
701
701
  const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
702
702
  if (original)
703
703
  await pi.setModel(original);
704
704
  }
705
+ if (pi && s.originalThinkingLevel) {
706
+ pi.setThinkingLevel(s.originalThinkingLevel);
707
+ }
705
708
  }
706
709
  catch (e) {
707
710
  debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
@@ -8,10 +8,12 @@ import { getAdaptiveTierAdjustment } from "./routing-history.js";
8
8
  import { parseUnitId } from "./unit-id.js";
9
9
  // ─── Unit Type → Default Tier Mapping ────────────────────────────────────────
10
10
  const UNIT_TYPE_TIERS = {
11
- // Tier 1 — Light: structured summaries, completion, UAT
12
- "complete-slice": "light",
11
+ // Tier 1 — Light: compact verification turns
13
12
  "run-uat": "light",
14
- // Tier 2 — Standard: research, routine discussion
13
+ // Tier 2 — Standard: research, routine discussion, slice completion
14
+ // complete-slice can carry large inlined context; avoid routing it to the
15
+ // cheapest "light" model by default (#4520).
16
+ "complete-slice": "standard",
15
17
  "discuss-milestone": "standard",
16
18
  "discuss-slice": "standard",
17
19
  "research-milestone": "standard",
@@ -1097,6 +1097,8 @@ let currentPath = null;
1097
1097
  let currentPid = 0;
1098
1098
  let _exitHandlerRegistered = false;
1099
1099
  let _dbOpenAttempted = false;
1100
+ let _lastDbError = null;
1101
+ let _lastDbPhase = null;
1100
1102
  export function getDbProvider() {
1101
1103
  loadProvider();
1102
1104
  return providerName;
@@ -1113,13 +1115,54 @@ export function isDbAvailable() {
1113
1115
  export function wasDbOpenAttempted() {
1114
1116
  return _dbOpenAttempted;
1115
1117
  }
1118
+ export function getDbStatus() {
1119
+ loadProvider();
1120
+ return {
1121
+ available: currentDb !== null,
1122
+ provider: providerName,
1123
+ attempted: _dbOpenAttempted,
1124
+ lastError: _lastDbError,
1125
+ lastPhase: _lastDbPhase,
1126
+ };
1127
+ }
1116
1128
  export function openDatabase(path) {
1117
1129
  _dbOpenAttempted = true;
1118
1130
  if (currentDb && currentPath !== path)
1119
1131
  closeDatabase();
1120
1132
  if (currentDb && currentPath === path)
1121
1133
  return true;
1122
- const rawDb = openRawDb(path);
1134
+ // Reset error state only when a new open attempt is actually going to run.
1135
+ _lastDbError = null;
1136
+ _lastDbPhase = null;
1137
+ let rawDb;
1138
+ let fallbackProvider = null;
1139
+ let fallbackModule = null;
1140
+ try {
1141
+ rawDb = openRawDb(path);
1142
+ }
1143
+ catch (primaryErr) {
1144
+ _lastDbPhase = "open";
1145
+ _lastDbError = primaryErr instanceof Error ? primaryErr : new Error(String(primaryErr));
1146
+ // node:sqlite loaded but failed to open this file — try better-sqlite3 as fallback.
1147
+ if (providerName === "node:sqlite") {
1148
+ try {
1149
+ const mod = _require("better-sqlite3");
1150
+ const Db = (mod && mod.default) ? mod.default : mod;
1151
+ if (typeof Db === "function") {
1152
+ rawDb = new Db(path);
1153
+ fallbackProvider = "better-sqlite3";
1154
+ fallbackModule = Db;
1155
+ _lastDbError = null;
1156
+ _lastDbPhase = null;
1157
+ }
1158
+ }
1159
+ catch {
1160
+ // fallback unavailable; surface original error
1161
+ }
1162
+ }
1163
+ if (!rawDb)
1164
+ throw primaryErr;
1165
+ }
1123
1166
  if (!rawDb)
1124
1167
  return false;
1125
1168
  const adapter = createAdapter(rawDb);
@@ -1137,6 +1180,8 @@ export function openDatabase(path) {
1137
1180
  process.stderr.write("gsd-db: recovered corrupt database via VACUUM\n");
1138
1181
  }
1139
1182
  catch (retryErr) {
1183
+ _lastDbPhase = "vacuum-recovery";
1184
+ _lastDbError = retryErr instanceof Error ? retryErr : new Error(String(retryErr));
1140
1185
  try {
1141
1186
  adapter.close();
1142
1187
  }
@@ -1147,15 +1192,22 @@ export function openDatabase(path) {
1147
1192
  }
1148
1193
  }
1149
1194
  else {
1195
+ _lastDbPhase = "initSchema";
1196
+ _lastDbError = err instanceof Error ? err : new Error(String(err));
1150
1197
  try {
1151
1198
  adapter.close();
1152
1199
  }
1153
1200
  catch (e) {
1154
- logWarning("db", `close after VACUUM failed: ${e.message}`);
1201
+ logWarning("db", `close after initSchema failed: ${e.message}`);
1155
1202
  }
1156
1203
  throw err;
1157
1204
  }
1158
1205
  }
1206
+ // Commit fallback provider switch only after open + schema both succeeded.
1207
+ if (fallbackProvider) {
1208
+ providerName = fallbackProvider;
1209
+ providerModule = fallbackModule;
1210
+ }
1159
1211
  currentDb = adapter;
1160
1212
  currentPath = path;
1161
1213
  currentPid = process.pid;
@@ -1194,8 +1246,12 @@ export function closeDatabase() {
1194
1246
  currentDb = null;
1195
1247
  currentPath = null;
1196
1248
  currentPid = 0;
1197
- _dbOpenAttempted = false;
1198
1249
  }
1250
+ // Reset session-scoped state unconditionally so stale error info from a
1251
+ // failed open doesn't persist into the next open attempt or status check.
1252
+ _dbOpenAttempted = false;
1253
+ _lastDbError = null;
1254
+ _lastDbPhase = null;
1199
1255
  }
1200
1256
  /** Run a full VACUUM — call sparingly (e.g. after milestone completion). */
1201
1257
  export function vacuumDatabase() {
@@ -8,7 +8,7 @@
8
8
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { showNextAction } from "../shared/tui.js";
11
- import { nativeInit } from "./native-git-bridge.js";
11
+ import { nativeInit, nativeAddAll, nativeCommit } from "./native-git-bridge.js";
12
12
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
13
13
  import { gsdRoot } from "./paths.js";
14
14
  import { assertSafeDirectory } from "./validate-directory.js";
@@ -40,6 +40,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
40
40
  ctx.ui.notify(`Project detected:\n${detectionSummary.join("\n")}`, "info");
41
41
  }
42
42
  // ── Step 2: Git setup ──────────────────────────────────────────────────────
43
+ let didInitGit = false;
43
44
  if (!signals.isGitRepo) {
44
45
  const gitChoice = await showNextAction(ctx, {
45
46
  title: "GSD — Project Setup",
@@ -54,6 +55,7 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
54
55
  return { completed: false, bootstrapped: false };
55
56
  if (gitChoice === "init_git") {
56
57
  nativeInit(basePath, prefs.mainBranch);
58
+ didInitGit = true;
57
59
  }
58
60
  }
59
61
  else {
@@ -244,6 +246,18 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
244
246
  // Ensure .gitignore
245
247
  ensureGitignore(basePath);
246
248
  untrackRuntimeFiles(basePath);
249
+ // Create initial commit so git log and git worktree work immediately (#4530).
250
+ // Without this, the branch is "unborn" (zero commits) and downstream operations
251
+ // like `git log` and `git worktree add` fail.
252
+ if (didInitGit) {
253
+ try {
254
+ nativeAddAll(basePath);
255
+ nativeCommit(basePath, "chore: init project");
256
+ }
257
+ catch {
258
+ // Non-fatal — user can commit manually; don't block project init
259
+ }
260
+ }
247
261
  // Auto-generate codebase map for instant agent orientation
248
262
  try {
249
263
  const result = generateCodebaseMap(basePath);
@@ -22,6 +22,27 @@ import { join, dirname } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
23
  import { homedir } from "node:os";
24
24
  import { logWarning } from "./workflow-logger.js";
25
+ function hasRequiredExtensionAssets(rootDir, exists = existsSync) {
26
+ return (exists(join(rootDir, "prompts")) &&
27
+ exists(join(rootDir, "templates", "task-summary.md")));
28
+ }
29
+ export function resolveExtensionDirFromCandidates(moduleDir, agentGsdDir, exists = existsSync) {
30
+ const moduleUsable = hasRequiredExtensionAssets(moduleDir, exists);
31
+ const agentUsable = hasRequiredExtensionAssets(agentGsdDir, exists);
32
+ // Prefer the user-local extension tree when both are valid. This avoids
33
+ // leaking npm/global-install paths into prompts on Windows.
34
+ if (agentUsable)
35
+ return agentGsdDir;
36
+ if (moduleUsable)
37
+ return moduleDir;
38
+ // Degraded fallback: if required template is missing in both locations,
39
+ // keep previous behavior and prefer whichever still has prompts/.
40
+ if (exists(join(moduleDir, "prompts")))
41
+ return moduleDir;
42
+ if (exists(join(agentGsdDir, "prompts")))
43
+ return agentGsdDir;
44
+ return moduleDir;
45
+ }
25
46
  /**
26
47
  * Resolve the GSD extension directory.
27
48
  *
@@ -34,15 +55,9 @@ import { logWarning } from "./workflow-logger.js";
34
55
  */
35
56
  function resolveExtensionDir() {
36
57
  const moduleDir = dirname(fileURLToPath(import.meta.url));
37
- if (existsSync(join(moduleDir, "prompts")))
38
- return moduleDir;
39
- // Fallback: user-local agent directory
40
58
  const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
41
59
  const agentGsdDir = join(gsdHome, "agent", "extensions", "gsd");
42
- if (existsSync(join(agentGsdDir, "prompts")))
43
- return agentGsdDir;
44
- // Last resort: return the module dir (warmCache will silently handle the miss)
45
- return moduleDir;
60
+ return resolveExtensionDirFromCandidates(moduleDir, agentGsdDir);
46
61
  }
47
62
  const __extensionDir = resolveExtensionDir();
48
63
  const promptsDir = join(__extensionDir, "prompts");
@@ -62,7 +62,7 @@ export function validateFileChanges(basePath, expectedOutput, plannedFiles) {
62
62
  // ─── Internals ──────────────────────────────────────────────────────────────
63
63
  function getChangedFilesFromLastCommit(basePath) {
64
64
  try {
65
- const result = execFileSync("git", ["diff", "--name-only", "HEAD~1", "HEAD"], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
65
+ const result = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
66
66
  return result ? result.split("\n").filter(Boolean) : [];
67
67
  }
68
68
  catch (e) {