Canon remembers the decisions your repo already made — and makes Claude Code and Cursor follow them automatically.
Governed project decision memory for AI coding agents.
Canon is a local-first CLI that sits between coding agents (Claude Code and Cursor first) and a project's institutional knowledge. It stops agents and new teammates from re-adopting rejected approaches, inventing conventions, or re-asking questions that were already settled.
It is not a chatbot, not another coding agent, not generic RAG, and not a website-first knowledge base. It is the living, agent-native layer for project decisions. It complements CLAUDE.md, AGENTS.md, and ADRs — it does not replace them.
start coding agent
↓
Canon automatically loads relevant active decisions
↓
agent receives them
Static files go stale. Agents forget last month's review thread. Asking people to stop mid-sprint and write an ADR does not survive week three.
Canon mines recent merged PRs (or Git history) and proposes candidate decisions. A human only approves or rejects. Confirmed decisions are injected automatically at the start of the next agent session. Old decisions are superseded, never silently deleted.
canon initwires local storage and agent integrations.canon suggestreads recent merged PRs or commits and proposes conservative candidates.- You approve or reject. That interaction should take seconds, not a writing session.
- On the next Claude Code session, a SessionStart hook injects relevant active decisions.
- Cursor reads an always-apply project rule that points at the generated snapshot.
Requires Python 3.11 or newer and Git.
pip install git+https://github.com/Adarshk18/Canon.gitFrom a local checkout (development):
python -m pip install -e ".[dev]"The product and CLI are Canon. The install name is canon-memory because canon is taken on PyPI by an unrelated package. There is no PyPI release yet; install from this GitHub repo.
canon --help
canon --versionIf canon is not recognized (common on Windows user installs):
python -m canon --versionAdd your Python Scripts folder to PATH, or keep using python -m canon.
cd my-project
canon init
canon suggest
canon approve 1
canon inject-previewThen start Claude Code or a new Cursor Agent chat in the same repository. The agent should see the confirmed decision without you running canon query.
V1 does not auto-inject into ChatGPT or standalone Grok. Those tools can only see Canon if you attach .canon/injection.md.
canon init installs an official SessionStart command hook in .claude/settings.json (exec form, no shell):
{
"type": "command",
"command": "canon",
"args": ["inject", "--for-hook"],
"timeout": 15
}Claude Code injects the hook's additionalContext at session start, resume, clear, compact, and fork. Output stays under the 10,000 character hook cap.
canon init also writes a small managed rule at .claude/rules/canon.md. It does not rewrite your CLAUDE.md.
Inspect: canon doctor
Disable: canon uninstall
Troubleshoot: confirm canon is on PATH inside the Claude Code environment.
Cursor has no session-start hook. Canon uses the officially supported project rules mechanism:
.cursor/rules/canon.mdcwithalwaysApply: true@.canon/injection.mdreferenced from that rule.canon/injection.mdregenerated oninit,approve,reject, andsuggest
Limitation: Cursor will not re-read a changed snapshot until a new Agent session (or a rule reload). Approve a decision, then start a new chat. This is a Cursor platform limitation, not a missing Canon command.
| Command | Purpose |
|---|---|
canon init |
Create local storage, schema, and agent wiring. Idempotent. |
canon status |
Project, database, GitHub, and integration status. |
canon suggest |
Mine recent PRs/commits for conservative candidates. |
canon approve [id] |
Candidate → active. May supersede an older decision. |
canon reject <id> |
Candidate → rejected. Record is kept. |
canon list |
List decisions. --active, --superseded, --rejected, --all, --tag. |
canon show <id> |
Full body and provenance. |
canon inject-preview |
Exactly what an agent would receive. |
canon doctor |
Environment and integration checks. |
canon config |
Show or set project configuration. |
canon export |
Portable JSON of stored decisions. |
canon import |
Validated import. |
canon uninstall |
Remove managed integrations. History is kept unless --purge-data. |
canon version |
Print the version. |
Most commands accept --json. Debug with --debug or CANON_DEBUG=1.
Exit codes: 0 success, 1 application error, 2 invalid usage.
candidate → active
candidate → rejected
active → superseded
Rejected and superseded records stay in SQLite. Injection uses active decisions only.
Decision #42
Status: SUPERSEDED
Superseded by: #57
Every suggested and approved decision records what Canon actually knows:
- source PR or commit
- source repository
- source date
- confirmation date and confirmer
If a field is unavailable, Canon says so. It never invents PR numbers, hashes, authors, dates, or URLs.
The default is local and private. No account is required.
- SQLite, decision history, and injected context stay on disk.
- Network is used only for optional GitHub PR metadata.
- Telemetry is off unless you set
CANON_TELEMETRY=1, and even then V1 only appends local events to.canon/telemetry.jsonl.
See PRIVACY.md.
Canon treats commit messages, PR titles, PR bodies, filenames, and API responses as untrusted data. They are never executed and never treated as instructions. Git and gh run with argument lists, not a shell. SQL is parameterized.
See SECURITY.md.
Precedence:
CLI arguments
↓
environment variables
↓
project config (.canon/config.toml)
↓
user config (~/.canon/config.toml)
↓
defaults
Defaults:
| Setting | Default | Meaning |
|---|---|---|
injection.max_decisions |
12 | Hard cap on injected decisions |
injection.max_chars |
4000 | Hard cap on injection characters |
injection.max_tokens |
1000 | Estimated token cap (chars / 4) |
mining.lookback_prs |
20 | Merged PRs to inspect |
mining.lookback_commits |
80 | Commits to inspect when GitHub is unavailable |
mining.min_score |
6 | Conservative suggestion threshold |
privacy.telemetry |
false | Local event log only, off by default |
Copy .env.example if you need environment overrides. Never commit .env.
| Symptom | What to do |
|---|---|
not a Canon project |
Run canon init inside a Git repository. |
GitHub CLI is installed, but you are not authenticated |
Run gh auth login, then canon suggest. |
canon is not recognized |
Use python -m canon ... or add Python Scripts to PATH. |
| No candidates | Expected on mechanical/UI-only history. Product/policy and stack choices should appear. |
| Claude session has no decisions | Run canon doctor. Confirm canon is on PATH. Start a new session. |
| Cursor ignores a new decision | Start a new Agent chat after canon approve. |
| Offline | list, status, and inject-preview work without a network. suggest falls back to Git history. |
See CONTRIBUTING.md.
python -m pip install -e ".[dev]"
pytest
ruff check src tests
mypy
python -m buildpytest covers decision lifecycle, supersession, mining filters, injection budget, CLI commands, path traversal, SQL injection, command injection, and prompt-injection-style repository content.
Intentionally not in V1, matching the source product plan:
- Slack or Notion connectors
- Daily multi-project drift engine
- Rich team dashboard
- Deep MCP query interface
- Enterprise self-host packaging
- Organization management and SSO
- Billing and cloud sync
The domain model is isolated from SQLite so those can be added later without rewriting the local core.
An earlier plan used the working name DecisionVault. The product is Canon.
MIT. See LICENSE.