get-shit-done-cc 1.10.0-experimental.0 → 1.10.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/README.md +16 -17
- package/agents/gsd-executor.md +375 -37
- package/agents/gsd-planner.md +108 -15
- package/bin/install.js +163 -238
- package/commands/gsd/help.md +0 -43
- package/commands/gsd/new-project.md +8 -94
- package/commands/gsd/plan-phase.md +5 -35
- package/get-shit-done/references/verification-patterns.md +1 -1
- package/get-shit-done/templates/phase-prompt.md +4 -4
- package/get-shit-done/templates/state.md +0 -37
- package/get-shit-done/workflows/execute-phase.md +1 -44
- package/get-shit-done/workflows/execute-plan.md +856 -34
- package/hooks/dist/gsd-statusline.js +9 -6
- package/package.json +7 -10
- package/agents/design-specialist.md +0 -222
- package/commands/gsd/autopilot.md +0 -518
- package/commands/gsd/checkpoints.md +0 -229
- package/commands/gsd/design-system.md +0 -70
- package/commands/gsd/discuss-design.md +0 -77
- package/commands/gsd/extend.md +0 -80
- package/get-shit-done/references/ccr-integration.md +0 -468
- package/get-shit-done/references/checkpoint-execution.md +0 -369
- package/get-shit-done/references/checkpoint-types.md +0 -728
- package/get-shit-done/references/deviation-rules.md +0 -215
- package/get-shit-done/references/framework-patterns.md +0 -543
- package/get-shit-done/references/ui-principles.md +0 -258
- package/get-shit-done/skills/gsd-extend/SKILL.md +0 -154
- package/get-shit-done/skills/gsd-extend/references/agent-structure.md +0 -305
- package/get-shit-done/skills/gsd-extend/references/extension-anatomy.md +0 -123
- package/get-shit-done/skills/gsd-extend/references/reference-structure.md +0 -408
- package/get-shit-done/skills/gsd-extend/references/template-structure.md +0 -370
- package/get-shit-done/skills/gsd-extend/references/validation-rules.md +0 -140
- package/get-shit-done/skills/gsd-extend/references/workflow-structure.md +0 -253
- package/get-shit-done/skills/gsd-extend/templates/agent-template.md +0 -234
- package/get-shit-done/skills/gsd-extend/templates/reference-template.md +0 -239
- package/get-shit-done/skills/gsd-extend/templates/workflow-template.md +0 -169
- package/get-shit-done/skills/gsd-extend/workflows/create-approach.md +0 -332
- package/get-shit-done/skills/gsd-extend/workflows/list-extensions.md +0 -133
- package/get-shit-done/skills/gsd-extend/workflows/remove-extension.md +0 -93
- package/get-shit-done/skills/gsd-extend/workflows/validate-extension.md +0 -184
- package/get-shit-done/templates/autopilot-script-simple.sh +0 -181
- package/get-shit-done/templates/autopilot-script.sh +0 -1142
- package/get-shit-done/templates/autopilot-script.sh.backup +0 -1142
- package/get-shit-done/templates/design-system.md +0 -238
- package/get-shit-done/templates/phase-design.md +0 -205
- package/get-shit-done/templates/phase-models-template.json +0 -71
- package/get-shit-done/tui/App.tsx +0 -169
- package/get-shit-done/tui/README.md +0 -107
- package/get-shit-done/tui/build.js +0 -37
- package/get-shit-done/tui/components/ActivityFeed.tsx +0 -126
- package/get-shit-done/tui/components/PhaseCard.tsx +0 -86
- package/get-shit-done/tui/components/StatsBar.tsx +0 -147
- package/get-shit-done/tui/dist/index.js +0 -387
- package/get-shit-done/tui/index.tsx +0 -12
- package/get-shit-done/tui/package-lock.json +0 -1074
- package/get-shit-done/tui/package.json +0 -22
- package/get-shit-done/tui/utils/pipeReader.ts +0 -129
- package/get-shit-done/workflows/design-system.md +0 -245
- package/get-shit-done/workflows/discuss-design.md +0 -330
- package/get-shit-done/workflows/execute-plan-auth.md +0 -122
- package/get-shit-done/workflows/execute-plan-checkpoints.md +0 -541
package/bin/install.js
CHANGED
|
@@ -21,22 +21,28 @@ const hasGlobal = args.includes('--global') || args.includes('-g');
|
|
|
21
21
|
const hasLocal = args.includes('--local') || args.includes('-l');
|
|
22
22
|
const hasOpencode = args.includes('--opencode');
|
|
23
23
|
const hasClaude = args.includes('--claude');
|
|
24
|
-
const
|
|
24
|
+
const hasGemini = args.includes('--gemini');
|
|
25
|
+
const hasBoth = args.includes('--both'); // Legacy flag, keeps working
|
|
26
|
+
const hasAll = args.includes('--all');
|
|
25
27
|
const hasUninstall = args.includes('--uninstall') || args.includes('-u');
|
|
26
28
|
|
|
27
29
|
// Runtime selection - can be set by flags or interactive prompt
|
|
28
30
|
let selectedRuntimes = [];
|
|
29
|
-
if (
|
|
31
|
+
if (hasAll) {
|
|
32
|
+
selectedRuntimes = ['claude', 'opencode', 'gemini'];
|
|
33
|
+
} else if (hasBoth) {
|
|
30
34
|
selectedRuntimes = ['claude', 'opencode'];
|
|
31
|
-
} else
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
} else {
|
|
36
|
+
if (hasOpencode) selectedRuntimes.push('opencode');
|
|
37
|
+
if (hasClaude) selectedRuntimes.push('claude');
|
|
38
|
+
if (hasGemini) selectedRuntimes.push('gemini');
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
// Helper to get directory name for a runtime (used for local/project installs)
|
|
38
42
|
function getDirName(runtime) {
|
|
39
|
-
|
|
43
|
+
if (runtime === 'opencode') return '.opencode';
|
|
44
|
+
if (runtime === 'gemini') return '.gemini';
|
|
45
|
+
return '.claude';
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
/**
|
|
@@ -66,7 +72,7 @@ function getOpencodeGlobalDir() {
|
|
|
66
72
|
|
|
67
73
|
/**
|
|
68
74
|
* Get the global config directory for a runtime
|
|
69
|
-
* @param {string} runtime - 'claude' or '
|
|
75
|
+
* @param {string} runtime - 'claude', 'opencode', or 'gemini'
|
|
70
76
|
* @param {string|null} explicitDir - Explicit directory from --config-dir flag
|
|
71
77
|
*/
|
|
72
78
|
function getGlobalDir(runtime, explicitDir = null) {
|
|
@@ -78,6 +84,17 @@ function getGlobalDir(runtime, explicitDir = null) {
|
|
|
78
84
|
return getOpencodeGlobalDir();
|
|
79
85
|
}
|
|
80
86
|
|
|
87
|
+
if (runtime === 'gemini') {
|
|
88
|
+
// Gemini: --config-dir > GEMINI_CONFIG_DIR > ~/.gemini
|
|
89
|
+
if (explicitDir) {
|
|
90
|
+
return expandTilde(explicitDir);
|
|
91
|
+
}
|
|
92
|
+
if (process.env.GEMINI_CONFIG_DIR) {
|
|
93
|
+
return expandTilde(process.env.GEMINI_CONFIG_DIR);
|
|
94
|
+
}
|
|
95
|
+
return path.join(os.homedir(), '.gemini');
|
|
96
|
+
}
|
|
97
|
+
|
|
81
98
|
// Claude Code: --config-dir > CLAUDE_CONFIG_DIR > ~/.claude
|
|
82
99
|
if (explicitDir) {
|
|
83
100
|
return expandTilde(explicitDir);
|
|
@@ -88,18 +105,17 @@ function getGlobalDir(runtime, explicitDir = null) {
|
|
|
88
105
|
return path.join(os.homedir(), '.claude');
|
|
89
106
|
}
|
|
90
107
|
|
|
91
|
-
const banner =
|
|
92
|
-
|
|
93
|
-
██╔════╝
|
|
94
|
-
██║ ███╗███████╗██║
|
|
95
|
-
██║ ██║╚════██║██║
|
|
96
|
-
|
|
97
|
-
╚═════╝
|
|
98
|
-
|
|
99
|
-
Get Shit Done
|
|
100
|
-
A meta-prompting, context engineering and spec-driven
|
|
101
|
-
development system for Claude Code
|
|
102
|
-
`;
|
|
108
|
+
const banner = '\n' +
|
|
109
|
+
cyan + ' ██████╗ ███████╗██████╗\n' +
|
|
110
|
+
' ██╔════╝ ██╔════╝██╔══██╗\n' +
|
|
111
|
+
' ██║ ███╗███████╗██║ ██║\n' +
|
|
112
|
+
' ██║ ██║╚════██║██║ ██║\n' +
|
|
113
|
+
' ╚██████╔╝███████║██████╔╝\n' +
|
|
114
|
+
' ╚═════╝ ╚══════╝╚═════╝' + reset + '\n' +
|
|
115
|
+
'\n' +
|
|
116
|
+
' Get Shit Done ' + dim + 'v' + pkg.version + reset + '\n' +
|
|
117
|
+
' A meta-prompting, context engineering and spec-driven\n' +
|
|
118
|
+
' development system for Claude Code, OpenCode, and Gemini by TÂCHES.\n';
|
|
103
119
|
|
|
104
120
|
// Parse --config-dir argument
|
|
105
121
|
function parseConfigDirArg() {
|
|
@@ -133,49 +149,7 @@ console.log(banner);
|
|
|
133
149
|
|
|
134
150
|
// Show help if requested
|
|
135
151
|
if (hasHelp) {
|
|
136
|
-
console.log(` ${yellow}Usage:${reset} npx get-shit-done-cc [options]
|
|
137
|
-
|
|
138
|
-
${yellow}Options:${reset}
|
|
139
|
-
${cyan}-g, --global${reset} Install globally (to config directory)
|
|
140
|
-
${cyan}-l, --local${reset} Install locally (to current directory)
|
|
141
|
-
${cyan}--claude${reset} Install for Claude Code only
|
|
142
|
-
${cyan}--opencode${reset} Install for OpenCode only
|
|
143
|
-
${cyan}--both${reset} Install for both Claude Code and OpenCode
|
|
144
|
-
${cyan}-u, --uninstall${reset} Uninstall GSD (remove all GSD files)
|
|
145
|
-
${cyan}-c, --config-dir <path>${reset} Specify custom config directory
|
|
146
|
-
${cyan}-h, --help${reset} Show this help message
|
|
147
|
-
${cyan}--force-statusline${reset} Replace existing statusline config
|
|
148
|
-
|
|
149
|
-
${yellow}Examples:${reset}
|
|
150
|
-
${dim}# Interactive install (prompts for runtime and location)${reset}
|
|
151
|
-
npx get-shit-done-cc
|
|
152
|
-
|
|
153
|
-
${dim}# Install for Claude Code globally${reset}
|
|
154
|
-
npx get-shit-done-cc --claude --global
|
|
155
|
-
|
|
156
|
-
${dim}# Install for OpenCode globally${reset}
|
|
157
|
-
npx get-shit-done-cc --opencode --global
|
|
158
|
-
|
|
159
|
-
${dim}# Install for both runtimes globally${reset}
|
|
160
|
-
npx get-shit-done-cc --both --global
|
|
161
|
-
|
|
162
|
-
${dim}# Install to custom config directory${reset}
|
|
163
|
-
npx get-shit-done-cc --claude --global --config-dir ~/.claude-bc
|
|
164
|
-
|
|
165
|
-
${dim}# Install to current project only${reset}
|
|
166
|
-
npx get-shit-done-cc --claude --local
|
|
167
|
-
|
|
168
|
-
${dim}# Uninstall GSD from Claude Code globally${reset}
|
|
169
|
-
npx get-shit-done-cc --claude --global --uninstall
|
|
170
|
-
|
|
171
|
-
${dim}# Uninstall GSD from current project${reset}
|
|
172
|
-
npx get-shit-done-cc --claude --local --uninstall
|
|
173
|
-
|
|
174
|
-
${yellow}Notes:${reset}
|
|
175
|
-
The --config-dir option is useful when you have multiple Claude Code
|
|
176
|
-
configurations (e.g., for different subscriptions). It takes priority
|
|
177
|
-
over the CLAUDE_CONFIG_DIR environment variable.
|
|
178
|
-
`);
|
|
152
|
+
console.log(` ${yellow}Usage:${reset} npx get-shit-done-cc [options]\n\n ${yellow}Options:${reset}\n ${cyan}-g, --global${reset} Install globally (to config directory)\n ${cyan}-l, --local${reset} Install locally (to current directory)\n ${cyan}--claude${reset} Install for Claude Code only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--gemini${reset} Install for Gemini only\n ${cyan}--all${reset} Install for all runtimes\n ${cyan}-u, --uninstall${reset} Uninstall GSD (remove all GSD files)\n ${cyan}-c, --config-dir <path>${reset} Specify custom config directory\n ${cyan}-h, --help${reset} Show this help message\n ${cyan}--force-statusline${reset} Replace existing statusline config\n\n ${yellow}Examples:${reset}\n ${dim}# Interactive install (prompts for runtime and location)${reset}\n npx get-shit-done-cc\n\n ${dim}# Install for Claude Code globally${reset}\n npx get-shit-done-cc --claude --global\n\n ${dim}# Install for Gemini globally${reset}\n npx get-shit-done-cc --gemini --global\n\n ${dim}# Install for all runtimes globally${reset}\n npx get-shit-done-cc --all --global\n\n ${dim}# Install to custom config directory${reset}\n npx get-shit-done-cc --claude --global --config-dir ~/.claude-bc\n\n ${dim}# Install to current project only${reset}\n npx get-shit-done-cc --claude --local\n\n ${dim}# Uninstall GSD from Claude Code globally${reset}\n npx get-shit-done-cc --claude --global --uninstall\n\n ${yellow}Notes:${reset}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over CLAUDE_CONFIG_DIR / GEMINI_CONFIG_DIR environment variables.\n`);
|
|
179
153
|
process.exit(0);
|
|
180
154
|
}
|
|
181
155
|
|
|
@@ -193,9 +167,9 @@ function expandTilde(filePath) {
|
|
|
193
167
|
* Build a hook command path using forward slashes for cross-platform compatibility.
|
|
194
168
|
* On Windows, $HOME is not expanded by cmd.exe/PowerShell, so we use the actual path.
|
|
195
169
|
*/
|
|
196
|
-
function buildHookCommand(
|
|
170
|
+
function buildHookCommand(configDir, hookName) {
|
|
197
171
|
// Use forward slashes for Node.js compatibility on all platforms
|
|
198
|
-
const hooksPath =
|
|
172
|
+
const hooksPath = configDir.replace(/\\/g, '/') + '/hooks/' + hookName;
|
|
199
173
|
return `node "${hooksPath}"`;
|
|
200
174
|
}
|
|
201
175
|
|
|
@@ -371,6 +345,47 @@ function convertClaudeToOpencodeFrontmatter(content) {
|
|
|
371
345
|
return `---\n${newFrontmatter}\n---${body}`;
|
|
372
346
|
}
|
|
373
347
|
|
|
348
|
+
/**
|
|
349
|
+
* Convert Claude Code markdown command to Gemini TOML format
|
|
350
|
+
* @param {string} content - Markdown file content with YAML frontmatter
|
|
351
|
+
* @returns {string} - TOML content
|
|
352
|
+
*/
|
|
353
|
+
function convertClaudeToGeminiToml(content) {
|
|
354
|
+
// Check if content has frontmatter
|
|
355
|
+
if (!content.startsWith('---')) {
|
|
356
|
+
return `prompt = ${JSON.stringify(content)}\n`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const endIndex = content.indexOf('---', 3);
|
|
360
|
+
if (endIndex === -1) {
|
|
361
|
+
return `prompt = ${JSON.stringify(content)}\n`;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const frontmatter = content.substring(3, endIndex).trim();
|
|
365
|
+
const body = content.substring(endIndex + 3).trim();
|
|
366
|
+
|
|
367
|
+
// Extract description from frontmatter
|
|
368
|
+
let description = '';
|
|
369
|
+
const lines = frontmatter.split('\n');
|
|
370
|
+
for (const line of lines) {
|
|
371
|
+
const trimmed = line.trim();
|
|
372
|
+
if (trimmed.startsWith('description:')) {
|
|
373
|
+
description = trimmed.substring(12).trim();
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Construct TOML
|
|
379
|
+
let toml = '';
|
|
380
|
+
if (description) {
|
|
381
|
+
toml += `description = ${JSON.stringify(description)}\n`;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
toml += `prompt = ${JSON.stringify(body)}\n`;
|
|
385
|
+
|
|
386
|
+
return toml;
|
|
387
|
+
}
|
|
388
|
+
|
|
374
389
|
/**
|
|
375
390
|
* Copy commands to a flat structure for OpenCode
|
|
376
391
|
* OpenCode expects: command/gsd-help.md (invoked as /gsd-help)
|
|
@@ -412,17 +427,14 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
|
|
|
412
427
|
const baseName = entry.name.replace('.md', '');
|
|
413
428
|
const destName = `${prefix}-${baseName}.md`;
|
|
414
429
|
const destPath = path.join(destDir, destName);
|
|
415
|
-
|
|
416
|
-
// Read, transform, and write
|
|
430
|
+
|
|
417
431
|
let content = fs.readFileSync(srcPath, 'utf8');
|
|
418
|
-
// Replace path references
|
|
419
432
|
const claudeDirRegex = /~\/\.claude\//g;
|
|
420
433
|
const opencodeDirRegex = /~\/\.opencode\//g;
|
|
421
434
|
content = content.replace(claudeDirRegex, pathPrefix);
|
|
422
435
|
content = content.replace(opencodeDirRegex, pathPrefix);
|
|
423
|
-
// Convert frontmatter for opencode compatibility
|
|
424
436
|
content = convertClaudeToOpencodeFrontmatter(content);
|
|
425
|
-
|
|
437
|
+
|
|
426
438
|
fs.writeFileSync(destPath, content);
|
|
427
439
|
}
|
|
428
440
|
}
|
|
@@ -434,7 +446,7 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
|
|
|
434
446
|
* @param {string} srcDir - Source directory
|
|
435
447
|
* @param {string} destDir - Destination directory
|
|
436
448
|
* @param {string} pathPrefix - Path prefix for file references
|
|
437
|
-
* @param {string} runtime - Target runtime ('claude'
|
|
449
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'gemini')
|
|
438
450
|
*/
|
|
439
451
|
function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
|
|
440
452
|
const isOpencode = runtime === 'opencode';
|
|
@@ -455,15 +467,24 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
|
|
|
455
467
|
if (entry.isDirectory()) {
|
|
456
468
|
copyWithPathReplacement(srcPath, destPath, pathPrefix, runtime);
|
|
457
469
|
} else if (entry.name.endsWith('.md')) {
|
|
458
|
-
//
|
|
470
|
+
// Always replace ~/.claude/ as it is the source of truth in the repo
|
|
459
471
|
let content = fs.readFileSync(srcPath, 'utf8');
|
|
460
|
-
const claudeDirRegex =
|
|
472
|
+
const claudeDirRegex = /~\/\.claude\//g;
|
|
461
473
|
content = content.replace(claudeDirRegex, pathPrefix);
|
|
474
|
+
|
|
462
475
|
// Convert frontmatter for opencode compatibility
|
|
463
476
|
if (isOpencode) {
|
|
464
477
|
content = convertClaudeToOpencodeFrontmatter(content);
|
|
478
|
+
fs.writeFileSync(destPath, content);
|
|
479
|
+
} else if (runtime === 'gemini') {
|
|
480
|
+
// Convert to TOML for Gemini
|
|
481
|
+
const tomlContent = convertClaudeToGeminiToml(content);
|
|
482
|
+
// Replace extension with .toml
|
|
483
|
+
const tomlPath = destPath.replace(/\.md$/, '.toml');
|
|
484
|
+
fs.writeFileSync(tomlPath, tomlContent);
|
|
485
|
+
} else {
|
|
486
|
+
fs.writeFileSync(destPath, content);
|
|
465
487
|
}
|
|
466
|
-
fs.writeFileSync(destPath, content);
|
|
467
488
|
} else {
|
|
468
489
|
fs.copyFileSync(srcPath, destPath);
|
|
469
490
|
}
|
|
@@ -473,14 +494,14 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime) {
|
|
|
473
494
|
/**
|
|
474
495
|
* Clean up orphaned files from previous GSD versions
|
|
475
496
|
*/
|
|
476
|
-
function cleanupOrphanedFiles(
|
|
497
|
+
function cleanupOrphanedFiles(configDir) {
|
|
477
498
|
const orphanedFiles = [
|
|
478
499
|
'hooks/gsd-notify.sh', // Removed in v1.6.x
|
|
479
500
|
'hooks/statusline.js', // Renamed to gsd-statusline.js in v1.9.0
|
|
480
501
|
];
|
|
481
502
|
|
|
482
503
|
for (const relPath of orphanedFiles) {
|
|
483
|
-
const fullPath = path.join(
|
|
504
|
+
const fullPath = path.join(configDir, relPath);
|
|
484
505
|
if (fs.existsSync(fullPath)) {
|
|
485
506
|
fs.unlinkSync(fullPath);
|
|
486
507
|
console.log(` ${green}✓${reset} Removed orphaned ${relPath}`);
|
|
@@ -537,7 +558,7 @@ function cleanupOrphanedHooks(settings) {
|
|
|
537
558
|
* Uninstall GSD from the specified directory for a specific runtime
|
|
538
559
|
* Removes only GSD-specific files/directories, preserves user content
|
|
539
560
|
* @param {boolean} isGlobal - Whether to uninstall from global or local
|
|
540
|
-
* @param {string} runtime - Target runtime ('claude'
|
|
561
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'gemini')
|
|
541
562
|
*/
|
|
542
563
|
function uninstall(isGlobal, runtime = 'claude') {
|
|
543
564
|
const isOpencode = runtime === 'opencode';
|
|
@@ -552,7 +573,10 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
552
573
|
? targetDir.replace(os.homedir(), '~')
|
|
553
574
|
: targetDir.replace(process.cwd(), '.');
|
|
554
575
|
|
|
555
|
-
|
|
576
|
+
let runtimeLabel = 'Claude Code';
|
|
577
|
+
if (runtime === 'opencode') runtimeLabel = 'OpenCode';
|
|
578
|
+
if (runtime === 'gemini') runtimeLabel = 'Gemini';
|
|
579
|
+
|
|
556
580
|
console.log(` Uninstalling GSD from ${cyan}${runtimeLabel}${reset} at ${cyan}${locationLabel}${reset}\n`);
|
|
557
581
|
|
|
558
582
|
// Check if target directory exists
|
|
@@ -579,7 +603,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
579
603
|
console.log(` ${green}✓${reset} Removed GSD commands from command/`);
|
|
580
604
|
}
|
|
581
605
|
} else {
|
|
582
|
-
// Claude Code: remove commands/gsd/ directory
|
|
606
|
+
// Claude Code & Gemini: remove commands/gsd/ directory
|
|
583
607
|
const gsdCommandsDir = path.join(targetDir, 'commands', 'gsd');
|
|
584
608
|
if (fs.existsSync(gsdCommandsDir)) {
|
|
585
609
|
fs.rmSync(gsdCommandsDir, { recursive: true });
|
|
@@ -616,7 +640,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
616
640
|
// 4. Remove GSD hooks
|
|
617
641
|
const hooksDir = path.join(targetDir, 'hooks');
|
|
618
642
|
if (fs.existsSync(hooksDir)) {
|
|
619
|
-
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh'
|
|
643
|
+
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh'];
|
|
620
644
|
let hookCount = 0;
|
|
621
645
|
for (const hook of gsdHooks) {
|
|
622
646
|
const hookPath = path.join(hooksDir, hook);
|
|
@@ -660,42 +684,18 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
660
684
|
});
|
|
661
685
|
if (settings.hooks.SessionStart.length < before) {
|
|
662
686
|
settingsModified = true;
|
|
663
|
-
console.log(` ${green}✓${reset} Removed GSD
|
|
687
|
+
console.log(` ${green}✓${reset} Removed GSD hooks from settings`);
|
|
664
688
|
}
|
|
665
689
|
// Clean up empty array
|
|
666
690
|
if (settings.hooks.SessionStart.length === 0) {
|
|
667
691
|
delete settings.hooks.SessionStart;
|
|
668
692
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (settings.hooks && settings.hooks.PostToolUse) {
|
|
673
|
-
const before = settings.hooks.PostToolUse.length;
|
|
674
|
-
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(entry => {
|
|
675
|
-
if (entry.hooks && Array.isArray(entry.hooks)) {
|
|
676
|
-
// Filter out GSD hooks
|
|
677
|
-
const hasGsdHook = entry.hooks.some(h =>
|
|
678
|
-
h.command && h.command.includes('gsd-activity')
|
|
679
|
-
);
|
|
680
|
-
return !hasGsdHook;
|
|
681
|
-
}
|
|
682
|
-
return true;
|
|
683
|
-
});
|
|
684
|
-
if (settings.hooks.PostToolUse.length < before) {
|
|
685
|
-
settingsModified = true;
|
|
686
|
-
console.log(` ${green}✓${reset} Removed GSD PostToolUse hooks from settings`);
|
|
687
|
-
}
|
|
688
|
-
// Clean up empty array
|
|
689
|
-
if (settings.hooks.PostToolUse.length === 0) {
|
|
690
|
-
delete settings.hooks.PostToolUse;
|
|
693
|
+
// Clean up empty hooks object
|
|
694
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
695
|
+
delete settings.hooks;
|
|
691
696
|
}
|
|
692
697
|
}
|
|
693
698
|
|
|
694
|
-
// Clean up empty hooks object
|
|
695
|
-
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
|
|
696
|
-
delete settings.hooks;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
699
|
if (settingsModified) {
|
|
700
700
|
writeSettings(settingsPath, settings);
|
|
701
701
|
removedCount++;
|
|
@@ -853,11 +853,12 @@ function verifyFileInstalled(filePath, description) {
|
|
|
853
853
|
/**
|
|
854
854
|
* Install to the specified directory for a specific runtime
|
|
855
855
|
* @param {boolean} isGlobal - Whether to install globally or locally
|
|
856
|
-
* @param {string} runtime - Target runtime ('claude'
|
|
856
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'gemini')
|
|
857
857
|
*/
|
|
858
858
|
function install(isGlobal, runtime = 'claude') {
|
|
859
859
|
const isOpencode = runtime === 'opencode';
|
|
860
|
-
const
|
|
860
|
+
const isGemini = runtime === 'gemini';
|
|
861
|
+
const dirName = getDirName(runtime);
|
|
861
862
|
const src = path.join(__dirname, '..');
|
|
862
863
|
|
|
863
864
|
// Get the target directory based on runtime and install type
|
|
@@ -870,13 +871,16 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
870
871
|
: targetDir.replace(process.cwd(), '.');
|
|
871
872
|
|
|
872
873
|
// Path prefix for file references in markdown content
|
|
873
|
-
// For global installs: use full path
|
|
874
|
-
// For local installs: use relative
|
|
874
|
+
// For global installs: use full path
|
|
875
|
+
// For local installs: use relative
|
|
875
876
|
const pathPrefix = isGlobal
|
|
876
877
|
? `${targetDir}/`
|
|
877
878
|
: `./${dirName}/`;
|
|
878
879
|
|
|
879
|
-
|
|
880
|
+
let runtimeLabel = 'Claude Code';
|
|
881
|
+
if (isOpencode) runtimeLabel = 'OpenCode';
|
|
882
|
+
if (isGemini) runtimeLabel = 'Gemini';
|
|
883
|
+
|
|
880
884
|
console.log(` Installing for ${cyan}${runtimeLabel}${reset} to ${cyan}${locationLabel}${reset}\n`);
|
|
881
885
|
|
|
882
886
|
// Track installation failures
|
|
@@ -885,8 +889,8 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
885
889
|
// Clean up orphaned files from previous versions
|
|
886
890
|
cleanupOrphanedFiles(targetDir);
|
|
887
891
|
|
|
888
|
-
// OpenCode uses 'command/' (singular) with flat structure
|
|
889
|
-
// Claude Code
|
|
892
|
+
// OpenCode uses 'command/' (singular) with flat structure
|
|
893
|
+
// Claude Code & Gemini use 'commands/' (plural) with nested structure
|
|
890
894
|
if (isOpencode) {
|
|
891
895
|
// OpenCode: flat structure in command/ directory
|
|
892
896
|
const commandDir = path.join(targetDir, 'command');
|
|
@@ -902,7 +906,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
902
906
|
failures.push('command/gsd-*');
|
|
903
907
|
}
|
|
904
908
|
} else {
|
|
905
|
-
// Claude Code: nested structure in commands/ directory
|
|
909
|
+
// Claude Code & Gemini: nested structure in commands/ directory
|
|
906
910
|
const commandsDir = path.join(targetDir, 'commands');
|
|
907
911
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
908
912
|
|
|
@@ -926,8 +930,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
926
930
|
failures.push('get-shit-done');
|
|
927
931
|
}
|
|
928
932
|
|
|
929
|
-
// Copy agents to agents directory
|
|
930
|
-
// Only delete gsd-*.md files to preserve user's custom agents
|
|
933
|
+
// Copy agents to agents directory
|
|
931
934
|
const agentsSrc = path.join(src, 'agents');
|
|
932
935
|
if (fs.existsSync(agentsSrc)) {
|
|
933
936
|
const agentsDest = path.join(targetDir, 'agents');
|
|
@@ -942,12 +945,13 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
942
945
|
}
|
|
943
946
|
}
|
|
944
947
|
|
|
945
|
-
// Copy new agents
|
|
948
|
+
// Copy new agents
|
|
946
949
|
const agentEntries = fs.readdirSync(agentsSrc, { withFileTypes: true });
|
|
947
950
|
for (const entry of agentEntries) {
|
|
948
951
|
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
949
952
|
let content = fs.readFileSync(path.join(agentsSrc, entry.name), 'utf8');
|
|
950
|
-
|
|
953
|
+
// Always replace ~/.claude/ as it is the source of truth in the repo
|
|
954
|
+
const dirRegex = /~\/\.claude\//g;
|
|
951
955
|
content = content.replace(dirRegex, pathPrefix);
|
|
952
956
|
// Convert frontmatter for opencode compatibility
|
|
953
957
|
if (isOpencode) {
|
|
@@ -975,7 +979,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
975
979
|
}
|
|
976
980
|
}
|
|
977
981
|
|
|
978
|
-
// Write VERSION file
|
|
982
|
+
// Write VERSION file
|
|
979
983
|
const versionDest = path.join(targetDir, 'get-shit-done', 'VERSION');
|
|
980
984
|
fs.writeFileSync(versionDest, pkg.version);
|
|
981
985
|
if (verifyFileInstalled(versionDest, 'VERSION')) {
|
|
@@ -992,7 +996,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
992
996
|
const hookEntries = fs.readdirSync(hooksSrc);
|
|
993
997
|
for (const entry of hookEntries) {
|
|
994
998
|
const srcFile = path.join(hooksSrc, entry);
|
|
995
|
-
// Only copy files, not directories
|
|
996
999
|
if (fs.statSync(srcFile).isFile()) {
|
|
997
1000
|
const destFile = path.join(hooksDest, entry);
|
|
998
1001
|
fs.copyFileSync(srcFile, destFile);
|
|
@@ -1005,30 +1008,13 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1005
1008
|
}
|
|
1006
1009
|
}
|
|
1007
1010
|
|
|
1008
|
-
// Copy GSD activity hook for autopilot real-time display
|
|
1009
|
-
const activityHookSrc = path.join(src, 'hooks', 'gsd-activity.sh');
|
|
1010
|
-
if (fs.existsSync(activityHookSrc)) {
|
|
1011
|
-
const hooksDest = path.join(targetDir, 'hooks');
|
|
1012
|
-
fs.mkdirSync(hooksDest, { recursive: true });
|
|
1013
|
-
const activityHookDest = path.join(hooksDest, 'gsd-activity.sh');
|
|
1014
|
-
fs.copyFileSync(activityHookSrc, activityHookDest);
|
|
1015
|
-
// Make executable on Unix systems
|
|
1016
|
-
try {
|
|
1017
|
-
fs.chmodSync(activityHookDest, 0o755);
|
|
1018
|
-
} catch (e) {
|
|
1019
|
-
// Windows doesn't support chmod
|
|
1020
|
-
}
|
|
1021
|
-
console.log(` ${green}✓${reset} Installed gsd-activity.sh hook`);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// If critical components failed, exit with error
|
|
1025
1011
|
if (failures.length > 0) {
|
|
1026
1012
|
console.error(`\n ${yellow}Installation incomplete!${reset} Failed: ${failures.join(', ')}`);
|
|
1027
|
-
console.error(` Try running directly: node ~/.npm/_npx/*/node_modules/get-shit-done-cc/bin/install.js --global\n`);
|
|
1028
1013
|
process.exit(1);
|
|
1029
1014
|
}
|
|
1030
1015
|
|
|
1031
1016
|
// Configure statusline and hooks in settings.json
|
|
1017
|
+
// Gemini shares same hook system as Claude Code for now
|
|
1032
1018
|
const settingsPath = path.join(targetDir, 'settings.json');
|
|
1033
1019
|
const settings = cleanupOrphanedHooks(readSettings(settingsPath));
|
|
1034
1020
|
const statuslineCommand = isGlobal
|
|
@@ -1038,7 +1024,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1038
1024
|
? buildHookCommand(targetDir, 'gsd-check-update.js')
|
|
1039
1025
|
: 'node ' + dirName + '/hooks/gsd-check-update.js';
|
|
1040
1026
|
|
|
1041
|
-
// Configure SessionStart hook for update checking (skip for opencode
|
|
1027
|
+
// Configure SessionStart hook for update checking (skip for opencode)
|
|
1042
1028
|
if (!isOpencode) {
|
|
1043
1029
|
if (!settings.hooks) {
|
|
1044
1030
|
settings.hooks = {};
|
|
@@ -1047,7 +1033,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1047
1033
|
settings.hooks.SessionStart = [];
|
|
1048
1034
|
}
|
|
1049
1035
|
|
|
1050
|
-
// Check if GSD update hook already exists
|
|
1051
1036
|
const hasGsdUpdateHook = settings.hooks.SessionStart.some(entry =>
|
|
1052
1037
|
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-check-update'))
|
|
1053
1038
|
);
|
|
@@ -1063,53 +1048,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1063
1048
|
});
|
|
1064
1049
|
console.log(` ${green}✓${reset} Configured update check hook`);
|
|
1065
1050
|
}
|
|
1066
|
-
|
|
1067
|
-
// Configure PostToolUse hook for autopilot activity display
|
|
1068
|
-
if (!settings.hooks.PostToolUse) {
|
|
1069
|
-
settings.hooks.PostToolUse = [];
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
// Build activity hook command path
|
|
1073
|
-
const activityHookCommand = isGlobal
|
|
1074
|
-
? `bash "${targetDir.replace(/\\/g, '/')}/hooks/gsd-activity.sh"`
|
|
1075
|
-
: `bash ${dirName}/hooks/gsd-activity.sh`;
|
|
1076
|
-
|
|
1077
|
-
// Check if GSD activity hook already exists (handles both old and new format)
|
|
1078
|
-
let hasGsdActivityHook = false;
|
|
1079
|
-
|
|
1080
|
-
// Remove old-format GSD activity hooks if present
|
|
1081
|
-
const beforeCount = settings.hooks.PostToolUse.length;
|
|
1082
|
-
settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter(entry => {
|
|
1083
|
-
// Check if this is a new-format GSD hook (with hooks array)
|
|
1084
|
-
if (entry.hooks && Array.isArray(entry.hooks)) {
|
|
1085
|
-
const isGsdHook = entry.hooks.some(h => h.command && h.command.includes('gsd-activity'));
|
|
1086
|
-
if (isGsdHook) {
|
|
1087
|
-
hasGsdActivityHook = true;
|
|
1088
|
-
return true; // Keep new-format hooks
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
// Check if this is an old-format GSD hook (with direct command field)
|
|
1092
|
-
const isOldGsdHook = entry.command && entry.command.includes('gsd-activity');
|
|
1093
|
-
if (isOldGsdHook) {
|
|
1094
|
-
return false; // Remove old-format hooks
|
|
1095
|
-
}
|
|
1096
|
-
// Keep non-GSD hooks
|
|
1097
|
-
return true;
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
// Add new-format hook if not found
|
|
1101
|
-
if (!hasGsdActivityHook) {
|
|
1102
|
-
settings.hooks.PostToolUse.push({
|
|
1103
|
-
matcher: "Task|Write|Edit|Read|Bash|TodoWrite",
|
|
1104
|
-
hooks: [
|
|
1105
|
-
{
|
|
1106
|
-
type: "command",
|
|
1107
|
-
command: activityHookCommand
|
|
1108
|
-
}
|
|
1109
|
-
]
|
|
1110
|
-
});
|
|
1111
|
-
console.log(` ${green}✓${reset} Configured autopilot activity hook`);
|
|
1112
|
-
}
|
|
1113
1051
|
}
|
|
1114
1052
|
|
|
1115
1053
|
return { settingsPath, settings, statuslineCommand, runtime };
|
|
@@ -1117,11 +1055,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1117
1055
|
|
|
1118
1056
|
/**
|
|
1119
1057
|
* Apply statusline config, then print completion message
|
|
1120
|
-
* @param {string} settingsPath - Path to settings.json
|
|
1121
|
-
* @param {object} settings - Settings object
|
|
1122
|
-
* @param {string} statuslineCommand - Statusline command
|
|
1123
|
-
* @param {boolean} shouldInstallStatusline - Whether to install statusline
|
|
1124
|
-
* @param {string} runtime - Target runtime ('claude' or 'opencode')
|
|
1125
1058
|
*/
|
|
1126
1059
|
function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, runtime = 'claude') {
|
|
1127
1060
|
const isOpencode = runtime === 'opencode';
|
|
@@ -1134,15 +1067,18 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
1134
1067
|
console.log(` ${green}✓${reset} Configured statusline`);
|
|
1135
1068
|
}
|
|
1136
1069
|
|
|
1137
|
-
// Always write settings
|
|
1070
|
+
// Always write settings
|
|
1138
1071
|
writeSettings(settingsPath, settings);
|
|
1139
1072
|
|
|
1140
|
-
// Configure OpenCode permissions
|
|
1073
|
+
// Configure OpenCode permissions
|
|
1141
1074
|
if (isOpencode) {
|
|
1142
1075
|
configureOpencodePermissions();
|
|
1143
1076
|
}
|
|
1144
1077
|
|
|
1145
|
-
|
|
1078
|
+
let program = 'Claude Code';
|
|
1079
|
+
if (runtime === 'opencode') program = 'OpenCode';
|
|
1080
|
+
if (runtime === 'gemini') program = 'Gemini';
|
|
1081
|
+
|
|
1146
1082
|
const command = isOpencode ? '/gsd-help' : '/gsd:help';
|
|
1147
1083
|
console.log(`
|
|
1148
1084
|
${green}Done!${reset} Launch ${program} and run ${cyan}${command}${reset}.
|
|
@@ -1157,19 +1093,16 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
1157
1093
|
function handleStatusline(settings, isInteractive, callback) {
|
|
1158
1094
|
const hasExisting = settings.statusLine != null;
|
|
1159
1095
|
|
|
1160
|
-
// No existing statusline - just install it
|
|
1161
1096
|
if (!hasExisting) {
|
|
1162
1097
|
callback(true);
|
|
1163
1098
|
return;
|
|
1164
1099
|
}
|
|
1165
1100
|
|
|
1166
|
-
// Has existing and --force-statusline flag
|
|
1167
1101
|
if (forceStatusline) {
|
|
1168
1102
|
callback(true);
|
|
1169
1103
|
return;
|
|
1170
1104
|
}
|
|
1171
1105
|
|
|
1172
|
-
// Has existing, non-interactive mode - skip
|
|
1173
1106
|
if (!isInteractive) {
|
|
1174
1107
|
console.log(` ${yellow}⚠${reset} Skipping statusline (already configured)`);
|
|
1175
1108
|
console.log(` Use ${cyan}--force-statusline${reset} to replace\n`);
|
|
@@ -1177,7 +1110,6 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1177
1110
|
return;
|
|
1178
1111
|
}
|
|
1179
1112
|
|
|
1180
|
-
// Has existing, interactive mode - prompt user
|
|
1181
1113
|
const existingCmd = settings.statusLine.command || settings.statusLine.url || '(custom)';
|
|
1182
1114
|
|
|
1183
1115
|
const rl = readline.createInterface({
|
|
@@ -1186,8 +1118,7 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1186
1118
|
});
|
|
1187
1119
|
|
|
1188
1120
|
console.log(`
|
|
1189
|
-
${yellow}⚠${reset} Existing statusline detected
|
|
1190
|
-
|
|
1121
|
+
${yellow}⚠${reset} Existing statusline detected\n
|
|
1191
1122
|
Your current statusline:
|
|
1192
1123
|
${dim}command: ${existingCmd}${reset}
|
|
1193
1124
|
|
|
@@ -1208,8 +1139,7 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1208
1139
|
}
|
|
1209
1140
|
|
|
1210
1141
|
/**
|
|
1211
|
-
* Prompt for runtime selection
|
|
1212
|
-
* @param {function} callback - Called with array of selected runtimes
|
|
1142
|
+
* Prompt for runtime selection
|
|
1213
1143
|
*/
|
|
1214
1144
|
function promptRuntime(callback) {
|
|
1215
1145
|
const rl = readline.createInterface({
|
|
@@ -1227,19 +1157,20 @@ function promptRuntime(callback) {
|
|
|
1227
1157
|
}
|
|
1228
1158
|
});
|
|
1229
1159
|
|
|
1230
|
-
console.log(` ${yellow}Which runtime(s) would you like to install for?${reset}
|
|
1231
|
-
|
|
1232
|
-
${cyan}1${reset}) Claude Code ${dim}(~/.claude)${reset}
|
|
1160
|
+
console.log(` ${yellow}Which runtime(s) would you like to install for?${reset}\n\n ${cyan}1${reset}) Claude Code ${dim}(~/.claude)${reset}
|
|
1233
1161
|
${cyan}2${reset}) OpenCode ${dim}(~/.config/opencode)${reset} - open source, free models
|
|
1234
|
-
${cyan}3${reset})
|
|
1162
|
+
${cyan}3${reset}) Gemini ${dim}(~/.gemini)${reset}
|
|
1163
|
+
${cyan}4${reset}) All
|
|
1235
1164
|
`);
|
|
1236
1165
|
|
|
1237
1166
|
rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
|
|
1238
1167
|
answered = true;
|
|
1239
1168
|
rl.close();
|
|
1240
1169
|
const choice = answer.trim() || '1';
|
|
1241
|
-
if (choice === '
|
|
1242
|
-
callback(['claude', 'opencode']);
|
|
1170
|
+
if (choice === '4') {
|
|
1171
|
+
callback(['claude', 'opencode', 'gemini']);
|
|
1172
|
+
} else if (choice === '3') {
|
|
1173
|
+
callback(['gemini']);
|
|
1243
1174
|
} else if (choice === '2') {
|
|
1244
1175
|
callback(['opencode']);
|
|
1245
1176
|
} else {
|
|
@@ -1250,11 +1181,8 @@ function promptRuntime(callback) {
|
|
|
1250
1181
|
|
|
1251
1182
|
/**
|
|
1252
1183
|
* Prompt for install location
|
|
1253
|
-
* @param {string[]} runtimes - Array of runtimes to install for
|
|
1254
1184
|
*/
|
|
1255
1185
|
function promptLocation(runtimes) {
|
|
1256
|
-
// Check if stdin is a TTY - if not, fall back to global install
|
|
1257
|
-
// This handles npx execution in environments like WSL2 where stdin may not be properly connected
|
|
1258
1186
|
if (!process.stdin.isTTY) {
|
|
1259
1187
|
console.log(` ${yellow}Non-interactive terminal detected, defaulting to global install${reset}\n`);
|
|
1260
1188
|
installAllRuntimes(runtimes, true, false);
|
|
@@ -1266,10 +1194,8 @@ function promptLocation(runtimes) {
|
|
|
1266
1194
|
output: process.stdout
|
|
1267
1195
|
});
|
|
1268
1196
|
|
|
1269
|
-
// Track whether we've processed the answer to prevent double-execution
|
|
1270
1197
|
let answered = false;
|
|
1271
1198
|
|
|
1272
|
-
// Handle readline close event (Ctrl+C, Escape, etc.) - cancel installation
|
|
1273
1199
|
rl.on('close', () => {
|
|
1274
1200
|
if (!answered) {
|
|
1275
1201
|
answered = true;
|
|
@@ -1278,18 +1204,14 @@ function promptLocation(runtimes) {
|
|
|
1278
1204
|
}
|
|
1279
1205
|
});
|
|
1280
1206
|
|
|
1281
|
-
// Show paths for selected runtimes
|
|
1282
1207
|
const pathExamples = runtimes.map(r => {
|
|
1283
|
-
// Use the proper global directory function for each runtime
|
|
1284
1208
|
const globalPath = getGlobalDir(r, explicitConfigDir);
|
|
1285
1209
|
return globalPath.replace(os.homedir(), '~');
|
|
1286
1210
|
}).join(', ');
|
|
1287
1211
|
|
|
1288
1212
|
const localExamples = runtimes.map(r => `./${getDirName(r)}`).join(', ');
|
|
1289
1213
|
|
|
1290
|
-
console.log(` ${yellow}Where would you like to install?${reset}
|
|
1291
|
-
|
|
1292
|
-
${cyan}1${reset}) Global ${dim}(${pathExamples})${reset} - available in all projects
|
|
1214
|
+
console.log(` ${yellow}Where would you like to install?${reset}\n\n ${cyan}1${reset}) Global ${dim}(${pathExamples})${reset} - available in all projects
|
|
1293
1215
|
${cyan}2${reset}) Local ${dim}(${localExamples})${reset} - this project only
|
|
1294
1216
|
`);
|
|
1295
1217
|
|
|
@@ -1304,9 +1226,6 @@ function promptLocation(runtimes) {
|
|
|
1304
1226
|
|
|
1305
1227
|
/**
|
|
1306
1228
|
* Install GSD for all selected runtimes
|
|
1307
|
-
* @param {string[]} runtimes - Array of runtimes to install for
|
|
1308
|
-
* @param {boolean} isGlobal - Whether to install globally
|
|
1309
|
-
* @param {boolean} isInteractive - Whether running interactively
|
|
1310
1229
|
*/
|
|
1311
1230
|
function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
1312
1231
|
const results = [];
|
|
@@ -1316,14 +1235,25 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
|
1316
1235
|
results.push(result);
|
|
1317
1236
|
}
|
|
1318
1237
|
|
|
1319
|
-
// Handle statusline for Claude
|
|
1238
|
+
// Handle statusline for Claude & Gemini (OpenCode uses themes)
|
|
1320
1239
|
const claudeResult = results.find(r => r.runtime === 'claude');
|
|
1240
|
+
const geminiResult = results.find(r => r.runtime === 'gemini');
|
|
1321
1241
|
|
|
1322
|
-
if
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1242
|
+
// Logic: if both are present, ask once if interactive? Or ask for each?
|
|
1243
|
+
// Simpler: Ask once and apply to both if applicable.
|
|
1244
|
+
|
|
1245
|
+
if (claudeResult || geminiResult) {
|
|
1246
|
+
// Use whichever settings exist to check for existing statusline
|
|
1247
|
+
const primaryResult = claudeResult || geminiResult;
|
|
1248
|
+
|
|
1249
|
+
handleStatusline(primaryResult.settings, isInteractive, (shouldInstallStatusline) => {
|
|
1250
|
+
if (claudeResult) {
|
|
1251
|
+
finishInstall(claudeResult.settingsPath, claudeResult.settings, claudeResult.statuslineCommand, shouldInstallStatusline, 'claude');
|
|
1252
|
+
}
|
|
1253
|
+
if (geminiResult) {
|
|
1254
|
+
finishInstall(geminiResult.settingsPath, geminiResult.settings, geminiResult.statuslineCommand, shouldInstallStatusline, 'gemini');
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1327
1257
|
const opencodeResult = results.find(r => r.runtime === 'opencode');
|
|
1328
1258
|
if (opencodeResult) {
|
|
1329
1259
|
finishInstall(opencodeResult.settingsPath, opencodeResult.settings, opencodeResult.statuslineCommand, false, 'opencode');
|
|
@@ -1336,7 +1266,7 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
|
1336
1266
|
}
|
|
1337
1267
|
}
|
|
1338
1268
|
|
|
1339
|
-
// Main
|
|
1269
|
+
// Main logic
|
|
1340
1270
|
if (hasGlobal && hasLocal) {
|
|
1341
1271
|
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
1342
1272
|
process.exit(1);
|
|
@@ -1344,10 +1274,8 @@ if (hasGlobal && hasLocal) {
|
|
|
1344
1274
|
console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
|
|
1345
1275
|
process.exit(1);
|
|
1346
1276
|
} else if (hasUninstall) {
|
|
1347
|
-
// Uninstall mode
|
|
1348
1277
|
if (!hasGlobal && !hasLocal) {
|
|
1349
1278
|
console.error(` ${yellow}--uninstall requires --global or --local${reset}`);
|
|
1350
|
-
console.error(` Example: npx get-shit-done-cc --claude --global --uninstall`);
|
|
1351
1279
|
process.exit(1);
|
|
1352
1280
|
}
|
|
1353
1281
|
const runtimes = selectedRuntimes.length > 0 ? selectedRuntimes : ['claude'];
|
|
@@ -1355,19 +1283,16 @@ if (hasGlobal && hasLocal) {
|
|
|
1355
1283
|
uninstall(hasGlobal, runtime);
|
|
1356
1284
|
}
|
|
1357
1285
|
} else if (selectedRuntimes.length > 0) {
|
|
1358
|
-
// Non-interactive: runtime specified via flags
|
|
1359
1286
|
if (!hasGlobal && !hasLocal) {
|
|
1360
|
-
// Need location but runtime is specified - prompt for location only
|
|
1361
1287
|
promptLocation(selectedRuntimes);
|
|
1362
1288
|
} else {
|
|
1363
|
-
// Both runtime and location specified via flags
|
|
1364
1289
|
installAllRuntimes(selectedRuntimes, hasGlobal, false);
|
|
1365
1290
|
}
|
|
1366
1291
|
} else if (hasGlobal || hasLocal) {
|
|
1367
|
-
//
|
|
1292
|
+
// Default to Claude if no runtime specified but location is
|
|
1368
1293
|
installAllRuntimes(['claude'], hasGlobal, false);
|
|
1369
1294
|
} else {
|
|
1370
|
-
//
|
|
1295
|
+
// Interactive
|
|
1371
1296
|
if (!process.stdin.isTTY) {
|
|
1372
1297
|
console.log(` ${yellow}Non-interactive terminal detected, defaulting to Claude Code global install${reset}\n`);
|
|
1373
1298
|
installAllRuntimes(['claude'], true, false);
|