@opengsd/gsd-pi 1.1.1-dev.75048e7 → 1.1.1-dev.9f86580

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 (149) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +18 -2
  3. package/dist/resources/extensions/browser-tools/engine/selection.js +1 -1
  4. package/dist/resources/extensions/browser-tools/extension-manifest.json +1 -1
  5. package/dist/resources/extensions/browser-tools/index.js +29 -2
  6. package/dist/resources/extensions/browser-tools/web-app-detect.js +52 -0
  7. package/dist/resources/extensions/gsd/auto/phases.js +45 -3
  8. package/dist/resources/extensions/gsd/auto/session.js +2 -0
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +10 -2
  10. package/dist/resources/extensions/gsd/auto-model-selection.js +26 -0
  11. package/dist/resources/extensions/gsd/auto-timers.js +24 -10
  12. package/dist/resources/extensions/gsd/auto.js +26 -4
  13. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +29 -21
  14. package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
  15. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -0
  16. package/dist/resources/extensions/gsd/commands-mcp-status.js +1 -1
  17. package/dist/resources/extensions/gsd/config-overlay.js +1 -0
  18. package/dist/resources/extensions/gsd/context-masker.js +129 -5
  19. package/dist/resources/extensions/gsd/guided-flow.js +4 -1
  20. package/dist/resources/extensions/gsd/planner-handoff.js +98 -0
  21. package/dist/resources/extensions/gsd/preferences-models.js +1 -0
  22. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  23. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  24. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  25. package/dist/resources/extensions/gsd/skill-manifest.js +12 -0
  26. package/dist/resources/extensions/gsd/tool-contract.js +1 -1
  27. package/dist/resources/extensions/gsd/tool-presentation-plan.js +19 -2
  28. package/dist/resources/extensions/gsd/tools/complete-slice.js +28 -1
  29. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +32 -4
  30. package/dist/resources/extensions/gsd/unit-tool-contracts.js +38 -14
  31. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -3
  32. package/dist/resources/extensions/gsd/worktree-manager.js +26 -0
  33. package/dist/resources/extensions/gsd/worktree-reentry.js +96 -0
  34. package/dist/resources/extensions/shared/gsd-browser-cli.js +6 -0
  35. package/dist/web/standalone/.next/BUILD_ID +1 -1
  36. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  37. package/dist/web/standalone/.next/build-manifest.json +2 -2
  38. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  39. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.html +1 -1
  56. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  63. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  64. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  66. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  67. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  68. package/package.json +1 -1
  69. package/packages/cloud-mcp-gateway/package.json +2 -2
  70. package/packages/contracts/package.json +1 -1
  71. package/packages/daemon/package.json +4 -4
  72. package/packages/gsd-agent-core/package.json +5 -5
  73. package/packages/gsd-agent-modes/package.json +7 -7
  74. package/packages/mcp-server/package.json +3 -3
  75. package/packages/native/package.json +1 -1
  76. package/packages/pi-agent-core/package.json +1 -1
  77. package/packages/pi-ai/dist/models.generated.d.ts +158 -2
  78. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  79. package/packages/pi-ai/dist/models.generated.js +149 -9
  80. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  81. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  82. package/packages/pi-ai/dist/providers/transform-messages.js +8 -1
  83. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  84. package/packages/pi-ai/package.json +1 -1
  85. package/packages/pi-coding-agent/package.json +7 -7
  86. package/packages/pi-tui/package.json +1 -1
  87. package/packages/rpc-client/package.json +2 -2
  88. package/pkg/package.json +1 -1
  89. package/scripts/install/handoff.js +16 -3
  90. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +21 -2
  91. package/src/resources/extensions/browser-tools/engine/selection.ts +1 -1
  92. package/src/resources/extensions/browser-tools/extension-manifest.json +1 -1
  93. package/src/resources/extensions/browser-tools/index.ts +36 -5
  94. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +2 -2
  95. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +37 -0
  96. package/src/resources/extensions/browser-tools/tests/web-app-detect.test.mjs +68 -0
  97. package/src/resources/extensions/browser-tools/web-app-detect.ts +63 -0
  98. package/src/resources/extensions/gsd/auto/phases.ts +48 -6
  99. package/src/resources/extensions/gsd/auto/session.ts +2 -0
  100. package/src/resources/extensions/gsd/auto-dispatch.ts +34 -2
  101. package/src/resources/extensions/gsd/auto-model-selection.ts +26 -0
  102. package/src/resources/extensions/gsd/auto-timers.ts +25 -9
  103. package/src/resources/extensions/gsd/auto.ts +28 -4
  104. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +40 -21
  105. package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
  106. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -0
  107. package/src/resources/extensions/gsd/commands-mcp-status.ts +1 -1
  108. package/src/resources/extensions/gsd/config-overlay.ts +1 -0
  109. package/src/resources/extensions/gsd/context-masker.ts +152 -5
  110. package/src/resources/extensions/gsd/guided-flow.ts +4 -1
  111. package/src/resources/extensions/gsd/planner-handoff.ts +149 -0
  112. package/src/resources/extensions/gsd/preferences-models.ts +1 -0
  113. package/src/resources/extensions/gsd/preferences-types.ts +8 -0
  114. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  115. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  116. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  117. package/src/resources/extensions/gsd/skill-manifest.ts +12 -0
  118. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +99 -0
  119. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +66 -4
  120. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +4 -0
  121. package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +9 -0
  122. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +118 -0
  123. package/src/resources/extensions/gsd/tests/context-masker.test.ts +56 -1
  124. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +1 -0
  125. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +24 -0
  126. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +1 -1
  127. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +27 -0
  128. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +1 -0
  129. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +7 -1
  130. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +1 -1
  131. package/src/resources/extensions/gsd/tests/planner-handoff.test.ts +100 -0
  132. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +113 -1
  133. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +55 -0
  134. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
  135. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +4 -3
  136. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +77 -10
  137. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +131 -2
  138. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +102 -0
  139. package/src/resources/extensions/gsd/tool-contract.ts +1 -1
  140. package/src/resources/extensions/gsd/tool-presentation-plan.ts +21 -2
  141. package/src/resources/extensions/gsd/tools/complete-slice.ts +29 -1
  142. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +46 -4
  143. package/src/resources/extensions/gsd/unit-tool-contracts.ts +38 -14
  144. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -3
  145. package/src/resources/extensions/gsd/worktree-manager.ts +32 -0
  146. package/src/resources/extensions/gsd/worktree-reentry.ts +103 -0
  147. package/src/resources/extensions/shared/gsd-browser-cli.ts +6 -0
  148. /package/dist/web/standalone/.next/static/{h4TGni4xJzlZjGkxaT6uU → zzYMrKpPGfRQRxSFO32Jr}/_buildManifest.js +0 -0
  149. /package/dist/web/standalone/.next/static/{h4TGni4xJzlZjGkxaT6uU → zzYMrKpPGfRQRxSFO32Jr}/_ssgManifest.js +0 -0
