@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.
- package/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +269 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +70 -0
- package/dist/logo.d.ts +16 -0
- package/dist/logo.js +25 -0
- package/dist/onboarding.d.ts +43 -0
- package/dist/onboarding.js +418 -0
- package/dist/pi-migration.d.ts +14 -0
- package/dist/pi-migration.js +57 -0
- package/dist/resource-loader.d.ts +22 -0
- package/dist/resource-loader.js +60 -0
- package/dist/tool-bootstrap.d.ts +4 -0
- package/dist/tool-bootstrap.js +74 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.js +25 -0
- package/package.json +60 -0
- package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
- package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +127 -0
- package/src/resources/GSD-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +249 -0
- package/src/resources/extensions/bg-shell/index.ts +2808 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4989 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/google-search/index.ts +323 -0
- package/src/resources/extensions/google-search/package.json +9 -0
- package/src/resources/extensions/gsd/activity-log.ts +69 -0
- package/src/resources/extensions/gsd/auto.ts +2744 -0
- package/src/resources/extensions/gsd/commands.ts +313 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
- package/src/resources/extensions/gsd/doctor.ts +690 -0
- package/src/resources/extensions/gsd/files.ts +732 -0
- package/src/resources/extensions/gsd/git-service.ts +597 -0
- package/src/resources/extensions/gsd/gitignore.ts +168 -0
- package/src/resources/extensions/gsd/guided-flow.ts +817 -0
- package/src/resources/extensions/gsd/index.ts +558 -0
- package/src/resources/extensions/gsd/metrics.ts +374 -0
- package/src/resources/extensions/gsd/migrate/command.ts +218 -0
- package/src/resources/extensions/gsd/migrate/index.ts +42 -0
- package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
- package/src/resources/extensions/gsd/migrate/types.ts +370 -0
- package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
- package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
- package/src/resources/extensions/gsd/observability-validator.ts +408 -0
- package/src/resources/extensions/gsd/package.json +11 -0
- package/src/resources/extensions/gsd/paths.ts +308 -0
- package/src/resources/extensions/gsd/preferences.ts +757 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
- package/src/resources/extensions/gsd/prompts/queue.md +85 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
- package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
- package/src/resources/extensions/gsd/prompts/system.md +187 -0
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
- package/src/resources/extensions/gsd/session-forensics.ts +487 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
- package/src/resources/extensions/gsd/state.ts +460 -0
- package/src/resources/extensions/gsd/templates/context.md +76 -0
- package/src/resources/extensions/gsd/templates/decisions.md +8 -0
- package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/gsd/templates/plan.md +131 -0
- package/src/resources/extensions/gsd/templates/preferences.md +24 -0
- package/src/resources/extensions/gsd/templates/project.md +31 -0
- package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
- package/src/resources/extensions/gsd/templates/requirements.md +81 -0
- package/src/resources/extensions/gsd/templates/research.md +46 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
- package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
- package/src/resources/extensions/gsd/templates/state.md +19 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
- package/src/resources/extensions/gsd/templates/uat.md +54 -0
- package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
- package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
- package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
- package/src/resources/extensions/gsd/types.ts +159 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
- package/src/resources/extensions/gsd/workspace-index.ts +203 -0
- package/src/resources/extensions/gsd/worktree-command.ts +845 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
- package/src/resources/extensions/gsd/worktree.ts +183 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/mcporter/index.ts +429 -0
- package/src/resources/extensions/remote-questions/config.ts +81 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
- package/src/resources/extensions/remote-questions/format.ts +163 -0
- package/src/resources/extensions/remote-questions/manager.ts +192 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
- package/src/resources/extensions/remote-questions/status.ts +31 -0
- package/src/resources/extensions/remote-questions/store.ts +77 -0
- package/src/resources/extensions/remote-questions/types.ts +75 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +65 -0
- package/src/resources/extensions/search-the-web/native-search.ts +157 -0
- package/src/resources/extensions/search-the-web/provider.ts +118 -0
- package/src/resources/extensions/search-the-web/tavily.ts +116 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +613 -0
- package/src/resources/extensions/shared/next-action-ui.ts +197 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/terminal.ts +23 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +88 -0
- package/src/resources/extensions/slash-commands/clear.ts +10 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1020 -0
- package/src/resources/extensions/voice/index.ts +195 -0
- package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- 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();
|
package/dist/loader.d.ts
ADDED
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;
|