agentleFS
chendrizzychendrizzy/self-cycle/self-compact/SKILL.md

self-compact

Skill0 starsChanged 58 days agoMITView on GitHub

Make THIS Claude Code session compact its own context and then automatically resume its outstanding work, by arming a detached watchdog that injects /compact into the session's own cmux pane and re-prompts it once compaction finishes. Use when the user asks a session or agent to self-compact, auto-compact, compact itself, compact and keep going, or free up its own context without losing the current task.

2 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions2 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 2

    1. dfrysinger/skills★ 3same name
    2. scitex-ai/claude-code-telegrammer★ 1same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository2 other files it gives its agents.

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 1 is distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Write a continuation prompt describing the actual outstanding work, then run:

    line 14
  2. Very specific:

    Do not re-run the command, do not try a different spelling, and do not reach for cmux yourself: every successful arming types another …

    line 37
  3. Very specific:

    If it still cannot deliver, it fires a cmux notify and flashes the pane.

    line 97
  4. Very specific:

    Every press is ctrl+enter (bound to chat:sendNow in 2.1.284).

    line 132
  5. Very specific:

    Your draft is never typed over, erased, or submitted: Before typing /compact or the continuation, the watchdog reads the whole input box …

    line 145
  6. Very specific:

    Every cmux call retries 4x with backoff, because socket writes can transiently fail.

    line 154
  7. Very specific:

    Run doctor if a session came back but never got re-prompted — it re-arms any orphaned cycle whose continuation was never delivered.

    line 196

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

self-compact

Compacts your own session, then re-prompts you so the work continues instead of stalling.

Do this

Write a continuation prompt describing the actual outstanding work, then run:

ENGINE="${SELFCYCLE_HOME:-$HOME/.claude/skills/self-cycle}/selfcycle.sh"
[ -f "$ENGINE" ] || ENGINE="$HOME/.agents/skills/self-cycle/selfcycle.sh"
bash "$ENGINE" compact "<continuation prompt>"

The prompt is what future-you wakes up to, so make it specific: name the current task, the next concrete step, and anything already finished that must not be redone. A vague prompt is the main reason a resumed session dithers. Example:

bash "$ENGINE" compact "Continue the payments-service retry refactor. The test fixtures are already seeded and all 12 migrations are applied - do not re-run them. Next step: extract the backoff helper out of worker.ts into lib/retry.ts, then update its three call sites. Do not ask the user what to do next."

Then end your turn immediately. Say one sentence telling the user you armed it. Do not start new work: anything you do after this races the compaction.

Reading the result

  • SELFCYCLE_ARMED mode=compact ... — armed. The watchdog fires in ~8s and runs on its own.
  • SELFCYCLE_ABORT <reason> — nothing was armed. Tell the user the reason plainly, then STOP. Do not re-run the command, do not try a different spelling, and do not reach for cmux yourself: every successful arming types another /compact, so a retry loop is how a pane ends up being compacted over and over. Do not claim the session will compact. Common reasons: not running in a cmux pane, no working cmux binary, jq missing from PATH, an unwritable state directory, a cycle already armed for this surface, or the surface is a native (non-terminal) agent surface where keystroke injection is impossible. A forked conversation no longer aborts with "cannot locate transcript". The pane may announce the fork's new session id while the process keeps writing the original id's transcript, so the engine checks every id the pane's process has announced (and the id Claude Code exports to the shell) and uses the one whose transcript was written most recently. If it still aborts, the message lists every candidate it tried.

Nothing is refused for being too long. The prompt is never typed. It is written to a file at arm time, and what gets injected is a short fixed pointer (~290 chars) telling the session to read that file. So write a prompt of any length and any shape — it reaches the session intact.

One of those reasons is deliberate: a session that compacted in the last 5 minutes is refused. Compacting again cannot improve on the compaction it already has, and each arming costs another /compact and more lost detail. Override with SELFCYCLE_COMPACT_COOLDOWN=0 only if you genuinely need a second one.

Progress and the terminal outcome land in state/selfcycle.log next to the engine. Lines are prefixed with the first 8 characters of the surface id, because every cycle on the machine appends to that one file.

What the watchdog does

