feat: package Recall as a native Claude Code plugin#246
Merged
Conversation
Adds `.claude-plugin/marketplace.json` and the `plugins/recall-claude/` bundle, so Claude Code installs the nine `recall-*` skills and the `recall-memory` MCP server from one manifest instead of user-level configuration. Both manifests pass `claude plugin validate --strict` against Claude Code 2.1.217. Reuses the seams from the Codex package (#245) rather than re-inventing them: same bundle shape, same generate-then-check-in skill payload, same isolated e2e contract. Skills are byte-verbatim copies of `agent-skills/` because those sources are already authored against Claude's own frontmatter; rewriting them would change behavior for users migrating off the lifecycle install. Hooks are deliberately not in the bundle. Claude merges plugin hooks with settings.json hooks rather than replacing them, so shipping lifecycle hooks would double every capture for anyone who also ran install.sh. Auto-capture stays installer-owned. Migration matters because Claude namespaces plugin components: the plugin registers as `plugin:recall:recall-memory` and does NOT override a user-scope `recall-memory`. Claude collapses the two only when they resolve to an identical command and env, and install.sh writes `bun run <path>` plus an env block, so every real existing install ends up exposing the same nine tools twice from two processes, plus a doubled skill surface. install.sh and update.sh now reconcile that idempotently, removing only Recall-owned symlinks and registrations. A registration pinned to a non-default database is kept, not removed, so migration can never silently repoint memory at the default file. `recall doctor` reports the plugin state and flags an un-reconciled install, and its skill-symlink probes stand down once the plugin owns that surface so a converged install no longer reports as broken. Verified with `bun run test:e2e:claude-plugin`, which installs the plugin into a disposable HOME with a disposable RECALL_DB_PATH, seeds a legacy install, proves the duplicates are removed twice over without touching a user-authored skill, exercises all nine MCP tools, and asserts the production database and the real ~/.claude were unchanged.
Owner
Author
|
@Atlas-Key the PR is good but it doesnt outline the correct issues to be closed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Packages Recall as a native Claude Code plugin, matching the Codex bundle that landed in #245. Recall already worked on Claude; what was missing was a single-manifest package.
No existing issue tracks this (searched open + closed via
gh-axi search issues).What ships
.claude-plugin/marketplace.json+ theplugins/recall-claude/bundle: ninerecall-*skills and therecall-memoryMCP server behind one manifest.scripts/build-claude-plugin.tsregenerates the skill payload from canonicalagent-skills/; a drift test fails if the checked-in copies diverge.docs/CLAUDE_INTEGRATION.md, written to theCODEX_INTEGRATION.mdstandard.Both manifests pass
claude plugin validate --strictagainst Claude Code 2.1.217, verified against the live CLI and the official plugin/marketplace reference rather than from memory.Reuses the Codex seams
Same bundle shape, same generate-then-check-in payload, same isolated e2e contract. Plugin detection was added to the existing
src/hosts/claude.tshost adapter rather than a new abstraction. No regression to the host decoupling.Skills are byte-verbatim copies of
agent-skills/, not adapters: those sources are already authored against Claude's own frontmatter, so rewriting them would change behavior for users migrating off the lifecycle install. Copied rather than symlinked, because Claude drops symlinks that leave the plugin root on local-path installs andcore.symlinks=falsecheckouts would degrade the payload silently.Migration is the substance of this PR
Claude namespaces plugin components. The plugin registers as
plugin:recall:recall-memoryand does not override a user-scoperecall-memory— verified empirically. Claude collapses the two only when they resolve to an identical command and env, andinstall.shwritesbun run <path>plus an env block. So every real existing install ends up exposing the same nine tools twice from two processes, plus a doubled skill surface.install.shandupdate.shnow reconcile that, idempotently:shared/skills/). Real files, user-authored skills, and other tools' links are untouched; a skill dir is removed only when already empty.A registration pinned to a non-default database is kept, not removed — deleting it would silently repoint memory at the default file and the user's history would read as empty. Those installs keep both surfaces by design; the doc gives the explicit manual collapse step rather than advice that no-ops.
recall doctorgains a Claude native plugin check, and its skill-symlink probes stand down once the plugin owns that surface so a converged install no longer reports as broken.Deliberately excluded: hooks
Plugin hooks merge with
settings.jsonhooks rather than replacing them, so bundlingStop/SessionStart/PreCompactwould double every capture for anyone who also raninstall.sh. Lifecycle capture stays installer-owned. This is a documented boundary, not an omission.Isolation evidence
bun run test:e2e:claude-pluginruns entirely in a disposableHOMEand a disposableRECALL_DB_PATH, and asserts before/after that both the production database and the real~/.claudewere unchanged:The legacy state is seeded by the installer's own
_recall_link_skills_to, so migration is tested against the symlink format production actually produces instead of a hand-rolled approximation.Independently confirmed afterwards: the real
~/.claudehas norecall-marketplace, no plugin entry, and its nine legacy skill symlinks are intact.Named behavior differences vs the Codex package
Full table in the doc. The notable one, since identical
SKILL.mdbytes do not imply identical behavior: Codex stripsdisable-model-invocationand substitutesagents/openai.yamlbecause it cannot interpret the field; the Claude bundle keeps it, because it is Claude's own contract.Honestly bounded: end-to-end enforcement of that field for plugin-shipped skills is unverified. Confirming it needs an authenticated session, and I would not authenticate against the captain's live
~/.claude. A negative control also showedclaude plugin validate --strictdoes not inspect skill frontmatter at all, so passing validation is not evidence. Named in the doc's Current Boundaries rather than assumed away.Verification
bun test— 1256 pass, 0 failbun run lint— cleanbun run test:e2e:claude-plugin— PASSbash -non all four lifecycle scriptsOut of scope: Pi packaging, which a parallel task owns. Its behavior is unchanged here.
Issues
This PR closes nothing, deliberately - no closing keyword is used.
Re-checked against every issue in this repo (125 total, 31 open) and against each open issue's acceptance criteria, not just its title. No open issue tracks native Claude plugin packaging, and nothing in this diff fully satisfies an existing issue. A wrong
Fixeswould silently close still-open work, so the adjacent issues are referenced without a keyword instead.Base branch is
main(the repo default), so any keyword added here would fire on merge - which is exactly why none is present.Related, but NOT closed
_recall_unlink_claude_skill_linksinlib/install-lib.shimplements precisely the ownership rule harden: uninstall wipes user-authored files — ownership inferred by name/dir while install is per-file (#230 audit) #236 asks for: remove only symlinks whose target resolves into$RECALL_SHARED_SKILLS_DIR, leave real files and foreign links alone,rmdirthe skill dir only when already empty. But it runs on the install/update path when the plugin is active. harden: uninstall wipes user-authored files — ownership inferred by name/dir while install is per-file (#230 audit) #236 is aboutuninstall.sh-remove_skills_from'srm -rf "$skills_root/$name"andremove_slash_commands'srm -rf ~/.claude/commands/Recall/- and this PR does not touchuninstall.shat all. The pattern is now available to reuse there; the defect is untouched. harden: uninstall wipes user-authored files — ownership inferred by name/dir while install is per-file (#230 audit) #236 stays open.Considered and deliberately NOT linked
--fixsymlink repairs unguarded + probes non-file canonical entries). This PR does changesrc/commands/doctor.ts, but none of harden: doctor --fix symlink repairs unguarded (EACCES aborts run) + probes non-file canonical entries (#230 audit) #237's three items: the repair loop still callssc.repair()bare instead of routing throughresolveProbeResult; thereaddirSyncprobe enumeration still admits dotfiles and subdirectories with no[[ -f ]]-equivalent filter;buildSymlinkProbesis still unexported and uncovered (the newtests/plugins/claude-plugin.test.tscovers the bundle andprobeClaudePlugin, not that function). What this PR adds is a gate aroundbuildSymlinkProbes, not the hardening harden: doctor --fix symlink repairs unguarded (EACCES aborts run) + probes non-file canonical entries (#230 audit) #237 specifies.docs/architecture.mdstill shows skill canonicals at~/.agents/Recall/claude/shared/skills/, andFOR_PI.mdstill lists a subset of the nine skills.~/.claude/backups/recallininstallation.md+upgrading.md) - neither doc is touched here._recall_jsonc_merge_mcp_entryor the affected test files.npxbootstrap create npm package for eady install #1 asks for.Attribution note
#48 (
Refactor conversation imports behind an internal source-adapter registry) was resolved by #245, viasrc/hosts/session-source.ts+src/hosts/session-sources.tsand thesrc/lib/import.tsrewiring in that PR. It is not re-claimed here.