gsd-pi 2.46.0 → 2.46.1-dev.79664f2

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 (183) hide show
  1. package/README.md +1 -0
  2. package/dist/resources/extensions/claude-code-cli/index.js +25 -0
  3. package/dist/resources/extensions/claude-code-cli/models.js +40 -0
  4. package/dist/resources/extensions/claude-code-cli/package.json +11 -0
  5. package/dist/resources/extensions/claude-code-cli/partial-builder.js +223 -0
  6. package/dist/resources/extensions/claude-code-cli/readiness.js +26 -0
  7. package/dist/resources/extensions/claude-code-cli/sdk-types.js +8 -0
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +293 -0
  9. package/dist/resources/extensions/gsd/auto-start.js +7 -7
  10. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +2 -0
  11. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  12. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
  13. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  14. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  15. package/dist/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  16. package/dist/resources/extensions/gsd/prompts/run-uat.md +2 -2
  17. package/dist/resources/extensions/gsd/workflow-events.js +1 -1
  18. package/dist/resources/extensions/remote-questions/config.js +42 -0
  19. package/dist/web/standalone/.next/BUILD_ID +1 -1
  20. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  21. package/dist/web/standalone/.next/build-manifest.json +3 -3
  22. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  23. package/dist/web/standalone/.next/required-server-files.json +3 -3
  24. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  25. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  27. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  35. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  51. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  89. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  95. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  109. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  111. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  113. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  115. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/index.html +1 -1
  125. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  126. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  127. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  128. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  130. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/page.js +2 -2
  132. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  134. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  135. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  136. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/middleware.js +2 -2
  138. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  140. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  141. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  142. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  143. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  144. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  145. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  147. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  148. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  149. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  150. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  151. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  152. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  153. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  154. package/dist/web/standalone/server.js +1 -1
  155. package/package.json +2 -1
  156. package/packages/pi-coding-agent/package.json +1 -1
  157. package/pkg/package.json +1 -1
  158. package/src/resources/extensions/claude-code-cli/index.ts +28 -0
  159. package/src/resources/extensions/claude-code-cli/models.ts +42 -0
  160. package/src/resources/extensions/claude-code-cli/package.json +11 -0
  161. package/src/resources/extensions/claude-code-cli/partial-builder.ts +258 -0
  162. package/src/resources/extensions/claude-code-cli/readiness.ts +30 -0
  163. package/src/resources/extensions/claude-code-cli/sdk-types.ts +149 -0
  164. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +355 -0
  165. package/src/resources/extensions/gsd/auto-start.ts +8 -8
  166. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +3 -0
  167. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  168. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +2 -2
  169. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  170. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  171. package/src/resources/extensions/gsd/prompts/research-milestone.md +2 -2
  172. package/src/resources/extensions/gsd/prompts/run-uat.md +2 -2
  173. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +7 -3
  174. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +40 -0
  175. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +84 -0
  176. package/src/resources/extensions/gsd/tests/run-uat.test.ts +25 -0
  177. package/src/resources/extensions/gsd/workflow-events.ts +1 -1
  178. package/src/resources/extensions/remote-questions/config.ts +45 -0
  179. package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.js +0 -1
  180. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  181. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  182. /package/dist/web/standalone/.next/static/{8zT99piZz8u3xAU3Omz2g → vP6aj-TThZymVNx5Pi2AN}/_buildManifest.js +0 -0
  183. /package/dist/web/standalone/.next/static/{8zT99piZz8u3xAU3Omz2g → vP6aj-TThZymVNx5Pi2AN}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  [![GitHub stars](https://img.shields.io/github/stars/gsd-build/GSD-2?style=for-the-badge&logo=github&color=181717)](https://github.com/gsd-build/GSD-2)
10
10
  [![Discord](https://img.shields.io/badge/Discord-Join%20us-5865F2?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/gsd)
11
11
  [![License](https://img.shields.io/badge/license-MIT-blue?style=for-the-badge)](LICENSE)
12
+ [![$GSD Token](https://img.shields.io/badge/$GSD-Dexscreener-1C1C1C?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIgZmlsbD0iIzAwRkYwMCIvPjwvc3ZnPg==&logoColor=00FF00)](https://dexscreener.com/solana/dwudwjvan7bzkw9zwlbyv6kspdlvhwzrqy6ebk8xzxkv)
12
13
 
13
14
  The original GSD went viral as a prompt framework for Claude Code. It worked, but it was fighting the tool — injecting prompts through slash commands, hoping the LLM would follow instructions, with no actual control over context windows, sessions, or execution.
14
15
 
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Claude Code CLI Provider Extension
3
+ *
4
+ * Registers a model provider that delegates inference to the user's
5
+ * locally-installed Claude Code CLI via the official Agent SDK.
6
+ *
7
+ * Users with a Claude Code subscription (Pro/Max/Team) get access to
8
+ * subsidized inference through GSD's UI — no API key required.
9
+ *
10
+ * TOS-compliant: uses Anthropic's official `@anthropic-ai/claude-agent-sdk`,
11
+ * never touches credentials, never offers a login flow.
12
+ */
13
+ import { CLAUDE_CODE_MODELS } from "./models.js";
14
+ import { isClaudeCodeReady } from "./readiness.js";
15
+ import { streamViaClaudeCode } from "./stream-adapter.js";
16
+ export default function claudeCodeCli(pi) {
17
+ pi.registerProvider("claude-code", {
18
+ authMode: "externalCli",
19
+ api: "anthropic-messages",
20
+ baseUrl: "local://claude-code",
21
+ isReady: isClaudeCodeReady,
22
+ streamSimple: streamViaClaudeCode,
23
+ models: CLAUDE_CODE_MODELS,
24
+ });
25
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Model definitions for the Claude Code CLI provider.
3
+ *
4
+ * Costs are zero because inference is covered by the user's Claude Code
5
+ * subscription. The SDK's `result` message still provides token counts
6
+ * for display in the TUI.
7
+ *
8
+ * Context windows and max tokens match the Anthropic API definitions
9
+ * in models.generated.ts.
10
+ */
11
+ const ZERO_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
12
+ export const CLAUDE_CODE_MODELS = [
13
+ {
14
+ id: "claude-opus-4-6",
15
+ name: "Claude Opus 4.6 (via Claude Code)",
16
+ reasoning: true,
17
+ input: ["text", "image"],
18
+ cost: ZERO_COST,
19
+ contextWindow: 1_000_000,
20
+ maxTokens: 128_000,
21
+ },
22
+ {
23
+ id: "claude-sonnet-4-6",
24
+ name: "Claude Sonnet 4.6 (via Claude Code)",
25
+ reasoning: true,
26
+ input: ["text", "image"],
27
+ cost: ZERO_COST,
28
+ contextWindow: 1_000_000,
29
+ maxTokens: 64_000,
30
+ },
31
+ {
32
+ id: "claude-haiku-4-5",
33
+ name: "Claude Haiku 4.5 (via Claude Code)",
34
+ reasoning: true,
35
+ input: ["text", "image"],
36
+ cost: ZERO_COST,
37
+ contextWindow: 200_000,
38
+ maxTokens: 64_000,
39
+ },
40
+ ];
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@gsd/claude-code-cli",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "pi": {
7
+ "extensions": [
8
+ "./index.js"
9
+ ]
10
+ }
11
+ }
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Content-block mapping helpers and streaming state tracker.
3
+ *
4
+ * Translates the Claude Agent SDK's `BetaRawMessageStreamEvent` sequence
5
+ * into GSD's `AssistantMessageEvent` deltas for incremental TUI rendering.
6
+ */
7
+ // ---------------------------------------------------------------------------
8
+ // Content-block mapping helpers
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Convert a single BetaContentBlock to the corresponding GSD content type.
12
+ */
13
+ export function mapContentBlock(block) {
14
+ switch (block.type) {
15
+ case "text":
16
+ return { type: "text", text: block.text };
17
+ case "thinking":
18
+ return {
19
+ type: "thinking",
20
+ thinking: block.thinking,
21
+ ...(block.signature ? { thinkingSignature: block.signature } : {}),
22
+ };
23
+ case "tool_use":
24
+ return {
25
+ type: "toolCall",
26
+ id: block.id,
27
+ name: block.name,
28
+ arguments: block.input,
29
+ };
30
+ case "server_tool_use":
31
+ return {
32
+ type: "serverToolUse",
33
+ id: block.id,
34
+ name: block.name,
35
+ input: block.input,
36
+ };
37
+ case "web_search_tool_result":
38
+ return {
39
+ type: "webSearchResult",
40
+ toolUseId: block.tool_use_id,
41
+ content: block.content,
42
+ };
43
+ default: {
44
+ const unknown = block;
45
+ return { type: "text", text: `[unknown content block: ${JSON.stringify(unknown)}]` };
46
+ }
47
+ }
48
+ }
49
+ export function mapStopReason(reason) {
50
+ switch (reason) {
51
+ case "end_turn":
52
+ case "stop_sequence":
53
+ return "stop";
54
+ case "max_tokens":
55
+ return "length";
56
+ case "tool_use":
57
+ return "toolUse";
58
+ default:
59
+ return "stop";
60
+ }
61
+ }
62
+ /**
63
+ * Convert SDK usage + total_cost_usd into GSD's Usage shape.
64
+ *
65
+ * The SDK does not break cost down per-bucket, so all cost is
66
+ * attributed to `cost.total`.
67
+ */
68
+ export function mapUsage(sdkUsage, totalCostUsd) {
69
+ return {
70
+ input: sdkUsage.input_tokens,
71
+ output: sdkUsage.output_tokens,
72
+ cacheRead: sdkUsage.cache_read_input_tokens,
73
+ cacheWrite: sdkUsage.cache_creation_input_tokens,
74
+ totalTokens: sdkUsage.input_tokens +
75
+ sdkUsage.output_tokens +
76
+ sdkUsage.cache_read_input_tokens +
77
+ sdkUsage.cache_creation_input_tokens,
78
+ cost: {
79
+ input: 0,
80
+ output: 0,
81
+ cacheRead: 0,
82
+ cacheWrite: 0,
83
+ total: totalCostUsd,
84
+ },
85
+ };
86
+ }
87
+ // ---------------------------------------------------------------------------
88
+ // Zero-cost usage constant
89
+ // ---------------------------------------------------------------------------
90
+ export const ZERO_USAGE = {
91
+ input: 0,
92
+ output: 0,
93
+ cacheRead: 0,
94
+ cacheWrite: 0,
95
+ totalTokens: 0,
96
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
97
+ };
98
+ // ---------------------------------------------------------------------------
99
+ // Streaming partial-message state tracker
100
+ // ---------------------------------------------------------------------------
101
+ /**
102
+ * Mutable accumulator that tracks the partial AssistantMessage being built
103
+ * from a sequence of stream_event messages. Produces AssistantMessageEvent
104
+ * deltas that the TUI can render incrementally.
105
+ */
106
+ export class PartialMessageBuilder {
107
+ partial;
108
+ /** Map from stream-event `index` to our content array index. */
109
+ indexMap = new Map();
110
+ /** Accumulated JSON input string per tool_use block (keyed by stream index). */
111
+ toolJsonAccum = new Map();
112
+ constructor(model) {
113
+ this.partial = {
114
+ role: "assistant",
115
+ content: [],
116
+ api: "anthropic-messages",
117
+ provider: "claude-code",
118
+ model,
119
+ usage: { ...ZERO_USAGE },
120
+ stopReason: "stop",
121
+ timestamp: Date.now(),
122
+ };
123
+ }
124
+ get message() {
125
+ return this.partial;
126
+ }
127
+ /**
128
+ * Feed a BetaRawMessageStreamEvent and return the corresponding
129
+ * AssistantMessageEvent (or null if the event is not mapped).
130
+ */
131
+ handleEvent(event) {
132
+ const streamIndex = event.index ?? 0;
133
+ switch (event.type) {
134
+ // ---- Block start ----
135
+ case "content_block_start": {
136
+ const block = event.content_block;
137
+ if (!block)
138
+ return null;
139
+ const contentIndex = this.partial.content.length;
140
+ this.indexMap.set(streamIndex, contentIndex);
141
+ if (block.type === "text") {
142
+ this.partial.content.push({ type: "text", text: "" });
143
+ return { type: "text_start", contentIndex, partial: this.partial };
144
+ }
145
+ if (block.type === "thinking") {
146
+ this.partial.content.push({ type: "thinking", thinking: "" });
147
+ return { type: "thinking_start", contentIndex, partial: this.partial };
148
+ }
149
+ if (block.type === "tool_use") {
150
+ this.toolJsonAccum.set(streamIndex, "");
151
+ this.partial.content.push({
152
+ type: "toolCall",
153
+ id: block.id,
154
+ name: block.name,
155
+ arguments: {},
156
+ });
157
+ return { type: "toolcall_start", contentIndex, partial: this.partial };
158
+ }
159
+ if (block.type === "server_tool_use") {
160
+ this.partial.content.push({
161
+ type: "serverToolUse",
162
+ id: block.id,
163
+ name: block.name,
164
+ input: block.input,
165
+ });
166
+ return { type: "server_tool_use", contentIndex, partial: this.partial };
167
+ }
168
+ return null;
169
+ }
170
+ // ---- Block delta ----
171
+ case "content_block_delta": {
172
+ const contentIndex = this.indexMap.get(streamIndex);
173
+ if (contentIndex === undefined)
174
+ return null;
175
+ const delta = event.delta;
176
+ if (!delta)
177
+ return null;
178
+ if (delta.type === "text_delta" && typeof delta.text === "string") {
179
+ const existing = this.partial.content[contentIndex];
180
+ existing.text += delta.text;
181
+ return { type: "text_delta", contentIndex, delta: delta.text, partial: this.partial };
182
+ }
183
+ if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
184
+ const existing = this.partial.content[contentIndex];
185
+ existing.thinking += delta.thinking;
186
+ return { type: "thinking_delta", contentIndex, delta: delta.thinking, partial: this.partial };
187
+ }
188
+ if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
189
+ const accum = (this.toolJsonAccum.get(streamIndex) ?? "") + delta.partial_json;
190
+ this.toolJsonAccum.set(streamIndex, accum);
191
+ return { type: "toolcall_delta", contentIndex, delta: delta.partial_json, partial: this.partial };
192
+ }
193
+ return null;
194
+ }
195
+ // ---- Block stop ----
196
+ case "content_block_stop": {
197
+ const contentIndex = this.indexMap.get(streamIndex);
198
+ if (contentIndex === undefined)
199
+ return null;
200
+ const block = this.partial.content[contentIndex];
201
+ if (block.type === "text") {
202
+ return { type: "text_end", contentIndex, content: block.text, partial: this.partial };
203
+ }
204
+ if (block.type === "thinking") {
205
+ return { type: "thinking_end", contentIndex, content: block.thinking, partial: this.partial };
206
+ }
207
+ if (block.type === "toolCall") {
208
+ const jsonStr = this.toolJsonAccum.get(streamIndex) ?? "{}";
209
+ try {
210
+ block.arguments = JSON.parse(jsonStr);
211
+ }
212
+ catch {
213
+ block.arguments = { _raw: jsonStr };
214
+ }
215
+ return { type: "toolcall_end", contentIndex, toolCall: block, partial: this.partial };
216
+ }
217
+ return null;
218
+ }
219
+ default:
220
+ return null;
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Readiness check for the Claude Code CLI provider.
3
+ *
4
+ * Verifies the `claude` binary is installed and responsive.
5
+ * Result is cached for 30 seconds to avoid shelling out on every
6
+ * model-availability check.
7
+ */
8
+ import { execSync } from "node:child_process";
9
+ let cachedReady = null;
10
+ let lastCheckMs = 0;
11
+ const CHECK_INTERVAL_MS = 30_000;
12
+ export function isClaudeCodeReady() {
13
+ const now = Date.now();
14
+ if (cachedReady !== null && now - lastCheckMs < CHECK_INTERVAL_MS) {
15
+ return cachedReady;
16
+ }
17
+ try {
18
+ execSync("claude --version", { timeout: 5_000, stdio: "pipe" });
19
+ cachedReady = true;
20
+ }
21
+ catch {
22
+ cachedReady = false;
23
+ }
24
+ lastCheckMs = now;
25
+ return cachedReady;
26
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Lightweight type mirrors for the Claude Agent SDK.
3
+ *
4
+ * These stubs allow the extension to compile without a hard dependency on
5
+ * `@anthropic-ai/claude-agent-sdk`. The real SDK is imported dynamically
6
+ * at runtime in stream-adapter.ts.
7
+ */
8
+ export {};
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Stream adapter: bridges the Claude Agent SDK into GSD's streamSimple contract.
3
+ *
4
+ * The SDK runs the full agentic loop (multi-turn, tool execution, compaction)
5
+ * in one call. This adapter translates the SDK's streaming output into
6
+ * AssistantMessageEvents for TUI rendering, then strips tool-call blocks from
7
+ * the final AssistantMessage so GSD's agent loop doesn't try to dispatch them.
8
+ */
9
+ import { EventStream } from "@gsd/pi-ai";
10
+ import { execSync } from "node:child_process";
11
+ import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
12
+ // ---------------------------------------------------------------------------
13
+ // Stream factory
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Construct an AssistantMessageEventStream using EventStream directly.
17
+ * (The class itself is only re-exported as a type from the @gsd/pi-ai barrel.)
18
+ */
19
+ function createAssistantStream() {
20
+ return new EventStream((event) => event.type === "done" || event.type === "error", (event) => {
21
+ if (event.type === "done")
22
+ return event.message;
23
+ if (event.type === "error")
24
+ return event.error;
25
+ throw new Error("Unexpected event type for final result");
26
+ });
27
+ }
28
+ // ---------------------------------------------------------------------------
29
+ // Claude binary resolution
30
+ // ---------------------------------------------------------------------------
31
+ let cachedClaudePath = null;
32
+ /**
33
+ * Resolve the path to the system-installed `claude` binary.
34
+ * The SDK defaults to a bundled cli.js which doesn't exist when
35
+ * installed as a library — we need to point it at the real CLI.
36
+ */
37
+ function getClaudePath() {
38
+ if (cachedClaudePath)
39
+ return cachedClaudePath;
40
+ try {
41
+ cachedClaudePath = execSync("which claude", { timeout: 5_000, stdio: "pipe" })
42
+ .toString()
43
+ .trim();
44
+ }
45
+ catch {
46
+ cachedClaudePath = "claude"; // fall back to PATH resolution
47
+ }
48
+ return cachedClaudePath;
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Prompt extraction
52
+ // ---------------------------------------------------------------------------
53
+ /**
54
+ * Extract the last user prompt text from GSD's context messages.
55
+ * The SDK manages its own conversation history — we only send
56
+ * the latest user message as the prompt.
57
+ */
58
+ function extractLastUserPrompt(context) {
59
+ for (let i = context.messages.length - 1; i >= 0; i--) {
60
+ const msg = context.messages[i];
61
+ if (msg.role === "user") {
62
+ if (typeof msg.content === "string")
63
+ return msg.content;
64
+ if (Array.isArray(msg.content)) {
65
+ const textParts = msg.content
66
+ .filter((part) => part.type === "text")
67
+ .map((part) => part.text);
68
+ if (textParts.length > 0)
69
+ return textParts.join("\n");
70
+ }
71
+ }
72
+ }
73
+ return "";
74
+ }
75
+ // ---------------------------------------------------------------------------
76
+ // Error helper
77
+ // ---------------------------------------------------------------------------
78
+ function makeErrorMessage(model, errorMsg) {
79
+ return {
80
+ role: "assistant",
81
+ content: [{ type: "text", text: `Claude Code error: ${errorMsg}` }],
82
+ api: "anthropic-messages",
83
+ provider: "claude-code",
84
+ model,
85
+ usage: { ...ZERO_USAGE },
86
+ stopReason: "error",
87
+ errorMessage: errorMsg,
88
+ timestamp: Date.now(),
89
+ };
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // streamSimple implementation
93
+ // ---------------------------------------------------------------------------
94
+ /**
95
+ * GSD streamSimple function that delegates to the Claude Agent SDK.
96
+ *
97
+ * Emits AssistantMessageEvent deltas for real-time TUI rendering
98
+ * (thinking, text, tool calls). The final AssistantMessage has tool-call
99
+ * blocks stripped so the agent loop ends the turn without local dispatch.
100
+ */
101
+ export function streamViaClaudeCode(model, context, options) {
102
+ const stream = createAssistantStream();
103
+ void pumpSdkMessages(model, context, options, stream);
104
+ return stream;
105
+ }
106
+ async function pumpSdkMessages(model, context, options, stream) {
107
+ const modelId = model.id;
108
+ let builder = null;
109
+ /** Track the last text content seen across all assistant turns for the final message. */
110
+ let lastTextContent = "";
111
+ let lastThinkingContent = "";
112
+ try {
113
+ // Dynamic import — the SDK is an optional dependency.
114
+ const sdkModule = "@anthropic-ai/claude-agent-sdk";
115
+ const sdk = (await import(/* webpackIgnore: true */ sdkModule));
116
+ // Bridge GSD's AbortSignal to SDK's AbortController
117
+ const controller = new AbortController();
118
+ if (options?.signal) {
119
+ options.signal.addEventListener("abort", () => controller.abort(), { once: true });
120
+ }
121
+ const prompt = extractLastUserPrompt(context);
122
+ const queryResult = sdk.query({
123
+ prompt,
124
+ options: {
125
+ pathToClaudeCodeExecutable: getClaudePath(),
126
+ model: modelId,
127
+ includePartialMessages: true,
128
+ persistSession: false,
129
+ abortController: controller,
130
+ cwd: process.cwd(),
131
+ permissionMode: "bypassPermissions",
132
+ allowDangerouslySkipPermissions: true,
133
+ settingSources: ["project"],
134
+ systemPrompt: { type: "preset", preset: "claude_code" },
135
+ betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
136
+ },
137
+ });
138
+ // Emit start with an empty partial
139
+ const initialPartial = {
140
+ role: "assistant",
141
+ content: [],
142
+ api: "anthropic-messages",
143
+ provider: "claude-code",
144
+ model: modelId,
145
+ usage: { ...ZERO_USAGE },
146
+ stopReason: "stop",
147
+ timestamp: Date.now(),
148
+ };
149
+ stream.push({ type: "start", partial: initialPartial });
150
+ for await (const msg of queryResult) {
151
+ if (options?.signal?.aborted)
152
+ break;
153
+ switch (msg.type) {
154
+ // -- Init --
155
+ case "system": {
156
+ // Nothing to emit — the stream is already started.
157
+ break;
158
+ }
159
+ // -- Streaming partial messages --
160
+ case "stream_event": {
161
+ const partial = msg;
162
+ if (partial.parent_tool_use_id !== null)
163
+ break; // skip subagent
164
+ const event = partial.event;
165
+ // New assistant turn starts with message_start
166
+ if (event.type === "message_start") {
167
+ builder = new PartialMessageBuilder(event.message?.model ?? modelId);
168
+ break;
169
+ }
170
+ if (!builder)
171
+ break;
172
+ const assistantEvent = builder.handleEvent(event);
173
+ if (assistantEvent) {
174
+ stream.push(assistantEvent);
175
+ }
176
+ break;
177
+ }
178
+ // -- Complete assistant message (non-streaming fallback) --
179
+ case "assistant": {
180
+ const sdkAssistant = msg;
181
+ if (sdkAssistant.parent_tool_use_id !== null)
182
+ break;
183
+ // Capture text content from complete messages
184
+ for (const block of sdkAssistant.message.content) {
185
+ if (block.type === "text") {
186
+ lastTextContent = block.text;
187
+ }
188
+ else if (block.type === "thinking") {
189
+ lastThinkingContent = block.thinking;
190
+ }
191
+ }
192
+ break;
193
+ }
194
+ // -- User message (synthetic tool result — signals turn boundary) --
195
+ case "user": {
196
+ const userMsg = msg;
197
+ if (userMsg.parent_tool_use_id !== null)
198
+ break;
199
+ // Capture accumulated text from the builder before resetting
200
+ if (builder) {
201
+ for (const block of builder.message.content) {
202
+ if (block.type === "text" && block.text) {
203
+ lastTextContent = block.text;
204
+ }
205
+ else if (block.type === "thinking" && block.thinking) {
206
+ lastThinkingContent = block.thinking;
207
+ }
208
+ }
209
+ }
210
+ builder = null;
211
+ break;
212
+ }
213
+ // -- Result (terminal) --
214
+ case "result": {
215
+ const result = msg;
216
+ // Build final message with text/thinking only (strip tool calls)
217
+ const finalContent = [];
218
+ // Use builder's accumulated content if available, falling back to captured text
219
+ if (builder) {
220
+ for (const block of builder.message.content) {
221
+ if (block.type === "text" && block.text) {
222
+ lastTextContent = block.text;
223
+ }
224
+ else if (block.type === "thinking" && block.thinking) {
225
+ lastThinkingContent = block.thinking;
226
+ }
227
+ }
228
+ }
229
+ if (lastThinkingContent) {
230
+ finalContent.push({ type: "thinking", thinking: lastThinkingContent });
231
+ }
232
+ if (lastTextContent) {
233
+ finalContent.push({ type: "text", text: lastTextContent });
234
+ }
235
+ // Fallback: use the SDK's result text if we have no content
236
+ if (finalContent.length === 0 && result.subtype === "success" && result.result) {
237
+ finalContent.push({ type: "text", text: result.result });
238
+ }
239
+ const finalMessage = {
240
+ role: "assistant",
241
+ content: finalContent,
242
+ api: "anthropic-messages",
243
+ provider: "claude-code",
244
+ model: modelId,
245
+ usage: mapUsage(result.usage, result.total_cost_usd),
246
+ stopReason: result.is_error ? "error" : "stop",
247
+ timestamp: Date.now(),
248
+ };
249
+ if (result.is_error) {
250
+ const errText = "errors" in result
251
+ ? result.errors?.join("; ")
252
+ : result.subtype;
253
+ finalMessage.errorMessage = errText;
254
+ stream.push({ type: "error", reason: "error", error: finalMessage });
255
+ }
256
+ else {
257
+ stream.push({ type: "done", reason: "stop", message: finalMessage });
258
+ }
259
+ return;
260
+ }
261
+ default:
262
+ break;
263
+ }
264
+ }
265
+ // Generator exhausted without a result message (unexpected)
266
+ const fallbackContent = [];
267
+ if (lastTextContent) {
268
+ fallbackContent.push({ type: "text", text: lastTextContent });
269
+ }
270
+ if (fallbackContent.length === 0) {
271
+ fallbackContent.push({ type: "text", text: "(Claude Code session ended without a response)" });
272
+ }
273
+ const fallback = {
274
+ role: "assistant",
275
+ content: fallbackContent,
276
+ api: "anthropic-messages",
277
+ provider: "claude-code",
278
+ model: modelId,
279
+ usage: { ...ZERO_USAGE },
280
+ stopReason: "stop",
281
+ timestamp: Date.now(),
282
+ };
283
+ stream.push({ type: "done", reason: "stop", message: fallback });
284
+ }
285
+ catch (err) {
286
+ const errorMsg = err instanceof Error ? err.message : String(err);
287
+ stream.push({
288
+ type: "error",
289
+ reason: "error",
290
+ error: makeErrorMessage(modelId, errorMsg),
291
+ });
292
+ }
293
+ }
@@ -399,16 +399,16 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
399
399
  const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
400
400
  const hasRequirements = existsSync(join(gsdDirPath, "REQUIREMENTS.md"));
401
401
  const hasMilestones = existsSync(join(gsdDirPath, "milestones"));
402
- if (hasDecisions || hasRequirements || hasMilestones) {
403
- try {
404
- const { openDatabase: openDb } = await import("./gsd-db.js");
402
+ try {
403
+ const { openDatabase: openDb } = await import("./gsd-db.js");
404
+ openDb(gsdDbPath);
405
+ if (hasDecisions || hasRequirements || hasMilestones) {
405
406
  const { migrateFromMarkdown } = await import("./md-importer.js");
406
- openDb(gsdDbPath);
407
407
  migrateFromMarkdown(s.basePath);
408
408
  }
409
- catch (err) {
410
- process.stderr.write(`gsd-migrate: auto-migration failed: ${err.message}\n`);
411
- }
409
+ }
410
+ catch (err) {
411
+ process.stderr.write(`gsd-migrate: auto-migration failed: ${err.message}\n`);
412
412
  }
413
413
  }
414
414
  if (existsSync(gsdDbPath) && !isDbAvailable()) {
@@ -57,6 +57,8 @@ export async function ensureDbOpen() {
57
57
  }
58
58
  return opened;
59
59
  }
60
+ // .gsd/ exists but has no Markdown content (fresh project) — create empty DB
61
+ return db.openDatabase(dbPath);
60
62
  }
61
63
  return false;
62
64
  }