Proprioception for coding agents — slice recent Claude Code transcript, redact, send to a model gateway, return structured steering guidance.
Project description
cept — proprioception for coding agents
Short for proprioception. Cept is the agent's mirror.
Coding agents loop. They polish corners while the center is wrong. They retry the same fix three times instead of asking what they're missing. cept is a meta-tool that gives an agent a structured way to step back, look at its own recent trajectory, and request outside-in steering — through OpenRouter, defaulting to a model with web search baked in.
What it does
When invoked (explicitly via "use cept" or by an agent that has reached a decision point):
- Locate the active Claude Code session JSONL under
~/.claude/projects/<dashed-cwd>/. - Slice the last N minutes of events (default 20).
- Distill raw events into a steering packet — decisions, attempts, errors, files touched, loops.
- Collect repo state — branch, dirty files, diff stat.
- Redact API keys, bearer tokens, env values, PEM blocks, emails, home paths.
- Ask an OpenRouter model (default
perplexity/sonar-reasoning— reasoning + live web search) with a mode-specific prompt. - Return a structured response: hypotheses, recommended next step, facts to verify, confidence.
Modes
| Mode | Use when |
|---|---|
steer |
Default. Broad outside-in guidance, blind spots, next step. |
debug |
Rank likely causes from evidence and errors. |
research |
Find external facts, docs, version gotchas. |
architecture |
Compare design alternatives and tradeoffs. |
Install
cd cept
uv sync
Per-tree keys with .ceptkey
Drop a .ceptkey (preferred) or ceptkey file anywhere in your directory tree. Cept walks up from the working directory until it finds one, then loads it as dotenv. The file overrides process env — so if you have OPENROUTER_API_KEY exported in your shell but a .ceptkey in the project tree, the project key wins. That's the point: per-folder cost attribution and project-specific model defaults.
Easiest way is to use the bundled scaffold:
cept-keyfile init \
--service openrouter \
--name cept-djs-01 \
--key sk-or-... \
--model perplexity/sonar-reasoning \
--scope "~/repos-eidos-agi/" \
--notes "Eidos AGI shared key" \
--path ~/repos-eidos-agi/.ceptkey
That writes a 0600-permissioned file with auto-populated provenance (created_at, created_on, created_by, created_os) plus the values you passed. Inspect at any time:
cept-keyfile show # nearest keyfile, metadata only — no values
cept-keyfile where # just the path
By hand it looks like:
# cept-meta:service=openrouter
# cept-meta:key_name=cept-djs-01
# cept-meta:created_at=2026-04-27T18:16:44+00:00
# cept-meta:created_on=daniels-mbp.local
# cept-meta:created_by=dev@example.com
# cept-meta:created_os=Darwin 24.3.0 (arm64)
# cept-meta:scope=~/repos-eidos-agi/
# cept-meta:notes=Eidos AGI shared key
OPENROUTER_API_KEY=sk-or-clientA...
CEPT_DEFAULT_MODEL=anthropic/claude-sonnet-4-5:online
CEPT_LOOKBACK_MINUTES=10
# cept-meta: lines are pure comments to anything that isn't cept (including source ./.ceptkey in your shell), but cept captures them as a metadata block surfaced in the result. Useful for auditing which key is which without leaking values.
Walk stops at the first match. Capped at $HOME when cwd is under home; otherwise capped at filesystem root. Add .ceptkey and ceptkey to your global gitignore so you never commit one by accident.
Recognized keys:
| Key | Effect |
|---|---|
OPENROUTER_API_KEY |
OpenRouter credential. |
OPENROUTER_REFERER |
Optional HTTP-Referer header for OpenRouter app rankings. |
OPENROUTER_TITLE |
Optional X-Title header. |
CEPT_DEFAULT_MODEL |
Per-tree default model (e.g. openai/gpt-5:online). |
CEPT_LOOKBACK_MINUTES |
Per-tree default lookback window. |
⚠️ Trust model: cept loads any
.ceptkeyit finds while walking up. If youcdinto a hostile repo with a malicious.ceptkey, your packets would route to that endpoint. Blast radius is the redacted packet (no real key exfil), but be aware. v1 doesn't dodirenv allow-style ceremony — just don'tcdinto untrusted trees.
Model selection (via OpenRouter)
cept uses OpenRouter as the gateway, so you can swap models without changing the client.
| Model id | Why |
|---|---|
perplexity/sonar-reasoning (default) |
Reasoning + live web search. Best for steer/debug. |
perplexity/sonar-pro |
Fast web search, no reasoning trace. |
anthropic/claude-sonnet-4-5:online |
Claude with web search via OpenRouter (:online suffix). |
openai/gpt-5:online |
GPT with web search. |
Append :online to any compatible model name to force web search.
MCP server
Register with Claude Code:
// ~/.claude/claude_desktop_config.json (or equivalent MCP host config)
{
"mcpServers": {
"cept": {
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/cept", "cept"],
"env": {
"OPENROUTER_API_KEY": "sk-or-...",
"OPENROUTER_TITLE": "cept",
"OPENROUTER_REFERER": "https://github.com/eidos-agi/cept"
}
}
}
}
The OPENROUTER_TITLE and OPENROUTER_REFERER env vars are optional — they show up on OpenRouter's app rankings.
Then in a Claude Code session: "use cept — I'm stuck on the OAuth callback."
CLI (dry-run / debugging)
# Distill the current session and print the redacted packet without calling OpenRouter:
cept-cli --goal "fix oauth callback" --dry-run
# Send for real:
OPENROUTER_API_KEY=sk-or-... cept-cli --goal "fix oauth callback" --mode debug
# Include source files for content-shape critique (not just trajectory):
OPENROUTER_API_KEY=sk-or-... cept-cli --goal "audit this readme" \
--file research-findings/README.md --file checklist.md --mode debug
# Try a different model:
OPENROUTER_API_KEY=sk-or-... cept-cli --goal "..." --model "anthropic/claude-sonnet-4-5:online"
Including source files in the packet
By default, cept's packet describes what the agent did — tool calls, decisions, errors. Trajectory critique catches workflow problems but misses content problems ("this README quotes a statute that's actually from a summary page", "this function ignores its null branch"). Pass file paths to include their content in the packet:
- MCP:
files=["README.md", "src/handler.py"] - CLI: repeat
--file PATH
Each file is capped at 50 KB; total at 256 KB across all files; max 24 files per call. Truncated files keep the head and append a marker. Binary files (NUL byte detected) are skipped with a note. Paths can be absolute or relative to cwd. Redaction still applies to the file content. When files are present, the system prompt asks the model to cite issues as path:line-range so you can navigate directly to them.
Examples
- 01 — catching a guardrail violation before it shipped: agent was about to write a persona before deploying the bot. Cept caught that this contradicted the project's own ship-simplest guardrails and surfaced two technical risks (socket-mode on Railway, non-swappable SYSTEM_PROMPT) the agent had missed.
Design rules
- Redact before send. Local secrets must never leave the machine.
- Compress aggressively. The model gets signal, not raw logs.
- Structured output. The agent consumes JSON fields, not prose.
- Bounded. Hard caps on transcript size, lookback, event count.
- Selective. Cept is an escalation tool, not a default tool.
Live progress (--emit) and the floating HUD
cept emits structured progress events at every phase boundary. Adapters consume them; you choose what surface you want.
# default: text events on stderr (stdout stays clean for the JSON result)
cept-cli --goal "..." --dry-run
# JSONL on stderr (machine-readable)
cept-cli --goal "..." --emit jsonl:stderr
# append to a log file
cept-cli --goal "..." --emit file:~/.cept/status.jsonl
# floating HUD panel (see below)
cept-cli --goal "..." --emit hud
# multiple at once: HUD + log
cept-cli --goal "..." --emit hud --emit file:~/.cept/status.jsonl
# silent
cept-cli --goal "..." --quiet
For the MCP server (where stdout is the JSON-RPC channel and must stay clean), set CEPT_EMIT in the server's env:
"env": {
"OPENROUTER_API_KEY": "sk-or-...",
"CEPT_EMIT": "hud,file:~/.cept/status.jsonl"
}
Adapter spec syntax
| Spec | What |
|---|---|
stdout / stderr |
Human-readable text |
jsonl:- / jsonl:stderr |
JSONL to stdout/stderr |
jsonl:PATH / file:PATH |
Append JSONL to file |
socket:PATH |
JSONL to a Unix domain socket (no-op if no listener) |
subprocess:CMD |
Spawn CMD, write JSONL to its stdin |
hud |
Spawn the bundled Swift HUD ($CEPT_HUD_CMD or cept-hud --once in $PATH) |
notify |
macOS notification banners (skips noisy phases) |
noop |
Drop everything |
Event schema (the stable contract)
{
"run_id": "abc123",
"seq": 7,
"ts": "2026-04-27T20:00:00.000Z",
"phase": "asking_model",
"level": "info",
"msg": "asking perplexity/sonar-reasoning",
"data": {"model": "perplexity/sonar-reasoning"}
}
Anyone can write a different consumer (dashboard, Slack bridge, log forwarder) by reading JSONL with this schema.
The HUD just works
--emit hud auto-builds the Swift binary on first use and caches it at ~/.cache/cept/cept-hud (or $XDG_CACHE_HOME/cept/cept-hud). First call costs ~5-10 seconds; subsequent calls are instant. You don't need to know Swift exists.
# First time: cept builds cept-hud, then runs.
cept-cli --goal "..." --emit hud
# Every time after: cache hit, no build, panel pops up.
Resolution order:
$CEPT_HUD_CMD— full command override (e.g. for testing builds)$CEPT_HUD_BIN— path to a binary you already havecept-hudon$PATH~/.cache/cept/cept-hud— auto-built and cached- Build from
hud/Package.swiftnext to the cept package — first-call cost
If none work (no Swift toolchain, source missing) --emit hud falls back to noop with a clear stderr message.
Want to pre-build (e.g. in CI) or refresh the cache?
cept-hud-install # build if missing
cept-hud-install --force # rebuild
cept-hud-install --path # print resolved binary path
The HUD itself is a translucent NSPanel (Swift 5, macOS 13+, top-right of the active screen, click-through, no Dock icon). Source lives in hud/.
Layers
┌─────────────────────────────────────────┐
│ Layer 2 — external steering (OpenRouter)│
└─────────────────────────────────────────┘
▲
┌─────────────────────────────────────────┐
│ Layer 1 — local introspection │
│ locator → distiller → redactor → packet│
└─────────────────────────────────────────┘
│
▼ (events fan out)
┌─────────────────────────────────────────┐
│ Adapters — stdout / file / socket / │
│ HUD / notify / noop │
└─────────────────────────────────────────┘
Layer 1 is independently useful and testable. Layer 2 is the consultation. Adapters are the surface — swap them without touching the pipeline.
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 cept-0.2.0.tar.gz.
File metadata
- Download URL: cept-0.2.0.tar.gz
- Upload date:
- Size: 44.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
971077f0868df8ea5f3f1a796919565e118416602ae30acd5caeea615e4b4326
|
|
| MD5 |
3ebe1f882ca34b1e20260de3fe22ded5
|
|
| BLAKE2b-256 |
7ac879e790e4e700e3ec525149b24b3ee391a46d0d887711993005a59e076593
|
Provenance
The following attestation bundles were made for cept-0.2.0.tar.gz:
Publisher:
publish.yml on eidos-agi/cept
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cept-0.2.0.tar.gz -
Subject digest:
971077f0868df8ea5f3f1a796919565e118416602ae30acd5caeea615e4b4326 - Sigstore transparency entry: 1404023779
- Sigstore integration time:
-
Permalink:
eidos-agi/cept@6c7a1d8930ec2c3e26bcffa77bb9e5cce6a6aa8c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/eidos-agi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c7a1d8930ec2c3e26bcffa77bb9e5cce6a6aa8c -
Trigger Event:
push
-
Statement type:
File details
Details for the file cept-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cept-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f864d010bffeb1b822eebf8f5421e62ff45732df1c7551579b0a44eb7bba403a
|
|
| MD5 |
4e4bba3a0882f4eb8015eeb116852306
|
|
| BLAKE2b-256 |
adeb275f61f4c07a290b7f93e8c5fd8b9bc273f739f48bcb8e8d50d6289ed259
|
Provenance
The following attestation bundles were made for cept-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on eidos-agi/cept
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cept-0.2.0-py3-none-any.whl -
Subject digest:
f864d010bffeb1b822eebf8f5421e62ff45732df1c7551579b0a44eb7bba403a - Sigstore transparency entry: 1404023970
- Sigstore integration time:
-
Permalink:
eidos-agi/cept@6c7a1d8930ec2c3e26bcffa77bb9e5cce6a6aa8c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/eidos-agi
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c7a1d8930ec2c3e26bcffa77bb9e5cce6a6aa8c -
Trigger Event:
push
-
Statement type: