gsd-pi 2.38.0-dev.bc2e21e → 2.38.0-dev.d533afb

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 (99) hide show
  1. package/dist/resource-loader.js +34 -1
  2. package/dist/resources/extensions/github-sync/cli.js +284 -0
  3. package/dist/resources/extensions/github-sync/index.js +73 -0
  4. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  5. package/dist/resources/extensions/github-sync/sync.js +424 -0
  6. package/dist/resources/extensions/github-sync/templates.js +118 -0
  7. package/dist/resources/extensions/github-sync/types.js +7 -0
  8. package/dist/resources/extensions/gsd/auto/session.js +3 -23
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
  10. package/dist/resources/extensions/gsd/auto-loop.js +292 -263
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +28 -3
  12. package/dist/resources/extensions/gsd/auto-prompts.js +23 -43
  13. package/dist/resources/extensions/gsd/auto-start.js +7 -1
  14. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  15. package/dist/resources/extensions/gsd/auto.js +143 -80
  16. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  17. package/dist/resources/extensions/gsd/commands.js +2 -1
  18. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  19. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  20. package/dist/resources/extensions/gsd/doctor-providers.js +27 -11
  21. package/dist/resources/extensions/gsd/doctor.js +20 -1
  22. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  23. package/dist/resources/extensions/gsd/files.js +4 -0
  24. package/dist/resources/extensions/gsd/git-service.js +15 -12
  25. package/dist/resources/extensions/gsd/guided-flow.js +82 -32
  26. package/dist/resources/extensions/gsd/index.js +22 -19
  27. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  28. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  29. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  30. package/dist/resources/extensions/gsd/preferences-validation.js +58 -10
  31. package/dist/resources/extensions/gsd/preferences.js +4 -2
  32. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -0
  33. package/dist/resources/extensions/gsd/repo-identity.js +19 -3
  34. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  35. package/dist/resources/extensions/mcp-client/index.js +14 -1
  36. package/package.json +1 -1
  37. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  38. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  39. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  40. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  41. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  42. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  43. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  44. package/src/resources/extensions/github-sync/cli.ts +364 -0
  45. package/src/resources/extensions/github-sync/index.ts +93 -0
  46. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  47. package/src/resources/extensions/github-sync/sync.ts +556 -0
  48. package/src/resources/extensions/github-sync/templates.ts +183 -0
  49. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  50. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  51. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  52. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  53. package/src/resources/extensions/github-sync/types.ts +47 -0
  54. package/src/resources/extensions/gsd/auto/session.ts +3 -25
  55. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
  56. package/src/resources/extensions/gsd/auto-loop.ts +382 -360
  57. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  58. package/src/resources/extensions/gsd/auto-prompts.ts +25 -45
  59. package/src/resources/extensions/gsd/auto-start.ts +11 -1
  60. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  61. package/src/resources/extensions/gsd/auto.ts +139 -86
  62. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  63. package/src/resources/extensions/gsd/commands.ts +2 -2
  64. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  65. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  66. package/src/resources/extensions/gsd/doctor-providers.ts +26 -9
  67. package/src/resources/extensions/gsd/doctor.ts +22 -1
  68. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  69. package/src/resources/extensions/gsd/files.ts +3 -1
  70. package/src/resources/extensions/gsd/git-service.ts +20 -10
  71. package/src/resources/extensions/gsd/guided-flow.ts +110 -38
  72. package/src/resources/extensions/gsd/index.ts +21 -16
  73. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  74. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  75. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  76. package/src/resources/extensions/gsd/preferences-validation.ts +50 -10
  77. package/src/resources/extensions/gsd/preferences.ts +3 -2
  78. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -0
  79. package/src/resources/extensions/gsd/repo-identity.ts +20 -3
  80. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  81. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  82. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
  83. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  84. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  85. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  86. package/src/resources/extensions/gsd/types.ts +0 -1
  87. package/src/resources/extensions/mcp-client/index.ts +17 -1
  88. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  89. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  90. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  91. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  92. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  93. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  94. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  95. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  96. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  97. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  98. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  99. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -707,16 +707,6 @@ export function validatePreferences(preferences) {
707
707
  errors.push("auto_report must be a boolean");
708
708
  }
709
709
  }
710
- // ─── Compression Strategy ───────────────────────────────────────────
711
- if (preferences.compression_strategy !== undefined) {
712
- const validStrategies = new Set(["truncate", "compress"]);
713
- if (typeof preferences.compression_strategy === "string" && validStrategies.has(preferences.compression_strategy)) {
714
- validated.compression_strategy = preferences.compression_strategy;
715
- }
716
- else {
717
- errors.push(`compression_strategy must be one of: truncate, compress`);
718
- }
719
- }
720
710
  // ─── Context Selection ──────────────────────────────────────────────
