gsd-pi 2.37.1 → 2.38.0-dev.63ad7e5

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 (201) hide show
  1. package/README.md +1 -1
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/onboarding.js +1 -0
  8. package/dist/remote-questions-config.js +2 -2
  9. package/dist/resources/extensions/browser-tools/package.json +3 -1
  10. package/dist/resources/extensions/cmux/index.js +55 -1
  11. package/dist/resources/extensions/context7/package.json +1 -1
  12. package/dist/resources/extensions/env-utils.js +29 -0
  13. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  14. package/dist/resources/extensions/google-search/package.json +3 -1
  15. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +74 -9
  17. package/dist/resources/extensions/gsd/auto-loop.js +68 -97
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +87 -69
  19. package/dist/resources/extensions/gsd/auto-prompts.js +98 -33
  20. package/dist/resources/extensions/gsd/auto-recovery.js +37 -1
  21. package/dist/resources/extensions/gsd/auto-start.js +13 -2
  22. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  23. package/dist/resources/extensions/gsd/auto.js +143 -96
  24. package/dist/resources/extensions/gsd/captures.js +9 -1
  25. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  26. package/dist/resources/extensions/gsd/commands-handlers.js +16 -3
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  28. package/dist/resources/extensions/gsd/commands.js +22 -2
  29. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  30. package/dist/resources/extensions/gsd/detection.js +1 -2
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  32. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  33. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  34. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  35. package/dist/resources/extensions/gsd/doctor-providers.js +62 -12
  36. package/dist/resources/extensions/gsd/doctor.js +184 -11
  37. package/dist/resources/extensions/gsd/export.js +1 -1
  38. package/dist/resources/extensions/gsd/files.js +43 -2
  39. package/dist/resources/extensions/gsd/forensics.js +1 -1
  40. package/dist/resources/extensions/gsd/index.js +2 -1
  41. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  42. package/dist/resources/extensions/gsd/observability-validator.js +24 -0
  43. package/dist/resources/extensions/gsd/package.json +1 -1
  44. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  45. package/dist/resources/extensions/gsd/preferences-types.js +2 -2
  46. package/dist/resources/extensions/gsd/preferences-validation.js +43 -11
  47. package/dist/resources/extensions/gsd/preferences.js +5 -5
  48. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  49. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -2
  50. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  51. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  52. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  53. package/dist/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  54. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  55. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  56. package/dist/resources/extensions/gsd/prompts/run-uat.md +25 -10
  57. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  58. package/dist/resources/extensions/gsd/reactive-graph.js +227 -0
  59. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  60. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  61. package/dist/resources/extensions/gsd/state.js +1 -1
  62. package/dist/resources/extensions/gsd/templates/task-plan.md +11 -3
  63. package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
  64. package/dist/resources/extensions/gsd/worktree.js +35 -16
  65. package/dist/resources/extensions/remote-questions/status.js +2 -1
  66. package/dist/resources/extensions/remote-questions/store.js +2 -1
  67. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  68. package/dist/resources/extensions/subagent/index.js +12 -3
  69. package/dist/resources/extensions/subagent/isolation.js +2 -1
  70. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  71. package/dist/resources/extensions/universal-config/package.json +1 -1
  72. package/dist/welcome-screen.d.ts +12 -0
  73. package/dist/welcome-screen.js +53 -0
  74. package/package.json +2 -1
  75. package/packages/pi-ai/dist/env-api-keys.js +13 -0
  76. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  77. package/packages/pi-ai/dist/models.generated.d.ts +172 -0
  78. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  79. package/packages/pi-ai/dist/models.generated.js +172 -0
  80. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  81. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +64 -0
  82. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -0
  83. package/packages/pi-ai/dist/providers/anthropic-shared.js +668 -0
  84. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -0
  85. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts +5 -0
  86. package/packages/pi-ai/dist/providers/anthropic-vertex.d.ts.map +1 -0
  87. package/packages/pi-ai/dist/providers/anthropic-vertex.js +85 -0
  88. package/packages/pi-ai/dist/providers/anthropic-vertex.js.map +1 -0
  89. package/packages/pi-ai/dist/providers/anthropic.d.ts +4 -30
  90. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  91. package/packages/pi-ai/dist/providers/anthropic.js +47 -764
  92. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  93. package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
  94. package/packages/pi-ai/dist/providers/register-builtins.js +6 -0
  95. package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
  96. package/packages/pi-ai/dist/types.d.ts +2 -2
  97. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  98. package/packages/pi-ai/dist/types.js.map +1 -1
  99. package/packages/pi-ai/package.json +1 -0
  100. package/packages/pi-ai/src/env-api-keys.ts +14 -0
  101. package/packages/pi-ai/src/models.generated.ts +172 -0
  102. package/packages/pi-ai/src/providers/anthropic-shared.ts +761 -0
  103. package/packages/pi-ai/src/providers/anthropic-vertex.ts +130 -0
  104. package/packages/pi-ai/src/providers/anthropic.ts +76 -868
  105. package/packages/pi-ai/src/providers/register-builtins.ts +7 -0
  106. package/packages/pi-ai/src/types.ts +2 -0
  107. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  109. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  110. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  112. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  113. package/packages/pi-coding-agent/package.json +1 -1
  114. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  115. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  116. package/pkg/package.json +1 -1
  117. package/src/resources/extensions/cmux/index.ts +57 -1
  118. package/src/resources/extensions/env-utils.ts +31 -0
  119. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  120. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  121. package/src/resources/extensions/gsd/auto-dispatch.ts +99 -8
  122. package/src/resources/extensions/gsd/auto-loop.ts +88 -133
  123. package/src/resources/extensions/gsd/auto-post-unit.ts +64 -40
  124. package/src/resources/extensions/gsd/auto-prompts.ts +132 -36
  125. package/src/resources/extensions/gsd/auto-recovery.ts +42 -0
  126. package/src/resources/extensions/gsd/auto-start.ts +18 -2
  127. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  128. package/src/resources/extensions/gsd/auto.ts +139 -101
  129. package/src/resources/extensions/gsd/captures.ts +10 -1
  130. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  131. package/src/resources/extensions/gsd/commands-handlers.ts +17 -2
  132. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  133. package/src/resources/extensions/gsd/commands.ts +24 -2
  134. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  135. package/src/resources/extensions/gsd/detection.ts +2 -2
  136. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  137. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  138. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  139. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  140. package/src/resources/extensions/gsd/doctor-providers.ts +64 -10
  141. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  142. package/src/resources/extensions/gsd/doctor.ts +177 -13
  143. package/src/resources/extensions/gsd/export.ts +1 -1
  144. package/src/resources/extensions/gsd/files.ts +47 -2
  145. package/src/resources/extensions/gsd/forensics.ts +1 -1
  146. package/src/resources/extensions/gsd/index.ts +3 -1
  147. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  148. package/src/resources/extensions/gsd/observability-validator.ts +27 -0
  149. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  150. package/src/resources/extensions/gsd/preferences-types.ts +5 -5
  151. package/src/resources/extensions/gsd/preferences-validation.ts +42 -11
  152. package/src/resources/extensions/gsd/preferences.ts +5 -5
  153. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  154. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -2
  155. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  156. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  157. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  158. package/src/resources/extensions/gsd/prompts/plan-slice.md +2 -1
  159. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  160. package/src/resources/extensions/gsd/prompts/reactive-execute.md +44 -0
  161. package/src/resources/extensions/gsd/prompts/run-uat.md +25 -10
  162. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  163. package/src/resources/extensions/gsd/reactive-graph.ts +289 -0
  164. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  165. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  166. package/src/resources/extensions/gsd/state.ts +1 -1
  167. package/src/resources/extensions/gsd/templates/task-plan.md +11 -3
  168. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  169. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +11 -31
  170. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  171. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  172. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +191 -3
  173. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +111 -0
  174. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  175. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  176. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +511 -0
  177. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +299 -0
  178. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  179. package/src/resources/extensions/gsd/tests/run-uat.test.ts +11 -3
  180. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  181. package/src/resources/extensions/gsd/types.ts +43 -1
  182. package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
  183. package/src/resources/extensions/gsd/worktree.ts +35 -15
  184. package/src/resources/extensions/remote-questions/status.ts +3 -1
  185. package/src/resources/extensions/remote-questions/store.ts +3 -1
  186. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  187. package/src/resources/extensions/subagent/index.ts +12 -3
  188. package/src/resources/extensions/subagent/isolation.ts +3 -1
  189. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  190. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  191. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  192. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  193. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  194. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  195. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  196. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  197. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  198. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  199. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  200. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  201. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -10,6 +10,7 @@ import { execFileSync } from "node:child_process";
