@opengsd/gsd-pi 1.0.2-dev.235ebf3 → 1.0.2-dev.2c204d3

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 (151) hide show
  1. package/README.md +63 -12
  2. package/dist/resource-loader.d.ts +7 -0
  3. package/dist/resource-loader.js +42 -9
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/context7/index.js +12 -2
  6. package/dist/resources/extensions/gsd/auto/loop.js +19 -0
  7. package/dist/resources/extensions/gsd/auto/phases.js +1 -1
  8. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  9. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  10. package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  12. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -15
  13. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  15. package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
  16. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  17. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  18. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -1
  19. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  20. package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
  21. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  22. package/dist/resources/shared/package-manager-detection.js +36 -0
  23. package/dist/update-check.d.ts +6 -2
  24. package/dist/update-check.js +7 -3
  25. package/dist/web/standalone/.next/BUILD_ID +1 -1
  26. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  27. package/dist/web/standalone/.next/build-manifest.json +2 -2
  28. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  29. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  49. package/dist/web/standalone/.next/server/app/index.html +1 -1
  50. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  57. package/dist/web/standalone/.next/server/chunks/1834.js +1 -1
  58. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  60. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  61. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  62. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  63. package/dist/web/standalone/package.json +0 -1
  64. package/dist/worktree-cli.d.ts +0 -2
  65. package/dist/worktree-cli.js +21 -9
  66. package/package.json +5 -2
  67. package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
  68. package/packages/cloud-mcp-gateway/package.json +4 -3
  69. package/packages/contracts/package.json +1 -1
  70. package/packages/daemon/package.json +4 -4
  71. package/packages/gsd-agent-core/package.json +5 -5
  72. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  73. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
  74. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  75. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  76. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  77. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  78. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
  79. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  80. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
  81. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  82. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  83. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
  84. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  85. package/packages/gsd-agent-modes/package.json +7 -7
  86. package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
  87. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  88. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  89. package/packages/mcp-server/package.json +5 -4
  90. package/packages/native/package.json +1 -1
  91. package/packages/pi-agent-core/dist/agent-loop.js +13 -13
  92. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  93. package/packages/pi-agent-core/package.json +1 -1
  94. package/packages/pi-ai/bin/pi-ai.js +14 -0
  95. package/packages/pi-ai/dist/models.generated.d.ts +40 -17
  96. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  97. package/packages/pi-ai/dist/models.generated.js +49 -30
  98. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  100. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  101. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  102. package/packages/pi-ai/dist/types.d.ts +2 -0
  103. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  104. package/packages/pi-ai/dist/types.js.map +1 -1
  105. package/packages/pi-ai/package.json +3 -2
  106. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
  107. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
  109. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  110. package/packages/pi-coding-agent/package.json +8 -8
  111. package/packages/pi-tui/package.json +1 -1
  112. package/packages/rpc-client/package.json +2 -2
  113. package/pkg/package.json +1 -1
  114. package/scripts/install/deps.js +10 -0
  115. package/scripts/install/detect-existing.js +17 -3
  116. package/scripts/install/npm-global.js +103 -33
  117. package/scripts/install.js +1 -0
  118. package/src/resources/extensions/context7/index.ts +15 -2
  119. package/src/resources/extensions/gsd/auto/loop.ts +22 -0
  120. package/src/resources/extensions/gsd/auto/phases.ts +1 -1
  121. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  122. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  123. package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
  124. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  125. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -15
  126. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  127. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  128. package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
  129. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  130. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  131. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  132. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  133. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
  134. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  135. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  136. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  137. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +60 -0
  138. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +54 -0
  139. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  140. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  141. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
  142. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  143. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  144. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +56 -4
  145. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  146. package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
  147. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  148. package/src/resources/shared/package-manager-detection.ts +39 -0
  149. package/dist/tsconfig.extensions.tsbuildinfo +0 -1
  150. /package/dist/web/standalone/.next/static/{-P554bKh56nzavKUmvFM2 → mijI90BL1BdUcMUnhC0HU}/_buildManifest.js +0 -0
  151. /package/dist/web/standalone/.next/static/{-P554bKh56nzavKUmvFM2 → mijI90BL1BdUcMUnhC0HU}/_ssgManifest.js +0 -0