721
711
  if (preferences.context_selection !== undefined) {
722
712
  const validModes = new Set(["full", "smart"]);
@@ -727,5 +717,63 @@ export function validatePreferences(preferences) {
727
717
  errors.push(`context_selection must be one of: full, smart`);
728
718
  }
729
719
  }
720
+ // ─── GitHub Sync ────────────────────────────────────────────────────────
721
+ if (preferences.github !== undefined) {
722
+ if (typeof preferences.github === "object" && preferences.github !== null) {
723
+ const gh = preferences.github;
724
+ const validGh = {};
725
+ if (gh.enabled !== undefined) {
726
+ if (typeof gh.enabled === "boolean")
727
+ validGh.enabled = gh.enabled;
728
+ else
729
+ errors.push("github.enabled must be a boolean");
730
+ }
731
+ if (gh.repo !== undefined) {
732
+ if (typeof gh.repo === "string" && gh.repo.includes("/"))
733
+ validGh.repo = gh.repo;
734
+ else
735
+ errors.push('github.repo must be a string in "owner/repo" format');
736
+ }
737
+ if (gh.project !== undefined) {
738
+ const p = typeof gh.project === "number" ? gh.project : Number(gh.project);
739
+ if (Number.isFinite(p) && p > 0)
740
+ validGh.project = Math.floor(p);
741
+ else
742
+ errors.push("github.project must be a positive number");
743
+ }
744
+ if (gh.labels !== undefined) {
745
+ if (Array.isArray(gh.labels) && gh.labels.every((l) => typeof l === "string")) {
746
+ validGh.labels = gh.labels;
747
+ }
748
+ else {
749
+ errors.push("github.labels must be an array of strings");
750
+ }
751
+ }
752
+ if (gh.auto_link_commits !== undefined) {
753
+ if (typeof gh.auto_link_commits === "boolean")
754
+ validGh.auto_link_commits = gh.auto_link_commits;
755
+ else
756
+ errors.push("github.auto_link_commits must be a boolean");
757
+ }
758
+ if (gh.slice_prs !== undefined) {
759
+ if (typeof gh.slice_prs === "boolean")
760
+ validGh.slice_prs = gh.slice_prs;
761
+ else
762
+ errors.push("github.slice_prs must be a boolean");
763
+ }
764
+ const knownGhKeys = new Set(["enabled", "repo", "project", "labels", "auto_link_commits", "slice_prs"]);
765
+ for (const key of Object.keys(gh)) {
766
+ if (!knownGhKeys.has(key)) {
767
+ warnings.push(`unknown github key "${key}" — ignored`);
768
+ }
769
+ }
770
+ if (Object.keys(validGh).length > 0) {
771
+ validated.github = validGh;
772
+ }
773
+ }
774
+ else {
775
+ errors.push("github must be an object");
776
+ }
777
+ }
730
778
  return { preferences: validated, errors, warnings };
731
779
  }
@@ -25,7 +25,7 @@ export { validatePreferences } from "./preferences-validation.js";
25
25
  // ─── Re-exports: skills ─────────────────────────────────────────────────────
26
26
  export { resolveAllSkillReferences, resolveSkillDiscoveryMode, resolveSkillStalenessDays, } from "./preferences-skills.js";
27
27
  // ─── Re-exports: models ─────────────────────────────────────────────────────
28
- export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveCompressionStrategy, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
28
+ export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
29
29
  // ─── Path Constants & Getters ───────────────────────────────────────────────
30
30
  const GLOBAL_PREFERENCES_PATH = join(gsdHome, "preferences.md");
31
31
  const LEGACY_GLOBAL_PREFERENCES_PATH = join(homedir(), ".pi", "agent", "gsd-preferences.md");
@@ -200,10 +200,12 @@ function mergePreferences(base, override) {
200
200
  verification_auto_fix: override.verification_auto_fix ?? base.verification_auto_fix,
201
201
  verification_max_retries: override.verification_max_retries ?? base.verification_max_retries,
202
202
  search_provider: override.search_provider ?? base.search_provider,
203
- compression_strategy: override.compression_strategy ?? base.compression_strategy,
204
203
  context_selection: override.context_selection ?? base.context_selection,
205
204
  auto_visualize: override.auto_visualize ?? base.auto_visualize,
206
205
  auto_report: override.auto_report ?? base.auto_report,
206
+ github: (base.github || override.github)
207
+ ? { ...(base.github ?? {}), ...(override.github ?? {}) }
208
+ : undefined,
207
209
  };
