mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
feat(workflow): add interactive init-guidelines wizard for project guidelines configuration
Add /workflow:init-guidelines command that interactively fills project-guidelines.json based on project analysis from project-tech.json. Features: - 5-round questionnaire covering coding style, naming, file structure, documentation, architecture, tech stack, performance, security, and quality rules - Dynamic question generation based on detected language/framework/architecture - Append/reset mode for existing guidelines - Multi-select options with "Other" for custom input Modify /workflow:init to ask user whether to run init-guidelines after initialization when guidelines are empty (scaffold only).
This commit is contained in:
408
.claude/commands/workflow/init-guidelines.md
Normal file
408
.claude/commands/workflow/init-guidelines.md
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user