Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Added
- iFlow CLI support — install with `--iflow` flag or select from interactive menu

## [1.22.0] - 2026-02-27

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# GET SHIT DONE

**A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini CLI, and Codex.**
**A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code, iFlow CLI, OpenCode, Gemini CLI, and Codex.**

**Solves context rot — the quality degradation that happens as Claude fills its context window.**

Expand Down Expand Up @@ -80,7 +80,7 @@ npx get-shit-done-cc@latest
```

The installer prompts you to choose:
1. **Runtime** — Claude Code, OpenCode, Gemini, Codex, or all
1. **Runtime** — Claude Code, iFlow CLI, OpenCode, Gemini, Codex, or all
2. **Location** — Global (all projects) or local (current project only)

Verify with:
Expand Down
85 changes: 64 additions & 21 deletions bin/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,35 +41,38 @@ const hasOpencode = args.includes('--opencode');
const hasClaude = args.includes('--claude');
const hasGemini = args.includes('--gemini');
const hasCodex = args.includes('--codex');
const hasIflow = args.includes('--iflow');
const hasBoth = args.includes('--both'); // Legacy flag, keeps working
const hasAll = args.includes('--all');
const hasUninstall = args.includes('--uninstall') || args.includes('-u');

// Runtime selection - can be set by flags or interactive prompt
let selectedRuntimes = [];
if (hasAll) {
selectedRuntimes = ['claude', 'opencode', 'gemini', 'codex'];
selectedRuntimes = ['claude', 'opencode', 'gemini', 'codex', 'iflow'];
} else if (hasBoth) {
selectedRuntimes = ['claude', 'opencode'];
} else {
if (hasOpencode) selectedRuntimes.push('opencode');
if (hasClaude) selectedRuntimes.push('claude');
if (hasGemini) selectedRuntimes.push('gemini');
if (hasCodex) selectedRuntimes.push('codex');
if (hasIflow) selectedRuntimes.push('iflow');
}

// Helper to get directory name for a runtime (used for local/project installs)
function getDirName(runtime) {
if (runtime === 'opencode') return '.opencode';
if (runtime === 'gemini') return '.gemini';
if (runtime === 'codex') return '.codex';
if (runtime === 'iflow') return '.iflow';
return '.claude';
}

/**
* Get the config directory path relative to home directory for a runtime
* Used for templating hooks that use path.join(homeDir, '<configDir>', ...)
* @param {string} runtime - 'claude', 'opencode', 'gemini', or 'codex'
* @param {string} runtime - 'claude', 'opencode', 'gemini', 'codex', or 'iflow'
* @param {boolean} isGlobal - Whether this is a global install
*/
function getConfigDirFromHome(runtime, isGlobal) {
Expand All @@ -85,6 +88,7 @@ function getConfigDirFromHome(runtime, isGlobal) {
}
if (runtime === 'gemini') return "'.gemini'";
if (runtime === 'codex') return "'.codex'";
if (runtime === 'iflow') return "'.iflow'";
return "'.claude'";
}

Expand All @@ -98,24 +102,24 @@ function getOpencodeGlobalDir() {
if (process.env.OPENCODE_CONFIG_DIR) {
return expandTilde(process.env.OPENCODE_CONFIG_DIR);
}

// 2. OPENCODE_CONFIG env var (use its directory)
if (process.env.OPENCODE_CONFIG) {
return path.dirname(expandTilde(process.env.OPENCODE_CONFIG));
}

// 3. XDG_CONFIG_HOME/opencode
if (process.env.XDG_CONFIG_HOME) {
return path.join(expandTilde(process.env.XDG_CONFIG_HOME), 'opencode');
}

// 4. Default: ~/.config/opencode (XDG default)
return path.join(os.homedir(), '.config', 'opencode');
}

/**
* Get the global config directory for a runtime
* @param {string} runtime - 'claude', 'opencode', 'gemini', or 'codex'
* @param {string} runtime - 'claude', 'opencode', 'gemini', 'codex', or 'iflow'
* @param {string|null} explicitDir - Explicit directory from --config-dir flag
*/
function getGlobalDir(runtime, explicitDir = null) {
Expand All @@ -126,7 +130,7 @@ function getGlobalDir(runtime, explicitDir = null) {
}
return getOpencodeGlobalDir();
}

if (runtime === 'gemini') {
// Gemini: --config-dir > GEMINI_CONFIG_DIR > ~/.gemini
if (explicitDir) {
Expand All @@ -148,7 +152,18 @@ function getGlobalDir(runtime, explicitDir = null) {
}
return path.join(os.homedir(), '.codex');
}


if (runtime === 'iflow') {
// iFlow: --config-dir > IFLOW_CONFIG_DIR > ~/.iflow
if (explicitDir) {
return expandTilde(explicitDir);
}
if (process.env.IFLOW_CONFIG_DIR) {
return expandTilde(process.env.IFLOW_CONFIG_DIR);
}
return path.join(os.homedir(), '.iflow');
}

// Claude Code: --config-dir > CLAUDE_CONFIG_DIR > ~/.claude
if (explicitDir) {
return expandTilde(explicitDir);
Expand All @@ -169,7 +184,7 @@ const banner = '\n' +
'\n' +
' Get Shit Done ' + dim + 'v' + pkg.version + reset + '\n' +
' A meta-prompting, context engineering and spec-driven\n' +
' development system for Claude Code, OpenCode, Gemini, and Codex by TÂCHES.\n';
' development system for Claude Code, iFlow CLI, OpenCode, Gemini, and Codex by TÂCHES.\n';

// Parse --config-dir argument
function parseConfigDirArg() {
Expand Down Expand Up @@ -203,7 +218,7 @@ console.log(banner);

// Show help if requested
if (hasHelp) {
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}--codex${reset} Install for Codex 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 Codex globally${reset}\n npx get-shit-done-cc --codex --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 --codex --global --config-dir ~/.codex-work\n\n ${dim}# Install to current project only${reset}\n npx get-shit-done-cc --claude --local\n\n ${dim}# Uninstall GSD from Codex globally${reset}\n npx get-shit-done-cc --codex --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 / CODEX_HOME environment variables.\n`);
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}--iflow${reset} Install for iFlow CLI only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--gemini${reset} Install for Gemini only\n ${cyan}--codex${reset} Install for Codex 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 iFlow CLI globally${reset}\n npx get-shit-done-cc --iflow --global\n\n ${dim}# Install for Gemini globally${reset}\n npx get-shit-done-cc --gemini --global\n\n ${dim}# Install for Codex globally${reset}\n npx get-shit-done-cc --codex --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 --codex --global --config-dir ~/.codex-work\n\n ${dim}# Install to current project only${reset}\n npx get-shit-done-cc --claude --local\n\n ${dim}# Uninstall GSD from Codex globally${reset}\n npx get-shit-done-cc --codex --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 / CODEX_HOME / IFLOW_CONFIG_DIR environment variables.\n`);
process.exit(0);
}

