@opengsd/gsd-pi 1.0.2-dev.235ebf3 → 1.0.2-dev.29398d2
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.
- package/README.md +63 -12
- package/dist/onboarding.js +22 -3
- package/dist/resource-loader.d.ts +7 -0
- package/dist/resource-loader.js +42 -9
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/context7/index.js +12 -2
- package/dist/resources/extensions/google-cli/index.js +30 -0
- package/dist/resources/extensions/google-cli/models.js +55 -0
- package/dist/resources/extensions/google-cli/package.json +11 -0
- package/dist/resources/extensions/google-cli/readiness.js +12 -0
- package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
- package/dist/resources/extensions/gsd/auto/loop.js +19 -0
- package/dist/resources/extensions/gsd/auto/phases.js +1 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-start.js +232 -49
- package/dist/resources/extensions/gsd/auto-worktree.js +2 -54
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -15
- package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +3 -0
- package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
- package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
- package/dist/resources/extensions/gsd/key-manager.js +45 -13
- package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
- package/dist/resources/extensions/gsd/worktree-post-create-hook.js +117 -0
- package/dist/resources/extensions/search-the-web/native-search.js +57 -8
- package/dist/resources/shared/package-manager-detection.js +36 -0
- package/dist/update-check.d.ts +6 -2
- package/dist/update-check.js +7 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
- package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/package.json +0 -1
- package/dist/worktree-cli.d.ts +0 -2
- package/dist/worktree-cli.js +21 -9
- package/package.json +5 -2
- package/packages/cloud-mcp-gateway/bin/gsd-cloud-mcp-gateway.js +14 -0
- package/packages/cloud-mcp-gateway/package.json +4 -3
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
- package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +3 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +2 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/bin/gsd-mcp-server.js +14 -0
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +5 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +13 -13
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/bin/pi-ai.js +14 -0
- package/packages/pi-ai/dist/models.generated.d.ts +40 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +49 -30
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +50 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/read.js +5 -3
- package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
- package/packages/pi-coding-agent/package.json +8 -8
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/install/deps.js +10 -0
- package/scripts/install/detect-existing.js +17 -3
- package/scripts/install/npm-global.js +103 -33
- package/scripts/install.js +1 -0
- package/src/resources/extensions/context7/index.ts +15 -2
- package/src/resources/extensions/google-cli/index.ts +34 -0
- package/src/resources/extensions/google-cli/models.ts +57 -0
- package/src/resources/extensions/google-cli/package.json +11 -0
- package/src/resources/extensions/google-cli/readiness.ts +15 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
- package/src/resources/extensions/gsd/auto/loop.ts +22 -0
- package/src/resources/extensions/gsd/auto/phases.ts +1 -1
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto-start.ts +307 -56
- package/src/resources/extensions/gsd/auto-worktree.ts +2 -56
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -15
- package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +2 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
- package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
- package/src/resources/extensions/gsd/key-manager.ts +57 -14
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +141 -1
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
- package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +56 -4
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
- package/src/resources/extensions/gsd/worktree-post-create-hook.ts +127 -0
- package/src/resources/extensions/search-the-web/native-search.ts +60 -8
- package/src/resources/shared/package-manager-detection.ts +39 -0
- package/dist/tsconfig.extensions.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{-P554bKh56nzavKUmvFM2 → bukT6Ux1YchPm2XqjaexX}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-P554bKh56nzavKUmvFM2 → bukT6Ux1YchPm2XqjaexX}/_ssgManifest.js +0 -0
|
@@ -1,14 +1,61 @@
|
|
|
1
1
|
import { execFileSync, spawn } from 'child_process'
|
|
2
|
-
import {
|
|
2
|
+
import { homedir } from 'os'
|
|
3
|
+
import { join, resolve as resolvePath, sep } from 'path'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
55
|
+
const PM_OUTPUT_LIMIT = 64 * 1024
|
|
9
56
|
|
|
10
|
-
function
|
|
11
|
-
return execFileSync(
|
|
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
|
|
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) =>
|
|
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 ||
|
|
73
|
+
return meaningful || result.error?.message || `${packageManager} install failed`
|
|
27
74
|
}
|
|
28
75
|
|
|
29
76
|
function appendLimited(value, chunk) {
|
|
30
|
-
if (value.length >=
|
|
31
|
-
return value + chunk.slice(0,
|
|
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
|
|
81
|
+
function runPackageManagerAsync(packageManager, args, {
|
|
35
82
|
captureStdout = false,
|
|
36
83
|
cwd,
|
|
37
84
|
timeout = 300_000,
|
|
38
85
|
} = {}) {
|
|
39
|
-
const
|
|
86
|
+
const bin = getPackageManagerBin(packageManager)
|
|
40
87
|
|
|
41
88
|
return new Promise((resolve) => {
|
|
42
|
-
const child = spawn(
|
|
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(
|
|
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(
|
|
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
|
|
111
|
-
|
|
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
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
'--ignore-scripts',
|
|
129
|
-
|
|
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(
|
|
195
|
+
throw new Error(formatPackageManagerFailure(packageManager, result))
|
|
133
196
|
}
|
|
134
|
-
const rootResult = await
|
|
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(
|
|
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
|
|
146
|
-
|
|
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(
|
|
220
|
+
throw new Error(formatPackageManagerFailure(packageManager, result))
|
|
151
221
|
}
|
|
152
222
|
return getLocalPackageRoot(cwd)
|
|
153
223
|
}
|
package/scripts/install.js
CHANGED
|
@@ -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`);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google local CLI providers.
|
|
3
|
+
*
|
|
4
|
+
* These deliberately use authMode "externalCli": GSD never owns the browser
|
|
5
|
+
* OAuth flow or cached tokens. Users authenticate with the official CLI, then
|
|
6
|
+
* /login activates the provider once the local binary is available.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
10
|
+
import { GOOGLE_ANTIGRAVITY_MODELS, GOOGLE_GEMINI_CLI_MODELS } from "./models.js";
|
|
11
|
+
import { isAntigravityCliReady, isGeminiCliReady } from "./readiness.js";
|
|
12
|
+
import { streamViaGoogleCli } from "./stream-adapter.js";
|
|
13
|
+
|
|
14
|
+
export default function googleCli(pi: ExtensionAPI) {
|
|
15
|
+
pi.registerProvider("google-gemini-cli", {
|
|
16
|
+
name: "Google Gemini CLI",
|
|
17
|
+
authMode: "externalCli",
|
|
18
|
+
api: "google-gemini-cli",
|
|
19
|
+
baseUrl: "local://google-gemini-cli",
|
|
20
|
+
isReady: isGeminiCliReady,
|
|
21
|
+
streamSimple: streamViaGoogleCli,
|
|
22
|
+
models: GOOGLE_GEMINI_CLI_MODELS,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
pi.registerProvider("google-antigravity", {
|
|
26
|
+
name: "Google Antigravity",
|
|
27
|
+
authMode: "externalCli",
|
|
28
|
+
api: "google-antigravity",
|
|
29
|
+
baseUrl: "local://google-antigravity",
|
|
30
|
+
isReady: isAntigravityCliReady,
|
|
31
|
+
streamSimple: streamViaGoogleCli,
|
|
32
|
+
models: GOOGLE_ANTIGRAVITY_MODELS,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const ZERO_COST = {
|
|
2
|
+
input: 0,
|
|
3
|
+
output: 0,
|
|
4
|
+
cacheRead: 0,
|
|
5
|
+
cacheWrite: 0,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const GOOGLE_GEMINI_CLI_MODELS = [
|
|
9
|
+
{
|
|
10
|
+
id: "gemini-2.5-flash",
|
|
11
|
+
name: "Gemini 2.5 Flash",
|
|
12
|
+
reasoning: true,
|
|
13
|
+
input: ["text" as const],
|
|
14
|
+
cost: ZERO_COST,
|
|
15
|
+
contextWindow: 1_000_000,
|
|
16
|
+
maxTokens: 65_536,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "gemini-2.5-pro",
|
|
20
|
+
name: "Gemini 2.5 Pro",
|
|
21
|
+
reasoning: true,
|
|
22
|
+
input: ["text" as const],
|
|
23
|
+
cost: ZERO_COST,
|
|
24
|
+
contextWindow: 1_000_000,
|
|
25
|
+
maxTokens: 65_536,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "gemini-3-flash-preview",
|
|
29
|
+
name: "Gemini 3 Flash Preview",
|
|
30
|
+
reasoning: true,
|
|
31
|
+
input: ["text" as const],
|
|
32
|
+
cost: ZERO_COST,
|
|
33
|
+
contextWindow: 1_000_000,
|
|
34
|
+
maxTokens: 65_536,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "gemini-3.1-pro-preview",
|
|
38
|
+
name: "Gemini 3.1 Pro Preview",
|
|
39
|
+
reasoning: true,
|
|
40
|
+
input: ["text" as const],
|
|
41
|
+
cost: ZERO_COST,
|
|
42
|
+
contextWindow: 1_000_000,
|
|
43
|
+
maxTokens: 65_536,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
export const GOOGLE_ANTIGRAVITY_MODELS = [
|
|
48
|
+
{
|
|
49
|
+
id: "default",
|
|
50
|
+
name: "Antigravity Default",
|
|
51
|
+
reasoning: true,
|
|
52
|
+
input: ["text" as const],
|
|
53
|
+
cost: ZERO_COST,
|
|
54
|
+
contextWindow: 1_000_000,
|
|
55
|
+
maxTokens: 65_536,
|
|
56
|
+
},
|
|
57
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
function isCommandInPath(command: string): boolean {
|
|
4
|
+
const resolver = process.platform === "win32" ? "where" : "which";
|
|
5
|
+
const result = spawnSync(resolver, [command], { stdio: "ignore" });
|
|
6
|
+
return result.status === 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isGeminiCliReady(): boolean {
|
|
10
|
+
return isCommandInPath("gemini");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isAntigravityCliReady(): boolean {
|
|
14
|
+
return isCommandInPath("agy");
|
|
15
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import type {
|
|
3
|
+
Api,
|
|
4
|
+
AssistantMessage,
|
|
5
|
+
AssistantMessageEventStream,
|
|
6
|
+
Context,
|
|
7
|
+
Message,
|
|
8
|
+
Model,
|
|
9
|
+
SimpleStreamOptions,
|
|
10
|
+
TextContent,
|
|
11
|
+
} from "@gsd/pi-ai";
|
|
12
|
+
import { createAssistantMessageEventStream } from "@gsd/pi-ai";
|
|
13
|
+
|
|
14
|
+
const ZERO_USAGE = {
|
|
15
|
+
input: 0,
|
|
16
|
+
output: 0,
|
|
17
|
+
cacheRead: 0,
|
|
18
|
+
cacheWrite: 0,
|
|
19
|
+
totalTokens: 0,
|
|
20
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type GoogleCliProviderId = "google-gemini-cli" | "google-antigravity";
|
|
24
|
+
|
|
25
|
+
interface CliRunResult {
|
|
26
|
+
stdout: string;
|
|
27
|
+
stderr: string;
|
|
28
|
+
code: number | null;
|
|
29
|
+
signal: NodeJS.Signals | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function textBlocks(content: (TextContent | { type: string })[]): string {
|
|
33
|
+
return content
|
|
34
|
+
.map((block) => block.type === "text" ? (block as TextContent).text : `[${block.type} omitted]`)
|
|
35
|
+
.join("\n");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function messageToText(message: Message): string {
|
|
39
|
+
if (message.role === "user") {
|
|
40
|
+
const content = typeof message.content === "string" ? message.content : textBlocks(message.content);
|
|
41
|
+
return `User:\n${content}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (message.role === "assistant") {
|
|
45
|
+
const text = message.content
|
|
46
|
+
.map((block) => {
|
|
47
|
+
if (block.type === "text") return block.text;
|
|
48
|
+
if (block.type === "thinking") return `[thinking omitted]`;
|
|
49
|
+
if (block.type === "toolCall") return `[tool call: ${block.name}]`;
|
|
50
|
+
if (block.type === "serverToolUse") return `[server tool: ${block.name}]`;
|
|
51
|
+
if (block.type === "webSearchResult") return `[web search result omitted]`;
|
|
52
|
+
return `[${(block as { type: string }).type} omitted]`;
|
|
53
|
+
})
|
|
54
|
+
.join("\n");
|
|
55
|
+
return `Assistant:\n${text}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `Tool result (${message.toolName}):\n${textBlocks(message.content)}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function buildGoogleCliPrompt(context: Context): string {
|
|
62
|
+
const parts: string[] = [];
|
|
63
|
+
|
|
64
|
+
if (context.systemPrompt?.trim()) {
|
|
65
|
+
parts.push(`System instructions:\n${context.systemPrompt.trim()}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (context.messages.length > 0) {
|
|
69
|
+
parts.push(context.messages.map(messageToText).join("\n\n"));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (context.tools?.length) {
|
|
73
|
+
const names = context.tools.map((tool) => tool.name).join(", ");
|
|
74
|
+
parts.push(
|
|
75
|
+
`Available local GSD tools were not forwarded to this external CLI bridge. ` +
|
|
76
|
+
`If you need to act, use the CLI's own tools or ask the user to switch to a provider with native tool-call support. ` +
|
|
77
|
+
`Requested GSD tools: ${names}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return parts.join("\n\n").trim();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildAssistantMessage(
|
|
85
|
+
model: Model<Api>,
|
|
86
|
+
text: string,
|
|
87
|
+
stopReason: AssistantMessage["stopReason"] = "stop",
|
|
88
|
+
errorMessage?: string,
|
|
89
|
+
): AssistantMessage {
|
|
90
|
+
return {
|
|
91
|
+
role: "assistant",
|
|
92
|
+
content: text ? [{ type: "text", text }] : [],
|
|
93
|
+
api: model.api,
|
|
94
|
+
provider: model.provider,
|
|
95
|
+
model: model.id,
|
|
96
|
+
usage: { ...ZERO_USAGE, cost: { ...ZERO_USAGE.cost } },
|
|
97
|
+
stopReason,
|
|
98
|
+
...(errorMessage ? { errorMessage } : {}),
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function extractGeminiJsonResponse(raw: string): string {
|
|
104
|
+
const trimmed = raw.trim();
|
|
105
|
+
if (!trimmed) return "";
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(trimmed) as Record<string, unknown>;
|
|
109
|
+
for (const key of ["response", "text", "content", "message"]) {
|
|
110
|
+
const value = parsed[key];
|
|
111
|
+
if (typeof value === "string") return value;
|
|
112
|
+
}
|
|
113
|
+
return JSON.stringify(parsed, null, 2);
|
|
114
|
+
} catch {
|
|
115
|
+
return trimmed;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function commandForProvider(provider: GoogleCliProviderId): string {
|
|
120
|
+
return provider === "google-gemini-cli" ? "gemini" : "agy";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function argsForProvider(provider: GoogleCliProviderId, model: Model<Api>, prompt: string): string[] {
|
|
124
|
+
if (provider === "google-gemini-cli") {
|
|
125
|
+
const args = ["-p", prompt, "--output-format", "json"];
|
|
126
|
+
if (model.id !== "default") args.unshift("-m", model.id);
|
|
127
|
+
return args;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const args = ["-p", prompt];
|
|
131
|
+
if (model.id !== "default") args.unshift("-m", model.id);
|
|
132
|
+
return args;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function buildGoogleCliSpawnInvocation(
|
|
136
|
+
command: string,
|
|
137
|
+
args: string[],
|
|
138
|
+
platform: NodeJS.Platform = process.platform,
|
|
139
|
+
): { command: string; args: string[] } {
|
|
140
|
+
if (platform === "win32") {
|
|
141
|
+
return { command: "cmd", args: ["/c", command, ...args] };
|
|
142
|
+
}
|
|
143
|
+
return { command, args };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function runCli(command: string, args: string[], options?: SimpleStreamOptions): Promise<CliRunResult> {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const invocation = buildGoogleCliSpawnInvocation(command, args);
|
|
149
|
+
const child = spawn(invocation.command, invocation.args, {
|
|
150
|
+
cwd: options?.cwd || process.cwd(),
|
|
151
|
+
env: process.env,
|
|
152
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
let stdout = "";
|
|
156
|
+
let stderr = "";
|
|
157
|
+
let settled = false;
|
|
158
|
+
|
|
159
|
+
const settle = (fn: () => void) => {
|
|
160
|
+
if (settled) return;
|
|
161
|
+
settled = true;
|
|
162
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
163
|
+
fn();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const onAbort = () => {
|
|
167
|
+
child.kill("SIGTERM");
|
|
168
|
+
settle(() => reject(new Error("Request was aborted")));
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
if (options?.signal?.aborted) {
|
|
172
|
+
onAbort();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
options?.signal?.addEventListener("abort", onAbort);
|
|
176
|
+
|
|
177
|
+
child.stdout.setEncoding("utf8");
|
|
178
|
+
child.stderr.setEncoding("utf8");
|
|
179
|
+
child.stdout.on("data", (chunk) => {
|
|
180
|
+
stdout += chunk;
|
|
181
|
+
});
|
|
182
|
+
child.stderr.on("data", (chunk) => {
|
|
183
|
+
stderr += chunk;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
child.on("error", (error) => {
|
|
187
|
+
settle(() => reject(error));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
child.on("close", (code, signal) => {
|
|
191
|
+
settle(() => resolve({ stdout, stderr, code, signal }));
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function emitText(stream: AssistantMessageEventStream, message: AssistantMessage, text: string): void {
|
|
197
|
+
stream.push({ type: "start", partial: { ...message, content: [] } });
|
|
198
|
+
if (text) {
|
|
199
|
+
stream.push({ type: "text_start", contentIndex: 0, partial: message });
|
|
200
|
+
stream.push({ type: "text_delta", contentIndex: 0, delta: text, partial: message });
|
|
201
|
+
stream.push({ type: "text_end", contentIndex: 0, content: text, partial: message });
|
|
202
|
+
}
|
|
203
|
+
stream.push({ type: "done", reason: "stop", message });
|
|
204
|
+
stream.end(message);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function emitError(stream: AssistantMessageEventStream, model: Model<Api>, error: unknown): void {
|
|
208
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
209
|
+
const output = buildAssistantMessage(model, "", "error", message);
|
|
210
|
+
stream.push({ type: "error", reason: "error", error: output });
|
|
211
|
+
stream.end(output);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function streamViaGoogleCli(
|
|
215
|
+
model: Model<Api>,
|
|
216
|
+
context: Context,
|
|
217
|
+
options?: SimpleStreamOptions,
|
|
218
|
+
): AssistantMessageEventStream {
|
|
219
|
+
const stream = createAssistantMessageEventStream();
|
|
220
|
+
const provider = model.provider as GoogleCliProviderId;
|
|
221
|
+
|
|
222
|
+
queueMicrotask(async () => {
|
|
223
|
+
try {
|
|
224
|
+
const prompt = buildGoogleCliPrompt(context);
|
|
225
|
+
const command = commandForProvider(provider);
|
|
226
|
+
const args = argsForProvider(provider, model, prompt);
|
|
227
|
+
const result = await runCli(command, args, options);
|
|
228
|
+
|
|
229
|
+
if (result.code !== 0) {
|
|
230
|
+
const detail = (result.stderr || result.stdout || `CLI exited with code ${result.code}`).trim();
|
|
231
|
+
throw new Error(detail);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const text = provider === "google-gemini-cli"
|
|
235
|
+
? extractGeminiJsonResponse(result.stdout)
|
|
236
|
+
: result.stdout.trim();
|
|
237
|
+
const message = buildAssistantMessage(model, text);
|
|
238
|
+
emitText(stream, message, text);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
emitError(stream, model, error);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
return stream;
|
|
245
|
+
}
|
|
@@ -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();
|