Persona
npx -y skills add bigspinai/toolkit --skill personaAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
20.5 KB, as published. Nobody here has run it
Skill: Run the Claude Code Practice Mirror
You're the orchestrator. The user invoked /persona (installed plugin) or
asked you to follow this skill from a local clone, and wants you to run the
analysis. Walk through the steps below, keeping the user in the loop at the
decision points. Everything stays local — no API key required, and no data
leaves the machine.
All per-run files are written under $OUT_DIR
(~/.claude/bigspin/persona-<timestamp>/), established by the shared run
contract in Setup below — never in the repo or the current directory.
The full voice + content rules live in $PERSONA/analysis/interpret.md. Read
that after you have $OUT_DIR/metrics.json + $OUT_DIR/findings.md and
before you write $OUT_DIR/report_content.json. Don't skip it.
At a glance — dual-track architecture
All script paths are under $PERSONA; all I/O paths are under $OUT_DIR.
preprocessing/sessions_to_csv.py → sessions.csv (1 row/session)
preprocessing/enrich.py → sessions_enriched.csv (+ deterministic signal cols)
[STRUCTURED TRACK — for positioned numbers]
tagging/tag_sessions.py → tag_prompt.md, transcripts/, transcripts/_manifest.json
[you orchestrate subagents] → annotations/<session_id>.json
tagging/tag_sessions.py --assemble → tagged_sessions.csv
analysis/compute_metrics.py → metrics.json
[OPEN TRACK — for rich behavioral observation]
[you orchestrate one open subagent] → findings.md
[AUTHORING — synthesis between both tracks]
[you author] → report_content.json
analysis/render_report.py → persona-report.html, persona-report.md,
persona-hero.md, persona-hero-card.txt,
persona-hero-card.plain.txt
The output is three artifacts sharing copy verbatim: an HTML report, a
CLI hero card, and a markdown report. All three render from a single
report_content.json.
The pipeline has three layers now, not two:
- Deterministic signals (
enrich.py) — ~14 structural columns from message structure. Iteration counts, tool diversity, tests_attempted. Free, instant, fully reproducible. - Structured interpretive tagging — ~36 schema-bound signals plus four categorical fields. Tagged by Claude Code subagents reading each session against a fixed taxonomy. Produces aggregated rates that position the user against a 4,846-session corpus baseline.
- Open behavioral observation — one subagent reads ~12 of the already-exported transcripts (a cross-project subset) without a schema and returns rich findings: co-occurrence patterns, failure modes, session anchors. The schema can't represent these because it didn't think to ask.
The structured track gives the report its archetype label, fingerprint gauge, and within-cohort bars. The open track gives the report its recognition lines, sensitivity framing, and experiments. Neither alone produces a credible report — the synthesis does.
You are running this for the user, on their data, on their machine. No data leaves this directory. (An older API-tagging path exists as a fallback — see "Legacy: API tagging" at the bottom — but the default flow is fully local.)
Critical: model pinning
The corpus baselines were tagged with Claude Opus 4.7. For within-cohort positioning to be calibrated, your subagents must run on the same model. Claude Code subagents inherit the parent session's model — so this happens automatically when the user invoked you from an Opus 4.7 session.
Before starting Step 4, verify which model you're on. If the user is on a different model (e.g., Sonnet 4.6, Haiku 4.5):
- The archetype label is robust across models — proceed.
- Within-cohort positioning will drift ~10pp on average — flag this in
interpret.md's edge-case section so the report can hedge precision claims. - Suggest the user re-run from an Opus 4.7 session if calibration matters to them.
The model dependency exists because Sonnet and Opus read the taxonomy's
categorical rubrics differently (especially interaction_style —
Sonnet defaults to mixed, Opus to augmentative). Empirically:
holding model constant, API and subagent paths produce ~5pp drift on
average. Switching models produces ~11pp drift. Model-pinning matters
more than delivery-pinning.
Setup — resolve paths and start the run
Resolve the plugin root. Prefer $BIGSPIN_PLUGIN_ROOT; if unset, walk up
from this file (two levels: skills/persona/SKILL.md → plugin root). Path
shorthands used below:
$PERSONA=$BIGSPIN_PLUGIN_ROOT/skills/persona(read-only code, fixtures, baselines, templates).$SCRIPTS=$BIGSPIN_PLUGIN_ROOT/scripts(shared run contract + helpers).
Start the run with the shared entrypoint:
eval "$(bash "$SCRIPTS/new_run.sh" persona)"
new_run.sh is the single source of truth for the run contract: it
bootstraps the shared venv, creates the output directory, and exports PY
(venv interpreter), RUN_ID (persona-<timestamp>), OUT_DIR
(~/.claude/bigspin/$RUN_ID, already created), plus BIGSPIN_PLUGIN_ROOT
and PYTHONPATH. Every file this skill writes — intermediate CSVs, the
user's exported transcripts, annotations, and the final report — goes under
$OUT_DIR, never in the repo or cwd. If it exits non-zero, surface stderr
and stop (exit 2 = no uv/python3, exit 3 = venv/pip failure).
Step 1 — orient and check prerequisites
Setup already bootstrapped the venv ($PY). Confirm the renderer works
before touching the user's data:
"$PY" "$PERSONA/tests/smoke_test.py"
Free, fast, < 1 second. Confirms the renderer can produce all five artifacts from the schema + the Showrunner exemplar + the Multi-Mode Journeyman edge case. If it fails, stop and surface the error to the user.
Confirm the user wants to proceed. Show them what you're about to do and ask if they want to override defaults:
- Default sample: their last 20 parent sessions in
~/.claude/projectsfor the structured track. (Down from 50 in the old API path — subagents read sequentially within a batch, so smaller batches keep wall-clock time and token usage reasonable. 20 is enough for a stable archetype label; fewer sessions add statistical noise to the within-cohort bars, not bias.) - Default open-pass sample: ~12 sessions, drawn as a cross-project
subset of the structured sample — the same transcripts already exported
in Step 4a, reused (not re-read from raw session files). When picking
the subset, maximize distinct
projectvalues from_manifest.jsonso the open pass still spans contexts; if the recent sample clusters in one project, that breadth is inherently limited and that's fine. - Default sessions root:
~/.claude/projects(override withCLAUDE_SESSIONS_ROOTor a path arg).
Step 2 — preprocess
"$PY" "$PERSONA/preprocessing/sessions_to_csv.py" \
--out "$OUT_DIR/sessions.csv" --min-messages 5
Or with a custom sessions root:
"$PY" "$PERSONA/preprocessing/sessions_to_csv.py" /path/to/sessions \
--out "$OUT_DIR/sessions.csv" --min-messages 5
Surface the script's summary back to the user — parent count, subagent count, date range, p95 message size. If p95 is over ~500 KB, mention that and ask if they want to filter (rare, usually fine).
Step 3 — enrich (deterministic signals, no API call)
"$PY" "$PERSONA/preprocessing/enrich.py" "$OUT_DIR/sessions.csv" \
--out "$OUT_DIR/sessions_enriched.csv"
Surface the per-script summary — it lists the 14 column names added.
Step 4 — structured tagging (subagents, local, no API key)
This step replaces the old API call with Claude Code subagents. It has
five sub-steps; the helpers all live in tagging/tag_sessions.py.
4a. Export the prompt + per-session transcripts
"$PY" "$PERSONA/tagging/tag_sessions.py" --export-prompt "$OUT_DIR/tag_prompt.md"
"$PY" "$PERSONA/tagging/tag_sessions.py" "$OUT_DIR/sessions_enriched.csv" \
--export-transcripts "$OUT_DIR/transcripts/" \
--limit 20
The first line writes the ~20 KB system prompt (built from
$PERSONA/tagging/taxonomy.json) to $OUT_DIR/tag_prompt.md. The second
writes one <session_id>.txt per selected session into
$OUT_DIR/transcripts/, plus a _manifest.json that the assembler reads
later.
4b. Disclose token usage and get explicit consent (required gate)
Everything up to here was free — local Python, no model tokens. Tagging is the first step that spends tokens, so stop here and get the user's explicit consent before going further.
Run the estimate (still free, no tokens spent):
"$PY" "$PERSONA/tagging/tag_sessions.py" --run-estimate "$OUT_DIR/transcripts/"
It prints the structured sample size (call it N) and an estimated
whole-run token band (call it X–Y million). Show the user this message,
filling in N and X–Y from the command output verbatim:
/personabuilds a personalized practice report by having Claude Code subagents read your recent session transcripts locally. Nothing leaves your machine. I've run an estimate and this run will analyze your last N sessions. Estimated token usage: ~X–Y million tokens for this run (it runs in your Claude Code session). Reply "yes, I understand this will use ~X–Y tokens" to proceed, or "no" to cancel.
Then STOP and wait for the user's reply. Do not spawn any tagging subagent (4c) or run the open behavioral pass (Step 6) until the user replies with an explicit affirmative that acknowledges the token usage (e.g. "yes, I understand…"). A bare "ok", silence, or any reply that doesn't acknowledge the cost is not consent — re-ask or stop. If the user declines, stop here: the files written so far are local scratch and cost nothing.
4c. Spawn subagents to tag
Spawn N subagents in parallel — typically 4 subagents × 5 transcripts
each for a 20-session run. Use the Explore subagent type or
general-purpose; they need read access to $OUT_DIR/transcripts/ and
write access to $OUT_DIR/annotations/ (create it first:
mkdir -p "$OUT_DIR/annotations/"). (compute_metrics.py assumes no fixed
session count, so other splits are fine.)
Each subagent's prompt:
Your job is to tag Claude Code session transcripts against a fixed
taxonomy. Read the system prompt at `$OUT_DIR/tag_prompt.md` end to end —
it defines the output schema, the categorical fields, and per-signal
rubrics with strength anchors.
For each transcript file in this batch:
<list of absolute paths to $OUT_DIR/transcripts/<session_id>.txt>
Read the FULL transcript, then write the JSON annotation to
`$OUT_DIR/annotations/<session_id>.json`. The JSON must conform to the
schema described in tag_prompt.md (the OUTPUT FORMAT section). No
markdown fencing, no commentary — just the JSON object.
Precision over recall: tag a signal only if you see clear evidence.
Every fired signal must include `evidence`. Anti-pattern signals are
presence-only. Reality-contact signals require `trigger` and
`surface_type`.
Return when all transcripts in the batch have annotation files written.
The subagents work in parallel. Each session takes ~30–90 seconds of subagent time depending on transcript length.
4d. Assemble
"$PY" "$PERSONA/tagging/tag_sessions.py" \
--assemble "$OUT_DIR/annotations/" \
--manifest "$OUT_DIR/transcripts/_manifest.json" \
--out "$OUT_DIR/tagged_sessions.csv"
This emits the canonical tagged_sessions.csv — same shape
compute_metrics.py already consumes. Reports ok / missing / invalid
counts. If the missing count is > 10% of the batch, re-spawn a subagent
to fill the gaps before continuing.
4e. Validate
A spot-check is worth doing: read 2–3 random files from
$OUT_DIR/annotations/ and confirm they conform to the schema (all
required top-level fields present, signals have evidence strings,
reality-contact signals have trigger + surface_type). The renderer
won't catch malformed annotations until much later.
Step 5 — compute metrics
"$PY" "$PERSONA/analysis/compute_metrics.py" "$OUT_DIR/tagged_sessions.csv" \
--raw "$OUT_DIR/sessions.csv" \
--out "$OUT_DIR/metrics.json"
Pure stdlib, fast. Same as before. Output is the JSON file the renderer
consumes for numbers via *_ref paths.
Step 6 — open behavioral pass
This is the new track. Spawn one general-purpose subagent (or the
persona-tagger agent in open mode). Hand it ~12 of the cleaned
transcript files already exported in Step 4a — pick a cross-project
subset by maximizing distinct project values in
$OUT_DIR/transcripts/_manifest.json. Reusing those stripped transcripts
(rather than re-reading raw ~/.claude/projects/*.jsonl) is the main
token saving: the open pass produces qualitative findings.md that is
never positioned against the baseline, so reading the cleaned export
can't move any calibrated number. The subagent's prompt:
You're characterizing a user's Claude Code practice — what's distinctive
about how they work, not what's generic-software-engineer-y. You have
~12 cleaned session transcripts to read, listed below (tool-output
payloads are stripped; the conversation is intact). Read enough of each
to form a behavioral picture; you don't need to read every line.
Sessions:
<list of absolute paths to $OUT_DIR/transcripts/<session_id>.txt>
(a cross-project subset of the exported transcripts)
Return findings as markdown to `$OUT_DIR/findings.md` with these sections:
## Character
One sentence describing how this user collaborates with Claude. Vivid,
specific, second-person.
## Three distinctive patterns
Three behavioral patterns that are this user's, not generic. Each:
- One-line claim
- 2–3 specific session anchors with session_id and brief paraphrase
(NOT verbatim quotes — paraphrase patterns)
- Why it's distinctive (vs. what most users do)
## Sensitivity
Where this user's practice is fragile — what they'd lose if a session
went sideways. Identify the *mechanism*, not just the missing tool-use.
## Two experiments
Two concrete moves to try. Each:
- One-sentence move name (imperative)
- Why this user specifically — anchored to a pattern from above
- Specific action in their next session
Be sharp, not flattering. The user wants recognition, not reassurance.
No verbatim transcript quotes — paraphrase. No percentages — those
belong on the structured side.
The output $OUT_DIR/findings.md is what carries the rich behavioral
observation into Step 7. This is the part the structured tagger can't do
because the schema is a ceiling on what it can find.
Step 7 — author report_content.json
Now read $PERSONA/analysis/interpret.md end to end. It has the voice rules,
structural contract, Y-vocabulary table for picking the title verb, and
the new section on synthesizing across structured + open tracks.
Then author $OUT_DIR/report_content.json:
- Title, fingerprint badge, traits, comparison bars, archetype label,
shadow → from
metrics.jsonvia*_refpaths. (Same as before.) - Recognition lines, Section II body, Section III move bodies →
from
findings.md. The open pass authored these specifically; lift them with light editing for voice. - Pullquote → synthesis. Should land both the structured thesis (the archetype) and the open thesis (the distinctive pattern) in one breath.
The schema is at $PERSONA/analysis/report_content.schema.json. The renderer
validates and exits non-zero on failure.
You write strings. The renderer fetches numbers via *_ref paths.
Don't round-trip arithmetic through your head.
Pre-flight checklist (also in interpret.md):
- Title is "The X Who Y" — modifier is 1–4 words, captures the user's within-archetype distinction.
- Subtitle has no numbers. Pure thesis.
- Three traits — typically two
kind: "high"+ onekind: "contrast". - Trait characterizations have no numbers; a friend could say each one out loud.
- Pullquote can be read aloud cold and captures the thesis in one breath.
- Three recognition lines — drawn from
findings.md, not invented. - Two moves — drawn from
findings.md's "Two experiments" section, cross-referenced with the structured track'sborrow_from. Each attributed to a named archetype.
Step 8 — render
"$PY" "$PERSONA/analysis/render_report.py" \
--content "$OUT_DIR/report_content.json" \
--metrics "$OUT_DIR/metrics.json" \
--out "$OUT_DIR" \
--slug persona
Validates the content JSON, resolves all *_ref paths, writes (all under
$OUT_DIR):
persona-report.html— full reportpersona-report.md— markdown versionpersona-hero.md— chat-paste summary (the inline deliverable)persona-hero-card.txt— CLI hero card with ANSIpersona-hero-card.plain.txt— same, no ANSI
If validation fails or a *_ref doesn't resolve, the renderer prints a
diff and exits non-zero. Fix the content JSON and re-run.
Step 9 — close
Open the report in the browser:
bash "$SCRIPTS/open_report.sh" "$OUT_DIR/persona-report.html"
Then paste the contents of $OUT_DIR/persona-hero.md inline in the chat.
That is the deliverable — a tight markdown summary the user can read in
place without opening anything. Then a single CTA:
Open
$OUT_DIR/persona-report.htmlfor the full read.
Stop there. No file inventory, no cat'ing the ANSI hero card, no
"want me to dive into…" follow-up question. The user can ask if they
want more.
If the user later asks "is this private?" — yes, fully. With the
subagent path, no data leaves the machine; all output stays under
$OUT_DIR. They can verify by reading $PERSONA/tagging/tag_sessions.py
and $PERSONA/analysis/compute_metrics.py.
Failure modes to watch for
- No sessions found. Wrong path, or no Claude Code history yet.
Suggest
ls ~/.claude/projects. - Few sessions tagged (< 10). Either the user has small history or
the filter dropped them. Tell
interpret.mdto flag this in the report. - Subagent annotation missing or invalid. Common in long transcripts. The assembler reports counts; re-spawn a subagent for the gaps before computing metrics. If a session is genuinely un-tag-able (e.g., transcript truncation), leave it out — the metrics will compute on what remains.
- Open-pass findings shallow. If
findings.mdreads as generic, the subagent likely under-sampled or skimmed. Re-spawn over the same ~12 transcripts and emphasize "specific session anchors required"; if the subset clustered in one project, widen it to more distinctprojectvalues from_manifest.json. - Baselines missing.
compute_metrics.pyruns anyway; the renderer drops cohort comparison bars;interpret.md's edge-case section describes how to author content JSON when comparisons are unavailable.
Legacy: API tagging (fallback)
The API-based tagger still works:
"$PY" "$PERSONA/tagging/tag_sessions.py" "$OUT_DIR/sessions_enriched.csv" \
--out "$OUT_DIR/tagged_sessions.csv" \
--limit 50 \
--model claude-opus-4-7 \
--dry-run # cost estimate first
"$PY" "$PERSONA/tagging/tag_sessions.py" "$OUT_DIR/sessions_enriched.csv" \
--out "$OUT_DIR/tagged_sessions.csv" \
--limit 50 \
--model claude-opus-4-7 \
--yes
Pin the model to match the corpus baseline (Opus 4.7). The default
in tag_sessions.py is Sonnet 4.6 for historical reasons — explicitly
pass --model claude-opus-4-7 for calibrated output.
Cost: ~$25–30 for 50 sessions on Opus 4.7 (~5× Sonnet). Requires
ANTHROPIC_API_KEY. Slightly faster than the subagent path (1–3 min)
and has a small failure mode of its own — Opus occasionally responds in
text instead of via the enforced tool call (~1 in 50 sessions in our
testing). The subagent path is the default because it's free, local,
runs without a key, and avoids the tool-enforcement edge case.
What you don't need to do
- Don't author the recognition lines from aggregates. That was the
old failure mode —
findings.mdis the source. - Don't invent baseline numbers. The CSVs in
baselines/are the source of truth. - Don't soften the framing voice. Specific over general; numbers in visualizations, characterizations in prose. Don't backslide into "you're doing great" / "you should improve X."
- Don't put numbers in prose. They go in the bars, the gauge, and the headline-stats tiles.
- Don't paste verbatim transcript quotes — even from
findings.md. Paraphrase patterns; verbatim feels like surveillance.