10
10
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  import { join, resolve } from "node:path";
13
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
13
14
  // ─── Repo Identity ──────────────────────────────────────────────────────────
14
15
  /**
15
16
  * Get the git remote URL for "origin", or "" if no remote is configured.
@@ -84,14 +85,30 @@ function resolveGitRoot(basePath) {
84
85
  return resolve(basePath);
85
86
  }
86
87
  }
88
+ /**
89
+ * Validate a GSD_PROJECT_ID value.
90
+ *
91
+ * Must contain only alphanumeric characters, hyphens, and underscores.
92
+ * Call this once at startup so the user gets immediate feedback on bad values.
93
+ */
94
+ export function validateProjectId(id) {
95
+ return /^[a-zA-Z0-9_-]+$/.test(id);
96
+ }
87
97
  /**
88
98
  * Compute a stable identity for a repository.
89
99
  *
90
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
91
- * Deterministic: same repo always produces the same hash regardless of
92
- * which worktree the caller is inside.
100
+ * If `GSD_PROJECT_ID` is set, returns it directly (validation is expected
101
+ * to have already happened at startup via `validateProjectId`).
102
+ *
103
+ * Otherwise returns SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated
104
+ * to 12 hex chars. Deterministic: same repo always produces the same hash
105
+ * regardless of which worktree the caller is inside.
93
106
  */
