Skip to content

Claude Code plugin: /path:share and /path:query with self-installing CLI - #148

Merged
benbaarber merged 7 commits into
mainfrom
ben/claude-plugin
Jul 30, 2026
Merged

Claude Code plugin: /path:share and /path:query with self-installing CLI#148
benbaarber merged 7 commits into
mainfrom
ben/claude-plugin

Conversation

@benbaarber

@benbaarber benbaarber commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a Claude Code plugin so users get the path CLI without a manual install:

/plugin marketplace add empathic/toolpath
/plugin install path@toolpath
  • .claude-plugin/marketplace.json (marketplace toolpath) + plugins/claude-code/ (plugin path 0.1.4, so commands are /path:share and /path:query). Future harness integrations land as siblings under plugins/.
  • /path:share uploads an agent session to Pathbase. With no arguments it shares the exact current session ($CLAUDE_CODE_SESSION_ID, read inside the wrapper's current-session mode; newest-session fallback); free text in the arguments is a session hint matched against past sessions (current session excluded — its own invocation quotes the hint), and --harness plus the share pass-through flags (--anon, --public, --repo, --name, --url) go through. Session listings are fetched lazily as tool output, never embedded in the command context — eager embedding re-injected old session titles as prompt text, where the harness re-expands @path file mentions (observed live).
  • /path:query answers questions about the user's session history, translating plain English into jaq filters. The command doc mandates a path kind schema lookup before referencing structural fields (never guess), token discipline (aggregate before enumerating, project to scalars, cap lists, --no-sync on repeat queries), and answer-only reporting: no mention of the cache/jaq/filters/methodology in the response, filters shown only on request. Includes cache-verified recipes for token-spend questions.
  • No committed binaries: both commands run through plugins/claude-code/scripts/ensure-path.sh, which prefers an existing Toolpath path on PATH (identity-checked), else ~/.local/bin/path, else ~/.toolpath/bin/path, else downloads the latest GitHub release (sha256-verified, adapted from install.sh) and installs globally to ~/.local/bin — diverting to ~/.toolpath/bin when a foreign binary named path claims the name. MIN_VERSION warns when the resolved CLI predates the command docs.

Constraints discovered during testing, baked into the design: inline ! context commands and auto-approved Bash must not contain $PWD/env variables (the permission checker rejects commands it cannot statically analyze — hence the sessions and current-session helper modes that read them inside the script); --project must always be absolute (path-cli does not canonicalize relative values; . silently matches nothing); and plugin updates are keyed on the plugin.json version string, not content, so every user-visible plugin change bumps the version (0.1.0 → 0.1.4 across this PR, kept in lockstep with the marketplace entry by test-plugin.sh).

Testing

  • scripts/test-plugin.sh (new plugin quality gate): 11 offline checks — manifest consistency/lockstep, resolution order, exec/sessions/current-session modes, min-version warning, clean install against a stubbed curl/release, foreign-name divert, tampered-checksum rejection. Plugin scripts join the shellcheck gate; ensure-path.sh also verified under macOS stock bash 3.2.
  • Fresh-user sandbox exercised the real GitHub release download (v0.15.0, checksum OK).
  • Real marketplace flow verified twice: from a local path, and GitHub-hosted via marketplace add "empathic/toolpath#ben/claude-plugin" (branch-ref clone) → install → both commands working from other projects.
  • Live shares against pathbase-dev.fly.dev and pathbase.dev: anon + --url, flagless authed, and hint-based — each verified to upload exactly the intended session by diffing session files and re-importing the returned graph URL. Failure modes (server down, logged out) report cleanly. Hint parsing hardened against an adversarial case (ambiguous hint + decoy sessions, haiku).
  • Command-doc iterations driven by real usage transcripts: token-efficiency feedback (schema probing, over-enumeration, repeated sync noise) and answer-style feedback (no cache/filter talk in responses).
  • scripts/quality_gates.sh: 8/8 pass under the repo-pinned toolchain; ci + deploy green on every commit.

Not covered: marketplace add empathic/toolpath resolving the default branch (testable only after merge; the same flow was verified against the branch ref); Linux bootstrap is tested offline but hasn't run on a Linux box.

CLI-side follow-ups surfaced by testing, deliberately not in this PR: canonicalize relative --project values, truncate values interpolated into jaq error messages, quiet repeated sync progress within one session.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

🔍 Preview deployed: https://11c2a786.toolpath.pages.dev

@akesling akesling left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@akesling akesling assigned benbaarber and unassigned akesling Jul 30, 2026
…elf-installing CLI

New .claude-plugin/marketplace.json (marketplace 'toolpath') + plugins/claude-code/
(plugin 'path'). Install: /plugin marketplace add empathic/toolpath, then
/plugin install path@toolpath. Future harness integrations land as siblings
under plugins/.

No committed binaries: both commands run through scripts/ensure-path.sh, which
prefers an existing Toolpath install (identity-checked via --help), else
~/.local/bin/path, else ~/.toolpath/bin/path, else downloads the latest GitHub
release (sha256-verified, adapted from install.sh) and installs globally to
~/.local/bin — diverting to ~/.toolpath/bin when a foreign binary named 'path'
claims the name. MIN_VERSION warns when the resolved CLI predates the command
docs.

Command docs encode two discovered constraints: inline !-context commands and
model-issued Bash must avoid $PWD (the permission checker rejects commands it
cannot statically analyze — hence the 'sessions' helper mode), and --project
must always be absolute (path-cli does not canonicalize relative values; '.'
silently matches nothing).

Tests: scripts/test-plugin.sh (manifest consistency + offline bootstrap tests
against a stubbed curl/release: resolution order, exec/sessions modes,
min-version warning, clean install, foreign-name fallback, checksum rejection),
wired into quality_gates.sh as the new 'plugin' gate; plugin scripts join the
shellcheck gate. Verified end-to-end headless: claude --plugin-dir with
/path:query answering from the real cache and /path:share resolving the live
session and failing cleanly against an unreachable server.
…a $CLAUDE_CODE_SESSION_ID

Claude Code exports CLAUDE_CODE_SESSION_ID to command shells, so share.md no
longer trusts the newest-row heuristic (which can race concurrent sessions in
the same project): a new current-session helper mode in ensure-path.sh echoes
the env var (read inside the script — variables on the inline command line
fail the permission checker's static analysis) and answers before binary
resolution so it can never block or download. Newest-row remains the fallback
when the variable is absent.

Verified live against pathbase-dev.fly.dev: a headless '/path:share --anon
--url …' run uploaded its own session (path-claude-code-665b7d09, the one
JSONL the run created), the returned graph URL serves 200, and re-importing
it round-trips with the session's first user message intact.
…sion hint

Drop the eager session listing from /path:share's context. It was the primary
selection mechanism before current-session existed; since then it only served
the hint case, and it cost twice: ~1k tokens of stale titles on every
invocation, and old user text re-injected as prompt text — where the harness
re-expands @path file mentions (observed live: a session title containing
@../clash/ pulled that directory into an unrelated /path:share). The model now
runs the sessions helper as a tool call only when a hint or --harness needs
it; tool output is not scanned for @-mentions.

Two selection rules hardened by headless testing with a deliberately awkward
hint ('raw output'): any non-flag text in the arguments is a session hint,
never an instruction or formatting request (haiku previously read it as noise
and shared the current session); and the current session is excluded from
hint matching, since its own first message quotes the hint. After the changes
the same run picks the intended past session over two decoys whose titles
also contain the hint words.
…hare redesign

Claude Code keys plugin updates on the plugin.json version string, not
content — commits without a bump never reach installed users. Marketplace
entry kept in lockstep.
Driven by a real usage transcript: an agent answered correctly but spent ~2x
the tokens it needed — enumerating 70 steps where an aggregate answered,
probing structural field names by trial and error (one bad guess dumped a
whole file into a jaq index error), and re-projecting the same step twice.

query.md now instructs: consult `path kind` before referencing structural
fields (the schema is the CLI's job to serve, not the doc's to duplicate);
aggregate before enumerating; project to scalars and cap lists; combine
projections over one selection into one call; and pass --no-sync on repeat
queries (0.16+). Adds two cache-verified recipes for the recurring question —
top-N steps by input tokens and per-session token totals. Plugin 0.1.2.

CLI-side follow-ups deliberately not addressed here: truncating values
interpolated into jaq error messages, and quieting repeated sync progress
output within one session.
User feedback on a real run: the response led with 'the Toolpath cache' (a
term that means nothing to end users) and closed by printing the jaq filter
it ran. The Report section now inverts its previous instruction: refer to
the data as the user's session history, never name the cache/Toolpath/jaq
or describe how the answer was computed, and show the filter only if asked
or when correcting one the user wrote. Data caveats that change how the
answer reads (per-message vs per-tool-call token accounting) stay welcome.
Plugin 0.1.3.
Simplify the command's description and argument hint — the palette is user
surface, and 'jaq filter' / '--source' jargon belongs in the command body,
not the hint. Bumped so installed 0.1.3 copies don't collide with changed
content under the same version.
@ben-emp
ben-emp force-pushed the ben/claude-plugin branch from 5686b16 to 30107bc Compare July 30, 2026 20:31
@benbaarber
benbaarber merged commit a0e1227 into main Jul 30, 2026
3 checks passed
@benbaarber
benbaarber deleted the ben/claude-plugin branch July 30, 2026 20:35
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.

2 participants