end

skill Wrap up the session safely: gate on uncommitted/unpushed work and missing journal, refuse to end if anything is unsaved, write the closing summary, then exit the harness when the session was light (st skills/end View on GitHub

/end — wrap up, or refuse to

The skill is a gate, then a closeout, then (maybe) an exit. It exists so "I'm done" is a checked claim, not a feeling. Three rules:

  1. Refuse to end with unsaved work. Uncommitted files you touched, unpushed commits, dirty worktrees, or a missing journal entry → you are not done.
  2. Closeout is short and stands alone. The last message must make sense to someone who reads only it.
  3. Exit only when it's cheap to come back. Light session → exit the harness. Substantial session → stay alive so the human can review before the context is gone. (--exit / --stay override.)

Arguments

$ARGUMENTS — optional flags:

Flag Effect
(none) check → closeout → exit iff CLEAN_LIGHT
--exit check → closeout → exit even if CLEAN_SUBSTANTIAL
--stay check → closeout → never exit
--dry-run run the check and report; change nothing, exit nothing

Step 1 — Run the gate (always)

# SKILL_DIR: $CLAUDE_SKILL_DIR in Claude Code; otherwise the directory of this
# SKILL.md (e.g. gptme-contrib/skills/end or ~/.codex/skills/end).
python3 "$SKILL_DIR/scripts/end-check.py"

It reports, per check, ✅ ok / ℹ️ info / ⚠️ warn / ❌ block, and ends with one of:

Verdict Meaning What you do
BLOCKED unsaved work attributed to this session Refuse. Go to Step 2.
CLEAN_LIGHT ≤2 commits, ≤10 files, no PRs/worktrees pending Closeout → exit (Step 3+4)
CLEAN_SUBSTANTIAL more than that, or open PRs / warnings Closeout → hand-off → stay alive

"This session" = files/commits newer than the harness process start (from /proc), or commits carrying this session's id. Other sessions' dirt in a shared worktree is listed as info, not a blocker. Pass --since 2h if the process-start heuristic is wrong (e.g. a resumed session).

Shared worktree with parallel sessions (Bob's brain repo): siblings share your time window, so declare your footprint — the repo paths you touched plus the absolute paths of any linked worktrees you created. Only dirt/commits under those paths and only declared worktrees can block; the rest is info:

python3 "$SKILL_DIR/scripts/end-check.py" \
  --paths journal/2026-08-22/my-session.md scripts/foo.py /tmp/worktrees/my-feature

A parallel session's fresh worktree can still show as a blocker; if git -C <wt> log -1 proves it isn't yours, say so in the closeout and treat it as info — don't touch it.

Also do the judgment checks the script can't:

Pre-close decision review (memory)

The review above is a judgment check, not a new executable. When a memory CLI is available (gptme: gptme memory), use it to inspect related entries and persist missing settled items:

gptme memory roots        # resolved roots, nearest layer first
gptme memory recall "topic"   # possible coverage — a hit is not proof of capture
gptme memory show <name>  # read one entry / its source path
gptme memory save <name> "description" --type general < body.md  # persist a missing durable decision (body from stdin or --body-file)

A workspace without that CLI performs the same review through its native file reader/writer. Keep this generic skill independent of any one agent's paths or queue; the memory commands above are illustrative of the shipped CLI, not a required dependency.

Step 2 — If BLOCKED: say so, then fix

Open with the refusal, verbatim shape:

Not ending — N blocker(s): <summary list from the check>

Then fix what is fixable without the human:

git commit <explicit paths> -m "..."          # brain repo: git-safe-commit --scope-only <paths> -m "..."
git push                                      # brain repo: git-safe-push-master
# journal entry for the work (Bob: journal/YYYY-MM-DD/<session-file>.md, append-only)

Re-run the check. If a blocker needs the human (a decision, a credential, a conflict you shouldn't resolve alone), stay alive and ask — one question, with the default you'd pick. Never "wrap up anyway".

Step 3 — Closeout message (≤ 12 lines)

## Session closeout
**Shipped**: <commits/PRs with links — or "nothing shipped">
**Verified**: <how you know it works — tests run, symptom re-checked — or "not verified: …">
**Not done / deferred**: <explicitly, so nobody assumes it happened>
**Next**: <the one action that unblocks continuation>
**Closing**: exiting now | staying alive for review (say `/end --exit` to close)

Bob-specific: the closeout goes into the journal file too if it isn't there already (append-only), and the journal commit must be pushed before Step 4 — re-run the check once more after writing it.

Step 4 — Exit decision

Verdict Default --exit --stay
CLEAN_LIGHT exit exit stay
CLEAN_SUBSTANTIAL stay exit stay
BLOCKED stay (refused) stay (refused) stay

To exit, the closeout message must already be sent; then, as the final tool call:

python3 "$SKILL_DIR/scripts/end-exit.py"        # SIGTERM to the harness after 3s
python3 "$SKILL_DIR/scripts/end-exit.py" --dry-run   # show the target first if unsure

It finds the interactive harness process (Claude Code via CLAUDE_PID; gptme and Codex via the /proc parent chain) and schedules the signal from a detached helper so your tool call returns first. Non-interactive runs (claude -p, gptme -n, codex exec) are left alone — they end with the turn. gptme caveat: SIGTERM skips SESSION_END hooks; when a human is present prefer telling them to type /exit.

Rationalizations this skill exists to block

Thought Reality
"It's just a journal file, I'll commit it next session." Next session is a different process with no memory of this one. Commit now.
"The other session's changes are in the way, I'll skip the push." Shared worktree: use git-safe-push-master; your commits are still yours to land.
"CI is still running, I'll call it shipped." Say "not verified: CI pending" in the closeout. Unverified ≠ done.
"The session was big, so exiting saves the human time." The opposite: big sessions are exactly the ones a human wants to inspect while the context still exists.
"Exit is just kill, no big deal." It's irreversible for this context. The closeout must already be on screen and in the journal.

Per-runtime install

Run once per machine (idempotent):

bash <this-dir>/install.sh          # symlinks into ~/.claude/skills/end and ~/.codex/skills/end
bash <this-dir>/install.sh --project   # also .claude/skills/end in the current repo