diff --git a/CHANGELOG.md b/CHANGELOG.md index 67b5bf9c88..0b4f4d1c25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 91332b8cef..eb911e1722 100644 --- a/README.md +++ b/README.md @@ -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.** @@ -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: diff --git a/bin/install.js b/bin/install.js index 8a265e643e..493a989be9 100755 --- a/bin/install.js +++ b/bin/install.js @@ -41,6 +41,7 @@ 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'); @@ -48,7 +49,7 @@ 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 { @@ -56,6 +57,7 @@ if (hasAll) { 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) @@ -63,13 +65,14 @@ 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, '', ...) - * @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) { @@ -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'"; } @@ -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) { @@ -126,7 +130,7 @@ function getGlobalDir(runtime, explicitDir = null) { } return getOpencodeGlobalDir(); } - + if (runtime === 'gemini') { // Gemini: --config-dir > GEMINI_CONFIG_DIR > ~/.gemini if (explicitDir) { @@ -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); @@ -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() { @@ -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 ${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 ${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); } @@ -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.) @@ -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(); } @@ -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'); @@ -934,9 +975,9 @@ function convertClaudeToGeminiToml(content) { if (description) { toml += `description = ${JSON.stringify(description)}\n`; } - + toml += `prompt = ${JSON.stringify(body)}\n`; - + return toml; } @@ -944,7 +985,7 @@ function convertClaudeToGeminiToml(content) { * 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') @@ -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)) { @@ -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 @@ -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 @@ -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, '..'); @@ -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`); @@ -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); @@ -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); diff --git a/package.json b/package.json index b9c9676869..48eb8174b9 100644 --- a/package.json +++ b/package.json @@ -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" }, @@ -20,6 +20,8 @@ "meta-prompting", "context-engineering", "spec-driven-development", + "iflow", + "iflow-cli", "gemini", "gemini-cli", "codex",