spare10 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -135,6 +135,23 @@ The cost is that a turn producing only text is not gated. Before launching, spar
135
135
  knows your quota from the previous run, so `spare10 claude` asks for confirmation rather than
136
136
  starting a session that would stop on its first move.
137
137
 
138
+ **Background sessions are stopped through the CLI.** `spare10 claude agents` guards the whole
139
+ fleet: the agent view passes spare10's settings to every session it dispatches, so each one
140
+ carries its own sensor and gate. But a background session does not run under the launcher — it
141
+ lives under Claude Code's daemon — so there is no process for the gate to signal. Each kind of
142
+ session offers exactly one handle, and they are different ones:
143
+
144
+ | | listed with | stopped with | resumed with |
145
+ |---|---|---|---|
146
+ | interactive | a `pid` | `SIGTERM` | `claude --resume <session>` |
147
+ | background | a short `id` | `claude stop <id>` | `claude --bg --resume <session>` |
148
+
149
+ Nothing is attached to a background session to ask a question on, so spare10 stops it, keeps the
150
+ record, and asks once the wrapper is back in front of you: one panel listing every session it
151
+ stopped, resuming them all into the reserve if you say so. Anything left stopped is listed by
152
+ `spare10 doctor` with the command to reopen it — which is also how `spare10 claude --bg …` ends,
153
+ since that returns before there is anything to ask about.
154
+
138
155
  Three consequences worth knowing:
139
156
 
140
157
  **Nothing you own is modified.** `claude --settings` accepts a raw JSON string, so a run
@@ -171,7 +188,9 @@ Not in this version, deliberately:
171
188
  - **Overshoot correction.** The reserve is indicative, not predictive: readings land at 1%
172
189
  granularity and up to one refresh interval late, and parallel subagents can land several
173
190
  requests at once. Keep back more than you think you need.
174
- - **Coordination across sessions.** Quota is account-wide, but each run tracks its own state.
191
+ - **Coordination across runs.** Sessions started under one `spare10` command share its state,
192
+ including every background session dispatched from it. Separate runs do not: quota is
193
+ account-wide, but each run tracks it on its own, seeded from the last one.
175
194
 
176
195
  ## Known limitations
177
196
 
@@ -182,12 +201,15 @@ Not in this version, deliberately:
182
201
  denies the tool call instead, and the model may retry with another tool before it gives up.
183
202
  - Status line chaining reads user-level settings only. A status line configured in project or
184
203
  local settings is not detected.
204
+ - Stopping a background session goes through `claude stop`, called from inside the session being
205
+ stopped. If it does not land, the gate falls back to denying the tool call, as it does
206
+ everywhere else.
185
207
 
186
208
  ## Development
187
209
 