Expand Down Expand Up @@ -364,6 +379,13 @@ const claudeToGeminiTools = {
AskUserQuestion: 'ask_user',
};

// Tool name mapping from Claude Code to iFlow CLI
// iFlow CLI is highly compatible with Claude Code, most tools can be used directly
const claudeToIflowTools = {
// iFlow uses the same tool names as Claude Code for most cases
// Add any specific mappings here if needed in the future
};

/**
* Convert a Claude Code tool name to OpenCode format
* - Applies special mappings (AskUserQuestion -> question, etc.)
Expand Down Expand Up @@ -406,6 +428,25 @@ function convertGeminiToolName(claudeTool) {
return claudeTool.toLowerCase();
}

/**
* Convert a Claude Code tool name to iFlow CLI format
* iFlow CLI is highly compatible with Claude Code, so most tools can be used directly
* @returns {string} iFlow tool name
*/
function convertIflowToolName(claudeTool) {
// Check for explicit mapping
if (claudeToIflowTools[claudeTool]) {
return claudeToIflowTools[claudeTool];
}
// Most tools can be used directly in iFlow (same as Claude Code)
// MCP tools keep their format
if (claudeTool.startsWith('mcp__')) {
return claudeTool;
}
// Default: return as-is (iFlow is compatible with Claude Code)
return claudeTool;
}

