diff --git a/README.md b/README.md index c15faec..1562d87 100644 --- a/README.md +++ b/README.md @@ -120,9 +120,9 @@ Every implementation ends with `dev-reviewer` checking: ```bash # No clone needed — run directly with bunx or npx: cd /path/to/your/project -bunx claude-dev-kit@latest +bunx claude-dev-kit@latest install # or -npx claude-dev-kit@latest +npx claude-dev-kit@latest install ``` Or if you prefer to clone first: @@ -132,6 +132,27 @@ git clone https://github.com/evandempsey/claude-dev-kit /tmp/cdk bash /tmp/cdk/scripts/install.sh /path/to/your/project ``` +### CLI subcommands + +| Command | Description | +|---------|-------------| +| `claude-dev-kit install [target]` | Full install: copy `.claude/`, hook deps, MCP wizard (default) | +| `claude-dev-kit update [target]` | Migration only — pull new agents/skills/hooks, no prompts | +| `claude-dev-kit mcp [target]` | Run MCP wizard only (add integrations to existing project) | +| `claude-dev-kit version` | Print installed kit version | +| `claude-dev-kit help` | Print usage summary | + +```bash +# Update an existing install non-interactively (e.g. in CI) +bunx claude-dev-kit@latest update + +# Add/reconfigure MCP integrations only +bunx claude-dev-kit@latest mcp + +# Run fully non-interactively (skips all prompts) +CI=true bunx claude-dev-kit@latest install +``` + ### What the installer does **Phase 1 — File install:** @@ -176,8 +197,8 @@ All MCPs are installed with `--scope project` — they activate only in this pro ### MCP-only install (add integrations to existing project) ```bash -bunx claude-dev-kit@latest --mcp-only -# or +bunx claude-dev-kit@latest mcp +# or (legacy flag still works) bash /path/to/install.sh --mcp-only ``` @@ -327,7 +348,7 @@ Some agent packs require additional MCPs or external tooling. They ship dormant | Pack | Requirement | How to activate | |------|-------------|------------------| -| **Designer** (`designer` + 4 sub-agents) | Figma MCP | Run `bash scripts/install.sh --mcp-only` and select **Figma** under Design Tools. Or select the **Designer** pack in Phase 1.5 during a fresh install — the installer will auto-prompt for your Figma token. | +| **Designer** (`designer` + 4 sub-agents) | Figma MCP | Run `bunx claude-dev-kit@latest mcp` and select **Figma** under Design Tools. Or select the **Designer** pack in Phase 1.5 during a fresh install — the installer will auto-prompt for your Figma token. | | **DevOps** *(planned)* | — | Future release. | | **Data** *(planned)* | — | Future release. | diff --git a/bin/claude-dev-kit.js b/bin/claude-dev-kit.js index b29b6bb..437d656 100644 --- a/bin/claude-dev-kit.js +++ b/bin/claude-dev-kit.js @@ -4,47 +4,107 @@ const { spawnSync } = require('child_process'); const path = require('path'); const os = require('os'); +const fs = require('fs'); const kitRoot = path.join(__dirname, '..'); -const args = process.argv.slice(2); +const argv = process.argv.slice(2); -// ── Non-Windows: always use bash ───────────────────────────────────────────── -if (os.platform() !== 'win32') { - const scriptPath = path.join(kitRoot, 'scripts', 'install.sh'); - const result = spawnSync('bash', [scriptPath, ...args], { stdio: 'inherit' }); - process.exit(result.status ?? 1); +function printHelp() { + console.log(` +Claude Dev Kit — CLI + +Usage: + claude-dev-kit [target] [options] + +Commands: + install [target] Full install: copy .claude/, install hook deps, run MCP wizard + update [target] Migration only: pull in new agents/skills/hooks without prompts + mcp [target] Run the MCP wizard only (configure integrations) + version Print the installed kit version + help Print this help summary + +Options: + --help Print usage for any subcommand + +Environment: + CI=true Run install/update non-interactively (skip all prompts) + TARGET= Alternative to passing target as a positional argument + +Examples: + bunx claude-dev-kit@latest install + bunx claude-dev-kit@latest install /path/to/project + bunx claude-dev-kit@latest update + bunx claude-dev-kit@latest mcp + bunx claude-dev-kit@latest version + CI=true bunx claude-dev-kit@latest install +`.trim()); } -// ── Windows: try bash (Git Bash / WSL) first, then PowerShell ──────────────── -function hasBash() { - const r = spawnSync('bash', ['--version'], { stdio: 'pipe' }); - return r.status === 0; +function printVersion() { + const pkgPath = path.join(kitRoot, 'package.json'); + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + console.log(pkg.version); } -if (hasBash()) { +function runScript(phase, scriptArgs) { const scriptPath = path.join(kitRoot, 'scripts', 'install.sh'); - const result = spawnSync('bash', [scriptPath, ...args], { stdio: 'inherit' }); + + if (os.platform() !== 'win32') { + const result = spawnSync('bash', [scriptPath, `--phase=${phase}`, ...scriptArgs], { stdio: 'inherit' }); + process.exit(result.status ?? 1); + } + + // ── Windows: try bash (Git Bash / WSL) first, then PowerShell ────────────── + function hasBash() { + return spawnSync('bash', ['--version'], { stdio: 'pipe' }).status === 0; + } + + if (hasBash()) { + const result = spawnSync('bash', [scriptPath, `--phase=${phase}`, ...scriptArgs], { stdio: 'inherit' }); + process.exit(result.status ?? 1); + } + + // No bash — fall back to PowerShell (install and mcp phases only) + const ps1Path = path.join(kitRoot, 'scripts', 'install.ps1'); + const psArgs = ['-ExecutionPolicy', 'Bypass', '-File', ps1Path]; + if (phase === 'mcp') psArgs.push('-McpOnly'); + for (const arg of scriptArgs) psArgs.push(arg); + + const pwsh = spawnSync('pwsh', ['--version'], { stdio: 'pipe' }).status === 0 ? 'pwsh' : 'powershell'; + const result = spawnSync(pwsh, psArgs, { stdio: 'inherit' }); process.exit(result.status ?? 1); } -// No bash — fall back to PowerShell -const ps1Path = path.join(kitRoot, 'scripts', 'install.ps1'); - -// Translate CLI args to PowerShell param style -// e.g. --mcp-only -> -McpOnly, positional target dir stays positional -const psArgs = ['-ExecutionPolicy', 'Bypass', '-File', ps1Path]; -for (const arg of args) { - if (arg === '--mcp-only') { - psArgs.push('-McpOnly'); - } else { - psArgs.push(arg); - } +// ── Parse subcommand ────────────────────────────────────────────────────────── +const [subcommand, ...rest] = argv; + +if (!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help') { + printHelp(); + process.exit(0); } -// Prefer pwsh (PowerShell 7+) over legacy powershell.exe (5.x) -const pwsh = spawnSync('pwsh', ['--version'], { stdio: 'pipe' }).status === 0 - ? 'pwsh' - : 'powershell'; +if (subcommand === 'version' || subcommand === '--version' || subcommand === '-v') { + printVersion(); + process.exit(0); +} -const result = spawnSync(pwsh, psArgs, { stdio: 'inherit' }); -process.exit(result.status ?? 1); +if (rest.includes('--help') || rest.includes('-h')) { + printHelp(); + process.exit(0); +} + +switch (subcommand) { + case 'install': + runScript('install', rest); + break; + case 'update': + runScript('update', rest); + break; + case 'mcp': + runScript('mcp', rest); + break; + default: + console.error(`Unknown command: ${subcommand}`); + console.error('Run "claude-dev-kit help" for usage.'); + process.exit(1); +} diff --git a/scripts/install.sh b/scripts/install.sh index 618d679..10bee7d 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash -# Claude Dev Kit — Installer v2.1 +# Claude Dev Kit — Installer v2.2 # # Copies .claude/ into your project, installs hook deps, # then runs an MCP wizard to configure Claude's integrations # (Git platform, ticket system, design tools, code search). # # Usage: -# bash install.sh [target-directory] +# bash install.sh [--phase=install|update|mcp] [target-directory] # TARGET=/path/to/project bash install.sh -# bash install.sh --mcp-only (skip file copy, just configure MCPs) +# bash install.sh --mcp-only (alias for --phase=mcp) set -euo pipefail @@ -101,14 +101,26 @@ mcp_add() { # ─── Main ───────────────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" KIT_ROOT="$(dirname "$SCRIPT_DIR")" -MCP_ONLY=false -if [[ "${1:-}" == "--mcp-only" ]]; then - MCP_ONLY=true - TARGET="${TARGET:-$(pwd)}" -else - TARGET="${1:-${TARGET:-$(pwd)}}" -fi +# ── Arg parsing: --phase=install|update|mcp, --mcp-only (legacy alias) ─────── +PHASE="install" +POSITIONAL_ARGS=() + +for arg in "$@"; do + case "$arg" in + --phase=install) PHASE="install" ;; + --phase=update) PHASE="update" ;; + --phase=mcp) PHASE="mcp" ;; + --mcp-only) PHASE="mcp" ;; # backward-compat alias + *) POSITIONAL_ARGS+=("$arg") ;; + esac +done + +TARGET="${POSITIONAL_ARGS[0]:-${TARGET:-$(pwd)}}" + +# Derive legacy MCP_ONLY for sections that still check it +MCP_ONLY=false +[[ "$PHASE" == "mcp" ]] && MCP_ONLY=true # Install log — all subprocess output goes here instead of being suppressed LOG_FILE="$TARGET/.claude/install.log" @@ -119,6 +131,51 @@ echo -e "${BOLD}║ Claude Dev Kit — Installer ║${NC}" echo -e "${BOLD}╚═══════════════════════════════════════╝${NC}" echo "" +# ─── Phase: update (migration only, non-interactive) ───────────────────────── +if [[ "$PHASE" == "update" ]]; then + header "Update: pulling in latest agents, skills, and hooks" + echo -e " ${DIM}Source: $KIT_ROOT${NC}" + echo -e " ${DIM}Target: $TARGET${NC}" + echo "" + + if ! CI=true bash "$SCRIPT_DIR/migrate.sh" "$KIT_ROOT" "$TARGET"; then + error "Migration failed — check output above" + exit 1 + fi + + # Ensure log file exists now that .claude/ is present + mkdir -p "$TARGET/.claude" + : > "$LOG_FILE" + + # Install hook dependencies + HOOK_DIR="$TARGET/.claude/hooks/skill-activation-prompt" + if [[ -f "$HOOK_DIR/package.json" ]]; then + info "Installing skill-activation-prompt hook dependencies..." + pushd "$HOOK_DIR" > /dev/null + if command -v bun &>/dev/null; then + if ! bun install --silent >> "$LOG_FILE" 2>&1; then + warn "bun install failed — see $LOG_FILE for details" + fi + elif command -v npm &>/dev/null; then + if ! npm install --silent >> "$LOG_FILE" 2>&1; then + warn "npm install failed — see $LOG_FILE for details" + fi + else + warn "Neither bun nor npm found. Run manually: cd $HOOK_DIR && npm install" + fi + popd > /dev/null + success "Hook dependencies installed" + fi + + echo "" + header "Update Complete" + echo "" + echo -e " ${GREEN}✓${NC} .claude/ updated in $TARGET/.claude" + echo -e " ${DIM} User-customized files were preserved${NC}" + echo "" + exit 0 +fi + # ─── Phase 1: File Installation ─────────────────────────────────────────────── if [[ "$MCP_ONLY" == "false" ]]; then header "Phase 1: Install .claude/ into your project"