Claude Code plugin that reminds the session when context is getting full and automatically drafts tailored /compact <instructions> for better post-compact resumption.
SaferSkills independently audited prep-compact (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
When invoked, read the warm handoff file maintained by the Stop hook, then perform a targeted current-conversation pass for analytical fields, then emit a /compact <instructions> block the user can copy and run. Users often re-invoke manually to refresh — produce a fresh output every time.
Resolve THIS session's own handoff via the helper — never by newest-modified file.
bash "<skill-base>/resolve-handoff.sh" "$PWD", where <skill-base> is this skill's base directory (substitute the literal path from the Base directory for this skill: line you were given at load — there is no $SKILL_BASE variable; a literal $SKILL_BASE would resolve to /resolve-handoff.sh and break this) and $PWD is the current directory.HIT → the second line is the handoff file's absolute path. Read that JSON and use its extractive fields (§2), then run the §3 analytical pass.MISS → no handoff matched this session (not written yet, or you changed directories). Treat all extractive sources as empty (cumulative_files=[], in_progress=[] with status unknown, recent_task_launches=[], recent_user_requests=[]) and run §3 against the live conversation alone. Prefix the output: "Note: no handoff matched this session; surveyed from in-memory conversation."NOSID → no session id available. Same in-memory survey as MISS. Prefix: "Note: session id unavailable; surveyed from in-memory conversation."MISS.The helper binds to the invoking session by $CLAUDE_CODE_SESSION_ID and validates the handoff's stored cwd, so a sibling session's handoff is never selected. If the session id is ever absent the helper returns NOSID and the skill degrades to the in-memory survey — safe, never cross-session.
These come directly from the warm handoff, no re-survey needed:
files: ← handoff cumulative_files. You MAY reorder for relevance to the inferred next-step (spec/plan first, then code in relevance order). The set is what the handoff says; the order is editorial.state.in_progress ← handoff in_progress if in_progress_status == "known". If unknown, attempt to resolve from the in-memory conversation; default to unknown if nothing in window.state.agents ← handoff recent_task_launches, filtered/annotated by your judgment from the conversation: mark each as wait, ignore, or close (agent <id>: wait / etc.). The hook never claims to know status — you do.recent_user_requests from the handoff is your source of user intent for the analytical synthesis below. Quote verbatim where it preserves user intent.For each analytical field, scan the LAST ~30 turns of the in-memory conversation. Specific source priorities and fallbacks:
goal: (one sentence) ← synthesize from the most recent goal-shaped user message AND the most recent assistant exchange. Default unknown if neither resolves.next: (verb-anchored: edit <path>[:<symbol>] / run <command> / inspect <file> for <issue> / ask user <question> / wait for agent <id>) ← in priority order: latest TodoWrite in_progress item → last assistant action description → latest unresolved blocker. If the session is genuinely uncertain what to do next, that uncertainty belongs in decisions.blockers, and next should name the blocker that must be resolved before work resumes.decisions: decided= ← scan recent assistant messages for decision markers ("we'll", "let's", "decided to", "going with") corroborated by user acceptance signals (no objection, "yes", "go", "do it"). Quote rationale verbatim where possible. Empty decided= if no clear signal.constraints= ← scan recent user messages for imperative constraints ("must", "should not", "do not", "never", "always") OR explicit anti-patterns. Quote verbatim. Empty if none.blockers= ← unresolved review/QA findings (look for reviewer-agent / Codex outputs the user has not addressed); failing tests; pending questions waiting on user answer. Empty if all clear.state.changes= ← you MAY invoke git status --porcelain if the user has authorized Bash for that pattern. Else unknown.state.tests= ← from the latest test invocation in transcript: passing if exit 0; failing (with count if extractable) if non-zero; unknown if no test invocation in window.state.verify= ← shortest test command from the most recent test invocation. Empty if none.Default for ANY field with no direct evidence: omit per the "omit rather than fabricate" rule below. Write unknown only when silence would be ambiguous.
Only claim what is observable. Omit rather than fabricate — if you do not know whether tests are passing, say nothing about tests.
Default output: single-line, fields separated by | . Single-line eliminates the "newline after /compact" failure mode. Field semantics: §2 (extractive), §3 (analytical).
/compact goal: <one sentence> | next: <verb anchor — edit/run/inspect/ask/wait — concrete enough to execute without re-asking the user> | files: <minimum set needed to execute `next`, spec/plan first, code files after in relevance order> | decisions: decided=<key decisions with rationale>; constraints=<hard requirements + anti-patterns user stated>; blockers=<unresolved review/QA findings, failing tests, pending user answers> | state: changes=<uncommitted files>; tests=<passing/failing/unknown>; verify=<shortest rerunnable command, e.g. "bash test/run-tests.sh" — omit if none>; in_progress=<mid-implementation markers>; agents=<"agent <id>: wait|ignore|close" per running agent, or "none">CRITICAL: /compact and goal: must be on the same line, separated by a single space. A newline directly after /compact makes Claude Code fire the bare command and drop the instructions. Subslots may be omitted when empty; write none only when silence would be ambiguous. Single-line is the ONLY permitted form: the entire command is one physical line with no line breaks anywhere — never split fields onto separate lines.
Compression: preserve verbatim paths, identifiers, decisions, constraints, blockers, agent-IDs. Drop chitchat, transient tool output, and exploratory dead ends that were not acted on — but keep error text or dead ends that underpin a current blocker or decision. If length presses, reference the plan/spec file path and omit redundant file enumerations rather than inlining everything.
Output a preamble, the §4 schema with values filled in inside a single fenced code block, then a closing note. Do NOT wrap any of this in > blockquote — some terminal/UI clients copy the prefix into the paste, which breaks the slash-command parse.
/compact goal: ...)files: and resume from next:."/compact goal:, and (c) contains no newline anywhere. If any check fails, rewrite it as one line before emitting.If you used the in-memory fallback (MISS or NOSID from §1), keep the exact prefix §1 specifies for that case.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.