It runs detached (reparented to launchd), so it survives anything that happens to your turn.

  1. Clears the input box, types /compact, presses Enter.
  2. Waits for one of four transcript signals — not screen text:
    • a record with isCompactSummary: true → compacted
    • <local-command-stdout>Not enough messages to compact. → declined, which is a success
    • <local-command-stderr>Error during compaction: ... → failed, stop waiting immediately
    • <local-command-stdout>Compaction canceled. → you interrupted it, stop waiting immediately
  3. Sends your continuation prompt on every one of those paths, including failure, so the session is never left idle with no instruction. The one exception is a cancel: an interrupt is your decision, so instead of the continuation the session is told not to resume and to ask you how to proceed. The message names the saved continuation file, so "carry on" picks it up. /compact is never typed a second time on any path.
  4. Confirms the prompt was actually consumed, in two layers:
    • keystroke level — it proves the payload positively: after typing, the box must start with the payload's opening characters; after Enter, it must no longer contain them. It never asks "is the box empty", because on this TUI that is not observable — an empty box carries placeholder chrome and the chrome moves. One run read a lone space back; another read back Press up to edit queued messages, the hint shown once messages are queued — and since our own successful Enter is what queues a message, success painted the box with what an emptiness check scored as failure. That cost 2 duplicate /compacts and 4 duplicate prompts in one cycle. "It changed" is not enough either, in either direction: leftover text sits ahead of our payload, so the box changed but holds JUNK/compact; and any unrelated repaint after Enter looks like a successful submit. A prefix test catches residue before Enter is ever pressed, and an unreadable box is treated as a failure, never as an empty one — folding those together once fired five blind Enters at a tool-permission dialog. This positive check is possible only because the pointer design caps what is ever typed to /compact or a ~290-char pointer, so the opening characters are always on the ❯ line and the box never scrolls. A just-respawned pane replaying its transcript will accept the paste but drop the Enter, and the payload-still-there check catches that in seconds without retyping.
    • transcript level — a new user record carrying a unique nonce, then an assistant turn positioned after that nonce record. Both layers are needed: the transcript alone cannot tell our Enter from a human's, so transcript-only verification once scored an operator's manual rescue as its own success.

If it still cannot deliver, it fires a cmux notify and flashes the pane. It never fails silently.