208
210
  }
209
211
  function mergeStringLists(base, override) {
@@ -25,6 +25,8 @@ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply a
25
25
  ### Automation rules by mode
26
26
 
27
27
  - `artifact-driven` — verify with shell commands, scripts, file reads, and artifact structure checks.
28
+ - `browser-executable` — use browser tools to navigate to the target URL and verify expected behavior. Capture screenshots as evidence. Record pass/fail with specific assertions.
29
+ - `runtime-executable` — execute the specified command or script. Capture stdout/stderr as evidence. Record pass/fail based on exit code and output.
28
30
  - `live-runtime` — exercise the real runtime path. Start or connect to the app/service if needed, use browser/runtime/network checks, and verify observable behavior.
29
31
  - `mixed` — run all automatable artifact-driven and live-runtime checks. Separate any remaining human-only checks explicitly.
30
32
  - `human-experience` — automate setup, preconditions, screenshots, logs, and objective checks, but do **not** invent subjective PASS results. Mark taste-based, experiential, or purely human-judgment checks as `NEEDS-HUMAN` and use an overall verdict of `PARTIAL` unless every required check was objective and passed.
@@ -85,14 +85,30 @@ function resolveGitRoot(basePath) {
85
85
  return resolve(basePath);
86
86
  }
87
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
+ }
88
97
  /**
89
98
  * Compute a stable identity for a repository.
90
99
  *
91
- * SHA-256 of `${remoteUrl}\n${resolvedRoot}`, truncated to 12 hex chars.
92
- * Deterministic: same repo always produces the same hash regardless of
93
- * 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.
94
106
  */
