Local-first memory, hook, MCP, and code graph runtime for coding agents
Project description
CCLG
Local-first memory, hook, MCP, and code graph runtime for coding agents.
This map shows the core CCLG runtime: agent hosts call lifecycle hooks and MCP tools, CCLG stores source evidence plus graph records, and only active packs return to the agent as context.
CCLG keeps coding-agent memory on your machine. It turns agent history into a Canonical Chat Ledger Graph:
- raw transcript and tool output stay as source evidence;
- long-term memory is stored as source-grounded JSON nodes;
- corrections are applied through explicit patches;
- agents consume only the active effective view;
- the whole ledger travels as one portable
.cclgcontainer file; - hooks inject ActiveMemoryPack and CodeGraphPack context;
- MCP exposes memory, patch, code-search, and bench tools.
The broader product name from the original design discussion was ACMC, Agentic Context Memory Compiler. This repository implements the local CCLG runtime first. Hosted API is intentionally out of scope for this phase.
Install CCLG
One command installs the local CLI, hook adapter, MCP server, Codex skills, and safe config examples:
curl -fsSL https://raw.githubusercontent.com/schift-io/cclg/main/scripts/install.sh | bash
Prerequisites:
- Python 3.11 or newer;
- Git;
- a POSIX shell;
- Codex or Claude only if you want to wire the optional host integrations.
If cclg is not on your shell path after install:
export PATH="$HOME/.local/bin:$PATH"
Installed commands:
~/.local/bin/cclg
~/.local/bin/acmc
~/.local/bin/cclg-hook
~/.local/bin/cclg-mcp
Default memory store:
~/.cclg
The installer also copies example host config files:
~/.codex/hooks.cclg-example.json
~/.codex/mcp.cclg-example.json
~/.claude/settings.cclg-example.json
These examples are intentionally not merged into your live config. Review and merge them only after checking your existing hooks and MCP entries.
Install From Checkout
git clone https://github.com/schift-io/cclg.git
cd cclg
./scripts/install.sh --from-checkout
For local development from the checkout:
./scripts/install.sh --from-checkout --editable
Optional Dense Retrieval
Dense retrieval is off by default. The local MVP works with exact grep, BM25, graph, temporal indexes, and source-grounded packs without embeddings.
Enable dense retrieval during install only when you want it:
CCLG_DENSE_PROVIDER=local ./scripts/install.sh --from-checkout
Supported provider values:
auto local ollama openai schift google cloudflare
Hosted providers need their normal environment keys. Local dense mode installs
cclg[dense] into the CCLG virtual environment and uses a local embedding model.
Check the current dense setting:
cclg dense status
Verify
After install, run:
cclg status
cclg doctor --json
cclg bench run --suite all --repo-root "$PWD"
doctor checks store invariants. bench exercises mutation, detection,
retrieval, pack, hook, MCP, and code graph surfaces.
Quick Start
Add and retrieve a source-grounded memory:
cclg add \
--type project_decision \
--content "CCLG is local-first by default." \
--source "manual:quickstart"
cclg grep "local-first"
cclg search "local-first"
cclg pack --query "what memory should this agent use?" --format toml
Code graph context:
cclg code-index "$PWD"
cclg code-search "MemoryPatch compile_pack" --repo "$PWD"
Hook smoke:
printf '%s' '{"prompt":"CCLG hook smoke","session_id":"demo"}' \
| cclg-hook user-prompt --include-codegraph --code-root "$PWD"
MCP smoke:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| cclg-mcp --line-delimited
Codex And Claude Setup
The installer writes Codex skills automatically:
~/.codex/skills/cclg-memory
~/.codex/skills/cclg-codegraph
~/.codex/skills/cclg-hooks
~/.codex/skills/cclg-bench
If you need to regenerate only the Codex skills:
cclg apply-codex --write-skill
Hook and MCP examples are copied to ~/.codex and ~/.claude as example files.
They are not live until you merge them into your host settings.
Use CCLG From Your Stack
One format, three consumption modes — the same .cclg ledger travels between
all of them:
.cclg (one portable memory format)
|
+-----------------+----------------------+
| | |
v v v
+------------------+ +--------------------+ +----------------------+
| coding agents | | agent runtimes | | hosted memory |
| | | (APM agent packs) | | |
| Claude Code | | | | example: |
| Codex | | example: | | Schift AI Memory |
| Hermes | | Schift runtime | | |
| | | | | |
| hooks + MCP | | store-less library | | lossless container |
| ~/.cclg store | | over tenant memory | | load + effective- |
| ActiveMemoryPack | | + .cclg export | | view projection |
+------------------+ +--------------------+ +----------------------+
With coding agents (Claude Code / Codex / Hermes)
The installer wires lifecycle hooks and the MCP server into the host. Every
session accumulates source-grounded nodes in ~/.cclg; corrections become
patches; the hook injects only the active effective view as an
ActiveMemoryPack. Hermes and other agent-workbench hosts integrate the same
way: hook on user prompt, MCP tools for explicit memory/patch/search calls.
With an APM agent runtime (example: Schift)
Runtimes that execute APM (Microsoft's agent package standard) packages can bind CCLG as their memory layer. Schift's runtime does exactly this — it consumes CCLG as a store-less library over its own tenant-scoped memory store, no second store, no sync:
tenant memory store (single source of truth)
| memories
v
CCLG effective view -> corrected/forgotten facts never reach sub-agents
CCLG patch detection -> user corrections become supersession patches
ActiveMemoryPack -> session context, order-stable + token-budgeted
.cclg export -> any session leaves as one portable container file
The pack is what the agent actually reads: stable ordering keeps provider prompt caches warm, and the budget keeps token spend bounded. The export means an agent's memory is never locked in — it is a file.
With hosted memory (example: Schift AI Memory)
The hosted wrapper loads .cclg containers losslessly: the container bytes are
preserved verbatim (patches and edges stay first-class records), and the
searchable flat view is derived as a projection of the on-load effective view —
never a second source of truth.
Bring your own runtime
The core stays host-agnostic. Runtime-specific install paths and host adapters
(a new agent host's hook config, an APM pack-runtime binding) land as pull
requests: adapters live under adapters/<host>/, integration docs under
docs/. If your runtime can read JSON and verify a sha256, it can speak
.cclg.
CCLG As The Schift AI Memory Format
Yes: the target architecture is that Schift AI Memory uses CCLG as its memory format/runtime. They should not be treated as two permanent memory formats. The current split exists only because the kernel and the product wrapper are being hardened at different layers.
Schift memory strategy
|
+-- CCLG
| canonical memory format / local runtime / kernel
| proves: ledger, provenance, patches, active packs, code graph
| ships: cclg, cclg-hook, cclg-mcp, ~/.cclg
|
+-- Schift AI Memory
Schift product wrapper / auth / hosted routing
proves: OAuth, API key, Schift bucket, work-log upload, MCP fetch
ships: npx installer, Schift auth, plugin/host config
The intended dependency direction:
Schift AI Memory
|
| uses CCLG records and packs
v
CCLG
NOT:
CCLG
|
| depends on Schift auth/buckets
v
Schift AI Memory
That means Schift AI Memory should add a Schift envelope around CCLG data, not replace the CCLG memory model:
CCLG node / session / pack
|
v
+--------------------------------------+
| Schift AI Memory transport envelope |
| org_id / user_id / bucket / policy |
| upload status / queue metadata |
+--------------------------------------+
|
v
Schift bucket / company memory surface
The practical reason to keep both repositories during this phase:
CCLG answers: "What is the memory model?"
Schift AI Memory answers: "How does a Schift user install, auth, sync, and query it?"
Maintenance rule:
Keep CCLG separate while the format/runtime is still being hardened.
Do not duplicate CCLG node/patch/pack semantics in Schift AI Memory.
Make Schift AI Memory consume CCLG as the canonical format.
Concrete adapter plan: docs/SCHIFT_AI_MEMORY_FORMAT.md.
Current Runtime Compatibility
The loader half of that adapter has landed: Schift AI Memory reads .cclg
containers losslessly and carries CCLG-shaped records instead of a competing
format. Operationally the two packages still run as side-by-side host
integrations with separate stores.
same Codex / Claude host
hooks / MCP config
|
+-----------------+-----------------+
| |
v v
+--------------------------+ +--------------------------+
| CCLG | | Schift AI Memory |
| local working memory | | company work log |
| store: ~/.cclg | | config: ~/.schift/... |
| raw / nodes / patches | | store: Schift bucket |
| ActiveMemoryPack | | summaries / metadata |
| CodeGraphPack | | search / fetch |
+------------+-------------+ +------------+-------------+
| |
v v
current task context company memory surface
What works today (verified end-to-end against real producer bytes):
runtime session export ---> .cclg (corrections preserved as patches)
.cclg container ---> Schift AI Memory envelope
(verbatim container payload, zero record loss;
patches/edges stay first-class records)
on-load effective view ---> searchable flat projection
(superseded/forgotten facts excluded)
Still pending on the hosted side:
live upload/search smoke against a real Schift bucket
Schift search result -X-> CCLG raw evidence import
Schift bucket policy -X-> CCLG local ledger state
Use CCLG for local task context and Schift AI Memory for Schift-owned work-log upload/retrieval. When both are installed today, merge hook and MCP config explicitly instead of overwriting either tool's generated examples.
Update Or Uninstall
Update from the public repository by rerunning the installer:
curl -fsSL https://raw.githubusercontent.com/schift-io/cclg/main/scripts/install.sh | bash
From a checkout:
git pull --ff-only
./scripts/install.sh --from-checkout
Uninstall commands and installed app files while keeping local memory:
~/.local/share/cclg/repo/scripts/uninstall.sh
Delete local memory too:
~/.local/share/cclg/repo/scripts/uninstall.sh --purge-store
From a checkout, the same commands can be run as ./scripts/uninstall.sh.
The PRD used acmc as the product command name. This repo keeps cclg as the
primary binary and installs acmc as the same local CLI alias.
Format
Visual explainer:
docs/explainer/index.html
docs/explainer/cclg-runtime-map.png
format/cclg.format.v0.1.toml
docs/DATA_MODEL.md
docs/explainer/demo-store/
CCLG records are versioned by format/cclg.format.v0.1.toml.
MemoryNode
{
"schema_version": "cclg.memory_node.v0.1",
"id": "mem_...",
"type": "project_decision",
"scope": {
"user": "user_local",
"org": null,
"workspace": "local",
"project": "CCLG",
"agent": "global",
"session": null
},
"key": "project.cclg.local_first",
"content": "CCLG is local-first by default.",
"status": "active",
"confidence": 1.0,
"priority": "high",
"source": {
"label": "manual:quickstart",
"session_ids": [],
"turn_ids": [],
"raw_spans": [],
"tool_result_ids": [],
"artifact_ids": []
},
"relations": {
"supersedes": [],
"superseded_by": [],
"refines": [],
"expands": [],
"narrows": [],
"contradicts": [],
"depends_on": [],
"derived_from": []
},
"retrieval": {
"sparse_keys": ["cclg", "local-first"],
"dense_text": "CCLG is local-first by default.",
"entity_keys": [],
"temporal_keys": []
}
}
Statuses:
active can be injected
active_session can be injected only for that session id
superseded replaced by newer memory
deprecated discouraged old memory
expired no longer valid
forgotten intentionally removed from active memory
discarded rollback/session discard output
MemoryPatch
{
"schema_version": "cclg.memory_patch.v0.1",
"id": "patch_...",
"operation": "refine",
"target_ids": ["mem_old"],
"new_node_ids": ["mem_new"],
"reason": "User clarified adapter scope.",
"new_content": "CCLG must support Claude Code, Codex, Hermes, and hosted ReACT agents.",
"source": {
"label": "manual",
"session_ids": [],
"turn_ids": [],
"raw_spans": [],
"tool_result_ids": [],
"artifact_ids": []
},
"resolution_policy": {
"rule": "manual",
"auto_applied": true,
"requires_review": false
}
}
Patch operations:
create update supersede refine expand narrow merge split expire deprecate
forget resolve_conflict rollback
MemoryEdge
{
"schema_version": "cclg.edge.v0.1",
"id": "edge_...",
"from": "mem_new",
"to": "mem_old",
"type": "refines",
"source_patch_id": "patch_..."
}
ActiveMemoryPack
Task-scoped prompt context. It includes active memory and labels suppressed memory so agents do not inject it as current truth.
cclg pack --query "current task" --format markdown
CodeGraphPack
Task-scoped code context. It ranks files and symbols using git files, definitions, imports, edges, and churn. It is context ordering, not source proof.
cclg code-search "symbol or task" --repo "$PWD"
Hooks and Tools
Hook adapter:
cclg-hook user-prompt --include-codegraph --code-root "$PWD"
MCP server:
cclg-mcp
MCP tools:
cclg.search
cclg.grep
cclg.bm25
cclg.pack
cclg.add
cclg.patch
cclg.raw
cclg.code_index
cclg.code_search
cclg.audit
cclg.bench
memory.search
memory.grep
memory.bm25
memory.pack
memory.patch
memory.audit
Safe config examples are installed to:
~/.codex/hooks.cclg-example.json
~/.codex/mcp.cclg-example.json
~/.claude/settings.cclg-example.json
The installer does not overwrite existing Codex or Claude config.
Bench Scorecard
Local deterministic suites:
mutation stale memory suppression
retrieval sparse active memory retrieval
pack ActiveMemoryPack excludes suppressed nodes
hook additionalContext injection
mcp initialize, tools/list, tools/call
codegraph files, symbols, edges, search
Run:
cclg bench run --suite all --repo-root "$PWD"
External benchmark tracks to add next:
- LoCoMo / LongMemEval / LongMemEval-V2 / FAMA for memory quality.
- MCP-Bench / MCPBench / LiveMCPBench / MCP-Universe style tasks for tool use.
- Aider repo-map and Tree-sitter code-graph tasks for code graph quality.
Storage
~/.cclg/
raw/ source evidence
nodes/ MemoryNode JSON
patches/ MemoryPatch JSON
edges/ MemoryEdge JSON
sessions/ hook/session event state
active/codegraphs/ generated CodeGraph artifacts
audit/ JSONL audit log
Raw sources are the source of truth. Summaries are routing aids, not authority.
Portable .cclg Container
The whole ledger can travel as one file. cclg pack-file packs the raw ledger
records (nodes, patches, edges, sessions) into a single portable .cclg
container; cclg open validates and inspects one without loading it into a
store.
cclg pack-file backup.cclg
cclg open backup.cclg
Container invariants:
ledger-only the effective view is never stored; it is recomputed on load
auth-free no tokens, credentials, or host paths inside the container
layout-free self-contained; does not depend on ~/.cclg internals
checksummed sha256 over the record payload; tampering fails open
Think .gguf for agent memory: the container is the interchange boundary.
Any runtime that speaks cclg.container.v0.1 — the local CLI, a hosted
loader, another agent host — can load the same memory losslessly, patches and
provenance included.
Spec: docs/CCLG_CONTAINER.md.
Public Release
Target:
https://github.com/schift-io/cclg
Dry run:
scripts/publish-github.sh
Create/push:
scripts/publish-github.sh --execute
Docs
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cclg-0.2.0.tar.gz.
File metadata
- Download URL: cclg-0.2.0.tar.gz
- Upload date:
- Size: 423.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00a29fff445d0923e0588d1cf7f5bd125117ee0f9e056eb4744149e63c6b6f8b
|
|
| MD5 |
68df75acb0db79ad77c419494509b8e0
|
|
| BLAKE2b-256 |
741c50e7a113f5e00bfc9cf2b3f76f86ca0decee7811e8d8500e5ba9ea4d1547
|
File details
Details for the file cclg-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cclg-0.2.0-py3-none-any.whl
- Upload date:
- Size: 129.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
38d0b1a15064af1a76df0ed96fd401992ebe129940afab26bff4c6380dbc63ce
|
|
| MD5 |
17303b32a1f0729d43b7071455b641d5
|
|
| BLAKE2b-256 |
0b65e572a5582dd54ae6e3ea10a433f3bf5c55938582d09e81b4151fb43fa947
|