@@ -1,14 +1,61 @@
1
1
  import { execFileSync, spawn } from 'child_process'
2
- import { join } from 'path'
2
+ import { homedir } from 'os'
3
+ import { join, resolve as resolvePath, sep } from 'path'
3
4
 
4
- function getNpm() {
5
- return process.platform === 'win32' ? 'npm.cmd' : 'npm'
5
+ const PACKAGE_MANAGERS = new Set(['npm', 'pnpm'])
6
+
7
+ function normalizePackageManager(value) {
8
+ return PACKAGE_MANAGERS.has(value) ? value : 'npm'
9
+ }
10
+
11
+ function hasPnpmPath(value = '') {
12
+ const normalized = value.replace(/\\/g, '/').toLowerCase()
13
+ return (
14
+ normalized.includes('/.pnpm/') ||
15
+ normalized.endsWith('/pnpm') ||
16
+ normalized.endsWith('/pnpm.cjs') ||
17
+ normalized.endsWith('/pnpm.js')
18
+ )
19
+ }
20
+
21
+ function pathStartsWith(pathValue = '', dir) {
22
+ if (!pathValue) return false
23
+ const resolvedPath = resolvePath(pathValue)
24
+ const resolvedDir = resolvePath(dir)
25
+ return resolvedPath === resolvedDir || resolvedPath.startsWith(resolvedDir + sep)
26
+ }
27
+
28
+ function hasPnpmBinPath(env, argv1) {
29
+ const pnpmBinDirs = []
30
+ if (env.PNPM_HOME) pnpmBinDirs.push(env.PNPM_HOME)
31
+ pnpmBinDirs.push(join(homedir(), 'Library', 'pnpm'))
32
+ pnpmBinDirs.push(join(homedir(), '.local', 'share', 'pnpm'))
33
+
34
+ return pnpmBinDirs.some((dir) => pathStartsWith(argv1, dir) || pathStartsWith(env.npm_execpath, dir))
35
+ }
36
+
37
+ export function detectPackageManager(env = process.env, argv1 = process.argv[1]) {
38
+ const userAgent = env.npm_config_user_agent || ''
39
+ if (userAgent.startsWith('pnpm/')) return 'pnpm'
40
+ // Installer runs under npm during npm install; keep that context authoritative.
41
+ if (userAgent.startsWith('npm/')) return 'npm'
42
+
43
+ if (hasPnpmPath(env.npm_execpath || '')) return 'pnpm'
44
+ if (hasPnpmPath(argv1 || '')) return 'pnpm'
45
+ if (hasPnpmBinPath(env, argv1)) return 'pnpm'
46
+
47
+ return 'npm'
48
+ }
49
+
50
+ function getPackageManagerBin(packageManager) {
51
+ const bin = normalizePackageManager(packageManager)
52
+ return process.platform === 'win32' ? `${bin}.cmd` : bin
6
53
  }
7
54
 
8
- const NPM_OUTPUT_LIMIT = 64 * 1024
55
+ const PM_OUTPUT_LIMIT = 64 * 1024
9
56
 
10
- function runNpm(args) {
11
- return execFileSync(getNpm(), args, {
57
+ function runPackageManager(packageManager, args) {
58
+ return execFileSync(getPackageManagerBin(packageManager), args, {
12
59
  encoding: 'utf-8',
13
60
  stdio: ['ignore', 'pipe', 'pipe'],
14
61
  timeout: 120_000,
@@ -16,30 +63,30 @@ function runNpm(args) {
16
63
  }).trim()
17
64
  }
18
65
 
19
- function formatNpmFailure(result) {
66
+ function formatPackageManagerFailure(packageManager, result) {
20
67
  const output = `${result.stderr}\n${result.stdout}`.trim()
21
68
  const meaningful = output
22
69
  .split('\n')
23
- .filter((line) => !line.includes('npm warn') && !line.includes('npm WARN') && line.trim())
70
+ .filter((line) => line.trim() && !/^(?:npm\s+warn|warn)\b/i.test(line.trim()))
24
71
  .slice(-3)
25
72
  .join('; ')
26
- return meaningful || result.error?.message || 'npm install failed'
73
+ return meaningful || result.error?.message || `${packageManager} install failed`
27
74
  }
28
75
 
29
76
  function appendLimited(value, chunk) {
30
- if (value.length >= NPM_OUTPUT_LIMIT) return value
31
- return value + chunk.slice(0, NPM_OUTPUT_LIMIT - value.length)
77
+ if (value.length >= PM_OUTPUT_LIMIT) return value
78
+ return value + chunk.slice(0, PM_OUTPUT_LIMIT - value.length)
32
79
  }
33
80
 
34
- function runNpmAsync(args, {
81
+ function runPackageManagerAsync(packageManager, args, {
35
82
  captureStdout = false,
36
83
  cwd,
37
84
  timeout = 300_000,
38
85
  } = {}) {
39
- const npm = getNpm()
86
+ const bin = getPackageManagerBin(packageManager)
40
87
 
41
88
  return new Promise((resolve) => {
42
- const child = spawn(npm, args, {
89
+ const child = spawn(bin, args, {
43
90
  cwd,
44
91
  shell: process.platform === 'win32',
45
92
  stdio: ['ignore', captureStdout ? 'pipe' : 'ignore', 'pipe'],
@@ -85,7 +132,7 @@ function runNpmAsync(args, {
85
132
  ok: false,
86
133
  stdout,
87
134
  stderr,
88
- error: new Error(`npm ${args.join(' ')} timed out after ${timeout}ms`),
135
+ error: new Error(`${packageManager} ${args.join(' ')} timed out after ${timeout}ms`),
89
136
  })
90
137
  return
91
138
  }
@@ -100,20 +147,35 @@ function runNpmAsync(args, {
100
147
  ok: false,
101
148
  stdout,
102
149
  stderr,
103
- error: new Error(`npm ${args.join(' ')} failed with ${reason}`),
150
+ error: new Error(`${packageManager} ${args.join(' ')} failed with ${reason}`),
104
151
  })
105
152
  })
106
153
  })
107
154
  }
108
155
 
109
- export function getGlobalPaths() {
110
- const prefix = runNpm(['prefix', '-g'])
111
- const root = runNpm(['root', '-g'])
156
+ export function getGlobalPaths(options = {}) {
157
+ const packageManager = normalizePackageManager(options.packageManager ?? detectPackageManager())
158
+
159
+ if (packageManager === 'pnpm') {
160
+ const binDir = runPackageManager(packageManager, ['bin', '-g'])
161
+ const root = runPackageManager(packageManager, ['root', '-g'])
162
+ return {
163
+ prefix: binDir,
164
+ root,
165
+ binDir,
166
+ packageRoot: join(root, '@opengsd', 'gsd-pi'),
167
+ packageManager,
168
+ }
169
+ }
170
+
171
+ const prefix = runPackageManager(packageManager, ['prefix', '-g'])
172
+ const root = runPackageManager(packageManager, ['root', '-g'])
112
173
  return {
113
174
  prefix,
114
175
  root,
115
176
  binDir: process.platform === 'win32' ? prefix : join(prefix, 'bin'),
116
177
  packageRoot: join(root, '@opengsd', 'gsd-pi'),
178
+ packageManager,
117
179
  }
118
180
  }
119
181
 
@@ -121,33 +183,41 @@ export function getLocalPackageRoot(cwd = process.cwd()) {
121
183
  return join(cwd, 'node_modules', '@opengsd', 'gsd-pi')
122
184
  }
123
185
 
124
- export async function installGlobalPackage(version) {
125
- const result = await runNpmAsync([
126
- 'install',
127
- '-g',
128
- '--ignore-scripts',
129
- `@opengsd/gsd-pi@${version}`,
130
- ])
186
+ export async function installGlobalPackage(version, options = {}) {
187
+ const packageManager = normalizePackageManager(options.packageManager ?? detectPackageManager())
188
+ const packageSpec = `@opengsd/gsd-pi@${version}`
189
+ const installArgs = packageManager === 'pnpm'
190
+ ? ['add', '-g', '--ignore-scripts', packageSpec]
191
+ : ['install', '-g', '--ignore-scripts', packageSpec]
192
+
193
+ const result = await runPackageManagerAsync(packageManager, installArgs)
131
194
  if (!result.ok) {
132
- throw new Error(formatNpmFailure(result))
195
+ throw new Error(formatPackageManagerFailure(packageManager, result))
133
196
  }
134
- const rootResult = await runNpmAsync(['root', '-g'], {
197
+ const rootResult = await runPackageManagerAsync(packageManager, ['root', '-g'], {
135
198
  captureStdout: true,
136
199
  timeout: 120_000,
137
200
  })
138
201
  if (!rootResult.ok) {
139
- throw new Error(formatNpmFailure(rootResult))
202
+ throw new Error(formatPackageManagerFailure(packageManager, rootResult))
140
203
  }
141
204
  return join(rootResult.stdout.trim(), '@opengsd', 'gsd-pi')
142
205
  }
143
206
 
144
- export async function installLocalPackage(version, cwd = process.cwd()) {
145
- const result = await runNpmAsync(
146
- ['install', '--ignore-scripts', `@opengsd/gsd-pi@${version}`],
207
+ export async function installLocalPackage(version, cwd = process.cwd(), options = {}) {
208
+ const packageManager = normalizePackageManager(options.packageManager ?? detectPackageManager())
209
+ const packageSpec = `@opengsd/gsd-pi@${version}`
210
+ const installArgs = packageManager === 'pnpm'
211
+ ? ['add', '--ignore-scripts', packageSpec]
212
+ : ['install', '--ignore-scripts', packageSpec]
213
+
214
+ const result = await runPackageManagerAsync(
215
+ packageManager,
216
+ installArgs,
147
217
  { cwd },
148
218
  )
149
219
  if (!result.ok) {
150
- throw new Error(formatNpmFailure(result))
220
+ throw new Error(formatPackageManagerFailure(packageManager, result))
151
221
  }
152
222
  return getLocalPackageRoot(cwd)
153
223
  }
@@ -71,6 +71,7 @@ if (HAS_HELP) {
71
71
 
72
72
  ${colors.yellow}Usage:${colors.reset}
73
73
  npx @opengsd/gsd-pi@latest Install GSD-Pi globally (recommended)
74
+ pnpm dlx @opengsd/gsd-pi@latest Install GSD-Pi globally with pnpm
74
75
  npx @opengsd/gsd-pi@latest --local Install to current project (advanced)
75
76
 
76
77
  ${colors.yellow}Options:${colors.reset}
@@ -132,6 +132,19 @@ function formatLibraryList(libs: C7Library[], query: string): string {
132
132
  return lines.join("\n");
133
133
  }
134
134
 
135
+ function getFirstTextContent(result: { content?: unknown }): string | undefined {
136
+ const content = result.content;
137
+ if (!Array.isArray(content)) return undefined;
138
+ const textBlock = content.find(
139
+ (block: unknown) =>
140
+ typeof block === "object" &&
141
+ block !== null &&
142
+ (block as { type?: unknown }).type === "text" &&
143
+ typeof (block as { text?: unknown }).text === "string",
144
+ ) as { text: string } | undefined;
145
+ return textBlock?.text.trim() || undefined;
146
+ }
147
+
135
148
  // ─── Tool details types ───────────────────────────────────────────────────────
136
149
 
137
150
  interface ResolveDetails {
@@ -234,7 +247,7 @@ export default function (pi: ExtensionAPI) {
234
247
  const d = result.details as ResolveDetails | undefined;
235
248
  if (isPartial) return new Text(theme.fg("warning", "Searching Context7..."), 0, 0);
236
249
  if ((result as any).isError || d?.error) {
237
- return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
250
+ return new Text(theme.fg("error", `Error: ${d?.error ?? getFirstTextContent(result) ?? "unknown"}`), 0, 0);
238
251
  }
239
252
  let text = theme.fg("success", `${d?.resultCount ?? 0} ${d?.resultCount === 1 ? "library" : "libraries"} found`);
240
253
  if (d?.cached) text += theme.fg("dim", " (cached)");
@@ -389,7 +402,7 @@ export default function (pi: ExtensionAPI) {
389
402
 
390
403
  if (isPartial) return new Text(theme.fg("warning", "Fetching documentation..."), 0, 0);
391
404
  if ((result as any).isError || d?.error) {
392
- return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
405
+ return new Text(theme.fg("error", `Error: ${d?.error ?? getFirstTextContent(result) ?? "unknown"}`), 0, 0);
393
406
  }
394
407
 
395
408
  let text = theme.fg("success", `${(d?.charCount ?? 0).toLocaleString()} chars`);
@@ -106,6 +106,7 @@ import {
106
106
  import { handleCustomEngineReconcile } from "./workflow-custom-engine-reconcile.js";
107
107
  import { handleCustomEngineReconcileOutcome } from "./workflow-custom-engine-reconcile-outcome.js";
108
108
  import { formatLeaseConflictNotice } from "./lease-conflict-notice.js";
109
+ import { setAutoOutcomeWidget, unitVerb } from "../auto-dashboard.js";
109
110
 
110
111
  /**
111
112
  * Returns true if workerId is an active worker in this project whose OS
@@ -834,6 +835,27 @@ export async function autoLoop(
834
835
  },
835
836
  });
836
837
  if (reconcileFlow.action === "break") break;
838
+ if (s.stepMode) {
839
+ if (ctx.hasUI) {
840
+ ctx.ui.setWidget?.("gsd-progress", undefined);
841
+ setAutoOutcomeWidget(ctx, {
842
+ status: "step",
843
+ title: "Step complete",
844
+ detail: `Completed ${unitVerb(iterData.unitType)} ${iterData.unitId}.`,
845
+ unitLabel: `${unitVerb(iterData.unitType)} ${iterData.unitId}`,
846
+ nextAction: "Advance one step, or resume automatic mode.",
847
+ commands: ["/gsd next", "/gsd auto", "/gsd status for overview"],
848
+ startedAt: s.autoStartTime,
849
+ });
850
+ }
851
+ ctx.ui.setStatus("gsd-auto", "next");
852
+ ctx.ui.notify(
853
+ `Step complete: ${unitVerb(iterData.unitType)} ${iterData.unitId}. Run /gsd next for the next step, or /gsd auto to continue automatically.`,
854
+ "info",
855
+ );
856
+ s.preserveStepSurfaceAfterLoopExit = true;
857
+ break;
858
+ }
837
859
  continue;
838
860
  }
839
861
 
@@ -2072,7 +2072,7 @@ export async function runUnitPhase(
2072
2072
  const nextDispatchCount = (s.unitDispatchCount.get(dispatchKey) ?? 0) + 1;
2073
2073
 
2074
2074
  // Status bar (widget + preconditions deferred until after model selection — see #2899)
2075
- ctx.ui.setStatus("gsd-auto", "auto");
2075
+ ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
2076
2076
  if (mid)
2077
2077
  deps.updateSliceProgressCache(s.basePath, mid, state.activeSlice?.id);
2078
2078
 
@@ -218,6 +218,8 @@ export class AutoSession {
218
218
  // ── Isolation degradation ────────────────────────────────────────────
219
219
  /** Set to true when worktree creation fails; prevents merge of nonexistent branch. */
220
220
  isolationDegraded = false;
221
+ /** Temporary recovery mode for stranded work adopted from physical git evidence. */
222
+ strandedRecoveryIsolationMode: "worktree" | "branch" | null = null;
221
223
  /** Project-root dirty snapshot captured before an isolated worktree unit runs. */
222
224
  rootWriteBaseline: RootDirtySnapshot | null = null;
223
225
 
@@ -382,6 +384,7 @@ export class AutoSession {
382
384
  this.lastGitActionFailure = null;
383
385
  this.lastGitActionStatus = null;
384
386
  this.isolationDegraded = false;
387
+ this.strandedRecoveryIsolationMode = null;
385
388
  this.rootWriteBaseline = null;
386
389
  this.milestoneMergedInPhases = false;
387
390
  this.milestoneStartShas = new Map();