A visual, interactive guide to understanding the codebase behind the AI-assisted development framework — no coding experience needed.
The problem it solves and what happens when you run it for the first time
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.
You say "add authentication" but the AI doesn't know if you mean passwords, social login, or magic links.
Code that "looks right" but nobody verified it actually works for all the edge cases.
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.
Like an architect who draws blueprints before any bricks are laid, SpecSafe splits every feature into a planning phase and a building phase.
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 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.
Here's the entry point — the very first file that runs when you type specsafe in your terminal.
const program = new Command();
program
.name('specsafe')
.description('SpecSafe — Skills-first TDD framework')
.version(pkg.version);
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.
The six main actors inside the codebase and what each one does
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.
The receptionist — hears your command, figures out who should handle it
The set designer — creates the folders, config files, and templates for a new project
The translator — takes the master skills and converts them for your specific AI tool
The localization team — 8 specialists, one for each AI tool's file format
The inspector — checks your project is healthy and flags anything broken
The maintenance crew — refreshes all tool files when the master skills change
Here's how the project is organized on disk. Each actor lives in a specific file.
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.
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);
});
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.
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.
await import() instead of loading everything at the top of the file?Follow the data as it flows from canonical skills to your AI tool
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.
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.
---
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
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.
Here's exactly what happens when you run specsafe install claude-code. Click "Next Step" to follow the data.
The Skill Loader reads each SKILL.md file and splits it into metadata and body. Here's the parser.
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] };
}
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.
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?
disable-model-invocation: true prevent?How one set of skills becomes files for 8 different AI tools
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.
export interface ToolAdapter {
name: string;
displayName: string;
detect(projectRoot: string): Promise<boolean>;
generate(
skills: CanonicalSkill[],
canonicalDir: string,
projectRoot?: string,
): Promise<GeneratedFile[]>;
}
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.
Here's how the Installer and Adapters talk to each other when you run specsafe install claude-code.
Here's the complete Claude Code adapter — it's one of the simplest. It takes canonical skills and outputs .claude/skills/ files.
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;
}
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.
Each adapter speaks its tool's language. Here's what they each produce.
Skills in .claude/skills/ + a CLAUDE.md rules file.
Skills + a .cursor/rules/specsafe.mdc config file.
Skills + TOML command definitions.
A .aider.conf.yml config + a CONVENTIONS.md guide.
A .zed/settings.json with embedded conventions.
Prompt templates in .continue/prompts/ + a YAML agent config.
You're building a new AI coding tool called "WindCode". You want it to work with SpecSafe. What contract must your adapter satisfy?
How SpecSafe protects your project with security checks and health diagnostics
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.
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
}
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.
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.
Checks that specsafe.config.json is present and has all required keys
Verifies PROJECT_STATE.md is present (the spec tracking dashboard)
Confirms specs/active/, specs/completed/, and specs/archive/ all exist
For each tool in your config, checks that the tool's files are actually present
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.
const TOOL_DETECT_MAP: Record<string, string> = {
'claude-code': '.claude/',
'cursor': '.cursor/',
'opencode': '.opencode/',
'gemini': '.gemini/',
'zed': '.zed/',
'aider': '.aider.conf.yml',
};
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.
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.
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?
startsWith instead of checking for ../ in the path?See the full architecture and understand why it's built this way
You've met every character. Now let's zoom out and see how they all connect. Click any component to learn more.
Every architectural decision in SpecSafe exists to solve a real problem. Here are the core design principles.
canonical/ is authoritative. Adapters read from it but never write to it. This prevents skills from drifting apart across tools.
Adding a new tool means creating one adapter file. No other code needs to change — the interface and registry handle the rest.
Specs must flow SPEC → TEST → CODE → QA → COMPLETE. No skipping. This prevents the "just ship it" impulse that causes bugs.
The QA stage requires test evidence, not just "it works trust me." Every requirement must have a passing test to prove it.
After this course, you have the vocabulary and mental model to:
You know the two-phase workflow, so you can tell AI "we're in the SPEC stage, don't write code yet"
If skills aren't updating, you know to run specsafe update. If files are missing, run specsafe doctor
You understand the adapter pattern, so you could ask AI to "add a new adapter for WindCode that implements ToolAdapter"
Frontmatter, adapters, canonical source, path traversal, lazy loading — you own these terms now
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.
Your team wants to add a new planning step called "Security Review" between Architecture and Readiness. Where would you make this change?
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?