get-shit-done-cc 1.9.13 → 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 -9
- package/bin/install.js +158 -146
- package/hooks/dist/gsd-statusline.js +9 -6
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# GET SHIT DONE
|
|
4
4
|
|
|
5
|
-
**A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code and
|
|
5
|
+
**A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, and Gemini CLI.**
|
|
6
6
|
|
|
7
7
|
**Solves context rot — the quality degradation that happens as Claude fills its context window.**
|
|
8
8
|
|
|
@@ -77,10 +77,10 @@ npx get-shit-done-cc
|
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
The installer prompts you to choose:
|
|
80
|
-
1. **Runtime** — Claude Code, OpenCode, or
|
|
80
|
+
1. **Runtime** — Claude Code, OpenCode, Gemini, or all
|
|
81
81
|
2. **Location** — Global (all projects) or local (current project only)
|
|
82
82
|
|
|
83
|
-
Verify with `/gsd:help` inside your
|
|
83
|
+
Verify with `/gsd:help` inside your chosen runtime.
|
|
84
84
|
|
|
85
85
|
### Staying Updated
|
|
86
86
|
|
|
@@ -99,14 +99,17 @@ npx get-shit-done-cc --claude --global # Install to ~/.claude/
|
|
|
99
99
|
npx get-shit-done-cc --claude --local # Install to ./.claude/
|
|
100
100
|
|
|
101
101
|
# OpenCode (open source, free models)
|
|
102
|
-
npx get-shit-done-cc --opencode --global # Install to ~/.opencode/
|
|
102
|
+
npx get-shit-done-cc --opencode --global # Install to ~/.config/opencode/
|
|
103
103
|
|
|
104
|
-
#
|
|
105
|
-
npx get-shit-done-cc --
|
|
104
|
+
# Gemini CLI
|
|
105
|
+
npx get-shit-done-cc --gemini --global # Install to ~/.gemini/
|
|
106
|
+
|
|
107
|
+
# All runtimes
|
|
108
|
+
npx get-shit-done-cc --all --global # Install to all directories
|
|
106
109
|
```
|
|
107
110
|
|
|
108
111
|
Use `--global` (`-g`) or `--local` (`-l`) to skip the location prompt.
|
|
109
|
-
Use `--claude`, `--opencode`, or `--
|
|
112
|
+
Use `--claude`, `--opencode`, `--gemini`, or `--all` to skip the runtime prompt.
|
|
110
113
|
|
|
111
114
|
</details>
|
|
112
115
|
|
|
@@ -568,10 +571,14 @@ This removes all GSD commands, agents, hooks, and settings while preserving your
|
|
|
568
571
|
|
|
569
572
|
## Community Ports
|
|
570
573
|
|
|
574
|
+
OpenCode and Gemini CLI are now natively supported via `npx get-shit-done-cc`.
|
|
575
|
+
|
|
576
|
+
These community ports pioneered multi-runtime support:
|
|
577
|
+
|
|
571
578
|
| Project | Platform | Description |
|
|
572
579
|
|---------|----------|-------------|
|
|
573
|
-
| [gsd-opencode](https://github.com/rokicool/gsd-opencode) | OpenCode |
|
|
574
|
-
| [gsd-gemini](https://github.com/uberfuzzy/gsd-gemini) | Gemini CLI |
|
|
580
|
+
| [gsd-opencode](https://github.com/rokicool/gsd-opencode) | OpenCode | Original OpenCode adaptation |
|
|
581
|
+
| [gsd-gemini](https://github.com/uberfuzzy/gsd-gemini) | Gemini CLI | Original Gemini adaptation |
|
|
575
582
|
|
|
576
583
|
---
|
|
577
584
|
|
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 });
|
|
@@ -829,11 +853,12 @@ function verifyFileInstalled(filePath, description) {
|
|
|
829
853
|
/**
|
|
830
854
|
* Install to the specified directory for a specific runtime
|
|
831
855
|
* @param {boolean} isGlobal - Whether to install globally or locally
|
|
832
|
-
* @param {string} runtime - Target runtime ('claude'
|
|
856
|
+
* @param {string} runtime - Target runtime ('claude', 'opencode', 'gemini')
|
|
833
857
|
*/
|
|
834
858
|
function install(isGlobal, runtime = 'claude') {
|
|
835
859
|
const isOpencode = runtime === 'opencode';
|
|
836
|
-
const
|
|
860
|
+
const isGemini = runtime === 'gemini';
|
|
861
|
+
const dirName = getDirName(runtime);
|
|
837
862
|
const src = path.join(__dirname, '..');
|
|
838
863
|
|
|
839
864
|
// Get the target directory based on runtime and install type
|
|
@@ -846,13 +871,16 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
846
871
|
: targetDir.replace(process.cwd(), '.');
|
|
847
872
|
|
|
848
873
|
// Path prefix for file references in markdown content
|
|
849
|
-
// For global installs: use full path
|
|
850
|
-
// For local installs: use relative
|
|
874
|
+
// For global installs: use full path
|
|
875
|
+
// For local installs: use relative
|
|
851
876
|
const pathPrefix = isGlobal
|
|
852
877
|
? `${targetDir}/`
|
|
853
878
|
: `./${dirName}/`;
|
|
854
879
|
|
|
855
|
-
|
|
880
|
+
let runtimeLabel = 'Claude Code';
|
|
881
|
+
if (isOpencode) runtimeLabel = 'OpenCode';
|
|
882
|
+
if (isGemini) runtimeLabel = 'Gemini';
|
|
883
|
+
|
|
856
884
|
console.log(` Installing for ${cyan}${runtimeLabel}${reset} to ${cyan}${locationLabel}${reset}\n`);
|
|
857
885
|
|
|
858
886
|
// Track installation failures
|
|
@@ -861,8 +889,8 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
861
889
|
// Clean up orphaned files from previous versions
|
|
862
890
|
cleanupOrphanedFiles(targetDir);
|
|
863
891
|
|
|
864
|
-
// OpenCode uses 'command/' (singular) with flat structure
|
|
865
|
-
// Claude Code
|
|
892
|
+
// OpenCode uses 'command/' (singular) with flat structure
|
|
893
|
+
// Claude Code & Gemini use 'commands/' (plural) with nested structure
|
|
866
894
|
if (isOpencode) {
|
|
867
895
|
// OpenCode: flat structure in command/ directory
|
|
868
896
|
const commandDir = path.join(targetDir, 'command');
|
|
@@ -878,7 +906,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
878
906
|
failures.push('command/gsd-*');
|
|
879
907
|
}
|
|
880
908
|
} else {
|
|
881
|
-
// Claude Code: nested structure in commands/ directory
|
|
909
|
+
// Claude Code & Gemini: nested structure in commands/ directory
|
|
882
910
|
const commandsDir = path.join(targetDir, 'commands');
|
|
883
911
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
884
912
|
|
|
@@ -902,8 +930,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
902
930
|
failures.push('get-shit-done');
|
|
903
931
|
}
|
|
904
932
|
|
|
905
|
-
// Copy agents to agents directory
|
|
906
|
-
// Only delete gsd-*.md files to preserve user's custom agents
|
|
933
|
+
// Copy agents to agents directory
|
|
907
934
|
const agentsSrc = path.join(src, 'agents');
|
|
908
935
|
if (fs.existsSync(agentsSrc)) {
|
|
909
936
|
const agentsDest = path.join(targetDir, 'agents');
|
|
@@ -918,12 +945,13 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
918
945
|
}
|
|
919
946
|
}
|
|
920
947
|
|
|
921
|
-
// Copy new agents
|
|
948
|
+
// Copy new agents
|
|
922
949
|
const agentEntries = fs.readdirSync(agentsSrc, { withFileTypes: true });
|
|
923
950
|
for (const entry of agentEntries) {
|
|
924
951
|
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
925
952
|
let content = fs.readFileSync(path.join(agentsSrc, entry.name), 'utf8');
|
|
926
|
-
|
|
953
|
+
// Always replace ~/.claude/ as it is the source of truth in the repo
|
|
954
|
+
const dirRegex = /~\/\.claude\//g;
|
|
927
955
|
content = content.replace(dirRegex, pathPrefix);
|
|
928
956
|
// Convert frontmatter for opencode compatibility
|
|
929
957
|
if (isOpencode) {
|
|
@@ -951,7 +979,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
951
979
|
}
|
|
952
980
|
}
|
|
953
981
|
|
|
954
|
-
// Write VERSION file
|
|
982
|
+
// Write VERSION file
|
|
955
983
|
const versionDest = path.join(targetDir, 'get-shit-done', 'VERSION');
|
|
956
984
|
fs.writeFileSync(versionDest, pkg.version);
|
|
957
985
|
if (verifyFileInstalled(versionDest, 'VERSION')) {
|
|
@@ -968,7 +996,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
968
996
|
const hookEntries = fs.readdirSync(hooksSrc);
|
|
969
997
|
for (const entry of hookEntries) {
|
|
970
998
|
const srcFile = path.join(hooksSrc, entry);
|
|
971
|
-
// Only copy files, not directories
|
|
972
999
|
if (fs.statSync(srcFile).isFile()) {
|
|
973
1000
|
const destFile = path.join(hooksDest, entry);
|
|
974
1001
|
fs.copyFileSync(srcFile, destFile);
|
|
@@ -981,14 +1008,13 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
981
1008
|
}
|
|
982
1009
|
}
|
|
983
1010
|
|
|
984
|
-
// If critical components failed, exit with error
|
|
985
1011
|
if (failures.length > 0) {
|
|
986
1012
|
console.error(`\n ${yellow}Installation incomplete!${reset} Failed: ${failures.join(', ')}`);
|
|
987
|
-
console.error(` Try running directly: node ~/.npm/_npx/*/node_modules/get-shit-done-cc/bin/install.js --global\n`);
|
|
988
1013
|
process.exit(1);
|
|
989
1014
|
}
|
|
990
1015
|
|
|
991
1016
|
// Configure statusline and hooks in settings.json
|
|
1017
|
+
// Gemini shares same hook system as Claude Code for now
|
|
992
1018
|
const settingsPath = path.join(targetDir, 'settings.json');
|
|
993
1019
|
const settings = cleanupOrphanedHooks(readSettings(settingsPath));
|
|
994
1020
|
const statuslineCommand = isGlobal
|
|
@@ -998,7 +1024,7 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
998
1024
|
? buildHookCommand(targetDir, 'gsd-check-update.js')
|
|
999
1025
|
: 'node ' + dirName + '/hooks/gsd-check-update.js';
|
|
1000
1026
|
|
|
1001
|
-
// Configure SessionStart hook for update checking (skip for opencode
|
|
1027
|
+
// Configure SessionStart hook for update checking (skip for opencode)
|
|
1002
1028
|
if (!isOpencode) {
|
|
1003
1029
|
if (!settings.hooks) {
|
|
1004
1030
|
settings.hooks = {};
|
|
@@ -1007,7 +1033,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1007
1033
|
settings.hooks.SessionStart = [];
|
|
1008
1034
|
}
|
|
1009
1035
|
|
|
1010
|
-
// Check if GSD update hook already exists
|
|
1011
1036
|
const hasGsdUpdateHook = settings.hooks.SessionStart.some(entry =>
|
|
1012
1037
|
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-check-update'))
|
|
1013
1038
|
);
|
|
@@ -1030,11 +1055,6 @@ function install(isGlobal, runtime = 'claude') {
|
|
|
1030
1055
|
|
|
1031
1056
|
/**
|
|
1032
1057
|
* Apply statusline config, then print completion message
|
|
1033
|
-
* @param {string} settingsPath - Path to settings.json
|
|
1034
|
-
* @param {object} settings - Settings object
|
|
1035
|
-
* @param {string} statuslineCommand - Statusline command
|
|
1036
|
-
* @param {boolean} shouldInstallStatusline - Whether to install statusline
|
|
1037
|
-
* @param {string} runtime - Target runtime ('claude' or 'opencode')
|
|
1038
1058
|
*/
|
|
1039
1059
|
function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallStatusline, runtime = 'claude') {
|
|
1040
1060
|
const isOpencode = runtime === 'opencode';
|
|
@@ -1047,15 +1067,18 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
1047
1067
|
console.log(` ${green}✓${reset} Configured statusline`);
|
|
1048
1068
|
}
|
|
1049
1069
|
|
|
1050
|
-
// Always write settings
|
|
1070
|
+
// Always write settings
|
|
1051
1071
|
writeSettings(settingsPath, settings);
|
|
1052
1072
|
|
|
1053
|
-
// Configure OpenCode permissions
|
|
1073
|
+
// Configure OpenCode permissions
|
|
1054
1074
|
if (isOpencode) {
|
|
1055
1075
|
configureOpencodePermissions();
|
|
1056
1076
|
}
|
|
1057
1077
|
|
|
1058
|
-
|
|
1078
|
+
let program = 'Claude Code';
|
|
1079
|
+
if (runtime === 'opencode') program = 'OpenCode';
|
|
1080
|
+
if (runtime === 'gemini') program = 'Gemini';
|
|
1081
|
+
|
|
1059
1082
|
const command = isOpencode ? '/gsd-help' : '/gsd:help';
|
|
1060
1083
|
console.log(`
|
|
1061
1084
|
${green}Done!${reset} Launch ${program} and run ${cyan}${command}${reset}.
|
|
@@ -1070,19 +1093,16 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
1070
1093
|
function handleStatusline(settings, isInteractive, callback) {
|
|
1071
1094
|
const hasExisting = settings.statusLine != null;
|
|
1072
1095
|
|
|
1073
|
-
// No existing statusline - just install it
|
|
1074
1096
|
if (!hasExisting) {
|
|
1075
1097
|
callback(true);
|
|
1076
1098
|
return;
|
|
1077
1099
|
}
|
|
1078
1100
|
|
|
1079
|
-
// Has existing and --force-statusline flag
|
|
1080
1101
|
if (forceStatusline) {
|
|
1081
1102
|
callback(true);
|
|
1082
1103
|
return;
|
|
1083
1104
|
}
|
|
1084
1105
|
|
|
1085
|
-
// Has existing, non-interactive mode - skip
|
|
1086
1106
|
if (!isInteractive) {
|
|
1087
1107
|
console.log(` ${yellow}⚠${reset} Skipping statusline (already configured)`);
|
|
1088
1108
|
console.log(` Use ${cyan}--force-statusline${reset} to replace\n`);
|
|
@@ -1090,7 +1110,6 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1090
1110
|
return;
|
|
1091
1111
|
}
|
|
1092
1112
|
|
|
1093
|
-
// Has existing, interactive mode - prompt user
|
|
1094
1113
|
const existingCmd = settings.statusLine.command || settings.statusLine.url || '(custom)';
|
|
1095
1114
|
|
|
1096
1115
|
const rl = readline.createInterface({
|
|
@@ -1099,8 +1118,7 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1099
1118
|
});
|
|
1100
1119
|
|
|
1101
1120
|
console.log(`
|
|
1102
|
-
${yellow}⚠${reset} Existing statusline detected
|
|
1103
|
-
|
|
1121
|
+
${yellow}⚠${reset} Existing statusline detected\n
|
|
1104
1122
|
Your current statusline:
|
|
1105
1123
|
${dim}command: ${existingCmd}${reset}
|
|
1106
1124
|
|
|
@@ -1121,8 +1139,7 @@ function handleStatusline(settings, isInteractive, callback) {
|
|
|
1121
1139
|
}
|
|
1122
1140
|
|
|
1123
1141
|
/**
|
|
1124
|
-
* Prompt for runtime selection
|
|
1125
|
-
* @param {function} callback - Called with array of selected runtimes
|
|
1142
|
+
* Prompt for runtime selection
|
|
1126
1143
|
*/
|
|
1127
1144
|
function promptRuntime(callback) {
|
|
1128
1145
|
const rl = readline.createInterface({
|
|
@@ -1140,19 +1157,20 @@ function promptRuntime(callback) {
|
|
|
1140
1157
|
}
|
|
1141
1158
|
});
|
|
1142
1159
|
|
|
1143
|
-
console.log(` ${yellow}Which runtime(s) would you like to install for?${reset}
|
|
1144
|
-
|
|
1145
|
-
${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}
|
|
1146
1161
|
${cyan}2${reset}) OpenCode ${dim}(~/.config/opencode)${reset} - open source, free models
|
|
1147
|
-
${cyan}3${reset})
|
|
1162
|
+
${cyan}3${reset}) Gemini ${dim}(~/.gemini)${reset}
|
|
1163
|
+
${cyan}4${reset}) All
|
|
1148
1164
|
`);
|
|
1149
1165
|
|
|
1150
1166
|
rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
|
|
1151
1167
|
answered = true;
|
|
1152
1168
|
rl.close();
|
|
1153
1169
|
const choice = answer.trim() || '1';
|
|
1154
|
-
if (choice === '
|
|
1155
|
-
callback(['claude', 'opencode']);
|
|
1170
|
+
if (choice === '4') {
|
|
1171
|
+
callback(['claude', 'opencode', 'gemini']);
|
|
1172
|
+
} else if (choice === '3') {
|
|
1173
|
+
callback(['gemini']);
|
|
1156
1174
|
} else if (choice === '2') {
|
|
1157
1175
|
callback(['opencode']);
|
|
1158
1176
|
} else {
|
|
@@ -1163,11 +1181,8 @@ function promptRuntime(callback) {
|
|
|
1163
1181
|
|
|
1164
1182
|
/**
|
|
1165
1183
|
* Prompt for install location
|
|
1166
|
-
* @param {string[]} runtimes - Array of runtimes to install for
|
|
1167
1184
|
*/
|
|
1168
1185
|
function promptLocation(runtimes) {
|
|
1169
|
-
// Check if stdin is a TTY - if not, fall back to global install
|
|
1170
|
-
// This handles npx execution in environments like WSL2 where stdin may not be properly connected
|
|
1171
1186
|
if (!process.stdin.isTTY) {
|
|
1172
1187
|
console.log(` ${yellow}Non-interactive terminal detected, defaulting to global install${reset}\n`);
|
|
1173
1188
|
installAllRuntimes(runtimes, true, false);
|
|
@@ -1179,10 +1194,8 @@ function promptLocation(runtimes) {
|
|
|
1179
1194
|
output: process.stdout
|
|
1180
1195
|
});
|
|
1181
1196
|
|
|
1182
|
-
// Track whether we've processed the answer to prevent double-execution
|
|
1183
1197
|
let answered = false;
|
|
1184
1198
|
|
|
1185
|
-
// Handle readline close event (Ctrl+C, Escape, etc.) - cancel installation
|
|
1186
1199
|
rl.on('close', () => {
|
|
1187
1200
|
if (!answered) {
|
|
1188
1201
|
answered = true;
|
|
@@ -1191,18 +1204,14 @@ function promptLocation(runtimes) {
|
|
|
1191
1204
|
}
|
|
1192
1205
|
});
|
|
1193
1206
|
|
|
1194
|
-
// Show paths for selected runtimes
|
|
1195
1207
|
const pathExamples = runtimes.map(r => {
|
|
1196
|
-
// Use the proper global directory function for each runtime
|
|
1197
1208
|
const globalPath = getGlobalDir(r, explicitConfigDir);
|
|
1198
1209
|
return globalPath.replace(os.homedir(), '~');
|
|
1199
1210
|
}).join(', ');
|
|
1200
1211
|
|
|
1201
1212
|
const localExamples = runtimes.map(r => `./${getDirName(r)}`).join(', ');
|
|
1202
1213
|
|
|
1203
|
-
console.log(` ${yellow}Where would you like to install?${reset}
|
|
1204
|
-
|
|
1205
|
-
${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
|
|
1206
1215
|
${cyan}2${reset}) Local ${dim}(${localExamples})${reset} - this project only
|
|
1207
1216
|
`);
|
|
1208
1217
|
|
|
@@ -1217,9 +1226,6 @@ function promptLocation(runtimes) {
|
|
|
1217
1226
|
|
|
1218
1227
|
/**
|
|
1219
1228
|
* Install GSD for all selected runtimes
|
|
1220
|
-
* @param {string[]} runtimes - Array of runtimes to install for
|
|
1221
|
-
* @param {boolean} isGlobal - Whether to install globally
|
|
1222
|
-
* @param {boolean} isInteractive - Whether running interactively
|
|
1223
1229
|
*/
|
|
1224
1230
|
function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
1225
1231
|
const results = [];
|
|
@@ -1229,14 +1235,25 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
|
1229
1235
|
results.push(result);
|
|
1230
1236
|
}
|
|
1231
1237
|
|
|
1232
|
-
// Handle statusline for Claude
|
|
1238
|
+
// Handle statusline for Claude & Gemini (OpenCode uses themes)
|
|
1233
1239
|
const claudeResult = results.find(r => r.runtime === 'claude');
|
|
1240
|
+
const geminiResult = results.find(r => r.runtime === 'gemini');
|
|
1234
1241
|
|
|
1235
|
-
if
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
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
|
+
|
|
1240
1257
|
const opencodeResult = results.find(r => r.runtime === 'opencode');
|
|
1241
1258
|
if (opencodeResult) {
|
|
1242
1259
|
finishInstall(opencodeResult.settingsPath, opencodeResult.settings, opencodeResult.statuslineCommand, false, 'opencode');
|
|
@@ -1249,7 +1266,7 @@ function installAllRuntimes(runtimes, isGlobal, isInteractive) {
|
|
|
1249
1266
|
}
|
|
1250
1267
|
}
|
|
1251
1268
|
|
|
1252
|
-
// Main
|
|
1269
|
+
// Main logic
|
|
1253
1270
|
if (hasGlobal && hasLocal) {
|
|
1254
1271
|
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
1255
1272
|
process.exit(1);
|
|
@@ -1257,10 +1274,8 @@ if (hasGlobal && hasLocal) {
|
|
|
1257
1274
|
console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
|
|
1258
1275
|
process.exit(1);
|
|
1259
1276
|
} else if (hasUninstall) {
|
|
1260
|
-
// Uninstall mode
|
|
1261
1277
|
if (!hasGlobal && !hasLocal) {
|
|
1262
1278
|
console.error(` ${yellow}--uninstall requires --global or --local${reset}`);
|
|
1263
|
-
console.error(` Example: npx get-shit-done-cc --claude --global --uninstall`);
|
|
1264
1279
|
process.exit(1);
|
|
1265
1280
|
}
|
|
1266
1281
|
const runtimes = selectedRuntimes.length > 0 ? selectedRuntimes : ['claude'];
|
|
@@ -1268,19 +1283,16 @@ if (hasGlobal && hasLocal) {
|
|
|
1268
1283
|
uninstall(hasGlobal, runtime);
|
|
1269
1284
|
}
|
|
1270
1285
|
} else if (selectedRuntimes.length > 0) {
|
|
1271
|
-
// Non-interactive: runtime specified via flags
|
|
1272
1286
|
if (!hasGlobal && !hasLocal) {
|
|
1273
|
-
// Need location but runtime is specified - prompt for location only
|
|
1274
1287
|
promptLocation(selectedRuntimes);
|
|
1275
1288
|
} else {
|
|
1276
|
-
// Both runtime and location specified via flags
|
|
1277
1289
|
installAllRuntimes(selectedRuntimes, hasGlobal, false);
|
|
1278
1290
|
}
|
|
1279
1291
|
} else if (hasGlobal || hasLocal) {
|
|
1280
|
-
//
|
|
1292
|
+
// Default to Claude if no runtime specified but location is
|
|
1281
1293
|
installAllRuntimes(['claude'], hasGlobal, false);
|
|
1282
1294
|
} else {
|
|
1283
|
-
//
|
|
1295
|
+
// Interactive
|
|
1284
1296
|
if (!process.stdin.isTTY) {
|
|
1285
1297
|
console.log(` ${yellow}Non-interactive terminal detected, defaulting to Claude Code global install${reset}\n`);
|
|
1286
1298
|
installAllRuntimes(['claude'], true, false);
|
|
@@ -18,22 +18,25 @@ process.stdin.on('end', () => {
|
|
|
18
18
|
const session = data.session_id || '';
|
|
19
19
|
const remaining = data.context_window?.remaining_percentage;
|
|
20
20
|
|
|
21
|
-
// Context window display (shows USED percentage)
|
|
21
|
+
// Context window display (shows USED percentage scaled to 80% limit)
|
|
22
|
+
// Claude Code enforces an 80% context limit, so we scale to show 100% at that point
|
|
22
23
|
let ctx = '';
|
|
23
24
|
if (remaining != null) {
|
|
24
25
|
const rem = Math.round(remaining);
|
|
25
|
-
const
|
|
26
|
+
const rawUsed = Math.max(0, Math.min(100, 100 - rem));
|
|
27
|
+
// Scale: 80% real usage = 100% displayed
|
|
28
|
+
const used = Math.min(100, Math.round((rawUsed / 80) * 100));
|
|
26
29
|
|
|
27
30
|
// Build progress bar (10 segments)
|
|
28
31
|
const filled = Math.floor(used / 10);
|
|
29
32
|
const bar = '█'.repeat(filled) + '░'.repeat(10 - filled);
|
|
30
33
|
|
|
31
|
-
// Color based on usage
|
|
32
|
-
if (used <
|
|
34
|
+
// Color based on scaled usage (thresholds adjusted for new scale)
|
|
35
|
+
if (used < 63) { // ~50% real
|
|
33
36
|
ctx = ` \x1b[32m${bar} ${used}%\x1b[0m`;
|
|
34
|
-
} else if (used <
|
|
37
|
+
} else if (used < 81) { // ~65% real
|
|
35
38
|
ctx = ` \x1b[33m${bar} ${used}%\x1b[0m`;
|
|
36
|
-
} else if (used <
|
|
39
|
+
} else if (used < 95) { // ~76% real
|
|
37
40
|
ctx = ` \x1b[38;5;208m${bar} ${used}%\x1b[0m`;
|
|
38
41
|
} else {
|
|
39
42
|
ctx = ` \x1b[5;31m💀 ${bar} ${used}%\x1b[0m`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "get-shit-done-cc",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by TÂCHES.",
|
|
3
|
+
"version": "1.10.0",
|
|
4
|
+
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode and Gemini by TÂCHES.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"get-shit-done-cc": "bin/install.js"
|
|
7
7
|
},
|
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
"ai",
|
|
20
20
|
"meta-prompting",
|
|
21
21
|
"context-engineering",
|
|
22
|
-
"spec-driven-development"
|
|
22
|
+
"spec-driven-development",
|
|
23
|
+
"gemini",
|
|
24
|
+
"gemini-cli"
|
|
23
25
|
],
|
|
24
26
|
"author": "TÂCHES",
|
|
25
27
|
"license": "MIT",
|