@tuan_son.dinh/gsd 2.6.0

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 (227) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +453 -0
  3. package/dist/app-paths.d.ts +4 -0
  4. package/dist/app-paths.js +6 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +269 -0
  7. package/dist/loader.d.ts +2 -0
  8. package/dist/loader.js +70 -0
  9. package/dist/logo.d.ts +16 -0
  10. package/dist/logo.js +25 -0
  11. package/dist/onboarding.d.ts +43 -0
  12. package/dist/onboarding.js +418 -0
  13. package/dist/pi-migration.d.ts +14 -0
  14. package/dist/pi-migration.js +57 -0
  15. package/dist/resource-loader.d.ts +22 -0
  16. package/dist/resource-loader.js +60 -0
  17. package/dist/tool-bootstrap.d.ts +4 -0
  18. package/dist/tool-bootstrap.js +74 -0
  19. package/dist/wizard.d.ts +7 -0
  20. package/dist/wizard.js +25 -0
  21. package/package.json +60 -0
  22. package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
  23. package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
  24. package/pkg/dist/modes/interactive/theme/dark.json +85 -0
  25. package/pkg/dist/modes/interactive/theme/light.json +84 -0
  26. package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
  27. package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
  28. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
  29. package/pkg/dist/modes/interactive/theme/theme.js +949 -0
  30. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
  31. package/pkg/package.json +8 -0
  32. package/scripts/postinstall.js +127 -0
  33. package/src/resources/GSD-WORKFLOW.md +661 -0
  34. package/src/resources/agents/researcher.md +29 -0
  35. package/src/resources/agents/scout.md +56 -0
  36. package/src/resources/agents/worker.md +31 -0
  37. package/src/resources/extensions/ask-user-questions.ts +249 -0
  38. package/src/resources/extensions/bg-shell/index.ts +2808 -0
  39. package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
  40. package/src/resources/extensions/browser-tools/core.js +1057 -0
  41. package/src/resources/extensions/browser-tools/index.ts +4989 -0
  42. package/src/resources/extensions/browser-tools/package.json +20 -0
  43. package/src/resources/extensions/context7/index.ts +428 -0
  44. package/src/resources/extensions/context7/package.json +11 -0
  45. package/src/resources/extensions/get-secrets-from-user.ts +352 -0
  46. package/src/resources/extensions/google-search/index.ts +323 -0
  47. package/src/resources/extensions/google-search/package.json +9 -0
  48. package/src/resources/extensions/gsd/activity-log.ts +69 -0
  49. package/src/resources/extensions/gsd/auto.ts +2744 -0
  50. package/src/resources/extensions/gsd/commands.ts +313 -0
  51. package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
  52. package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
  53. package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
  54. package/src/resources/extensions/gsd/doctor.ts +690 -0
  55. package/src/resources/extensions/gsd/files.ts +732 -0
  56. package/src/resources/extensions/gsd/git-service.ts +597 -0
  57. package/src/resources/extensions/gsd/gitignore.ts +168 -0
  58. package/src/resources/extensions/gsd/guided-flow.ts +817 -0
  59. package/src/resources/extensions/gsd/index.ts +558 -0
  60. package/src/resources/extensions/gsd/metrics.ts +374 -0
  61. package/src/resources/extensions/gsd/migrate/command.ts +218 -0
  62. package/src/resources/extensions/gsd/migrate/index.ts +42 -0
  63. package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
  64. package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
  65. package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
  66. package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
  67. package/src/resources/extensions/gsd/migrate/types.ts +370 -0
  68. package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
  69. package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
  70. package/src/resources/extensions/gsd/observability-validator.ts +408 -0
  71. package/src/resources/extensions/gsd/package.json +11 -0
  72. package/src/resources/extensions/gsd/paths.ts +308 -0
  73. package/src/resources/extensions/gsd/preferences.ts +757 -0
  74. package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
  75. package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
  76. package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
  77. package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
  78. package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
  79. package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
  80. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
  81. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
  82. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
  83. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
  84. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
  85. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
  86. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
  87. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
  88. package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
  89. package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
  90. package/src/resources/extensions/gsd/prompts/queue.md +85 -0
  91. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
  92. package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
  93. package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
  94. package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
  95. package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
  96. package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
  97. package/src/resources/extensions/gsd/prompts/system.md +187 -0
  98. package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
  99. package/src/resources/extensions/gsd/session-forensics.ts +487 -0
  100. package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
  101. package/src/resources/extensions/gsd/state.ts +460 -0
  102. package/src/resources/extensions/gsd/templates/context.md +76 -0
  103. package/src/resources/extensions/gsd/templates/decisions.md +8 -0
  104. package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
  105. package/src/resources/extensions/gsd/templates/plan.md +131 -0
  106. package/src/resources/extensions/gsd/templates/preferences.md +24 -0
  107. package/src/resources/extensions/gsd/templates/project.md +31 -0
  108. package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
  109. package/src/resources/extensions/gsd/templates/requirements.md +81 -0
  110. package/src/resources/extensions/gsd/templates/research.md +46 -0
  111. package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
  112. package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
  113. package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
  114. package/src/resources/extensions/gsd/templates/state.md +19 -0
  115. package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
  116. package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
  117. package/src/resources/extensions/gsd/templates/uat.md +54 -0
  118. package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
  119. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
  120. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
  121. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
  122. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
  123. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
  124. package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
  125. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
  126. package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
  127. package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
  128. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
  129. package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
  130. package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
  131. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
  132. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
  133. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
  134. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
  135. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
  136. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
  137. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
  138. package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
  139. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
  140. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
  141. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
  142. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
  143. package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
  144. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
  145. package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
  146. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
  147. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
  148. package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
  149. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
  150. package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
  151. package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
  152. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
  153. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
  154. package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
  155. package/src/resources/extensions/gsd/types.ts +159 -0
  156. package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
  157. package/src/resources/extensions/gsd/workspace-index.ts +203 -0
  158. package/src/resources/extensions/gsd/worktree-command.ts +845 -0
  159. package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
  160. package/src/resources/extensions/gsd/worktree.ts +183 -0
  161. package/src/resources/extensions/mac-tools/index.ts +852 -0
  162. package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
  163. package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
  164. package/src/resources/extensions/mcporter/index.ts +429 -0
  165. package/src/resources/extensions/remote-questions/config.ts +81 -0
  166. package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
  167. package/src/resources/extensions/remote-questions/format.ts +163 -0
  168. package/src/resources/extensions/remote-questions/manager.ts +192 -0
  169. package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
  170. package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
  171. package/src/resources/extensions/remote-questions/status.ts +31 -0
  172. package/src/resources/extensions/remote-questions/store.ts +77 -0
  173. package/src/resources/extensions/remote-questions/types.ts +75 -0
  174. package/src/resources/extensions/search-the-web/cache.ts +78 -0
  175. package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
  176. package/src/resources/extensions/search-the-web/format.ts +258 -0
  177. package/src/resources/extensions/search-the-web/http.ts +238 -0
  178. package/src/resources/extensions/search-the-web/index.ts +65 -0
  179. package/src/resources/extensions/search-the-web/native-search.ts +157 -0
  180. package/src/resources/extensions/search-the-web/provider.ts +118 -0
  181. package/src/resources/extensions/search-the-web/tavily.ts +116 -0
  182. package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
  183. package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
  184. package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
  185. package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
  186. package/src/resources/extensions/shared/confirm-ui.ts +126 -0
  187. package/src/resources/extensions/shared/interview-ui.ts +613 -0
  188. package/src/resources/extensions/shared/next-action-ui.ts +197 -0
  189. package/src/resources/extensions/shared/progress-widget.ts +282 -0
  190. package/src/resources/extensions/shared/terminal.ts +23 -0
  191. package/src/resources/extensions/shared/thinking-widget.ts +107 -0
  192. package/src/resources/extensions/shared/ui.ts +400 -0
  193. package/src/resources/extensions/shared/wizard-ui.ts +551 -0
  194. package/src/resources/extensions/slash-commands/audit.ts +88 -0
  195. package/src/resources/extensions/slash-commands/clear.ts +10 -0
  196. package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
  197. package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
  198. package/src/resources/extensions/slash-commands/index.ts +12 -0
  199. package/src/resources/extensions/subagent/agents.ts +126 -0
  200. package/src/resources/extensions/subagent/index.ts +1020 -0
  201. package/src/resources/extensions/voice/index.ts +195 -0
  202. package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
  203. package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
  204. package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
  205. package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
  206. package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
  207. package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
  208. package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
  209. package/src/resources/skills/frontend-design/SKILL.md +45 -0
  210. package/src/resources/skills/swiftui/SKILL.md +208 -0
  211. package/src/resources/skills/swiftui/references/animations.md +921 -0
  212. package/src/resources/skills/swiftui/references/architecture.md +1561 -0
  213. package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
  214. package/src/resources/skills/swiftui/references/navigation.md +1492 -0
  215. package/src/resources/skills/swiftui/references/networking-async.md +214 -0
  216. package/src/resources/skills/swiftui/references/performance.md +1706 -0
  217. package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
  218. package/src/resources/skills/swiftui/references/state-management.md +1443 -0
  219. package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
  220. package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
  221. package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
  222. package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
  223. package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
  224. package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
  225. package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
  226. package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
  227. package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
