/stack-it — Persistent Work Stack
You manage a LIFO stack of work snapshots. When the user discovers a bug or a refactor
mid-plan, they push the remaining work onto the stack, handle the interruption, then pop
to resume — often in a different conversation, where none of the original context
survives.
That last part is the whole design constraint. A frame is read by a session that knows
nothing: it did not watch the work happen, cannot see the old conversation, and has no
memory of what was half-finished. Write every frame for that reader. If a detail lives
only in the current conversation, it is exactly the detail that must go into the file.
Frames persist as files in .claude/plans/stack/, relative to the project root, so they
survive across conversations and stay scoped to the project.
Subcommands
Parse the argument string to determine the subcommand. Default to push when there is
active work to capture, show when there is not.
| Argument |
Action |
(empty) or push |
Push the current plan and progress onto the stack |
pop |
Restore the top frame and resume from it |
show |
Display the top frame without popping |
list |
List all frames with summaries |
drop |
Discard the top frame |
Push
Capture the current state of the work as a stack frame.
Where the state comes from
Two sources, in order of preference:
- An active plan file. If the work came from plan mode, a plan file exists — the
path was named when plan mode started, typically under
~/.claude/plans/. Read it and
copy its content into the frame verbatim. Copy rather than link: plan files are
named per session and get reused or garbage-collected, so a frame holding only a path
will resolve to the wrong plan, or to nothing, exactly when it matters.
- The conversation itself. If the work was never planned formally, reconstruct the
step list from what has actually been done and agreed in this conversation.
Steps
Locate the work. Identify the active plan file, or reconstruct the step list from
the conversation. If there is genuinely no in-flight work, say so and stop rather than
writing an empty frame.
Ask why (only if not given as an argument) — one line, becomes the reason.
Establish progress honestly. For each step, mark it done, not started, or in
progress. For anything in progress, ask the user what was actually finished and what
is half-done. Do not infer this from the plan; the plan says what was intended, and
the gap between that and what exists on disk is the single most expensive thing for a
fresh session to rediscover.
Sweep for loose ends. Check, and record what you find:
- uncommitted changes in every repository touched, not just the current one
(
git status --short in each)
- background processes, monitors, or long-running jobs still live
- anything left somewhere volatile — a scratchpad,
/tmp, a stopped container, a
branch that exists only locally
The test is "would a fresh session with only the repo and this frame find this?" If
not, it goes in the file.
Determine the frame number — glob .claude/plans/stack/*.md, take the highest
number and add 1, or start at 001.
Write the frame to .claude/plans/stack/{NNN}.md. Get the timestamp from
Bash("date -u +%Y-%m-%dT%H:%M:%SZ"):
---
frame: {N}
stacked_at: {ISO timestamp}
reason: "{one line}"
project_dir: {absolute cwd}
plan_source: {original plan file path, or "conversation" if there was none}
---
## Why this was stacked
{the reason, expanded to a sentence or two if the user gave more}
## Plan
{verbatim content of the plan file, or the reconstructed step list}
## Progress at the time of stacking
- [x] {step finished}
- [ ] {step not started}
- [~] {step in progress} — {what is done, what is half-done, what to check first}
## Loose ends
{uncommitted work per repository, live background processes, volatile state —
or "none" if the sweep in step 4 genuinely found nothing}
- Report.
Stacked frame #{frame} — "{reason}"
{N} steps: {done} done, {open} open
Loose ends: {count, or "none"}
Use /stack-it pop to resume.
Pop
Restore the top frame and resume the work.
Steps
Find the top frame — glob .claude/plans/stack/*.md, take the highest-numbered
file. If none exist, tell the user the stack is empty and stop.
Read it — parse the frontmatter and all four sections.
Check the project — if project_dir differs from the current working directory,
warn: "This frame was stacked in {project_dir}, you are in {cwd}. Continue?" Warn
only; the user may have moved the repo or be working from a worktree.
Surface loose ends first. Before restoring anything, report what the frame
recorded as outstanding, and verify it still holds — uncommitted changes may have been
committed since, background processes are certainly dead. Resuming work on top of
unreconciled state is how a stacked frame turns into a merge conflict.
Show the summary — reason, step list with progress, so the user can confirm or
/stack-it drop instead.
Restore the plan. Write the frame's ## Plan section back to a plan file so it is
durable again rather than living only in this conversation, and note the path. Carry
the progress markers across: a step marked [~] resumes with its progress note
attached, phrased so the next reader knows where to pick up.
Delete the frame file — the work is live again; leaving the frame would let a
later pop resurrect a stale copy.
State what happens next — the first open step, and anything from the loose-end
reconciliation that has to be handled before it.
Show
Peek at the top frame without popping it.
- Find and read the highest-numbered frame file.
- Display the reason, timestamp, project directory, step list with progress, and loose
ends.
- Remind the user:
/stack-it pop to resume, /stack-it drop to discard.
List
Show all frames with one-line summaries.
- Glob
.claude/plans/stack/*.md.
- For each file, sorted by frame number descending so the top of the stack reads first,
display the frontmatter summary:
Stack (3 frames):
#3 [top] "Found login bug" — 5 steps, 2 open (2026-03-25, ~/Projects/api-service)
#2 "Refactor auth module" — 3 steps, 3 open (2026-03-24, ~/Projects/web-app)
#1 "Initial pipeline work" — 7 steps, 1 open (2026-03-23, ~/Projects/api-service)
Drop
Discard the top frame without resuming.
- Find the highest-numbered frame file.
- Show what is being dropped — reason, open step count, and any loose ends it
recorded. Dropping a frame discards the only record that those exist, so the user
should see them before agreeing.
- Delete the file.
- Confirm: "Dropped frame #{N}. Stack now has {remaining} frames."
1---2name: stack-it3description: Manage a persistent stack of paused work — plans, progress, and loose ends — so it can be resumed in a later conversation. Use when the user says /stack-it, or when they want to pause current work to handle an interruption (bug fix, refactoring) and come back later. Supports push, pop, show, list, and drop subcommands. Also trigger when the user says things like "stack this", "save this for later", "park this work", or "resume stacked work".4---56# /stack-it — Persistent Work Stack78You manage a LIFO stack of work snapshots. When the user discovers a bug or a refactor9mid-plan, they push the remaining work onto the stack, handle the interruption, then pop10to resume — often in a different conversation, where none of the original context11survives.1213That last part is the whole design constraint. A frame is read by a session that knows14nothing: it did not watch the work happen, cannot see the old conversation, and has no15memory of what was half-finished. Write every frame for that reader. If a detail lives16only in the current conversation, it is exactly the detail that must go into the file.1718Frames persist as files in `.claude/plans/stack/`, relative to the project root, so they19survive across conversations and stay scoped to the project.2021## Subcommands2223Parse the argument string to determine the subcommand. Default to `push` when there is24active work to capture, `show` when there is not.2526| Argument | Action |27|----------|--------|28| _(empty)_ or `push` | Push the current plan and progress onto the stack |29| `pop` | Restore the top frame and resume from it |30| `show` | Display the top frame without popping |31| `list` | List all frames with summaries |32| `drop` | Discard the top frame |3334---3536## Push3738Capture the current state of the work as a stack frame.3940### Where the state comes from4142Two sources, in order of preference:43441. **An active plan file.** If the work came from plan mode, a plan file exists — the45 path was named when plan mode started, typically under `~/.claude/plans/`. Read it and46 copy its content into the frame **verbatim**. Copy rather than link: plan files are47 named per session and get reused or garbage-collected, so a frame holding only a path48 will resolve to the wrong plan, or to nothing, exactly when it matters.492. **The conversation itself.** If the work was never planned formally, reconstruct the50 step list from what has actually been done and agreed in this conversation.5152### Steps53541. **Locate the work.** Identify the active plan file, or reconstruct the step list from55 the conversation. If there is genuinely no in-flight work, say so and stop rather than56 writing an empty frame.57582. **Ask why** (only if not given as an argument) — one line, becomes the `reason`.59603. **Establish progress honestly.** For each step, mark it done, not started, or in61 progress. For anything in progress, ask the user what was actually finished and what62 is half-done. Do not infer this from the plan; the plan says what was *intended*, and63 the gap between that and what exists on disk is the single most expensive thing for a64 fresh session to rediscover.65664. **Sweep for loose ends.** Check, and record what you find:67 - uncommitted changes in **every** repository touched, not just the current one68 (`git status --short` in each)69 - background processes, monitors, or long-running jobs still live70 - anything left somewhere volatile — a scratchpad, `/tmp`, a stopped container, a71 branch that exists only locally7273 The test is "would a fresh session with only the repo and this frame find this?" If74 not, it goes in the file.75765. **Determine the frame number** — glob `.claude/plans/stack/*.md`, take the highest77 number and add 1, or start at `001`.78796. **Write the frame** to `.claude/plans/stack/{NNN}.md`. Get the timestamp from80 `Bash("date -u +%Y-%m-%dT%H:%M:%SZ")`:8182```markdown83---84frame: {N}85stacked_at: {ISO timestamp}86reason: "{one line}"87project_dir: {absolute cwd}88plan_source: {original plan file path, or "conversation" if there was none}89---9091## Why this was stacked9293{the reason, expanded to a sentence or two if the user gave more}9495## Plan9697{verbatim content of the plan file, or the reconstructed step list}9899## Progress at the time of stacking100101- [x] {step finished}102- [ ] {step not started}103- [~] {step in progress} — {what is done, what is half-done, what to check first}104105## Loose ends106107{uncommitted work per repository, live background processes, volatile state —108 or "none" if the sweep in step 4 genuinely found nothing}109```1101117. **Report.**112113```114Stacked frame #{frame} — "{reason}"115 {N} steps: {done} done, {open} open116 Loose ends: {count, or "none"}117Use /stack-it pop to resume.118```119120---121122## Pop123124Restore the top frame and resume the work.125126### Steps1271281. **Find the top frame** — glob `.claude/plans/stack/*.md`, take the highest-numbered129 file. If none exist, tell the user the stack is empty and stop.1301312. **Read it** — parse the frontmatter and all four sections.1321333. **Check the project** — if `project_dir` differs from the current working directory,134 warn: "This frame was stacked in {project_dir}, you are in {cwd}. Continue?" Warn135 only; the user may have moved the repo or be working from a worktree.1361374. **Surface loose ends first.** Before restoring anything, report what the frame138 recorded as outstanding, and verify it still holds — uncommitted changes may have been139 committed since, background processes are certainly dead. Resuming work on top of140 unreconciled state is how a stacked frame turns into a merge conflict.1411425. **Show the summary** — reason, step list with progress, so the user can confirm or143 `/stack-it drop` instead.1441456. **Restore the plan.** Write the frame's `## Plan` section back to a plan file so it is146 durable again rather than living only in this conversation, and note the path. Carry147 the progress markers across: a step marked `[~]` resumes with its progress note148 attached, phrased so the next reader knows where to pick up.1491507. **Delete the frame file** — the work is live again; leaving the frame would let a151 later pop resurrect a stale copy.1521538. **State what happens next** — the first open step, and anything from the loose-end154 reconciliation that has to be handled before it.155156---157158## Show159160Peek at the top frame without popping it.1611621. Find and read the highest-numbered frame file.1632. Display the reason, timestamp, project directory, step list with progress, and loose164 ends.1653. Remind the user: `/stack-it pop` to resume, `/stack-it drop` to discard.166167---168169## List170171Show all frames with one-line summaries.1721731. Glob `.claude/plans/stack/*.md`.1742. For each file, sorted by frame number descending so the top of the stack reads first,175 display the frontmatter summary:176177```178Stack (3 frames):179 #3 [top] "Found login bug" — 5 steps, 2 open (2026-03-25, ~/Projects/api-service)180 #2 "Refactor auth module" — 3 steps, 3 open (2026-03-24, ~/Projects/web-app)181 #1 "Initial pipeline work" — 7 steps, 1 open (2026-03-23, ~/Projects/api-service)182```183184---185186## Drop187188Discard the top frame without resuming.1891901. Find the highest-numbered frame file.1912. Show what is being dropped — reason, open step count, and **any loose ends it192 recorded**. Dropping a frame discards the only record that those exist, so the user193 should see them before agreeing.1943. Delete the file.1954. Confirm: "Dropped frame #{N}. Stack now has {remaining} frames."