function toSingleLine(value) {
return value.replace(/\s+/g, ' ').trim();
}
Expand Down Expand Up @@ -917,7 +958,7 @@ function convertClaudeToGeminiToml(content) {

const frontmatter = content.substring(3, endIndex).trim();
const body = content.substring(endIndex + 3).trim();

// Extract description from frontmatter
let description = '';
const lines = frontmatter.split('\n');
Expand All @@ -934,17 +975,17 @@ function convertClaudeToGeminiToml(content) {
if (description) {
toml += `description = ${JSON.stringify(description)}\n`;
}

toml += `prompt = ${JSON.stringify(body)}\n`;

return toml;
}

/**
* Copy commands to a flat structure for OpenCode
* OpenCode expects: command/gsd-help.md (invoked as /gsd-help)
* Source structure: commands/gsd/help.md
*
*
* @param {string} srcDir - Source directory (e.g., commands/gsd/)
* @param {string} destDir - Destination directory (e.g., command/)
* @param {string} prefix - Prefix for filenames (e.g., 'gsd')
Expand All @@ -955,7 +996,7 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
if (!fs.existsSync(srcDir)) {
return;
}

// Remove old gsd-*.md files before copying new ones
if (fs.existsSync(destDir)) {
for (const file of fs.readdirSync(destDir)) {
Expand All @@ -966,12 +1007,12 @@ function copyFlattenedCommands(srcDir, destDir, prefix, pathPrefix, runtime) {
} else {
fs.mkdirSync(destDir, { recursive: true });
}

const entries = fs.readdirSync(srcDir, { withFileTypes: true });

for (const entry of entries) {
const srcPath = path.join(srcDir, entry.name);

if (entry.isDirectory()) {
// Recurse into subdirectories, adding to prefix
// e.g., commands/gsd/debug/start.md -> command/gsd-debug-start.md
Expand Down Expand Up @@ -1594,7 +1635,7 @@ function configureOpencodePermissions(isGlobal = true) {
const gsdPath = opencodeConfigDir === defaultConfigDir
? '~/.config/opencode/get-shit-done/*'
: `${opencodeConfigDir.replace(/\\/g, '/')}/get-shit-done/*`;

let modified = false;

// Configure read permission
Expand Down Expand Up @@ -1824,6 +1865,7 @@ function install(isGlobal, runtime = 'claude') {
const isOpencode = runtime === 'opencode';
const isGemini = runtime === 'gemini';
const isCodex = runtime === 'codex';
const isIflow = runtime === 'iflow';
const dirName = getDirName(runtime);
const src = path.join(__dirname, '..');

Expand All @@ -1847,6 +1889,7 @@ function install(isGlobal, runtime = 'claude') {
if (isOpencode) runtimeLabel = 'OpenCode';
if (isGemini) runtimeLabel = 'Gemini';
if (isCodex) runtimeLabel = 'Codex';
if (isIflow) runtimeLabel = 'iFlow CLI';

console.log(` Installing for ${cyan}${runtimeLabel}${reset} to ${cyan}${locationLabel}${reset}\n`);

Expand All @@ -1864,7 +1907,7 @@ function install(isGlobal, runtime = 'claude') {
// OpenCode: flat structure in command/ directory
const commandDir = path.join(targetDir, 'command');
fs.mkdirSync(commandDir, { recursive: true });

// Copy commands/gsd/*.md as command/gsd-*.md (flatten structure)
const gsdSrc = path.join(src, 'commands', 'gsd');
copyFlattenedCommands(gsdSrc, commandDir, 'gsd', pathPrefix, runtime);
Expand All @@ -1888,7 +1931,7 @@ function install(isGlobal, runtime = 'claude') {
// Claude Code & Gemini: nested structure in commands/ directory
const commandsDir = path.join(targetDir, 'commands');
fs.mkdirSync(commandsDir, { recursive: true });

const gsdSrc = path.join(src, 'commands', 'gsd');
const gsdDest = path.join(commandsDir, 'gsd');
copyWithPathReplacement(gsdSrc, gsdDest, pathPrefix, runtime, true);
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "get-shit-done-cc",
"version": "1.22.0",
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, iFlow CLI, OpenCode, Gemini and Codex by TÂCHES.",
"bin": {
"get-shit-done-cc": "bin/install.js"
},
Expand All @@ -20,6 +20,8 @@
"meta-prompting",
"context-engineering",
"spec-driven-development",
"iflow",
"iflow-cli",
"gemini",
"gemini-cli",
"codex",
Expand Down