Architecture

How vibe-init is built: module structure, data flow, design decisions, and extension points.

System overview

Module Architecture
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

Command data flows

vibe init — Framework Generation
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
vibe build — Idea to Codebase
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"]
vibe audit — Governance Scorecard
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"]

AI integration paths

Claude CLI vs Anthropic SDK
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

Design decisions

Claude CLI vs. Anthropic API split

vibe-init uses two distinct AI integration paths:

PathUsed forWhy
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.

Registry pattern for features

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.

Specificity-first stack detection

Stack detectors run in order of specificity:

  1. Next.js — checks for next.config.*
  2. Python / FastAPI — checks for requirements.txt, pyproject.toml
  3. Go — checks for go.mod
  4. Generic Node.js — checks for package.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).

Weighted health scoring

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.

Three-tier validation

  1. Type level: TypeScript strict mode + Zod parsing for Claude responses
  2. Filesystem level: fileExists(), readPackageJson() for detection
  3. Process level: checkClaudeCli(), checkAnthropicApiKey() before AI operations

Dry-run mode

Every write operation respects the --dry-run flag. This enables safe previewing of all file operations without committing to disk. Critical for building user confidence.

Data flow

Init command (framework generation)

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

Build command

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

Scan command

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

Add command

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

Doctor command

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

Error handling

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).

Build pipeline

# 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.

Extension points

The architecture supports extension in several ways:

ExtensionHow
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

Tech stack

ComponentTechnologyWhy
CLI frameworkCommander.jsMature, minimal API, great help generation
Interactive promptsInquirer.jsRich prompt types (confirm, input, select)
Template engineEJSSimple, familiar syntax, supports conditionals/loops
ValidationZodRuntime type safety for Claude responses
AI (interactive)Claude CLIStreaming, interactive sessions
AI (batch)Anthropic SDKTyped API, retry logic
BuildesbuildSub-second builds, ESM output
TestsVitestFast, ESM-native, compatible with Jest API
Terminal UIChalk + OraColors + spinners for polished CLI UX