npm publishing is for distribution to other people. While developing the skill itself — or dogfooding it against a real re-frame2 app before the first release — you run it straight from a clone. Three install paths, most to least convenient.
Same as the README's Requirements:
babashkaonPATH— the shell shims execbbregardless of how the skill is installed.- Claude Code.
- A re-frame2 + shadow-cljs app to exercise it against. (Optional: re-com — used as a fallback source-coord source, not required.)
Edits you make in the repo are live immediately — no copy to keep in sync.
mkdir -p ~/.claude/skills
ln -s "$HOME/code/re-frame-pair2" ~/.claude/skills/re-frame-pair2With Developer Mode or admin:
New-Item -ItemType SymbolicLink `
-Path "$env:USERPROFILE\.claude\skills\re-frame-pair2" `
-Target "$env:USERPROFILE\code\re-frame-pair2"Without admin, use a directory junction:
mklink /J %USERPROFILE%\.claude\skills\re-frame-pair2 %USERPROFILE%\code\re-frame-pair2Junctions behave like symlinks for read purposes; fine for skill loading.
cp -r ~/code/re-frame-pair2 ~/.claude/skills/re-frame-pair2Simple, but you have to re-copy after every change. Useful if you want to pin a specific commit and keep iterating on the repo itself without affecting Claude's view of the skill.
Same content, but under a specific target project rather than your home directory:
cd ~/some-re-frame2-app
mkdir -p .claude/skills
ln -s "$HOME/code/re-frame-pair2" .claude/skills/re-frame-pair2Useful if you only want the skill loaded when you open the specific app you're debugging — and useful for testing what the project-local install flow feels like before anyone ships the skill.
Once the skill directory is in place:
- Implicit: ask about your running re-frame2 app in natural language ("what's in
app-dbunder:cart?"). Claude auto-matches the skill's description. - Explicit: type
/re-frame-pair2or name it in a prompt ("Using re-frame-pair2, trace[:cart/apply-coupon ...]").
First use of a session runs scripts/discover-app.sh — that connects to your shadow-cljs nREPL, verifies prerequisites, and injects the runtime namespace.
The power of the symlink approach is that editing SKILL.md / scripts/runtime.cljs / scripts/ops.clj in the repo takes effect immediately:
| You edited... | What Claude sees after your next prompt |
|---|---|
SKILL.md frontmatter or body |
New vocabulary / recipes on next invocation (may need to restart the Claude Code session for the description change to be re-indexed). |
scripts/ops.clj |
Next bb invocation picks it up — no action needed. |
scripts/runtime.cljs |
The injected code in the browser is stale until you explicitly re-push it. Run scripts/inject-runtime.sh in the connected session; it now force-reinjects regardless of the session sentinel so your edits land without a full page refresh. |
Shell shims (*.sh) |
Next invocation picks them up. |
- Confirm the directory landed where Claude Code looks:
ls ~/.claude/skills/re-frame-pair2/(or the project-local equivalent). - Confirm
SKILL.mdis at the top level of that directory, not nested. - Restart Claude Code — it reads the skill registry at session start.
- Check the skill name in
SKILL.md's frontmatter — it must match the directory name (re-frame-pair2).
bb isn't on PATH. Verify with which bb (macOS/Linux) or where bb (Windows). Install:
- macOS:
brew install borkdude/brew/babashka - Linux / Windows: babashka install guide
Restart the shell (and Claude Code) so the new PATH takes effect.
discover-app.sh couldn't locate target/shadow-cljs/nrepl.port, .shadow-cljs/nrepl.port, or $SHADOW_CLJS_NREPL_PORT. Start your dev build:
npx shadow-cljs watch <build-id>...and make sure nREPL is enabled for the build.
re-frame.interop/debug-enabled? is false (production build, or goog.DEBUG was forced off). Trace and epoch surfaces are elided. For a dev build this should be true automatically; for a release/staging build you'll need to flip the closure-define.
The app hasn't called (rf/init!) yet (or the only frame was destroyed). Wait for boot or call (rf/init!) manually.
Multiple frames are registered and the session hasn't selected one. Use frames/select or pass --frame :foo per call. Reads proceed against :rf/default after warning; mutating ops refuse.
Two likely causes:
- No epoch history yet.
(rf/epoch-history :rf/default)returns[]until the app dispatches at least one event. Click around or fire one synthetic dispatch. - No activity matches the predicate. Try
scripts/watch-epochs.sh --count 5with no predicate to confirm the transport works, then add filters.
Two preconditions, at least one must hold:
(rf/configure :source-coords {:annotate-dom? true})enabled at startup, or- re-com debug instrumentation enabled and the call site passed
:src (at).
If neither, dom/source-at returns :reason :source-coord-annotation-disabled for every element.
The session sentinel fast-path in discover-app.sh skips re-injection if the runtime is already present. To push edits into a live session, run scripts/inject-runtime.sh — it force-reinjects regardless.
# symlink or junction:
rm ~/.claude/skills/re-frame-pair2
# copy:
rm -rf ~/.claude/skills/re-frame-pair2Restart Claude Code. The skill disappears from completion; no residual state.