Skip to content

Trail-code 0.22.0: publish as @trailhq/trail-code, keep @nanonets/graft updating - #595

Merged
shhdwi merged 16 commits into
mainfrom
fix/trail-release-prep
Oct 14, 2026
Merged

shhdwi merged 16 commits into
mainfrom
fix/trail-release-prep

Conversation

@shhdwi

@shhdwi shhdwi commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

graft becomes Trail-code in 0.22.0. One build ships as two npm packages:

  • @trailhq/trail-code: the trail command (new)
  • @nanonets/graft: the graft command, unchanged, and it keeps getting every release

Nothing about graft is removed or deprecated. npm skips deprecated versions, so deprecating graft would quietly stop updates for everyone on it.

How a graft user ends up on trail

  1. graft 0.21.1 already tells people when a new version is out, so they run npm i -g @nanonets/graft@latest and get 0.22.0.
  2. On a machine with graft and no trail, Claude Code's session start tells the agent, at most once a week, to mention that graft upgrade adds trail. A person typing graft sees the same tip once a day.
  3. graft upgrade installs @trailhq/trail-code next to graft.
  4. The next Claude Code session in a graft-wired repo moves it to trail by itself: hooks, skill and .mcp.json switch to the trail names, and the past-session read starts in the background.

npx -y @nanonets/graft and npx -y @trailhq/trail-code both pick up 0.22.0.

What else changed

  • Each Claude Code hook answers once. init registers hooks in the repo and in ~/.claude, and Claude Code ran both, so learnings arrived twice per prompt. The repo copy now stands down when the ~/.claude copy covers the same event and can find trail.
  • Hook shims find @trailhq/trail-code, then @nanonets/graft. On a trail-only machine, trail warns in a repo whose older graft hooks can't reach it and says to run trail init there.
  • Under trail, hints name trail's commands. A trail-only install has no graft binary, so a hint saying graft ask cost the agent a failed tool call. The tokens-saved tally also counts "trail saved".
  • Other agents' files say trail too. In a repo on trail, Cursor, Codex, Gemini, Copilot and the rest get trail-named files (.cursor/rules/trail.mdc), a trail:start block that replaces graft's in shared files like AGENTS.md, and a trail MCP server. The session-start move to trail moves them too, and their hooks speak trail. Under graft the output is byte-identical.
  • The workspace outbox only queues while connected, and only for repos in the workspace. Connecting empties anything queued before; sending drops records from before the connection or from other repos. This is what the README's privacy section promises.
  • The package points at github.com/trailhq/Trail-Code, and the README is the Trail-code one, with every claim checked against the code (two audits, ~250 claims).

Tested

  • Full suite on macOS: 1401 tests.
  • The upgrade path end to end against a local npm registry: a 0.21.1 user, then 0.22.0, then graft upgrade, then the repo moving to trail at the next session start.
  • A real Claude Code session in the moved repo: one copy of each hook's context, [trail] tags, no graft commands suggested.
  • Install under npm 12, which blocks install scripts: a Go + Kotlin repo builds, Kotlin falls back to WASM with the right fix command.
  • trail init --all-agents in a fresh repo writes no graft-named file; a repo wired by graft init --all-agents moves fully at session start.

After this merges: publish @trailhq/trail-code first, then @nanonets/graft, both 0.22.0.

🤖 Generated with Claude Code

Shrish Dwivedi added 15 commits October 14, 2026 17:43
…Code

The repo was renamed. npm provenance checks the repository URL against the
GitHub repo that runs the publish, case included, so a release from Actions
needs the exact name.
…ch it

graft 0.21 and older committed hook shims that look for @nanonets/graft
alone, and registered a bare `graft mcp` server. On a machine with trail
and no graft, those hooks run and do nothing and the server never starts,
so the session start that moves a repo to trail never happens.

