Repository navigation
Releases: forgeprint/forgelore
Release list
v0.1.10
Cursor support, verified against a running agent rather than read about.
Added
Cursor. sessionStart, postToolUse, postToolUseFailure and
sessionEnd — the four canonical events exactly, which is not what an
earlier reading of the documentation suggested. Hooks go in
.cursor/hooks.json, shaped {"version": 1, "hooks": {…}}.
Six payloads were captured from cursor-agent 2026.10.01 and three of
them changed the mapping:
- One command fires two events. A failing build arrives as
afterShellExecution, carrying the command and its output, and as
postToolUseFailure, carrying the same failure with a tool name, a
working directory and an error message. Only the tool events are mapped;
mapping both would look one error up twice. - The exit code is readable.
postToolUsereturns its result as a JSON
string —{"output":"…","exitCode":0}— so failure is read out of the
payload rather than guessed from the output's shape. - Context goes back as a top-level
additional_context, snake_case,
unlike every other agent here.
That last one was measured, not taken on trust: a hook returned a probe
token and the model repeated it verbatim. It is the only reply path in
this project verified by watching the model receive it, and the reason is
that Codex's documented path turned out to be wrong in a way that would
have been completely silent.
What Cursor cannot do
A piped build is invisible. postToolUse hands its result back as a JSON
string, so a masked failure's output arrives with its newlines escaped,
on one line, and the fingerprinter finds nothing in it. No mapping can fix
that, and the agents that can see a piped failure are listed in
docs/compatibility.md.
Also
Every Cursor payload carries user_email. The capture script now scrubs
email addresses by shape rather than by field name, so an agent that
starts sending one tomorrow is covered, and a test refuses any address
reaching the corpus that is not the placeholder.
Upgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. No fingerprint changes. The record schema is 1 and the
mapping format is 2.
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgelore
npm audit signaturesWhich agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.290 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Gemini CLI 0.62.0 | hooks only — its MCP client has never been connected | B |
| Cursor 2026.10.01 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is derived from schemas in its binary | unverified |
v0.1.9
Two fixes to how Forgelore decides a command failed, both found by leaving
it running on its own repository and reading what it recorded.
Fixed
A slow machine stopped it measuring, silently. The hook's budget was
checked before the first lookup, but the expensive part — opening the
index — happens before that. On a machine slow enough to spend the budget
there, nothing was recorded: no injection, no ledger line, and a report
showing fewer errors than really happened while saying nothing about the
difference. That is paying the whole cost and throwing away the result.
The budget now limits how many lookups happen, not whether any does. The
first always runs; the deadline applies from the second. Found by a CI
runner slower than any machine this was developed on.
A guess that comes to nothing now leaves nothing behind. Since v0.1.6 a
successful command whose output looks like an error is treated as a failure,
because a build piped through head exits zero and that is the only way to
see it. Running Forgelore on its own repository measured what that costs:
thirteen inferred failures in one session, not one of them real. They
were scripts printing error examples while the matchers were being worked
on. Nothing was injected, because nothing matched — but the ledger filled
with misses for commands that never failed, and the report's "nothing
known" count stopped meaning anything.
So an inferred failure that matches nothing is no longer recorded at all:
no ledger line, no session state. The lookup still happens, and a match is
still injected and still counted, because then something really was known.
Narrowing by command shape was tried first and the same measurement killed
it: all thirteen came from commands containing a pipe or a chain, so "only
infer when the exit status could have been masked" would have caught every
one. There is no signal in a hook payload that separates output which is
an error from output which is about one, and the risk that remains is
stated plainly in
ADR-0022:
a guess that does match injects a line into a session where nothing broke.
Upgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. No fingerprint changes, so every record still matches
what it matched before. The record schema is 1 and the mapping format is 2.
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgelore
npm audit signaturesWhich agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.290 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Gemini CLI 0.62.0 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Cursor | researched, not started | — |
v0.1.8
Forgelore could not remember the errors of its own build day. This release
is what fixing that took.
Added
The extractor recognises CLI errors. Until now it knew compiler and
runtime diagnostics — a file with a line and a column, a panic, an
exception header, a test failure — because that is what all twenty-nine
corpus families are. A shell command that failed for any other reason
produced no fingerprint at all, so there was nothing to key a memory on and
the injection path never fired.
Four shapes join it, each written from captured output rather than from
memory:
- a package manager's code,
npm error code E403, and only that line: the
prose around it carries a log path with a timestamp, which would hash
differently on every run; - a line beginning
error:orfatal:, with rustc's bracketed code —
how git, cargo, rustc and clang report; Failed to …, which is all some tools give;- an exception behind a label. Gemini writes
Error authenticating: IneligibleTierError: …, and the label belongs to
whoever reported the error, not to the error: it is dropped, and both
forms now hash the same.
npm/registry-404 and git/not-a-repository join the corpus as captured
families.
Changed
A line beginning error: is now a failure, and for Claude Code that
changes behaviour: PostToolUse decides failure by asking the
fingerprinter (ADR-0022),
so a command that succeeded while printing such a line is now treated as
one that failed. Nothing is written to memory either way — the visible
effect is a lookup that probably misses.
The guarantee that survives is narrower and worth stating: the word in the
middle of a sentence still proves nothing. Every matcher is anchored at
the start of a line, which is the whole defence here, and a test holds both
halves.
Unable to reach the cache, continuing will now be read as a failure.
That is the cost of the loosest of the four matchers, and it was accepted
because the alternative is not seeing an entire class of error.
Some failures still have no shape. the working tree is not clean is a
sentence with nothing diagnostic about it; matching it would mean matching
any sentence.
Does this change existing records?
No. Every fingerprint that matched before still matches: the change adds
shapes, it does not alter how a recognised one is hashed. The one exception
is an exception behind a label, which now hashes as its bare form — if you
recorded one of those, forgelore dedupe finds the pair.
Upgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is 1 and the mapping format is 2.
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgelore
npm audit signaturesWhich agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.290 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Gemini CLI 0.62.0 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Cursor | researched, not started | — |
v0.1.7
Two ways Forgelore was quietly failing to recognise an error it already
knew, both found by watching a real agent rather than by a test.
Fixed
A chained command is attributed to the tool that produced the error.
An agent writes cd web && npm run build far more often than it runs a
build on its own. Command normalisation took the first word, so a compiler
diagnostic was fingerprinted under cd — or, in the session that found
this, under ls:
ls -a && go build ./... 2>&1 | head -40
That fingerprint matched nothing, and nothing is what the agent got. The
same session then ran the plain command and the hint appeared, which is
what made the cause certain. A chain is now reduced to its last link before
anything else applies.
It is a guess, and it is wrong for go build ./... && echo ok, where the
error comes from the first link. It is the better guess: a chain is written
to reach its last command.
A package manager is the tool, not its subcommand. npm, yarn and
pnpm were treated like npx, so npm run build normalised to run and
npm test to test. A script name is not a tool, and whatever the script
invokes is nowhere on the command line — so one error was fingerprinted
separately under every script that could provoke it. They now normalise to
the package manager, exactly as go build and go test both normalise to
go.
npx and bunx still hand over to the binary they name, and so do the
subcommands that genuinely name one: npm exec, pnpm exec, yarn dlx,
bun x.
One case gets worse: yarn tsc now gives yarn rather than tsc. Yarn 1
allows running a binary directly, but a script and a binary cannot be told
apart from the command line, and the script form is far more common.
Does this change existing records?
Only for commands that were never matching. A record whose fingerprint came
from a plain command — go build ./..., npx tsc — is unaffected. If you
recorded a fix under npm run build or a chained command before this
release, re-record it or run forgelore dedupe to find the pair.
Also verified in this release
Claude Code 2.1.290, with both corpora kept and replayed. scratchpad_dir
is gone from every event where 2.1.289 carried it on all four; nothing
breaks, because session state falls back to the store's own cache.
Gemini CLI is supported and verified against 0.62.0 — hooks, tier B.
Its AfterTool reports a failing command's exit code inside llmContent
rather than in the documented error field, and the wrapper it puts around
the output does not reach the fingerprint: the same error reported by
Gemini and by Claude Code hashes identically.
Upgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is 1 and the mapping format is 2.
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgelore
npm audit signaturesWhich agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.290 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Gemini CLI 0.62.0 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Cursor | researched, not started | — |
v0.1.6
The program is unchanged. This release exists to take publishing out of
human hands, after two releases that were damaged by being in them.
Changed
npm packages are published by a workflow, with trusted publishing. No
npm token exists anywhere: the workflow authenticates to npm over OIDC, and
npm generates provenance for each package on its own. So from this release
the npm packages carry provenance, as the GitHub artifacts already did.
It runs on release: published rather than on the tag — after a person has
read the drafted release and pressed publish. npm's unpublish window is 72
hours, so the human gate is worth more there than it is here.
Two things it makes impossible rather than merely discouraged:
- The binaries are downloaded from this release, not rebuilt. What npm
ships and whatgh attestation verifycovers cannot drift apart.
forgelore@0.1.4shipped a development build because they were allowed
to. - The publish order is read from a file, not from a human reading
instructions.forgelore@0.1.4reached the registry before the packages
it depends on, which leaves anyone installing in that window with no
binary at all. The script printed the right order; that was not enough.
Verifying
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgeloreIt prints nothing when it succeeds; the exit status is the answer.
For the npm package, from this release onwards:
npm audit signaturesforgelore@0.1.4 and 0.1.5 were published by hand and have no
provenance. 0.1.4 is deprecated for a separate reason — it carries a
binary stamped v0.1.4-1-g9b2beba-dirty.
Upgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is 1 and the mapping format is 2,
both unchanged.
Which agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.289 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Gemini CLI, Cursor | researched, not started | — |
v0.1.5
A packaging fix. The program is unchanged from v0.1.4.
Why this release exists
forgelore@0.1.4 on npm shipped the wrong bytes. The binary inside it
reported v0.1.4-1-g9b2beba-dirty instead of v0.1.4, and its bytes were
not the ones the GitHub release attests. The source was identical — the
extra commit touched only documentation — but a version string that lies is
not something to leave on a registry, least of all in a release whose
subject was provenance.
What happened: dist/ is rebuilt in place by the test script, which stamps
the version from git describe. It ran between building the release and
packing the npm tarballs, and SHA256SUMS was left behind describing bytes
that no longer existed.
scripts/npm-pack.sh now refuses to pack unless dist/ still matches its
own SHA256SUMS, and reads the version back out of the host binary rather
than taking it from the command line. Both checks were tried against the
stale dist/ that caused this, and both refuse it.
The npm packages for this release are packed from the artifacts downloaded
from this release, so the bytes on npm are the bytes gh attestation verify
covers.
forgelore@0.1.4 on npm is deprecated and points here. The GitHub release
v0.1.4 was always correct and is untouched.
Windows on npm
forgelore-win32-x64 and forgelore-win32-arm64 were refused by npm's spam
detection on first publish, so v0.1.4 on npm covered Linux and macOS only.
Windows users installing through npm get a wrapper that explains it rather
than a stack trace; the direct install has always worked:
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashUpgrading
npm install -g forgelore # or
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is 1 and the mapping format is 2, both
unchanged.
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgeloreIt prints nothing when it succeeds; the exit status is the answer.
Which agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.289 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Gemini CLI, Cursor | researched, not started | — |
v0.1.4
Nothing changed in the program. This release is about proving where it came
from and adding a second way to install it.
Added
Build provenance. Every artifact here was built by the tag-triggered
workflow and attested there, before the release was created. SHA256SUMS
says the bytes are the published ones; it cannot say who published them,
because it travels in the same release as the binaries. This can:
gh attestation verify forgelore_linux_amd64 -R forgeprint/forgelorev0.1.3 and earlier have no attestation — the step did not exist yet.
Install with npm, if that is the idiom you already have:
npm install -g forgeloreIt installs the same binary as an ordinary dependency, one package per
platform, chosen by npm from os and cpu. There is no postinstall script
and nothing is downloaded at install time, so --ignore-scripts works —
which the download-on-install shape this project first sketched would not
have. A wrapper of one file runs the binary with stdio inherited, so
forgelore mcp speaks its protocol through it unchanged.
What is not covered
- The npm packages carry no provenance. They are published by hand, and
npm provenance requires publishing from a workflow. The GitHub release is
attested; the npm packages are not. - This is not operating-system code signing. macOS Gatekeeper and
Windows SmartScreen know nothing about Sigstore, and you will still meet
their warnings on first run.
Both are written down in
ADR-0023
and
ADR-0024
rather than left to be discovered.
Upgrading
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is 1 and the mapping format is 2, both
unchanged since v0.1.3.
Which agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.289 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Gemini CLI, Cursor | researched, not started | — |
v0.1.3
Forgelore stopped seeing a whole class of failure, and the only reason anyone
found out was installing the plugin from the marketplace and watching it work.
Fixed
A build piped through head is a failure again. A Bash command's exit
status reaches Forgelore only through which hook event fires. When the agent
writes
go build ./... 2>&1 | head -40the pipeline exits with head's status, which is zero. Claude Code correctly
sends PostToolUse rather than PostToolUseFailure, and no field anywhere in
that payload says the build broke. Forgelore called it a success: nothing was
recorded, nothing was recalled, and it looked exactly like a session that met
no errors.
The mapping now carries a fourth failure test, output_has_diagnostic, which
asks the fingerprinter whether the output contains an error it recognises.
Verified in live Claude Code 2.1.289 sessions with the plugin installed from
the marketplace.
Changed
The mapping format moves to 2, and only claude-code.json uses it.
copilot-cli.json and codex-cli.json still say 1, and a Forgelore from
before this release reads them unchanged.
The number moved although the field is an addition. The test is not whether
something was added but whether an older binary can read the file and still
be right: it does not know output_has_diagnostic, drops it, and calls
every piped failure a success. Refusing the file out loud is the only honest
behaviour. A mapping that sets the field while claiming version 1 is refused
for the same reason.
If you override a mapping on disk with forgelore hook --mapping, nothing
breaks — your file keeps working as it did. To use the new test, set
"mapping_version": 2 in it.
What it costs
A command that succeeds while printing error-shaped text — cat build.log,
rg "undefined" — is now read as a failure. Nothing is written to memory
either way: the visible effect is a lookup that probably misses, and no "it
works now" proposal from that one event. ADR-0022
names the rest, including the one ADR-0020 promise this breaks: a failure test
is now answered by Go rather than read out of the payload.
Also in this release
The plugin is listed in the Forgeprint marketplace:
claude plugin marketplace add forgeprint/forgeprint
claude plugin install forgelore@forgeprintThe plugin ships no binary on purpose, so install that too or the hooks do
nothing.
Upgrading
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. The record schema is unchanged at 1.
Which agents this release supports
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.289 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Gemini CLI, Cursor | researched, not started | — |
v0.1.2
Lookups made through the MCP server can now be measured.
Added
recall_error takes an optional session. Before this, a lookup through
MCP returned its hint and recorded nothing, so a team running Forgelore
through MCP rather than hooks got an empty report with no way to tell why.
It now writes the same ledger line a hook does: injected, withheld or
unmatched, with the bytes and the estimated tokens.
{ "name": "recall_error",
"arguments": { "output": "...", "command": "go build ./...",
"session": "whatever identifies this conversation" } }This is not a session in the protocol sense. MCP is stateless and a server
may not infer anything from a previous request; the identifier is passed
explicitly on every call, which is what the specification's own guidance on
stateful tools prescribes.
A lookup without one still answers. It counts towards what was spent and
drops out of the per-session comparisons, because attributing unrelated
calls to one run would invent repeated-error loops that never happened.
The A/B control arm is honoured here too. A control call returns no
matches and is indistinguishable from a miss, while the ledger still records
that a match existed. An agent that could tell which arm it was in would make
the two arms incomparable.
Changed
docs/team-trial.md was reviewed and corrected. Three things in it would
have quietly voided a trial: the pre-commit hook was described as shareable
when .git/hooks/ is never committed, MCP was offered as an equivalent to
hooks when it recorded nothing, and nothing told anyone to prove the wiring
worked before spending a week on it. It now opens with a day-zero smoke test.
Upgrading
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate.
Which agents this release supports
Added after publishing, because docs/compatibility.md was reviewed and one
of its claims was too generous. No binary changed; this is the same v0.1.2.
| Agent | Checked against a running agent | |
|---|---|---|
| Claude Code 2.1.289 | hooks, both MCP protocol eras, usage reader | A |
| Copilot CLI 1.0.91 | hooks only — its MCP client has never been connected | B |
| Codex CLI | nothing; the mapping is a reading of the documentation | unverified |
| Gemini CLI, Cursor | researched, not started | — |
Copilot CLI was listed as A when this was published. It is B: tier A
means hooks and MCP, and nobody has pointed Copilot's MCP client at
forgelore mcp. The table exists to stop exactly that kind of claim, so it
was corrected rather than defended.
Codex CLI is worth reading twice before relying on it. Its mapping was
written from the official configuration reference and not one real payload
backs it. Every other mapping in this project started the same way and
every one of them turned out to be wrong somewhere that would have left
Forgelore silent — so assume this one is too. Two specific guesses to
distrust: that a failed command arrives as PostToolUse carrying an error
field, and that a cancelled one can be recognised by an interrupted field
when Codex documents Interrupt as an event instead.
docs/compatibility.md
is kept current and now also carries the Copilot CLI hook configuration,
which the team-trial protocol pointed at and which was missing.
v0.1.1
A bug fix. v0.1.0 could not read go vet output at all.
Fixed
go vet errors were invisible. go vet prefixes its diagnostics with
vet: , and the position matcher is anchored to the first token on a line,
so nothing matched and nothing was ever recalled — silently, with no error
anywhere to show for it.
This mattered more than it looks. The reason a command's subcommand is not
part of a fingerprint is precisely so that one error is one memory across
go build, go test, go run and go vet. That promise was only three
quarters true.
A fix recorded while running go build is now found when go vet reports
the same thing:
$ forgelore recall --command "go vet ./..." --error-file err.txt
1 error(s), 1 with something recorded
cd023fb609411574 undefined: greet
fix 01M45W8AM40B94WTEANTXK4BX5 greet lives in internal/greeter; import itThe error corpus gained go/vet-undefined, captured from a real run, and
the test that checks two errors never collide now also insists that these two
families do share a fingerprint. They were captured on different operating
systems, so it proves the fingerprint survives the OS, the path shape, the
line number and the subcommand at once.
scripts/capture-errors.sh wrote the wrong platform. The frontmatter
field was hard-coded to windows/amd64; it now comes from the machine that
captured the file.
Upgrading
curl -fsSL https://raw.githubusercontent.com/forgeprint/forgelore/main/scripts/install.sh | bashNothing to migrate. Records written by v0.1.0 are read unchanged, and any
fingerprint recorded then is still correct — v0.1.0 simply never produced
one from go vet.