94
107
  export function repoIdentity(basePath) {
108
+ const projectId = process.env.GSD_PROJECT_ID;
109
+ if (projectId) {
110
+ return projectId;
111
+ }
95
112
  const remoteUrl = getRemoteUrl(basePath);
96
113
  const root = resolveGitRoot(basePath);
97
114
  const input = `${remoteUrl}\n${root}`;
@@ -105,7 +122,7 @@ export function repoIdentity(basePath) {
105
122
  * otherwise `~/.gsd/projects/<hash>`.
106
123
  */
107
124
  export function externalGsdRoot(basePath) {
108
- const base = process.env.GSD_STATE_DIR || join(homedir(), ".gsd");
125
+ const base = process.env.GSD_STATE_DIR || gsdHome;
109
126
  return join(base, "projects", repoIdentity(basePath));
110
127
  }
111
128
  // ─── Symlink Management ─────────────────────────────────────────────────────
@@ -9,6 +9,7 @@ import { loadJsonFileOrNull } from "./json-persistence.js";
9
9
  import { join } from "node:path";
10
10
  import { homedir } from "node:os";
11
11
  import { resolveProjectRoot } from "./worktree.js";
12
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
12
13
  // ─── Resource Staleness ───────────────────────────────────────────────────
13
14
  /**
14
15
  * Read the resource version (semver) from the managed-resources manifest.
@@ -19,7 +20,7 @@ function isManifestWithVersion(data) {
19
20
  return data !== null && typeof data === "object" && "gsdVersion" in data && typeof data.gsdVersion === "string";
20
21
  }
21
22
  export function readResourceVersion() {
22
- const agentDir = process.env.GSD_CODING_AGENT_DIR || join(homedir(), ".gsd", "agent");
23
+ const agentDir = process.env.GSD_CODING_AGENT_DIR || join(gsdHome, "agent");
23
24
  const manifestPath = join(agentDir, "managed-resources.json");
24
25
  const manifest = loadJsonFileOrNull(manifestPath, isManifestWithVersion);
25
26
  return manifest?.gsdVersion ?? null;
@@ -3,7 +3,7 @@
3
3
  // Pure TypeScript, zero Pi dependencies.
4
4
  import { parseRoadmap, parsePlan, parseSummary, loadFile, parseRequirementCounts, parseContextDependsOn, } from './files.js';
5
5
  import { resolveMilestoneFile, resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTasksDir, resolveGsdRootFile, gsdRoot, } from './paths.js';
6
- import { findMilestoneIds } from './guided-flow.js';
6
+ import { findMilestoneIds } from './milestone-ids.js';
7
7
  import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
8
8
  import { join, resolve } from 'path';
9
9
  import { existsSync, readdirSync } from 'node:fs';
@@ -42,11 +42,19 @@ estimated_files: {{estimatedFiles}}
42
42
 
43
43
  ## Inputs
44
44
 
45
+ <!-- Every input MUST be a backtick-wrapped file path. These paths are machine-parsed to
46
+ derive task dependencies — vague descriptions without paths break dependency detection.
47
+ For the first task in a slice with no prior task outputs, list the existing source files
48
+ this task reads or modifies. -->
49
+
45
50
  - `{{filePath}}` — {{whatThisTaskNeedsFromPriorWork}}
46
- - {{priorTaskSummaryInsight}}
47
51
 
48
52
  ## Expected Output
49
53
 
50
- <!-- This task should produce a real increment toward making the slice goal/demo true. A full slice plan should not be able to mark every task complete while the claimed slice behavior still does not work at the stated proof level. -->
54
+ <!-- Every output MUST be a backtick-wrapped file path the specific files this task creates
55
+ or modifies. These paths are machine-parsed to derive task dependencies.
56
+ This task should produce a real increment toward making the slice goal/demo true. A full
57
+ slice plan should not be able to mark every task complete while the claimed slice behavior
58
+ still does not work at the stated proof level. -->
51
59
 
52
- - `{{filePath}}` — {{whatThisTaskShouldProduceOrModify}}
60
+ - `{{filePath}}` — {{whatThisTaskCreatesOrModifies}}
@@ -2,7 +2,7 @@
2
2
  import { existsSync, readFileSync, statSync } from 'node:fs';
3
3
  import { deriveState } from './state.js';
4
4
  import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
5
- import { findMilestoneIds } from './guided-flow.js';
5
+ import { findMilestoneIds } from './milestone-ids.js';
6
6
  import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js';
7
7
  import { getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice, aggregateByModel, aggregateByTier, formatTierSavings, loadLedgerFromDisk, } from './metrics.js';
8
8
  import { loadAllCaptures, countPendingCaptures } from './captures.js';
@@ -57,42 +57,61 @@ export function captureIntegrationBranch(basePath, milestoneId, options) {
57
57
  writeIntegrationBranch(basePath, milestoneId, current, options);
58
58
  }
59
59
  // ─── Pure Utility Functions (unchanged) ────────────────────────────────────
60
+ /**
61
+ * Find the worktrees segment in a path, supporting both direct
62
+ * (`/.gsd/worktrees/`) and symlink-resolved (`/.gsd/projects/<hash>/worktrees/`)
63
+ * layouts. When `.gsd` is a symlink to `~/.gsd/projects/<hash>`, resolved
64
+ * paths contain the intermediate `projects/<hash>/` segment that the old
65
+ * single-marker check missed.
66
+ */
67
+ function findWorktreeSegment(normalizedPath) {
68
+ // Direct layout: /.gsd/worktrees/<name>
69
+ const directMarker = "/.gsd/worktrees/";
70
+ const idx = normalizedPath.indexOf(directMarker);
71
+ if (idx !== -1) {
72
+ return { gsdIdx: idx, afterWorktrees: idx + directMarker.length };
73
+ }
74
+ // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/<name>
75
+ const symlinkRe = /\/\.gsd\/projects\/[a-f0-9]+\/worktrees\//;
76
+ const match = normalizedPath.match(symlinkRe);
77
+ if (match && match.index !== undefined) {
78
+ return { gsdIdx: match.index, afterWorktrees: match.index + match[0].length };
79
+ }
80
+ return null;
81
+ }
60
82
  /**
61
83
  * Detect the active worktree name from the current working directory.
62
84
  * Returns null if not inside a GSD worktree (.gsd/worktrees/<name>/).
63
85
  */
64
86
  export function detectWorktreeName(basePath) {
65
87
  const normalizedPath = basePath.replaceAll("\\", "/");
66
- const marker = "/.gsd/worktrees/";
67
- const idx = normalizedPath.indexOf(marker);
68
- if (idx === -1)
88
+ const seg = findWorktreeSegment(normalizedPath);
89
+ if (!seg)
69
90
  return null;
70
- const afterMarker = normalizedPath.slice(idx + marker.length);
91
+ const afterMarker = normalizedPath.slice(seg.afterWorktrees);
71
92
  const name = afterMarker.split("/")[0];
72
93
  return name || null;
73
94
  }
74
95
  /**
75
96
  * Resolve the project root from a path that may be inside a worktree.
76
- * If the path contains `/.gsd/worktrees/<name>/`, returns the portion
77
- * before `/.gsd/`. Otherwise returns the input unchanged.
97
+ * If the path contains a worktrees segment, returns the portion before
98
+ * `/.gsd/`. Otherwise returns the input unchanged.
78
99
  *
79
100
  * Use this in commands that call `process.cwd()` to ensure they always
80
101
  * operate against the real project root, not a worktree subdirectory.
81
102
  */
82
103
  export function resolveProjectRoot(basePath) {
83
104
  const normalizedPath = basePath.replaceAll("\\", "/");
84
- const marker = "/.gsd/worktrees/";
85
- const idx = normalizedPath.indexOf(marker);
86
- if (idx === -1)
105
+ const seg = findWorktreeSegment(normalizedPath);
106
+ if (!seg)
87
107
  return basePath;
88
- // Return the original path up to the .gsd/ marker (un-normalized)
89
- // Account for potential OS-specific separators
108
+ // Return the original path up to the /.gsd/ boundary
90
109
  const sep = basePath.includes("\\") ? "\\" : "/";
91
- const markerOs = `${sep}.gsd${sep}worktrees${sep}`;
92
- const idxOs = basePath.indexOf(markerOs);
93
- if (idxOs !== -1)
94
- return basePath.slice(0, idxOs);
95
- return basePath.slice(0, idx);
110
+ const gsdMarker = `${sep}.gsd${sep}`;
111
+ const gsdIdx = basePath.indexOf(gsdMarker);
112
+ if (gsdIdx !== -1)
113
+ return basePath.slice(0, gsdIdx);
114
+ return basePath.slice(0, seg.gsdIdx);
96
115
  }
97
116
  /**
98
117
  * Get the slice branch name, namespaced by worktree when inside one.
@@ -5,8 +5,9 @@ import { existsSync, readdirSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
7
7
  import { readPromptRecord } from "./store.js";
8
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
8
9
  export function getLatestPromptSummary() {
9
- const runtimeDir = join(homedir(), ".gsd", "runtime", "remote-questions");
10
+ const runtimeDir = join(gsdHome, "runtime", "remote-questions");
10
11
  if (!existsSync(runtimeDir))
11
12
  return null;
12
13
  const files = readdirSync(runtimeDir).filter((f) => f.endsWith(".json"));
@@ -4,8 +4,9 @@
4
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
  import { homedir } from "node:os";
7
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
7
8
  function runtimeDir() {
8
- return join(homedir(), ".gsd", "runtime", "remote-questions");
9
+ return join(gsdHome, "runtime", "remote-questions");
9
10
  }
10
11
  function recordPath(id) {
11
12
  return join(runtimeDir(), `${id}.json`);
@@ -15,7 +15,8 @@ import { resolveSearchProviderFromPreferences } from '../gsd/preferences.js';
15
15
  // Compute authFilePath locally instead of importing from app-paths.ts,
16
16
  // because extensions are copied to ~/.gsd/agent/extensions/ at runtime
17
17
  // where the relative import '../../../app-paths.ts' doesn't resolve.
18
- const authFilePath = join(homedir(), '.gsd', 'agent', 'auth.json');
18
+ const gsdHome = process.env.GSD_HOME || join(homedir(), '.gsd');
19
+ const authFilePath = join(gsdHome, 'agent', 'auth.json');
19
20
  const VALID_PREFERENCES = new Set(['tavily', 'brave', 'ollama', 'auto']);
20
21
  const PREFERENCE_KEY = 'search_provider';
21
22
  /** Returns the Tavily API key from the environment, or empty string if not set. */
@@ -360,7 +360,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
360
360
  }
361
361
  }
