Architecture
This page explains how SpecKeep is structured internally.
High-Level Layers
SpecKeep is split into a few practical layers:
src/cmd/speckeep/for the CLI entrypointsrc/internal/cli/for Cobra commands and user-facing command wiringsrc/internal/project/for workspace lifecycle operations such asinit, agent maintenance, and cleanupsrc/internal/config/for loading, defaulting, and saving.speckeep/speckeep.yamlsrc/internal/templates/for localized generated assets and file generationsrc/internal/agents/for project-local command or prompt file generationsrc/internal/specs/for reading spec files used by the public CLIsrc/internal/trace/for parsingProof:entries fromtasks.mdsrc/internal/doctor/for workspace health checks
CLI Layer
The public CLI is intentionally small. src/internal/cli/ wires commands such as:
initadd-agentlist-agentsremove-agentcleanup-agentsdoctordashboardtracelist-specsshow-spec
The CLI layer should stay thin. Most behavior belongs in project, config, templates, agents, and doctor packages.
Config Layer
The config source of truth lives in .speckeep/speckeep.yaml.
src/internal/config/config.go is responsible for:
- defining the config schema
- applying defaults
- resolving important workspace paths
- loading config from disk
- saving updated config back to disk
This is what allows the rest of the codebase to avoid hardcoding too many .speckeep/... paths.
Template and Asset Layer
SpecKeep generates files from localized assets stored under:
src/internal/templates/assets/lang/en/src/internal/templates/assets/lang/ru/
These include:
constitution.md- spec, plan, tasks, and archive templates
- prompts for
constitution,spec,inspect,plan,tasks,implement, andverify - localized
agents-snippet.md
Shared shell scripts live in:
src/internal/templates/assets/scripts/
The templates package assembles these assets into the generated .speckeep/ workspace.
Agent Generation Layer
src/internal/agents/ generates a composite skill pack for every supported target:
claude,codex,copilot,cursor,kilocodeopencode,trae,windsurf,roocode,aideramazonq,gemini,jules,cline,continuedevin,goose,refact,codiumate,qwen-code
All targets share one renderer (skill_pack.go): a lightweight sdd/SKILL.md overview skill (progressive-disclosure entry point for ambiguous requests) plus one independent, directly slash-invocable spk-<phase>/SKILL.md skill per phase, laid into each target's skills directory (targetSkillDirs). Per-command wrapper generation and the user-facing skills-management subsystem were removed; each phase skill inlines the canonical prompt body from .speckeep/templates/prompts/ at render time (via templates.PromptContent) so an agent reads full phase instructions from a single file instead of following a pointer, while the prompt file remains the single authored source. The deterministic CLI (check/guard/converge) is the verification spine that skills call as gates.
Phase skills are independent top-level directories (spk-spec/SKILL.md, spk-plan/SKILL.md, ...) rather than nested resource files under one composite skill (sdd/phases/*.md), because skill loaders such as Claude Code only ever slash-invoke a top-level <dir>/SKILL.md — a nested file is reachable only when the model decides to open it, never by the user typing a command. agents.LegacySkillPhasePaths covers cleanup of the older nested layout for any workspace refreshed before this change.
This keeps one main source of truth for workflow prompts while still supporting multiple agent ecosystems.
Project Lifecycle Layer
src/internal/project/init.go handles the workspace lifecycle:
- initialize a project
- add agent targets
- list agent targets
- remove agent targets
- clean up orphaned agent artifacts
- create helper scripts in
.speckeep/scripts/
This layer is responsible for:
- creating the
.speckeep/layout - writing generated files
- updating
AGENTS.md - syncing enabled agent targets into config
Traceability and Verification Layer
src/internal/trace/trace.go handles the core logic for linking code to requirements:
- parses
Proof:entries fromtasks.md, mapping each completed task to its implementation evidence (kind, path, optional anchor) - reports orphaned/duplicate/missing proof, missing files, and warns on missing anchors
- filters findings by feature slug
- supports JSON output for integration with verification workflows
This layer allows the verify phase to remain token-efficient by replacing manual code review with deterministic metadata parsing.
Health and Maintenance Layer
src/internal/doctor/doctor.go and src/internal/gitutil/ check workspace health and alignment.
It verifies:
- required directories and files exist
- configured languages are valid
- enabled agent targets have their generated files
- disabled targets do not leave stale artifacts behind unnoticed
- Smart Branching: checks if the current Git branch matches the feature slug (expected
feature/<slug>) - Traceability Integrity: warns about orphaned/missing
Proof:entries or invalidAC-*references
Related maintenance commands:
remove-agentdisables a target and removes its generated filescleanup-agentsremoves orphaned leftovers for disabled targetsdoctorreportserror,warning, andokdashboardprovides a visual summary of project progress, status, and branch health
Design Principles
The internal architecture follows a few important principles:
- keep the public CLI small
- keep generated assets language-aware but structurally consistent
- push readiness checks into shell scripts when possible
- prefer one canonical prompt source over duplicated prompt logic
- keep workflow phases strict without embedding a heavy orchestration engine
- optimize for low token usage by controlling read sets and artifact scope
Anti-Bloat Checklist
Use this checklist before adding a new feature, prompt rule, script, or artifact:
- Does it increase the default read set? If yes, treat it as risky by default.
- Can it be solved with a cheap deterministic helper instead of more reasoning?
- Does it make a new artifact mandatory for every feature? If yes, reconsider.
- Does it require reading implementation code by default? If yes, it is probably too heavy.
- Can the workflow still begin from constitution, spec, plan, or tasks before touching code?
- Does it expand the public CLI without clear value for everyday users?
- Does it add a brand-new gate, or can it strengthen an existing phase instead?
- Can its value be explained in one short sentence? If not, it may be process complexity without enough return.
- Does it push SpecKeep closer to spec-kit-style bureaucracy without matching value?
- Does it improve brownfield ergonomics in a real way?
A change is usually a good fit when it does at least one of these:
- improves quality without expanding the default context
- replaces expensive reasoning with a cheap structural check