@@ -52,8 +52,14 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
52
52
  requiredWorkflowTools: ["gsd_summary_save"],
53
53
  },
54
54
  "plan-milestone": {
55
- allowedGsdTools: ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
56
- requiredWorkflowTools: ["gsd_plan_milestone"],
55
+ allowedGsdTools: [
56
+ "gsd_milestone_status",
57
+ "gsd_plan_milestone",
58
+ "gsd_plan_slice",
59
+ "gsd_decision_save",
60
+ "gsd_requirement_update",
61
+ ],
62
+ requiredWorkflowTools: ["gsd_milestone_status", "gsd_plan_milestone", "gsd_plan_slice"],
57
63
  },
58
64
  "discuss-milestone": {
59
65
  allowedGsdTools: [
@@ -77,27 +83,38 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
77
83
  requiredWorkflowTools: ["gsd_summary_save"],
78
84
  },
79
85
  "validate-milestone": {
80
- allowedGsdTools: ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
86
+ allowedGsdTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
81
87
  requiredWorkflowTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap"],
82
88
  },
83
89
  "complete-milestone": {
84
- allowedGsdTools: ["gsd_complete_milestone", "subagent"],
85
- requiredWorkflowTools: ["gsd_milestone_status", "gsd_complete_milestone"],
90
+ allowedGsdTools: [
91
+ "gsd_milestone_status",
92
+ "gsd_requirement_update",
93
+ "gsd_summary_save",
94
+ "gsd_complete_milestone",
95
+ "subagent",
96
+ ],
97
+ requiredWorkflowTools: [
98
+ "gsd_milestone_status",
99
+ "gsd_requirement_update",
100
+ "gsd_summary_save",
101
+ "gsd_complete_milestone",
102
+ ],
86
103
  },
87
104
  "research-slice": {
88
105
  allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
89
106
  requiredWorkflowTools: ["gsd_summary_save"],
90
107
  },
91
108
  "plan-slice": {
92
- allowedGsdTools: ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
93
- requiredWorkflowTools: ["gsd_plan_slice"],
109
+ allowedGsdTools: ["gsd_plan_slice", "gsd_reassess_roadmap", "gsd_decision_save"],
110
+ requiredWorkflowTools: ["gsd_plan_slice", "gsd_reassess_roadmap"],
94
111
  },
95
112
  "refine-slice": {
96
- allowedGsdTools: ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
97
- requiredWorkflowTools: [],
113
+ allowedGsdTools: ["gsd_plan_slice", "gsd_decision_save"],
114
+ requiredWorkflowTools: ["gsd_plan_slice"],
98
115
  },
99
116
  "replan-slice": {
100
- allowedGsdTools: ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
117
+ allowedGsdTools: ["gsd_replan_slice", "gsd_decision_save"],
101
118
  requiredWorkflowTools: ["gsd_replan_slice"],
102
119
  },
103
120
  "complete-slice": {
@@ -107,15 +124,22 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
107
124
  "gsd_replan_slice",
108
125
  "gsd_decision_save",
109
126
  "gsd_requirement_update",
127
+ "gsd_summary_save",
110
128
  "subagent",
111
129
  ],
112
- requiredWorkflowTools: ["gsd_slice_complete", "gsd_task_reopen", "gsd_replan_slice"],
130
+ requiredWorkflowTools: [
131
+ "gsd_slice_complete",
132
+ "gsd_task_reopen",
133
+ "gsd_replan_slice",
134
+ "gsd_requirement_update",
135
+ "gsd_summary_save",
136
+ ],
113
137
  forbiddenGsdTools: {
114
138
  gsd_uat_result_save: "Run UAT owns persisted UAT Assessment.",
115
139
  },
116
140
  },