362
362
  }
363
- async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
363
+ async function runSingleAgentInCmuxSplit(cmuxClient, directionOrSurfaceId, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
364
364
  const agent = agents.find((a) => a.name === agentName);
365
365
  if (!agent) {
366
366
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
@@ -397,7 +397,12 @@ async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agen
397
397
  const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
398
398
  const stderrPath = path.join(tmpOutputDir, "stderr.log");
399
399
  const exitPath = path.join(tmpOutputDir, "exit.code");
400
- const cmuxSurfaceId = await cmuxClient.createSplit(direction);
400
+ // Accept either a pre-created surface ID or a direction to create a new split
401
+ const isDirection = directionOrSurfaceId === "right" || directionOrSurfaceId === "down"
402
+ || directionOrSurfaceId === "left" || directionOrSurfaceId === "up";
403
+ const cmuxSurfaceId = isDirection
404
+ ? await cmuxClient.createSplit(directionOrSurfaceId)
405
+ : directionOrSurfaceId;
401
406
  if (!cmuxSurfaceId) {
402
407
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
403
408
  }
@@ -656,10 +661,14 @@ export default function (pi) {
656
661
  const MAX_RETRIES = 1; // Retry failed tasks once
657
662
  const batchId = crypto.randomUUID();
658
663
  const batchSize = params.tasks.length;
664
+ // Pre-create a grid layout for cmux splits so agents get a clean tiled arrangement
665
+ const gridSurfaces = cmuxSplitsEnabled
666
+ ? await cmuxClient.createGridLayout(Math.min(batchSize, MAX_CONCURRENCY))
667
+ : [];
659
668
  const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
660
669
  const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
661
670
  const runTask = () => cmuxSplitsEnabled
662
- ? runSingleAgentInCmuxSplit(cmuxClient, index % 2 === 0 ? "right" : "down", ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
671
+ ? runSingleAgentInCmuxSplit(cmuxClient, gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"), ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
663
672
  if (partial.details?.results[0]) {
664
673
  allResults[index] = partial.details.results[0];
665
674
  emitParallelUpdate();
@@ -17,8 +17,9 @@ const execFile = promisify(execFileCb);
17
17
  function encodeCwd(cwd) {
18
18
  return cwd.replace(/\//g, "--");
19
19
  }
20
+ const gsdHome = process.env.GSD_HOME || path.join(os.homedir(), ".gsd");
20
21
  function getIsolationBaseDir(cwd, taskId) {
21
- return path.join(os.homedir(), ".gsd", "wt", encodeCwd(cwd), taskId);
22
+ return path.join(gsdHome, "wt", encodeCwd(cwd), taskId);
22
23
  }
23
24
  // Track active isolation dirs for cleanup on exit
24
25
  const activeIsolations = new Set();
@@ -8,6 +8,7 @@
8
8
  import { readdirSync, readFileSync, existsSync } from "node:fs";
9
9
  import { join, basename } from "node:path";
10
10
  import { homedir } from "node:os";
11
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
11
12
  import { splitFrontmatter, parseFrontmatterMap } from "../shared/frontmatter.js";
12
13
  function parseRuleFile(filePath) {
13
14
  let content;
@@ -56,7 +57,7 @@ function scanDir(dir) {
56
57
  * Project rules override global rules with the same name.
57
58
  */
58
59
  export function loadRules(cwd) {
59
- const globalDir = join(homedir(), ".gsd", "agent", "rules");
60
+ const globalDir = join(gsdHome, "agent", "rules");
60
61
  const projectDir = join(cwd, ".gsd", "rules");
61
62
  const globalRules = scanDir(globalDir);
62
63
  const projectRules = scanDir(projectDir);
@@ -5,7 +5,7 @@
5
5
  "type": "module",
6
6
  "pi": {
7
7
  "extensions": [
8
- "./index.ts"
8
+ "./index.js"
9
9
  ]
10
10
  }
11
11
  }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * GSD Welcome Screen
3
+ *
4
+ * Rendered to stderr before the TUI takes over.
5
+ * No box, no panels — logo with metadata alongside, dim hint below.
6
+ */
7
+ export interface WelcomeScreenOptions {
8
+ version: string;
9
+ modelName?: string;
10
+ provider?: string;
11
+ }
12
+ export declare function printWelcomeScreen(opts: WelcomeScreenOptions): void;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * GSD Welcome Screen
3
+ *
4
+ * Rendered to stderr before the TUI takes over.
5
+ * No box, no panels — logo with metadata alongside, dim hint below.
6
+ */
7
+ import os from 'node:os';
8
+ import chalk from 'chalk';
9
+ import { GSD_LOGO } from './logo.js';
10
+ function getShortCwd() {
11
+ const cwd = process.cwd();
12
+ const home = os.homedir();
13
+ return cwd.startsWith(home) ? '~' + cwd.slice(home.length) : cwd;
14
+ }
15
+ export function printWelcomeScreen(opts) {
16
+ if (!process.stderr.isTTY)
17
+ return;
18
+ const { version, modelName, provider } = opts;
19
+ const shortCwd = getShortCwd();
20
+ // Info lines to sit alongside the logo (one per logo row)
21
+ const modelLine = [modelName, provider].filter(Boolean).join(' · ');
22
+ const INFO = [
23
+ ` ${chalk.bold('Get Shit Done')} ${chalk.dim('v' + version)}`,
24
+ undefined,
25
+ modelLine ? ` ${chalk.dim(modelLine)}` : undefined,
26
+ ` ${chalk.dim(shortCwd)}`,
27
+ undefined,
28
+ undefined,
29
+ ];
30
+ const lines = [''];
31
+ for (let i = 0; i < GSD_LOGO.length; i++) {
32
+ lines.push(chalk.cyan(GSD_LOGO[i]) + (INFO[i] ?? ''));
33
+ }
34
+ // Tool status + hint — dim, aligned under the info text
35
+ const pad = ' '.repeat(28) + ' '; // aligns with the info text column
36
+ const toolParts = [];
37
+ if (process.env.BRAVE_API_KEY)
38
+ toolParts.push('Brave ✓');
39
+ if (process.env.BRAVE_ANSWERS_KEY)
40
+ toolParts.push('Answers ✓');
41
+ if (process.env.JINA_API_KEY)
42
+ toolParts.push('Jina ✓');
43
+ if (process.env.TAVILY_API_KEY)
44
+ toolParts.push('Tavily ✓');
45
+ if (process.env.CONTEXT7_API_KEY)
46
+ toolParts.push('Context7 ✓');
47
+ if (toolParts.length > 0) {
48
+ lines.push(chalk.dim(pad + ['Web search loaded', ...toolParts].join(' · ')));
49
+ }
50
+ lines.push(chalk.dim(pad + '/gsd to begin · /gsd help for all commands'));
51
+ lines.push('');
52
+ process.stderr.write(lines.join('\n') + '\n');
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.37.1",
3
+ "version": "2.38.0-dev.63ad7e5",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -84,6 +84,7 @@
84
84
  },
85
85
  "dependencies": {
86
86
  "@anthropic-ai/sdk": "^0.73.0",
87
+ "@anthropic-ai/vertex-sdk": "^0.14.4",
87
88
  "@aws-sdk/client-bedrock-runtime": "^3.983.0",
88
89
  "@clack/prompts": "^1.1.0",
89
90
  "@google/genai": "^1.40.0",
@@ -53,6 +53,19 @@ export function getEnvApiKey(provider) {
53
53
  if (provider === "anthropic") {
54
54
  return process.env.ANTHROPIC_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY;
55
55
  }
56
+ // Anthropic on Vertex AI uses Application Default Credentials.
57
+ // Detected via ANTHROPIC_VERTEX_PROJECT_ID (same env var as Claude Code).
58
+ if (provider === "anthropic-vertex") {
59
+ const hasProject = !!process.env.ANTHROPIC_VERTEX_PROJECT_ID;
60
+ if (hasProject) {
61
+ return "<authenticated>";
62
+ }
63
+ // Fall back to Google Cloud project env vars
64
+ const hasGoogleProject = !!(process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT);
65
+ if (hasGoogleProject && hasVertexAdcCredentials()) {
66
+ return "<authenticated>";
67
+ }
68
+ }
56
69
  // Vertex AI uses Application Default Credentials, not API keys.
57
70
  // Auth is configured via `gcloud auth application-default login`.
58
71
  if (provider === "google-vertex") {
@@ -1 +1 @@
1
- {"version":3,"file":"env-api-keys.js","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"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","sourcesContent":["// NEVER convert to top-level imports - breaks browser/Vite builds (web-ui)\nlet _existsSync: typeof import(\"node:fs\").existsSync | null = null;\nlet _homedir: typeof import(\"node:os\").homedir | null = null;\nlet _join: typeof import(\"node:path\").join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\n// Eagerly load in Node.js/Bun environment only\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as typeof import(\"node:fs\")).existsSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as typeof import(\"node:os\")).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as typeof import(\"node:path\")).join;\n\t});\n}\n\nimport type { KnownProvider } from \"./types.js\";\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check GOOGLE_APPLICATION_CREDENTIALS env var first (standard way)\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\t// Fall back to default ADC path (lazy evaluation)\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: any): string | undefined {\n\t// Fall back to environment variables\n\tif (provider === \"github-copilot\") {\n\t\treturn process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn process.env.ANTHROPIC_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY;\n\t}\n\n\t// Vertex AI uses Application Default Credentials, not API keys.\n\t// Auth is configured via `gcloud auth application-default login`.\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT);\n\t\tconst hasLocation = !!process.env.GOOGLE_CLOUD_LOCATION;\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\t// Amazon Bedrock supports multiple credential sources:\n\t\t// 1. AWS_PROFILE - named profile from ~/.aws/credentials\n\t\t// 2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY - standard IAM keys\n\t\t// 3. AWS_BEARER_TOKEN_BEDROCK - Bedrock API keys (bearer token)\n\t\t// 4. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI - ECS task roles\n\t\t// 5. AWS_CONTAINER_CREDENTIALS_FULL_URI - ECS task roles (full URI)\n\t\t// 6. AWS_WEB_IDENTITY_TOKEN_FILE - IRSA (IAM Roles for Service Accounts)\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"alibaba-coding-plan\": \"ALIBABA_API_KEY\",\n\t\t\"ollama-cloud\": \"OLLAMA_API_KEY\",\n\t\t\"custom-openai\": \"CUSTOM_OPENAI_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? process.env[envVar] : undefined;\n}\n"]}
1
+ {"version":3,"file":"env-api-keys.js","sourceRoot":"","sources":["../src/env-api-keys.ts"],"names":[],"mappings":"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","sourcesContent":["// NEVER convert to top-level imports - breaks browser/Vite builds (web-ui)\nlet _existsSync: typeof import(\"node:fs\").existsSync | null = null;\nlet _homedir: typeof import(\"node:os\").homedir | null = null;\nlet _join: typeof import(\"node:path\").join | null = null;\n\ntype DynamicImport = (specifier: string) => Promise<unknown>;\n\nconst dynamicImport: DynamicImport = (specifier) => import(specifier);\nconst NODE_FS_SPECIFIER = \"node:\" + \"fs\";\nconst NODE_OS_SPECIFIER = \"node:\" + \"os\";\nconst NODE_PATH_SPECIFIER = \"node:\" + \"path\";\n\n// Eagerly load in Node.js/Bun environment only\nif (typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun)) {\n\tdynamicImport(NODE_FS_SPECIFIER).then((m) => {\n\t\t_existsSync = (m as typeof import(\"node:fs\")).existsSync;\n\t});\n\tdynamicImport(NODE_OS_SPECIFIER).then((m) => {\n\t\t_homedir = (m as typeof import(\"node:os\")).homedir;\n\t});\n\tdynamicImport(NODE_PATH_SPECIFIER).then((m) => {\n\t\t_join = (m as typeof import(\"node:path\")).join;\n\t});\n}\n\nimport type { KnownProvider } from \"./types.js\";\n\nlet cachedVertexAdcCredentialsExists: boolean | null = null;\n\nfunction hasVertexAdcCredentials(): boolean {\n\tif (cachedVertexAdcCredentialsExists === null) {\n\t\t// If node modules haven't loaded yet (async import race at startup),\n\t\t// return false WITHOUT caching so the next call retries once they're ready.\n\t\t// Only cache false permanently in a browser environment where fs is never available.\n\t\tif (!_existsSync || !_homedir || !_join) {\n\t\t\tconst isNode = typeof process !== \"undefined\" && (process.versions?.node || process.versions?.bun);\n\t\t\tif (!isNode) {\n\t\t\t\t// Definitively in a browser — safe to cache false permanently\n\t\t\t\tcachedVertexAdcCredentialsExists = false;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check GOOGLE_APPLICATION_CREDENTIALS env var first (standard way)\n\t\tconst gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n\t\tif (gacPath) {\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(gacPath);\n\t\t} else {\n\t\t\t// Fall back to default ADC path (lazy evaluation)\n\t\t\tcachedVertexAdcCredentialsExists = _existsSync(\n\t\t\t\t_join(_homedir(), \".config\", \"gcloud\", \"application_default_credentials.json\"),\n\t\t\t);\n\t\t}\n\t}\n\treturn cachedVertexAdcCredentialsExists;\n}\n\n/**\n * Get API key for provider from known environment variables, e.g. OPENAI_API_KEY.\n *\n * Will not return API keys for providers that require OAuth tokens.\n */\nexport function getEnvApiKey(provider: KnownProvider): string | undefined;\nexport function getEnvApiKey(provider: string): string | undefined;\nexport function getEnvApiKey(provider: any): string | undefined {\n\t// Fall back to environment variables\n\tif (provider === \"github-copilot\") {\n\t\treturn process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN;\n\t}\n\n\t// ANTHROPIC_OAUTH_TOKEN takes precedence over ANTHROPIC_API_KEY\n\tif (provider === \"anthropic\") {\n\t\treturn process.env.ANTHROPIC_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY;\n\t}\n\n\t// Anthropic on Vertex AI uses Application Default Credentials.\n\t// Detected via ANTHROPIC_VERTEX_PROJECT_ID (same env var as Claude Code).\n\tif (provider === \"anthropic-vertex\") {\n\t\tconst hasProject = !!process.env.ANTHROPIC_VERTEX_PROJECT_ID;\n\t\tif (hasProject) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t\t// Fall back to Google Cloud project env vars\n\t\tconst hasGoogleProject = !!(process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT);\n\t\tif (hasGoogleProject && hasVertexAdcCredentials()) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\t// Vertex AI uses Application Default Credentials, not API keys.\n\t// Auth is configured via `gcloud auth application-default login`.\n\tif (provider === \"google-vertex\") {\n\t\tconst hasCredentials = hasVertexAdcCredentials();\n\t\tconst hasProject = !!(process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT);\n\t\tconst hasLocation = !!process.env.GOOGLE_CLOUD_LOCATION;\n\n\t\tif (hasCredentials && hasProject && hasLocation) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tif (provider === \"amazon-bedrock\") {\n\t\t// Amazon Bedrock supports multiple credential sources:\n\t\t// 1. AWS_PROFILE - named profile from ~/.aws/credentials\n\t\t// 2. AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY - standard IAM keys\n\t\t// 3. AWS_BEARER_TOKEN_BEDROCK - Bedrock API keys (bearer token)\n\t\t// 4. AWS_CONTAINER_CREDENTIALS_RELATIVE_URI - ECS task roles\n\t\t// 5. AWS_CONTAINER_CREDENTIALS_FULL_URI - ECS task roles (full URI)\n\t\t// 6. AWS_WEB_IDENTITY_TOKEN_FILE - IRSA (IAM Roles for Service Accounts)\n\t\tif (\n\t\t\tprocess.env.AWS_PROFILE ||\n\t\t\t(process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) ||\n\t\t\tprocess.env.AWS_BEARER_TOKEN_BEDROCK ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||\n\t\t\tprocess.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||\n\t\t\tprocess.env.AWS_WEB_IDENTITY_TOKEN_FILE\n\t\t) {\n\t\t\treturn \"<authenticated>\";\n\t\t}\n\t}\n\n\tconst envMap: Record<string, string> = {\n\t\topenai: \"OPENAI_API_KEY\",\n\t\t\"azure-openai-responses\": \"AZURE_OPENAI_API_KEY\",\n\t\tgoogle: \"GEMINI_API_KEY\",\n\t\tgroq: \"GROQ_API_KEY\",\n\t\tcerebras: \"CEREBRAS_API_KEY\",\n\t\txai: \"XAI_API_KEY\",\n\t\topenrouter: \"OPENROUTER_API_KEY\",\n\t\t\"vercel-ai-gateway\": \"AI_GATEWAY_API_KEY\",\n\t\tzai: \"ZAI_API_KEY\",\n\t\tmistral: \"MISTRAL_API_KEY\",\n\t\tminimax: \"MINIMAX_API_KEY\",\n\t\t\"minimax-cn\": \"MINIMAX_CN_API_KEY\",\n\t\thuggingface: \"HF_TOKEN\",\n\t\topencode: \"OPENCODE_API_KEY\",\n\t\t\"opencode-go\": \"OPENCODE_API_KEY\",\n\t\t\"kimi-coding\": \"KIMI_API_KEY\",\n\t\t\"alibaba-coding-plan\": \"ALIBABA_API_KEY\",\n\t\t\"ollama-cloud\": \"OLLAMA_API_KEY\",\n\t\t\"custom-openai\": \"CUSTOM_OPENAI_API_KEY\",\n\t};\n\n\tconst envVar = envMap[provider];\n\treturn envVar ? process.env[envVar] : undefined;\n}\n"]}
@@ -1822,6 +1822,178 @@ export declare const MODELS: {
1822
1822
  maxTokens: number;
1823
1823
  };
1824
1824
  };
1825
+ readonly "anthropic-vertex": {
1826
+ readonly "claude-opus-4-6": {
1827
+ id: string;
1828
+ name: string;
1829
+ api: "anthropic-vertex";
1830
+ provider: string;
1831
+ baseUrl: string;
1832
+ reasoning: true;
1833
+ input: ("text" | "image")[];
1834
+ cost: {
1835
+ input: number;
1836
+ output: number;
1837
+ cacheRead: number;
1838
+ cacheWrite: number;
1839
+ };
1840
+ contextWindow: number;
1841
+ maxTokens: number;
1842
+ };
1843
+ readonly "claude-opus-4-6[1m]": {
1844
+ id: string;
1845
+ name: string;
1846
+ api: "anthropic-vertex";
1847
+ provider: string;
1848
+ baseUrl: string;
1849
+ reasoning: true;
1850
+ input: ("text" | "image")[];
1851
+ cost: {
1852
+ input: number;
1853
+ output: number;
1854
+ cacheRead: number;
1855
+ cacheWrite: number;
1856
+ };
1857
+ contextWindow: number;
1858
+ maxTokens: number;
1859
+ };
1860
+ readonly "claude-sonnet-4-6": {
1861
+ id: string;
1862
+ name: string;
1863
+ api: "anthropic-vertex";
1864
+ provider: string;
1865
+ baseUrl: string;
1866
+ reasoning: true;
1867
+ input: ("text" | "image")[];
1868
+ cost: {
1869
+ input: number;
1870
+ output: number;
1871
+ cacheRead: number;
1872
+ cacheWrite: number;
1873
+ };
1874
+ contextWindow: number;
1875
+ maxTokens: number;
1876
+ };
1877
+ readonly "claude-sonnet-4-6[1m]": {
1878
+ id: string;
1879
+ name: string;
1880
+ api: "anthropic-vertex";
1881
+ provider: string;
1882
+ baseUrl: string;
1883
+ reasoning: true;
1884
+ input: ("text" | "image")[];
1885
+ cost: {
1886
+ input: number;
1887
+ output: number;
1888
+ cacheRead: number;
1889
+ cacheWrite: number;
1890
+ };
1891
+ contextWindow: number;
1892
+ maxTokens: number;
1893
+ };
1894
+ readonly "claude-sonnet-4-5@20250929": {
1895
+ id: string;
1896
+ name: string;
1897
+ api: "anthropic-vertex";
1898
+ provider: string;
1899
+ baseUrl: string;
1900
+ reasoning: true;
1901
+ input: ("text" | "image")[];
1902
+ cost: {
1903
+ input: number;
1904
+ output: number;
1905
+ cacheRead: number;
1906
+ cacheWrite: number;
1907
+ };
1908
+ contextWindow: number;
1909
+ maxTokens: number;
1910
+ };
1911
+ readonly "claude-sonnet-4@20250514": {
1912
+ id: string;
1913
+ name: string;
1914
+ api: "anthropic-vertex";
1915
+ provider: string;
1916
+ baseUrl: string;
1917
+ reasoning: true;
1918
+ input: ("text" | "image")[];
1919
+ cost: {
1920
+ input: number;
1921
+ output: number;
1922
+ cacheRead: number;
1923
+ cacheWrite: number;
1924
+ };
1925
+ contextWindow: number;
1926
+ maxTokens: number;
1927
+ };
1928
+ readonly "claude-opus-4-5@20251101": {
1929
+ id: string;
1930
+ name: string;
1931
+ api: "anthropic-vertex";
1932
+ provider: string;
1933
+ baseUrl: string;
1934
+ reasoning: true;
1935
+ input: ("text" | "image")[];
1936
+ cost: {
1937
+ input: number;
1938
+ output: number;
1939
+ cacheRead: number;
1940
+ cacheWrite: number;
1941
+ };
1942
+ contextWindow: number;
1943
+ maxTokens: number;
1944
+ };
1945
+ readonly "claude-opus-4-1@20250805": {
1946
+ id: string;
1947
+ name: string;
1948
+ api: "anthropic-vertex";
1949
+ provider: string;
1950
+ baseUrl: string;
1951
+ reasoning: true;
1952
+ input: ("text" | "image")[];
1953
+ cost: {
1954
+ input: number;
1955
+ output: number;
1956
+ cacheRead: number;
1957
+ cacheWrite: number;
1958
+ };
1959
+ contextWindow: number;
1960
+ maxTokens: number;
1961
+ };
1962
+ readonly "claude-opus-4@20250514": {
1963
+ id: string;
1964
+ name: string;
1965
+ api: "anthropic-vertex";
1966
+ provider: string;
1967
+ baseUrl: string;
1968
+ reasoning: true;
1969
+ input: ("text" | "image")[];
1970
+ cost: {
1971
+ input: number;
1972
+ output: number;
1973
+ cacheRead: number;
1974
+ cacheWrite: number;
1975
+ };
1976
+ contextWindow: number;
1977
+ maxTokens: number;
1978
+ };
1979
+ readonly "claude-haiku-4-5@20251001": {
1980
+ id: string;
1981
+ name: string;
1982
+ api: "anthropic-vertex";
1983
+ provider: string;
1984
+ baseUrl: string;
1985
+ reasoning: false;
1986
+ input: ("text" | "image")[];
1987
+ cost: {
1988
+ input: number;
1989
+ output: number;
1990
+ cacheRead: number;
1991
+ cacheWrite: number;
1992
+ };
1993
+ contextWindow: number;
1994
+ maxTokens: number;
1995
+ };
1996
+ };
1825
1997
  readonly "azure-openai-responses": {
1826
1998
  readonly "codex-mini-latest": {
1827
1999
  id: string;