Interactive Course

How SpecSafe Works Under the Hood

A visual, interactive guide to understanding the codebase behind the AI-assisted development framework — no coding experience needed.

6 modules ~25 min read TypeScript • CLI tool
01

What Is SpecSafe?

The problem it solves and what happens when you run it for the first time

The Problem: AI Coding Without a Plan

Imagine hiring a builder who starts pouring concrete before looking at the blueprints. That's what happens when AI coding tools write code without a structured plan first.

?

Vague Requirements

You say "add authentication" but the AI doesn't know if you mean passwords, social login, or magic links.

!

Skipped Tests

Code that "looks right" but nobody verified it actually works for all the edge cases.

×

Bug Loops

AI fixes one bug but creates two more because it doesn't understand the full picture.

SpecSafe solves this by enforcing a two-phase workflow: plan thoroughly first, then build with discipline.

Two Phases, One Principle: Think Before You Build

Like an architect who draws blueprints before any bricks are laid, SpecSafe splits every feature into a planning phase and a building phase.

Phase 1: Planning
Brainstorm
Principles
Brief
PRD
UX
Architecture
Readiness Check
Phase 2: Development
SPEC — Write the specification
TEST — Generate tests first
CODE — Write code to pass tests
QA — Validate everything
COMPLETE — Human approval

Under the Hood: A CLI Tool

SpecSafe is a CLI tool you install from npm. It scaffolds your project, installs workflow “skills” for your AI tool, and keeps everything on track.

specsafe init
specsafe install
specsafe update
specsafe doctor
💡
Key Insight: Skills, Not Rules

SpecSafe doesn't hardcode instructions. It installs "skills" — reusable workflow definitions that teach your AI coding tool how to plan and build. Different AI tools get different skill formats, but the knowledge is the same.

Your First Look at the Code

Here's the entry point — the very first file that runs when you type specsafe in your terminal.

CODE
const program = new Command();

program
  .name('specsafe')
  .description('SpecSafe — Skills-first TDD framework')
  .version(pkg.version);
PLAIN ENGLISH

Create a new command-line program.

Give it the name "specsafe" — that's what users type to run it.

Add a description that shows when users ask for help.

Set the version number by reading it from the project's package.json file.

Check Your Understanding

You're building an app and want to add a payment feature. With SpecSafe, what would you do first?

Why does SpecSafe install different "skill" files for different AI tools?

02

Meet the Cast

The six main actors inside the codebase and what each one does

Six Characters, One Story

Think of SpecSafe like a film crew. Each person has one job, and they coordinate to produce the final product. Here are the main characters.

R
CLI Router index.ts

The receptionist — hears your command, figures out who should handle it

I
Initializer init.ts

The set designer — creates the folders, config files, and templates for a new project

N
Installer install.ts

The translator — takes the master skills and converts them for your specific AI tool

