/done — Session Wrap-Up
Save a durable record of this session into the hub (see the plugin's hub concept — resolved from JARVIS_HUB env, then ~/.jarvis-hub-path, then ~/jarvis-hub). Scale effort to session weight: light/conversational → minimal note; substantial work (commits, decisions, multi-step) → structured note. Don't fabricate. Don't pad.
Token discipline (read this first)
This skill runs at the end of every session, so cost compounds. Four rules to keep it cheap:
- No narration between tool calls. Don't write "Running command...", "Edit succeeded, continuing...", "Now committing..." — those are wasted tokens. Run tools silently. Only the final confirmation is shown to the user.
- One bash call for context. The resolver script bundles every piece of prep info you need. Don't make additional
git log,ls, orcatcalls unless the resolver output is genuinely missing something. - Skip steps when they don't apply. If the resolver printed no
dirty:lines → no commit step. If session was light → no patterns scan. Iftoday_daily_note=(none)→ no link to it. - Never full-Read
CONTEXT.mdorpatterns.md. Both can grow large over a long-lived hub. Step 4 updatesCONTEXT.mdvia a script (no read); step 5 dedups patterns viagrep(no read). If you ever must touch them directly, useoffset/limit— never load the whole file.
Workflow
1. Resolve context
bash "${CLAUDE_PLUGIN_ROOT}/skills/done/scripts/resolve-context.sh"
The output is a flat key=value block. Key fields you'll use:
hub,sessions_dir,project— where the session note goes and what the work belongs tohub_is_git—nomeans the hub isn't a git repo at all; skip the commit step entirely (see edge cases)hub_dirty— any uncommitted changes anywhere in the hubsessions_dirty,wiki_dirty,memory_dirty,other_hub_dirty— per-area dirt flagsdirty:hub:<path> <count>lines — every dirty top-level path in the hub. This is the authoritative list for what to stage; don't guess from the flags alone.hub_branch/_remote/_upstream/_ahead— push state. See step 6.wiki_recent_count— wiki files modified in the last 24h (informational only)current_branch— branch of the repo you were working in (not the hub) — note it in the session note if relevanttoday_daily_note— path to today's daily note, or(none)(most hubs won't have this — that's fine, skip the line)last_session_note,last_session_date— for continuity contexttoday_session:<path>lines — existing session notes for today (idempotency)commit:<hash> <msg>lines — commits in the hub since last session date
2. Decide session weight
- Light — purely conversational, no code changes, < 5 substantive turns, no decisions worth keeping. → 3-line note. Skip patterns.
- Substantial — code/files changed, real decisions, multi-step work. → Structured note + patterns scan.
Weight decides how much you write, never whether you commit. Step 6 is driven by the resolver's dirty: lines alone — a light session that still left patterns.md or CONTEXT.md dirty gets committed, because the alternative is drift that compounds silently.
When unsure, lean light. A thin honest note beats an inflated one.
3. Write the session note
Path: <sessions_dir>/YYYY-MM-DD-<slug>.md. Slug = 2-5 word kebab-case describing topic.
Idempotency: check the today_session: lines from the resolver. If one matches your slug → read that file, append ## Part 2 — HH:MM with new content. Different slug, same date → new file (fine). Never overwrite.
Substantial-session structure:
---
type: session
date: YYYY-MM-DD
project: <project>
status: <completed | in-progress | blocked>
tags: [session, ...]
---
# Session: YYYY-MM-DD — <Title>
**Goal:** <one sentence>
**One-line takeaway:** <single most important thing for future-you>
**Branch:** <current_branch> (only include if not main/master)
## Work done
- <commit-hash> <message> — <context if non-obvious>
## Decisions
- **<decision>** — <reasoning>
## Open threads
- <thread> — <what's blocked or pending>
## Next steps
- <concrete action>
For light sessions, drop the structured sections — frontmatter + Goal + 2-3 bullets is enough.
If today_daily_note exists, you may add a one-line **Daily note:** [link](daily-notes/YYYY-MM-DD.md) near the top. Skip if (none).
Use the commit:<hash> <msg> lines from the resolver to populate "Work done" — those are real commits since the last session note.
4. Update CONTEXT.md
Do NOT Read CONTEXT.md — reading the whole file every run is the cost this step exists to avoid on a long-lived hub. Pipe a one-line entry to the updater instead; it prepends it, keeps the newest 12, and auto-archives the overflow — the model never loads the file:
python3 "${CLAUDE_PLUGIN_ROOT}/skills/done/scripts/update-context.py" <<'ENTRY'
- Date: YYYY-MM-DD (topic) — one-line summary of what happened. Open: <blocker if any>. Note: `sessions/YYYY-MM-DD-<slug>.md`
ENTRY
You already have the content — you just wrote the session note. One line, newest-first. The script owns the ## Recent Sessions region between the SESSIONS:START/END markers and creates CONTEXT.md from a minimal scaffold if it doesn't exist yet — the ## Active Projects / ## Open Threads / ## Recent Decisions sections above that region are untouched by this script.
Re-running /done in the same day is safe: the script matches on the sessions/…md path in your entry and replaces the existing line instead of stacking a duplicate (it reports replaced=1). So keep the note path in the entry — it's what makes the update idempotent.
Exit code 2 means you piped an empty entry, which means step 4 didn't actually happen — fix the entry and rerun.
State sections (Active Projects / Open Threads) update separately and only when something genuinely changed: edit in place with a targeted offset/limit Read around that section, never a full-file Read.
5. Patterns (substantial sessions only)
Only if a real lesson surfaced — user corrected you, non-obvious tooling discovery, failure mode worth remembering. Before appending, dedup: grep patterns.md for similar phrasing — if the same lesson is already there, skip.
grep -i "<key-phrase>" "<hub>/patterns.md" 2>/dev/null
If patterns.md doesn't exist yet, don't create it from /done — that's a job for a memory-consolidation pass, not this skill. Skip step 5 silently in that case.
If no match, append under ## YYYY-MM-DD heading:
- **Pattern**: <one-line>. **Why it matters**: <one-line>. **Trigger**: <when this applies>.
If nothing genuinely new, skip step 5 entirely. Manufactured patterns silently bias future decisions — worse than missing ones.
5.5. Wiki auto-update
The contract: after /done, the hub's wiki reflects this session's work. /done doesn't hand the work back to the user; it detects what the wiki owes and writes it automatically, then reports what changed.
Runs after the session note is written, BEFORE the commit in step 6.
Step 5.5a — detect. Run the freshness detector (pass nothing; it auto-finds this session's transcript):
bash "${CLAUDE_PLUGIN_ROOT}/skills/done/scripts/wiki-check.sh"
Parse the WIKI_* block from stdout:
WIKI_VERDICT—update_owed|up_to_date|skippedWIKI_REASON— one-line explanation of the verdict (quote it if you report "skipped")WIKI_PROJECT— the project (hub subdirectory) the work touchedWIKI_FILES— comma-separated basenames of the non-wiki files edited this session (targeting hint)WIKI_EDITS/WIKI_DISCOVERY— counts (real file edits / discovery actions)WIKI_TARGET— suggested article path skeleton
The detector locates this session's transcript itself: $PWD with /, _ and spaces flattened to - under ~/.claude/projects/, newest .jsonl. It fails open — any problem finding or parsing the transcript yields skipped, never a blocked /done.
One blind spot to cover yourself: it only classifies paths inside the hub. A session that spent its time editing this plugin's own skills/hooks/settings returns up_to_date because nothing hub-shaped was touched — if that's the kind of work this session actually did and it's worth remembering, write an article regardless of the verdict.
Trigger model: any changed files. If the session edited/created real files in a hub-tracked project (or did ≥3 discovery actions on one) and did NOT already write a wiki article, WIKI_VERDICT=update_owed.
up_to_date→ nothing owed. Skip to step 6.skipped→ escape flag was set or transcript missing. Note "wiki-check skipped" in the confirmation and skip to step 6.update_owed→ continue to 5.5b. Do not skip. Do not ask the user first — if the user prefers to review before writing, that's a one-time setting, not a per-session question.
Step 5.5b — write the wiki (automatic).
Articles live at <hub>/wiki/<project>/<topic>/<article>.md, indexed by <hub>/wiki/<project>/index.md. See the wiki-article skill for the template and the anti-fabrication guard.
- Look at what changed. The session note you just wrote (step 3) + the
WIKI_FILEShint + the work you actually did this session tell you which component/concept needs a wiki article. - Decide update vs create. List the project's existing articles:
ls "<hub>/wiki/<project>"/**/*.md 2>/dev/null- If an existing article already covers this component → Read it and update in place (refresh
How it works,Code references, bumplast-updated, add new failure modes). Don't create a duplicate. - If nothing covers it → create using the
wiki-articleskill's template.
- If an existing article already covers this component → Read it and update in place (refresh
- Update the index. Add or refresh the
[[topic/article|Title]]line in<hub>/wiki/<project>/index.mdso the new/changed article is discoverable. - Multiple distinct components touched → write/update one article each (don't cram unrelated work into one).
Anti-fabrication guard (hard rule). The wiki must never contain invented content. If the work was too shallow to write ≥200 honest words (1–2 file reads, a one-line tweak, no real structural understanding gained), do NOT manufacture an article. Instead append a 5-line "what's missing for a wiki article" stub to <hub>/wiki/<project>/index.md naming the component and where the next session should start. A thin honest stub beats a padded fake article. Note this choice in the confirmation.
Manual opt-out still exists: passing /done --skip-wiki-check sets DONE_SKIP_WIKI_CHECK=1 in front of the detector call (or touch /tmp/done-skip-wiki) so it returns skipped.
The articles you just wrote are uncommitted; step 6 re-reads git state at commit time and picks them up (the resolver's wiki_dirty from step 1 predates them — don't read it as "nothing to commit"). Record which articles you wrote/updated; you'll list them in the confirmation and in the session note's "Work done".
6. Commit the hub (only what's dirty)
The point of this step is that nothing you just wrote stays unversioned. Always run it when there's anything to commit — hub_is_git=no from step 1 is no longer a reason to skip: commit-hub.sh initializes the hub as a git repo on its own the first time it has something real to stage (see the edge cases entry). It only reports nothing:hub when there is genuinely no dirt.
One call does the whole step — staging, the push decision:
bash "${CLAUDE_PLUGIN_ROOT}/skills/done/scripts/commit-hub.sh" --slug <the-slug-you-used>
Add --dry-run first if you want to see the file list without committing.
It re-reads git state at commit time rather than trusting the step-1 snapshot — which matters because everything you wrote in steps 3–5.5 came into existence after the resolver ran. Staging by hand from the stale dirty: lines is exactly how freshly-written wiki articles used to get left behind.
Parse its output for the confirmation:
| Line | Meaning |
|---|---|
initialized:hub <path> |
this was the hub's first commit ever — it just became a git repo |
cleaned:hub <files> |
one-time placeholder removal fired this run (see below) — name the files in the confirmation |
committed:hub <hash> <n> files |
landed |
nothing:hub |
clean, nothing owed |
pushed:hub <branch> -> <remote> |
synced |
nopush:hub <reason> |
deliberately not pushed — report the reason verbatim |
error:hub <msg> |
say so plainly in the confirmation; don't retry, don't force |
What it guarantees, so you don't have to re-verify:
- Allowlist only —
sessions wiki patterns.md conversation-log.md CONTEXT.md daily-notes ideas research learning. Any file that looks hand-curated (MEMORY.md, anyuser_*/project_*/feedback_*/reference_*memory file) can never be staged by this script — those are the user's own edit-in-progress, same reasoning as never touchingCLAUDE.mdin a code repo. - One narrow, deliberate exception to the line above: the very first time a run has real content to commit, it also retires
memory-template's seededexample-*.mdplaceholders (example-feedback-rule.md,example-project-note.md,example-user-fact.md) and their index lines inMEMORY.md— but only files that still contain the literal placeholder marker text, so a file you kept and genuinely repurposed under that name is never touched. Rationale: those three files exist only to show the memory format; shipping forever, they'd get injected into every session's context alongside your real memory. "The hub's first real commit" is the natural signal that you're actually using it now. In practice this fires once, ever, per hub. - Initializes the hub as a git repo on first use if it wasn't one already, instead of silently doing nothing — a hub with no version history was the old failure mode (see edge cases).
- Checks git identity before committing. If
user.name/user.emailwere never set (local or global), it reportserror:hub git identity not set — run: git config --global user.name "Your Name" && git config --global user.email "you@example.com" ...— surface that verbatim; there is nothing to retry until the user runs it. - Refuses to commit mid-rebase/merge — reports
error:instead of burying an unfinished operation. - Never auto-pushes — the hub holds personal memory by definition, so pushing requires an explicit
JARVIS_HUB_PUSH=1opt-in even when a remote and upstream exist. See the script's own comment for the reasoning.
conversation-log.md, if present, is typically owned by a scheduled memory-consolidation pass, not /done. Two writers on a rolling-window file produce duplicate entries and a corrupt boundary. /done commits whatever consolidation left there; it doesn't author it.
git push from a code repo is a separate, higher-stakes operation than this — this exception covers the hub only, and only with the explicit opt-in above.
7. Confirm
Short. Skip lines that don't apply.
Saved.
Session: sessions/YYYY-MM-DD-<slug>.md
Wiki: <article(s) written/updated> / up to date / stub added / skipped
Patterns: +N / nothing added
Context: updated
Hub: <N files> committed / clean
Commit: <hash> / no changes
Push: <branch> -> <remote> / no remote / skipped — <reason>
The Wiki line is required. State exactly what happened: which articles you wrote or updated (by path), or "up to date" if nothing was owed, or "stub added" if the anti-fabrication guard fired, or "skipped" if the detector was skipped.
If something failed, say so explicitly: Missed <X> because <reason>.
Edge cases (load only when hit)
For unusual cases — detached HEAD, no git repo, write errors, hub-not-found, conflicting today files — read references/edge-cases.md. Don't preload it.
What this does NOT do
- Push a hub with no upstream, or push without the explicit
JARVIS_HUB_PUSH=1opt-in (see step 6). - Modify or stage
MEMORY.mdor any hand-curated memory file. - Write
conversation-log.md(typically owned by a memory-consolidation pass, if the user has one configured). - Send anything to a chat bridge, Telegram, or any other notification channel — this skill reports into the session only.
- Close tasks in daily notes (separate concern).
- Auto-trigger — only on explicit closing signal.