trail init in the repo moves it, as does a session under the hooks trail
init writes to ~/.claude. For someone who has done neither, trail now
prints one line in such a repo saying to run trail init there. Only for a
person at a terminal; agents, pipes, CI and graft never see it.
The package takes the product's name; the command stays `trail` and
@nanonets/graft is unchanged. Hook shims also still find @trailhq/trail,
the name builds from source had before anything was published.
init registers the hooks in the repo and in ~/.claude, and Claude Code
runs both, so every prompt got its learnings twice and the ask to save one
came twice. The repo's copy now stands down for an event the ~/.claude copy
answers, when that copy can find trail. Cursor and Codex shims never do.
…o get it

A person typing graft already sees that graft is now trail. Most people
only meet graft through their agent, which was told nothing. Once a week,
in a repo graft set up, the session start now asks the agent to say in one
line that `graft upgrade` adds trail and graft keeps working.
A trail-only install has no graft binary, so a hint that told the agent to
run graft ask, or a blast header tagged [graft], cost a failed tool call.
The saved-tokens tally now also counts "trail saved".
Rewrites the README around Trail-code's two parts: the graft map and
learnings. Adds the how-it-works diagram and drops the old graft assets;
TELEMETRY, SECURITY, CREDITS and docs/github-app.md use the new name.

Matches what main does since #585: trail init reads past sessions once,
corrections are saved as learnings, skills and the workspace commands are
out of help and marked soon, and the save lines read "saved for your team".
Says how a repo graft set up moves to trail.
…trail

trail init wrote graft-branded files for every agent but Claude Code:
AGENTS.md "## Graft", .cursor/rules/graft.mdc and the rest told the agent
to run graft ask/grep/callers, which a machine with only
@trailhq/trail-code doesn't have, and their MCP entries pulled
npx -y @nanonets/graft.

The other agents now follow the repo's name the way Claude Code's wiring
does (hostWiringName, read from the repo, so a teammate on graft never
flips it back). Under trail:
- instruction text is graft's in trail's spelling (trailSpelling, shared
  with the Claude skill: graft check -> trail build --check)
- owned files carry the name (.cursor/rules/trail.mdc,
  .adal/skills/trail/SKILL.md, .cursor/hooks/trail-hooks.cjs,
  ~/.gemini/skills/trail/SKILL.md) and graft's copy goes
- shared files get a <!-- trail:start --> block that replaces a graft-era
  block in place, never beside it
- MCP servers are `trail` (trail mcp / npx -y @trailhq/trail-code mcp) and
  graft's entry is dropped; a graft repo leaves a machine-wide trail
  server alone rather than add graft's beside it

The session-start move to trail (upkeep toTrail) now moves these hosts
too, upkeep finds them under either name, and uninstall removes either
name, Cursor's hooks included. Under graft the output is unchanged.
…ace repos

Every trail ask/check that showed a learning appended the search text,
files and names to ~/.trail/outbox.jsonl even with no workspace
connected, and connecting sent all of it, for every repo, including
repos the person never picked.

recordActivity now writes only while this machine is connected and only
for a repo in the workspace, so nothing is queued with cloud features
off. Connecting empties the outbox first, and a send drops anything from
before the connection or for a repo outside the workspace (what an older
trail queued).
…r it

Their hooks run the same entry as Claude's, but the name check only looked
at .claude, so a repo with only Cursor or Codex on trail got graft's words.
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

🌱 graft blast radius

5 areas changed → 8 areas can be affected. 17 dependent symbols, depth 2.
Tests: Workspace Federation has tests the diff did not touch; 3 areas updated their tests.
Tag: @anirudhkumar-nanonets — 13 of 13 areas · Buseong Kim — Ask Formatting · @Frankie-Xu — Claude Initialization