95
107
  export function repoIdentity(basePath) {
108
+ const projectId = process.env.GSD_PROJECT_ID;
109
+ if (projectId) {
110
+ return projectId;
111
+ }
96
112
  const remoteUrl = getRemoteUrl(basePath);
97
113
  const root = resolveGitRoot(basePath);
98
114
  const input = `${remoteUrl}\n${root}`;
@@ -32,6 +32,30 @@ export function markSliceDoneInRoadmap(basePath, mid, sid) {
32
32
  clearParseCache();
33
33
  return true;
34
34
  }
35
+ /**
36
+ * Mark a slice as not done ([ ]) in the milestone roadmap.
37
+ * Idempotent — no-op if already unchecked or if the slice isn't found.
38
+ *
39
+ * @returns true if the roadmap was modified, false if no change was needed
40
+ */
41
+ export function markSliceUndoneInRoadmap(basePath, mid, sid) {
42
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
43
+ if (!roadmapFile)
44
+ return false;
45
+ let content;
46
+ try {
47
+ content = readFileSync(roadmapFile, "utf-8");
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ const updated = content.replace(new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sid}:`, "m"), `$1[ ] **${sid}:`);
53
+ if (updated === content)
54
+ return false;
55
+ atomicWriteSync(roadmapFile, updated);
56
+ clearParseCache();
57
+ return true;
58
+ }
35
59
  /**
36
60
  * Mark a task as done ([x]) in the slice plan.
37
61
  * Idempotent — no-op if already checked or if the task isn't found.
@@ -76,6 +76,19 @@ function readConfigs() {
76
76
  function getServerConfig(name) {
77
77
  return readConfigs().find((s) => s.name === name);
78
78
  }
79
+ /** Resolve ${VAR} references in env values against process.env. */
80
+ function resolveEnv(env) {
81
+ const resolved = {};
82
+ for (const [key, value] of Object.entries(env)) {
83
+ if (typeof value === "string") {
84
+ resolved[key] = value.replace(/\$\{([^}]+)\}/g, (_match, varName) => process.env[varName] ?? "");
85
+ }
86
+ else {
87
+ resolved[key] = value;
88
+ }
89
+ }
90
+ return resolved;
91
+ }
79
92
  async function getOrConnect(name, signal) {
80
93
  const existing = connections.get(name);
81
94
  if (existing)
@@ -89,7 +102,7 @@ async function getOrConnect(name, signal) {
89
102
  transport = new StdioClientTransport({
90
103
  command: config.command,
91
104
  args: config.args,
92
- env: config.env ? { ...process.env, ...config.env } : undefined,
105
+ env: config.env ? { ...process.env, ...resolveEnv(config.env) } : undefined,
93
106
  cwd: config.cwd,
94
107
  stderr: "pipe",
95
108
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.38.0-dev.bc2e21e",
3
+ "version": "2.38.0-dev.d533afb",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -5,8 +5,8 @@ import { generatePKCE } from "./pkce.js";
5
5
  const decode = (s) => atob(s);
6
6
  const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
7
7
  const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
8
- const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
9
- const REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
8
+ const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
9
+ const REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
10
10
  const SCOPES = "org:create_api_key user:profile user:inference";
11
11
  /**
12
12
  * Login with Anthropic OAuth (device code flow)
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/utils/oauth/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n */\n\nimport { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://claude.ai/oauth/authorize\";\nconst TOKEN_URL = \"https://console.anthropic.com/v1/oauth/token\";\nconst REDIRECT_URI = \"https://console.anthropic.com/oauth/code/callback\";\nconst SCOPES = \"org:create_api_key user:profile user:inference\";\n\n/**\n * Login with Anthropic OAuth (device code flow)\n *\n * @param onAuthUrl - Callback to handle the authorization URL (e.g., open browser)\n * @param onPromptCode - Callback to prompt user for the authorization code\n */\nexport async function loginAnthropic(\n\tonAuthUrl: (url: string) => void,\n\tonPromptCode: () => Promise<string>,\n): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\n\t// Build authorization URL\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: verifier,\n\t});\n\n\tconst authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;\n\n\t// Notify caller with URL to open\n\tonAuthUrl(authUrl);\n\n\t// Wait for user to paste authorization code (format: code#state)\n\tconst authCode = await onPromptCode();\n\tconst splits = authCode.split(\"#\");\n\tconst code = splits[0];\n\tconst state = splits[1];\n\n\t// Exchange code for tokens\n\tconst tokenResponse = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode: code,\n\t\t\tstate: state,\n\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\tcode_verifier: verifier,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!tokenResponse.ok) {\n\t\tconst error = await tokenResponse.text();\n\t\tthrow new Error(`Token exchange failed: ${error}`);\n\t}\n\n\tconst tokenData = (await tokenResponse.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\t// Calculate expiry time (current time + expires_in seconds - 5 min buffer)\n\tconst expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;\n\n\t// Save credentials\n\treturn {\n\t\trefresh: tokenData.refresh_token,\n\t\taccess: tokenData.access_token,\n\t\texpires: expiresAt,\n\t};\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nexport async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!response.ok) {\n\t\tconst error = await response.text();\n\t\tthrow new Error(`Anthropic token refresh failed: ${error}`);\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\treturn {\n\t\trefresh: data.refresh_token,\n\t\taccess: data.access_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const anthropicOAuthProvider: OAuthProviderInterface = {\n\tid: \"anthropic\",\n\tname: \"Anthropic (Claude Pro/Max)\",\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginAnthropic(\n\t\t\t(url) => callbacks.onAuth({ url }),\n\t\t\t() => callbacks.onPrompt({ message: \"Paste the authorization code:\" }),\n\t\t);\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshAnthropicToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/utils/oauth/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n */\n\nimport { generatePKCE } from \"./pkce.js\";\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from \"./types.js\";\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode(\"OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl\");\nconst AUTHORIZE_URL = \"https://claude.ai/oauth/authorize\";\nconst TOKEN_URL = \"https://platform.claude.com/v1/oauth/token\";\nconst REDIRECT_URI = \"https://platform.claude.com/oauth/code/callback\";\nconst SCOPES = \"org:create_api_key user:profile user:inference\";\n\n/**\n * Login with Anthropic OAuth (device code flow)\n *\n * @param onAuthUrl - Callback to handle the authorization URL (e.g., open browser)\n * @param onPromptCode - Callback to prompt user for the authorization code\n */\nexport async function loginAnthropic(\n\tonAuthUrl: (url: string) => void,\n\tonPromptCode: () => Promise<string>,\n): Promise<OAuthCredentials> {\n\tconst { verifier, challenge } = await generatePKCE();\n\n\t// Build authorization URL\n\tconst authParams = new URLSearchParams({\n\t\tcode: \"true\",\n\t\tclient_id: CLIENT_ID,\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: REDIRECT_URI,\n\t\tscope: SCOPES,\n\t\tcode_challenge: challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: verifier,\n\t});\n\n\tconst authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;\n\n\t// Notify caller with URL to open\n\tonAuthUrl(authUrl);\n\n\t// Wait for user to paste authorization code (format: code#state)\n\tconst authCode = await onPromptCode();\n\tconst splits = authCode.split(\"#\");\n\tconst code = splits[0];\n\tconst state = splits[1];\n\n\t// Exchange code for tokens\n\tconst tokenResponse = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t},\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\tcode: code,\n\t\t\tstate: state,\n\t\t\tredirect_uri: REDIRECT_URI,\n\t\t\tcode_verifier: verifier,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!tokenResponse.ok) {\n\t\tconst error = await tokenResponse.text();\n\t\tthrow new Error(`Token exchange failed: ${error}`);\n\t}\n\n\tconst tokenData = (await tokenResponse.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\t// Calculate expiry time (current time + expires_in seconds - 5 min buffer)\n\tconst expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;\n\n\t// Save credentials\n\treturn {\n\t\trefresh: tokenData.refresh_token,\n\t\taccess: tokenData.access_token,\n\t\texpires: expiresAt,\n\t};\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nexport async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {\n\tconst response = await fetch(TOKEN_URL, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: CLIENT_ID,\n\t\t\trefresh_token: refreshToken,\n\t\t}),\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\n\tif (!response.ok) {\n\t\tconst error = await response.text();\n\t\tthrow new Error(`Anthropic token refresh failed: ${error}`);\n\t}\n\n\tconst data = (await response.json()) as {\n\t\taccess_token: string;\n\t\trefresh_token: string;\n\t\texpires_in: number;\n\t};\n\n\treturn {\n\t\trefresh: data.refresh_token,\n\t\taccess: data.access_token,\n\t\texpires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n\t};\n}\n\nexport const anthropicOAuthProvider: OAuthProviderInterface = {\n\tid: \"anthropic\",\n\tname: \"Anthropic (Claude Pro/Max)\",\n\n\tasync login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n\t\treturn loginAnthropic(\n\t\t\t(url) => callbacks.onAuth({ url }),\n\t\t\t() => callbacks.onPrompt({ message: \"Paste the authorization code:\" }),\n\t\t);\n\t},\n\n\tasync refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n\t\treturn refreshAnthropicToken(credentials.refresh);\n\t},\n\n\tgetApiKey(credentials: OAuthCredentials): string {\n\t\treturn credentials.access;\n\t},\n};\n"]}
@@ -8,8 +8,8 @@ import type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } fr
8
8
  const decode = (s: string) => atob(s);
9
9
  const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
10
10
  const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
11
- const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
12
- const REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
11
+ const TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
12
+ const REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
13
13
  const SCOPES = "org:create_api_key user:profile user:inference";
14
14
 
15
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyBH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,KAAK,EACX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAKpB,MAAM,YAAY,CAAC;AAoGpB,wBAAsB,qBAAqB,CAAC,CAAC,GAAG,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAI/G;AA6BD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAmCzD;AAkMD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,GACxB,OAAO,CAAC,SAAS,CAAC,CAKpB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyBrH;AAmHD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAoD/B"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA+BH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,KAAK,EACX,SAAS,EAET,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EAKpB,MAAM,YAAY,CAAC;AAsTpB,wBAAsB,qBAAqB,CAAC,CAAC,GAAG,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAI/G;AA6BD;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,gBAAgB,CAmCzD;AAkMD;;GAEG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,gBAAgB,EACzB,aAAa,SAAa,GACxB,OAAO,CAAC,SAAS,CAAC,CAKpB;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyBrH;AAmHD;;GAEG;AACH,wBAAsB,yBAAyB,CAC9C,eAAe,EAAE,MAAM,EAAE,EACzB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAsB,EAChC,QAAQ,CAAC,EAAE,QAAQ,GACjB,OAAO,CAAC,oBAAoB,CAAC,CAoD/B"}
@@ -21,6 +21,12 @@ import * as _bundledYaml from "yaml";
21
21
  import * as _bundledMcpClient from "@modelcontextprotocol/sdk/client";
22
22
  import * as _bundledMcpStdio from "@modelcontextprotocol/sdk/client/stdio.js";
23
23
  import * as _bundledMcpStreamableHttp from "@modelcontextprotocol/sdk/client/streamableHttp.js";
24
+ import * as _bundledMcpSse from "@modelcontextprotocol/sdk/client/sse.js";
25
+ import * as _bundledMcpServer from "@modelcontextprotocol/sdk/server";
26
+ import * as _bundledMcpServerStdio from "@modelcontextprotocol/sdk/server/stdio.js";
27
+ import * as _bundledMcpServerSse from "@modelcontextprotocol/sdk/server/sse.js";
28
+ import * as _bundledMcpServerStreamableHttp from "@modelcontextprotocol/sdk/server/streamableHttp.js";
29
+ import * as _bundledMcpTypes from "@modelcontextprotocol/sdk/types.js";
24
30
  import { getAgentDir, isBunBinary } from "../../config.js";
25
31
  // NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
26
32
  // avoiding a circular dependency. Extensions can import from @gsd/pi-coding-agent.
@@ -29,8 +35,11 @@ import { createEventBus } from "../event-bus.js";
29
35
  import { execCommand } from "../exec.js";
30
36
  import { getUntrustedExtensionPaths } from "./project-trust.js";
31
37
  export { isProjectTrusted, trustProject, getUntrustedExtensionPaths } from "./project-trust.js";
32
- /** Modules available to extensions via virtualModules (for compiled Bun binary) */
33
- const VIRTUAL_MODULES = {
38
+ /**
39
+ * Statically imported modules for Bun binary virtualModules.
40
+ * Maps specifier -> module object for subpaths that must be available in compiled binaries.
41
+ */
42
+ const STATIC_BUNDLED_MODULES = {
34
43
  "@sinclair/typebox": _bundledTypebox,
35
44
  "@gsd/pi-agent-core": _bundledPiAgentCore,
36
45
  "@gsd/pi-tui": _bundledPiTui,
@@ -43,6 +52,17 @@ const VIRTUAL_MODULES = {
43
52
  "@modelcontextprotocol/sdk/client/stdio.js": _bundledMcpStdio,
44
53
  "@modelcontextprotocol/sdk/client/streamableHttp": _bundledMcpStreamableHttp,
45
54
  "@modelcontextprotocol/sdk/client/streamableHttp.js": _bundledMcpStreamableHttp,
55
+ "@modelcontextprotocol/sdk/client/sse": _bundledMcpSse,
56
+ "@modelcontextprotocol/sdk/client/sse.js": _bundledMcpSse,
57
+ "@modelcontextprotocol/sdk/server": _bundledMcpServer,
58
+ "@modelcontextprotocol/sdk/server/stdio": _bundledMcpServerStdio,
59
+ "@modelcontextprotocol/sdk/server/stdio.js": _bundledMcpServerStdio,
60
+ "@modelcontextprotocol/sdk/server/sse": _bundledMcpServerSse,
61
+ "@modelcontextprotocol/sdk/server/sse.js": _bundledMcpServerSse,
62
+ "@modelcontextprotocol/sdk/server/streamableHttp": _bundledMcpServerStreamableHttp,
63
+ "@modelcontextprotocol/sdk/server/streamableHttp.js": _bundledMcpServerStreamableHttp,
64
+ "@modelcontextprotocol/sdk/types": _bundledMcpTypes,
65
+ "@modelcontextprotocol/sdk/types.js": _bundledMcpTypes,
46
66
  // Aliases for external PI ecosystem packages that import from the original scope
47
67
  "@mariozechner/pi-agent-core": _bundledPiAgentCore,
48
68
  "@mariozechner/pi-tui": _bundledPiTui,
@@ -50,8 +70,180 @@ const VIRTUAL_MODULES = {
50
70
  "@mariozechner/pi-ai/oauth": _bundledPiAiOauth,
51
71
  "@mariozechner/pi-coding-agent": _bundledPiCodingAgent,
52
72
  };
73
+ /** Modules available to extensions via virtualModules (for compiled Bun binary) */
74
+ const VIRTUAL_MODULES = { ...STATIC_BUNDLED_MODULES };
53
75
  const require = createRequire(import.meta.url);
54
76
  const EXTENSION_TIMING_ENABLED = process.env.GSD_STARTUP_TIMING === "1" || process.env.PI_TIMING === "1";
77
+ /**
78
+ * Bundled npm packages whose subpath exports should be auto-resolved for extensions.
79
+ * Each package listed here will have its `exports` field read from package.json,
80
+ * and all subpath exports will be registered as jiti aliases (Node.js mode) so that
81
+ * extensions can import any standard subpath without hitting jiti's CJS double-resolve bug.
82
+ */
83
+ const BUNDLED_PACKAGES_WITH_EXPORTS = [
84
+ "@modelcontextprotocol/sdk",
85
+ "yaml",
86
+ ];
87
+ /**
88
+ * Read a package's `exports` field and return alias entries mapping
89
+ * specifiers (e.g. `@modelcontextprotocol/sdk/server`) to resolved file paths.
90
+ *
91
+ * Handles:
92
+ * - Explicit subpath exports: `./client` -> `@pkg/client`
93
+ * - Wildcard exports (`./*`): scans the package's dist directory for actual files
94
+ * - Both `.js`-suffixed and bare specifiers for each subpath
95
+ */
96
+ function resolveSubpathExports(packageName) {
97
+ const aliases = {};
98
+ let packageJsonPath;
99
+ try {
100
+ // Resolve the package's root directory via its package.json
101
+ packageJsonPath = require.resolve(`${packageName}/package.json`);
102
+ }
103
+ catch {
104
+ // Package doesn't allow importing package.json via exports — find it manually
105
+ try {
106
+ const anyEntry = require.resolve(packageName);
107
+ // Walk up from the resolved entry to find package.json
108
+ let dir = path.dirname(anyEntry);
109
+ while (dir !== path.dirname(dir)) {
110
+ const candidate = path.join(dir, "package.json");
111
+ if (fs.existsSync(candidate)) {
112
+ try {
113
+ const pkg = JSON.parse(fs.readFileSync(candidate, "utf-8"));
114
+ if (pkg.name === packageName) {
115
+ packageJsonPath = candidate;
116
+ break;
117
+ }
118
+ }
119
+ catch {
120
+ // not valid JSON, keep walking
121
+ }
122
+ }
123
+ dir = path.dirname(dir);
124
+ }
125
+ }
126
+ catch {
127
+ return aliases;
128
+ }
129
+ if (!packageJsonPath)
130
+ return aliases;
131
+ }
132
+ let pkg;
133
+ try {
134
+ pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
135
+ }
136
+ catch {
137
+ return aliases;
138
+ }
139
+ const exports = pkg.exports;
140
+ if (!exports || typeof exports !== "object")
141
+ return aliases;
142
+ const packageDir = path.dirname(packageJsonPath);
143
+ for (const [subpath, target] of Object.entries(exports)) {
144
+ if (subpath === ".")
145
+ continue; // Root export handled by static imports
146
+ // Handle wildcard exports like "./*"
147
+ if (subpath.includes("*")) {
148
+ resolveWildcardExports(packageName, packageDir, subpath, target, aliases);
149
+ continue;
150
+ }
151
+ // Explicit subpath: "./client" -> "@pkg/client"
152
+ const specifier = `${packageName}/${subpath.replace(/^\.\//, "")}`;
153
+ try {
154
+ const resolved = require.resolve(specifier);
155
+ aliases[specifier] = resolved;
156
+ // Add .js-suffixed variant if the specifier doesn't already end in .js
157
+ if (!specifier.endsWith(".js")) {
158
+ const jsSpecifier = `${specifier}.js`;
159
+ try {
160
+ const jsResolved = require.resolve(jsSpecifier);
161
+ aliases[jsSpecifier] = jsResolved;
162
+ }
163
+ catch {
164
+ // .js variant doesn't resolve — that's fine
165
+ }
166
+ }
167
+ // Add bare variant (without .js) if it ends in .js
168
+ if (specifier.endsWith(".js")) {
169
+ const bareSpecifier = specifier.slice(0, -3);
170
+ try {
171
+ const bareResolved = require.resolve(bareSpecifier);
172
+ aliases[bareSpecifier] = bareResolved;
173
+ }
174
+ catch {
175
+ // bare variant doesn't resolve — that's fine
176
+ }
177
+ }
178
+ }
179
+ catch {
180
+ // Subpath doesn't resolve — skip it
181
+ }
182
+ }
183
+ return aliases;
184
+ }
185
+ /**
186
+ * Resolve wildcard export patterns (e.g. `./*`) by scanning the package's
187
+ * file structure to find all matching files and generate alias entries.
188
+ */
189
+ function resolveWildcardExports(packageName, packageDir, subpathPattern, target, aliases) {
190
+ // Extract the target directory pattern from the export target
191
+ // e.g. { "require": "./dist/cjs/*" } -> "dist/cjs"
192
+ let targetDir = null;
193
+ if (typeof target === "string") {
194
+ targetDir = target.replace(/\/\*$/, "").replace(/^\.\//, "");
195
+ }
196
+ else if (target && typeof target === "object") {
197
+ const targetObj = target;
198
+ // Prefer "require" for CJS compatibility with jiti, fall back to "import"
199
+ const resolved = targetObj.require ?? targetObj.import ?? targetObj.default;
200
+ if (typeof resolved === "string") {
201
+ targetDir = resolved.replace(/\/\*$/, "").replace(/^\.\//, "");
202
+ }
203
+ }
204
+ if (!targetDir)
205
+ return;
206
+ const fullTargetDir = path.join(packageDir, targetDir);
207
+ if (!fs.existsSync(fullTargetDir))
208
+ return;
209
+ // Scan for .js files and generate specifiers
210
+ const subpathPrefix = subpathPattern.replace(/\/?\*$/, "").replace(/^\.\//, "");
211
+ scanDirForExports(packageName, fullTargetDir, subpathPrefix, aliases);
212
+ }
213
+ /**
214
+ * Recursively scan a directory for .js files and register them as aliases.
215
+ */
216
+ function scanDirForExports(packageName, dir, relativePath, aliases) {
217
+ let entries;
218
+ try {
219
+ entries = fs.readdirSync(dir, { withFileTypes: true });
220
+ }
221
+ catch {
222
+ return;
223
+ }
224
+ for (const entry of entries) {
225
+ const entryRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
226
+ if (entry.isDirectory()) {
227
+ // Skip examples/test directories — extensions don't need them
228
+ if (entry.name === "examples" || entry.name === "__tests__" || entry.name === "test")
229
+ continue;
230
+ scanDirForExports(packageName, path.join(dir, entry.name), entryRelative, aliases);
231
+ }
232
+ else if (entry.name.endsWith(".js") && !entry.name.endsWith(".d.js")) {
233
+ const filePath = path.join(dir, entry.name);
234
+ const specifier = `${packageName}/${entryRelative}`;
235
+ // Only add if not already covered by an explicit export
236
+ if (!(specifier in aliases)) {
237
+ aliases[specifier] = filePath;
238
+ }
239
+ // Also add bare (no .js) variant
240
+ const bareSpecifier = specifier.replace(/\.js$/, "");
241
+ if (!(bareSpecifier in aliases)) {
242
+ aliases[bareSpecifier] = filePath;
243
+ }
244
+ }
245
+ }
246
+ }
55
247
  function logExtensionTiming(extensionPath, ms, outcome) {
56
248
  if (!EXTENSION_TIMING_ENABLED)
57
249
  return;
@@ -79,7 +271,18 @@ function getAliases() {
79
271
  }
80
272
  return fileURLToPath(import.meta.resolve(specifier));
81
273
  };
274
+ // Auto-discover subpath exports from bundled npm packages.
275
+ // This ensures extensions can import any standard subpath (e.g. @modelcontextprotocol/sdk/server)
276
+ // without hitting jiti's CJS double-resolve bug.
277
+ const autoDiscovered = {};
278
+ for (const packageName of BUNDLED_PACKAGES_WITH_EXPORTS) {
279
+ const subpathAliases = resolveSubpathExports(packageName);
280
+ Object.assign(autoDiscovered, subpathAliases);
281
+ }
82
282
  _aliases = {
283
+ // Auto-discovered subpath exports (lowest priority — overridden by manual entries below)
284
+ ...autoDiscovered,
285
+ // Manual entries for workspace packages and packages needing special resolution
83
286
  "@gsd/pi-coding-agent": packageIndex,
84
287
  "@gsd/pi-agent-core": resolveWorkspaceOrImport("agent/dist/index.js", "@gsd/pi-agent-core"),
85
288
  "@gsd/pi-tui": resolveWorkspaceOrImport("tui/dist/index.js", "@gsd/pi-tui"),
@@ -87,11 +290,6 @@ function getAliases() {
87
290
  "@gsd/pi-ai/oauth": resolveWorkspaceOrImport("ai/dist/oauth.js", "@gsd/pi-ai/oauth"),
88
291
  "@sinclair/typebox": typeboxRoot,
89
292
  "yaml": yamlRoot,
90
- "@modelcontextprotocol/sdk/client": require.resolve("@modelcontextprotocol/sdk/client"),
91
- "@modelcontextprotocol/sdk/client/stdio": require.resolve("@modelcontextprotocol/sdk/client/stdio.js"),
92
- "@modelcontextprotocol/sdk/client/stdio.js": require.resolve("@modelcontextprotocol/sdk/client/stdio.js"),
93
- "@modelcontextprotocol/sdk/client/streamableHttp": require.resolve("@modelcontextprotocol/sdk/client/streamableHttp.js"),
94
- "@modelcontextprotocol/sdk/client/streamableHttp.js": require.resolve("@modelcontextprotocol/sdk/client/streamableHttp.js"),
95
293
  // Aliases for external PI ecosystem packages that import from the original scope
96
294
  "@mariozechner/pi-coding-agent": packageIndex,
97
295
  "@mariozechner/pi-agent-core": resolveWorkspaceOrImport("agent/dist/index.js", "@gsd/pi-agent-core"),