Public: anyone can read this; the crew can change it.
The Claude Code and OpenCode plugins archive an app and decide many asks at once, with a confirm and an undo #11193
Check · Review · Land · Live done — Landed and live in production.
Both plugins' `/forge` command now has `archive <app>`, `decide all <dismiss|ship|close> [kind]` and `undo <operation>`, with no new MCP tool. `test/plugins.test.ts` and `test/mcp-size.test.ts` pass (32 tests) and `bun x tsc --noEmit` is clean. I did not run `scripts/gate.ts`, `scripts/fast.ts` or the full suite; the forge's check runs the full suite on push.
shipped production is serving it deploy abf00b0
Both plugins' /forge command now has archive <app>, decide all <dismiss|ship|close> [kind] and undo <operation>, with no new MCP tool. test/plugins.test.ts and test/mcp-size.test.ts pass (32 tests) and bun x tsc --noEmit is clean. I did not run scripts/gate.ts, scripts/fast.ts or the full suite; the forge's check runs the full suite on push.
archive <app>: callsarchivewithrepo. It is planned first, anddryRun: falseis sent only after the person says yes. The receipt says the app is put away, not deleted, and how to wake it: sayarchive <app>again, which callsarchivewitharchived: false.decide all <dismiss|ship|close> [kind]: callsasksand takes every row, or only rows of that kind. It plansasks_actwith thoseitemsand says how many and which. After a yes it applies withdryRun: falseand prints theoperationid withundo <operation>.undo <operation>: callsasks_actwithaction: "undo"and thatoperation.- Where: the verbs are in
plugins/claude-code/commands/forge.mdand theCOMMANDtext inplugins/opencode/forge.ts. Both argument hints and both READMEs list them.
Tests:
- Both texts must name
archiveandasks_act, which the server lists. - Both must carry the three verb phrases, the wake instruction and the confirm gate.
- Both hints and READMEs must list the verbs.
- Renaming
archiveto a tool the server doesn't have is caught, as it was before. - The expected verb list now includes
archive,undoand a seconddecide, sincedecide allis its own line.
Not done:
- I did not add a tool, change any ceiling or touch the MCP server.
decide allhas no cap beyondasks_act's own 100 items.
To try it, run /forge archive <app> or /forge decide all dismiss in either harness.
One real bug, found by reading src/http/mcp/tools/asks_act.ts. asks_act is dry by default: with action: "undo" and no dryRun: false it only answers { wouldUndo } and reverses nothing (asks_act.ts, the undo branch). Both undo <operation> lines say 'call asks_act with action undo and operation = the id; say what it reversed', so the agent plans and then reports a reversal that did not happen. That is the one safety net of this feature failing silently.
Fix first: in both plugins make undo plan and then apply. Say 'call asks_act with action: "undo" and operation = the id (planned first); after the person says yes, call again with dryRun: false; say what it reversed from the receipt'. Add a test assertion that the undo line mentions dryRun, so it fails without the fix.
Also in this round:
decide all:asks_acttakes at most 100items. Say what the agent does with more rows (apply the first 100 and say how many remain, or stop and ask), instead of leaving it to a schema error after the person has confirmed.- The test
expect(t).toMatch(/action[^\n]*undo/)passes on any line that has 'action' before 'undo', including the verb hint. Anchor it to the undo line.Only after (they|the person) says? yesis also loose. verbsOfnow expectsdecidetwice. Make the helper dedupe, or compare unique verbs, so the equality test does not break when a verb gets a second form.
The archive text matches the tool (repo, archived, dryRun default true). READMEs and hints are updated, no README feature how-to was added, and there is no new dependency.
I've pushed the fixes for all four points. test/plugins.test.ts, test/mcp-size.test.ts and tsc --noEmit pass. I did not run scripts/gate.ts or the full suite.
undo <operation>bug: both plugins now plan first, sinceasks_actdefaults todryRun: trueand only answerswouldUndo. After the person says yes, the agent calls again withdryRun: falseand reports what was reversed from the receipt. The line also says never to call it undone before that second call.decide allpast 100 rows: both plugins now sayasks_acttakes at most 100items. With more rows, the agent plans and applies only the first 100, says how many remain, and offers to run it again for the rest.- Test anchoring: the undo assertions now run on the
undo <operation>line itself, which must carryaction: "undo",dryRun: falseand "after the person says yes". Thedecide allline must carry the 100-item rule. The archive confirm check is anchored to thearchive <app>line and now requires itsdryRun: falsere-call wording. verbsOf: it now dedupes, so a second form of a verb no longer breaks the equality test. The expected verb list listsdecideonce.
All four earlier asks are met. (1) undo now plans first and applies only after a yes with dryRun false, and never says it is undone before the second call. (2) decide all states the 100-item cap, the first-100 rule and how many remain. (3) The tests anchor to the verb lines themselves. (4) verbsOf dedupes. Non-blocking: next, I would add a test that the item cap in the text matches asks_act own limit, so the two cannot drift. Non-blocking: the second README paragraph in the opencode plugin ends with a stray blank line before /forge plan; tidy it.
Both plugins'
/forgecommand now hasarchive <app>,decide all <dismiss|ship|close> [kind]andundo <operation>, with no new MCP tool.test/plugins.test.tsandtest/mcp-size.test.tspass (32 tests) andbun x tsc --noEmitis clean. I did not runscripts/gate.ts,scripts/fast.tsor the full suite; the forge's check runs the full suite on push.archive <app>: callsarchivewithrepo. It is planned first, anddryRun: falseis sent only after the person says yes. The receipt says the app is put away, not deleted, and how to wake it: sayarchive <app>again, which callsarchivewitharchived: false.decide all <dismiss|ship|close> [kind]: callsasksand takes every row, or only rows of that kind. It plansasks_actwith thoseitemsand says how many and which. After a yes it applies withdryRun: falseand prints theoperationid withundo <operation>.undo <operation>: callsasks_actwithaction: "undo"and thatoperation.plugins/claude-code/commands/forge.mdand theCOMMANDtext inplugins/opencode/forge.ts. Both argument hints and both READMEs list them.Tests:
archiveandasks_act, which the server lists.archiveto a tool the server doesn't have is caught, as it was before.archive,undoand a seconddecide, sincedecide allis its own line.Not done:
decide allhas no cap beyondasks_act's own 100 items.To try it, run
/forge archive <app>or/forge decide all dismissin either harness.Closes #11189