From 014b13b972144873b9e603edc865f1e86902dafa Mon Sep 17 00:00:00 2001 From: Dean Sharon Date: Mon, 1 Dec 2025 21:25:00 +0000 Subject: [PATCH 1/2] feat(cli): add --verbose flag for clean default init output (#21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor `devflow init` to show clean, command-focused output by default. Users who want detailed installation progress can use `--verbose` flag. Changes: - Add DEVFLOW_COMMANDS constant with user-friendly descriptions - Add DEVFLOW_SKILLS constant for verbose output - Create renderCleanOutput() for minimal default mode - Create renderVerboseOutput() preserving current detailed behavior - Wrap all progress/status logging in verbose checks - Update CLI help text with --verbose example Closes #21 šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/cli/cli.ts | 2 +- src/cli/commands/init.ts | 221 ++++++++++++++++++++++++++++----------- 2 files changed, 160 insertions(+), 63 deletions(-) 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..3d07977c 100644 --- a/src/cli/commands/init.ts +++ b/src/cli/commands/init.ts @@ -42,10 +42,106 @@ async function promptUser(question: string): Promise { }); } +/** + * DevFlow commands with user-friendly descriptions + */ +const DEVFLOW_COMMANDS = [ + { 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 for verbose output + */ +const DEVFLOW_SKILLS = [ + { 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 output for default mode + */ +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 verbose output preserving current detailed behavior + */ +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) + .option('--verbose', 'Show detailed installation output') .action(async (options) => { // Get package version const packageJsonPath = path.resolve(__dirname, '../../package.json'); @@ -57,7 +153,11 @@ 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 @@ -68,24 +168,32 @@ export const initCommand = new Command('init') // 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 +207,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 +226,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 +246,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 +342,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 +362,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 +388,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 +612,7 @@ Pipfile.lock // Not a git repository or other error - skip .claudeignore creation } - if (claudeignoreCreated) { + if (claudeignoreCreated && verbose) { console.log('āœ“ .claudeignore created'); } @@ -517,10 +643,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 +670,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); From 9ef01874cea1339fe831de912e0e7bdad1d2e491 Mon Sep 17 00:00:00 2001 From: Dean Sharon Date: Mon, 1 Dec 2025 23:08:37 +0000 Subject: [PATCH 2/2] docs: add changelog entry and fix code review issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add [Unreleased] section to CHANGELOG.md for --verbose flag - Update README.md CLI commands table with --verbose option - Add JSDoc documentation for renderCleanOutput/renderVerboseOutput - Add InitOptions and CommandDefinition interfaces for type safety - Add runtime validation for scope parameter - Use nullish coalescing for verbose flag Addresses code review feedback from PR #22 šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 14 +++++++++++ README.md | 2 +- src/cli/commands/init.ts | 54 +++++++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 10 deletions(-) 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/commands/init.ts b/src/cli/commands/init.ts index 3d07977c..73841b40 100644 --- a/src/cli/commands/init.ts +++ b/src/cli/commands/init.ts @@ -43,9 +43,27 @@ async function promptUser(question: string): Promise { } /** - * DevFlow commands with user-friendly descriptions + * Options for the init command parsed by Commander.js */ -const DEVFLOW_COMMANDS = [ +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' }, @@ -62,9 +80,10 @@ const DEVFLOW_COMMANDS = [ ]; /** - * DevFlow skills for verbose output + * DevFlow skills with descriptions. + * Displayed only in verbose mode to show auto-activating capabilities. */ -const DEVFLOW_SKILLS = [ +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' }, @@ -75,7 +94,10 @@ const DEVFLOW_SKILLS = [ ]; /** - * Render clean output for default mode + * 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`); @@ -94,7 +116,15 @@ function renderCleanOutput(version: string): void { } /** - * Render verbose output preserving current detailed behavior + * 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, @@ -142,7 +172,7 @@ export const initCommand = new Command('init') .option('--skip-docs', 'Skip creating .docs/ structure') .option('--scope ', 'Installation scope: user (user-wide) or local (project-only)', /^(user|local)$/i) .option('--verbose', 'Show detailed installation output') - .action(async (options) => { + .action(async (options: InitOptions) => { // Get package version const packageJsonPath = path.resolve(__dirname, '../../package.json'); let version = ''; @@ -153,7 +183,7 @@ export const initCommand = new Command('init') version = 'unknown'; } - const verbose = options.verbose || false; + const verbose = options.verbose ?? false; if (verbose) { console.log(`šŸš€ DevFlow v${version}\n`); @@ -163,7 +193,13 @@ export const initCommand = new Command('init') 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) {