package/dist/cli.js ADDED
@@ -0,0 +1,269 @@
1
+ import { AuthStorage, DefaultResourceLoader, ModelRegistry, SettingsManager, SessionManager, createAgentSession, InteractiveMode, runPrintMode, runRpcMode, } from '@mariozechner/pi-coding-agent';
2
+ import { existsSync, readdirSync, renameSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { agentDir, sessionsDir, authFilePath } from './app-paths.js';
5
+ import { initResources, buildResourceLoader } from './resource-loader.js';
6
+ import { ensureManagedTools } from './tool-bootstrap.js';
7
+ import { loadStoredEnvKeys } from './wizard.js';
8
+ import { migratePiCredentials } from './pi-migration.js';
9
+ import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
10
+ function parseCliArgs(argv) {
11
+ const flags = { extensions: [], messages: [] };
12
+ const args = argv.slice(2); // skip node + script
13
+ for (let i = 0; i < args.length; i++) {
14
+ const arg = args[i];
15
+ if (arg === '--mode' && i + 1 < args.length) {
16
+ const m = args[++i];
17
+ if (m === 'text' || m === 'json' || m === 'rpc')
18
+ flags.mode = m;
19
+ }
20
+ else if (arg === '--print' || arg === '-p') {
21
+ flags.print = true;
22
+ }
23
+ else if (arg === '--no-session') {
24
+ flags.noSession = true;
25
+ }
26
+ else if (arg === '--model' && i + 1 < args.length) {
27
+ flags.model = args[++i];
28
+ }
29
+ else if (arg === '--extension' && i + 1 < args.length) {
30
+ flags.extensions.push(args[++i]);
31
+ }
32
+ else if (arg === '--append-system-prompt' && i + 1 < args.length) {
33
+ flags.appendSystemPrompt = args[++i];
34
+ }
35
+ else if (arg === '--tools' && i + 1 < args.length) {
36
+ flags.tools = args[++i].split(',');
37
+ }
38
+ else if (arg === '--version' || arg === '-v') {
39
+ process.stdout.write((process.env.GSD_VERSION || '0.0.0') + '\n');
40
+ process.exit(0);
41
+ }
42
+ else if (arg === '--help' || arg === '-h') {
43
+ process.stdout.write(`GSD v${process.env.GSD_VERSION || '0.0.0'} — Get Shit Done\n\n`);
44
+ process.stdout.write('Usage: gsd [options] [message...]\n\n');
45
+ process.stdout.write('Options:\n');
46
+ process.stdout.write(' --mode <text|json|rpc> Output mode (default: interactive)\n');
47
+ process.stdout.write(' --print, -p Single-shot print mode\n');
48
+ process.stdout.write(' --model <id> Override model (e.g. claude-opus-4-6)\n');
49
+ process.stdout.write(' --no-session Disable session persistence\n');
50
+ process.stdout.write(' --extension <path> Load additional extension\n');
51
+ process.stdout.write(' --tools <a,b,c> Restrict available tools\n');
52
+ process.stdout.write(' --version, -v Print version and exit\n');
53
+ process.stdout.write(' --help, -h Print this help and exit\n');
54
+ process.stdout.write('\nSubcommands:\n');
55
+ process.stdout.write(' config Re-run the setup wizard\n');
56
+ process.exit(0);
57
+ }
58
+ else if (!arg.startsWith('--') && !arg.startsWith('-')) {
59
+ flags.messages.push(arg);
60
+ }
61
+ }
62
+ return flags;
63
+ }
64
+ const cliFlags = parseCliArgs(process.argv);
65
+ const isPrintMode = cliFlags.print || cliFlags.mode !== undefined;
66
+ // `gsd config` — replay the setup wizard and exit
67
+ if (cliFlags.messages[0] === 'config') {
68
+ const authStorage = AuthStorage.create(authFilePath);
69
+ await runOnboarding(authStorage);
70
+ process.exit(0);
71
+ }
72
+ // Pi's tool bootstrap can mis-detect already-installed fd/rg on some systems
73
+ // because spawnSync(..., ["--version"]) returns EPERM despite a zero exit code.
74
+ // Provision local managed binaries first so Pi sees them without probing PATH.
75
+ ensureManagedTools(join(agentDir, 'bin'));
76
+ const authStorage = AuthStorage.create(authFilePath);
77
+ loadStoredEnvKeys(authStorage);
78
+ migratePiCredentials(authStorage);
79
+ // Run onboarding wizard on first launch (no LLM provider configured)
80
+ if (!isPrintMode && shouldRunOnboarding(authStorage)) {
81
+ await runOnboarding(authStorage);
82
+ }
83
+ const modelRegistry = new ModelRegistry(authStorage);
84
+ const settingsManager = SettingsManager.create(agentDir);
85
+ // Validate configured model on startup — catches stale settings from prior installs
86
+ // (e.g. grok-2 which no longer exists) and fresh installs with no settings.
87
+ // Only resets the default when the configured model no longer exists in the registry;
88
+ // never overwrites a valid user choice.
89
+ const configuredProvider = settingsManager.getDefaultProvider();
90
+ const configuredModel = settingsManager.getDefaultModel();
91
+ const allModels = modelRegistry.getAll();
92
+ const configuredExists = configuredProvider && configuredModel &&
93
+ allModels.some((m) => m.provider === configuredProvider && m.id === configuredModel);
94
+ if (!configuredModel || !configuredExists) {
95
+ // Fallback: pick the best available Anthropic model
96
+ const preferred = allModels.find((m) => m.provider === 'anthropic' && m.id === 'claude-opus-4-6') ||
97
+ allModels.find((m) => m.provider === 'anthropic' && m.id.includes('opus')) ||
98
+ allModels.find((m) => m.provider === 'anthropic');
99
+ if (preferred) {
100
+ settingsManager.setDefaultModelAndProvider(preferred.provider, preferred.id);
101
+ }
102
+ }
103
+ // Default thinking level: off (always reset if not explicitly set)
104
+ if (settingsManager.getDefaultThinkingLevel() !== 'off' && !configuredExists) {
105
+ settingsManager.setDefaultThinkingLevel('off');
106
+ }
107
+ // GSD always uses quiet startup — the gsd extension renders its own branded header
108
+ if (!settingsManager.getQuietStartup()) {
109
+ settingsManager.setQuietStartup(true);
110
+ }
111
+ // Collapse changelog by default — avoid wall of text on updates
112
+ if (!settingsManager.getCollapseChangelog()) {
113
+ settingsManager.setCollapseChangelog(true);
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // Print / subagent mode — single-shot execution, no TTY required
117
+ // ---------------------------------------------------------------------------
118
+ if (isPrintMode) {
119
+ const sessionManager = cliFlags.noSession
120
+ ? SessionManager.inMemory()
121
+ : SessionManager.create(process.cwd());
122
+ // Read --append-system-prompt file content (subagent writes agent system prompts to temp files)
123
+ let appendSystemPrompt;
124
+ if (cliFlags.appendSystemPrompt) {
125
+ try {
126
+ appendSystemPrompt = readFileSync(cliFlags.appendSystemPrompt, 'utf-8');
127
+ }
128
+ catch {
129
+ // If it's not a file path, treat it as literal text
130
+ appendSystemPrompt = cliFlags.appendSystemPrompt;
131
+ }
132
+ }
133
+ initResources(agentDir);
134
+ const resourceLoader = new DefaultResourceLoader({
135
+ agentDir,
136
+ additionalExtensionPaths: cliFlags.extensions.length > 0 ? cliFlags.extensions : undefined,
137
+ appendSystemPrompt,
138
+ });
139
+ await resourceLoader.reload();
140
+ const { session, extensionsResult } = await createAgentSession({
141
+ authStorage,
142
+ modelRegistry,
143
+ settingsManager,
144
+ sessionManager,
145
+ resourceLoader,
146
+ });
147
+ if (extensionsResult.errors.length > 0) {
148
+ for (const err of extensionsResult.errors) {
149
+ process.stderr.write(`[gsd] Extension load error: ${err.error}\n`);
150
+ }
151
+ }
152
+ // Apply --model override if specified
153
+ if (cliFlags.model) {
154
+ const available = modelRegistry.getAvailable();
155
+ const match = available.find((m) => m.id === cliFlags.model) ||
156
+ available.find((m) => `${m.provider}/${m.id}` === cliFlags.model);
157
+ if (match) {
158
+ session.setModel(match);
159
+ }
160
+ }
161
+ const mode = cliFlags.mode || 'text';
162
+ if (mode === 'rpc') {
163
+ await runRpcMode(session);
164
+ process.exit(0);
165
+ }
166
+ await runPrintMode(session, {
167
+ mode,
168
+ messages: cliFlags.messages,
169
+ });
170
+ process.exit(0);
171
+ }
172
+ // ---------------------------------------------------------------------------
173
+ // Interactive mode — normal TTY session
174
+ // ---------------------------------------------------------------------------
175
+ // Per-directory session storage — same encoding as the upstream SDK so that
176
+ // /resume only shows sessions from the current working directory.
177
+ const cwd = process.cwd();
178
+ const safePath = `--${cwd.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--`;
179
+ const projectSessionsDir = join(sessionsDir, safePath);
180
+ // Migrate legacy flat sessions: before per-directory scoping, all .jsonl session
181
+ // files lived directly in ~/.gsd/sessions/. Move them into the correct per-cwd
182
+ // subdirectory so /resume can find them.
183
+ if (existsSync(sessionsDir)) {
184
+ try {
185
+ const entries = readdirSync(sessionsDir);
186
+ const flatJsonl = entries.filter(f => f.endsWith('.jsonl'));
187
+ if (flatJsonl.length > 0) {
188
+ const { mkdirSync } = await import('node:fs');
189
+ mkdirSync(projectSessionsDir, { recursive: true });
190
+ for (const file of flatJsonl) {
191
+ const src = join(sessionsDir, file);
192
+ const dst = join(projectSessionsDir, file);
193
+ if (!existsSync(dst)) {
194
+ renameSync(src, dst);
195
+ }
196
+ }
197
+ }
198
+ }
199
+ catch {
200
+ // Non-fatal — don't block startup if migration fails
201
+ }
202
+ }
203
+ const sessionManager = SessionManager.create(cwd, projectSessionsDir);
204
+ initResources(agentDir);
205
+ const resourceLoader = buildResourceLoader(agentDir);
206
+ await resourceLoader.reload();
207
+ const { session, extensionsResult } = await createAgentSession({
208
+ authStorage,
209
+ modelRegistry,
210
+ settingsManager,
211
+ sessionManager,
212
+ resourceLoader,
213
+ });
214
+ if (extensionsResult.errors.length > 0) {
215
+ for (const err of extensionsResult.errors) {
216
+ process.stderr.write(`[gsd] Extension load error: ${err.error}\n`);
217
+ }
218
+ }
219
+ // Restore scoped models from settings on startup.
220
+ // The upstream InteractiveMode reads enabledModels from settings when /scoped-models is opened,
221
+ // but doesn't apply them to the session at startup — so Ctrl+P cycles all models instead of
222
+ // just the saved selection until the user re-runs /scoped-models.
223
+ const enabledModelPatterns = settingsManager.getEnabledModels();
224
+ if (enabledModelPatterns && enabledModelPatterns.length > 0) {
225
+ const availableModels = modelRegistry.getAvailable();
226
+ const scopedModels = [];
227
+ const seen = new Set();
228
+ for (const pattern of enabledModelPatterns) {
229
+ // Patterns are "provider/modelId" exact strings saved by /scoped-models
230
+ const slashIdx = pattern.indexOf('/');
231
+ if (slashIdx !== -1) {
232
+ const provider = pattern.substring(0, slashIdx);
233
+ const modelId = pattern.substring(slashIdx + 1);
234
+ const model = availableModels.find((m) => m.provider === provider && m.id === modelId);
235
+ if (model) {
236
+ const key = `${model.provider}/${model.id}`;
237
+ if (!seen.has(key)) {
238
+ seen.add(key);
239
+ scopedModels.push({ model });
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ // Fallback: match by model id alone
245
+ const model = availableModels.find((m) => m.id === pattern);
246
+ if (model) {
247
+ const key = `${model.provider}/${model.id}`;
248
+ if (!seen.has(key)) {
249
+ seen.add(key);
250
+ scopedModels.push({ model });
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // Only apply if we resolved some models and it's a genuine subset
256
+ if (scopedModels.length > 0 && scopedModels.length < availableModels.length) {
257
+ session.setScopedModels(scopedModels);
258
+ }
259
+ }
260
+ if (!process.stdin.isTTY) {
261
+ process.stderr.write('[gsd] Error: Interactive mode requires a terminal (TTY).\n');
262
+ process.stderr.write('[gsd] Non-interactive alternatives:\n');
263
+ process.stderr.write('[gsd] gsd --print "your message" Single-shot prompt\n');
264
+ process.stderr.write('[gsd] gsd --mode rpc JSON-RPC over stdin/stdout\n');
265
+ process.stderr.write('[gsd] gsd --mode text "message" Text output mode\n');
266
+ process.exit(1);
267
+ }
268
+ const interactiveMode = new InteractiveMode(session);
269
+ await interactiveMode.run();
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/loader.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, resolve, join } from 'path';
4
+ import { readFileSync } from 'fs';
5
+ import { agentDir } from './app-paths.js';
6
+ // pkg/ is a shim directory: contains gsd's piConfig (package.json) and pi's
7
+ // theme assets (dist/modes/interactive/theme/) without a src/ directory.
8
+ // This allows config.js to:
9
+ // 1. Read piConfig.name → "gsd" (branding)
10
+ // 2. Resolve themes via dist/ (no src/ present → uses dist path)
11
+ const pkgDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'pkg');
12
+ // MUST be set before any dynamic import of pi SDK fires — this is what config.js
13
+ // reads to determine APP_NAME and CONFIG_DIR_NAME
14
+ process.env.PI_PACKAGE_DIR = pkgDir;
15
+ process.env.PI_SKIP_VERSION_CHECK = '1'; // GSD ships its own update check — suppress pi's
16
+ process.title = 'gsd';
17
+ // First-launch branding is handled by the onboarding wizard (src/onboarding.ts)
18
+ // GSD_CODING_AGENT_DIR — tells pi's getAgentDir() to return ~/.gsd/agent/ instead of ~/.gsd/agent/
19
+ process.env.GSD_CODING_AGENT_DIR = agentDir;
20
+ // NODE_PATH — make gsd's own node_modules available to extensions loaded via jiti.
21
+ // Without this, extensions (e.g. browser-tools) can't resolve dependencies like
22
+ // `playwright` because jiti resolves modules from pi-coding-agent's location, not gsd's.
23
+ // Prepending gsd's node_modules to NODE_PATH fixes this for all extensions.
24
+ const gsdRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
25
+ const gsdNodeModules = join(gsdRoot, 'node_modules');
26
+ process.env.NODE_PATH = process.env.NODE_PATH
27
+ ? `${gsdNodeModules}:${process.env.NODE_PATH}`
28
+ : gsdNodeModules;
29
+ // Force Node to re-evaluate module search paths with the updated NODE_PATH.
30
+ // Must happen synchronously before cli.js imports → extension loading.
31
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
32
+ const { Module } = await import('module');
33
+ Module._initPaths?.();
34
+ // GSD_VERSION — expose package version so extensions can display it
35
+ try {
36
+ const gsdPkg = JSON.parse(readFileSync(join(gsdRoot, 'package.json'), 'utf-8'));
37
+ process.env.GSD_VERSION = gsdPkg.version || '0.0.0';
38
+ }
39
+ catch {
40
+ process.env.GSD_VERSION = '0.0.0';
41
+ }
42
+ // GSD_BIN_PATH — absolute path to this loader (dist/loader.js), used by patched subagent
43
+ // to spawn gsd instead of pi when dispatching workflow tasks
44
+ process.env.GSD_BIN_PATH = process.argv[1];
45
+ // GSD_WORKFLOW_PATH — absolute path to bundled GSD-WORKFLOW.md, used by patched gsd extension
46
+ // when dispatching workflow prompts (dist/loader.js → ../src/resources/GSD-WORKFLOW.md)
47
+ const resourcesDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'resources');
48
+ process.env.GSD_WORKFLOW_PATH = join(resourcesDir, 'GSD-WORKFLOW.md');
49
+ // GSD_BUNDLED_EXTENSION_PATHS — colon-joined list of all bundled extension entry point absolute
50
+ // paths, used by patched subagent to pass --extension <path> to spawned gsd processes.
51
+ // IMPORTANT: paths point to agentDir (~/.gsd/agent/extensions/) NOT src/resources/extensions/.
52
+ // initResources() syncs bundled extensions to agentDir before any extension loading occurs,
53
+ // so these paths are always valid at runtime. Using agentDir paths matches what buildResourceLoader
54
+ // discovers (it scans agentDir), so pi's deduplication works correctly and extensions are not
55
+ // double-loaded in subagent child processes.
56
+ // Note: shared/ is NOT included — it's a library imported by gsd and ask-user-questions, not an entry point.
57
+ process.env.GSD_BUNDLED_EXTENSION_PATHS = [
58
+ join(agentDir, 'extensions', 'gsd', 'index.ts'),
59
+ join(agentDir, 'extensions', 'bg-shell', 'index.ts'),
60
+ join(agentDir, 'extensions', 'browser-tools', 'index.ts'),
61
+ join(agentDir, 'extensions', 'context7', 'index.ts'),
62
+ join(agentDir, 'extensions', 'search-the-web', 'index.ts'),
63
+ join(agentDir, 'extensions', 'slash-commands', 'index.ts'),
64
+ join(agentDir, 'extensions', 'subagent', 'index.ts'),
65
+ join(agentDir, 'extensions', 'mac-tools', 'index.ts'),
66
+ join(agentDir, 'extensions', 'ask-user-questions.ts'),
67
+ join(agentDir, 'extensions', 'get-secrets-from-user.ts'),
68
+ ].join(':');
69
+ // Dynamic import defers ESM evaluation — config.js will see PI_PACKAGE_DIR above
70
+ await import('./cli.js');
package/dist/logo.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Shared GSD block-letter ASCII logo.
3
+ *
4
+ * Single source of truth — imported by:
5
+ * - scripts/postinstall.js (via dist/logo.js)
6
+ * - src/onboarding.ts (via ./logo.js)
7
+ */
8
+ /** Raw logo lines — no ANSI codes, no leading newline. */
9
+ export declare const GSD_LOGO: string[];
10
+ /**
11
+ * Render the logo block with a color function applied to each line.
12
+ *
13
+ * @param color — e.g. picocolors.cyan or `(s) => `\x1b[36m${s}\x1b[0m``
14
+ * @returns Ready-to-write string with leading/trailing newlines.
15
+ */
16
+ export declare function renderLogo(color: (s: string) => string): string;
package/dist/logo.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared GSD block-letter ASCII logo.
3
+ *
4
+ * Single source of truth — imported by:
5
+ * - scripts/postinstall.js (via dist/logo.js)
6
+ * - src/onboarding.ts (via ./logo.js)
7
+ */
8
+ /** Raw logo lines — no ANSI codes, no leading newline. */
9
+ export const GSD_LOGO = [
10
+ ' ██████╗ ███████╗██████╗ ',
11
+ ' ██╔════╝ ██╔════╝██╔══██╗',
12
+ ' ██║ ███╗███████╗██║ ██║',
13
+ ' ██║ ██║╚════██║██║ ██║',
14
+ ' ╚██████╔╝███████║██████╔╝',
15
+ ' ╚═════╝ ╚══════╝╚═════╝ ',
16
+ ];
17
+ /**
18
+ * Render the logo block with a color function applied to each line.
19
+ *
20
+ * @param color — e.g. picocolors.cyan or `(s) => `\x1b[36m${s}\x1b[0m``
21
+ * @returns Ready-to-write string with leading/trailing newlines.
22
+ */
23
+ export function renderLogo(color) {
24
+ return '\n' + GSD_LOGO.map(color).join('\n') + '\n';
25
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Unified first-run onboarding wizard.
3
+ *
4
+ * Replaces the raw API-key-only wizard with a branded, clack-based experience
5
+ * that guides users through LLM provider authentication before the TUI launches.
6
+ *
7
+ * Flow: logo -> choose LLM provider -> authenticate (OAuth or API key) ->
8
+ * optional tool keys -> summary -> TUI launches.
9
+ *
10
+ * All steps are skippable. All errors are recoverable. Never crashes boot.
11
+ */
12
+ import type { AuthStorage } from '@mariozechner/pi-coding-agent';
13
+ /**
14
+ * Determine if the onboarding wizard should run.
15
+ *
16
+ * Returns true when:
17
+ * - No LLM provider has credentials in authStorage
18
+ * - We're on a TTY (interactive terminal)
19
+ *
20
+ * Returns false (skip wizard) when:
21
+ * - Any LLM provider is already authed (returning user)
22
+ * - Not a TTY (piped input, subagent, CI)
23
+ */
24
+ export declare function shouldRunOnboarding(authStorage: AuthStorage): boolean;
25
+ /**
26
+ * Run the unified onboarding wizard.
27
+ *
28
+ * Walks the user through:
29
+ * 1. Choose LLM provider
30
+ * 2. Authenticate (OAuth or API key)
31
+ * 3. Optional tool API keys
32
+ * 4. Summary
33
+ *
34
+ * All steps are skippable. All errors are recoverable.
35
+ * Writes status to stderr during execution.
36
+ */
37
+ export declare function runOnboarding(authStorage: AuthStorage): Promise<void>;
38
+ /**
39
+ * Hydrate process.env from stored auth.json credentials for optional tool keys.
40
+ * Runs on every launch so extensions see Brave/Context7/Jina keys stored via the
41
+ * wizard on prior launches.
42
+ */
43
+ export declare function loadStoredEnvKeys(authStorage: AuthStorage): void;