Why it does not stall

  • No readiness guessing from chrome. It never reads footer text to decide whether the TUI is ready, because that text changes between releases. It injects, then proves the injection was consumed.
  • One exception, and it is structural: it waits for the input box to exist. A pane with no ❯ line cannot be typed into at all, so waiting for one is not counted as an injection attempt (default budget 7 minutes, SELFCYCLE_BOX_WAIT_SECS). This matters far more after a restart than after a compaction, but the same code path serves both.
  • It retypes only what provably did not land. Once the keystroke layer has seen our text enter the box and the box change back off it, the text is gone from the box and will not be typed a second time. And because the keystroke layer judges its own keystrokes and can be wrong about them, a submit that reports failure is checked against the transcript and the compaction effect before anything is retyped — two independent things must now be wrong to send a second copy. This matters because an absent transcript record has two innocent causes — the session is mid-turn, so Claude Code has queued the message and will not write it to the transcript until the turn ends; or a human deleted our injection, which is a refusal. Treating that silence as a failed injection is what once submitted seven /compact commands into one busy session. The engine now waits out a processing budget instead (default 10 minutes, SELFCYCLE_PROCESS_WAIT_SECS), then reports compact-accepted-not-run and notifies rather than typing again. The one thing that does earn a resend is a truncated arrival, because seeing a short copy on disk is evidence the payload is wrong, not an absence of evidence.
  • Injecting mid-turn does not interrupt the session and is not dropped — but the queued message is invisible to the transcript until it runs, so never read transcript silence as non-delivery.
  • A queued /compact is force-sent, never retyped. Claude Code runs a mid-turn message only when the turn ends, and two things keep a turn from ending: long tool/subagent work, and an unmet /goal, whose stop-hook re-opens the turn every time it tries to end. That one cost four consecutive compact-accepted-not-run cycles. So once /compact has provably left the box, the watchdog presses Claude Code's send-now key when either blocker is detected:

    • goal — a transcript record {"type":"attachment","attachment":{"type":"goal_status","met":false}} written after /compact was queued (transcript, not the /goal active footer) → press at once;
    • queue — /compact still has not run SELFCYCLE_SENDNOW_AFTER_SECS (default 60) after it was queued → press.

    Every press is ctrl+enter (bound to chat:sendNow in 2.1.284). Measured in a scratch pane, cmux sends it as a distinct sequence in both keyboard modes Claude Code may request (kitty \e[13;5u, modifyOtherKeys \e[27;5;13~), never as a newline. The other binding, the chord ctrl+x ctrl+s, is used only when cmux fails to send ctrl+enter, because under the kitty protocol cmux emits no bytes for it at all. A press happens only on a provably empty box with you hands-off: send-now submits whatever is in the box before flushing the queue (verified live), so if your draft is there, or the keyboard/mouse was used within SELFCYCLE_SENDNOW_IDLE_SECS (default 8, read from macOS HIDIdleTime), the press is deferred, logged once, and not counted. Presses are spaced SELFCYCLE_SENDNOW_AFTER_SECS apart, at most SELFCYCLE_SENDNOW_MAX (default 4). Send-now moves running tools to the background rather than cancelling the turn, and nothing is typed, so it can never produce a second /compact. This does interrupt the session's current turn — safe only because everything worth keeping was saved before the skill was armed. The continuation prompt is not forced: it is delivered mid-turn as an ordinary queued message.

  • Your draft is never typed over, erased, or submitted. Before typing /compact or the continuation, the watchdog reads the whole input box (the ❯ row through the bottom border, so wrapped and multi-line drafts count). It backspaces only an empty box or the leftovers of its own typing. If the box holds anything else, it notifies you and waits up to SELFCYCLE_DRAFT_WAIT_SECS (default 90) for you to send or clear it, then gives up that step rather than touch your text. An untouched draft survives compaction in place (verified live), so nothing needs restoring. Any box it cannot fully see, or hint text it does not know, counts as a draft. That fails safe. Remaining gap: reading the box and pressing a key are two separate calls, so a keystroke landing in that instant is not covered. The idle check makes it very unlikely, not impossible.
  • Every cmux call retries 4x with backoff, because socket writes can transiently fail.
  • A hard 30-minute deadline kills the watchdog, announcing itself in the log and by notify first so a death-by-cap is never mistaken for the machine sleeping; a lock stops two cycles racing.
  • abort stops the running watchdog (it does not merely free the lock — an armed refresh would otherwise respawn your pane ~8s later regardless) and retires the envelope.
  • doctor retires an envelope whose surface no longer hosts that session, instead of re-arming a watchdog that could only fail on a pane that is gone.

Embedded cmux facts (so you need not load the cmux skill)

  • cmux send --surface <id> <text> types into a pane; cmux send-key --surface <id> enter submits. Both are focus-independent — the pane does not need to be focused or the app frontmost.
  • An unknown key name fails loudly (Error: invalid_params: Unknown key, exit 1), never silently.
  • A single Enter submits a slash command even with the autocomplete popup open.
  • ctrl+u, ctrl+w, and escape do not clear Claude Code's input box; text accumulates. Only backspace clears it, which is why the engine backspaces before every submit.
  • cmux send delivers an embedded newline as ENTER, so a multi-line prompt sent to a Claude Code pane self-submits line by line. Verified with no Enter pressed at all: a 12-line paste arrived as three separate messages while the rest sat in the box. The engine therefore flattens every prompt to a single line before sending. Write your continuation prompt however you like — it gets flattened for you — but never assume newlines survive. A shell pane behaves the opposite way (newlines are literal text there), so testing injection against a shell proves nothing about a Claude pane.
  • A large single paste gets COLLAPSED, and the collapsed form is what submits. Measured: one cmux send of 1622 chars rendered as head, then an elision, then tail, with the footer offering "paste again to expand" — and a live 1726-char continuation prompt was submitted in exactly that shape, losing 72% of its body. Head and tail are also the only parts cheap verification checks (a probe of the first chars, a nonce at the end), so it passed as a clean delivery. The engine therefore sends in ~350-char chunks, which the TUI treats as ordinary typing and does not collapse, and it verifies the whole payload in the transcript rather than just the nonce. read-screen cannot help here: the elision is the rendering.
  • The claude_code status pill is not a liveness signal: it has two writers with different vocabularies and is often missing for a perfectly healthy session. Use the process table.

Recovery

bash "$ENGINE" status   # is a cycle armed? recent log lines
bash "$ENGINE" abort    # really cancel: kill the watchdog, retire state
bash "$ENGINE" doctor   # finish a cycle whose watchdog died

Run doctor if a session came back but never got re-prompted — it re-arms any orphaned cycle whose continuation was never delivered. Use the same ENGINE path as above.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.