| name | where-am-i |
| description | Build a glanceable quest-map of a long session — pure user-intent roots, ground-truth check-marks, [from → to] ranges, PR/docket overlays, a metadata header. A shared recap-on-steroids built by a fresh unbiased agent; light at the top, full detail one cat away. On-demand orientation for when the arc of a long, compacted, multi-thread session is lost and you (and the user) need to decide what to tackle next. |
| argument-hint | [session-id prefix — defaults to the current session] |
| user-invocable | true |
| disable-model-invocation | true |
| allowed-tools | ["Bash(\"$(realpath \"${CLAUDE_SKILL_DIR}/gather-where-am-i.py\")\":*)","Bash(.claude/skills/where-am-i/hoist-where-am-i.py:*)"] |
Where Am I — orient a long session as a quest-map
A long session drifts: threads open, sub-quests get pushed and never popped, compactions blur the arc.
This builds a shared map both you and the user read to decide what's next — what the user drove,
and how far each thread got. Intent is the tree; commits and PRs are delivery laid over it. Light at
the top; full detail is one cat away (progressive disclosure — completed threads needn't be
reloaded, so the session tends to reach its context ceiling slower).
Gathered (deterministic)
!"$(realpath "${CLAUDE_SKILL_DIR}/gather-where-am-i.py")" $ARGUMENTS
The gather wrote three inputs (paths above): user-intent-spine.txt (the user's verbatim directives —
normal turns, queued mid-work, and fork-direct, via the shared cc_lib.transcript_spine filter — the
load-bearing record of intent, which a model summary rounds off and the native recap loses across
compactions), gh-ground-truth.txt (PRs merged in the session window + open, recent commits,
worktrees), and session-metadata.txt (compactions, subagents, most-used skills).
Build it — run the build workflow
Do not build the map from your own working memory — that drift is exactly what this skill exists to
escape. The build is a committed Workflow (build-where-am-i.js here) so its control flow runs the same way
every time and resumes cleanly if an agent dies mid-run. Invoke it with the gather's staging dir:
Workflow({ scriptPath: '.claude/skills/where-am-i/build-where-am-i.js', args: { gatherDir: '{dir}' } })
{dir} is the STAGING DIR the gather printed (an ephemeral temp dir) — it holds user-intent-spine.txt / gh-ground-truth.txt
/ session-metadata.txt and receives quest-map.md + nodes/. The workflow runs roots → nodes → top,
deliberately in that order so the map is a roll-up of ground-truthed nodes, never a frozen first guess:
- Roots — one agent reads the spine and returns the user-driven thread list. Pure intent, no
ground-truthing yet; it's the only thing that must exist before the fan-out.
- Nodes (parallel) — one agent per root authoritatively ground-truths it (spine + truth +
git log
directly, not a broad guess), writes nodes/{slug}.md, and returns its facts (landed?, PRs, span).
- Quest-map — assembled last from those node facts +
session-metadata.txt, then self-validated
against validate-quest-map.py until valid ✓. The structural validator can't catch a wrong PR label, so
the nodes are the semantic check — that's why they run first.
The heavy gathering happens in those throwaway build contexts, so the main session needn't carry the bulk; later,
opening a node is a cat you choose to make, not a recompute. If a run dies (an overload mid-fan-out), resume it — finished
agents return from cache, only the tail re-runs. The map and its nodes are machine-local artifacts — full
fidelity, never committed.
Render spec (the agent follows this exactly)
Node rule — PURE user-intent. A node exists ONLY if the USER drove it: a goal they stated, or
something they explicitly reacted to ("let's do that", "great — build on it"). NOT system notifications,
NOT other-sessions merely discovered, NOT AI-incidental work or tooling — those are HOW, not goals.
Commits / PR-numbers / assistant-work are NEVER nodes; they only decide a node's check-mark. Multiple
roots for independent threads, numbered [1]..[N] — the handle for paging a node in later; sub-bullets
for the sub-quests under each.
Header. YAML frontmatter first (machine-readable: session, span, volume, skills, roots, provenance),
then a prose header line:
WHERE AM I — session {id} "{arc-title}" · [{from} → {to}] ~{span} · {n} directives · {compactions} compactions · {subagents} subagents · most-used: {top skills}
The quoted title is a synthesized arc-title — how you'd name the campaign to a colleague
("bootstrap → ideal-state crusade → where-am-i"), not the raw CC session label (that stays in
frontmatter session.title). {n} directives is the spine's deduped directive count (normal +
queued + fork-direct) — label it directives, never msgs (it undercounts raw message records by
design). Then one honest "the shape of it" sentence, and one act next: line — the single
first-unfinished step (the freshest unblocked open root), not a menu.
Frontmatter counts (roots.total / landed / open) must match the rendered tree exactly —
roots.landed equals the number of ✓-marked roots, and total = landed + open. session.id is
the full session UUID (the gather writes it to session-metadata.txt, with the title, machine, and
directive count) — the artifact's traceable identity back to its transcript, not the short prefix the
prose header line may abbreviate to. The bundled validate-quest-map.py
enforces this structure and is the post-run conformance gate.
Check-marks. ✓ = landed (verified against truth); the absence of a mark = open. No other emojis.
A root is marked ✓ on its own line when its goal landed; it stays unmarked while a sub-quest is open.
Date ranges — [from → to] (square brackets, the definitive notation), computed from spine
timestamps (already Pacific — render as-is, never re-interpret as UTC). Per item
[first-touched → last-touched]; for an open/abandoned item the end = when we lost it (last
activity). Carry date + time on both endpoints, uniformly — M/D H:MMam/pm, no year, :00
dropped on whole hours (3pm). Uniformity is the point: no reader should wonder why one item shows
a time and the next doesn't, and every to time then reads coldness at a glance. One prune keeps it
dense: when both endpoints fall on the same day, write the date once — [4/28 1:59am → 7:06am]
— a same-day range then doubles as an effort signal.
PR / docket overlays — never structural. Below the tree: a PR overlay where a PR spanning multiple
roots floats on its own line above the roots it served, and a PR within one root attaches to that
root; then a docket overlay mapping each entry seeded this session to the root it backs. Items never
bend to fit PR or docket boundaries. Each overlay (and the footer) opens with a — {label} — marker
line, so the tree ends at the first — marker — where the validator stops counting roots.
Provenance. If a thread entered from elsewhere (a git pull of another session's work, a discovered
session the user then built on), note its origin (session, machine, still-alive?). It counts as a node
only if the user reacted to it.
Footer. End with — open parent quests never popped back up to —: the dangling frames, sorted
oldest-first by their start date. Each line carries its coldness age (— cold 7w, from its
last-touched to the map's to date; days/hours when fresher) and, where the spine shows it, whether
the user parked it ("we'll do it later") or it silently dropped (a pivot, a compaction
boundary) — parked and dropped want different re-commit decisions.
Conventions. Arrows are → / ←, never ASCII ->. File paths use {squiggly} braces, never
<angle> (angle brackets are for CLI help text only). Plain ASCII tree, glanceable in seconds. Top
level only — the per-node detail lives in nodes/{slug}.md, never inlined here.
Hoist — promote to the browsable store
The build wrote to an ephemeral staging dir (off ~/.claude — skills don't litter Anthropic's session
dir). Promote it to the long-lived, gitignored, browsable store, passing the staging dir + session id the
gather printed:
.claude/skills/where-am-i/hoist-where-am-i.py {staging-dir} {session-id}
It resolves {main-repo}/claude-sessions/{id8}/where-am-i/, backs the prior up as where-am-i.prev — the
left operand for the analyze step below (one level is all a since-last-look delta needs) — then atomically
swaps the new copy in. It prints the store path you'll cat next, and whether a prior was backed up (which
gates the analyze step).
Analyze the delta (re-runs only)
If the hoist reports a prior was backed up (where-am-i.prev exists), the payoff of a re-run isn't the
rebuilt map — you mostly hold that already — it's what changed since the last one. The build is
deliberately blind to the prior (it reconstructs fresh from the transcript so it can't anchor to a stale
map), so the comparison is a separate step — and a semantic one: node slugs are rewritten every
build, so a slug set-diff is pure noise (it would flag every renamed node as removed + re-added).
Spawn one fresh subagent — it reads the two maps from disk, never from your working memory:
Agent({
description: 'where-am-i delta',
subagent_type: 'general-purpose',
prompt: `Compare two quest-maps of the SAME session and report what changed between them.
New: {store}/quest-map.md (+ {store}/nodes/)
Prior: {store-root}/where-am-i.prev/quest-map.md (older builds name it top.md) (+ .../nodes/)
Match roots by MEANING — never by slug or number (both are rewritten each build). Sort every change
into two kinds and LEAD with the first:
1. SESSION changes — what actually moved in the work since the prior build: PRs that merged,
genuinely new threads, a status that truly flipped. These are the steer; lead with them and with
what's still OPEN to act on next. If nothing in the work really moved, say that plainly first.
2. MAP changes — build-judgment only: a re-scoped checkmark, a relabeled/corrected PR overlay, a
root split or renamed slug. These say the map got better/worse, not that the work progressed.
Then SELF-GRADE the build under a "Build feedback" heading — two kinds, both grounded in what THIS run
revealed (evidence, not a wishlist):
- REGRESSION / defect — the new build dropped or mislabeled what the prior had right, or a flaw the
log keeps showing: what's wrong, which script/brief to fix. A prior ✓-landed root with NO
meaning-successor in the new map is always the lead regression (a dropped landed root).
- IMPROVEMENT toward ideal state — a concrete capability or quality the build could gain, shown by
this run (e.g., it hand-corrected the same PR label twice → the build should cross-check labels
against git). Lead toward better, not only away from broken.
Then PREPEND a dated section to {store-root}/where-am-i-analysis.md (create if absent; newest on top;
head it with the new map's span 'to' date + a one-line headline naming the real movement). Headline
first, supporting detail below — a steering delta, no filler. Return the section you wrote.`,
})
{store} is the hoisted path; {store-root} its parent (claude-sessions/{id8}/). The subagent reads the
bulk; only its short delta returns here, so the main session stays light — same discipline as the build.
The maps rotate 1-deep, but where-am-i-analysis.md accumulates: the deltas are the durable record,
the old maps just scaffolding. On a first build (no prior), skip this — there's nothing to compare yet.
Contribute back — build self-grading. If the analysis returns a Build feedback item — a fixable
regression OR a concrete improvement toward ideal state, grounded in what the run revealed — surface it and
ask the user whether to do it now in a quick, focused PR (AskUserQuestion). The analysis is the skill
grading itself; worth a small PR while the context is hot — not a ticket, not a memory. Regressions are the
easy half to codify; the better half is the improvement that moves the build toward ideal state. The user
decides; nothing is auto-fixed, auto-filed, or auto-remembered.
Present
cat the hoisted quest-map.md (the store path the hoist printed) and show it verbatim. It IS the
artifact — do not regenerate, summarize, or paraphrase it; you and the user read the same file. On a
re-run, also show the delta the analyze step returned (or cat the top section of
where-am-i-analysis.md) — on a long, evolving session, that "what changed since last look" is the steer.
When the user picks a thread to act on, cat its pre-gathered nodes/{slug}.md — the detail was
computed once at build time, so paging it in is a file read, not a re-search, and you page in just that
node, not the rest of the session.