flowchart TB
  A0(("Pull Request Review<br/>5 symbols"))
  A1(("Ask Formatting<br/>2 symbols"))
  A2(("Graph Building<br/>2 symbols"))
  A3(("Editor Integration<br/>2 symbols"))
  A4(("Claude Initialization<br/>2 symbols"))
  AX(("3 smaller areas<br/>4 symbols"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3,A4 reached;
  classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
  class AX tail;
Loading
Can be affected Symbols Nearest hop Reached from
Pull Request Review 5 src/app/review.ts:L45-L99 reviewPullRequest — calls, depth 1 zeroHitNote, Workspace Federation
Ask Formatting 2 src/ask/ask.ts:L1552-L1565 askSavingsLine — calls, depth 1 zeroHitNote
Graph Building 2 src/graph/build.ts:L204-L472 buildGraph — calls, depth 1 Workspace Federation
Editor Integration 2 src/hosts/claude-global.ts:L93-L134 installClaudeGlobal — calls, depth 2 Claude Formatting
Claude Initialization 2 src/claude/statusline.ts:L26-L38 main — calls, depth 1 zeroHitNote, Claude Formatting
MCP Server 2 src/mcp/server.ts:L1-L156 server.ts — imports, depth 2 zeroHitNote
2 smaller areas 2 Graph Engine, Telemetry Tracking see below
Who knows this code — 4 people across 13 areas
Area Who knows it
zeroHitNote · changed @anirudhkumar-nanonets — 61 commits, last today
Workspace Federation · changed @anirudhkumar-nanonets — 15 commits, last yesterday
Claude Formatting · changed @anirudhkumar-nanonets — 22 commits, last today
Host Configuration · changed @anirudhkumar-nanonets — 8 commits, last yesterday
Build Scripts · changed @anirudhkumar-nanonets — 2 commits, last yesterday
Pull Request Review · affected @anirudhkumar-nanonets — 11 commits, last 1mo ago
Ask Formatting · affected @anirudhkumar-nanonets — 14 commits, last today · Buseong Kim — 3 commits, last 2mo ago
Graph Building · affected @anirudhkumar-nanonets — 19 commits, last yesterday
…5 further areas

Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 17 dependent symbols, grouped by area

Pull Request Review — 5 symbols in 5 files

  • src/app/review.ts:L45-L99 — reviewPullRequest (calls, depth 1)
    79: let body = `${MARKER}\n${markdownReport(report)}`;
  • src/app/brain-build.ts:L251-L358 — readRepository (calls, depth 2)
    259: // pull-request walk, the digest — and without the elapsed time on each, the
  • src/app/review-process.ts:L180-L184 — childReviewer (references, depth 2)
  • src/app/review-worker.ts:L67-L87 — run (calls, depth 2)
  • src/app/server.ts:L34-L46 — AppSeams (references, depth 2)

Ask Formatting — 2 symbols in 1 file

  • src/ask/ask.ts:L1552-L1565 — askSavingsLine (calls, depth 1)
    1563: savingsTurnNudge(saved)
  • src/ask/ask.ts:L1476-L1531 — formatAsk (calls, depth 2)
    1478: // The note prints as its own prominent line(s) right under the header —

Graph Building — 2 symbols in 2 files

  • src/graph/build.ts:L204-L472 — buildGraph (calls, depth 1)
    343: // has its own cache (wiring.json itself, keyed on body_hash); this one is
  • src/graph/refresh.ts:L151-L228 — ensureFreshGraph (calls, depth 2)
    167: // under a query is a surprise, and it's the one case where the user hasn't

Editor Integration — 2 symbols in 2 files

  • src/hosts/claude-global.ts:L93-L134 — installClaudeGlobal (calls, depth 2)
    94: const [shim, settings, mcp] = claudeGlobalTargets(home, brand);
  • src/hosts/codex-hooks.ts:L60-L89 — installCodexHooks (calls, depth 2)
    61: const targets = hookTargets(home);

Claude Initialization — 2 symbols in 2 files

  • src/claude/statusline.ts:L26-L38 — main (calls, depth 1)
    26: export function main(): void {
  • src/claude/init.ts:L80-L136 — runInit (calls, depth 2)
    106: const skillPath = skill;

MCP Server — 2 symbols in 1 file

  • src/mcp/server.ts:L1-L156 — server.ts (imports, depth 2)
    64: const main = mainWorktreeRoot(root);
  • src/mcp/server.ts:L73-L155 — startMcpServer (calls, depth 2)
    76: // refreshes rule files an older `graft init` wrote, and kicks off the cached

Graph Engine — 1 symbol in 1 file

  • src/engine.ts:L92-L102 — graph (calls, depth 2)

Telemetry Tracking — 1 symbol in 1 file

  • src/telemetry/track.ts:L1-L161 — track.ts (imports, depth 2)
    18: * even under a key that happens to be allowed.
Test signal per changed area — 3 ✓ · 1 ⚠ · 1 –

Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.

  • ✓ zeroHitNote — 19 of 31 reached · 6 test files changed here: test/hosts-trail.test.ts, test/trail-cloud.test.ts, test/trail-name.test.ts, test/trail-notes.test.ts, test/trail-nudge.test.ts, test/trail-spelling.test.ts
    • not reached: gitRootOf, wireTarget, rewriteWiring, stripManagedBlocks, noGraph, unknownSymbolText, callSingleTool, runGrepCommand, …4 more
  • ⚠ Workspace Federation — 5 of 8 reached · 2 test files reach it, none changed here
    • not reached: writeIndex, looseNoteFor, runCallersCommand
  • ✓ Claude Formatting — 6 of 10 reached · 4 test files changed here: test/claude-hook-layers.test.ts, test/hosts-trail.test.ts, test/trail-name.test.ts, test/trail-spelling.test.ts
    • not reached: findsTrail, main, shim, trailSkill
  • ✓ Host Configuration — 15 of 36 reached · 2 test files changed here: test/claude-shim-template.test.ts, test/hosts-trail.test.ts
    • not reached: skillPath, shimPathFor, removeGraftCopy, graftBody, inName, opencodeEntry, tomlHeader, stripTomlSection, …13 more
  • – Build Scripts — no function, method or class changed here
67 test suites also reference this code

73 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/ask-index.test.ts
  • test/ask.test.ts
  • test/blast-render.test.ts
  • test/claude-format.test.ts
  • test/claude-hooks.test.ts
  • test/claude-init.test.ts
  • test/claude-shim-resolve.test.ts
  • test/claude-skill-template.test.ts
  • test/claude-tally.test.ts
  • test/cli-meta.test.ts
  • test/cli-picker.test.ts
  • test/container-extract.test.ts
  • test/context-only-dir.test.ts
  • test/context.test.ts
  • test/covers.test.ts
  • test/generic-extract.test.ts
  • test/graft-to-trail.test.ts
  • test/graph-follow-submodules.test.ts
  • test/graph-go.test.ts
  • test/graph-incremental.test.ts
  • …47 more

⚠️ 7 deleted files (assets/graft-cold-vs-graft-dark.png, assets/graft-cold-vs-graft.png, assets/graft-comparison-demo.gif, assets/graft-hero.png, assets/graft-hooks-demo.gif) — their dependents cannot be computed from a graph built at this commit, since the files are gone from it.
⚠️ 10 changed files not in the graph (CHANGELOG.md, CREDITS.md, README.md, SECURITY.md, TELEMETRY.md) — no parser claims the extension, or the index predates the file.

graft blast · origin/main...HEAD · depth 2 · 68 changed files

Open the interactive graph → — click an area to see its dependent symbols at file:line.

github-actions Bot added a commit that referenced this pull request Oct 14, 2026
@shhdwi
shhdwi merged commit d45a801 into main Oct 14, 2026
7 of 8 checks passed
github-actions Bot added a commit that referenced this pull request Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant