Aletheia

Write your AI coding rules once and sync them to every tool

Aletheia (aletheia-mem) keeps your standing rules in one list and writes them into the instruction file each AI coding tool reads, such as CLAUDE.md and AGENTS.md. You edit the list in one place, preview the result, and then write the files.

This guide walks through the full loop with the CLI and the dashboard. Every command output below came from a temporary home folder and project folder, not from real files.

Step 1: Add rules

Add a rule from the command line. The text is the rule. Add --when for a rule that applies only in a situation.

aletheia rules add "Run the tests before you commit"
aletheia rules add "Use the repo's formatter" --when "you edit a .mjs file"
Added r_a188fedbf2.
Added r_daf40b0979.

Adding the same rule again prints Already there (<id>). and changes nothing.

In the dashboard, open the Rules tab and click Add a rule. The dialog has these fields:

List your rules at any time:

aletheia rules list
on  r_a188fedbf2  Run the tests before you commit  [everywhere]  (added 2026-10-29, source: you, last confirmed 2026-10-29)
on  r_daf40b0979  When you edit a .mjs file: Use the repo's formatter  [everywhere]  (added 2026-10-29, source: you, last confirmed 2026-10-29)

Rules live in the Aletheia database. The tool files are an output, not the source. Editing a tool file by hand does not change the list.

Step 2: Choose a scope

A rule has a scope and an optional tool filter.

The scope sets the heading. It does not filter by folder. In a test, a rule scoped to the project shop appeared under ## In shop in the AGENTS.md of a different folder. A rule limited to codex was missing from the CLAUDE.md that was written in the same run.

The CLI rules add command makes Everywhere rules. Set a project scope or a tool filter in the dashboard dialog, or import rules from a file that already has headings (Step 3).

Step 3: Import the rules you already have

If your tools already have rule files, import them instead of retyping.

aletheia rules import --tool claude-code --dir /path/to/project
aletheia rules import --tool claude-code --global
{
  "found": 3,
  "added": 3,
  "skipped": 0
}

The importer reads bullet lines (- ... or * ...). A ## Heading above a bullet becomes the scope name, so the bullets under ## Testing become rules in the situation Testing. A file with no heading gives Everywhere rules. Running the import again skips rules you already have.

Two limits to know:

In the dashboard, use Rules, then Import: pick the tool, type a project folder (blank means the global file) and click Import rules.

Step 4: Preview, diff and lint

Nothing is written until you ask. Two read-only commands show where the files and the list disagree.

aletheia rules diff --dir /path/to/project --tools claude-code,codex
claude-code  /path/to/project/CLAUDE.md  differs
  only in store  r_8358622fa2  Always run bun test before a commit
  ...
codex  /path/to/project/AGENTS.md  differs
  only in file   /path/to/project/AGENTS.md:10  Prefer tabs
  text differs   r_a188fedbf2 vs /path/to/project/AGENTS.md:8
    store: Run the tests before you commit
    file:  Run all tests before you commit
2 tools out of sync.

rules diff compares only files that contain the managed block. A file without the block is reported as unmanaged, not as a difference. Add --json for machine-readable output. The command exits with code 0 even when files differ.

rules lint checks the list and the managed blocks for hygiene problems:

aletheia rules lint --dir /path/to/project --tools claude-code,codex
r_1732204248  drift_store_only  Rule r_1732204248 is in the store but missing from the managed block in .../CLAUDE.md.
  fix: Run aletheia rules sync --write for that tool.
file:.../AGENTS.md:3  drift_file_only  The managed block in .../AGENTS.md has a rule the store does not: "Prefer tabs".
  fix: Add it in the Rules view or run aletheia rules sync --write to restore the block.

The checks, from the lint code: duplicate rules, direct contradictions (same scope, opposite polarity), a rule over 300 characters, an enabled set over 4,000 characters, text that tells an agent to ignore safety, a secret inside a rule, a stale rule, and drift between the store and a managed block. The command exits with code 1 when it finds anything, so you can run it in CI. With no findings it prints No findings.

Step 5: Write the files

Write global files (in your home folder) or project files (in one folder). Add --write to apply; without it the command is a dry run.

aletheia rules sync --tools claude-code,codex --global --write
aletheia rules sync --tools claude-code,codex --dir /path/to/project --write
- claude-code: /path/to/project/CLAUDE.md — 5 rules, written (backup /path/to/project/CLAUDE.md.aletheia-2026-10-29T19-47-38-813Z.bak)
- codex: /path/to/project/AGENTS.md — 5 rules, written

Run the same command again and each line ends with already up to date.

Always pass --tools. Without it, rules sync targets all ten entries in the tool registry, including copilot, aider and agents-md. The tool ids and their files:

Tool idProject fileGlobal file
claude-codeCLAUDE.md~/.claude/CLAUDE.md
command-codeAGENTS.md~/.commandcode/AGENTS.md
codexAGENTS.md~/.codex/AGENTS.md
gemini-cliGEMINI.md~/.gemini/GEMINI.md
cursor.cursor/rules/aletheia.mdcnone
windsurf.windsurf/rules/aletheia.md~/.codeium/windsurf/memories/global_rules.md
opencodeAGENTS.md~/.config/opencode/AGENTS.md
copilot.github/copilot-instructions.mdnone
aiderCONVENTIONS.mdnone
agents-mdAGENTS.mdnone

A tool with no global file is skipped, and the command prints a line such as - cursor: Cursor has no global rules file.

In the dashboard, open Rules, then Write to tools. Tick the tools, choose A project folder (type its absolute path) or Global, click Preview changes, then Write files. The page asks you to confirm before it writes. The Rules page also shows a line such as 1 tool out of sync with these rules when a file has drifted.

What the generated file looks like

This is the AGENTS.md that rules sync --tools codex wrote after the steps above. The same block goes into CLAUDE.md.

<!-- aletheia:rules:begin -->
# Rules

Managed by aletheia-mem. Edit them in the Rules page; this block is rewritten on sync.

## Everywhere

- Run the tests before you commit
- When you edit a .mjs file: use the repo's formatter

## In shop

- Deploy only from the staging branch

## Testing

- Always run bun test before a commit
- Never mock the database in integration tests
<!-- aletheia:rules:end -->

Cursor gets a whole file that Aletheia owns, with the front matter Cursor documents:

---
description: Standing rules managed by aletheia-mem
alwaysApply: true
---

<!-- aletheia:rules:begin -->
...
<!-- aletheia:rules:end -->

Windsurf's project file is also written whole, with trigger: always_on front matter. Global scope always uses the block form.

Rules for Cursor, Gemini CLI and Windsurf: config written from docs, not tested on a real install. The files above are written and checked in temporary folders; see the rules targets record.

Keep rules fresh

A rule can go stale. Aletheia flags it and keeps syncing it until you decide.

aletheia rules list --stale
aletheia rules confirm r_a188fedbf2
aletheia rules retire r_1732204248
Confirmed r_a188fedbf2 on 2026-10-29. The rule is unchanged.
Retired r_1732204248: turned off, not deleted. Turn it back on in the Rules page.

A rule is flagged when its evidence changed after the last confirmation, or when nobody confirmed it within the review window (90 days by default). Confirm a rule to restart the clock. Retire a rule to turn it off. A retired rule stays in the list and leaves the files on the next sync. In a test, the managed block went from 5 rules to 4 after one retire and one sync --write.

Safety

Next steps