Files
gstack/bin/gstack-ship-budget
T
Garry Tanandcapy-ai[bot] b6cb70aea1 v1.91.355.0 ship: poll readers in-program, 32 KB returns, gated fix program, unattended push defers the guard offer, calls meter (GSTA-167 calls PR 1) (#3662)
* ship: poll readers in-program, never wait on wakes; 32 KB returns; gated fix program; unattended push defers the guard offer; calls meter (GSTA-167 calls PR 1)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes (inventory lib/ship-release.ts as a GSTACK_SESSION_KIND reader)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(ship-budget): document calls and dotted --step notes

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* ship: judge treats a missing severity as invalid; Capy passes rendered briefs via cat (replay C1-34 breaks)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: keep the Capy skill under its context budget after the main merge

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-31 07:22:56 +00:00

58 lines
4.0 KiB
TypeScript
Executable File

#!/usr/bin/env -S bun --no-env-file --config=/dev/null
/**
* gstack-ship-budget — the /ship token budget and fresh-context handoff
* (docs/ship-budget.md). An admission cap: no step or dispatch starts unless
* used + open reservations + unknown + its projection + the tail and stop
* reserve fit under the cap. --mode observe (ship/DRIVER.md) only records:
* note at each program's end, and the cap is one hard stop. Every --reading
* takes --credits <cost.credits> beside it.
*
* start --record <path> [--token-cap <n|none>] [--mode admit|observe] [--meter capy|claude|estimate] [--reading <n>] [--credits <c>]
* [--prior-reading <n>] [--session <id>|--nonce <v>] [--reserve <n>] [--delegate auto|never] [--calibration <file>] [--ledger <path>]
* note --run|--record --step <id> [--label <text>] --reading <n> [--credits <c>] [--dispatched-at <n> [--dispatched-credits <c>] --readers <k>]
* check --run <id>|--record <path> --step <id> --next <id|dispatch:<role>[,<role>...]> [--reading <n>] [--credits <c>] [--prior-reading <n>]
* [--returned <id>[,<id>...]] [--runtime-lines <n>] [--dispatched-at <n> --readers <k>]
* settle --run|--record --reservation <id> [--unknown] [--reading <n>] [--credits <c>] [--dispatched-at <n> --readers <k>]
* release --run|--record --reservation <id>
* handoff --run|--record --from <step> --to <step> --base origin/<base> [--reviewed-tree <t>] [--trail <f> --trail-run <id>]
* [--sub-cap <n>] [--reading <n>] [--child-session <id>]
* launch --in <brief> (Claude Code: background headless child with explicit permissions)
* verify-handoff --in <brief> [--reading <n>] [--session <id>]
* return --run <child> --to <step> [--changed <sections>] [--status complete|refused|incomplete] [--reading <n>]
* reconcile --run|--record (--return <file>|--handoff <sha256>) [--reading <n>]
* resume --record <path>|--run <id> [--reading <n>] [--meter <m>] [--session <id>] [--token-cap <n> --approved-by <who> --words <text>]
* [--rebuild-from-record]
* close --run|--record [--reading <n>] [--approved-by <who> --words <text>]
* calls --events <file> [--since <iso>] [--until <iso>] (the host's transcript items: one CALLS: line, lib/ship-calls.ts)
*
* Exit: 0 continue/delegate · 3 stop (the next step does not start) · 2 usage
* or validation (stderr). Logic: lib/ship-budget.ts, lib/ship-budget-cli.ts,
* lib/ship-handoff.ts.
*/
import { BudgetError } from '../lib/ship-budget';
import { cmdCalls, cmdCheck, cmdClose, cmdNote, cmdResume, cmdSettle, cmdStart, type Outcome } from '../lib/ship-budget-cli';
import { cmdHandoff, cmdLaunch, cmdReconcile, cmdReturn, cmdVerifyHandoff } from '../lib/ship-handoff';
const [command, ...rest] = process.argv.slice(2);
const ctx = { env: process.env, cwd: process.cwd() };
const commands: Record<string, () => Outcome> = {
start: () => cmdStart(rest, ctx), note: () => cmdNote(rest, ctx), check: () => cmdCheck(rest, ctx), settle: () => cmdSettle(rest, ctx), release: () => cmdSettle(rest, ctx, true),
handoff: () => cmdHandoff(rest, ctx), launch: () => cmdLaunch(rest, ctx), 'verify-handoff': () => cmdVerifyHandoff(rest, ctx), return: () => cmdReturn(rest, ctx),
reconcile: () => cmdReconcile(rest, ctx), resume: () => cmdResume(rest, ctx), close: () => cmdClose(rest, ctx), calls: () => cmdCalls(rest),
};
if (!command || command === '--help' || command === '-h' || !commands[command]) {
const lines = (await Bun.file(import.meta.path).text()).split('\n');
const text = lines.slice(2, lines.indexOf(' */')).map(l => l.replace(/^ \* ?/, '')).join('\n');
(command && command !== '--help' && command !== '-h' ? console.error : console.log)(text);
process.exit(command && command !== '--help' && command !== '-h' ? 2 : 0);
}
try {
const out = commands[command]!();
if (out.stdout.length) console.log(out.stdout.join('\n'));
process.exit(out.exit);
} catch (e) {
if (!(e instanceof BudgetError)) throw e;
if (e.exit === 3) console.log(e.message); else console.error(e.message);
process.exit(e.exit);
}