diff --git a/.claude/commands/workflow/init-guidelines.md b/.claude/commands/workflow/init-guidelines.md new file mode 100644 index 00000000..ab7c3ecb --- /dev/null +++ b/.claude/commands/workflow/init-guidelines.md @@ -0,0 +1,408 @@ +--- +name: init-guidelines +description: Interactive wizard to fill project-guidelines.json based on project analysis +argument-hint: "[--reset]" +examples: + - /workflow:init-guidelines + - /workflow:init-guidelines --reset +--- + +# Workflow Init Guidelines Command (/workflow:init-guidelines) + +## Overview + +Interactive multi-round wizard that analyzes the current project (via `project-tech.json`) and asks targeted questions to populate `.workflow/project-guidelines.json` with coding conventions, constraints, and quality rules. + +**Design Principle**: Questions are dynamically generated based on the project's tech stack, architecture, and patterns — not generic boilerplate. + +**Note**: This command may be called by `/workflow:init` after initialization. Upon completion, return to the calling workflow if applicable. + +## Usage +```bash +/workflow:init-guidelines # Fill guidelines interactively (skip if already populated) +/workflow:init-guidelines --reset # Reset and re-fill guidelines from scratch +``` + +## Execution Process + +``` +Input Parsing: + └─ Parse --reset flag → reset = true | false + +Step 1: Check Prerequisites + ├─ project-tech.json must exist (run /workflow:init first) + ├─ project-guidelines.json: check if populated or scaffold-only + └─ If populated + no --reset → Ask: "Guidelines already exist. Overwrite or append?" + +Step 2: Load Project Context + └─ Read project-tech.json → extract tech stack, architecture, patterns + +Step 3: Multi-Round Interactive Questionnaire + ├─ Round 1: Coding Conventions (coding_style, naming_patterns) + ├─ Round 2: File & Documentation Conventions (file_structure, documentation) + ├─ Round 3: Architecture & Tech Constraints (architecture, tech_stack) + ├─ Round 4: Performance & Security Constraints (performance, security) + └─ Round 5: Quality Rules (quality_rules) + +Step 4: Write project-guidelines.json + +Step 5: Display Summary +``` + +## Implementation + +### Step 1: Check Prerequisites + +```bash +bash(test -f .workflow/project-tech.json && echo "TECH_EXISTS" || echo "TECH_NOT_FOUND") +bash(test -f .workflow/project-guidelines.json && echo "GUIDELINES_EXISTS" || echo "GUIDELINES_NOT_FOUND") +``` + +**If TECH_NOT_FOUND**: Exit with message +``` +Project tech analysis not found. Run /workflow:init first. +``` + +**Parse --reset flag**: +```javascript +const reset = $ARGUMENTS.includes('--reset') +``` + +**If GUIDELINES_EXISTS and not --reset**: Check if guidelines are populated (not just scaffold) + +```javascript +const guidelines = JSON.parse(Read('.workflow/project-guidelines.json')) +const isPopulated = + guidelines.conventions.coding_style.length > 0 || + guidelines.conventions.naming_patterns.length > 0 || + guidelines.constraints.architecture.length > 0 || + guidelines.constraints.tech_stack.length > 0 + +if (isPopulated) { + AskUserQuestion({ + questions: [{ + question: "Project guidelines already contain entries. How would you like to proceed?", + header: "Mode", + multiSelect: false, + options: [ + { label: "Append", description: "Keep existing entries and add new ones from the wizard" }, + { label: "Reset", description: "Clear all existing entries and start fresh" }, + { label: "Cancel", description: "Exit without changes" } + ] + }] + }) + // If Cancel → exit + // If Reset → clear all arrays before proceeding + // If Append → keep existing, wizard adds to them +} +``` + +### Step 2: Load Project Context + +```javascript +const projectTech = JSON.parse(Read('.workflow/project-tech.json')) + +// Extract key info for generating smart questions +const languages = projectTech.technology_analysis?.technology_stack?.languages + || projectTech.overview?.technology_stack?.languages || [] +const primaryLang = languages.find(l => l.primary)?.name || languages[0]?.name || 'Unknown' +const frameworks = projectTech.technology_analysis?.technology_stack?.frameworks + || projectTech.overview?.technology_stack?.frameworks || [] +const testFrameworks = projectTech.technology_analysis?.technology_stack?.test_frameworks + || projectTech.overview?.technology_stack?.test_frameworks || [] +const archStyle = projectTech.technology_analysis?.architecture?.style + || projectTech.overview?.architecture?.style || 'Unknown' +const archPatterns = projectTech.technology_analysis?.architecture?.patterns + || projectTech.overview?.architecture?.patterns || [] +const buildTools = projectTech.technology_analysis?.technology_stack?.build_tools + || projectTech.overview?.technology_stack?.build_tools || [] +``` + +### Step 3: Multi-Round Interactive Questionnaire + +Each round uses `AskUserQuestion` with project-aware options. The user can always select "Other" to provide custom input. + +**⚠️ CRITICAL**: After each round, collect the user's answers and convert them into guideline entries. Do NOT batch all rounds — process each round's answers before proceeding to the next. + +--- + +#### Round 1: Coding Conventions + +Generate options dynamically based on detected language/framework: + +```javascript +// Build language-specific coding style options +const codingStyleOptions = [] + +if (['TypeScript', 'JavaScript'].includes(primaryLang)) { + codingStyleOptions.push( + { label: "Strict TypeScript", description: "Use strict mode, no 'any' type, explicit return types for public APIs" }, + { label: "Functional style", description: "Prefer pure functions, immutability, avoid class-based patterns where possible" }, + { label: "Const over let", description: "Always use const; only use let when reassignment is truly needed" } + ) +} else if (primaryLang === 'Python') { + codingStyleOptions.push( + { label: "Type hints", description: "Use type hints for all function signatures and class attributes" }, + { label: "Functional style", description: "Prefer pure functions, list comprehensions, avoid mutable state" }, + { label: "PEP 8 strict", description: "Strict PEP 8 compliance with max line length 88 (Black formatter)" } + ) +} else if (primaryLang === 'Go') { + codingStyleOptions.push( + { label: "Error wrapping", description: "Always wrap errors with context using fmt.Errorf with %w" }, + { label: "Interface first", description: "Define interfaces at the consumer side, not the provider" }, + { label: "Table-driven tests", description: "Use table-driven test pattern for all unit tests" } + ) +} +// Add universal options +codingStyleOptions.push( + { label: "Early returns", description: "Prefer early returns / guard clauses over deep nesting" } +) + +AskUserQuestion({ + questions: [ + { + question: `Your project uses ${primaryLang}. Which coding style conventions do you follow?`, + header: "Coding Style", + multiSelect: true, + options: codingStyleOptions.slice(0, 4) // Max 4 options + }, + { + question: `What naming conventions does your ${primaryLang} project use?`, + header: "Naming", + multiSelect: true, + options: [ + { label: "camelCase variables", description: "Variables and functions use camelCase (e.g., getUserName)" }, + { label: "PascalCase types", description: "Classes, interfaces, type aliases use PascalCase (e.g., UserService)" }, + { label: "UPPER_SNAKE constants", description: "Constants use UPPER_SNAKE_CASE (e.g., MAX_RETRIES)" }, + { label: "Prefix interfaces", description: "Prefix interfaces with 'I' (e.g., IUserService)" } + ] + } + ] +}) +``` + +**Process Round 1 answers** → add to `conventions.coding_style` and `conventions.naming_patterns` arrays. + +--- + +#### Round 2: File Structure & Documentation + +```javascript +AskUserQuestion({ + questions: [ + { + question: `Your project has a ${archStyle} architecture. What file organization rules apply?`, + header: "File Structure", + multiSelect: true, + options: [ + { label: "Co-located tests", description: "Test files live next to source files (e.g., foo.ts + foo.test.ts)" }, + { label: "Separate test dir", description: "Tests in a dedicated __tests__ or tests/ directory" }, + { label: "One export per file", description: "Each file exports a single main component/class/function" }, + { label: "Index barrels", description: "Use index.ts barrel files for clean imports from directories" } + ] + }, + { + question: "What documentation standards does your project follow?", + header: "Documentation", + multiSelect: true, + options: [ + { label: "JSDoc/docstring public APIs", description: "All public functions and classes must have JSDoc/docstrings" }, + { label: "README per module", description: "Each major module/package has its own README" }, + { label: "Inline comments for why", description: "Comments explain 'why', not 'what' — code should be self-documenting" }, + { label: "No comment requirement", description: "Code should be self-explanatory; comments only for non-obvious logic" } + ] + } + ] +}) +``` + +**Process Round 2 answers** → add to `conventions.file_structure` and `conventions.documentation`. + +--- + +#### Round 3: Architecture & Tech Stack Constraints + +```javascript +// Build architecture-specific options +const archOptions = [] + +if (archStyle.toLowerCase().includes('monolith')) { + archOptions.push( + { label: "No circular deps", description: "Modules must not have circular dependencies" }, + { label: "Layer boundaries", description: "Strict layer separation: UI → Service → Data (no skipping layers)" } + ) +} else if (archStyle.toLowerCase().includes('microservice')) { + archOptions.push( + { label: "Service isolation", description: "Services must not share databases or internal state" }, + { label: "API contracts", description: "All inter-service communication through versioned API contracts" } + ) +} +archOptions.push( + { label: "Stateless services", description: "Service/business logic must be stateless (state in DB/cache only)" }, + { label: "Dependency injection", description: "Use dependency injection for testability, no hardcoded dependencies" } +) + +AskUserQuestion({ + questions: [ + { + question: `Your ${archStyle} architecture uses ${archPatterns.join(', ') || 'various'} patterns. What architecture constraints apply?`, + header: "Architecture", + multiSelect: true, + options: archOptions.slice(0, 4) + }, + { + question: `Tech stack: ${frameworks.join(', ')}. What technology constraints apply?`, + header: "Tech Stack", + multiSelect: true, + options: [ + { label: "No new deps without review", description: "Adding new dependencies requires explicit justification and review" }, + { label: "Pin dependency versions", description: "All dependencies must use exact versions, not ranges" }, + { label: "Prefer native APIs", description: "Use built-in/native APIs over third-party libraries when possible" }, + { label: "Framework conventions", description: `Follow official ${frameworks[0] || 'framework'} conventions and best practices` } + ] + } + ] +}) +``` + +**Process Round 3 answers** → add to `constraints.architecture` and `constraints.tech_stack`. + +--- + +#### Round 4: Performance & Security Constraints + +```javascript +AskUserQuestion({ + questions: [ + { + question: "What performance requirements does your project have?", + header: "Performance", + multiSelect: true, + options: [ + { label: "API response time", description: "API endpoints must respond within 200ms (p95)" }, + { label: "Bundle size limit", description: "Frontend bundle size must stay under 500KB gzipped" }, + { label: "Lazy loading", description: "Large modules/routes must use lazy loading / code splitting" }, + { label: "No N+1 queries", description: "Database access must avoid N+1 query patterns" } + ] + }, + { + question: "What security requirements does your project enforce?", + header: "Security", + multiSelect: true, + options: [ + { label: "Input sanitization", description: "All user input must be validated and sanitized before use" }, + { label: "No secrets in code", description: "No API keys, passwords, or tokens in source code — use env vars" }, + { label: "Auth on all endpoints", description: "All API endpoints require authentication unless explicitly public" }, + { label: "Parameterized queries", description: "All database queries must use parameterized/prepared statements" } + ] + } + ] +}) +``` + +**Process Round 4 answers** → add to `constraints.performance` and `constraints.security`. + +--- + +#### Round 5: Quality Rules + +```javascript +AskUserQuestion({ + questions: [ + { + question: `Testing with ${testFrameworks.join(', ') || 'your test framework'}. What quality rules apply?`, + header: "Quality", + multiSelect: true, + options: [ + { label: "Min test coverage", description: "Minimum 80% code coverage for new code; no merging below threshold" }, + { label: "No skipped tests", description: "Tests must not be skipped (.skip/.only) in committed code" }, + { label: "Lint must pass", description: "All code must pass linter checks before commit (enforced by pre-commit)" }, + { label: "Type check must pass", description: "Full type checking (tsc --noEmit) must pass with zero errors" } + ] + } + ] +}) +``` + +**Process Round 5 answers** → add to `quality_rules` array as `{ rule, scope, enforced_by }` objects. + +### Step 4: Write project-guidelines.json + +```javascript +// Build the final guidelines object +const finalGuidelines = { + conventions: { + coding_style: existingCodingStyle.concat(newCodingStyle), + naming_patterns: existingNamingPatterns.concat(newNamingPatterns), + file_structure: existingFileStructure.concat(newFileStructure), + documentation: existingDocumentation.concat(newDocumentation) + }, + constraints: { + architecture: existingArchitecture.concat(newArchitecture), + tech_stack: existingTechStack.concat(newTechStack), + performance: existingPerformance.concat(newPerformance), + security: existingSecurity.concat(newSecurity) + }, + quality_rules: existingQualityRules.concat(newQualityRules), + learnings: existingLearnings, // Preserve existing learnings + _metadata: { + created_at: existingMetadata?.created_at || new Date().toISOString(), + version: "1.0.0", + last_updated: new Date().toISOString(), + updated_by: "workflow:init-guidelines" + } +} + +Write('.workflow/project-guidelines.json', JSON.stringify(finalGuidelines, null, 2)) +``` + +### Step 5: Display Summary + +```javascript +const countConventions = finalGuidelines.conventions.coding_style.length + + finalGuidelines.conventions.naming_patterns.length + + finalGuidelines.conventions.file_structure.length + + finalGuidelines.conventions.documentation.length + +const countConstraints = finalGuidelines.constraints.architecture.length + + finalGuidelines.constraints.tech_stack.length + + finalGuidelines.constraints.performance.length + + finalGuidelines.constraints.security.length + +const countQuality = finalGuidelines.quality_rules.length + +console.log(` +✓ Project guidelines configured + +## Summary +- Conventions: ${countConventions} rules (coding: ${cs}, naming: ${np}, files: ${fs}, docs: ${doc}) +- Constraints: ${countConstraints} rules (arch: ${ar}, tech: ${ts}, perf: ${pf}, security: ${sc}) +- Quality rules: ${countQuality} + +File: .workflow/project-guidelines.json + +Next steps: +- Use /workflow:session:solidify to add individual rules later +- Guidelines will be auto-loaded by /workflow:plan for task generation +`) +``` + +## Answer Processing Rules + +When converting user selections to guideline entries: + +1. **Selected option** → Use the option's `description` as the guideline string (it's more precise than the label) +2. **"Other" with custom text** → Use the user's text directly as the guideline string +3. **Deduplication** → Skip entries that already exist in the guidelines (exact string match) +4. **Quality rules** → Convert to `{ rule: description, scope: "all", enforced_by: "code-review" }` format + +## Error Handling + +- **No project-tech.json**: Exit with instruction to run `/workflow:init` first +- **User cancels mid-wizard**: Save whatever was collected so far (partial is better than nothing) +- **File write failure**: Report error, suggest manual edit + +## Related Commands + +- `/workflow:init` - Creates scaffold; optionally calls this command +- `/workflow:session:solidify` - Add individual rules one at a time diff --git a/.claude/commands/workflow/init.md b/.claude/commands/workflow/init.md index 686264ff..49d6bca5 100644 --- a/.claude/commands/workflow/init.md +++ b/.claude/commands/workflow/init.md @@ -44,11 +44,16 @@ Analysis Flow: │ └─ Write .workflow/project-tech.json ├─ Create guidelines scaffold (if not exists) │ └─ Write .workflow/project-guidelines.json (empty structure) - └─ Display summary + ├─ Display summary + └─ Ask about guidelines configuration + ├─ If guidelines empty → Ask user: "Configure now?" or "Skip" + │ ├─ Configure now → SlashCommand(/workflow:init-guidelines) + │ └─ Skip → Show next steps + └─ If guidelines populated → Show next steps only Output: ├─ .workflow/project-tech.json (+ .backup if regenerate) - └─ .workflow/project-guidelines.json (scaffold if new) + └─ .workflow/project-guidelines.json (scaffold or configured) ``` ## Implementation @@ -210,11 +215,63 @@ Files created: - Tech analysis: .workflow/project-tech.json - Guidelines: .workflow/project-guidelines.json ${guidelinesExists ? '(scaffold)' : ''} ${regenerate ? '- Backup: .workflow/project-tech.json.backup' : ''} +`); +``` +### Step 5: Ask About Guidelines Configuration + +After displaying the summary, ask the user if they want to configure project guidelines interactively. + +```javascript +// Check if guidelines are just a scaffold (empty) or already populated +const guidelines = JSON.parse(Read('.workflow/project-guidelines.json')); +const isGuidelinesPopulated = + guidelines.conventions.coding_style.length > 0 || + guidelines.conventions.naming_patterns.length > 0 || + guidelines.constraints.architecture.length > 0 || + guidelines.constraints.security.length > 0; + +// Only ask if guidelines are not yet populated +if (!isGuidelinesPopulated) { + const userChoice = AskUserQuestion({ + questions: [{ + question: "Would you like to configure project guidelines now? The wizard will ask targeted questions based on your tech stack.", + header: "Guidelines", + multiSelect: false, + options: [ + { + label: "Configure now (Recommended)", + description: "Interactive wizard to set up coding conventions, constraints, and quality rules" + }, + { + label: "Skip for now", + description: "You can run /workflow:init-guidelines later or use /workflow:session:solidify to add rules individually" + } + ] + }] + }); + + if (userChoice.answers["Guidelines"] === "Configure now (Recommended)") { + console.log("\n🔧 Starting guidelines configuration wizard...\n"); + SlashCommand(command="/workflow:init-guidelines"); + } else { + console.log(` Next steps: -- Use /workflow:session:solidify to add project guidelines +- Use /workflow:init-guidelines to configure guidelines interactively +- Use /workflow:session:solidify to add individual rules - Use /workflow:plan to start planning `); + } +} else { + console.log(` +Guidelines already configured (${guidelines.conventions.coding_style.length + guidelines.constraints.architecture.length}+ rules). + +Next steps: +- Use /workflow:init-guidelines --reset to reconfigure +- Use /workflow:session:solidify to add individual rules +- Use /workflow:plan to start planning +`); +} ``` ## Error Handling @@ -222,3 +279,10 @@ Next steps: **Agent Failure**: Fall back to basic initialization with placeholder overview **Missing Tools**: Agent uses Qwen fallback or bash-only **Empty Project**: Create minimal JSON with all gaps identified + +## Related Commands + +- `/workflow:init-guidelines` - Interactive wizard to configure project guidelines (called after init) +- `/workflow:session:solidify` - Add individual rules/constraints one at a time +- `/workflow:plan` - Start planning with initialized project context +- `/workflow:status --project` - View project state and guidelines