117
141
  "reassess-roadmap": {
118
- allowedGsdTools: ["gsd_reassess_roadmap"],
142
+ allowedGsdTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
119
143
  requiredWorkflowTools: ["gsd_milestone_status", "gsd_reassess_roadmap"],
120
144
  },
121
145
  "execute-task": {
@@ -127,8 +151,8 @@ export const UNIT_TOOL_CONTRACTS: Record<string, UnitToolSurfaceContract> = {
127
151
  requiredWorkflowTools: ["gsd_task_complete"],
128
152
  },
129
153
  "reactive-execute": {
130
- allowedGsdTools: ["gsd_task_complete", "gsd_decision_save"],
131
- requiredWorkflowTools: ["gsd_task_complete"],
154
+ allowedGsdTools: ["gsd_task_complete", "gsd_summary_save", "gsd_decision_save"],
155
+ requiredWorkflowTools: ["gsd_task_complete", "gsd_summary_save"],
132
156
  },
133
157
  "run-uat": {
134
158
  allowedGsdTools: [...RUN_UAT_WORKFLOW_TOOL_NAMES, "subagent"],
@@ -487,10 +487,9 @@ export function getWorkflowTransportSupportError(
487
487
  }
488
488
 
489
489
  const uniqueRequired = [...new Set(requiredTools)];
490
- const piRuntimeRequired = uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
491
490
  const missing = (options.activeTools && options.activeTools.length > 0)
492
- ? piRuntimeRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools!))
493
- : piRuntimeRequired;
491
+ ? uniqueRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools!))
492
+ : uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
494
493
  if (missing.length === 0) return null;