188
210
  ```bash
189
211
  npm install
190
- npm test # 213 tests: unit, plus the hooks and CLI as real subprocesses
212
+ npm test # 254 tests: unit, plus the hooks and CLI as real subprocesses
191
213
  npm run typecheck
192
214
  npm run build # single dependency-free bundle in dist/
193
215
  npm run demo # the pause prompt at 91% usage, without burning a session
package/dist/spare10.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- var fe={version:null,model:null,effort:null,cwd:null,fastMode:!1},W={pct:null,resetsAt:null,updatedAt:null,missingStreak:0,blind:!1,disarmedUntil:null,pausePromptInjectedTo:[],halted:null,tick:0,session:null},it="main";function me(e){return e??it}var q=10,ut=2,w={reserve:q,pausePrompt:null,refresh:ut,badge:!0,chain:null},ge=3,he=3,be=3600;function v(e){return 100-e.reserve}function C(e){return 100-e}var h=class extends Error{},J=`spare10 \u2014 pause Claude Code before the 5-hour quota runs out
2
+ var kt={version:null,model:null,effort:null,cwd:null,fastMode:!1},tt={pct:null,resetsAt:null,updatedAt:null,missingStreak:0,blind:!1,disarmedUntil:null,pausePromptInjectedTo:[],halted:null,tick:0,session:null},Me="main",Oe="unknown";function At(t,e){return`${t??Oe}:${e??Me}`}var et=10,Le=2,y={reserve:et,pausePrompt:null,refresh:Le,badge:!0,chain:null},Rt=3,Pt=3,It=3600;function C(t){return 100-t.reserve}function _(t){return 100-t}var b=class extends Error{},nt=`spare10 \u2014 pause Claude Code before the 5-hour quota runs out
3
3
 
4
4
  spare10 [options] <command> [args...]
5
5
 
@@ -16,26 +16,31 @@ Commands:
16
16
  Examples:
17
17
  spare10 claude
18
18
  spare10 --reserve 15 claude
19
- spare10 --pause-prompt "Finish this block, commit, then stop." claude --resume`;function at(e){if(!/^\d+$/.test(e))throw new h(`--reserve must be a whole number: the API reports quota in integer percentages, so "${e}" cannot be honoured.`);let t=Number(e);if(t<1||t>99)throw new h(`--reserve must be between 1 and 99, got ${t}.`);return t}function lt(e){if(!/^\d+$/.test(e)||Number(e)<1)throw new h(`--refresh must be a whole number of seconds >= 1, got "${e}".`);return Number(e)}function Se(e){let t={reserve:w.reserve,pausePrompt:w.pausePrompt,refresh:w.refresh,badge:w.badge},n=0,r=o=>{let i=e[n+1];if(i===void 0)throw new h(`${o} requires a value.`);return n+=1,i};for(;n<e.length;n+=1){let o=e[n];if(!o.startsWith("-"))break;switch(o){case"--reserve":t.reserve=at(r(o));break;case"--threshold":throw new h('--threshold was replaced by --reserve, which is the quota kept back rather than the level that trips. "--threshold 90" is now "--reserve 10".');case"--pause-prompt":t.pausePrompt=r(o);break;case"--refresh":t.refresh=lt(r(o));break;case"--no-badge":t.badge=!1;break;case"-h":case"--help":throw new h("");default:throw new h(`Unknown option "${o}".`)}}let s=e.slice(n);if(s.length===0)throw new h("No command given. Try: spare10 claude");return{config:t,command:s}}import{spawnSync as wt}from"node:child_process";import{readFileSync as ve}from"node:fs";var I={kind:"pass"};function K(e,t,n){return e.resetsAt!==null?n<e.resetsAt:e.updatedAt===null?!1:n-e.updatedAt<=t.refresh*he}function D(e){return e===null?"an unknown time":new Date(e*1e3).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function we(e,t){return`into your ${t.reserve}% reserve \xB7 ${C(e.pct??0)}% of quota left \xB7 resets ${D(e.resetsAt)}`}function ct(e,t){return`spare10 \u2014 ${we(e,t)}`}function dt(e,t){return`spare10 budget guard. You have reached the safe usage limit for this session (${we(e,t)}). Immediately wrap up your work and stop. Immediately stop any subagent, unless the user instructs otherwise.
19
+ spare10 --pause-prompt "Finish this block, commit, then stop." claude --resume`;function Ue(t){if(!/^\d+$/.test(t))throw new b(`--reserve must be a whole number: the API reports quota in integer percentages, so "${t}" cannot be honoured.`);let e=Number(t);if(e<1||e>99)throw new b(`--reserve must be between 1 and 99, got ${e}.`);return e}function De(t){if(!/^\d+$/.test(t)||Number(t)<1)throw new b(`--refresh must be a whole number of seconds >= 1, got "${t}".`);return Number(t)}function Et(t){let e={reserve:y.reserve,pausePrompt:y.pausePrompt,refresh:y.refresh,badge:y.badge},n=0,r=o=>{let i=t[n+1];if(i===void 0)throw new b(`${o} requires a value.`);return n+=1,i};for(;n<t.length;n+=1){let o=t[n];if(!o.startsWith("-"))break;switch(o){case"--reserve":e.reserve=Ue(r(o));break;case"--threshold":throw new b('--threshold was replaced by --reserve, which is the quota kept back rather than the level that trips. "--threshold 90" is now "--reserve 10".');case"--pause-prompt":e.pausePrompt=r(o);break;case"--refresh":e.refresh=De(r(o));break;case"--no-badge":e.badge=!1;break;case"-h":case"--help":throw new b("");default:throw new b(`Unknown option "${o}".`)}}let s=t.slice(n);if(s.length===0)throw new b("No command given. Try: spare10 claude");return{config:e,command:s}}import{spawnSync as Xe}from"node:child_process";import{readFileSync as Kt}from"node:fs";import{spawnSync as vt}from"node:child_process";var Ct=1e4,D=t=>typeof t=="string"&&t?t:null;function je(t){let e;try{e=JSON.parse(t)}catch{return[]}if(!Array.isArray(e))return[];let n=[];for(let r of e){if(typeof r!="object"||r===null)continue;let s=r,o=D(s.sessionId);o!==null&&n.push({sessionId:o,backgroundId:D(s.id),kind:D(s.kind),name:D(s.name),cwd:D(s.cwd)})}return n}function _t(t,e){for(let n of t)if(n.sessionId===e)return n.kind!=="background"||n.backgroundId===null?null:{...n,backgroundId:n.backgroundId};return null}function B(t,e){let n=new Set(e.map(r=>r.sessionId));return t.filter(r=>!n.has(r.sessionId))}function Tt(t,e){try{return vt("claude",t,{encoding:"utf8",timeout:Ct,stdio:"ignore",...e===void 0?{}:{cwd:e}}).status===0}catch{return!1}}function T(){try{let t=vt("claude",["agents","--json"],{encoding:"utf8",timeout:Ct});return t.status!==0||typeof t.stdout!="string"?[]:je(t.stdout)}catch{return[]}}function Nt(t){return Tt(["stop",t])}function Fe(t){return["--bg","--resume",t]}function Mt(t,e){return Tt(Fe(t),e??void 0)}var N={kind:"pass"};function rt(t,e,n){return t.resetsAt!==null?n<t.resetsAt:t.updatedAt===null?!1:n-t.updatedAt<=e.refresh*Pt}function H(t){return t===null?"an unknown time":new Date(t*1e3).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function Ot(t,e){return`into your ${e.reserve}% reserve \xB7 ${_(t.pct??0)}% of quota left \xB7 resets ${H(t.resetsAt)}`}function Be(t,e){return`spare10 \u2014 ${Ot(t,e)}`}function He(t,e){return`spare10 budget guard. You have reached the safe usage limit for this session (${Ot(t,e)}). Immediately wrap up your work and stop. Immediately stop any subagent, unless the user instructs otherwise.
20
20
 
21
- User instructions: ${t.pausePrompt??""}`}function ye(e,t,n){return U({state:e,config:t,now:n,agent:null}).kind!=="pass"}function U({state:e,config:t,now:n,agent:r}){return e.pct===null||e.updatedAt===null||e.blind||!K(e,t,n)||e.disarmedUntil!==null&&n<e.disarmedUntil||e.pct<v(t)?I:t.pausePrompt!==null?r!==null&&e.pausePromptInjectedTo.includes(r)?I:{kind:"inject",text:dt(e,t)}:{kind:"halt",reason:`${ct(e,t)}. Stop now and wait for the user. Do not call any further tools.`}}var $e={sessionId:null,toolName:null,agentId:null};function xe(e){try{let t=JSON.parse(e);if(typeof t!="object"||t===null)return $e;let n=t;return{sessionId:typeof n.session_id=="string"?n.session_id:null,toolName:typeof n.tool_name=="string"?n.tool_name:null,agentId:typeof n.agent_id=="string"?n.agent_id:null}}catch{return $e}}import{readFileSync as pt,writeFileSync as ft,renameSync as mt,mkdirSync as gt,unlinkSync as ht}from"node:fs";import{join as Y}from"node:path";var Pe=e=>Y(e,"state.json"),X=e=>Y(e,"config.json"),O=e=>Y(e,"claude.pid");function y(){return Math.floor(Date.now()/1e3)}function Re(e){try{let t=JSON.parse(pt(e,"utf8"));return typeof t=="object"&&t!==null?t:null}catch{return null}}var A=(e,t)=>typeof e=="number"&&Number.isFinite(e)?e:t,Ae=(e,t)=>typeof e=="boolean"?e:t,bt=e=>Array.isArray(e)?e.filter(t=>typeof t=="string"):[],F=e=>typeof e=="string"&&e?e:null;function St(e){if(typeof e!="object"||e===null)return null;let t=e;return{...fe,version:F(t.version),model:F(t.model),effort:F(t.effort),cwd:F(t.cwd),fastMode:t.fastMode===!0}}function x(e){let t=Re(Pe(e));return t?{pct:A(t.pct,null),resetsAt:A(t.resetsAt,null),updatedAt:A(t.updatedAt,null),missingStreak:A(t.missingStreak,0)??0,blind:Ae(t.blind,!1),disarmedUntil:A(t.disarmedUntil,null),pausePromptInjectedTo:bt(t.pausePromptInjectedTo),halted:typeof t.halted=="string"&&t.halted?t.halted:null,tick:A(t.tick,0)??0,session:St(t.session)}:{...W}}function R(e,t){let n=Pe(e),r=`${n}.${process.pid}.tmp`;try{gt(e,{recursive:!0}),ft(r,JSON.stringify(t),"utf8"),mt(r,n)}catch{try{ht(r)}catch{}}}function _(e){let t=Re(X(e));if(!t)return{...w};let n=A(t.reserve,w.reserve)??w.reserve,r=A(t.refresh,w.refresh)??w.refresh;return{reserve:Math.min(99,Math.max(1,Math.round(n))),pausePrompt:typeof t.pausePrompt=="string"&&t.pausePrompt?t.pausePrompt:null,refresh:Math.max(1,Math.round(r)),badge:Ae(t.badge,w.badge),chain:typeof t.chain=="string"&&t.chain?t.chain:null}}function Q(e,t){return e.resetsAt??t+be}function ke(e,t){let r=e.filter(s=>s.pct!==null&&s.updatedAt!==null&&s.resetsAt!==null&&s.resetsAt>t).reduce((s,o)=>s===null||o.updatedAt>s.updatedAt?o:s,null);return r===null?null:{...W,pct:r.pct,resetsAt:r.resetsAt,updatedAt:r.updatedAt}}function Ee(){try{return ve(0,"utf8")}catch{return""}}function Ce(e){process.stdout.write(JSON.stringify({hookSpecificOutput:e}))}var yt=2e3;function $t(e){try{let t=Number(ve(O(e),"utf8").trim());return Number.isInteger(t)&&t>1?t:null}catch{return null}}function xt(e){let t=wt("ps",["-o","ppid=","-p",String(e)],{encoding:"utf8"}),n=Number(t.stdout.trim());return t.status===0&&Number.isInteger(n)&&n>0?n:null}function Pt(e,t=process.ppid){let n=t;for(let r=0;n!==null&&n>1&&r<32;r+=1){if(n===e)return!0;n=xt(n)}return!1}function Rt(e){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,e)}function At(e,t,n,r){let s=n===null?null:$t(e);if(s!==null&&Pt(s)){R(e,{...t,halted:n});try{process.kill(s,"SIGTERM"),Rt(yt)}catch{}}return Ce({hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:r}),0}function Ie(e){let t=_(e),n=x(e),r=y();if(U({state:n,config:t,now:r,agent:null}).kind==="pass")return Ee(),0;let{sessionId:s,agentId:o}=xe(Ee()),i=me(o),u=U({state:n,config:t,now:r,agent:i});switch(u.kind){case"pass":return 0;case"inject":return R(e,{...n,pausePromptInjectedTo:[...n.pausePromptInjectedTo,i]}),Ce({hookEventName:"PreToolUse",additionalContext:u.text}),0;case"halt":return At(e,n,s,u.reason)}}import{execFileSync as Qe}from"node:child_process";import{accessSync as vn,constants as Cn,readdirSync as In,statSync as ue}from"node:fs";import{join as _n}from"node:path";function _e(e,t,n){if(e.pct===null||e.updatedAt===null)return{status:"no-data",detail:"no quota reading yet \u2014 start a session and wait a moment"};if(e.blind)return{status:"blind",detail:"Claude Code is not reporting rate_limits on this plan"};if(!K(e,t,n))return{status:"stale",detail:"the window this reading described has already reset"};if(e.disarmedUntil!==null&&n<e.disarmedUntil)return{status:"disarmed",detail:`consent given; quiet until ${Z(e.disarmedUntil)}`};if(e.pct>=v(t)){let r=e.pausePromptInjectedTo.length,s=r>0?`; pause prompt delivered to ${r} agent${r===1?"":"s"}`:"";return{status:"tripped",detail:`into the ${t.reserve}% reserve${s}`}}return{status:"armed",detail:`${C(e.pct)}% left, of which ${t.reserve}% is reserved`}}function z(e){let t=Math.abs(Math.round(e));if(t<60)return`${t}s`;let n=Math.floor(t/60);return n<60?`${n}m`:`${Math.floor(n/60)}h ${n%60}m`}function Z(e){return new Date(e*1e3).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}import{spawn as Qt}from"node:child_process";import{copyFileSync as zt,mkdirSync as Zt,readdirSync as qe,rmSync as en,statSync as tn,unlinkSync as nn,writeFileSync as Je}from"node:fs";import{homedir as Ke}from"node:os";import{basename as rn,join as k}from"node:path";import{fileURLToPath as sn}from"node:url";import{readFileSync as kt}from"node:fs";function ee(e){return`'${e.replace(/'/g,"'\\''")}'`}function j(e){let t;try{t=JSON.parse(kt(e,"utf8"))}catch{return{command:null,padding:void 0}}if(typeof t!="object"||t===null)return{command:null,padding:void 0};let n=t.statusLine;if(typeof n!="object"||n===null)return{command:null,padding:void 0};let r=n,s=typeof r.command=="string"?r.command:null,o=typeof r.padding=="number"?r.padding:void 0;return s!==null&&s.includes("spare10")?{command:null,padding:o}:{command:s,padding:o}}function Te(e){let t=s=>`${ee(e.nodePath)} ${ee(e.selfPath)} ${s} --run ${ee(e.runDir)}`,n=s=>[{matcher:"",hooks:[{type:"command",command:t(s)}]}],r={type:"command",command:t("sensor"),refreshInterval:e.refresh};return e.padding!==void 0&&(r.padding=e.padding),{statusLine:r,hooks:{PreToolUse:n("gate")}}}import{closeSync as Ge,openSync as ne,readSync as Dt,writeSync as Ut}from"node:fs";import{homedir as Ft}from"node:os";import{ReadStream as jt,WriteStream as Ht,isatty as Vt}from"node:tty";var te=46,Et=78,De=/\u001b\[[0-9;]*m/g,L=e=>e.replace(De,"").length,T=[217,119,87],Ne=173;var vt=236,Ct=245;function Ue(e){if(!e.color){let r=s=>s;return{brand:r,dim:r,bold:r,chosen:s=>`[${s}]`}}let t=e.truecolor?`\x1B[38;2;${T[0]};${T[1]};${T[2]}m`:`\x1B[38;5;${Ne}m`,n=e.truecolor?`\x1B[48;2;${T[0]};${T[1]};${T[2]}m`:`\x1B[48;5;${Ne}m`;return{brand:r=>`${t}${r}\x1B[39m`,dim:r=>`\x1B[38;5;${Ct}m${r}\x1B[39m`,bold:r=>`\x1B[1m${r}\x1B[22m`,chosen:r=>`${n}\x1B[38;5;${vt}m\x1B[1m${r}\x1B[0m`}}var Fe=[" \u2590\u259B\u2588\u2588\u2588\u259B\u2588","\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2580"," \u259D\u259D \u259D\u259D"],je=Math.max(...Fe.map(e=>e.length));function It(e){if(!e.unicode||!e.color)return[];let t=Ue(e);return Fe.map(n=>t.brand(n.padEnd(je)))}function _t(e,t){return t&&(e===t||e.startsWith(`${t}/`))?`~${e.slice(t.length)}`:e}function Tt(e,t,n){let r=n.bold("Claude Code"),s=e?.version?` ${n.dim(`v${e.version}`)}`:"",o=[`${r}${s}`],i=e?.model??null;if(i!==null){let u=e?.effort?` with ${e.effort} effort`:"",a=e?.fastMode?" \xB7 fast mode":"";o.push(n.dim(`${i}${u}${a}`))}return e?.cwd&&o.push(n.dim(_t(e.cwd,t))),o}var Me={unicode:{filled:"\u2588",empty:"\u2592"},ascii:{filled:"#",empty:"-"}};function Nt(e,t,n,r){let s=r?Me.unicode:Me.ascii,o=Math.max(4,t),i=Math.min(o,Math.max(0,Math.round(e/100*o)));return`${n.brand(s.filled.repeat(i))}${n.dim(s.empty.repeat(o-i))}`}function He(e){return e.pct===null?`quota unknown \xB7 resets ${D(e.resetsAt)}`:`${e.pct}% used \xB7 ${C(e.pct)}% left \xB7 resets ${D(e.resetsAt)}`}function Mt(e,t){return L(e)<=t?e:`${e.replace(De,"").slice(0,Math.max(1,t-1))}\u2026`}function Oe(e,t){let n=[],r="";for(let s of e.split(/\s+/).filter(Boolean))r===""?r=s:r.length+1+s.length<=t?r+=` ${s}`:(n.push(r),r=s);return r!==""&&n.push(r),n}var Le={unicode:{tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502"},ascii:{tl:"+",tr:"+",bl:"+",br:"+",h:"-",v:"|"}};function Ot(e){return Math.max(te,Math.min(Et,e-2))}function Lt(e,t,n){return e.map((r,s)=>s===t?n.chosen(` ${r.label} `):n.dim(` ${r.label} `)).join(" ")}function Ve({view:e,state:t,config:n,selected:r,caps:s,home:o}){let i=Ue(s),u=s.unicode?Le.unicode:Le.ascii,a=Ot(s.columns),l=a-6,b=It(s),S=Tt(e.session,o,i),d=b.length>0?je:0,p=d>0?2:0,f=[];for(let c=0;c<Math.max(b.length,S.length);c+=1){let M=(b[c]??"").padEnd(d);f.push(`${M}${" ".repeat(p)}${Mt(S[c]??"",a-d-p)}`.trimEnd())}let P=[],g=(c="")=>P.push(c);g(i.bold(e.headline));let N=He(t);if(t.pct!==null){let c=Math.max(6,l-L(N)-2);g(`${Nt(t.pct,c,i,s.unicode)} ${i.dim(N)}`)}g();for(let c of e.body)for(let M of Oe(c,l))g(i.dim(M));g(),g(Lt(e.choices,r,i));let E=e.choices.map(c=>Oe(c.hint,l)),V=Math.max(...E.map(c=>c.length)),$=E[r];for(let c=0;c<V;c+=1)g(i.dim($[c]??""));g(),g(i.dim(s.unicode?"\u2190/\u2192 choose \xB7 enter confirm \xB7 esc cancel":"left/right choose, enter confirm, esc cancel"));let B=` ${i.brand(i.bold("spare10"))} `,pe=`${n.reserve}% reserve`,st=Math.max(1,a-6-L(B)-L(pe)),ot=`${u.tl}${u.h}${B}${u.h.repeat(st)} ${i.dim(pe)} ${u.h}${u.tr}`,G=[...f,"",ot];for(let c of P){let M=Math.max(0,a-4-L(c));G.push(`${u.v} ${c}${" ".repeat(M)}${u.v}`)}return G.push(`${u.bl}${u.h.repeat(a-2)}${u.br}`),G}function Be(e,t){return`
22
- spare10 \u2014 ${He(t)}
23
- ${e.headline}
24
- ${e.choices[0].label}? [y/N] `}function Bt(e){switch(e){case"\x1B[D":case"\x1B[A":case"\x1BOD":case"\x1BOA":return"prev";case"\x1B[C":case"\x1B[B":case"\x1BOC":case"\x1BOB":case" ":return"next";case"\r":case`
25
- `:return"confirm";case"\x1B":case"":case"":return"cancel"}return/^[yY]$/.test(e)?"yes":/^[nNqQ]$/.test(e)?"no":e==="h"||e==="k"?"prev":e==="l"||e==="j"?"next":null}function Gt(e){let t=[];for(let n=0;n<e.length;){let r=e[n+1];if(e[n]==="\x1B"&&(r==="["||r==="O")){let s=n+2;for(;s<e.length&&!/[@-~]/.test(e[s]);)s+=1;t.push(e.slice(n,s+1)),n=s+1;continue}t.push(e[n]),n+=1}return t}function Wt(e,t){let n=e.TERM==="dumb"||e.TERM===void 0,r=!e.NO_COLOR&&!n,s=e.LC_ALL??e.LC_CTYPE??e.LANG??"";return{color:r,truecolor:r&&/truecolor|24bit/i.test(e.COLORTERM??""),unicode:!n&&(s===""||/utf-?8/i.test(s)),columns:t}}function qt(){let e;try{e=ne("/dev/tty","r")}catch{return null}let t=null,n=2;if(!process.stderr.isTTY)try{n=ne("/dev/tty","w"),t=new Ht(n)}catch{n=2}return{fd:e,write:r=>{try{Ut(n,r)}catch{}},columns:(t??process.stderr).columns||80,close:()=>{try{Ge(e)}catch{}try{t?.destroy()}catch{}}}}var Jt="\x1B[?25l",Kt="\x1B[?25h",Yt=e=>`\r${"\x1B[K\x1B[A".repeat(e-1)}\x1B[K`;async function re({view:e,state:t,config:n,initial:r=1}){let s=qt();if(s===null)return null;let o=Wt(process.env,s.columns),i=Ft(),u=()=>{let f=Xt(s,Be(e,t));return s.close(),f};if(!Vt(s.fd)||o.columns<te+2)return u();let a;try{a=new jt(s.fd),a.setRawMode(!0)}catch{return u()}let l=r,b=0,S=()=>{let f=Ve({view:e,state:t,config:n,selected:l,caps:o,home:i});s.write(`${b>0?Yt(b):""}${f.join(`
21
+ User instructions: ${e.pausePrompt??""}`}function Lt(t,e,n){return V({state:t,config:e,now:n,agent:null}).kind!=="pass"}function V({state:t,config:e,now:n,agent:r}){return t.pct===null||t.updatedAt===null||t.blind||!rt(t,e,n)||t.disarmedUntil!==null&&n<t.disarmedUntil||t.pct<C(e)?N:e.pausePrompt!==null?r!==null&&t.pausePromptInjectedTo.includes(r)?N:{kind:"inject",text:He(t,e)}:{kind:"halt",reason:`${Be(t,e)}. Stop now and wait for the user. Do not call any further tools.`}}var Ut={sessionId:null,toolName:null,agentId:null};function Dt(t){try{let e=JSON.parse(t);if(typeof e!="object"||e===null)return Ut;let n=e;return{sessionId:typeof n.session_id=="string"?n.session_id:null,toolName:typeof n.tool_name=="string"?n.tool_name:null,agentId:typeof n.agent_id=="string"?n.agent_id:null}}catch{return Ut}}import{readFileSync as Ve,readdirSync as Ge,writeFileSync as We,renameSync as qe,mkdirSync as Je,unlinkSync as jt}from"node:fs";import{join as I}from"node:path";var Ft=t=>I(t,"state.json"),st=t=>I(t,"config.json"),j=t=>I(t,"claude.pid"),G=t=>I(t,"stopped");function S(){return Math.floor(Date.now()/1e3)}function ot(t){try{let e=JSON.parse(Ve(t,"utf8"));return typeof e=="object"&&e!==null?e:null}catch{return null}}var A=(t,e)=>typeof t=="number"&&Number.isFinite(t)?t:e,Bt=(t,e)=>typeof t=="boolean"?t:e,Ke=t=>Array.isArray(t)?t.filter(e=>typeof e=="string"):[],R=t=>typeof t=="string"&&t?t:null;function Ye(t){if(typeof t!="object"||t===null)return null;let e=t;return{...kt,version:R(e.version),model:R(e.model),effort:R(e.effort),cwd:R(e.cwd),fastMode:e.fastMode===!0}}function $(t){let e=ot(Ft(t));return e?{pct:A(e.pct,null),resetsAt:A(e.resetsAt,null),updatedAt:A(e.updatedAt,null),missingStreak:A(e.missingStreak,0)??0,blind:Bt(e.blind,!1),disarmedUntil:A(e.disarmedUntil,null),pausePromptInjectedTo:Ke(e.pausePromptInjectedTo),halted:typeof e.halted=="string"&&e.halted?e.halted:null,tick:A(e.tick,0)??0,session:Ye(e.session)}:{...tt}}function Ht(t,e,n){let r=`${e}.${process.pid}.tmp`;try{Je(t,{recursive:!0}),We(r,JSON.stringify(n),"utf8"),qe(r,e)}catch{try{jt(r)}catch{}}}function k(t,e){Ht(t,Ft(t),e)}function Vt(t,e){let n=G(t);Ht(n,I(n,`${e.sessionId}.json`),e)}function W(t){let e;try{e=Ge(G(t))}catch{return[]}let n=[];for(let r of e){if(!r.endsWith(".json"))continue;let s=ot(I(G(t),r));if(!s)continue;let o=R(s.sessionId),i=R(s.backgroundId);o===null||i===null||n.push({sessionId:o,backgroundId:i,name:R(s.name),cwd:R(s.cwd),at:A(s.at,0)??0})}return n.sort((r,s)=>r.at-s.at)}function it(t,e){for(let n of e)try{jt(I(G(t),`${n}.json`))}catch{}}function M(t){let e=ot(st(t));if(!e)return{...y};let n=A(e.reserve,y.reserve)??y.reserve,r=A(e.refresh,y.refresh)??y.refresh;return{reserve:Math.min(99,Math.max(1,Math.round(n))),pausePrompt:typeof e.pausePrompt=="string"&&e.pausePrompt?e.pausePrompt:null,refresh:Math.max(1,Math.round(r)),badge:Bt(e.badge,y.badge),chain:typeof e.chain=="string"&&e.chain?e.chain:null}}function q(t,e){return t.resetsAt??e+It}function Gt(t,e){let r=t.filter(s=>s.pct!==null&&s.updatedAt!==null&&s.resetsAt!==null&&s.resetsAt>e).reduce((s,o)=>s===null||o.updatedAt>s.updatedAt?o:s,null);return r===null?null:{...tt,pct:r.pct,resetsAt:r.resetsAt,updatedAt:r.updatedAt}}function Wt(){try{return Kt(0,"utf8")}catch{return""}}function Yt(t){process.stdout.write(JSON.stringify({hookSpecificOutput:t}))}var qt=2e3;function Qe(t){try{let e=Number(Kt(j(t),"utf8").trim());return Number.isInteger(e)&&e>1?e:null}catch{return null}}function ze(t){let e=Xe("ps",["-o","ppid=","-p",String(t)],{encoding:"utf8"}),n=Number(e.stdout.trim());return e.status===0&&Number.isInteger(n)&&n>0?n:null}function Ze(t,e=process.ppid){let n=e;for(let r=0;n!==null&&n>1&&r<32;r+=1){if(n===t)return!0;n=ze(n)}return!1}function Jt(t){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,t)}function tn(t,e,n,r){let s=n===null?null:Qe(t);if(s!==null&&Ze(s)){k(t,{...e,halted:n});try{process.kill(s,"SIGTERM"),Jt(qt)}catch{}}else if(n!==null){let o=_t(T(),n);o!==null&&(Vt(t,{sessionId:n,backgroundId:o.backgroundId,name:o.name,cwd:o.cwd,at:S()}),Nt(o.backgroundId),Jt(qt))}return Yt({hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:r}),0}function Xt(t){let e=M(t),n=$(t),r=S();if(V({state:n,config:e,now:r,agent:null}).kind==="pass")return Wt(),0;let{sessionId:s,agentId:o}=Dt(Wt()),i=At(s,o),u=V({state:n,config:e,now:r,agent:i});switch(u.kind){case"pass":return 0;case"inject":return k(t,{...n,pausePromptInjectedTo:[...n.pausePromptInjectedTo,i]}),Yt({hookEventName:"PreToolUse",additionalContext:u.text}),0;case"halt":return tn(t,n,s,u.reason)}}import{execFileSync as Ae}from"node:child_process";import{accessSync as er,constants as nr,readdirSync as rr,statSync as bt}from"node:fs";import{join as sr}from"node:path";function Qt(t,e,n){if(t.pct===null||t.updatedAt===null)return{status:"no-data",detail:"no quota reading yet \u2014 start a session and wait a moment"};if(t.blind)return{status:"blind",detail:"Claude Code is not reporting rate_limits on this plan"};if(!rt(t,e,n))return{status:"stale",detail:"the window this reading described has already reset"};if(t.disarmedUntil!==null&&n<t.disarmedUntil)return{status:"disarmed",detail:`consent given; quiet until ${at(t.disarmedUntil)}`};if(t.pct>=C(e)){let r=t.pausePromptInjectedTo.length,s=r>0?`; pause prompt delivered to ${r} agent${r===1?"":"s"}`:"";return{status:"tripped",detail:`into the ${e.reserve}% reserve${s}`}}return{status:"armed",detail:`${_(t.pct)}% left, of which ${e.reserve}% is reserved`}}function ut(t){let e=Math.abs(Math.round(t));if(e<60)return`${e}s`;let n=Math.floor(e/60);return n<60?`${n}m`:`${Math.floor(n/60)}h ${n%60}m`}function at(t){return new Date(t*1e3).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}import{spawn as Pn}from"node:child_process";import{copyFileSync as In,mkdirSync as En,readdirSync as fe,rmSync as vn,statSync as Cn,unlinkSync as _n,writeFileSync as me}from"node:fs";import{homedir as mt}from"node:os";import{basename as Tn,join as E}from"node:path";import{fileURLToPath as Nn}from"node:url";var lt=46,en=78,ne=/\u001b\[[0-9;]*m/g,F=t=>t.replace(ne,"").length,O=[217,119,87],zt=173;var nn=236,rn=245;function re(t){if(!t.color){let r=s=>s;return{brand:r,dim:r,bold:r,chosen:s=>`[${s}]`}}let e=t.truecolor?`\x1B[38;2;${O[0]};${O[1]};${O[2]}m`:`\x1B[38;5;${zt}m`,n=t.truecolor?`\x1B[48;2;${O[0]};${O[1]};${O[2]}m`:`\x1B[48;5;${zt}m`;return{brand:r=>`${e}${r}\x1B[39m`,dim:r=>`\x1B[38;5;${rn}m${r}\x1B[39m`,bold:r=>`\x1B[1m${r}\x1B[22m`,chosen:r=>`${n}\x1B[38;5;${nn}m\x1B[1m${r}\x1B[0m`}}var se=[" \u2590\u259B\u2588\u2588\u2588\u259B\u2588","\u259D\u259C\u2588\u2588\u2588\u2588\u2588\u2588\u2580"," \u259D\u259D \u259D\u259D"],oe=Math.max(...se.map(t=>t.length));function sn(t){if(!t.unicode||!t.color)return[];let e=re(t);return se.map(n=>e.brand(n.padEnd(oe)))}function ct(t,e){return e&&(t===e||t.startsWith(`${e}/`))?`~${t.slice(e.length)}`:t}function on(t,e,n){let r=n.bold("Claude Code"),s=t?.version?` ${n.dim(`v${t.version}`)}`:"",o=[`${r}${s}`],i=t?.model??null;if(i!==null){let u=t?.effort?` with ${t.effort} effort`:"",a=t?.fastMode?" \xB7 fast mode":"";o.push(n.dim(`${i}${u}${a}`))}return t?.cwd&&o.push(n.dim(ct(t.cwd,e))),o}var Zt={unicode:{filled:"\u2588",empty:"\u2592"},ascii:{filled:"#",empty:"-"}};function un(t,e,n,r){let s=r?Zt.unicode:Zt.ascii,o=Math.max(4,e),i=Math.min(o,Math.max(0,Math.round(t/100*o)));return`${n.brand(s.filled.repeat(i))}${n.dim(s.empty.repeat(o-i))}`}function ie(t){return t.pct===null?`quota unknown \xB7 resets ${H(t.resetsAt)}`:`${t.pct}% used \xB7 ${_(t.pct)}% left \xB7 resets ${H(t.resetsAt)}`}function an(t,e){return F(t)<=e?t:`${t.replace(ne,"").slice(0,Math.max(1,e-1))}\u2026`}function te(t,e){let n=[],r="";for(let s of t.split(/\s+/).filter(Boolean))r===""?r=s:r.length+1+s.length<=e?r+=` ${s}`:(n.push(r),r=s);return r!==""&&n.push(r),n}var ee={unicode:{tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502"},ascii:{tl:"+",tr:"+",bl:"+",br:"+",h:"-",v:"|"}};function ln(t){return Math.max(lt,Math.min(en,t-2))}function cn(t,e,n){return t.map((r,s)=>s===e?n.chosen(` ${r.label} `):n.dim(` ${r.label} `)).join(" ")}function ue({view:t,state:e,config:n,selected:r,caps:s,home:o}){let i=re(s),u=s.unicode?ee.unicode:ee.ascii,a=ln(s.columns),l=a-6,f=sn(s),w=on(t.session,o,i),g=f.length>0?oe:0,d=g>0?2:0,m=[];for(let c=0;c<Math.max(f.length,w.length);c+=1){let U=(f[c]??"").padEnd(g);m.push(`${U}${" ".repeat(d)}${an(w[c]??"",a-g-d)}`.trimEnd())}let P=[],h=(c="")=>P.push(c);h(i.bold(t.headline));let L=ie(e);if(e.pct!==null){let c=Math.max(6,l-F(L)-2);h(`${un(e.pct,c,i,s.unicode)} ${i.dim(L)}`)}h();for(let c of t.body)for(let U of te(c,l))h(i.dim(U));h(),h(cn(t.choices,r,i));let v=t.choices.map(c=>te(c.hint,l)),Q=Math.max(...v.map(c=>c.length)),x=v[r];for(let c=0;c<Q;c+=1)h(i.dim(x[c]??""));h(),h(i.dim(s.unicode?"\u2190/\u2192 choose \xB7 enter confirm \xB7 esc cancel":"left/right choose, enter confirm, esc cancel"));let z=` ${i.brand(i.bold("spare10"))} `,xt=`${n.reserve}% reserve`,Te=Math.max(1,a-6-F(z)-F(xt)),Ne=`${u.tl}${u.h}${z}${u.h.repeat(Te)} ${i.dim(xt)} ${u.h}${u.tr}`,Z=[...m,"",Ne];for(let c of P){let U=Math.max(0,a-4-F(c));Z.push(`${u.v} ${c}${" ".repeat(U)}${u.v}`)}return Z.push(`${u.bl}${u.h.repeat(a-2)}${u.br}`),Z}function ae(t,e){return`
22
+ spare10 \u2014 ${ie(e)}
23
+ ${t.headline}
24
+ ${t.choices[0].label}? [y/N] `}import{readFileSync as dn}from"node:fs";function dt(t){return`'${t.replace(/'/g,"'\\''")}'`}function J(t){let e;try{e=JSON.parse(dn(t,"utf8"))}catch{return{command:null,padding:void 0}}if(typeof e!="object"||e===null)return{command:null,padding:void 0};let n=e.statusLine;if(typeof n!="object"||n===null)return{command:null,padding:void 0};let r=n,s=typeof r.command=="string"?r.command:null,o=typeof r.padding=="number"?r.padding:void 0;return s!==null&&s.includes("spare10")?{command:null,padding:o}:{command:s,padding:o}}function le(t){let e=s=>`${dt(t.nodePath)} ${dt(t.selfPath)} ${s} --run ${dt(t.runDir)}`,n=s=>[{matcher:"",hooks:[{type:"command",command:e(s)}]}],r={type:"command",command:e("sensor"),refreshInterval:t.refresh};return t.padding!==void 0&&(r.padding=t.padding),{statusLine:r,hooks:{PreToolUse:n("gate")}}}import{closeSync as ce,openSync as pt,readSync as pn,writeSync as fn}from"node:fs";import{homedir as mn}from"node:os";import{ReadStream as gn,WriteStream as hn,isatty as bn}from"node:tty";function Sn(t){switch(t){case"\x1B[D":case"\x1B[A":case"\x1BOD":case"\x1BOA":return"prev";case"\x1B[C":case"\x1B[B":case"\x1BOC":case"\x1BOB":case" ":return"next";case"\r":case`
25
+ `:return"confirm";case"\x1B":case"":case"":return"cancel"}return/^[yY]$/.test(t)?"yes":/^[nNqQ]$/.test(t)?"no":t==="h"||t==="k"?"prev":t==="l"||t==="j"?"next":null}function wn(t){let e=[];for(let n=0;n<t.length;){let r=t[n+1];if(t[n]==="\x1B"&&(r==="["||r==="O")){let s=n+2;for(;s<t.length&&!/[@-~]/.test(t[s]);)s+=1;e.push(t.slice(n,s+1)),n=s+1;continue}e.push(t[n]),n+=1}return e}function yn(t,e){let n=t.TERM==="dumb"||t.TERM===void 0,r=!t.NO_COLOR&&!n,s=t.LC_ALL??t.LC_CTYPE??t.LANG??"";return{color:r,truecolor:r&&/truecolor|24bit/i.test(t.COLORTERM??""),unicode:!n&&(s===""||/utf-?8/i.test(s)),columns:e}}function $n(){let t;try{t=pt("/dev/tty","r")}catch{return null}let e=null,n=2;if(!process.stderr.isTTY)try{n=pt("/dev/tty","w"),e=new hn(n)}catch{n=2}return{fd:t,write:r=>{try{fn(n,r)}catch{}},columns:(e??process.stderr).columns||80,close:()=>{try{ce(t)}catch{}try{e?.destroy()}catch{}}}}var xn="\x1B[?25l",kn="\x1B[?25h",An=t=>`\r${"\x1B[K\x1B[A".repeat(t-1)}\x1B[K`;async function K({view:t,state:e,config:n,initial:r=1}){let s=$n();if(s===null)return null;let o=yn(process.env,s.columns),i=mn(),u=()=>{let m=Rn(s,ae(t,e));return s.close(),m};if(!bn(s.fd)||o.columns<lt+2)return u();let a;try{a=new gn(s.fd),a.setRawMode(!0)}catch{return u()}let l=r,f=0,w=()=>{let m=ue({view:t,state:e,config:n,selected:l,caps:o,home:i});s.write(`${f>0?An(f):""}${m.join(`
26
26
  `)}
27
- `),b=f.length+1},d=()=>{try{a.setRawMode(!1)}catch{}s.write(Kt)};process.once("exit",d),s.write(Jt),S();let p=await new Promise(f=>{let P=E=>{a.off("data",N),a.off("end",g),a.off("error",g),f(E)},g=()=>P(null),N=E=>{for(let V of Gt(E.toString("utf8"))){let $=Bt(V);if($==="prev"||$==="next"){l=(($==="next"?l+1:l-1)+e.choices.length)%e.choices.length,S();continue}if($==="yes"||$==="no"){l=$==="yes"?0:1,S(),P($==="yes"?"y":"n");return}if($==="confirm"){P(l===0?"y":"n");return}if($==="cancel"){l=1,S(),P("n");return}}};a.on("data",N),a.once("end",g),a.once("error",g),a.resume()});d(),process.off("exit",d);try{a.destroy()}catch{}return p}function Xt(e,t){let n;try{n=ne("/dev/tty","r")}catch{return null}e.write(t);let r=Buffer.alloc(1),s="";try{for(;Dt(n,r,0,1,null)>0;){let o=r.toString("utf8");if(o===`
28
- `||o==="\r")break;s+=o}}catch{return null}finally{Ge(n)}return s.trim()}var on=7*24*60*60*1e3,se=()=>k(process.env.SPARE10_HOME??k(Ke(),".spare10"),"runs"),oe=()=>k(Ke(),".claude","settings.json");function un(e,t){let n;try{n=qe(e)}catch{return}for(let r of n){let s=k(e,r);try{t-tn(s).mtimeMs>on&&en(s,{recursive:!0,force:!0})}catch{}}}function an(e,t){let n;try{n=qe(e)}catch{return}let r=n.map(o=>k(e,o)).filter(o=>o!==t).map(x),s=ke(r,y());s!==null&&R(t,s)}function ln(e){let t=`${Date.now().toString(36)}-${process.pid.toString(36)}`,n=k(e,t);return Zt(n,{recursive:!0}),n}var Ye=e=>/^y(es)?$/i.test(e.trim());function cn(e,t){return e?t===null?"proceeding":Ye(t)?"consented":"declined":"clear"}function dn(){return{version:null,model:null,effort:null,cwd:process.cwd(),fastMode:!1}}function pn(e){return{session:dn(),headline:"This session would start inside the reserve.",body:[`You have less than the ${e.reserve}% reserve left for this session. If you decide to start anyway, spare10 will stay quiet until the limit resets.`],choices:[{label:"Start anyway",hint:"Runs now on your reserve."},{label:"Do not start",hint:"Back to the shell."}]}}function fn(e){return{session:e.session,headline:"Reserve reached. The session is paused.",body:["spare10 stopped the agent between tool calls, so nothing is half-written and the whole conversation is saved."],choices:[{label:"Resume",hint:"Continue on the reserve. spare10 stays quiet until the limit resets."},{label:"Stop here",hint:`Back to the shell. You can still resume later with: claude --resume ${e.halted??""}`}]}}async function mn(e,t){let n=x(e);if(!ye(n,t,y()))return"clear";let r=await re({view:pn(t),state:n,config:t});return cn(!0,r)}function ie(){return sn(import.meta.url)}function gn(e,t=ie()){let n=k(e,"spare10.js");try{return zt(t,n),n}catch{return t}}function hn(e,t=process.env){return!(e.includes("--no-session-persistence")||t.CLAUDE_CODE_CHILD_SESSION&&!t.CLAUDE_CODE_FORCE_SESSION_PERSISTENCE)}var bn=new Set(["--agent","--agents","--append-system-prompt","--append-system-prompt-file","--autocompact","--debug-file","--effort","--environment","--fallback-model","--input-format","--json-schema","--max-budget-usd","--model","-n","--name","--output-format","--permission-mode","--permission-prompts","--plugin-dir","--plugin-url","--remote-control-session-name-prefix","--setting-sources","--settings","--system-prompt","--system-prompt-file","--system-prompt-snapshot"]),Sn=new Set(["--add-dir","--allowedTools","--allowed-tools","--betas","--disallowedTools","--disallowed-tools","--file","--mcp-config","--tools"]),wn=new Set(["-d","--debug","--prompt-suggestions","--remote-control"]),yn=new Set(["-r","--resume","-w","--worktree","--from-pr","--teleport","--cloud"]),$n=new Set(["--session-id"]),xn=new Set(["-c","--continue","--tmux","--bg","--background"]),Pn="spare10 budget guard: the user stopped this session as it entered the reserve and has now chosen to resume it. Continue from where you left off.";function Rn(e,t){let n=[],r=o=>e[o],s=o=>r(o)!==void 0&&!r(o).startsWith("-");for(let o=0;o<e.length;o+=1){let i=r(o);if(i==="--")break;let u=i.startsWith("-")&&i.includes("="),a=u?i.split("=")[0]:i;if(!xn.has(a)){if($n.has(a)){u||(o+=1);continue}if(yn.has(a)){!u&&s(o+1)&&(o+=1);continue}if(bn.has(a)){n.push(i),!u&&r(o+1)!==void 0&&n.push(r(++o));continue}if(wn.has(a)){n.push(i),!u&&s(o+1)&&n.push(r(++o));continue}if(Sn.has(a)){for(n.push(i);!u&&s(o+1);)n.push(r(++o));continue}if(i.startsWith("-")){n.push(i);continue}}}return["--resume",t,...n,Pn]}function An(e,t,n,r){return new Promise(s=>{let o=Qt(e,t,{stdio:"inherit"});r&&o.pid!==void 0&&Je(O(n),String(o.pid));let i=u=>{try{nn(O(n))}catch{}s(u)};o.once("error",u=>i({status:null,signal:null,error:u})),o.once("exit",(u,a)=>i({status:u,signal:a,error:null}))})}var kn=e=>e.signal==="SIGTERM"||e.status===143;function We(e){return e.signal?128+(e.signal==="SIGINT"?2:15):e.status??0}function En(e){return e===null?"unattended":Ye(e)?"resume":"declined"}async function Xe({config:e,command:t}){let[n,...r]=t;if(rn(n)!=="claude")throw new h(`spare10 wraps the "claude" command, but got "${n}". Run it as: ${["spare10",...r,"claude"].join(" ")}`);let s=se();un(s,Date.now());let o=ln(s);an(s,o);let i=await mn(o,{...e,chain:null});if(i==="declined")return process.stderr.write(`Not started.
29
- `),0;if(i==="consented"){let p=x(o);R(o,{...p,disarmedUntil:Q(p,y())}),process.stderr.write(`Continuing into the reserve for this window.
30
-
31
- `)}let u=j(oe()),a={...e,chain:u.command};Je(X(o),JSON.stringify(a,null,2));let l=JSON.stringify(Te({nodePath:process.execPath,selfPath:gn(o),runDir:o,refresh:e.refresh,padding:u.padding})),b=hn(r);b||process.stderr.write(`spare10: this session will not be saved, so it cannot be stopped and resumed; on trip the gate will deny tool calls instead.
32
- `);let S=()=>{};process.on("SIGINT",S),process.on("SIGTERM",S);let d=r;for(;;){let p=await An(n,["--settings",l,...d],o,b);if(p.error)return process.stderr.write(`spare10: could not start ${n}: ${p.error.message}
33
- `),127;let f=x(o);if(!kn(p)||f.halted===null)return We(p);let P=En(await re({view:fn(f),state:f,config:a}));if(P!=="resume")return process.stderr.write(`Not resumed. To pick it up later: claude --resume ${f.halted}
34
- `),P==="declined"?0:We(p);R(o,{...f,halted:null,disarmedUntil:Q(f,y())}),process.stderr.write(`Resuming into the reserve for this window.
35
-
36
- `),d=Rn(r,f.halted)}}var Tn="0.4.0",m={ok:"\u2713",warn:"\u26A0",info:"\xB7"};function Nn(e){try{return In(e).map(n=>_n(e,n)).filter(n=>{try{return ue(n).isDirectory()}catch{return!1}}).sort((n,r)=>ue(r).mtimeMs-ue(n).mtimeMs)[0]??null}catch{return null}}function Mn(e){try{return Qe("command",["-v",e],{shell:!0,encoding:"utf8"}).trim()||null}catch{return null}}function On(){try{return Qe("claude",["--version"],{encoding:"utf8",timeout:1e4}).trim()}catch{return null}}function Ln(e){try{return vn(e,Cn.X_OK),!0}catch{return!1}}function ze(){let e=(d="")=>process.stdout.write(`${d}
37
- `),t=y(),n=0;e(`spare10 ${Tn}`),e(),e("Environment"),e(` ${m.info} node ${process.execPath} (${process.version})`),e(` ${m.info} spare10 ${ie()}`);let r=Mn("claude"),s=r?On():null;r&&s?e(` ${m.ok} claude ${r} (${s})`):(n+=1,e(` ${m.warn} claude not found on PATH \u2014 spare10 has nothing to wrap`)),e(),e("Status line");let o=j(oe());if(o.command===null)e(` ${m.info} nothing to chain \u2014 spare10 will own the status line`);else{let d=o.command.split(/\s+/)[0]??"",p=Ln(d);p||(n+=1),e(` ${p?m.ok:m.warn} chains into ${o.command}`),p||e(" that path is not executable; its output will be dropped")}e();let i=Nn(se());if(i===null)return e("No runs yet. Start one with: spare10 claude"),n>0?1:0;let u=_(i),a=x(i),l=_e(a,u,t);if(e(`Latest run ${i}`),e(` ${m.info} reserve ${u.reserve}% of the 5-hour window`),e(` ${m.info} on trip ${u.pausePrompt===null?"stop the session and ask in the terminal":`inject: ${JSON.stringify(u.pausePrompt)}`}`),e(` ${m.info} refresh ${u.refresh}s`),a.pct!==null&&a.updatedAt!==null){e(` ${m.ok} quota ${a.pct}% used, ${100-a.pct}% left`);let d=t-a.updatedAt;e(` ${m.info} last reading ${z(d)} ago`+(d>u.refresh*3?" (no session running; still valid for this window)":""))}a.resetsAt!==null&&e(` ${m.info} resets ${Z(a.resetsAt)} (in ${z(a.resetsAt-t)})`);let b=l.status==="blind";b&&(n+=1);let S=b?m.warn:l.status==="no-data"?m.info:m.ok;return e(` ${S} state ${l.status.toUpperCase()} \u2014 ${l.detail}`),n>0?1:0}import{spawnSync as Un}from"node:child_process";import{readFileSync as Fn}from"node:fs";function Ze(e){return typeof e=="number"&&Number.isFinite(e)?e:null}var le=e=>typeof e=="string"&&e?e:null,ae=(e,t,n)=>{let r=e[t];return typeof r=="object"&&r!==null?le(r[n]):null};function Dn(e){let t={version:le(e.version),model:ae(e,"model","display_name"),effort:ae(e,"effort","level"),cwd:ae(e,"workspace","current_dir")??le(e.cwd),fastMode:e.fast_mode===!0};return t.version!==null||t.model!==null||t.cwd!==null||t.fastMode?t:null}function et(e){let t={sessionId:null,fiveHour:null,session:null},n;try{n=JSON.parse(e)}catch{return t}if(typeof n!="object"||n===null)return t;let r=n,s=typeof r.session_id=="string"?r.session_id:null,o=Dn(r),i=r.rate_limits;if(typeof i!="object"||i===null)return{sessionId:s,fiveHour:null,session:o};let u=i.five_hour;if(typeof u!="object"||u===null)return{sessionId:s,fiveHour:null,session:o};let a=u,l=Ze(a.used_percentage);return l===null?{sessionId:s,fiveHour:null,session:o}:{sessionId:s,session:o,fiveHour:{usedPercentage:Math.min(100,Math.max(0,l)),resetsAt:Ze(a.resets_at)}}}function jn(e,t,n){let r=t.fiveHour,s=e.tick+1,o=t.session??e.session;if(!r){let u=e.missingStreak+1;return{...e,tick:s,session:o,missingStreak:u,blind:u>=ge}}let i=r.resetsAt!==null&&e.resetsAt!==null&&r.resetsAt!==e.resetsAt;return{tick:s,session:o,pct:r.usedPercentage,resetsAt:r.resetsAt,updatedAt:n,missingStreak:0,blind:!1,disarmedUntil:i?null:e.disarmedUntil,pausePromptInjectedTo:i?[]:e.pausePromptInjectedTo,halted:e.halted}}var Hn="\x1B[38;5;208m",Vn="\x1B[38;5;40m",Bn="\x1B[38;5;245m",de="\x1B[39m",ce=e=>`${Hn}${e}${de}`,Gn=e=>`${Vn}${e}${de}`,Wn=e=>`${Bn}${e}${de}`,H=e=>e.reserve===q?"spare10":`spare10 (${e.reserve}%)`;function qn(e,t,n){if(!t.badge)return"";if(e.blind)return"\u26A0 spare10 quota unavailable";if(e.pct===null)return Wn(`\u29D7 ${H(t)}`);if(e.pct<v(t))return Gn(`\u25CF ${H(t)}`);if(e.disarmedUntil!==null&&n<e.disarmedUntil)return ce(`\u2A2F ${H(t)}`);if(e.pausePromptInjectedTo.length>0)return ce(`\u23F8 ${H(t)}`);let s=e.tick%2===0?"\u26A0":" ";return ce(`${s} Pausing at next tool call`)}function Jn(e,t){if(!e||e.includes("spare10 sensor"))return"";try{return(Un(e,{shell:!0,input:t,encoding:"utf8",timeout:5e3,maxBuffer:1e6}).stdout??"").replace(/\n+$/,"")}catch{return""}}function Kn(){try{return Fn(0,"utf8")}catch{return""}}function tt(e){let t=Kn(),n=_(e),r=jn(x(e),et(t),y());R(e,r);let s=[qn(r,n,y()),Jn(n.chain,t)].filter(Boolean);return s.length>0&&process.stdout.write(s.join(" ")),0}function nt(e,t){let n=e.indexOf(t);return n===-1?null:e[n+1]??null}function Yn(e){let[t,...n]=e;switch(t){case"sensor":{let r=nt(n,"--run");return r?tt(r):0}case"gate":{let r=nt(n,"--run");return r?Ie(r):0}case"doctor":return ze();default:return Xe(Se(e))}}function rt(e){throw e instanceof h&&(e.message&&(process.stderr.write(`spare10: ${e.message}
38
-
39
- ${J}
40
- `),process.exit(2)),process.stdout.write(`${J}
41
- `),process.exit(0)),e}try{Promise.resolve(Yn(process.argv.slice(2))).then(e=>process.exit(e),rt)}catch(e){rt(e)}
27
+ `),f=m.length+1},g=()=>{try{a.setRawMode(!1)}catch{}s.write(kn)};process.once("exit",g),s.write(xn),w();let d=await new Promise(m=>{let P=v=>{a.off("data",L),a.off("end",h),a.off("error",h),m(v)},h=()=>P(null),L=v=>{for(let Q of wn(v.toString("utf8"))){let x=Sn(Q);if(x==="prev"||x==="next"){l=((x==="next"?l+1:l-1)+t.choices.length)%t.choices.length,w();continue}if(x==="yes"||x==="no"){l=x==="yes"?0:1,w(),P(x==="yes"?"y":"n");return}if(x==="confirm"){P(l===0?"y":"n");return}if(x==="cancel"){l=1,w(),P("n");return}}};a.on("data",L),a.once("end",h),a.once("error",h),a.resume()});g(),process.off("exit",g);try{a.destroy()}catch{}return d}function Rn(t,e){let n;try{n=pt("/dev/tty","r")}catch{return null}t.write(e);let r=Buffer.alloc(1),s="";try{for(;pn(n,r,0,1,null)>0;){let o=r.toString("utf8");if(o===`
28
+ `||o==="\r")break;s+=o}}catch{return null}finally{ce(n)}return s.trim()}var Mn=7*24*60*60*1e3,Y=()=>E(process.env.SPARE10_HOME??E(mt(),".spare10"),"runs"),gt=()=>E(mt(),".claude","settings.json");function On(t,e){let n;try{n=fe(t)}catch{return}for(let r of n){let s=E(t,r);try{e-Cn(s).mtimeMs>Mn&&vn(s,{recursive:!0,force:!0})}catch{}}}function Ln(t,e){let n;try{n=fe(t)}catch{return}let r=n.map(o=>E(t,o)).filter(o=>o!==e).map($),s=Gt(r,S());s!==null&&k(e,s)}function Un(t){let e=`${Date.now().toString(36)}-${process.pid.toString(36)}`,n=E(t,e);return En(n,{recursive:!0}),n}var ge=t=>/^y(es)?$/i.test(t.trim());function Dn(t,e){return t?e===null?"proceeding":ge(e)?"consented":"declined":"clear"}function he(){return{version:null,model:null,effort:null,cwd:process.cwd(),fastMode:!1}}function jn(t){return{session:he(),headline:"This session would start inside the reserve.",body:[`You have less than the ${t.reserve}% reserve left for this session. If you decide to start anyway, spare10 will stay quiet until the limit resets.`],choices:[{label:"Start anyway",hint:"Runs now on your reserve."},{label:"Do not start",hint:"Back to the shell."}]}}function Fn(t){return{session:t.session,headline:"Reserve reached. The session is paused.",body:["spare10 stopped the agent between tool calls, so nothing is half-written and the whole conversation is saved."],choices:[{label:"Resume",hint:"Continue on the reserve. spare10 stays quiet until the limit resets."},{label:"Stop here",hint:`Back to the shell. You can still resume later with: claude --resume ${t.halted??""}`}]}}var Bn=5,ft=(t,e,n)=>t===1?e:`${t} ${n}`;function Hn(t,e=mt()){let n=t.slice(0,Bn),r=[`spare10 stopped ${ft(t.length,"a background session","background sessions")} at the reserve, between tool calls. Their conversations are kept.`,...n.map(s=>`${s.name??s.backgroundId} \xB7 ${ct(s.cwd??"",e)}`.trim())];return t.length>n.length&&r.push(`\u2026and ${t.length-n.length} more.`),{session:he(),headline:`Reserve reached. ${ft(t.length,"A background session is","background sessions are")} paused.`,body:r,choices:[{label:t.length===1?"Resume":"Resume all",hint:"Back to the background, on your reserve. spare10 stays quiet until the limit resets."},{label:"Leave stopped",hint:"Back to the shell. Each one can still be opened with: claude attach <id>"}]}}async function Vn(t,e){let n=W(t);if(n.length===0)return;let r=B(n,T());if(it(t,n.filter(i=>!r.includes(i)).map(i=>i.sessionId)),r.length===0)return;let s=$(t);if(xe(await K({view:Hn(r),state:s,config:e}))!=="resume"){process.stderr.write(`Left stopped. To pick ${r.length===1?"it":"them"} up later:
29
+ `);for(let i of r)process.stderr.write(` claude attach ${i.backgroundId}
30
+ `);return}k(t,{...s,disarmedUntil:q(s,S())});let o=[];for(let i of r)Mt(i.sessionId,i.cwd)?o.push(i.sessionId):process.stderr.write(`spare10: could not resume ${i.backgroundId}. Try: claude attach ${i.backgroundId}
31
+ `);it(t,o),o.length>0&&process.stderr.write(`Resumed ${ft(o.length,"one session","sessions")} into the reserve for this window.
32
+ `)}async function Gn(t,e){let n=$(t);if(!Lt(n,e,S()))return"clear";let r=await K({view:jn(e),state:n,config:e});return Dn(!0,r)}function ht(){return Nn(import.meta.url)}function Wn(t,e=ht()){let n=E(t,"spare10.js");try{return In(e,n),n}catch{return e}}function qn(t,e=process.env){return!(t.includes("--no-session-persistence")||e.CLAUDE_CODE_CHILD_SESSION&&!e.CLAUDE_CODE_FORCE_SESSION_PERSISTENCE)}var be=new Set(["--agent","--agents","--append-system-prompt","--append-system-prompt-file","--autocompact","--debug-file","--effort","--environment","--fallback-model","--input-format","--json-schema","--max-budget-usd","--model","-n","--name","--output-format","--permission-mode","--permission-prompts","--plugin-dir","--plugin-url","--remote-control-session-name-prefix","--setting-sources","--settings","--system-prompt","--system-prompt-file","--system-prompt-snapshot"]),Se=new Set(["--add-dir","--allowedTools","--allowed-tools","--betas","--disallowedTools","--disallowed-tools","--file","--mcp-config","--tools"]),we=new Set(["-d","--debug","--prompt-suggestions","--remote-control"]),ye=new Set(["-r","--resume","-w","--worktree","--from-pr","--teleport","--cloud"]),$e=new Set(["--session-id"]),Jn=new Set(["-c","--continue","--tmux","--bg","--background"]),Kn=new Set(["agents","attach","auth","auto-mode","doctor","gateway","import","install","logs","mcp","plugin","plugins","project","respawn","rm","setup-token","stop","kill","ultrareview","update","upgrade"]);function Yn(t){for(let e=0;e<t.length;e+=1){let n=t[e];if(n==="--")return null;if(!n.startsWith("-"))return Kn.has(n)?n:null;if(n.includes("="))continue;let r=s=>t[s]!==void 0&&!t[s].startsWith("-");if(be.has(n)||$e.has(n))e+=1;else if(we.has(n)||ye.has(n))r(e+1)&&(e+=1);else if(Se.has(n))for(;r(e+1);)e+=1}return null}function de(t){return t.includes("--bg")||t.includes("--background")}var Xn="spare10 budget guard: the user stopped this session as it entered the reserve and has now chosen to resume it. Continue from where you left off.";function Qn(t,e){let n=[],r=o=>t[o],s=o=>r(o)!==void 0&&!r(o).startsWith("-");for(let o=0;o<t.length;o+=1){let i=r(o);if(i==="--")break;let u=i.startsWith("-")&&i.includes("="),a=u?i.split("=")[0]:i;if(!Jn.has(a)){if($e.has(a)){u||(o+=1);continue}if(ye.has(a)){!u&&s(o+1)&&(o+=1);continue}if(be.has(a)){n.push(i),!u&&r(o+1)!==void 0&&n.push(r(++o));continue}if(we.has(a)){n.push(i),!u&&s(o+1)&&n.push(r(++o));continue}if(Se.has(a)){for(n.push(i);!u&&s(o+1);)n.push(r(++o));continue}if(i.startsWith("-")){n.push(i);continue}}}return["--resume",e,...n,Xn]}function zn(t,e,n,r){return new Promise(s=>{let o=Pn(t,e,{stdio:"inherit"});r&&o.pid!==void 0&&me(j(n),String(o.pid));let i=u=>{try{_n(j(n))}catch{}s(u)};o.once("error",u=>i({status:null,signal:null,error:u})),o.once("exit",(u,a)=>i({status:u,signal:a,error:null}))})}var Zn=t=>t.signal==="SIGTERM"||t.status===143;function pe(t){return t.signal?128+(t.signal==="SIGINT"?2:15):t.status??0}function xe(t){return t===null?"unattended":ge(t)?"resume":"declined"}async function ke({config:t,command:e}){let[n,...r]=e;if(Tn(n)!=="claude")throw new b(`spare10 wraps the "claude" command, but got "${n}". Run it as: ${["spare10",...r,"claude"].join(" ")}`);let s=Y();On(s,Date.now());let o=Un(s);Ln(s,o);let i=await Gn(o,{...t,chain:null});if(i==="declined")return process.stderr.write(`Not started.
33
+ `),0;if(i==="consented"){let m=$(o);k(o,{...m,disarmedUntil:q(m,S())}),process.stderr.write(`Continuing into the reserve for this window.
34
+
35
+ `)}let u=J(gt()),a={...t,chain:u.command};me(st(o),JSON.stringify(a,null,2));let l=JSON.stringify(le({nodePath:process.execPath,selfPath:Wn(o),runDir:o,refresh:t.refresh,padding:u.padding})),f=Yn(r)===null&&!de(r),w=f&&qn(r);de(r)?process.stderr.write(`spare10: --bg returns as soon as the session is dispatched, so spare10 will not be here to ask about resuming it. It still stops the session at the reserve; "spare10 doctor" lists what it stopped.
36
+ `):f&&!w&&process.stderr.write(`spare10: this session will not be saved, so it cannot be stopped and resumed; on trip the gate will deny tool calls instead.
37
+ `);let g=()=>{};process.on("SIGINT",g),process.on("SIGTERM",g);let d=await tr({executable:n,rest:r,settings:l,runDir:o,runConfig:a,canResume:w});return await Vn(o,a),d}async function tr(t){let{executable:e,rest:n,settings:r,runDir:s,runConfig:o,canResume:i}=t,u=n;for(;;){let a=await zn(e,["--settings",r,...u],s,i);if(a.error)return process.stderr.write(`spare10: could not start ${e}: ${a.error.message}
38
+ `),127;let l=$(s);if(!Zn(a)||l.halted===null)return pe(a);let f=xe(await K({view:Fn(l),state:l,config:o}));if(f!=="resume")return process.stderr.write(`Not resumed. To pick it up later: claude --resume ${l.halted}
39
+ `),f==="declined"?0:pe(a);k(s,{...l,halted:null,disarmedUntil:q(l,S())}),process.stderr.write(`Resuming into the reserve for this window.
40
+
41
+ `),u=Qn(n,l.halted)}}var or="0.5.0",p={ok:"\u2713",warn:"\u26A0",info:"\xB7"};function Re(t){try{return rr(t).map(e=>sr(t,e)).filter(e=>{try{return bt(e).isDirectory()}catch{return!1}}).sort((e,n)=>bt(n).mtimeMs-bt(e).mtimeMs)}catch{return[]}}var ir=t=>Re(t)[0]??null;function ur(t){try{return Ae("command",["-v",t],{shell:!0,encoding:"utf8"}).trim()||null}catch{return null}}function ar(){try{return Ae("claude",["--version"],{encoding:"utf8",timeout:1e4}).trim()}catch{return null}}function lr(t){try{return er(t,nr.X_OK),!0}catch{return!1}}function Pe(){let t=(d="")=>process.stdout.write(`${d}
42
+ `),e=S(),n=0;t(`spare10 ${or}`),t(),t("Environment"),t(` ${p.info} node ${process.execPath} (${process.version})`),t(` ${p.info} spare10 ${ht()}`);let r=ur("claude"),s=r?ar():null;r&&s?t(` ${p.ok} claude ${r} (${s})`):(n+=1,t(` ${p.warn} claude not found on PATH \u2014 spare10 has nothing to wrap`)),t(),t("Status line");let o=J(gt());if(o.command===null)t(` ${p.info} nothing to chain \u2014 spare10 will own the status line`);else{let d=o.command.split(/\s+/)[0]??"",m=lr(d);m||(n+=1),t(` ${m?p.ok:p.warn} chains into ${o.command}`),m||t(" that path is not executable; its output will be dropped")}t();let i=ir(Y());if(i===null)return t("No runs yet. Start one with: spare10 claude"),n>0?1:0;let u=M(i),a=$(i),l=Qt(a,u,e);if(t(`Latest run ${i}`),t(` ${p.info} reserve ${u.reserve}% of the 5-hour window`),t(` ${p.info} on trip ${u.pausePrompt===null?"stop the session and ask in the terminal":`inject: ${JSON.stringify(u.pausePrompt)}`}`),t(` ${p.info} refresh ${u.refresh}s`),a.pct!==null&&a.updatedAt!==null){t(` ${p.ok} quota ${a.pct}% used, ${100-a.pct}% left`);let d=e-a.updatedAt;t(` ${p.info} last reading ${ut(d)} ago`+(d>u.refresh*3?" (no session running; still valid for this window)":""))}a.resetsAt!==null&&t(` ${p.info} resets ${at(a.resetsAt)} (in ${ut(a.resetsAt-e)})`);let f=l.status==="blind";f&&(n+=1);let w=f?p.warn:l.status==="no-data"?p.info:p.ok;t(` ${w} state ${l.status.toUpperCase()} \u2014 ${l.detail}`);let g=B(Re(Y()).flatMap(W),T());if(g.length>0){t(),t(`Stopped in the background, waiting to be resumed (${g.length})`);for(let d of g)t(` ${p.info} ${d.name??d.backgroundId}`),t(` claude attach ${d.backgroundId}`)}return n>0?1:0}import{spawnSync as dr}from"node:child_process";import{readFileSync as pr}from"node:fs";function Ie(t){return typeof t=="number"&&Number.isFinite(t)?t:null}var wt=t=>typeof t=="string"&&t?t:null,St=(t,e,n)=>{let r=t[e];return typeof r=="object"&&r!==null?wt(r[n]):null};function cr(t){let e={version:wt(t.version),model:St(t,"model","display_name"),effort:St(t,"effort","level"),cwd:St(t,"workspace","current_dir")??wt(t.cwd),fastMode:t.fast_mode===!0};return e.version!==null||e.model!==null||e.cwd!==null||e.fastMode?e:null}function Ee(t){let e={sessionId:null,fiveHour:null,session:null},n;try{n=JSON.parse(t)}catch{return e}if(typeof n!="object"||n===null)return e;let r=n,s=typeof r.session_id=="string"?r.session_id:null,o=cr(r),i=r.rate_limits;if(typeof i!="object"||i===null)return{sessionId:s,fiveHour:null,session:o};let u=i.five_hour;if(typeof u!="object"||u===null)return{sessionId:s,fiveHour:null,session:o};let a=u,l=Ie(a.used_percentage);return l===null?{sessionId:s,fiveHour:null,session:o}:{sessionId:s,session:o,fiveHour:{usedPercentage:Math.min(100,Math.max(0,l)),resetsAt:Ie(a.resets_at)}}}function fr(t,e,n){let r=e.fiveHour,s=t.tick+1,o=e.session??t.session;if(!r){let u=t.missingStreak+1;return{...t,tick:s,session:o,missingStreak:u,blind:u>=Rt}}let i=r.resetsAt!==null&&t.resetsAt!==null&&r.resetsAt!==t.resetsAt;return{tick:s,session:o,pct:r.usedPercentage,resetsAt:r.resetsAt,updatedAt:n,missingStreak:0,blind:!1,disarmedUntil:i?null:t.disarmedUntil,pausePromptInjectedTo:i?[]:t.pausePromptInjectedTo,halted:t.halted}}var mr="\x1B[38;5;208m",gr="\x1B[38;5;40m",hr="\x1B[38;5;245m",$t="\x1B[39m",yt=t=>`${mr}${t}${$t}`,br=t=>`${gr}${t}${$t}`,Sr=t=>`${hr}${t}${$t}`,X=t=>t.reserve===et?"spare10":`spare10 (${t.reserve}%)`;function wr(t,e,n){if(!e.badge)return"";if(t.blind)return"\u26A0 spare10 quota unavailable";if(t.pct===null)return Sr(`\u29D7 ${X(e)}`);if(t.pct<C(e))return br(`\u25CF ${X(e)}`);if(t.disarmedUntil!==null&&n<t.disarmedUntil)return yt(`\u2A2F ${X(e)}`);if(t.pausePromptInjectedTo.length>0)return yt(`\u23F8 ${X(e)}`);let s=t.tick%2===0?"\u26A0":" ";return yt(`${s} Pausing at next tool call`)}function yr(t,e){if(!t||t.includes("spare10 sensor"))return"";try{return(dr(t,{shell:!0,input:e,encoding:"utf8",timeout:5e3,maxBuffer:1e6}).stdout??"").replace(/\n+$/,"")}catch{return""}}function $r(){try{return pr(0,"utf8")}catch{return""}}function ve(t){let e=$r(),n=M(t),r=fr($(t),Ee(e),S());k(t,r);let s=[wr(r,n,S()),yr(n.chain,e)].filter(Boolean);return s.length>0&&process.stdout.write(s.join(" ")),0}function Ce(t,e){let n=t.indexOf(e);return n===-1?null:t[n+1]??null}function xr(t){let[e,...n]=t;switch(e){case"sensor":{let r=Ce(n,"--run");return r?ve(r):0}case"gate":{let r=Ce(n,"--run");return r?Xt(r):0}case"doctor":return Pe();default:return ke(Et(t))}}function _e(t){throw t instanceof b&&(t.message&&(process.stderr.write(`spare10: ${t.message}
43
+
44
+ ${nt}
45
+ `),process.exit(2)),process.stdout.write(`${nt}
46
+ `),process.exit(0)),t}try{Promise.resolve(xr(process.argv.slice(2))).then(t=>process.exit(t),_e)}catch(t){_e(t)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spare10",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Circuit breaker for Claude Code: pause autonomous sessions before the 5-hour quota runs out",
5
5
  "keywords": [
6
6
  "claude-code",