Problem: AI agents write code without tests, skip design validation, and lack consistency across projects.
Solution: Portable patterns and guides that enforce test-first development (BDD/TDD), quality standards, and best practices across all your projects.
Repository: https://github.com/TheMostlyGreat/safeword
1. Install in your project:
cd /path/to/your/project
bunx safeword@latest setup2. Verify installation:
# Check for SAFEWORD files
test -f .safeword/SAFEWORD.md && echo ".safeword/SAFEWORD.md ✓"
test -f AGENTS.md && echo "AGENTS.md ✓"Result: Your project now has:
.safeword/SAFEWORD.md- Global patterns and workflows.safeword/guides/- Testing methodology (BDD/TDD), code philosophy.safeword/hooks/- Auto-linting, quality review hooks.claude/settings.json- Hook configuration for Claude Code.claude/commands/- Slash commands for Claude Code.cursor/hooks.json- Hook configuration for Cursor.cursor/rules/- Behavior rules for Cursor.cursor/commands/- Slash commands for CursorAGENTS.md- Project context with framework reference
Commit these to your repo for team consistency.
Stack-agnostic — Safeword is a process layer, not a framework opinion. It works alongside any stack — Next, Elysia, Astro, Django, Gin, whatever you use. Your application code and runtime dependencies are never touched.
Your agent config stays yours — Safeword uses AGENTS.md as the primary entry point. If you have an existing CLAUDE.md, it adds one import line at the top — your content is untouched.
Dev-only tools — For JS/TS projects, safeword installs ESLint, Prettier, and supporting plugins as devDependencies. These are code quality tools for development — they never ship with your application or affect your runtime.
AI guardrails, not human blockers — Hooks and stricter linting rules only fire during AI agent sessions (Claude Code / Cursor events). They never run during normal human development. Safeword does not install git hooks or modify your commit workflow.
Use in CI if you want — Safeword adds lint and format scripts to your package.json. You can wire these into your CI pipeline or precommit hooks — but it's your choice, not forced.
Every session moves through five phases, in order — and three hard gates stop your agent skipping ahead:
flowchart TD
start([You ask for something])
start --> propose
subgraph clarify ["1 · Clarify — propose and converge"]
direction TB
propose["Agent proposes a direction<br/>and surfaces the open questions"]
converged{"Converged?"}
propose --> converged
converged -->|"not yet"| propose
end
converged -->|"yes"| classify{"2 · Classify<br/>how big is it?"}
classify -->|"1 file, no new behavior"| patch["patch — fix it directly"]
classify -->|"1–2 files, one behavior"| task["task — TDD"]
classify -->|"3+ files · new state · many flows"| feature["feature — BDD"]
feature --> phase0["Phase 0 spec:<br/>Jobs To Be Done → Acceptance Criteria → scope"]
phase0 --> g1{{"Phase gate:<br/>scope / out_of_scope / done_when"}}
g1 --> scenarios["Define-behavior scenarios"]
scenarios --> g2{{"Phase gate:<br/>test-definitions.md exists"}}
g2 --> build
task --> build["3 · Build<br/>RED → GREEN → REFACTOR"]
patch --> verify
build --> verify["4 · Verify<br/>the agent runs the tests"]
verify --> g3{{"Done gate:<br/>verify.md exists"}}
g3 --> done([5 · Done])
loc{{"LOC gate — commit every ~400 lines"}} -.->|throughout build| build
- Clarify — the agent proposes a direction and converges with you before building. For features, this writes the product framing first: Jobs To Be Done → Acceptance Criteria → engineering scope.
- Classify — sizes the work as a patch (fix directly), task (TDD), or feature (BDD).
- Build — patches go straight to the fix; tasks and features run the RED → GREEN → REFACTOR loop, with features defining behavior scenarios first.
- Verify — the agent runs the relevant tests itself, never handing you something untested.
- Done — hard-blocked until
/verifywritesverify.mdto the ticket.
The framework is project-local: it writes to .safeword/, .claude/, and .cursor/ in your repo — no global install — so teammates get the same discipline the moment they pull. Guides and learnings live in-repo and evolve as you work.
Key directories created in your project:
.safeword/guides/- Core methodology and best practices.safeword/templates/- Fillable document structures.safeword-project/tickets/- Tickets for complex/multi-step work (context anchors).safeword/hooks/- Automation scripts (Claude Code + Cursor).claude/commands/,.cursor/commands/- Slash commands.claude/skills/,.cursor/rules/- Specialized agent capabilities
Purpose: Reusable methodology applicable to all projects
| Guide | Purpose | When to Read |
|---|---|---|
| planning-guide.md | Feature planning workflow, spec creation, BDD/TDD integration | Starting any feature |
| testing-guide.md | Test-first workflow (RED/GREEN/REFACTOR), test pyramid, test types | Writing tests |
| learning-extraction.md | Extract learnings from debugging, recognition triggers | After complex debugging |
Purpose: Writing effective feature documentation
| Guide | Purpose | When to Read |
|---|---|---|
| design-doc-guide.md | Design doc structure and best practices | Designing complex features |
| architecture-guide.md | Architecture decisions (tech choices, data models) | Making architectural decisions |
| data-architecture-guide.md | Data model design (schemas, validation, flows) | Database/schema design |
| context-files-guide.md | CLAUDE.md/AGENTS.md structure and best practices | Setting up project context |
Purpose: Working with LLMs and documentation structure
| Guide | Purpose | When to Read |
|---|---|---|
| llm-writing-guide.md | Writing docs that LLMs follow (MECE, examples, context placement) | Writing skills, commands, hooks |
| zombie-process-cleanup.md | Port-based cleanup, multi-project isolation | Managing dev servers |
Purpose: Fillable structures for feature documentation
| Template | Purpose | Used By |
|---|---|---|
| feature-spec-template.md | Feature spec (user stories + constraints) | planning-guide.md |
| task-spec-template.md | Bug, improvement, refactor, or internal task | planning-guide.md |
| test-definitions-feature.md | BDD scenarios (Rule + Scenario + G/W/T + R/G/R) | planning-guide.md |
| design-doc-template.md | Design doc structure (architecture, components) | design-doc-guide.md |
| architecture-template.md | ADR for decisions with long-term impact | planning-guide.md |
| ticket-template.md | Context anchor for complex/multi-step work | SAFEWORD.md |
| work-log-template.md | Scratch pad and working memory during execution | SAFEWORD.md |
Purpose: Extracted knowledge that compounds across sessions
Location: .safeword-project/learnings/[concept].md
What goes here:
- Debugging discoveries (non-obvious gotchas, integration struggles)
- Trial-and-error findings (tried 3+ approaches before right one)
- Architecture insights (discovered during implementation)
- Testing traps (tests pass but UX broken, or vice versa)
How to extract: Follow learning-extraction.md recognition triggers and templates
Purpose: Context anchors for complex/multi-step work to prevent LLM loops
Location: .safeword-project/tickets/{id}-{slug}/
Structure:
.safeword-project/
├── tickets/
│ ├── 001-feature-name/
│ │ ├── ticket.md # Ticket definition (frontmatter + work log)
│ │ ├── test-definitions.md # BDD scenarios (Given/When/Then)
│ │ ├── spec.md # Feature spec for epics (optional)
│ │ └── design.md # Design doc for complex features (optional)
│ └── completed/ # Archive for done tickets
├── learnings/ # Extracted knowledge (gotchas, discoveries)
└── tmp/ # Scratch space (research, logs, etc.)
When to create: Multiple attempts likely, multi-step with dependencies, investigation needed, or risk of losing context
Hooks (in .safeword/hooks/): TypeScript automation scripts (Bun runtime)
session-verify-agents.ts- Verifies AGENTS.md link on session startsession-version.ts- Shows safeword version on session startsession-lint-check.ts- Checks for lint errors on session startsession-cleanup-quality.ts- Garbage-collects old quality state files on session endsession-compact-context.ts- Re-injects active ticket context after context compactionprompt-timestamp.ts- Injects timestamp into promptsprompt-questions.ts- Reminds agent to ask clarifying questionspost-tool-lint.ts- Auto-lints after file editspost-tool-quality.ts- Tracks LOC, detects phase changes and TDD stepspost-tool-bypass-warn.ts- Warns when agent bypasses quality gatespre-tool-quality.ts- Blocks edits when quality gate is active (LOC, phase, or TDD)pre-tool-config-guard.ts- Guards against settings.json modificationsstop-quality.ts- Quality review prompt on stopcursor/after-file-edit.ts- Auto-lints after Cursor file editscursor/stop.ts- Quality review prompt on Cursor stop
Skills (in .claude/skills/): Specialized agent capabilities
bdd/- BDD orchestrator for feature-level work (Discovery, Scenarios, TDD, Verify, Splitting, Done)debug/- Four-phase debugging (investigate before fixing)quality-review/- Deep code review with web researchrefactor/- Small-step refactoring with test verificationtesting/- Test writing methodology (iron laws, anti-patterns)ticket-system/- Ticket system and work logs for context anchoring
Commands (in .claude/commands/): Slash commands
/audit- Run architecture and dead code analysis/bdd- Force BDD flow for current task/cleanup-zombies- Kill zombie processes on ports/debug- Four-phase debugging framework/lint- Run linters and formatters/quality-review- Deep code review with web research/refactor- Systematic refactoring with small-step discipline/testing- Test writing guidance and best practices/verify- Verify ticket criteria (tests, build, lint, scenarios, dep drift)
MCP Servers (in .mcp.json / .cursor/mcp.json): Auto-configured integrations
- context7 - Up-to-date library documentation lookup
- playwright - Browser automation for testing
# Set up safeword in current project
bunx safeword@latest setup
bunx safeword@latest setup -y # Non-interactive mode
# Check project health and versions
bunx safeword@latest check
bunx safeword@latest check --offline # Skip remote version check
# Upgrade to latest version
bunx safeword@latest upgrade
# Preview changes before upgrading
bunx safeword@latest diff
bunx safeword@latest diff -v # Show full diff output
# Regenerate architecture config for /audit
bunx safeword@latest sync-config
# Remove safeword from project
bunx safeword reset
bunx safeword reset -y # Skip confirmation
bunx safeword reset --full # Also remove linting config + packages# From packages/cli/
bun publishAuto-detection: Detects project type from package.json and enables relevant ESLint plugins only when the framework is installed:
- TypeScript, React, Next.js, Astro
- Vitest, Playwright, Storybook, Tailwind, Turbo, TanStack Query
- Publishable libraries (adds publint)
AGENTS.md contains a link to .safeword/SAFEWORD.md (also added to CLAUDE.md if present).
SAFEWORD.md then imports guides via the Guides table. Both Claude Code and Cursor auto-load these as context.
ls .safeword-project/learnings/- Follow recognition triggers in
learning-extraction.md - Create
.safeword-project/learnings/[concept].md - Use template: Problem → Gotcha → Examples → Testing Trap
Commit .safeword/, .claude/, and .cursor/ in your project repo for team consistency.
Safeword reads project-level documentation files (personas, glossary, architecture) from .safeword-project/ by default. If you already maintain these docs elsewhere, point safeword at your existing files via the optional paths block in .safeword/config.json:
{
"installedPacks": ["typescript"],
"paths": {
"personas": "docs/personas.md",
"glossary": "docs/glossary.md",
"architecture": "ARCHITECTURE.md"
}
}Rules:
- All
paths.*keys are optional. Unset keys fall back to.safeword-project/<key>.md. - Relative paths resolve against project root (the directory containing
.safeword/config.json). - Absolute paths are used verbatim — useful for shared monorepo setups where the file lives outside this project's tree.
- When an override is set,
safeword setupdoes NOT scaffold the default-location stub — one personas.md per project, where you named it. safeword checkvalidates the configured file. If the file is missing, you get apersonas-path:error with non-zero exit (loud failure on configured-but-missing). If.safeword-project/personas.mdstill exists from a prior install, you get a zero-exit advisory naming the orphaned file (cleanup is up to you — safeword never deletes user content).
Currently only personas is wired through to a read site; glossary and architecture slots are reserved for forthcoming sibling tickets.
How it works:
AGENTS.mdlinks to.safeword/SAFEWORD.md(also adds one import line toCLAUDE.mdif present)SAFEWORD.mdimports guides via Guides table- Guides cross-reference each other and templates
- Learnings stored in
.safeword-project/learnings/
Result: Modular, maintainable documentation with clear separation of concerns
- Guides - Reusable methodology (test pyramid, BDD/TDD workflow)
- Templates - Fillable structures (user stories, test definitions)
- Learnings - Extracted knowledge (gotchas, discoveries)
- Planning - Feature planning and design (user stories, test definitions, design docs)
- Hooks/Skills - Automation and specialized capabilities
Living Documentation: Update as you learn, archive completed work, consolidate when needed
Will safeword change my stack or framework? No. Safeword is a process overlay — it adds quality enforcement (BDD/TDD, linting, code review) on top of whatever you already use. It doesn't install application dependencies or modify your source code.
Will it overwrite my CLAUDE.md?
No. Safeword uses AGENTS.md as the primary entry point. If you have an existing CLAUDE.md, it prepends a single 4-line block that links to .safeword/SAFEWORD.md. Your existing content stays exactly where it is.
What packages does it install?
For JS/TS projects: ESLint, Prettier, and supporting plugins — all as devDependencies (the -D flag). These are code quality tools, not application dependencies. Python, Go, and Rust (beta) use their language-native linters (ruff, golangci-lint, clippy).
I use biome/dprint — is that a problem?
Safeword detects biome/dprint and skips Prettier installation. ESLint is still installed because biome doesn't support security scanning (eslint-plugin-security), cyclomatic complexity checks (sonarjs), or framework-specific rules (React hooks, Next.js, Astro). Both tools coexist without conflict.
Do teammates need to install safeword separately?
No. Commit the .safeword/, .claude/, and .cursor/ directories to git. When teammates pull, they get the full setup. The linting devDependencies install automatically with npm install / bun install.
Will it interfere with my development workflow?
No. Safeword's hooks and stricter linting rules only fire during AI agent sessions. They don't run when you code normally, and safeword does not install git hooks. It adds lint and format scripts to package.json that you can optionally use in CI or precommit hooks.
What Claude Code permissions does safeword need?
Safeword's done-gate verifies that /verify and /audit were actually invoked by reading a session-scoped log written via bash injection at the top of each skill. If Claude Code denies that bash injection, the gate hard-blocks at done-phase.
To pre-approve the injection without prompts (recommended for headless / non-interactive sessions), add these two patterns to .claude/settings.json:
{
"permissions": {
"allow": ["Bash(mkdir -p:*)", "Bash(echo:*)"]
}
}This is the minimum surgical scope:
Bash(mkdir -p:*)matches onlymkdir -p ...invocations (word boundary enforced), not baremkdir.Bash(echo:*)is the only way to pre-approveecho— Claude Code bash patterns cannot constrain by what is being echoed or where it writes (>>redirects are part of the command string, not a separator).
The injection itself only writes timestamped lines to .safeword-project/skill-invocations.log — no network calls, no file mutation outside that path. If you cannot or do not want to allow bash injection in your environment, the done-gate is currently inoperable — please open an issue if this affects you.
This section is for contributors to safeword itself.
| Component | Technology |
|---|---|
| Runtime | Bun (dev), Node 22+ (users) |
| CLI | TypeScript, Commander.js |
| Build | tsup (ESM-only output) |
| Tests | Vitest |
| Linting | ESLint 9 + Prettier |
These tools enhance development scripts but are not required:
| Binary | Purpose | Script | Install |
|---|---|---|---|
shfmt |
Format shell scripts in repo | bun format:sh |
brew install shfmt |
dot |
Generate dependency graph SVG | bun deps:graph |
brew install graphviz |
Without these binaries, the scripts print a message and skip.
Editing Source Templates:
- Edit in
packages/cli/templates/(source of truth) - Run
bunx safeword upgradeto sync to.safeword/ - Test changes
Running Tests:
# Important: Use `bun run test` (Vitest), NOT `bun test` (Bun's runner)
bun run test # All tests
bunx vitest run tests/foo.test.ts # Single file
bun run test:integration # Integration tests
bun run test:watch # Watch modePublishing:
Always run bun publish from packages/cli/ directory, not the monorepo root.
The CLI installs matching skills for both Claude Code and Cursor IDEs.
Source of truth: packages/cli/src/schema.ts
Parity tests: packages/cli/tests/schema.test.ts
| IDE | Skills Location | Commands Location |
|---|---|---|
| Claude Code | .claude/skills/safeword-*/ |
.claude/commands/*.md |
| Cursor | .cursor/rules/{safeword-*,bdd-*}.mdc |
.cursor/commands/*.md |
Editing skills:
- Edit templates in
packages/cli/templates/skills/(Claude) andpackages/cli/templates/cursor/rules/(Cursor) - Update
packages/cli/src/schema.tsif adding/removing skills - Run parity tests:
bun run test -- --testNamePattern="parity" - Run
bunx safeword upgradeto sync to local project
- Claude Code docs: https://docs.claude.com/en/docs/claude-code
- Issues: https://github.com/anthropics/claude-code/issues
- This repo: https://github.com/TheMostlyGreat/safeword