495
494
 
496
495
  if (options.activeTools && options.activeTools.length > 0) {
@@ -259,6 +259,38 @@ export function resolveCanonicalMilestoneRoot(
259
259
  return wtPath;
260
260
  }
261
261
 
262
+ /**
263
+ * Build human-facing guidance for manually validating a milestone's work.
264
+ *
265
+ * When a milestone runs in a git worktree, its checkout lives under the hidden
266
+ * `.gsd/worktrees/<MID>/` path that a human can't easily discover. The UAT
267
+ * pause/handoff and the saved assessment use this to tell the human exactly
268
+ * where to `cd` to run or inspect the app before signing off on NEEDS-HUMAN
269
+ * checks, rather than leaving them to hunt for a buried path.
270
+ *
271
+ * Returns null when no milestone id is available.
272
+ */
273
+ export function buildManualValidationGuidance(
274
+ basePath: string,
275
+ milestoneId: string,
276
+ opts: { uatPath?: string } = {},
277
+ ): string | null {
278
+ if (!milestoneId) return null;
279
+ const validationRoot = resolveCanonicalMilestoneRoot(basePath, milestoneId);
280
+ const inWorktree = validationRoot.includes(`${sep}.gsd${sep}worktrees${sep}`);
281
+ const lines: string[] = [`Validate the work here: ${validationRoot}`];
282
+ if (inWorktree) {
283
+ lines.push(
284
+ "This milestone runs in a git worktree, so the code lives under the hidden " +
285
+ `\`.gsd/worktrees/\` path. Open it with: cd "${validationRoot}"`,
286
+ );
287
+ }
288
+ if (opts.uatPath) {
289
+ lines.push(`Follow the UAT checklist at: ${opts.uatPath}`);
290
+ }
291
+ return lines.join("\n");
292
+ }
293
+
262
294
  // ─── Core Operations ───────────────────────────────────────────────────────
263
295
 
264
296
  /**
@@ -0,0 +1,103 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Deterministically re-enter the active milestone's worktree on a
3
+ // cold start (after /quit + relaunch). Without this, Claude Code relaunches with
4
+ // cwd at the project root, and a bare /gsd leaves the agent there — forcing it to
5
+ // search the filesystem ("git worktree list", branch sniffing) to find its way
6
+ // back into the worktree. The worktree path is fully derivable from state, so we
7
+ // resolve and chdir into it directly instead.
8
+
9
+ import { readdirSync } from "node:fs";
10
+
11
+ import { enterAutoWorktree, getAutoWorktreePath } from "./auto-worktree.js";
12
+ import { getIsolationMode } from "./preferences.js";
13
+ import { worktreesDir } from "./worktree-manager.js";
14
+ import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "./worktree-root.js";
15
+
16
+ interface LiveWorktree {
17
+ id: string;
18
+ path: string;
19
+ }
20
+
21
+ /**
22
+ * Enumerate the live (valid git) auto-worktrees under <projectRoot>/.gsd/worktrees/.
23
+ * Reuses getAutoWorktreePath's validation so stray directories are ignored.
24
+ */
25
+ function liveMilestoneWorktrees(projectRoot: string): LiveWorktree[] {
26
+ let names: string[];
27
+ try {
28
+ names = readdirSync(worktreesDir(projectRoot));
29
+ } catch {
30
+ return [];
31
+ }
32
+ const live: LiveWorktree[] = [];
33
+ for (const id of names) {
34
+ const path = getAutoWorktreePath(projectRoot, id);
35
+ if (path) live.push({ id, path });
36
+ }
37
+ return live;
38
+ }
39
+
40
+ /**
41
+ * If we're sitting at the project root with worktree isolation enabled and the
42
+ * active milestone has a live worktree, chdir into it. No-op when already inside
43
+ * a worktree, when isolation is off, or when the target is ambiguous.
44
+ *
45
+ * Single live worktree → enter it (covers the common case without deriveState).
46
+ * Multiple live worktrees → disambiguate by the active milestone from state;
47
+ * if that can't be resolved unambiguously, do nothing.
48
+ *
49
+ * Best-effort: any failure resolves to a no-op so it can never block startup.
50
+ *
51
+ * @returns the worktree path entered, or null when nothing was done.
52
+ */
53
+ export async function reenterActiveWorktreeIfNeeded(
54
+ basePath: string,
55
+ opts: { notify?: (message: string) => void } = {},
56
+ ): Promise<string | null> {
57
+ let projectRoot: string;
58
+ try {
59
+ projectRoot = resolveWorktreeProjectRoot(basePath);
60
+ } catch {
61
+ return null;
62
+ }
63
+
64
+ // Only worktree-isolation projects have worktrees to re-enter.
65
+ if (getIsolationMode(projectRoot) !== "worktree") return null;
66
+
67
+ // Already inside a worktree (warm session, or auto-mode already entered) —
68
+ // nothing to do.
69
+ let cwd: string;
70
+ try {
71
+ cwd = process.cwd();
72
+ } catch {
73
+ return null;
74
+ }
75
+ if (isGsdWorktreePath(cwd)) return null;
76
+
77
+ const live = liveMilestoneWorktrees(projectRoot);
78
+ if (live.length === 0) return null;
79
+
80
+ let target: LiveWorktree | null = live.length === 1 ? live[0]! : null;
81
+ if (!target) {
82
+ // Multiple live worktrees — disambiguate by the active milestone.
83
+ try {
84
+ const { deriveState } = await import("./state.js");
85
+ const state = await deriveState(projectRoot);
86
+ const activeId = state.activeMilestone?.id;
87
+ target = activeId ? live.find((w) => w.id === activeId) ?? null : null;
88
+ } catch {
89
+ target = null;
90
+ }
91
+ }
92
+ if (!target) return null;
93
+
94
+ try {
95
+ const entered = enterAutoWorktree(projectRoot, target.id);
96
+ opts.notify?.(`Resumed in worktree for ${target.id}.`);
97
+ return entered;
98
+ } catch {
99
+ // Worktree vanished or chdir failed — leave cwd as-is, caller falls back to
100
+ // the project root.
101
+ return null;
102
+ }
103
+ }
@@ -140,6 +140,10 @@ export function resolveGsdBrowserMcpLaunchConfig(
140
140
  : null;
141
141
  const sessionName =
142
142
  options.sessionName?.trim() || buildGsdBrowserSessionName(resolvedProjectRoot, options.sessionSuffix);
143
+ // Stable per-project identity key (no per-session suffix) so the browser
144
+ // profile/cookies persist across pi sessions for the same project. gsd-browser
145
+ // rejects --identity-scope unless --identity-key is also supplied.
146
+ const identityKey = env.GSD_BROWSER_IDENTITY_KEY?.trim() || buildGsdBrowserSessionName(resolvedProjectRoot);
143
147
  const command =
144
148
  explicitCommand
145
149
  || explicitCliPath
@@ -155,6 +159,8 @@ export function resolveGsdBrowserMcpLaunchConfig(
155
159
  sessionName,
156
160
  "--identity-scope",
157
161
  "project",
162
+ "--identity-key",
163
+ identityKey,
158
164
  "--identity-project",
159
165
  resolvedProjectRoot,
160
166
  ];