How vibe-init is built: module structure, data flow, design decisions, and extension points.
graph TB
subgraph CLI["CLI Layer — Commander.js"]
direction LR
init["vibe init"]
build["vibe build"]
scan["vibe scan"]
add["vibe add"]
audit["vibe audit"]
anchor["vibe anchor"]
run["vibe run"]
ask["vibe ask"]
end
subgraph PHASES["Command Phases"]
framework["framework.ts
CLAUDE.md + Skills"]
ignition["ignition.ts
Capture Idea"]
enrichment["enrichment.ts
Claude Enrichment"]
adr["adr.ts
ADR Generation"]
scaffold["scaffold.ts
Template Rendering"]
end
subgraph ANALYZERS["Analyzers"]
detector["detector.ts
Orchestrator"]
stacks["Stack Detectors
Next.js · FastAPI · Go · Node"]
practices["Practice Detectors
10 categories"]
end
subgraph GOV["Governance"]
registry["registry.ts
10 Categories"]
yaml_gen["yaml-generator.ts
59 Policy Files"]
health["health.ts
Scoring Engine"]
end
subgraph FEATURES["Feature Modules"]
feat_reg["registry.ts
DI Container"]
template_feat["Template-based
Docker · CI · Testing
Logging · Health · Hooks"]
claude_feat["Claude-powered
API · Component · Model"]
end
subgraph AI["AI Layer"]
cli_ai["cli.ts
Claude CLI Spawner"]
api_ai["api.ts
Anthropic SDK"]
prompts["prompts/
6 Prompt Builders"]
end
init --> framework
init --> yaml_gen
build --> ignition --> enrichment --> adr
scan --> detector
add --> feat_reg
audit --> registry --> health
anchor --> framework
detector --> stacks
detector --> practices
feat_reg --> template_feat
feat_reg --> claude_feat
enrichment --> cli_ai
framework --> api_ai
cli_ai --> prompts
api_ai --> prompts
run --> cli_ai
ask --> cli_ai
flowchart LR
A["Project Dir"] --> B{"Greenfield
or Brownfield?"}
B -->|Brownfield| C["Stack + Practice
Detection"]
B -->|Greenfield| D["Ask Project Name
+ Stack Preference"]
C --> D
D --> E["Generate CLAUDE.md
via Anthropic SDK"]
D --> F["Generate 59 YAML
Governance Policies"]
D --> G["Auto-detect Skills
→ .claude/commands/"]
D --> H["Generate Settings
+ ADR Template"]
E --> I["writeFileTree()"]
F --> I
G --> I
H --> I
flowchart LR
A["CLAUDE.md
must exist"] --> B["Ignition
Capture idea + name"]
B --> C["Claude Enrichment
Personas · Features · Arch"]
C --> D{"User Review"}
D -->|Edit| C
D -->|Restart| B
D -->|Accept| E["Generate ADR"]
E --> F["Generate README"]
F --> G["Spawn Claude Code
CLAUDE.md + Brief + ADR"]
G --> H["Built Project
With Full Governance"]
flowchart LR
A["Project Dir"] --> B["59 Policy Checks
10 Categories"]
B --> C["Security
9 rules"]
B --> D["Clean Code
10 rules"]
B --> E["Reliability
8 rules"]
B --> F["12-Factor
7 rules"]
B --> G["+ 6 more
categories"]
C --> H["Weighted Score
Calculation"]
D --> H
E --> H
F --> H
G --> H
H --> I["Grade: A+ to F
+ Fix Suggestions"]
graph LR
subgraph INTERACTIVE["Claude CLI — Interactive"]
init_enrich["init enrichment"]
run_cmd["vibe run"]
ask_cmd["vibe ask"]
claude_gen["Claude-powered generators"]
end
subgraph BATCH["Anthropic SDK — Batch"]
readme["README.md generation"]
claudemd["CLAUDE.md generation"]
adr_gen["ADR generation"]
end
CLI_PROC["Claude CLI Process
Streaming · Real-time"] --- INTERACTIVE
API_REQ["Anthropic API
Request/Response · Structured"] --- BATCH
vibe-init uses two distinct AI integration paths:
| Path | Used for | Why |
|---|---|---|
| Claude CLI | Interactive features: init enrichment, run, ask, Claude-powered generators |
Streaming output, real-time user interaction, interactive sessions |
| Anthropic SDK | Batch generation: README.md, CLAUDE.md, ADR | Fire-and-forget, structured output, no interaction needed |
This split keeps interactive commands responsive while batch operations run with simpler request/response semantics.
Feature modules are registered in a central registry (features/registry.ts) that acts as a dependency injection container. Each module implements the FeatureModule interface:
interface FeatureModule {
id: string;
name: string;
description: string;
detect(projectDir: string): boolean;
apply(projectDir: string, options: FeatureApplyOptions): ApplyResult;
}
This makes it trivial to add new features — create a module, implement the interface, register it.
Stack detectors run in order of specificity:
next.config.*requirements.txt, pyproject.tomlgo.modpackage.json (fallback)First match wins. This prevents false positives (e.g., a Next.js project also has package.json, but should be detected as Next.js, not generic Node).
The doctor command uses a weighted scoring system rather than simple pass/fail:
score = Math.round((earnedWeight / totalWeight) * 100)
grade = lookupGrade(score) // A+ through F
Weights reflect relative importance: testing (20pts) and security (20pts) matter more than documentation (10pts). Each check maps to a vibe add command so failures are directly actionable.
fileExists(), readPackageJson() for detectioncheckClaudeCli(), checkAnthropicApiKey() before AI operationsEvery write operation respects the --dry-run flag. This enables safe previewing of all file operations without committing to disk. Critical for building user confidence.
Project directory
→ Detect project type (greenfield vs brownfield)
→ [If brownfield] Run stack + practice detection
→ Ask project name + stack preference
→ Generate CLAUDE.md via Claude (stack-aware, context-aware)
→ Generate .claude/commands/ skills (test, lint, build, commit, review)
→ Generate .claude/settings.json (permissions)
→ Generate docs/adr/000-template.md
→ writeFileTree() → framework files on disk
CLAUDE.md must exist (run vibe init first)
→ [Ignition] Capture idea + project name
→ [Enrichment] Claude CLI → EnrichmentBrief (personas, features, arch)
→ User review loop: Accept / Edit / Restart
→ [ADR] Generate Architecture Decision Record → write to docs/adr/
→ [README] Generate README.md
→ [Build] Spawn Claude Code interactively with system prompt:
CLAUDE.md + EnrichmentBrief + ADR
→ Claude Code builds the project following the framework
Project directory
→ Stack detectors (ordered, first match wins)
→ Practice detectors (10 in parallel)
→ Build recommendations for missing practices
→ ProjectAnalysis { stack, practices, missing, recommendations }
→ [Optional] Anthropic API → CLAUDE.md file
Feature name + args
→ Registry lookup → FeatureModule
→ Stack detection (quick heuristic)
→ feature.detect() → already installed?
→ feature.apply() → template rendering or Claude generation
→ writeFeatureFiles() → files on disk
→ Print results + next steps
Project directory
→ 17 health checks (each has weight + detector function)
→ Weighted score calculation
→ Grade lookup (A+ through F)
→ HealthReport { score, grade, categories, checks[] }
→ Formatted terminal report with fix suggestions
Custom error hierarchy rooted in VibeError:
VibeError (base)
├── ClaudeCliError — Claude CLI not found or failed
├── ApiKeyMissingError — ANTHROPIC_API_KEY not set
├── TemplateError — EJS rendering failure
├── ValidationError — Zod parsing failure (invalid Claude response)
├── FileSystemError — File read/write failures
└── FeatureError — Feature detection or apply failures
Each error includes a userMessage (user-facing, actionable) and debugInfo (internal details, shown with --verbose). The theme system color-codes error output (red for errors, yellow for warnings, green for success).
# Development
npm run dev # esbuild watch mode
# Production
npm run lint # tsc --noEmit (type check only)
npm run test # vitest (43 tests, 80% coverage thresholds)
npm run build # esbuild bundle → build/index.js
# + copy templates to build/
# CI (GitHub Actions)
push/PR → npm ci → lint → test → build → verify executable
The build process uses esbuild for sub-second bundling. It produces a single ESM bundle (build/index.js) with a shebang, then copies EJS templates and feature templates alongside it. External dependencies (@anthropic-ai/sdk, commander, etc.) are bundled into the output.
The architecture supports extension in several ways:
| Extension | How |
|---|---|
| New stack templates | Add a directory under src/templates/stacks/, register it in templates/registry.ts |
| New feature modules | Create a module implementing FeatureModule, register in features/registry.ts |
| New health checks | Add entries to the checks array in scoring/checks.ts |
| New stack detectors | Add a detector to analyzers/stack-detectors/, insert in the ordered list in detector.ts |
| New practice detectors | Add a detector to analyzers/practice-detectors/, register in detector.ts |
| Component | Technology | Why |
|---|---|---|
| CLI framework | Commander.js | Mature, minimal API, great help generation |
| Interactive prompts | Inquirer.js | Rich prompt types (confirm, input, select) |
| Template engine | EJS | Simple, familiar syntax, supports conditionals/loops |
| Validation | Zod | Runtime type safety for Claude responses |
| AI (interactive) | Claude CLI | Streaming, interactive sessions |
| AI (batch) | Anthropic SDK | Typed API, retry logic |
| Build | esbuild | Sub-second builds, ESM output |
| Tests | Vitest | Fast, ESM-native, compatible with Jest API |
| Terminal UI | Chalk + Ora | Colors + spinners for polished CLI UX |