A
Adapters adapters/*.ts

The localization team — 8 specialists, one for each AI tool's file format

D
Doctor doctor.ts

The inspector — checks your project is healthy and flags anything broken

U
Updater update.ts

The maintenance crew — refreshes all tool files when the master skills change

Where They Live

Here's how the project is organized on disk. Each actor lives in a specific file.

specsafe/ The whole project
generators/src/ Where the CLI code lives
index.ts CLI Router — parses commands
init.ts Initializer — creates new projects
install.ts Installer — deploys skills to tools
doctor.ts Doctor — health check
update.ts Updater — regenerates files
registry.ts Adapter lookup table
adapters/ One file per AI tool (8 total)
canonical/ The master copy of all skills, personas, and templates

How the Router Dispatches Commands

When you type specsafe install cursor, the Router figures out you want the install command and sends “cursor” to the Installer. Here's exactly how.

CODE
program
  .command('install')
  .description('Install SpecSafe skills for a specific AI tool')
  .argument('<tool>', 'Tool name (claude-code, cursor, ...)')
  .action(async (tool: string) => {
    const { install } = await import('./install.js');
    await install(tool);
  });
PLAIN ENGLISH

Register a new command called "install".

Describe what it does (this shows in the help screen).

Say it requires one argument: the tool name. The angle brackets mean it's required.

When someone runs this command, do the following…

Load the installer module on the fly (not upfront — saves startup time).

Call the install function with whatever tool name the user typed.

💡
Key Insight: Lazy Loading

The await import('./install.js') pattern is called dynamic import. It means the install code isn't loaded until someone actually runs the install command. This makes the CLI start faster.

Check Your Understanding

You want to add a new AI tool (say, "windsurf") to SpecSafe. Which files would you need to create or change?

Why does the Router use await import() instead of loading everything at the top of the file?

03

How the Pieces Talk

Follow the data as it flows from canonical skills to your AI tool

One Master Copy, Many Translations

Imagine a bestselling novel. The author writes one original manuscript. Then professional translators convert it into Spanish, Japanese, French, and more. The story is always the same — only the language changes.

SpecSafe works the same way. The canonical/ folder is the original manuscript. Adapters are the translators.

canonical/
Master skills
Claude Code
.claude/skills/
Cursor
.cursor/skills/
Aider
CONVENTIONS.md
…5 more

Anatomy of a Skill

Each skill is a Markdown file with a metadata header (called frontmatter) at the top. The metadata describes the skill; the body contains the instructions.

CODE
---
name: specsafe-new
description: 'Create a new spec with unique ID'
disable-model-invocation: true
---

# Create New Spec
When the user runs /specsafe-new...
1. Generate a unique SPEC ID
2. Copy the spec template
3. Open it for editing
PLAIN ENGLISH

Start of the metadata section (three dashes).

This skill is named "specsafe-new".

A human-readable description of what it does.

The AI can't run this on its own — the user has to trigger it explicitly.

End of metadata. Everything below is the actual instruction.

 

The step-by-step workflow the AI follows when this skill is activated.

The Install Flow: Step by Step

Here's exactly what happens when you run specsafe install claude-code. Click "Next Step" to follow the data.

R
CLI Router
N
Installer
L
Skill Loader
A
Adapter
D
Disk
Click "Next Step" to begin
Step 0 / 7

How Skills Get Parsed

The Skill Loader reads each SKILL.md file and splits it into metadata and body. Here's the parser.

CODE
export function parseFrontmatter(content: string) {
  const normalized = content.replace(/\r\n/g, '\n');
  const match = normalized.match(
    /^---\n([\s\S]*?)\n---\n([\s\S]*)$/
  );
  if (!match) return { frontmatter: {}, body: content };
  // ... parse key: value pairs from match[1]
  return { frontmatter, body: match[2] };
}
PLAIN ENGLISH

Create a function that takes text content and splits it into parts.

First, fix any Windows-style line breaks (different computers use different line endings).

Look for the pattern: ---, then metadata, then ---, then the rest. This regex captures both sections.

If there's no frontmatter block, just return everything as the body.

Otherwise, parse each line of metadata into key-value pairs.

Return the metadata and the body separately.

Check Your Understanding

Scenario

You update a skill's workflow in the canonical/ folder to fix a bug. But your AI tool is still using the old version. What happened?

What does the frontmatter disable-model-invocation: true prevent?

04

The Adapter Pattern

How one set of skills becomes files for 8 different AI tools

One Plug, Every Country

You know those universal power adapters for international travel? Your laptop charger is the same everywhere — but you need a different plug shape for each country. The electricity (the knowledge) doesn't change. Only the connector does.

That's exactly how SpecSafe's adapter pattern works. One interface, eight implementations.

CODE
export interface ToolAdapter {
  name: string;
  displayName: string;
  detect(projectRoot: string): Promise<boolean>;
  generate(
    skills: CanonicalSkill[],
    canonicalDir: string,
    projectRoot?: string,
  ): Promise<GeneratedFile[]>;
}
PLAIN ENGLISH

Define a contract that every adapter must follow:

It must have an internal name (like "claude-code").

It must have a human-friendly name (like "Claude Code").

It must be able to detect if the tool is installed in a project (returns true/false).

It must be able to take master skills and produce a list of files in the tool's format.

Watch the Adapters Coordinate

Here's how the Installer and Adapters talk to each other when you run specsafe install claude-code.

#install-channel
0 / 7 messages

Inside the Claude Code Adapter

Here's the complete Claude Code adapter — it's one of the simplest. It takes canonical skills and outputs .claude/skills/ files.

CODE
async generate(skills: CanonicalSkill[], canonicalDir: string) {
  const files: GeneratedFile[] = [];

  for (const skill of skills) {
    files.push({
      path: `.claude/skills/${skill.directory}/SKILL.md`,
      content: reconstructSkillMd(skill),
    });
  }

  return files;
}
PLAIN ENGLISH

This function takes the master skills and the path to canonical files.

Start with an empty list of files to create.

 

For each skill in the master list…

Add a file to the list. The path goes into .claude/skills/ with the skill's folder name.

The content is the reconstructed SKILL.md (frontmatter + body reassembled).

 

 

Return the complete list of files for the Installer to write to disk.

Eight Adapters, Eight Formats

Each adapter speaks its tool's language. Here's what they each produce.

C

Claude Code

Skills in .claude/skills/ + a CLAUDE.md rules file.

Cu

Cursor

Skills + a .cursor/rules/specsafe.mdc config file.

G

Gemini

Skills + TOML command definitions.

A

Aider

A .aider.conf.yml config + a CONVENTIONS.md guide.

Z

Zed

A .zed/settings.json with embedded conventions.

Co

Continue

Prompt templates in .continue/prompts/ + a YAML agent config.

Check Your Understanding

Scenario

You're building a new AI coding tool called "WindCode". You want it to work with SpecSafe. What contract must your adapter satisfy?

05

Safety Nets

How SpecSafe protects your project with security checks and health diagnostics

The Bouncer: Path Security

Imagine a nightclub bouncer who checks IDs at the door. No valid ID, no entry. SpecSafe's Installer has a similar guard — before writing any file, it checks that the file path doesn't escape your project folder.

This prevents a malicious or buggy skill from writing files to /etc/ or your home directory. It's called path traversal protection.

CODE
const resolvedCwd = resolve(cwd);
for (const file of files) {
  const fullPath = resolve(cwd, file.path);
  if (!fullPath.startsWith(`${resolvedCwd}/`)) {
    console.error(
      `Security error: path escapes project`
    );
    continue;
  }
  // Safe to write the file
}
PLAIN ENGLISH

Get the full, absolute path to the current project folder.

For each file the adapter wants to create…

Calculate where this file would actually end up on disk.

Does this path start with the project folder? If NOT…

Print a security warning and refuse to write the file.

Skip this file entirely and move to the next one.

 

Only if the path is inside the project do we write it.

The Pre-Flight Checklist: Doctor

Pilots run a checklist before every takeoff — engines, fuel, instruments. The specsafe doctor command does the same for your project. It checks everything and gives you a clear status report.

1
Config file exists?

Checks that specsafe.config.json is present and has all required keys

2
Project state exists?

Verifies PROJECT_STATE.md is present (the spec tracking dashboard)

3
Directory structure intact?

Confirms specs/active/, specs/completed/, and specs/archive/ all exist

4
Installed tools detected?

For each tool in your config, checks that the tool's files are actually present

Smart Detection: Finding Your Tools

During specsafe init, the Initializer scans your project for known AI tools. It looks for specific marker files — like checking for a doorbell to know if someone lives there.

CODE
const TOOL_DETECT_MAP: Record<string, string> = {
  'claude-code': '.claude/',
  'cursor':      '.cursor/',
  'opencode':    '.opencode/',
  'gemini':      '.gemini/',
  'zed':         '.zed/',
  'aider':       '.aider.conf.yml',
};
PLAIN ENGLISH

Create a lookup table that maps each tool name to its "marker" — a file or folder that proves the tool is installed.

If there's a .claude/ folder, Claude Code is present.

If there's a .cursor/ folder, Cursor is present.

If there's a .opencode/ folder, OpenCode is present.

And so on for each supported tool.

 

Aider uses a config file instead of a folder.

💡
Key Insight: Convention Over Configuration

Instead of asking you to manually list your tools, SpecSafe uses file-system conventions to auto-detect them. This is a common pattern in software — smart defaults that reduce the number of questions you have to answer.

Check Your Understanding

Scenario

A user reports: "I ran specsafe doctor and got a WARNING for tool: cursor with the message 'Tool files not detected'." What's likely wrong?

Why does the path security check use startsWith instead of checking for ../ in the path?

06

The Big Picture

See the full architecture and understand why it's built this way

The Full Map

You've met every character. Now let's zoom out and see how they all connect. Click any component to learn more.

CLI Layer
CLI Router
Registry
Core Logic
Initializer
Installer
Doctor
Updater
Adapter Layer
Skill Loader
8 Tool Adapters
Data Layer
canonical/
specsafe.config.json
PROJECT_STATE.md
Click any component to learn what it does

Why It's Built This Way

Every architectural decision in SpecSafe exists to solve a real problem. Here are the core design principles.

1

Single Source of Truth

canonical/ is authoritative. Adapters read from it but never write to it. This prevents skills from drifting apart across tools.

2

Open for Extension

Adding a new tool means creating one adapter file. No other code needs to change — the interface and registry handle the rest.

3

Stage Gating

Specs must flow SPEC → TEST → CODE → QA → COMPLETE. No skipping. This prevents the "just ship it" impulse that causes bugs.

4

Evidence Over Assertions

The QA stage requires test evidence, not just "it works trust me." Every requirement must have a passing test to prove it.

What You Can Do Now

After this course, you have the vocabulary and mental model to:

Steer AI better

You know the two-phase workflow, so you can tell AI "we're in the SPEC stage, don't write code yet"

Debug confidently

If skills aren't updating, you know to run specsafe update. If files are missing, run specsafe doctor

Extend the system

You understand the adapter pattern, so you could ask AI to "add a new adapter for WindCode that implements ToolAdapter"

Speak the language

Frontmatter, adapters, canonical source, path traversal, lazy loading — you own these terms now

🏁
Course Complete!

You've traced the full journey from specsafe init to generated skill files, met every actor, and understood the architectural decisions that make it all work. Go build something.

Final Challenge

Scenario

Your team wants to add a new planning step called "Security Review" between Architecture and Readiness. Where would you make this change?

Scenario

You discover that a colleague's SpecSafe project has specs jumping straight from SPEC to CODE, skipping the TEST stage. What principle is being violated, and why does it matter?