Claude-skill-registry scope-capture
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/clojure-scope-capture" ~/.claude/skills/majiayu000-claude-skill-registry-scope-capture && rm -rf "$T"
manifest:
skills/data/clojure-scope-capture/SKILL.mdsource content
scope-capture
Capture local scope at runtime and recreate it in the REPL for debugging.
Quick Start
(require 'sc.api) ;; 1. Add spy to capture scope (defn my-fn [x y] (let [result (+ x y)] (sc.api/spy result))) ; Captures x, y, result ;; 2. Call the function - spy logs EP id (my-fn 3 4) ; SPY [1 -1] ... saved scope with locals [x y result] ; => 7 ;; 3. Recreate scope with defsc (defs all locals as vars) (sc.api/defsc 1) ; => [#'user/x #'user/y #'user/result] ;; 4. Now evaluate expressions using captured values x ; => 3 y ; => 4 result ; => 7 (* x y) ; => 12 ;; 5. Clean up when done (sc.api/dispose! 1)
Core Concepts
Code Site (CS) - A location in code where
spy or brk is placed. Has a negative ID like -1, -2.
Execution Point (EP) - A specific execution of a Code Site. Has a positive ID like
1, 2, 3.
Log format:
SPY [EP CS] e.g., SPY [7 -3] means Execution Point 7 at Code Site -3.
Recording Scope
spy - Capture and continue
;; Wrap an expression - captures scope AND the expression value (sc.api/spy (some-expression)) ;; Without expression - just capture scope at this point (sc.api/spy) ;; With options (sc.api/spy {:sc/dynamic-vars [*out* *my-var*]} (some-expression))
brk - Capture and block (breakpoint)
(defn process [data] (let [transformed (transform data)] (sc.api/brk transformed))) ; Blocks here until released ;; In another thread, call the function (future (process my-data)) ; BRK [2 -1] ... saved scope, use sc.api/loose to resume ;; Release the breakpoint (sc.api/loose 2) ; Continue normally (sc.api/loose-with 2 value) ; Continue with replacement value (sc.api/loose-with-err 2 ex) ; Continue by throwing exception
Recreating Scope
defsc - Define vars (preferred for editor eval)
(sc.api/defsc 1) ; Defines vars for all captured locals in current namespace ; Now you can evaluate sub-expressions directly in your editor x ; works (+ x y) ; works
letsc - Let bindings
(sc.api/letsc 1 (+ x y)) ; x and y bound from captured scope ; => 7 ;; Useful for one-off evaluations without polluting namespace
ep-repl - Sub-REPL (JVM only)
(sc.repl/ep-repl 1) ; SC[1 -1]=> x ; => 3 ; SC[1 -1]=> :repl/quit
Common Patterns
Spy without wrapping expression
(let [x (foo) y (bar x)] (sc.api/spy) ; Just capture x and y here (something x y))
Spy only on errors
(try (my-fallible-code) (catch Throwable err (sc.api/spy err) ; Capture scope when error occurs (throw err)))
Disable in loops
;; If spy/brk is in a loop, disable after first capture (sc.api/disable! -1) ; Use the Code Site ID (negative)
Combine multiple scopes
;; Capture from different places, then combine (sc.api/defsc 1) ; Defines a, b from first spy (sc.api/defsc 2) ; Defines x, y from second spy ;; Now all four vars available
Restore last execution point
(eval `(sc.api/defsc ~(sc.api/last-ep-id)))
Utility Functions
;; Get info about execution point (sc.api/ep-info 1) ; => {:sc.ep/id 1 ; :sc.ep/local-bindings {x 3, y 4, ...} ; :sc.ep/value 7 ; :sc.ep/code-site {...}} ;; Get info about code site (sc.api/cs-info -1) ;; Get last execution point ID (sc.api/last-ep-id) ;; Quiet versions (only log types, not values) (sc.api/spyqt expr) (sc.api/brkqt expr) ;; Clean up vars created by defsc (sc.api/undefsc 1) ;; Free memory from execution point (sc.api/dispose! 1)
Caveats
Memory leak warning: Remove
spy/brk before production - they accumulate captured data.
Dynamic vars: Must be explicitly declared:
(sc.api/spy {:sc/dynamic-vars [*out* *my-var*]} expr)
defsc overwrites vars: If a var with same name exists (especially
defonce), defsc will fail or overwrite it.
ClojureScript: Must use
[ep-id cs-id] vector syntax:
;; ClojureScript requires both IDs (sc.api/defsc [1 -1]) (sc.api/letsc [1 -1] expr)
Workflow Summary
- Add spy where you want to capture:
or just(sc.api/spy expr)(sc.api/spy) - Execute the code path (call the function, run the test, etc.)
- Note the EP ID from the log:
SPY [1 -1] - Recreate scope:
(sc.api/defsc 1) - Debug by evaluating sub-expressions in your editor
- Clean up:
and remove the spy call(sc.api/dispose! 1)
Quick Reference
| Function | Purpose |
|---|---|
| Capture scope + evaluate expr |
| Capture scope only |
| Capture scope + block execution |
| Def vars from captured scope |
| Let-bind captured scope |
| Resume blocked brk |
| Resume with replacement value |
| Free captured data |
| Disable a code site |
| Get execution point info |
| Get most recent EP id |