🇰🇷 한국어 버전
Time required: ~40 minutes Key Insight: Once requirements are clear from the deep interview, implementation can start with a single prompt line.
Through the deep interview in Chapter 2, we reduced ambiguity to 19%. Now we write actual code based on the confirmed spec. The core architectural decisions are already made:
- LLM-based analysis via Claude Agent SDK
- Weighted average scoring across 6 categories (3 required + 3 recommended)
- Terminal output (chalk formatting)
- TypeScript + ESM
Since the deep interview results are saved in .omc/specs/deep-interview-vibe-ready-cli.md, the implementation request is simple:
Based on the deep interview results, implement the MVP
The AI read the deep interview spec and generated 5 modules in sequence.
Data models are defined first. The ontology confirmed in the deep interview maps directly to types:
export type Grade = "A" | "B" | "C" | "D" | "F";
export type CategoryTier = "must" | "nice";
export interface CategoryResult {
name: string;
tier: CategoryTier;
score: number; // 0~100
grade: Grade;
recommendations: Recommendation[];
rawFindings: RawFinding[];
}
export const CATEGORY_WEIGHTS: Record<string, { tier: CategoryTier; weight: number }> = {
"테스트 커버리지": { tier: "must", weight: 0.20 },
"CI/CD": { tier: "must", weight: 0.20 },
"훅 기반 검증": { tier: "must", weight: 0.20 },
"리포지토리 구조": { tier: "nice", weight: 0.133 },
"문서화 수준": { tier: "nice", weight: 0.133 },
"바이브 코딩 설정": { tier: "nice", weight: 0.134 },
};You can see how the "must vs. nice" classification confirmed in deep interview Round 5 maps directly to CategoryTier, and the grading system from Round 4 maps directly to Grade.
We write the analysis prompt to pass to the Claude Agent SDK. This prompt assigns the LLM the role of "repository analyst":
export function buildAnalysisPrompt(): string {
return `You are a Vibe Coding Readiness Analyst.
Your job is to analyze a repository and score how ready it is
for AI-assisted "vibe coding"...
## Categories
### Must-Have (필수) Categories:
1. **테스트 커버리지** (tier: "must")
Check for:
- Test configuration files (jest.config, vitest.config, ...)
- Test directories and test files
- Coverage configuration
...`;
}For each category, we specify "what to look for" and "scoring criteria." This is the core of LLM-based analysis — instead of coding rules, judgment criteria are delivered in natural language.
The LLM is invoked using the Claude Agent SDK's query() function:
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt,
options: {
cwd: repoPath,
tools: ["Read", "Glob", "Grep"],
allowedTools: ["Read", "Glob", "Grep"],
permissionMode: "dontAsk",
maxTurns: 20,
outputFormat: {
type: "json_schema",
schema: ANALYSIS_JSON_SCHEMA,
},
},
})) {
// LLM explores the repo and performs analysis
}Key points:
tools: ["Read", "Glob", "Grep"]— only file reading/searching tools are allowed for the LLMpermissionMode: "dontAsk"— runs automatically without user confirmationoutputFormat: json_schema— forces LLM output into structured JSON
Converts the LLM's raw output into a weighted average score + grade:
export function computeResult(llmOutput: LLMAnalysisOutput): AnalysisResult {
const categories = llmOutput.categories.map((cat) => ({
...cat,
grade: gradeFromScore(cat.score),
}));
const totalScore = computeWeightedAverage(categories);
let totalGrade = gradeFromScore(totalScore);
// Penalty: required category F → overall grade capped at C
const { penaltyApplied } = checkPenalty(categories);
if (penaltyApplied && gradeRank(totalGrade) < gradeRank("C")) {
totalGrade = "C";
}
return { categories, totalScore, totalGrade, ... };
}The "required category F → overall grade capped at C" rule confirmed in the deep interview is implemented as-is.
Terminal formatting using chalk:
═══════════════════════════════════════════════════
🎵 Vibe Ready Score
═══════════════════════════════════════════════════
Overall Score: 72 / 100 Grade: C
⚠ Required category graded F: Hook validation → overall grade capped at C
Results by Category
─────────────────────────────────────────────────
Test Coverage Required 85 B
CI/CD Required 90 A
Hook Validation Required 45 F
...
CLI interface configured with commander:
program
.name("vibe-ready")
.argument("[path]", "Path to the repository to analyze", ".")
.option("-v, --verbose", "Show detailed analysis findings")
.option("--max-turns <number>", "Max LLM agent turns", "20")
.option("--max-budget <number>", "Max budget in USD", "0.50")
.option("--timeout <number>", "Timeout in seconds", "120")Running build and tests:
$ npm run build
# TypeScript compilation successful
$ npm test
# 12 tests passed
✓ scorer.test.ts (8 tests)
✓ types.test.ts (4 tests)User: vibe-ready /path/to/repo
→ index.ts: CLI parsing
→ analyzer.ts: LLM invocation via Claude SDK
→ LLM explores repo via Read/Glob/Grep
→ Returns JSON with 6-category analysis results
→ scorer.ts: Weighted average + grade + penalty calculation
→ reporter.ts: Terminal report output via chalk
-
Deep interview → code mapping is direct: The ontology confirmed in the interview (Entity, Category, Grade) became TypeScript types almost as-is. When requirements are clear, implementation is mechanical.
-
LLM prompt is the business logic: In traditional tools, you'd need to code per-language patterns as rules — in an LLM-based approach, the natural language prompt is the rule. A single prompt in
analyze.tscovers all languages and frameworks. -
Force LLM output with JSON Schema: Using
outputFormat: { type: "json_schema" }makes the LLM return JSON matching the specified schema. You get structured data without worrying about parse failures. -
Pure functions can be tested without LLM: Functions like
computeResultandgradeFromScoreinscorer.tsare pure functions independent of LLM, making unit testing straightforward. Separating LLM-dependent parts from pure logic is key.
# After cloning the project
npm install
npm run build
# Analyze your own repository
node dist/index.js /path/to/your/repo --verbose
# Run tests
npm testPrevious Chapter: 02 - Clarifying Requirements with Deep Interview Next Chapter: 04 - Harness Engineering