AgentRecall 2.2.1
See the version list below for details.
dotnet tool install --global AgentRecall --version 2.2.1
dotnet new tool-manifest
dotnet tool install --local AgentRecall --version 2.2.1
#tool dotnet:?package=AgentRecall&version=2.2.1
nuke :add-package AgentRecall --version 2.2.1
AgentRecall
A local-first memory for AI coding agents. AgentRecall captures the feedback and failures you run into while coding, turns them into reusable technical rules, and serves those rules back — on the command line or directly to Claude Code over MCP. Everything stays on your machine: no cloud sync, no web UI, no API keys.
What it does
AgentRecall is more than a notes file — it actively manages a body of technical knowledge so the right guidance reaches your agent at the right moment:
- Turns feedback into structured rules. Each correction is parsed into a
readable
trigger,rule,do,do_not,reason, andapplies_to, then validated for quality — not stored as a raw note. - Ranks what to surface. Retrieval scores rules by keyword + semantic +
domain + task-type + scope match, weighted by confidence, and returns them
bucketed into must-follow, suggested, and warnings within a token
budget — not a flat keyword dump. Here "semantic" means a deterministic,
built-in concept graph (e.g. refund relates to money), not vector
embeddings — ranking is keyword + concept based out of the box. Embedding-based
similarity is an optional extension point that stays off until an
IEmbeddingProvideris configured (the default contributes nothing); see Search ranking. - Applies universal constraints every turn. Style, tone, and quality rules that belong on every task (e.g. "don't leave unnecessary comments") are captured as standing rules and injected regardless of keyword relevance — capped and prominent so they reach the model when it matters, not lost below the relevance floor; see Standing Rules.
- Resolves conflicts automatically. When rules disagree ("use the repository pattern" vs "do not"), the policy engine picks the effective one by scope, explicit supersede, priority, recency, then confidence.
- Learns from failures. Build, test, and lint logs feed back in; repeatedly hitting the same problem raises a rule's confidence and can auto-promote the rule that prevents it.
- Compresses its own memory. Duplicate, near-duplicate, and overlapping rules are detected and merged into one canonical rule, with the originals kept as an audit trail.
- Measures retrieval quality. A bundled evaluation reports Precision@1/@3 and Recall@5 and fails CI on a regression, so recall stays trustworthy as the rule set grows.
- Injects deterministically. A gated Claude Code hook prepends the relevant rules to the model's context on every matching prompt — not just when the model remembers to ask.
Everything below is the detail behind each of these.
Install
AgentRecall is a .NET global tool. You need the .NET SDK (8 or newer) installed, then:
dotnet tool install -g AgentRecall
This installs agentrecall into the .NET tools directory (~/.dotnet/tools on
macOS/Linux, %USERPROFILE%\.dotnet\tools on Windows). Verify it:
agentrecall --version
PATH is handled for you. A .NET global tool has no post-install step, so a
fresh install often isn't on PATH in your current shell — which is why
agentrecall can report "command not found" right after a successful install.
The first time AgentRecall runs it adds its directory to your PATH permanently
(your shell profile on macOS/Linux, the user PATH on Windows) and prints a
one-time notice; open a new terminal and it's found automatically. If the very
first invocation is the one that can't be found, either open a new terminal, or
run it once by full path / via the bootstrap below.
Bootstrap install (installs and fixes PATH in one step):
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/AkbarDizaji/AgentRecall/main/scripts/install.sh | bash
# Windows (PowerShell)
iwr -useb https://raw.githubusercontent.com/AkbarDizaji/AgentRecall/main/scripts/install.ps1 | iex
You can also fix PATH any time with agentrecall setup.
Update or remove it later with:
dotnet tool update -g AgentRecall
dotnet tool uninstall -g AgentRecall
Dev containers
A global tool installs under ~/.dotnet/tools, which lives on the container
filesystem and is wiped by "Rebuild Container" — so a manual dotnet tool install
disappears on every rebuild. Run this once from your project root to make the
install survive rebuilds:
agentrecall devcontainer init
It always wires the UserPromptSubmit hook into .claude/settings.json (see
Guarantee it with a hook),
so relevant rules are injected automatically on every prompt rather than only
when the agent decides to call an MCP tool, and appends the CLAUDE.md
guidance block so the agent recalls rules and captures accepted PR comments as
Active rules by default. These work in any environment, with or without a dev
container. Existing settings, CLAUDE.md content, and JSONC are merged or left
untouched — never overwritten — and a re-run is a no-op.
The dev-container scaffolding is separate and opt-in:
- If you already have a
devcontainer.json, it's wired into automatically: the.devcontainer/agentrecall-post-create.shscript is written and the exact keys to merge in are printed — your JSONC is left untouched, never rewritten. - If you have none, none is created. AgentRecall doesn't hand your project a dev container it didn't ask for; it prints the one command to generate one when you want it:
agentrecall devcontainer init --create
--create writes .devcontainer/devcontainer.json and the
agentrecall-post-create.sh script, which reinstalls AgentRecall from NuGet, persists
the database on a named Docker volume, and re-registers the MCP server on every
create/rebuild. The script logs each step and, on failure, names the command that
aborted it.
PATH note. A global tool lives in
~/.dotnet/tools, and VS Code often opens integrated terminals as non-login shells that don't read~/.profile, soagentrecallcan be installed yet "not found". The generated manifest adds the directory viaremoteEnv, and the setup script also appends it to~/.bashrc; for an existingdevcontainer.json, add"remoteEnv": { "PATH": "${containerEnv:PATH}:/home/vscode/.dotnet/tools" }.
Quick start
# 1. Create the local database (~/.agentrecall/agentrecall.db by default)
agentrecall init
# 2. Teach it something you learned
agentrecall feedback add \
--task "writing a SQL query" \
--feedback "use parameterized queries to avoid injection" \
--tags "sql,security"
# 3. Recall it later
agentrecall search "sql injection"
That's the whole loop: capture → recall. Everything below is detail on each step.
Using AgentRecall
Capture feedback
Record a lesson learned. --task and --feedback are required; the rest add
context and improve later matching:
agentrecall feedback add \
--task "writing a SQL query for user lookup" \
--feedback "use parameterized queries to avoid injection" \
--bad-output "string-concatenated SQL" \
--fixed-output "a parameterized command with @name" \
--scope-level Repository --scope-value my-repo \
--tags "security,sql"
Each entry is stored as an event and turned into a rule you can search for.
Search and read rules
agentrecall search "sql injection" # ranked keyword search
agentrecall search "sql" --scope-value my-repo --limit 5
agentrecall rules list # everything you've captured
agentrecall rules show 1 # full detail for one rule
Results are ranked by relevance, rule status, and confidence.
Search ranking
Ranking is keyword-based out of the box — there is no semantic vector search
unless you wire one up. search scores rules by weighted term matches across the
trigger, tags, rule text, mistake, and technical context, blended with status and
confidence. Context injection adds a deterministic, built-in concept graph so a
task about refunds can surface a money rule with no shared words; this is rule-based
relatedness, not embeddings.
The pipeline has an IEmbeddingProvider extension point for hybrid (keyword + vector)
search, but the only provider shipped is a no-op (IsAvailable == false), so no
embeddings are computed and no external service is contacted by default. Until a real
provider is configured, semantic similarity contributes nothing to the score — ranking
stays keyword + concept based and fully local and deterministic.
Tags are a comma-separated string, not a normalized table. Tag matching therefore happens in memory (search tokenizes the field), with no indexed/SQL-level tag filter. This is fine for the local-first, single-user corpus sizes AgentRecall targets; a tag join table would be the change to make if tag querying ever needs to scale.
Curate rules
Captured rules are Active by default, so they apply immediately. Promote the best, retire the rest:
agentrecall rules promote 1 # Active → Promoted
agentrecall rules approve 1 # Pending → Active (if captured as pending)
agentrecall rules supersede 1 2 # replace rule 1 with rule 2
agentrecall rules archive 3 # hide a rule from search
A rule's lifecycle states (the RuleStatus enum) are Draft, Pending, Active,
Promoted, Superseded, Retired, and Archived. The usual flow is
Pending → Active → Promoted. Active and Promoted are the in-force states —
the only ones the policy engine applies and context injection can mark must-follow.
Superseded, Retired, and Archived are dead: they never show up in search and
are never reused when deduplicating. Draft and Pending are still searchable but are
not auto-applied until approved.
Prefer to review before a rule counts? Capture it as pending with
agentrecall feedback add … --pending, or make pending the default for every
capture by setting AutoApproveFeedback to false (see Configuration).
Learn from failures
Point AgentRecall at a build, test, or lint log. Each failure is recorded, and failures that match an existing rule increase its confidence — repeatedly hitting the same problem can automatically promote the rule that prevents it:
agentrecall import build-log ./build.log
agentrecall import test-log ./test.log
agentrecall import lint-log ./lint.log
Learn from PR review comments
Reviewer comments are feedback too. Point AgentRecall at a comments file — JSON
from the GitHub CLI or plain text — and each comment that reads as a reusable
correction becomes a pending rule (tagged pr-review); praise, questions and
nits are skipped:
gh pr view 42 --json comments > comments.json
agentrecall import pr-comments ./comments.json --task "PR #42: add refunds" --scope-value my-repo
Then review what it captured with agentrecall rules list and approve the keepers.
If the comments have already been accepted — you acted on them, so they're not
guesses to vet — add --accepted and they're recorded as Active rules
straight away (the MCP tool takes the same accepted: true). The scaffolded
CLAUDE.md guidance tells the agent to do this on its own: when you ask it to
apply what a review comment says, it captures that comment as an Active rule
without being asked.
Evaluate retrieval quality
Retrieval is only useful if the right rule comes back for a task. eval retrieval
runs a bundled dataset of rules and query scenarios through the ranker in a
throwaway store (your real database is never touched) and reports Precision@1,
Precision@3, and Recall@5:
agentrecall eval retrieval # bundled dataset
agentrecall eval retrieval --dataset ./my-eval.json
It exits non-zero when any metric falls below the dataset's baseline, so CI fails on a retrieval regression. The same check also runs as a unit test.
Command reference
| Command | Description |
|---|---|
init |
Create the local data directory and database. |
setup |
Ensure the .NET tools directory is on your PATH (runs automatically on first use). |
devcontainer init |
Wire AgentRecall's hooks + CLAUDE.md guidance; pass --create to also scaffold a dev container that reinstalls it on every rebuild (optional [path]). |
feedback add |
Record feedback and extract a rule from it. |
search "<query>" |
Search rules by keyword (--scope-level, --scope-value, --limit). |
rules list |
List all rules (--status <status>, e.g. Pending). |
rules show <id> |
Show a single rule in detail. |
rules approve <id> |
Move a Pending rule to Active. |
rules promote <id> |
Promote a rule. |
rules supersede <oldId> <newId> |
Replace one rule with another (versioned). |
rules archive <id> |
Archive a rule (excluded from search). |
import build-log <file> |
Ingest build failures. |
import test-log <file> |
Ingest test failures. |
import lint-log <file> |
Ingest lint failures. |
import pr-comments <file> |
Capture PR review comments as rules (--task, --scope-level, --scope-value, --tags; --accepted records them Active instead of pending). |
inject-context "<task>" |
Build agent-ready context (must-follow, warnings, preferred/anti-patterns) for a task. |
dna |
Summarise the repo's engineering personality for onboarding (--markdown, --json, --top <n>, --scope-level, --scope-value, --output <file>). |
eval retrieval |
Evaluate retrieval quality against the bundled dataset (--dataset <path>); non-zero exit below baseline. |
activity last |
Show the latest AgentRecall activity notice (--json). |
activity list |
Show recent activity notices, newest first (--limit <n>, --json). |
turn-summary --last |
Show the aggregated Turn Memory Summary for the last turn (--json, --detailed, --compact). |
seed list |
List built-in seed packs and whether they are installed (--json). |
seed show <pack> |
Show a seed pack's rules, defaults, and provenance (--json). |
seed install <pack> |
Install a seed pack; rules are Active by default (--active spells that out, --suggested installs them as Pending for manual approval, --force, --json). |
seed remove <pack> |
Remove an installed seed pack (--force, --json). |
seed status |
Show installed seed packs and rule counts by status (--json). |
career impact --last |
Show the last turn's career-impact candidate (--json, --detailed). |
career journal --last |
Generate a promotion-ready journal entry on demand (--json, --file <path>). |
career status |
Show the career-impact pack/mode and the last candidate (--json). |
cleanup pending-noise |
Find and archive noisy Pending rules the Stop hook created before source/outcome-aware capture (dry run by default; --apply, --json, --tag <tag>, --status <status>). |
mcp |
Run the MCP server over stdio (for Claude Code). |
status |
Show where data is stored. |
help / --help / -h |
Show usage. |
version / --version / -v |
Show the installed version. |
Run agentrecall help any time for the same list.
User Preferences
AgentRecall remembers explicit user preferences separately from repository
conventions and engineering lessons. When you state a durable preference for how the
assistant should behave, it is captured as a UserPreference (or, for communication
style, a CommunicationPreference) — not a repository coding rule.
Examples of preferences it recognises (English and Persian):
- preferred explanation style — "answer short and simple, add an example if needed"
- language — "reply in Persian unless I ask in English" / "فارسی جواب بده مگر اینکه انگلیسی پرسیدم"
- prompt format — "when I ask for a prompt, give it directly with tests and edge cases"
- interaction style — "don't ask me too many questions; make a reasonable assumption"
- verbosity — concise vs. detailed answers
How preferences differ from other memory:
- Higher confidence. An explicit preference is your own word, so it is captured with high confidence (≈0.9) and auto-captured when safe — not parked as a low-confidence pending suggestion.
- User scope, not repository scope. A communication preference applies to you
everywhere, so it is stored at user (global) scope. Technical repository conventions
still use
Repositoryscope; engineering lessons still use their own scope. - The phrasing is normalized. Raw wording ("always answer caveman form") is rewritten into durable, bounded guidance ("Answer briefly and simply first, and prefer concrete examples; provide more detail when asked.") — overbroad absolutes and throwaway wording are dropped, your intent is kept.
- Unsafe preferences are refused. A preference that conflicts with correctness or honesty ("always agree even if I'm wrong") is skipped, never stored.
- Conflicts are surfaced. A newer preference about the same dimension (e.g. answer
length) as an older one raises a
Supersederecommendation rather than silently keeping both.
Record one explicitly:
agentrecall feedback add \
--task "how to answer me" \
--feedback "From now on, answer short and simple, add examples only if needed."
# → captured a CommunicationPreference at user scope with high confidence
agentrecall rules explain <id> # shows Type: CommunicationPreference, Captured because:
# ExplicitUserPreference, Evidence, Scope, Confidence
Preferences are recalled alongside other rules when relevant, and captured preferences appear in the activity log and Turn Memory Summary ("🧠 AgentRecall: captured 1 user preference.").
Standing Rules
Most rules are contextual: AgentRecall retrieves them by relevance to the task in front of you (keyword, semantic, changed-code, and task-type match, weighted by confidence). That works well for "scope validators to the tenant" — but it structurally misses universal constraints like "don't leave unnecessary comments" or "always run the formatter". A universal rule shares no keyword with "add a date parser", so it scores below the relevance floor and never gets injected — exactly on the turn it should apply.
A standing rule solves this. It carries the AlwaysApply flag, which makes AgentRecall
deliver it on every task rather than only when it matches — bypassing the relevance
floor and surfacing as must-follow guidance (prohibitions surface as warnings).
AlwaysApply vs. ScopeLevel
These are orthogonal — a rule has both:
ScopeLevelanswers where a rule is true —Global,Language,Repository, a path. It bounds which projects/files a rule belongs to and drives ranking specificity.AlwaysApplyanswers how a rule is delivered — as a standing constraint injected every turn, or as a contextual rule retrieved by relevance.
A standing rule is typically Global scope (it applies everywhere), but the two are
independent: AlwaysApply is a delivery property, not a new scope level.
How a rule becomes standing
- Preferences are standing by default. A
UserPreferenceorCommunicationPreferenceapplies to you everywhere, so it is captured as standing automatically — no flag needed. (See User Preferences.) - The capture judge flags a universal constraint. When a correction is a style, tone,
process, or quality rule that applies to every task, the judge marks it
always_applyand it is captured standing. - The repeated-correction backstop promotes it. If you keep making the same correction, AgentRecall promotes that rule to standing on its own — you should not have to repeat yourself to make a lesson stick.
Standing rules are marked [standing] in the Turn Memory Summary and
[standing — applies every turn] on capture, so you can see when a correction became one.
The band is capped at 5
Always-injecting is only useful if it stays few and prominent — a rule that appears on
every turn becomes wallpaper the model stops reading, which is the same failure as a bloated
CLAUDE.md. So the standing band is capped at the 5 highest-confidence standing rules per
prompt. Any beyond the cap fall back to ordinary relevance gating (they are not dropped —
they simply compete on relevance like everything else). Keep the standing set small and
deliberate.
Project and contextual rules are unaffected
Non-standing rules work exactly as before: they are retrieved by relevance, respect their
ScopeLevel, and never force their way into a turn they do not match. Standing rules take
their reserved slots first; the rest of the token budget is filled by the usual
relevance-ranked rules. A project-scoped convention still applies only in its repository.
Good vs. overbroad standing rules
Standing rules earn their always-on cost by being universal and actionable. Good ones:
- "Keep comments minimal and purposeful; don't restate what the code already says."
- "Run the formatter before finishing a change."
- "Answer briefly; add examples only when they help." (a preference — standing by default)
- "Prefer descriptive names over abbreviations."
Bad standing rules are contextual guidance masquerading as universal — they should be ordinary relevance-gated rules, not always-on:
- "Use
Moneyvalue objects in the billing service." → contextual: only when touching billing. Let relevance surface it. - "In
PaymentController, validate the tenant before charging." → project/path-specific: scope it to the repository, don't make it standing. - "Always write the most performant code possible." → vague and unactionable; it adds noise to every turn without changing behaviour.
- "Never use inheritance." → an overbroad absolute; the useful, non-standing form is "prefer composition over inheritance when designing class hierarchies."
Rule of thumb: if it only applies to some tasks, files, or projects, keep it contextual and let ranking do its job. Reserve standing for the handful of constraints that genuinely apply to every turn.
Project DNA
Project DNA distils everything AgentRecall has learned about a repository into a single, onboarding-ready summary of its engineering personality — the conventions, patterns, risks, and recurring lessons that explain how the project actually works. The goal is to get a new developer (or a new AI coding agent) productive in under five minutes.
It reads the same local data the rest of AgentRecall uses — active and promoted rules, how often each rule is retrieved, mined lesson candidates, accepted lifecycle recommendations, outcomes, and detected conflicts — and organises it into fixed sections:
- Core Principles — the highest-confidence, most broadly applicable lessons.
- Repository Conventions — repo-specific "when X, do Y" rules.
- Testing Patterns — testing rules and common testing mistakes.
- Architecture Patterns — architecture and design preferences.
- Error Handling — exceptions,
Result<T>, validation, domain failures. - Feature Gates / Authorization / Security — gates, permissions, auth, access control.
- Common Mistakes — frequently corrected or mined lessons.
- Agent Warnings — high-impact anti-patterns to avoid.
- Stale or Risky Knowledge — low-confidence or conflict-prone guidance to review.
How it differs from search and reports
Search answers "which rules match this task?" and the report commands give you
metrics (counts, growth, staleness). Project DNA is neither: it's a curated,
deterministic narrative of the whole corpus, ranked so the most trustworthy and
broadly useful guidance rises to the top — Promoted over Active over Pending, then by
confidence, retrieval frequency, recency, outcome evidence, and lifecycle signals.
There are no LLM calls, no embeddings, and no external services — the same inputs
always produce the same output.
Generate it
agentrecall dna # human-readable summary
agentrecall dna --markdown # Markdown for onboarding docs
agentrecall dna --json # stable, structured JSON
agentrecall dna --top 10 # more items per section
agentrecall dna --scope-level Repository --scope-value my-repo # one repo only
agentrecall dna --markdown --output PROJECT_DNA.md # write straight to a file
The Markdown output is designed to drop straight into a PROJECT_DNA.md,
CONTRIBUTING.md, or a wiki page. The JSON output is stable (snake_case keys —
generated_at, scope, sections, items, rule_ids, confidence, evidence,
category, source_counts) so you can feed it to other tooling.
Use it for onboarding
- Humans: run
agentrecall dna --markdown --output PROJECT_DNA.md, commit it, and point new contributors at it. Regenerate it whenever the rule corpus changes. - Agents: paste
agentrecall dna(or the Markdown) into an agent's context at the start of a session so it inherits the project's conventions before writing any code.
Seed Packs
Seed packs provide optional starter engineering memories — curated, conditional rules that give an agent useful instincts before a project has accumulated enough learned memory of its own. They are not project facts, they are opt-in at the pack level, and they can be removed at any time. AgentRecall never installs a seed pack automatically.
The first built-in pack is tidy-first: ten paraphrased, practical rules for separating
behaviour-preserving cleanup from behaviour change (guard clauses, naming, extraction,
scoping a tidy, and so on).
agentrecall seed list # what packs exist, and whether they're installed
agentrecall seed show tidy-first # rules, defaults, and provenance
agentrecall seed install tidy-first # install as Active (the default)
agentrecall seed install tidy-first --active # same as the default, spelled out
agentrecall seed install tidy-first --suggested # conservative mode: install as Pending for manual approval
agentrecall seed status # installed packs and rule counts by status
agentrecall seed remove tidy-first # archive the pack's rules
How seed rules behave:
- Active by default. Installing a pack is the opt-in, so its rules are in force from day
one at a moderate confidence (~0.65). Prefer manual approval? Pass
--suggestedto install them as Pending/Suggested and approve individual rules withagentrecall rules approve <id>. - Marked as seed-derived (source
BuiltInSeed, taggedseedand the pack name) so you can always tell starter guidance from learned memory. Used seed rules show a[seed]marker in the Turn Memory Summary. - Ranked below learned rules. Project-specific
RepositoryConvention/EngineeringLessonrules and explicit user corrections override seed rules: they outrank seed rules in retrieval and win conflicts against them, and a seed rule is never injected as "must-follow". Seed injection is capped (at most two per prompt) unless the task is itself about tidying or refactoring, so starter guidance never floods the context. - Not absolute. Apply a seed rule only when its condition matches. If a seed rule is not applicable in this project, reject or archive it: rejections, corrections, or archiving lower its confidence and can trigger an archive/suppress lifecycle recommendation — exactly like any learned rule. Repeated uneventful use raises confidence a small, capped amount; explicit acceptance raises it more.
- Idempotent and safe. Installing a pack only ever adds seed rules — it never edits or
deletes rules you already have. Your own (learned) rules are matched by source and left
completely untouched, so installing on top of an existing corpus is safe; if a seed rule
happens to overlap one of yours, the two coexist and your rule wins (seed rules rank lower).
Installing the same pack twice creates no duplicates, an install never overwrites a seed rule
you edited, and removing a pack archives only that pack's rules (preserving ones you edited or
promoted). A reinstall will not resurrect a removed rule unless you pass
--force.
The tidy-first pack is paraphrased practical guidance inspired by common tidying and
refactoring practices. It contains no copied or quoted book text.
Career Impact Pack
The career-impact seed pack helps you avoid losing promotion-worthy engineering work. It
identifies Staff-level impact, evidence, metrics, stakeholders, ADRs, leadership behaviors,
and promotion-ready achievements — as coaching guidance, not project facts.
It is opt-in and installs active starter guidance (ten conditional rules), exactly like any other seed pack:
agentrecall seed install career-impact # install the pack (Active starter guidance)
agentrecall seed show career-impact # its rules, defaults, and provenance
agentrecall career impact --last # the last turn's career-impact candidate
agentrecall career impact --last --json # deterministic JSON for the candidate
agentrecall career journal --last # a promotion-ready journal entry (on demand)
agentrecall career journal --last --file ./career-journal.md # append it to a Markdown file
agentrecall career status # pack/mode and the last candidate
It is low-token by design, using a two-stage approach:
- A cheap, deterministic end-of-turn detector (no LLM calls, no embeddings, no network) analyzes the turn's text with keyword/heuristic signals and decides whether the work was significant.
- The full impact/journal detail is generated only on demand (the
careercommands) or when a significant candidate already exists.
How it behaves:
SignificantOnlyby default. When the pack is installed, the detector runs at the end of a turn and prints a compact summary only for significant work. For trivial turns (typo fixes, renames, formatting) it says nothing. SetAgentRecall.CareerImpactModetoSilent(never auto-run) orAlways(surface lower-confidence candidates too, still bounded).- Never spams. The automatic summary is at most five bullets plus a pointer; it never prints a full promotion packet. The full journal is generated only when you ask.
- Human-visible only. The automatic summary is not fed back into model context; the Turn Memory Summary carries only a short one-line pointer.
- Retrieval is capped. Career-impact rules are ordinary seed rules — ranked below learned rules and capped so they never flood a prompt.
Configuration:
AgentRecall.CareerImpactMode = Silent | SignificantOnly | Always(defaultSignificantOnly)AgentRecall.CareerImpactSummaryLevel = Compact | Detailed(defaultCompact)
Example compact summary (printed automatically for significant work):
🧠 **AgentRecall Career Impact:** possible Staff-level impact detected.
- Why it matters: This work involves a migration; it targets performance.
- Evidence: PR, before/after metrics, dashboard, design doc
- Metrics: latency, error rate, adoption
- Stakeholders: Platform, Product
- ADR: probably yes
Run `agentrecall career journal --last` for a promotion-ready entry.
The career-impact pack is original, paraphrased career-impact coaching guidance. It copies
no book, article, or third-party text.
Use it from Claude Code
AgentRecall ships an MCP server so Claude Code can recall your rules and record new feedback while you work. Register it once:
claude mcp add agentrecall agentrecall mcp
That exposes these tools to the agent:
| Tool | What it does |
|---|---|
inject_context |
Given a task (and optionally its type, scope, files, changed entities), return the most useful rules — ranked by relevance and confidence, conflict-resolved, and bucketed into must-follow, suggested, and warnings, each with an explanation. |
resolve_rules |
When several rules match a task, decide which to follow and which to ignore — resolving direct conflicts and superseded rules. |
compress_memory |
Find duplicate, near-duplicate, and overlapping rules and merge each group into one canonical rule (dry run by default; originals are preserved for audit). |
get_relevant_context |
Given a task, return the rules to know before starting (keyword-based). |
get_project_rules |
The rules that always apply here (project → promoted → active). |
get_reminders |
A short checklist of high-signal reminders for a kind of work. |
capture_status |
Report the last turn-finalization result (captured/suggested/skipped rule ids, source, timestamp). Call it to answer "did AgentRecall capture anything?" instead of guessing — equivalent to agentrecall finalize-turn status. |
search_rules |
Find rules relevant to a query. |
suggest_feedback_candidate |
Detect whether a message is a reusable correction. |
capture_feedback |
Save a correction in one step (creates an Active rule by default; pending=true to require approval). |
add_feedback |
Record feedback with full task/scope context. |
import_pr_comments |
Capture PR review comments as pending rules (skips praise/questions/nits). |
Each rule comes back as guidance shaped for an agent: trigger, rule, do,
do_not, reason, applies_to, confidence, and status. The server speaks
JSON-RPC 2.0 over stdio and writes only protocol messages to stdout (all logs go
to stderr), so it behaves identically whether installed or run from source.
A typical agent loop: call inject_context before working (it ranks rules and
flags must-follow guidance and warnings), then run suggest_feedback_candidate
on user corrections and capture_feedback to remember the good ones — no manual
command needed.
Make the agent use it automatically
Registering the server only makes the tools available — the agent still has to
choose to call them. To make recall part of every task, add a short instruction
to your project's CLAUDE.md (or AGENTS.md). Drop this in:
## Memory (AgentRecall)
The `agentrecall` MCP server holds rules learned from past feedback. Use it:
- **Before** coding, refactoring, reviewing, or debugging, call `inject_context`
with the task description (and `task_type`, `scope_value`, `file_names`,
`changed_entities` when known). Follow every **must-follow** rule and heed the
**warnings** before writing code.
- **When the user corrects you**, call `suggest_feedback_candidate`; if it's a
reusable lesson, call `capture_feedback` so the same mistake isn't repeated.
- **After a PR review**, pass the reviewer's comments to `import_pr_comments` so
the actionable ones are remembered as rules.
Because CLAUDE.md is loaded into the agent's context each session, this turns
"recall the relevant rules first" into default behaviour rather than something
you have to ask for.
Guarantee it with a hook (deterministic injection)
A CLAUDE.md instruction only nudges the model — calling an MCP tool is still
its choice. To make rule injection deterministic, wire AgentRecall into a
Claude Code UserPromptSubmit hook.
Hooks are run by Claude Code itself (not the model), so the context is injected
before the model responds, every time the gate matches.
Add this to your project's .claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{ "hooks": [ { "type": "command", "command": "PATH=$HOME/.dotnet/tools:$PATH agentrecall hook user-prompt-submit" } ] }
]
}
}
PATH note. Claude Code runs hooks through a non-login shell that may not have
~/.dotnet/toolsonPATH— so a bareagentrecallcan fail withcommand not found. The command above prepends the .NET global-tools directory so the hook resolves regardless of how Claude Code was launched. If you still hit "command not found", make sure .NET global tools are on the PATH Claude Code sees:export PATH="$HOME/.dotnet/tools:$PATH"For hooks specifically, add that through a Claude Code
SessionStarthook (which Claude Code applies to every session and to the subprocesses it spawns), or launch Claude Code from a shell where the path is already configured.agentrecall devcontainer initwrites the PATH-prefixed form for you, and re-running it upgrades an older bare command in place.
How it works. On each prompt, Claude Code pipes a small JSON payload (the
prompt text and working directory) to agentrecall hook user-prompt-submit. The
command:
- checks the prompt against a keyword gate — non-development prompts are skipped entirely (no cost, no output);
- for a development prompt, retrieves the most relevant rules for the current repository (scope = the repo containing the working directory);
- prints a compact block that Claude Code prepends to the model's context:
## AgentRecall Technical Context
Must Follow:
- ...
Warnings:
- ...
Preferred Patterns:
- ...
Anti Patterns:
- ...
Source Rules:
- #12, #4
Empty sections are omitted, and only Active/Promoted rules are included. The hook never blocks — if AgentRecall errors, it logs to stderr and injects nothing.
Configure it in agentrecall.json (no need to touch settings.json to tune):
| Setting | Default | Purpose |
|---|---|---|
HookEnabled |
true |
Master switch; false makes the hook a no-op. |
HookKeywords |
(see below) | Words/phrases that mark a prompt as dev work. |
HookMaxRules |
5 |
Maximum rules injected (keeps the block small). |
HookIncludePending |
false |
Whether unapproved (Pending) rules may be injected. |
{
"AgentRecall": {
"HookEnabled": true,
"HookMaxRules": 5,
"HookIncludePending": false,
"HookKeywords": [
"implement", "write", "create", "fix", "debug", "refactor", "review",
"test", "unit test", "integration test", "api", "endpoint", "repository",
"service", "controller", "moq", "build", "lint"
]
}
}
Disable it — set
"HookEnabled": false, or remove the hook fromsettings.json.Tune gating — edit
HookKeywords. Single words match whole words ("api" won't fire on "rapid"); multi-word entries match as phrases.Troubleshoot — run it by hand and inspect the output; failures go to stderr:
echo '{"prompt":"Write Moq tests for OrderService","cwd":"'"$PWD"'"}' \ | agentrecall hook user-prompt-submitAn empty result means the gate didn't match, the hook is disabled, or no rules were relevant. Use
claude --debugto see hook execution inside Claude Code.
Verify the flow. With a Moq rule stored and promoted, the prompt "Write Moq tests for OrderService" drives:
User prompt
→ Claude Code fires the UserPromptSubmit hook
→ agentrecall hook user-prompt-submit (gate matches "write", "moq", "test", "service")
→ inject-context retrieves the Moq rule
→ "## AgentRecall Technical Context …" is prepended to the model context
→ Claude responds with the rule already in view
Turn Finalizer
Recall is deterministic through the UserPromptSubmit hook. Capture is decided by a semantic capture judge: after a turn finishes, AgentRecall finalizes it through a single command, and the model — not keyword heuristics — decides what is worth remembering.
Stop hook
→ agentrecall finalize-turn
→ build a bounded judge input (user + assistant text, scope, retrieved rules)
→ the semantic judge returns a strict-JSON verdict
→ validate the verdict (schema, enums, lengths, required fields)
→ map by confidence thresholds (Capture / SuggestCapture / Skip / Reinforce / Supersede)
→ store / suggest / skip / reinforce, then report a structured summary
This makes AgentRecall — not the agent — the owner of the capture decision. The agent should not guess whether the Stop hook captured something, and should not ask "want me to save it?". When you want to know, ask AgentRecall:
agentrecall finalize-turn status # the last finalization result
Semantic Capture Judge
AgentRecall no longer promotes memories from incidental keywords. A word like "validation", "scope", or "auth" appearing in a sentence is never enough to capture anything. Instead the semantic judge decides whether the turn contains memory-worthy content, and the system only validates the judge's structured output and persists it.
- Explicit save requests are respected. If the user asks to save/capture/remember a rule, it is saved — even when it is narrow, project-local, stylistic, or a preference. The rule is normalized into clean, bounded form; specific is fine.
- Explicit do-not-save requests are respected. Nothing is stored, active or pending.
- Model-discovered lessons are judged for durable value. Reviewer corrections, observed agent failures, repeated mistakes, confirmed conventions, and stated preferences are strong signals. Rules may be narrow — they do not have to be universal.
- Source docs and tool/skill instructions are not saved merely because they were read. A documentation-backed correction (a documented instruction the agent failed to apply and was corrected on) can be captured.
- Uncertain decisions become suggestions. Confidence
≥ 0.80captures,0.55–0.79suggests a Pending rule, and below0.55skips. An explicit save always captures. - Duplicates are reinforced, not duplicated, and an explicit supersede replaces the prior rule.
- If the semantic judge is unavailable, automatic capture is skipped for the turn. AgentRecall never falls back to keyword capture.
AgentRecall.CaptureJudgeMode selects the behavior: Semantic (default) or Off
(disable automatic Stop-hook capture entirely). Manual agentrecall feedback add
continues to work through the existing explicit flow.
The verdict is supplied by the host on the finalize-turn payload as a judgment
object (AgentRecall itself makes no network or LLM calls). Its strict-JSON shape is:
{
"decision": "Capture | SuggestCapture | Skip | ReinforceExisting | SupersedeExisting",
"memory_type": "EngineeringLesson | RepositoryConvention | UserPreference | CommunicationPreference | DocBackedCorrection | ToolWorkflowConvention | ReviewLesson | CodeFact | NotMemory",
"confidence": 0.0,
"capture_reason": "ExplicitUserSave | ExplicitUserDoNotSave | ObservedAgentFailure | ReviewerCorrection | UserCorrection | RepositoryConvention | UserPreference | RepeatedMistake | DocBackedCorrection | DuplicateExisting | AssistantProse | SourceDocumentOnly | CommandOutputOnly | LogOutputOnly | CodeFact | NotReusable | Ambiguous | NotMemory",
"target_existing_rule_id": null,
"normalized_rule": { "title": "", "condition": "", "action": "", "avoid": "", "because": "", "scope": "", "tags": [] },
"evidence": "", "why_not_saved": "", "dedupe_notes": ""
}
capture-status and turn-summary report the decision source, decision, reason, and
confidence, so the agent answers "did AgentRecall capture anything?" from the recorded
verdict — never a guess.
Use it. finalize-turn reads a Claude Code Stop-hook payload on stdin
(tolerant of missing fields — cwd, prompt, assistant_response,
transcript/transcript_path, source):
agentrecall finalize-turn < payload.json # human-readable summary
agentrecall finalize-turn --json < payload.json # structured result
agentrecall finalize-turn status # show the last finalization
agentrecall finalize-turn status --json
agentrecall capture-status --last-turn # alias for status
A captured turn reads:
AgentRecall finalized turn.
Decision source: Semantic capture judge
Decision: Capture (reason: ReviewerCorrection, confidence: 0.86)
Captured:
- #14 Repository rule: When emitting validator messages, apply the same tenant scope…
When nothing reusable was said, it prints No lessons found.
It never blocks Claude Code. The command always exits 0, logs errors to stderr, makes no network or LLM calls, and is deterministic. Malformed input mutates nothing.
Duplicates are avoided. Capture is deduplicated against existing rules (same normalized guidance and scope), so:
- if a lesson was already captured this turn (a manual
capture_feedback, or an earlier candidate), the finalizer records a duplicate skip instead of a second rule; - re-running the finalizer on the same turn is idempotent — it returns the prior result and creates nothing new.
Privacy. The raw transcript is not stored by default — only a content hash,
the resulting rule ids, and skip reasons. Set StoreTurnTranscript: true to keep
the transcript for debugging.
Wiring. agentrecall devcontainer init registers the finalizer as the Stop hook
and upgrades an older agentrecall hook capture registration in place:
{
"hooks": {
"Stop": [
{ "hooks": [ { "type": "command", "command": "PATH=$HOME/.dotnet/tools:$PATH agentrecall finalize-turn --hook" } ] }
]
}
}
Stop-hook limitation. Finalization is only automatic when the Stop hook is installed. The hook payload Claude Code provides may not include the full prompt and assistant response inline; the finalizer falls back to the referenced
transcript_pathwhen available, and to whatever fields are present otherwise. The command always works when run manually, regardless of the hook.
Configure it in agentrecall.json:
| Setting | Default | Purpose |
|---|---|---|
TurnFinalizerEnabled |
true |
Master switch; false makes finalization a no-op. |
StoreTurnTranscript |
false |
Persist the raw transcript with each finalization. |
MaxCandidatesPerTurn |
5 |
Maximum lessons captured from one turn. |
MaxCandidateCharacters |
1000 |
Per-candidate length cap (bounds a huge turn). |
CaptureAutoConfidence |
0.5 |
Minimum confidence to auto-capture without explicit acceptance. |
FinalizerShowUserNotice |
true |
Emit a Stop-hook systemMessage notice after a turn. |
SuppressDuplicateNotices |
true |
Stay silent when only a duplicate was reinforced. |
Asking AgentRecall what it did (don't guess)
AgentRecall records every run and every capture decision, so its state is always queryable — an agent should never answer a question about it by speculating or by reasoning from whether it personally called a tool. Use the command that matches the question:
| User asks | Command to run |
|---|---|
| Did you save anything? / Was anything captured? / Any lesson for AgentRecall? | agentrecall capture-status --last-turn |
| Did AgentRecall run? / What did AgentRecall do? / What rules were fetched? | agentrecall activity last |
| Did the Stop hook capture anything? | agentrecall finalize-turn status |
The generated CLAUDE.md encodes this as a behavior contract (agentrecall devcontainer init writes it, and refreshes an older block in place). The agent must
check status first, report the actual recorded result, and only offer manual capture
when status shows nothing was captured and the user explicitly asks to save.
Troubleshooting: "the Stop hook may have captured it"
If the agent answers a capture question by guessing — "the Stop hook may have captured it", "I didn't manually call AgentRecall", "I don't control whether it fired", or "want me to save it?" — it is not consulting the recorded decision. Fix it deterministically:
Ask AgentRecall, don't guess. The answer is one command away:
agentrecall finalize-turn status # or: agentrecall capture-status --last-turnIt reports the last finalization — captured rule ids, suggested/pending ids, skipped reasons, and duplicates — or
No finalized AgentRecall capture is recorded for the last turn.Verify the Stop hook is installed.
.claude/settings.jsonshould contain aStophook running…agentrecall finalize-turn --hook. Re-runagentrecall devcontainer initto install or upgrade it (it replaces an olderagentrecall hook captureregistration in place).Verify the installed tool supports it.
agentrecall finalize-turn statusmust be a known command; if it errors, update the global tool:dotnet tool update --global AgentRecall agentrecall --versionReinforce the guidance. The scaffolded
CLAUDE.mdblock tells the agent to check status before answering and never to speculate. If your project pre-dates the current block, re-runagentrecall devcontainer init— it refreshes an older AgentRecall block in place (no duplicate) rather than appending a second copy.
The MCP capture_status tool returns the same result for agents that prefer a tool
call over the CLI.
In short: if Claude still says "the Stop hook may have captured it", update AgentRecall (
dotnet tool update --global AgentRecall) and re-runagentrecall devcontainer initto install the current Stop hook and the guidance that points the agent atcapture_status.
Outcome-aware capture
AgentRecall captures lessons not only from text, but from evidence that the agent actually made a mistake or the user corrected behaviour. The same words can be worth keeping or worth skipping depending on what produced them.
A generic refactoring rule is normally skipped as textbook advice:
"Preserve else semantics when flattening nested conditionals."
But if the agent really did flatten nested template conditionals and changed the
{{else}} behaviour — and the user corrected it — that is no longer generic advice. It
is an observed agent failure, and AgentRecall captures it as a reusable, conditional
lesson:
When flattening nested template conditionals, preserve
{{else}}semantics. If the innerifhas anelse, use an equivalent branch-preserving form such as{{else if (not …)}}instead of a plain(and …)merge.
This is deterministic and layered on top of the existing decision policy — it never replaces it. The text-only worthiness verdict and the capture decision are computed first; an adaptive worthiness policy then raises or lowers that decision using the outcome context (no LLM, no embeddings, same inputs → same output).
The rules are deterministic:
- Generic advice with no observed mistake → skipped.
- Generic advice backed by an observed agent failure or user correction → captured or suggested (and rewritten into conditional form).
- A bare code fact is still rejected, even when something broke — it is recoverable from the repository with search and is never auto-captured.
- Project-specific conventions are still captured.
- Repeated corrections raise confidence and strongly favour capture.
- A duplicate reinforces the existing rule; a conflict is held for review.
- An explicit "do not save" skips; an explicit "save this" can capture a worthy low-confidence lesson.
Capture reasons
Every adaptive capture records why it was kept, persisted on the rule and surfaced by
agentrecall rules explain <id>:
| Reason | What it means |
|---|---|
ObservedAgentFailure |
The agent's output broke or changed behaviour this turn. |
UserCorrection |
The user corrected the agent ("no, preserve the else branch"). |
AcceptedReviewComment |
An accepted/applied code-review comment. |
RepeatedCorrection |
The same correction was observed two or more times. |
LessonMined |
Surfaced by lesson mining over repeated historical signals. |
$ agentrecall rules explain 24
Rule:
When flattening nested template conditionals, preserve `{{else}}` semantics …
Captured because:
ObservedAgentFailure
Evidence:
Agent changed `{{else}}` behavior while flattening nested conditionals; user corrected the implementation.
The turn finalizer detects these signals from the turn ("that broke behavior", "no, preserve the else branch", "you changed semantics", "the review comment was applied", "tests failed because…", "this is the same mistake again") and passes them into the adaptive policy, so capture stays deterministic and the agent never has to guess.
Interactive Memory
AgentRecall usually captures high-confidence lessons automatically. When it is unsure,
it can ask you whether to remember the lesson — turning an ambiguous capture into a
quick, visible choice instead of a silent pending rule you forget about. It reuses the
existing capture decision (AutoCapture / SuggestCapture / Skip); Interactive Memory
only changes how a SuggestCapture is surfaced. It never re-classifies worthiness.
- AutoCapture → stored automatically, with a notice. No question.
- SuggestCapture → AgentRecall asks (when a terminal is attached).
- Skip → never asks.
When a terminal is attached, an ambiguous lesson is shown as a prompt:
🧠 **AgentRecall:** possible lesson detected.
Candidate:
When flattening nested template conditionals, preserve `{{else}}` semantics.
Why:
This came from an observed agent mistake, but the rule may be broad.
Actions:
[y] Remember
[n] Ignore
[v] View details
yapproves the pending rule (it becomes Active):🧠 **AgentRecall:** remembered rule #31.narchives it:🧠 **AgentRecall:** ignored suggestion #31.vshows the full rule, reason, confidence, evidence, and scope, then asks again.
Modes
AgentRecall.InteractiveMemoryMode controls whether AgentRecall asks. It is distinct
from ActivityNoticeLevel — that controls how loud the notices are; this controls
whether AgentRecall prompts.
| Mode | Behaviour |
|---|---|
Auto (default) |
Capture high-confidence lessons automatically; ask only for ambiguous SuggestCapture. |
Ask |
More conservative; a borderline auto-capture is downgraded to a question. |
Silent |
Never prompts; suggestions become Pending rules to approve later. |
// agentrecall.json
{ "AgentRecall": { "InteractiveMemoryMode": "Auto" } }
Non-interactive surfaces never block
Hooks (finalize-turn --hook), pipes, and MCP never wait for input. An ambiguous lesson
becomes a Pending rule and the output names the follow-up command:
🧠 **AgentRecall:** suggested 1 pending rule.
Run `agentrecall rules approve 31` to remember it.
Over MCP, the structured capture_feedback / add_feedback response carries
capture_decision: "SuggestCapture", pending_rule_id, and
suggested_actions: ["approve", "reject", "view_details"] — no terminal prompt text.
Approve or ignore a pending suggestion later with the existing rule commands:
agentrecall rules list --status Pending # see what is waiting
agentrecall rules approve <id> # remember it (becomes Active)
agentrecall rules archive <id> # ignore it
agentrecall capture-status --last-turn # what the last turn captured / suggested / skipped
Activity Notices
AgentRecall is visible by default. As you work, it tells you what it just did — what it fetched, captured, skipped, resolved, mined, and recommended — with a recognizable badge:
🧠 **AgentRecall:** captured 1 new rule.
- #24 Validator auth/scope safety
Crucially, the human-facing notices are kept separate from the model-visible context. Verbose detail goes to your terminal and the activity log; the hook that injects rules into Claude stays compact, so notices never bloat the token budget.
Verbosity is configurable with two independent settings:
{
"AgentRecall": {
"ActivityNoticeLevel": "Verbose",
"HookNoticeLevel": "Normal"
}
}
AgentRecall.ActivityNoticeLevel=Verbose|Normal|Silent— controls the human-facing CLI/status notices. Defaults toVerbose.Verbose— summary plus useful detail bullets:🧠 **AgentRecall:** captured 1 new rule. - #24 Validator auth/scope safetyNormal— a concise summary only:🧠 **AgentRecall:** fetched 3 rules · captured 1 · skipped 1Silent— no user-visible notices (activity is still recorded, and errors still go to stderr/logs).
AgentRecall.HookNoticeLevel=Normal|Silent— controls the notice the UserPromptSubmit hook injects alongside the rules. Defaults toNormal. It is always a single compact line and never carries verbose detail or repeats rule text, so it cannot inflate the injected context:🧠 **AgentRecall:** fetched 3 rules.An invalid value for either setting falls back to its default and prints a clear warning rather than failing.
Review what AgentRecall has done with the activity log:
agentrecall activity last # the latest notice (verbose)
agentrecall activity list # recent notices, newest first
agentrecall activity list --json # structured output for tooling
agentrecall activity list --limit 20 # show more entries
--json emits plain fields (no Markdown); the styled badge string lives only in a
separate renderedNotice field. When feeding inject-context output to a model,
pass --no-notice to suppress the human notice entirely.
Turn Memory Summary
At the end of each turn — after the Stop hook finalizes it — AgentRecall prints one aggregated summary of everything it did that turn, instead of many scattered notices. It answers, proactively and from recorded state, the questions you'd otherwise have to ask: which rules were used, what was captured, what was suggested, what was skipped, and whether anything went wrong.
It aggregates, per turn:
- rules used — retrieved/injected by the UserPromptSubmit hook
- rules captured — auto-captured while finalizing the turn
- pending suggestions — rules parked as
Pendingfor review - skipped candidates — candidates rejected, with the reason
- remembered / ignored — Interactive Memory decisions made this turn
- errors — any recoverable problem the finalizer hit
The summary is human-visible only: it is never injected back into the model context, and even the detailed form is bounded (short titles only, at most five items per section), so it can't bloat a Claude Code session.
This is governed by its own setting, distinct from ActivityNoticeLevel (which controls
per-event notices):
{ "AgentRecall": { "TurnSummaryLevel": "Compact" } }
AgentRecall.TurnSummaryLevel = Silent | Compact | Detailed. Defaults to Compact.
Silent— no automatic end-of-turn summary (theturn-summarycommand still works).Compact— one short line:🧠 **AgentRecall:** used 5 rules, captured 1, suggested 0, skipped 1.Detailed— grouped sections with short titles and reasons (never full rule bodies):🧠 **AgentRecall Turn Summary** Used: - #12 Scope-safe validators - #18 Pass loaded entities instead of re-querying Captured: - #28 Preserve else semantics when flattening nested template conditionals Suggested: - none Skipped: - Generic template refactoring: not reusable enough
Review the last turn any time:
agentrecall turn-summary --last # human-readable, at the configured level
agentrecall turn-summary --last --json # stable, deterministic JSON
agentrecall turn-summary --last --detailed # force grouped detail
agentrecall turn-summary --last --compact # force the one-line summary
When the last turn had no memory activity:
🧠 **AgentRecall:** no memory activity recorded for the last turn.
capture-status answers a narrower question — what did capture decide? — and points
to agentrecall turn-summary --last for the full per-turn activity.
Source/Outcome-Aware Capture
AgentRecall classifies candidate source and outcome before capture. Documentation, tool instructions, command output, and logs are not captured merely because they were read. They can become memory only when paired with observed agent failure, explicit save intent, or confirmed repository convention.
The classification is deterministic, offline (no LLM, no network), and English-only. It runs in two ordered steps before any rule is created:
- Structured metadata first. If the candidate came from structured activity metadata that already names its source — a skill doc, tool doc, command output, or log line — that source is trusted directly.
- Regex classifier fallback. With no metadata, a small set of compiled, timeout-guarded
pattern groups classify the text by shape (a CLI flag, an
ALL_CAPSplaceholder, a command invocation, a log level, correction/save phrasing) — never by matching exact sentences.
Each candidate is labelled one of: UserFeedback, UserExplicitSave, UserExplicitDoNotSave,
AssistantMetaProse, SourceDocumentInstruction, ToolOrSkillInstruction, CommandOutput,
LogOutput, ReviewFeedback, ObservedAgentFailure, RepositoryConventionConfirmation, or
Unknown.
The decision matrix
| Candidate | Decision |
|---|---|
| Source document / tool instruction / command output / log output / assistant meta-prose, on its own | Skip |
| Any of the above + observed agent failure | Allow → quality gate |
| Any of the above + explicit save | Allow → quality gate |
| Source document + confirmed repository convention | Allow → quality gate |
| Explicit do-not-save | Hard skip (no pairing overrides it) |
| User/review feedback, correction, repository confirmation, explicit save | Allow → quality gate |
Because a correction ("use X instead of Y") and an explicit save ("save this: …") are recognised before any source-shape, user technical guidance that merely starts with "Use" is never mistaken for documentation — the same sentence read from a tool doc is skipped, but the user's own guidance is not.
Explicit do-not-save
An explicit do-not-save instruction (don't save this, no need to save, …) hard-skips
capture: no rule and no Pending candidate are created. capture-status and the Turn Memory
Summary report the skip ("explicit do-not-save instruction"), and a structured skip activity is
recorded. If a turn carries both a do-not-save and a save request, the most recent instruction
wins; ties prefer do-not-save.
Structure/quality gate
A candidate that clears the source/outcome matrix still passes the existing structure and
quality gate before it becomes a Pending rule: it must read as a real rule — a condition, an
action, and (ideally) a reason — not a bare fragment. Rejected candidates are recorded with a
reason (SourceDocument, ToolOrSkillInstruction, CommandOutput, LogOutput,
AssistantProse, MalformedTrigger, TooVague, ExplicitDoNotSave, …) instead of being
parked for you to clean up later. An explicit "save this" still captures a clean lesson, but
never stores raw prose.
Cleaning up existing noise
For Pending rules created before source/outcome-aware capture, cleanup pending-noise finds
and archives the noisy ones using the same filters. It is a dry run by default, never
hard-deletes, and never
touches Active/Promoted, user-modified, or clean rules:
agentrecall cleanup pending-noise # dry run: report what would be archived
agentrecall cleanup pending-noise --apply # archive the noisy Pending rules
agentrecall cleanup pending-noise --json # machine-readable counts by reason
🧠 **AgentRecall:** found 47 noisy pending rules.
- Assistant prose: 31
- Malformed trigger: 11
- Duplicate noise: 5
Run `agentrecall cleanup pending-noise --apply` to archive them.
Configuration
AgentRecall reads an optional agentrecall.json from the current directory, then
applies environment-variable overrides prefixed with AGENTRECALL_.
{
"AgentRecall": {
"DataDirectory": "~/.agentrecall",
"LogLevel": "Information",
"AutoApproveFeedback": true,
"ActivityNoticeLevel": "Verbose",
"HookNoticeLevel": "Normal",
"TurnSummaryLevel": "Compact"
}
}
Set AutoApproveFeedback to false to make every captured rule start as
Pending (requiring rules approve) instead of going straight to Active.
# Override a setting for a single run
AGENTRECALL_AgentRecall__LogLevel=Debug agentrecall status
Data lives in a single SQLite database under DataDirectory (default
~/.agentrecall/agentrecall.db), so backing up or moving your memory is just
copying that folder.
Building from source
If you'd rather work from the repository instead of installing the published tool:
git clone https://github.com/AkbarDizaji/AgentRecall.git
cd AgentRecall
dotnet build
dotnet test
# Run any command without installing
dotnet run --project src/AgentRecall.Cli -- init
To build and install your local copy as the global tool:
dotnet pack
dotnet tool install --global --add-source ./nupkg AgentRecall
(dotnet pack writes the package to ./nupkg. If a build is already installed,
use dotnet tool update --global --add-source ./nupkg AgentRecall.)
Project layout
| Project | Purpose |
|---|---|
AgentRecall.Cli |
Command-line entry point (agentrecall) and MCP server. |
AgentRecall.Core |
Domain entities, services, and contracts. |
AgentRecall.Infrastructure |
Configuration, logging, and EF Core SQLite persistence. |
AgentRecall.Tests |
Tests (run against temporary SQLite databases). |
Releasing
Releases are automated by .github/workflows/release.yml on v* tags. It
builds, tests, packs, and publishes to NuGet using Trusted Publishing (OIDC)
— no API key is stored in the repository.
One-time setup: create a Trusted Publishing policy for the AgentRecall package
on NuGet.org (pointing at this repo and release.yml), and add a repository
variable NUGET_USER with your NuGet.org username.
To cut a release: bump VersionPrefix in Directory.Build.props, add an entry
to CHANGELOG.md and update <PackageReleaseNotes> in
src/AgentRecall.Cli/AgentRecall.Cli.csproj (this is what NuGet shows for the
version), then tag:
git tag v0.2.0
git push origin v0.2.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.6.0 | 0 | 8/3/2026 |
| 2.5.0 | 48 | 7/31/2026 |
| 2.4.1 | 57 | 7/30/2026 |
| 2.4.0 | 78 | 7/28/2026 |
| 2.3.1 | 94 | 7/27/2026 |
| 2.3.0 | 93 | 7/27/2026 |
| 2.2.6 | 91 | 7/24/2026 |
| 2.2.5 | 101 | 7/24/2026 |
| 2.2.4 | 104 | 7/21/2026 |
| 2.2.3 | 96 | 7/19/2026 |
| 2.2.2 | 93 | 7/17/2026 |
| 2.2.1 | 102 | 7/15/2026 |
| 2.2.0 | 97 | 7/14/2026 |
| 2.1.0 | 102 | 7/11/2026 |
| 2.0.0 | 116 | 7/9/2026 |
| 1.5.0 | 110 | 7/8/2026 |
| 1.4.0 | 108 | 7/7/2026 |
| 1.3.0 | 100 | 7/5/2026 |
| 1.2.0 | 101 | 7/3/2026 |
| 1.1.0 | 107 | 7/2/2026 |
2.2.1 hardens the 2.2.0 PreToolUse recall hook. File- and directory-scoped rules are now rewarded when the file being written is within their scope (previously they scored below a global rule and never surfaced). The hook's turn id is derived from the turn's prompt like the other hooks, so its retrievals join the end-of-turn summary. Rules already surfaced earlier in a turn are no longer re-injected or re-counted as used on every subsequent write. The database uses a busy command timeout and WAL so concurrent hook processes retry instead of silently dropping a write on SQLITE_BUSY. And the "fetched N rules" status line moved from the model's context to the user-facing systemMessage channel. Full notes: https://github.com/AkbarDizaji/AgentRecall/blob/main/CHANGELOG.md