Getting it into your agent
This one installs as part of its plugin. Adding the marketplace and installing the plugin brings it with everything else the plugin ships.
/plugin marketplace add skymanbp/cc-memory/plugin install cc-memoryWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/commands/skymanbp/cc-memory/cc-mem)<a href="https://agentmods.dev/commands/skymanbp/cc-memory/cc-mem"><img src="https://agentmods.dev/badge/commands/skymanbp/cc-memory/cc-mem/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/commands/skymanbp/cc-memory/cc-mem"><img src="https://agentmods.dev/badge/commands/skymanbp/cc-memory/cc-mem.svg" alt="Reviewed on agentmods" width="80" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00049 | $0.03955 |
| Opus 5.5 | $0.00020 | $0.01582 |
| Sonnet 5.5 | $0.00010 | $0.00791 |
| Haiku 4.5 | $0.00005 | $0.00396 |
Grade A, and why
cc-mem scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 3d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 136 lines — stays where its author put it; the contents beside it link to each section on GitHub.
/cc-mem — cc-memory CLI front-end
Run cc-memory CLI commands against the current project. The wrapper resolves
--project . for you.
Subcommands
Every subcommand cc_memory/cli/mem.py defines is listed here, and
tests/smoke_test.py fails if one is missing — an undocumented subcommand is
one nobody uses, and sql's read-only guard is a security fix that only helps
someone who knows the command exists.
| Subcommand | Effect |
|---|---|
stats |
Database statistics + supersede-chain count |
status |
Full health check (hooks, DB, API key, PROGRESS state, consolidation: last run, backlog and which LLM stages ran — v2.16.0) |
search <q> |
Search active memories. FTS5 ranked by bm25, with a LIKE substring fallback whenever the index answers empty. CJK and Latin do not behave identically, and the difference is the tokenizer's, not a bug: the index is built with tokenize='trigram', which segments Chinese / Japanese / Korean but has a 3-character floor, so a 1- or 2-character CJK query (超时) is answered by the substring fallback rather than by the index — same rows, no ranking. Latin text tokenises into words, so a multi-word query matches non-contiguously (deploy rotated finds "the deploy key is rotated monthly"), which the substring fallback cannot do. A query that strips to empty returns nothing — list is the surface that means "show me everything" |
list [category] [--limit N] [--sessions N] |
List memories (filter by decision/result/bug/...). --limit caps the rows (default 20); --sessions scopes to memories from the N most recent sessions (default 5) plus every session-less manual save |
add <category> "<text>" [--importance 1-5] [--tags a,b] [--topic T] |
Add one memory through the anti-patch writer. Defaults: importance 3, tags manual (--tags replaces it, comma-separated), no topic |
topics |
Show topic summaries |
keywords |
Top project vocabulary by frequency |
sessions |
Compaction history with archive paths |
observations [--limit N] |
Raw PostToolUse rows still awaiting extraction (default 30) |
schema |
Print the live SQLite schema (tables, indexes, migrations) |
paths [--json] |
Print the resolved project artifact paths — database, PROGRESS.md, PLAN.md, MEMORY.md — each with an exists/absent verdict. Read-only: an absent artifact is an answer, not an error. Exists because status reports counts with no locations, and hunting for the DB with an rglob found another project's file first |
sql "<SELECT ...>" [--json|--full] |
Run a read-only query. Write statements are refused — only plain SELECT, WITH … SELECT, EXPLAIN and read-only PRAGMA run, and the PRAGMA name(value) setter form is refused too (an =-only test used to let it through). The default table truncates cells at 60 chars; --full prints untruncated column: value blocks, and --json emits pure-ASCII JSON (\uXXXX escapes), which no capturing shell's decode codec can garble — use it whenever CJK text comes back as � |
progress |
Force-regenerate .ccm/PROGRESS.md from DB and print it |
supersedes <id> |
Walk the supersede chain for a memory ID (anti-patch history) |
adopt-stray <path> [--dry-run|--yes] |
Merge ONE stray database below this project into it — the remedy status names on each [WARN] Separate database below this project row. A dry run unless --yes. With --yes the stray's whole state directory is first MOVED (never deleted) to .ccm/.adopted/<date>/<relative path>/, then every table is imported with a provenance row in adopted_rows: memories active and archived (re-numbered, supersede links re-mapped; an active exact duplicate of a project row is imported archived and linked to it), sessions (archive paths re-pointed), summaries, topics by name, keywords summed, directives by slug, observations only where retention keeps them. Re-running is a no-op; a run cut off after the move resumes. Refuses anything not strictly below the project root, the project's own database, a .ccm-root-pinned directory and a link (exit 1); a stray in use is reported pending with nothing moved (exit 3) |
archive <id>... [--supersedes ID] |
Retire memories found to be WRONG: is_active=0, recoverable, never DELETE. The supported exit from "this stored fact is false" — sql is read-only and add reconciles only when the new text scores similar enough to the old. --supersedes records which memory replaced them, keeping the chain walkable. Refuses ids from another project (memories.id is global to the DB file) |
consolidate [--deep] [--no-llm] |
Run the full LLM-backed consolidation pipeline, then stamp the cadence marker (so the Stop hook's backpressure probe doesn't re-run what you just ran). --deep first loops the semantic-dedup judge until a round confirms nothing new — the way to pay a months-old backlog down in one sitting; already-refused groups are never re-judged within the run |
cleanup |
Lightweight no-LLM cleanup + MEMORY.md regen |
summary |
Latest session summary (request/done/next_steps) |
mode [name] |
Show/set project mode (code/research/writing) |
announce [full|batch|off] |
Show/set what the hooks tell YOU they did (v2.17.0): one line per act in the transcript — ccm ▸ 注入(inject) … at session start, 召回(recall) when a prompt pulled memories, 提炼(extract) / 整理(consolidate) / 存档(save) when a background run or a compaction finished, 计划捕获 / 计划同步 for the plan anchor, 失败(error) when a hook swallowed a failure. full (default) shows every line, batch drops the per-prompt recall line, off shows none. The lines are systemMessages: shown to you, never sent to the model. Per project, like mode |
serve [--port N] [--no-open] |
Launch the browser-based web viewer (stdlib http.server; port 9377 by default). --no-open skips opening a browser tab |
dashboard |
Launch the Tkinter GUI dashboard for this project |
plan-status |
Live-plan counters + freshness summary (no LLM) |
plan-show |
Regenerate + print .ccm/PLAN.md |
plan-set --raw '<text>' |
Capture a raw plan, mark needs_refine=1 |
plan-set --raw-file FILE |
Same, but read raw from a file (UTF-8, a BOM tolerated; any other encoding is refused with a one-line remedy) |
plan-set --from-refiner |
Read structured JSON from stdin (refiner output; a leading BOM, as PowerShell 5.1's > writes, is stripped) |
plan-check |
Record a guardian check you have just run (resets the drift counters) — guardian FIRST, then this |
plan-replan |
Re-arm needs_refine on the current raw |
plan-clear |
Drop the active plan + delete PLAN.md. Archived to .ccm/.plan_history/ first; --reason "<why>" is required when unfinished steps exist (refuses and exits 1 otherwise — v2.4.0 carryover gate) |
directive-list [--status active|blocked|done|superseded|dropped|all] [--json|--full] |
Standing user directives, most-repeated first. A directive is a unit of user INTENT and outlives every plan; a plan step is a unit of execution and dies with its plan. Default filter is active; blocked rows show as [b]. --full lifts the per-field truncation, --json emits full rows as pure-ASCII JSON |
directive-add <slug> [--quote "..."] [--demand "..."] [--kind standing|feature|process|oneoff|constraint] [--times N] |
Record a directive. Re-adding the same slug bumps times_stated on the ONE row rather than creating a second — repetition is the importance signal a plan cannot express. --times sets the count outright, for backfilling from a transcript audit. Reference plan steps by TITLE, never by number — step ids are re-assigned on every replacement, and the command warns when the text contains step #N / 步骤 N |
directive-edit <slug> [--demand ...] [--quote ...] [--kind ...] [--status active|blocked] |
Correct a directive's record without bumping its count — an edit is maintenance, not a re-statement, and directive-list sorts by the count. Never creates (exits 1 on an unknown slug). --status blocked parks a directive that is waiting on the user — idle enforcement skips it — and --status active un-parks it; closure stays with directive-close, whose evidence gate this door must not bypass |
directive-close <slug> --evidence "<checkable>" [--status done|superseded|dropped] |
Close a directive. --evidence is mandatory and refuses an empty value (exits 1): a commit sha, file:line, or a gate name. A directive closed on an assertion is the exact failure the ledger exists to prevent |
inject-show [--templates] |
Show exactly what the last SessionStart injected (ground truth); --templates lists every Claude-visible text template in core/prompts.py with its size (v2.16.0) |
inject-usage [--window N] [--judge] |
Deterministic signals, each naming the window it was measured over — a count over an unstated window reads as evidence of absence and is not. Counts Read-tool observations targeting PROGRESS.md / MEMORY.md across the last --window observation rows (default 200; widen it when a long session may have buried the Read past them), and reports whether the handoff ack was stated — the sentence SessionStart's forced reminder demands, matched through the same constant that emits it (core.progress.ACK_TEMPLATE), in the transcript of the session that received the last injection. The ack is tri-state: yes / no / unmeasured, and unmeasured (no injection recorded, no session id in the manifest, no transcript on disk) is never rendered as no. Quoting the reminder is not stating the ack, and neither is a tool argument that contains it. Per-memory #id matching is deliberately not done — it is feasible, since the timeline layer renders #<id> for every entry past the fifth, but it would need the ids that layer actually spelled rather than the whole manifest, or it measures the injection's shape and reports it as Claude's behaviour. --judge adds layer 2, off by default: one LLM call that reads Claude's own replies from that session and judges, per delivered memory, whether it was used / unused / unknown. Layer 1 above is deterministic, free and always runs; layer 2 costs an API call, which is why it is asked for rather than assumed. unknown — no credential, a refused call, an unparsable answer, an id the judge skipped — is never printed as unused: an outage of ours is not evidence about Claude |
encoding-check [--apply] |
Scan for U+FFFD corruption (read-only; --apply quarantines) |
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 3d ago Changed · +2 lines 5e023bf001e3
- 14d ago Changed d44f96a4ef8f
- 22d ago Changed 976daae765f1
- 1mo ago Changed 43ff54599799
- 1mo ago Changed 9c77b7b671f9
- 1mo ago First seen · 134 lines · 49 tokens per session scan A 7b7926a90d23
cc-mem is a command published in the GitHub repository skymanbp/cc-memory (9 stars, last pushed 2d ago), licensed MIT. It adds 49 tokens to every session and 3,955 once invoked, about $0.0002 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.
Other commands, from other repositories
recall-save
Generate / overwrite .recall/context.md with Recall's local offline summarizer.
show
Show this project's saved Recall context (.recall/context.md).
evolve-lite-adapt-memory
Mirror a just-saved native memory into the shared evolve store so it becomes shareable and auditable.
patterns
Scan recent sessions for recurring patterns and turn them into captures (rules, concept notes, writing seeds).
ccc-context-budget
Show how full the context window is, which components use the most of it, and when to save the session and compact. Use when the session feels slow, long or near its context limit.
tour
Tour this project's memory — what's captured, where it lives, how to recall it (core-memory-kit).