diff --git a/CHANGELOG.md b/CHANGELOG.md index 5332f4ca..541a8734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to DevFlow will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added +- **`--verbose` flag for `devflow init`** - Clean, command-focused output by default + - Default output shows only version, available commands, and docs link + - Use `--verbose` for detailed installation progress, paths, and skills list + - Improves first-run experience by reducing noise + +### Changed +- Refactored init command output rendering into separate functions +- Extracted command and skill lists into maintainable constants + +--- + ## [0.8.0] - 2025-11-21 ### Added diff --git a/README.md b/README.md index 2eb77da7..7848e1a0 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ The `.docs/` structure provides a searchable history of decisions, designs, and | Command | Purpose | Options | |---------|---------|---------| -| `npx devflow-kit init` | Initialize DevFlow for Claude Code | `--scope ` - Installation scope (user: user-wide, local: project-only)
`--skip-docs` - Skip creating `.docs/` structure | +| `npx devflow-kit init` | Initialize DevFlow for Claude Code | `--scope ` - Installation scope (user: user-wide, local: project-only)
`--verbose` - Show detailed installation output
`--skip-docs` - Skip creating `.docs/` structure | | `npx devflow-kit uninstall` | Remove DevFlow from Claude Code | `--scope ` - Uninstall from specific scope only (default: auto-detect all)
`--keep-docs` - Keep `.docs/` directory | **What `npx devflow-kit init` does:** diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 40133fac..2768e675 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -22,7 +22,7 @@ program .description('Agentic Development Toolkit for Claude Code\n\nEnhance your AI-assisted development with intelligent commands and workflows.') .version(packageJson.version, '-v, --version', 'Display version number') .helpOption('-h, --help', 'Display help information') - .addHelpText('after', '\nExamples:\n $ devflow init Install DevFlow for Claude Code\n $ devflow init --skip-docs Install without creating .docs/ structure\n $ devflow uninstall Remove DevFlow from Claude Code\n $ devflow --version Show version\n $ devflow --help Show help\n\nDocumentation:\n https://github.com/dean0x/devflow#readme'); + .addHelpText('after', '\nExamples:\n $ devflow init Install DevFlow for Claude Code\n $ devflow init --verbose Install with detailed output\n $ devflow init --skip-docs Install without creating .docs/ structure\n $ devflow uninstall Remove DevFlow from Claude Code\n $ devflow --version Show version\n $ devflow --help Show help\n\nDocumentation:\n https://github.com/dean0x/devflow#readme'); // Register commands program.addCommand(initCommand); diff --git a/src/cli/commands/init.ts b/src/cli/commands/init.ts index 891c1050..73841b40 100644 --- a/src/cli/commands/init.ts +++ b/src/cli/commands/init.ts @@ -42,11 +42,137 @@ async function promptUser(question: string): Promise { }); } +/** + * Options for the init command parsed by Commander.js + */ +interface InitOptions { + skipDocs?: boolean; + scope?: string; + verbose?: boolean; +} + +/** + * Command definition with name and user-friendly description + */ +interface CommandDefinition { + name: string; + description: string; +} + +/** + * DevFlow commands with user-friendly descriptions. + * Used for displaying available commands in init output. + */ +const DEVFLOW_COMMANDS: CommandDefinition[] = [ + { name: '/catch-up', description: 'Get up to speed on project state' }, + { name: '/brainstorm', description: 'Explore design decisions' }, + { name: '/design', description: 'Create implementation plan' }, + { name: '/plan', description: 'Triage issues from discussion' }, + { name: '/breakdown', description: 'Break down tasks quickly' }, + { name: '/implement', description: 'Interactive implementation' }, + { name: '/code-review', description: 'Comprehensive code review' }, + { name: '/commit', description: 'Smart atomic commits' }, + { name: '/pull-request', description: 'Create PR with description' }, + { name: '/release', description: 'Automated releases' }, + { name: '/devlog', description: 'Document session progress' }, + { name: '/debug', description: 'Systematic debugging' }, + { name: '/resolve-comments', description: 'Address PR feedback' }, +]; + +/** + * DevFlow skills with descriptions. + * Displayed only in verbose mode to show auto-activating capabilities. + */ +const DEVFLOW_SKILLS: CommandDefinition[] = [ + { name: 'pattern-check', description: 'Architectural pattern validation' }, + { name: 'test-design', description: 'Test quality enforcement' }, + { name: 'code-smell', description: 'Anti-pattern detection' }, + { name: 'research', description: 'Pre-implementation planning (auto)' }, + { name: 'debug', description: 'Systematic debugging (auto)' }, + { name: 'input-validation', description: 'Boundary validation' }, + { name: 'error-handling', description: 'Result type consistency' }, +]; + +/** + * Render clean, minimal output for default (non-verbose) mode. + * Shows only essential information: version, available commands, and docs link. + * + * @param version - The DevFlow version string to display + */ +function renderCleanOutput(version: string): void { + console.log(`\nāœ“ DevFlow v${version} installed\n`); + console.log('Commands available:'); + + // Calculate max command name length for alignment + const maxLen = Math.max(...DEVFLOW_COMMANDS.map(c => c.name.length)); + + for (const cmd of DEVFLOW_COMMANDS) { + const padding = ' '.repeat(maxLen - cmd.name.length + 2); + console.log(` ${cmd.name}${padding}${cmd.description}`); + } + + console.log('\nRun any command in Claude Code to get started.'); + console.log('\nDocs: https://github.com/dean0x/devflow'); +} + +/** + * Render detailed output for verbose mode. + * Shows full installation details including paths, merge instructions, and skills. + * + * @param version - The DevFlow version string to display + * @param scope - Installation scope ('user' for user-wide, 'local' for project-only) + * @param claudeDir - Path to the Claude Code directory + * @param devflowDir - Path to the DevFlow directory + * @param settingsExists - Whether existing settings.json was preserved + * @param claudeMdExists - Whether existing CLAUDE.md was preserved + */ +function renderVerboseOutput( + version: string, + scope: 'user' | 'local', + claudeDir: string, + devflowDir: string, + settingsExists: boolean, + claudeMdExists: boolean +): void { + console.log(`\nāœ… DevFlow v${version} installed!\n`); + + console.log(`šŸ“ Installation scope: ${scope}`); + console.log(` Claude dir: ${claudeDir}`); + console.log(` DevFlow dir: ${devflowDir}\n`); + + // Show manual merge instructions if needed + if (settingsExists || claudeMdExists) { + console.log('šŸ“ Manual merge recommended:\n'); + if (settingsExists) { + console.log(' Settings: Review settings.devflow.json and merge desired config into settings.json'); + console.log(' Key setting: statusLine configuration for DevFlow statusline\n'); + } + if (claudeMdExists) { + console.log(' Instructions: Review CLAUDE.devflow.md and adopt desired practices'); + console.log(" This contains DevFlow's recommended development patterns\n"); + } + } + + console.log('Available commands:'); + for (const cmd of DEVFLOW_COMMANDS) { + console.log(` ${cmd.name.padEnd(18)}${cmd.description}`); + } + + console.log('\nInstalled skills (auto-activate):'); + for (const skill of DEVFLOW_SKILLS) { + console.log(` ${skill.name.padEnd(18)}${skill.description}`); + } + + console.log('\nNote: debug exists as both command (manual) and skill (auto)'); + console.log('Docs: https://github.com/dean0x/devflow'); +} + export const initCommand = new Command('init') .description('Initialize DevFlow for Claude Code') .option('--skip-docs', 'Skip creating .docs/ structure') .option('--scope ', 'Installation scope: user (user-wide) or local (project-only)', /^(user|local)$/i) - .action(async (options) => { + .option('--verbose', 'Show detailed installation output') + .action(async (options: InitOptions) => { // Get package version const packageJsonPath = path.resolve(__dirname, '../../package.json'); let version = ''; @@ -57,35 +183,53 @@ export const initCommand = new Command('init') version = 'unknown'; } - console.log(`šŸš€ DevFlow v${version}\n`); + const verbose = options.verbose ?? false; + + if (verbose) { + console.log(`šŸš€ DevFlow v${version}\n`); + } // Determine installation scope let scope: 'user' | 'local' = 'user'; // Default to user for backwards compatibility if (options.scope) { - scope = options.scope.toLowerCase() as 'user' | 'local'; + const normalizedScope = options.scope.toLowerCase(); + // Runtime validation (Commander regex already validates, but be defensive) + if (normalizedScope !== 'user' && normalizedScope !== 'local') { + console.error('āŒ Invalid scope. Use "user" or "local"\n'); + process.exit(1); + } + scope = normalizedScope; } else { // Check if running in interactive terminal (TTY) if (!process.stdin.isTTY) { // Non-interactive environment (CI/CD, scripts) - use default - console.log('šŸ“¦ Non-interactive environment detected, using default scope: user'); - console.log(' To specify scope in CI/CD, use: devflow init --scope \n'); + if (verbose) { + console.log('šŸ“¦ Non-interactive environment detected, using default scope: user'); + console.log(' To specify scope in CI/CD, use: devflow init --scope \n'); + } scope = 'user'; } else { // Interactive prompt for scope - console.log('šŸ“¦ Installation Scope:\n'); - console.log(' user - Install for all projects (user-wide)'); - console.log(' └─ ~/.claude/ and ~/.devflow/'); - console.log(' local - Install for current project only'); - console.log(' └─ /.claude/ and /.devflow/\n'); + if (verbose) { + console.log('šŸ“¦ Installation Scope:\n'); + console.log(' user - Install for all projects (user-wide)'); + console.log(' └─ ~/.claude/ and ~/.devflow/'); + console.log(' local - Install for current project only'); + console.log(' └─ /.claude/ and /.devflow/\n'); + } const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const prompt = verbose + ? 'Choose scope (user/local) [user]: ' + : 'Install scope - user (all projects) or local (this project only) [user]: '; + const answer = await new Promise((resolve) => { - rl.question('Choose scope (user/local) [user]: ', (input) => { + rl.question(prompt, (input) => { rl.close(); resolve(input.trim().toLowerCase() || 'user'); }); @@ -99,7 +243,9 @@ export const initCommand = new Command('init') console.error('āŒ Invalid scope. Use "user" or "local"\n'); process.exit(1); } - console.log(); + if (verbose) { + console.log(); + } } } @@ -116,9 +262,11 @@ export const initCommand = new Command('init') // Cache git root for later use (already computed in getInstallationPaths for local scope) gitRoot = await getGitRoot(); - console.log(`šŸ“ Installation scope: ${scope}`); - console.log(` Claude dir: ${claudeDir}`); - console.log(` DevFlow dir: ${devflowDir}\n`); + if (verbose) { + console.log(`šŸ“ Installation scope: ${scope}`); + console.log(` Claude dir: ${claudeDir}`); + console.log(` DevFlow dir: ${devflowDir}\n`); + } } catch (error) { console.error('āŒ Path configuration error:', error instanceof Error ? error.message : error); process.exit(1); @@ -134,12 +282,16 @@ export const initCommand = new Command('init') console.error(' Or set CLAUDE_CODE_DIR if installed elsewhere\n'); process.exit(1); } - console.log('āœ“ Claude Code detected'); + if (verbose) { + console.log('āœ“ Claude Code detected'); + } } else { // Local scope - create .claude directory if it doesn't exist try { await fs.mkdir(claudeDir, { recursive: true }); - console.log('āœ“ Local .claude directory ready'); + if (verbose) { + console.log('āœ“ Local .claude directory ready'); + } } catch (error) { console.error(`āŒ Failed to create ${claudeDir}:`, error); process.exit(1); @@ -226,7 +378,9 @@ export const initCommand = new Command('init') await fs.chmod(path.join(scriptsDir, script), 0o755); } - console.log('āœ“ Installing components... (commands, agents, skills, scripts)'); + if (verbose) { + console.log('āœ“ Installing components... (commands, agents, skills, scripts)'); + } // Install settings.json - never override existing files (atomic operation) const settingsPath = path.join(claudeDir, 'settings.json'); @@ -244,13 +398,17 @@ export const initCommand = new Command('init') try { // Atomic exclusive create - fails if file already exists await fs.writeFile(settingsPath, settingsContent, { encoding: 'utf-8', flag: 'wx' }); - console.log('āœ“ Settings configured'); + if (verbose) { + console.log('āœ“ Settings configured'); + } } catch (error: unknown) { if (isNodeSystemError(error) && error.code === 'EEXIST') { // Existing settings.json found - install as settings.devflow.json settingsExists = true; await fs.writeFile(devflowSettingsPath, settingsContent, 'utf-8'); - console.log('āš ļø Existing settings.json preserved → DevFlow config: settings.devflow.json'); + if (verbose) { + console.log('āš ļø Existing settings.json preserved → DevFlow config: settings.devflow.json'); + } } else { throw error; } @@ -266,13 +424,17 @@ export const initCommand = new Command('init') // Atomic exclusive create - fails if file already exists const content = await fs.readFile(sourceClaudeMdPath, 'utf-8'); await fs.writeFile(claudeMdPath, content, { encoding: 'utf-8', flag: 'wx' }); - console.log('āœ“ CLAUDE.md configured'); + if (verbose) { + console.log('āœ“ CLAUDE.md configured'); + } } catch (error: unknown) { if (isNodeSystemError(error) && error.code === 'EEXIST') { // Existing CLAUDE.md found - install as CLAUDE.devflow.md claudeMdExists = true; await fs.copyFile(sourceClaudeMdPath, devflowClaudeMdPath); - console.log('āš ļø Existing CLAUDE.md preserved → DevFlow guide: CLAUDE.devflow.md'); + if (verbose) { + console.log('āš ļø Existing CLAUDE.md preserved → DevFlow guide: CLAUDE.devflow.md'); + } } else { throw error; } @@ -486,7 +648,7 @@ Pipfile.lock // Not a git repository or other error - skip .claudeignore creation } - if (claudeignoreCreated) { + if (claudeignoreCreated && verbose) { console.log('āœ“ .claudeignore created'); } @@ -517,10 +679,14 @@ Pipfile.lock : `# DevFlow local scope installation\n${linesToAdd.join('\n')}\n`; await fs.writeFile(gitignorePath, newContent, 'utf-8'); - console.log('āœ“ .gitignore updated (excluded .claude/ and .devflow/)'); + if (verbose) { + console.log('āœ“ .gitignore updated (excluded .claude/ and .devflow/)'); + } } } catch (error) { - console.warn('āš ļø Could not update .gitignore:', error instanceof Error ? error.message : error); + if (verbose) { + console.warn('āš ļø Could not update .gitignore:', error instanceof Error ? error.message : error); + } } } @@ -540,49 +706,16 @@ Pipfile.lock } } - if (docsCreated) { + if (docsCreated && verbose) { console.log('āœ“ .docs/ structure ready'); } - console.log('\nāœ… Installation complete!\n'); - - // Show manual merge instructions if needed - if (settingsExists || claudeMdExists) { - console.log('šŸ“ Manual merge recommended:\n'); - if (settingsExists) { - console.log(' Settings: Review settings.devflow.json and merge desired config into settings.json'); - console.log(' Key setting: statusLine configuration for DevFlow statusline\n'); - } - if (claudeMdExists) { - console.log(' Instructions: Review CLAUDE.devflow.md and adopt desired practices'); - console.log(' This contains DevFlow\'s recommended development patterns\n'); - } + // Render output based on verbose flag + if (verbose) { + renderVerboseOutput(version, scope, claudeDir, devflowDir, settingsExists, claudeMdExists); + } else { + renderCleanOutput(version); } - - console.log('Available commands:'); - console.log(' /catch-up Session context and status'); - console.log(' /brainstorm Explore design decisions and approaches'); - console.log(' /design Create detailed implementation plan'); - console.log(' /debug Systematic debugging (manual)'); - console.log(' /plan Triage issues - now, defer to GH issue, or skip'); - console.log(' /breakdown Quickly break down discussion into tasks'); - console.log(' /implement Interactive implementation orchestrator'); - console.log(' /code-review Comprehensive code review'); - console.log(' /commit Intelligent atomic commits'); - console.log(' /pull-request Create PR with smart description'); - console.log(' /resolve-comments Address PR review feedback'); - console.log(' /devlog Session documentation'); - console.log(' /release Release automation'); - console.log('\nInstalled skills (auto-activate):'); - console.log(' pattern-check Architectural pattern validation'); - console.log(' test-design Test quality enforcement'); - console.log(' code-smell Anti-pattern detection'); - console.log(' research Pre-implementation planning (auto)'); - console.log(' debug Systematic debugging (auto)'); - console.log(' input-validation Boundary validation'); - console.log(' error-handling Result type consistency'); - console.log('\nNote: debug exists as both command (manual) and skill (auto)'); - console.log('Docs: npm home devflow-kit'); } catch (error) { console.error('āŒ Installation failed:', error); process.exit(1);