borgmcp 1.0.55 → 1.0.57

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
@@ -78,6 +78,10 @@ To join under a specific role:
78
78
  borg assimilate code-reviewer --cli codex
79
79
  ```
80
80
 
81
+ For Claude Code launches, Borg adds `--allowedTools mcp__borg__*` so Borg
82
+ coordination tools can run without repeated permission prompts. The allowlist is
83
+ only for Borg MCP tools; shell, file, and web actions still prompt normally.
84
+
81
85
  ## Core MCP tools
82
86
 
83
87
  After assimilation, the agent session has `borg_` tools available:
@@ -48,6 +48,19 @@ export declare function shouldShowLaunchMenu(args: {
48
48
  stdinIsTTY: boolean;
49
49
  stdoutIsTTY: boolean;
50
50
  }): boolean;
51
+ export declare function explicitCliLaunchHint(args: {
52
+ explicitCli: BorgCli | undefined;
53
+ stdinIsTTY: boolean;
54
+ stdoutIsTTY: boolean;
55
+ hasActiveCube: boolean;
56
+ hasLaunchAllTargets: boolean;
57
+ }): string | null;
58
+ export declare function shouldResolveExplicitCliLaunchHintTargets(args: {
59
+ explicitCli: BorgCli | undefined;
60
+ stdinIsTTY: boolean;
61
+ stdoutIsTTY: boolean;
62
+ hasActiveCube: boolean;
63
+ }): boolean;
51
64
  /**
52
65
  * The context-filtered option set. Option 1 is always present; options 2/3 are
53
66
  * included only when applicable. Keys are sequential with no gaps, so a hidden
@@ -1,5 +1,6 @@
1
- const u={claude:"Claude",codex:"Codex"};function f(n){return n.extraArgs.length===0&&n.stdinIsTTY&&n.stdoutIsTTY}function h(n){const t=[{key:"1",label:`Launch (default \xB7 ${u[n.defaultCli]})`,action:{kind:"launch",cli:n.defaultCli}}];return n.otherInstalledCli&&t.push({key:String(t.length+1),label:`Launch with ${u[n.otherInstalledCli]} instead (one-shot)`,action:{kind:"launch",cli:n.otherInstalledCli}}),n.hasLaunchAllTargets&&t.push({key:String(t.length+1),label:"Launch all (this cube's drone worktrees)",action:{kind:"launch-all"}}),t}function s(n,t){const e=t.trim();if(e==="")return{ok:!0,action:n[0].action};const o=n.find(a=>a.key===e);return o?{ok:!0,action:o.action}:{ok:!1}}function d(n){return`borg \u2014 how do you want to launch?
1
+ const c={claude:"Claude",codex:"Codex"};function f(n){return n.extraArgs.length===0&&n.stdinIsTTY&&n.stdoutIsTTY}function x(n){return!n.explicitCli||!n.stdinIsTTY||!n.stdoutIsTTY||!n.hasActiveCube||!n.hasLaunchAllTargets?null:`borg --cli ${n.explicitCli} launches ${c[n.explicitCli]} directly; use bare borg for the launch menu or borg launch-all --cli ${n.explicitCli} for all drone worktrees.
2
+ `}function C(n){return!!(n.explicitCli&&n.stdinIsTTY&&n.stdoutIsTTY&&n.hasActiveCube)}function h(n){const t=[{key:"1",label:`Launch (default \xB7 ${c[n.defaultCli]})`,action:{kind:"launch",cli:n.defaultCli}}];return n.otherInstalledCli&&t.push({key:String(t.length+1),label:`Launch with ${c[n.otherInstalledCli]} instead (one-shot)`,action:{kind:"launch",cli:n.otherInstalledCli}}),n.hasLaunchAllTargets&&t.push({key:String(t.length+1),label:"Launch all (this cube's drone worktrees)",action:{kind:"launch-all"}}),t}function s(n,t){const e=t.trim();if(e==="")return{ok:!0,action:n[0].action};const i=n.find(l=>l.key===e);return i?{ok:!0,action:i.action}:{ok:!1}}function d(n){return`borg \u2014 how do you want to launch?
2
3
  ${n.map(e=>` ${e.key}) ${e.label}`).join(`
3
4
  `)}
4
- [1]: `}async function k(n,t,e={}){const o=h(n);if(o.length===1)return o[0].action;const a=e.maxAttempts??3,c=d(o);for(let l=0;l<a;l++){const i=await t(l===0?c:`Invalid choice.
5
- ${c}`),r=s(o,i);if(r.ok)return r.action;e.warn?.(`invalid launch-menu selection: ${JSON.stringify(i.trim())}`)}return o[0].action}export{h as buildLaunchMenuOptions,d as renderLaunchMenu,s as resolveLaunchMenuChoice,k as runBareLaunchMenu,f as shouldShowLaunchMenu};
5
+ [1]: `}async function T(n,t,e={}){const i=h(n);if(i.length===1)return i[0].action;const l=e.maxAttempts??3,u=d(i);for(let o=0;o<l;o++){const a=await t(o===0?u:`Invalid choice.
6
+ ${u}`),r=s(i,a);if(r.ok)return r.action;e.warn?.(`invalid launch-menu selection: ${JSON.stringify(a.trim())}`)}return i[0].action}export{h as buildLaunchMenuOptions,x as explicitCliLaunchHint,d as renderLaunchMenu,s as resolveLaunchMenuChoice,T as runBareLaunchMenu,C as shouldResolveExplicitCliLaunchHintTargets,f as shouldShowLaunchMenu};
package/dist/claude.js CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as E}from"child_process";import{randomUUID as M}from"node:crypto";import{basename as O}from"node:path";import{createInterface as H}from"node:readline/promises";import t from"chalk";import{findProjectRoot as y,getActiveCube as F,getLaunchModel as N,inboxPathForDrone as B,setCodexWakeTarget as _,pruneDeadCodexWakeTargets as W}from"./cubes.js";import{applyOllamaLaunchEnv as G,checkModelReachable as Y}from"./model-presets.js";import{handleVersionFlag as U,getPackageVersion as h}from"./version.js";import{isHelpFlag as T,setupHelpText as V,topLevelHelpText as K,assimilateHelpText as j}from"./cli-help.js";import{runSpawn as q}from"./spawn.js";import{buildClaudeLaunchArgs as X}from"./claude-launch-args.js";import{parseSyncArgs as z,runSync as J}from"./sync.js";import{parseCleanupArgs as Q,runCleanup as Z}from"./cleanup-cmd.js";import{parseAssimilateArgs as ee}from"./parse-assimilate-args.js";import{runAssimilate as re}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as oe}from"./assimilate-deps.js";import{parseLaunchAllArgs as A}from"./parse-launch-all-args.js";import{unknownSubcommand as se}from"./unknown-subcommand.js";import{runLaunchAll as I}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as C}from"./launch-all-deps.js";import{discoverDroneCandidates as te}from"./launch-all-discovery.js";import{runBareLaunchMenu as ae,shouldShowLaunchMenu as ie}from"./bare-launch-menu.js";import{setTerminalTitle as ce}from"./terminal-title.js";import{initConsolePrefix as ne,consolePrefix as o}from"./console-prefix.js";import{initDebugFromArgv as le}from"./debug.js";import{fetchLatestBorgmcpVersion as de,compareVersionsForStaleness as pe}from"./stale-version-check.js";import{defaultCliChoiceDeps as ue,detectCliAvailability as k,installedCliNames as P,parseCliFlag as me,resolveCliChoice as fe}from"./cli-platform.js";import{getRefreshToken as ge,getIdToken as he}from"./config.js";import{composeGetStarted as we,shouldShowGetStarted as xe}from"./get-started.js";import{prepareCodexRemoteLaunch as Ce,withCodexCwdArg as ke,defaultCodexRemoteDeps as ve,checkCodexBridgeHealthy as be}from"./codex-remote.js";import{findLoadedCodexThread as $e}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Se,buildKickoffWakePathClause as ye,recordCodexWakeTarget as Te,socketPathFromRemoteArgs as R}from"./codex-launch.js";import{codexBorgSessionConfigArgs as Ae}from"./launch-gate.js";import{addCodexMcpServer as Ie,addCodexSessionStartHook as Pe,addCodexUserPromptSubmitHook as Re,addMcpServer as Le,addProjectSessionStartHook as De,addUserPromptSubmitHook as Ee,isCodexMcpServerConfigured as Me,isMcpServerConfigured as Oe,removeSessionStartHook as He}from"./config-utils.js";async function Fe(){le(process.argv),U(),await ne();const n=(async()=>{if(!process.stderr.isTTY)return;const e=h(),r=await de();if(!r)return;const i=pe(e,r);i.stale&&i.message&&process.stderr.write(`${o()}${i.message}
3
- `)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(K(h())),process.exit(0)),process.argv[2]==="setup"){T(process.argv[3])&&(process.stdout.write(V(h())),process.exit(0)),await import("./setup.js");return}if(process.argv[2]==="assimilate"){process.argv.slice(3).some(T)&&(process.stdout.write(j(h())),process.exit(0));const e=ee(process.argv.slice(3));e.ok||(process.stderr.write(t.red(`${o()}\u25FC borg assimilate: ${e.error}
4
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=oe(),i=await re({role:e.role,flags:e.flags},r);process.exit(i)}if(process.argv[2]==="spawn"){const e=await q();process.exit(e)}if(process.argv[2]==="sync"){const e=z(process.argv.slice(3));e.ok||(process.stderr.write(t.red(`${o()}\u25FC borg sync: ${e.error}
5
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await J({},e.options);process.exit(r)}if(process.argv[2]==="cleanup"){const e=Q(process.argv.slice(3));e.ok||(process.stderr.write(t.red(`${o()}\u25FC borg cleanup: ${e.error}
6
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await Z({},e.options);process.exit(r)}if(process.argv[2]==="launch-all"){const e=A(process.argv.slice(3));e.ok||(process.stderr.write(t.red(`${o()}\u25FC borg launch-all: ${e.error}
7
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=C(),i=await I(e.args,r);process.exit(i)}const c=se(process.argv[2]);if(c!==null&&(process.stderr.write(t.red(`${o()}\u25FC unknown command: ${c}
8
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),xe(await ge()!==null,await he()!==null)){const e=P(k()).length>0;process.stdout.write(we(e)),process.exit(0)}const f=me(process.argv.slice(2));f.error&&(process.stderr.write(t.red(`${o()}\u25FC ${f.error}
9
- `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const v=async e=>{const r=H({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}};let s=await fe(f.cli,ue(v,()=>process.stdin.isTTY===!0));Ne();const a=await F();if(ie({extraArgs:process.argv.slice(2),stdinIsTTY:process.stdin.isTTY===!0,stdoutIsTTY:process.stdout.isTTY===!0})){const e=P(k()).find(m=>m!==s)??null;let r=!1;a&&(r=(await te({targetCubeId:a.cubeId},C())).length>0);const i=await ae({defaultCli:s,otherInstalledCli:e,hasLaunchAllTargets:r},v);if(i.kind==="launch-all"){const m=A([]),D=m.ok?await I(m.args,C()):1;process.exit(D)}s=i.cli}const l=f.rest;ce(a?{label:a.droneLabel,cubeName:a.name}:null,O(process.cwd()));const L=ye(s==="codex"?"codex":"claude",a&&s==="claude"?B(a.cubeId,a.droneId):null);await Promise.race([n,new Promise(e=>setTimeout(e,2e3))]);const b=s==="codex"?`borg-wake-${M()}`:null;let g,$=[],d={...process.env,BORG_SESSION:"1"},p=null,u=null;if(s==="codex"&&!l.includes("--remote")){console.error(`${o()}${t.gray("\u25FC Starting Codex remote-wake app-server\u2026")}`);const e=await Ce(ve());e.warning?(console.error(`${o()}${t.yellow(`warning: ${e.warning}`)}`),g="\u26A0 Codex wake-path capability check failed: remote-control is unavailable for this session. Run borg_regen manually whenever you return, and expect only fallback wakeups until relaunch."):g="Codex wake-path capability check passed: remote-control socket established for this session.",$=e.args,d={...process.env,...e.env,BORG_SESSION:"1"},p=R(e.args),u=e.server?.cleanup??null}else s==="codex"&&l.includes("--remote")&&(g="Codex wake-path capability check: using caller-provided --remote socket; if no wake arrives, run borg_regen manually when returning to the session.",p=R(l),p&&(d={...process.env,BORG_CODEX_REMOTE_WAKE:"1",BORG_SESSION:"1"}));if(a){const e=await N(a.cubeId,y(),a.droneId),r=G(d,e,process.env);if(d=r.env,r.probe){const i=await Y(r.probe.descriptor,fetch,r.probe.baseUrl);i.ok||console.error(`${o()}${t.yellow(`warning: ${i.message}`)}`)}}const w=Se({cli:s,codexWakeNonce:b,monitorClause:L,codexWakePathClause:g});let x;s==="codex"?x=[...Ae(),...$,...ke([...l,w],process.cwd())]:x=X(l,w),console.error(`${o()}${t.blue(`\u25FC Launching ${s==="claude"?"Claude Code":"Codex"}\u2026`)}`);const S=E(s,x,{stdio:"inherit",shell:!1,env:d});s==="codex"&&a&&p&&(Te({deps:{setCodexWakeTarget:_,findLoadedCodexThread:$e},cubeId:a.cubeId,droneId:a.droneId,socketPath:p,passthroughArgs:l,previewNeedle:b??w.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),W(e=>be(e))),S.on("error",e=>{if(u)try{u()}catch{}e.code==="ENOENT"?(console.error(`${o()}${t.red(`
10
- \u25FC Failed to launch ${s}`)}`),console.error(`${o()}${t.gray(`Make sure ${s} is installed.
11
- `)}`)):console.error(`${o()}${t.red(`
2
+ import{spawn as F}from"child_process";import{randomUUID as N}from"node:crypto";import{basename as B}from"node:path";import{createInterface as _}from"node:readline/promises";import i from"chalk";import{findProjectRoot as L,getActiveCube as W,getLaunchModel as G,inboxPathForDrone as Y,setCodexWakeTarget as U,pruneDeadCodexWakeTargets as V}from"./cubes.js";import{applyOllamaLaunchEnv as K,checkModelReachable as j}from"./model-presets.js";import{handleVersionFlag as q,getPackageVersion as g}from"./version.js";import{isHelpFlag as R,setupHelpText as X,topLevelHelpText as z,assimilateHelpText as J}from"./cli-help.js";import{runSpawn as Q}from"./spawn.js";import{buildClaudeLaunchArgs as Z}from"./claude-launch-args.js";import{parseSyncArgs as ee,runSync as re}from"./sync.js";import{parseCleanupArgs as oe,runCleanup as se}from"./cleanup-cmd.js";import{parseAssimilateArgs as te}from"./parse-assimilate-args.js";import{runAssimilate as ie}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as ae}from"./assimilate-deps.js";import{parseLaunchAllArgs as P}from"./parse-launch-all-args.js";import{unknownSubcommand as ce}from"./unknown-subcommand.js";import{runLaunchAll as D}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as b}from"./launch-all-deps.js";import{discoverDroneCandidates as ne}from"./launch-all-discovery.js";import{explicitCliLaunchHint as le,runBareLaunchMenu as de,shouldResolveExplicitCliLaunchHintTargets as pe,shouldShowLaunchMenu as ue}from"./bare-launch-menu.js";import{setTerminalTitle as me}from"./terminal-title.js";import{initConsolePrefix as fe,consolePrefix as o}from"./console-prefix.js";import{initDebugFromArgv as ge}from"./debug.js";import{fetchLatestBorgmcpVersion as he,compareVersionsForStaleness as we}from"./stale-version-check.js";import{defaultCliChoiceDeps as xe,detectCliAvailability as k,installedCliNames as E,parseCliFlag as Ce,resolveCliChoice as ve}from"./cli-platform.js";import{getRefreshToken as be,getIdToken as ke}from"./config.js";import{composeGetStarted as $e,shouldShowGetStarted as Se}from"./get-started.js";import{prepareCodexRemoteLaunch as Ae,withCodexCwdArg as Te,defaultCodexRemoteDeps as ye,checkCodexBridgeHealthy as Ie}from"./codex-remote.js";import{findLoadedCodexThread as Le}from"./codex-app-server.js";import{buildAgentKickoffPrompt as Re,buildKickoffWakePathClause as Pe,recordCodexWakeTarget as De,socketPathFromRemoteArgs as H}from"./codex-launch.js";import{codexBorgSessionConfigArgs as Ee}from"./launch-gate.js";import{addCodexMcpServer as He,addCodexSessionStartHook as Me,addCodexUserPromptSubmitHook as Oe,addMcpServer as Fe,addProjectSessionStartHook as Ne,addUserPromptSubmitHook as Be,isCodexMcpServerConfigured as _e,isMcpServerConfigured as We,removeSessionStartHook as Ge}from"./config-utils.js";async function Ye(){ge(process.argv),q(),await fe();const n=(async()=>{if(!process.stderr.isTTY)return;const e=g(),r=await he();if(!r)return;const a=we(e,r);a.stale&&a.message&&process.stderr.write(`${o()}${a.message}
3
+ `)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(z(g())),process.exit(0)),process.argv[2]==="setup"){R(process.argv[3])&&(process.stdout.write(X(g())),process.exit(0)),await import("./setup.js");return}if(process.argv[2]==="assimilate"){process.argv.slice(3).some(R)&&(process.stdout.write(J(g())),process.exit(0));const e=te(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${o()}\u25FC borg assimilate: ${e.error}
4
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=ae(),a=await ie({role:e.role,flags:e.flags},r);process.exit(a)}if(process.argv[2]==="spawn"){const e=await Q();process.exit(e)}if(process.argv[2]==="sync"){const e=ee(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${o()}\u25FC borg sync: ${e.error}
5
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await re({},e.options);process.exit(r)}if(process.argv[2]==="cleanup"){const e=oe(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${o()}\u25FC borg cleanup: ${e.error}
6
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await se({},e.options);process.exit(r)}if(process.argv[2]==="launch-all"){const e=P(process.argv.slice(3));e.ok||(process.stderr.write(i.red(`${o()}\u25FC borg launch-all: ${e.error}
7
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=b(),a=await D(e.args,r);process.exit(a)}const c=ce(process.argv[2]);if(c!==null&&(process.stderr.write(i.red(`${o()}\u25FC unknown command: ${c}
8
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),Se(await be()!==null,await ke()!==null)){const e=E(k()).length>0;process.stdout.write($e(e)),process.exit(0)}const l=Ce(process.argv.slice(2));l.error&&(process.stderr.write(i.red(`${o()}\u25FC ${l.error}
9
+ `)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const $=async e=>{const r=_({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}};let s=await ve(l.cli,xe($,()=>process.stdin.isTTY===!0));Ue();const t=await W();let h;const S=async()=>t?(h===void 0&&(h=(await ne({targetCubeId:t.cubeId},b())).length>0),h):!1,w=process.stdin.isTTY===!0,x=process.stdout.isTTY===!0,A=le({explicitCli:l.cli,stdinIsTTY:w,stdoutIsTTY:x,hasActiveCube:t!==null,hasLaunchAllTargets:pe({explicitCli:l.cli,stdinIsTTY:w,stdoutIsTTY:x,hasActiveCube:t!==null})?await S():!1});if(A&&process.stderr.write(A),ue({extraArgs:process.argv.slice(2),stdinIsTTY:w,stdoutIsTTY:x})){const e=E(k()).find(a=>a!==s)??null,r=await de({defaultCli:s,otherInstalledCli:e,hasLaunchAllTargets:await S()},$);if(r.kind==="launch-all"){const a=P([]),O=a.ok?await D(a.args,b()):1;process.exit(O)}s=r.cli}const d=l.rest;me(t?{label:t.droneLabel,cubeName:t.name}:null,B(process.cwd()));const M=Pe(s==="codex"?"codex":"claude",t&&s==="claude"?Y(t.cubeId,t.droneId):null);await Promise.race([n,new Promise(e=>setTimeout(e,2e3))]);const T=s==="codex"?`borg-wake-${N()}`:null;let f,y=[],p={...process.env,BORG_SESSION:"1"},u=null,m=null;if(s==="codex"&&!d.includes("--remote")){console.error(`${o()}${i.gray("\u25FC Starting Codex remote-wake app-server\u2026")}`);const e=await Ae(ye());e.warning?(console.error(`${o()}${i.yellow(`warning: ${e.warning}`)}`),f="\u26A0 Codex wake-path capability check failed: remote-control is unavailable for this session. Run borg_regen manually whenever you return, and expect only fallback wakeups until relaunch."):f="Codex wake-path capability check passed: remote-control socket established for this session.",y=e.args,p={...process.env,...e.env,BORG_SESSION:"1"},u=H(e.args),m=e.server?.cleanup??null}else s==="codex"&&d.includes("--remote")&&(f="Codex wake-path capability check: using caller-provided --remote socket; if no wake arrives, run borg_regen manually when returning to the session.",u=H(d),u&&(p={...process.env,BORG_CODEX_REMOTE_WAKE:"1",BORG_SESSION:"1"}));if(t){const e=await G(t.cubeId,L(),t.droneId),r=K(p,e,process.env);if(p=r.env,r.probe){const a=await j(r.probe.descriptor,fetch,r.probe.baseUrl);a.ok||console.error(`${o()}${i.yellow(`warning: ${a.message}`)}`)}}const C=Re({cli:s,codexWakeNonce:T,monitorClause:M,codexWakePathClause:f});let v;s==="codex"?v=[...Ee(),...y,...Te([...d,C],process.cwd())]:v=Z(d,C),console.error(`${o()}${i.blue(`\u25FC Launching ${s==="claude"?"Claude Code":"Codex"}\u2026`)}`);const I=F(s,v,{stdio:"inherit",shell:!1,env:p});s==="codex"&&t&&u&&(De({deps:{setCodexWakeTarget:U,findLoadedCodexThread:Le},cubeId:t.cubeId,droneId:t.droneId,socketPath:u,passthroughArgs:d,previewNeedle:T??C.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),V(e=>Ie(e))),I.on("error",e=>{if(m)try{m()}catch{}e.code==="ENOENT"?(console.error(`${o()}${i.red(`
10
+ \u25FC Failed to launch ${s}`)}`),console.error(`${o()}${i.gray(`Make sure ${s} is installed.
11
+ `)}`)):console.error(`${o()}${i.red(`
12
12
  \u25FC Failed to launch ${s}: ${e.message}
13
- `)}`),process.exit(1)}),S.on("exit",e=>{if(u)try{u()}catch{}process.exit(e??0)})}function Ne(){const n=k();if(n.claude)try{Oe()||Le(),De(y(process.cwd())),He(),Ee()}catch(c){console.error(`${o()}${t.yellow(`warning: Claude Code integration check failed: ${c?.message??c}`)}`)}if(n.codex)try{Me()||Ie(),Pe(),Re()}catch(c){console.error(`${o()}${t.yellow(`warning: Codex integration check failed: ${c?.message??c}`)}`)}}Fe().catch(n=>{console.error(`${o()}${t.red(`
13
+ `)}`),process.exit(1)}),I.on("exit",e=>{if(m)try{m()}catch{}process.exit(e??0)})}function Ue(){const n=k();if(n.claude)try{We()||Fe(),Ne(L(process.cwd())),Ge(),Be()}catch(c){console.error(`${o()}${i.yellow(`warning: Claude Code integration check failed: ${c?.message??c}`)}`)}if(n.codex)try{_e()||He(),Me(),Oe()}catch(c){console.error(`${o()}${i.yellow(`warning: Codex integration check failed: ${c?.message??c}`)}`)}}Ye().catch(n=>{console.error(`${o()}${i.red(`
14
14
  \u25FC Error: ${n.message}
15
15
  `)}`),process.exit(1)});
package/dist/cli-help.js CHANGED
@@ -1,4 +1,4 @@
1
- function o(e){return e==="--help"||e==="-h"}function n(e){return`borgmcp ${e} \u2014 run several AI coding agents on one project, together.
1
+ function n(e){return e==="--help"||e==="-h"}function o(e){return`borgmcp ${e} \u2014 run several AI coding agents on one project, together.
2
2
  They coordinate through a shared log (a "cube"). For Claude Code & Codex.
3
3
 
4
4
  Docs & quickstart: https://borgmcp.ai/get-started
@@ -7,7 +7,7 @@ Install Claude Code or Codex first. Type \`borg ...\` in your terminal;
7
7
  type \`borg_...\` inside your agent session once you've joined a cube ("assimilate").
8
8
 
9
9
  Usage:
10
- borg Launch your agent CLI with cube context
10
+ borg Launch your agent CLI; in a TTY, bare borg may show the launch menu
11
11
  borg setup Set up OAuth + register MCP server
12
12
  borg setup --no-browser Set up from SSH/headless terminals
13
13
  borg assimilate [role] Join a cube (creates one if needed)
@@ -16,7 +16,9 @@ Usage:
16
16
  borg sync [--prune] Sync this worktree's branch to origin/main
17
17
  borg cleanup [--prune] Report (or --prune) worktrees orphaned by evicted drones
18
18
  borg launch-all [cube] Launch all drone worktrees of a cube (default: active cube)
19
- borg --cli claude|codex Choose agent CLI for this project
19
+ borg launch-all [cube] --cli claude|codex
20
+ Launch all drone worktrees with that agent CLI
21
+ borg --cli claude|codex Launch that agent CLI directly
20
22
  borg --version Show installed version
21
23
 
22
24
  All other arguments are passed through to the selected agent CLI.
@@ -59,4 +61,4 @@ Usage:
59
61
  for SSH / headless / container terminals. Alias: --device.
60
62
  Auto-detected on SSH/headless; this forces it.
61
63
  borg setup --help Show this help
62
- `}export{t as assimilateHelpText,o as isHelpFlag,r as setupHelpText,n as topLevelHelpText};
64
+ `}export{t as assimilateHelpText,n as isHelpFlag,r as setupHelpText,o as topLevelHelpText};
@@ -3,7 +3,7 @@ import { CodexAppServerClient } from './codex-app-server.js';
3
3
  import { checkCodexBridgeHealthy } from './codex-remote.js';
4
4
  export declare const CODEX_WAKE_PROMPT = "New Borg cube-log activity arrived.";
5
5
  export declare function formatCodexWakePrompt(inboxLine: string): string;
6
- export declare const CODEX_CATCHUP_PROMPT = "Borg cube activity arrived while you were busy. Run `borg_read-log unread_only=true` and DRAIN \u2014 repeat until the returned page is under the limit and behind_by is 0 \u2014 so no entries are skipped.";
6
+ export declare const CODEX_CATCHUP_PROMPT = "Borg cube activity arrived while you were busy. Wake triage: run `borg_read-log unread_only=true` and DRAIN \u2014 repeat until the returned page is under the limit and behind_by is 0 \u2014 so no entries are skipped. Then handle actionable entries; if none, resume the prior interrupted work.";
7
7
  export declare function isCodexRemoteWakeEnabled(env?: NodeJS.ProcessEnv): boolean;
8
8
  export declare function resolveSessionAgentKind(env?: NodeJS.ProcessEnv): 'claude' | 'codex';
9
9
  export interface CodexWakeTarget {
@@ -1,2 +1,2 @@
1
1
  import{getActiveCube as C,getCodexWakeTarget as m,setCodexWakeTarget as R}from"./cubes.js";import{CodexAppServerClient as S}from"./codex-app-server.js";import{checkCodexBridgeHealthy as K}from"./codex-remote.js";import{recordEventReceipt as W}from"./health-beat.js";import{codexAppServerSocketFromEnv as O,pickFreshThread as M,wakeTargetChanged as B,wakeRetryBackoffMs as F,wakeRetryExpired as H,WAKE_RETRY_MAX_ATTEMPTS as N,shouldFireHeartbeat as j}from"./codex-wake-resolve.js";const L="New Borg cube-log activity arrived.";function ie(e){return`New Borg cube-log activity arrived:
2
- ${e}`}const D="Borg cube activity arrived while you were busy. Run `borg_read-log unread_only=true` and DRAIN \u2014 repeat until the returned page is under the limit and behind_by is 0 \u2014 so no entries are skipped.";function P(e=process.env){return e.BORG_CODEX_REMOTE_WAKE==="1"}function X(e=process.env){return P(e)?"codex":"claude"}function q(e=process.env){return P(e)?{enabled:!0}:{enabled:!1}}async function ce(e,t={}){try{const r=await(t.getCodexWakeTarget??m)(e.cubeId,e.droneId);return r?(t.checkBridge??K)(r.socketPath):!1}catch{return null}}let f=!1;const h=[],l=new Set,w=[],$=100;let g=!1,k=null;function se(){return k}function T(e){k=(e.now??Date.now)()}let v=!1,y=!1;function p(){return y?!1:(y=!0,!0)}function A(){y=!1}function V(e){return e?.code==="ENOENT"}function Y(e){return new Promise(t=>setTimeout(t,e))}function x(e,t){return t.createClient?t.createClient(e):new S(e)}async function b(e,t){const n=O(t.env??process.env);if(n){const a=x(n,t);await a.connect();try{const o=await a.loadedThreadIds(),i=[];for(const u of o){const s=await a.readThread(u);s&&i.push({id:s.id,cwd:s.cwd,updatedAt:s.updatedAt})}const c=M(i,{cwd:(t.cwd??(()=>process.cwd()))()});return c?(await G(e,{socketPath:n,threadId:c},t),{socketPath:n,threadId:c}):null}finally{a.close()}}const r=await(t.getCodexWakeTarget??m)(e.cubeId,e.droneId);return r?{socketPath:r.socketPath,threadId:r.threadId}:null}async function G(e,t,n){try{const r=n.getCodexWakeTarget??m,a=n.setCodexWakeTarget??R,o=await r(e.cubeId,e.droneId),i=o?{socketPath:o.socketPath,threadId:o.threadId}:null;B(i,t)&&await a(e.cubeId,e.droneId,t)}catch{}}function ue(e=L,t=process.env,n={}){q(t).enabled&&(h.push({reason:e,deps:n}),!f&&(f=!0,Q().finally(()=>{f=!1})))}async function Q(){for(;h.length>0;){const e=h.shift();await U(e.reason,e.deps)}}async function U(e,t){if(!p()){E(t);return}try{const n=await(t.getActiveCube??C)();if(!n)return;const r=await b(n,t);if(!r)return;const{socketPath:a,threadId:o}=r,i=`${o}\0${e}`;if(l.has(i))return;const c=x(a,t);await c.connect();try{if((await c.readThread(o))?.status?.type==="active"){E(t);return}await c.startTurn(o,e),W(),Z(i),T(t)}finally{c.close()}}catch{E(t)}finally{A()}}function E(e){g||(g=!0,z(e).finally(()=>{g=!1}))}async function z(e){const t=e.sleep??Y,n=e.now??Date.now,r=e.jitter??(()=>Math.random()*500),a=e.maxAttempts??N,o=n();let i=0;for(;!H(o,n())&&i<a;)if(await t(F(i,r())),i++,!!p())try{const c=await(e.getActiveCube??C)();if(!c)continue;const u=await b(c,e);if(!u)continue;const{socketPath:s,threadId:I}=u,d=x(s,e);await d.connect();try{if((await d.readThread(I))?.status?.type==="active")continue;await d.startTurn(I,D),W(),T(e);return}finally{d.close()}}catch{}finally{A()}}const _=20*6e4;async function J(e={},t=_){if(v)return;const n=(e.now??Date.now)();if(j(k,n,t)&&!(e.isStreamOwner&&!e.isStreamOwner())&&p()){v=!0;try{const r=await(e.getActiveCube??C)();if(!r)return;const a=await b(r,e);if(!a)return;const o=x(a.socketPath,e);await o.connect();try{if((await o.readThread(a.threadId))?.status?.type==="active")return;await o.startTurn(a.threadId,D),T(e)}finally{o.close()}}catch(r){V(r)&&e.onAppServerSocketDead?.()}finally{v=!1,A()}}}function le(e={}){if((e.agentKind??X())!=="codex")return null;const n=e.intervalMs??_,r=e.tick??(()=>{J()}),a=setInterval(r,n);return a.unref?.(),a}function de(){f=!1,h.length=0,l.clear(),w.length=0,g=!1,k=null,v=!1,y=!1}function Z(e){if(!l.has(e))for(l.add(e),w.push(e);w.length>$;){const t=w.shift();t&&l.delete(t)}}export{D as CODEX_CATCHUP_PROMPT,_ as CODEX_HEARTBEAT_CADENCE_MS,L as CODEX_WAKE_PROMPT,J as fireCodexHeartbeatTick,ie as formatCodexWakePrompt,se as getLastDeliveredAt,P as isCodexRemoteWakeEnabled,ce as probeCodexBridgeArmed,de as resetCodexWakeForTests,q as resolveCodexWakeTarget,X as resolveSessionAgentKind,le as startCodexHeartbeat,ue as wakeCodexViaAppServer};
2
+ ${e}`}const D="Borg cube activity arrived while you were busy. Wake triage: run `borg_read-log unread_only=true` and DRAIN \u2014 repeat until the returned page is under the limit and behind_by is 0 \u2014 so no entries are skipped. Then handle actionable entries; if none, resume the prior interrupted work.";function P(e=process.env){return e.BORG_CODEX_REMOTE_WAKE==="1"}function X(e=process.env){return P(e)?"codex":"claude"}function q(e=process.env){return P(e)?{enabled:!0}:{enabled:!1}}async function ce(e,t={}){try{const r=await(t.getCodexWakeTarget??m)(e.cubeId,e.droneId);return r?(t.checkBridge??K)(r.socketPath):!1}catch{return null}}let f=!1;const h=[],l=new Set,w=[],$=100;let g=!1,k=null;function se(){return k}function p(e){k=(e.now??Date.now)()}let v=!1,y=!1;function T(){return y?!1:(y=!0,!0)}function A(){y=!1}function V(e){return e?.code==="ENOENT"}function Y(e){return new Promise(t=>setTimeout(t,e))}function x(e,t){return t.createClient?t.createClient(e):new S(e)}async function b(e,t){const n=O(t.env??process.env);if(n){const a=x(n,t);await a.connect();try{const o=await a.loadedThreadIds(),i=[];for(const u of o){const s=await a.readThread(u);s&&i.push({id:s.id,cwd:s.cwd,updatedAt:s.updatedAt})}const c=M(i,{cwd:(t.cwd??(()=>process.cwd()))()});return c?(await G(e,{socketPath:n,threadId:c},t),{socketPath:n,threadId:c}):null}finally{a.close()}}const r=await(t.getCodexWakeTarget??m)(e.cubeId,e.droneId);return r?{socketPath:r.socketPath,threadId:r.threadId}:null}async function G(e,t,n){try{const r=n.getCodexWakeTarget??m,a=n.setCodexWakeTarget??R,o=await r(e.cubeId,e.droneId),i=o?{socketPath:o.socketPath,threadId:o.threadId}:null;B(i,t)&&await a(e.cubeId,e.droneId,t)}catch{}}function ue(e=L,t=process.env,n={}){q(t).enabled&&(h.push({reason:e,deps:n}),!f&&(f=!0,Q().finally(()=>{f=!1})))}async function Q(){for(;h.length>0;){const e=h.shift();await U(e.reason,e.deps)}}async function U(e,t){if(!T()){E(t);return}try{const n=await(t.getActiveCube??C)();if(!n)return;const r=await b(n,t);if(!r)return;const{socketPath:a,threadId:o}=r,i=`${o}\0${e}`;if(l.has(i))return;const c=x(a,t);await c.connect();try{if((await c.readThread(o))?.status?.type==="active"){E(t);return}await c.startTurn(o,e),W(),Z(i),p(t)}finally{c.close()}}catch{E(t)}finally{A()}}function E(e){g||(g=!0,z(e).finally(()=>{g=!1}))}async function z(e){const t=e.sleep??Y,n=e.now??Date.now,r=e.jitter??(()=>Math.random()*500),a=e.maxAttempts??N,o=n();let i=0;for(;!H(o,n())&&i<a;)if(await t(F(i,r())),i++,!!T())try{const c=await(e.getActiveCube??C)();if(!c)continue;const u=await b(c,e);if(!u)continue;const{socketPath:s,threadId:I}=u,d=x(s,e);await d.connect();try{if((await d.readThread(I))?.status?.type==="active")continue;await d.startTurn(I,D),W(),p(e);return}finally{d.close()}}catch{}finally{A()}}const _=20*6e4;async function J(e={},t=_){if(v)return;const n=(e.now??Date.now)();if(j(k,n,t)&&!(e.isStreamOwner&&!e.isStreamOwner())&&T()){v=!0;try{const r=await(e.getActiveCube??C)();if(!r)return;const a=await b(r,e);if(!a)return;const o=x(a.socketPath,e);await o.connect();try{if((await o.readThread(a.threadId))?.status?.type==="active")return;await o.startTurn(a.threadId,D),p(e)}finally{o.close()}}catch(r){V(r)&&e.onAppServerSocketDead?.()}finally{v=!1,A()}}}function le(e={}){if((e.agentKind??X())!=="codex")return null;const n=e.intervalMs??_,r=e.tick??(()=>{J()}),a=setInterval(r,n);return a.unref?.(),a}function de(){f=!1,h.length=0,l.clear(),w.length=0,g=!1,k=null,v=!1,y=!1}function Z(e){if(!l.has(e))for(l.add(e),w.push(e);w.length>$;){const t=w.shift();t&&l.delete(t)}}export{D as CODEX_CATCHUP_PROMPT,_ as CODEX_HEARTBEAT_CADENCE_MS,L as CODEX_WAKE_PROMPT,J as fireCodexHeartbeatTick,ie as formatCodexWakePrompt,se as getLastDeliveredAt,P as isCodexRemoteWakeEnabled,ce as probeCodexBridgeArmed,de as resetCodexWakeForTests,q as resolveCodexWakeTarget,X as resolveSessionAgentKind,le as startCodexHeartbeat,ue as wakeCodexViaAppServer};
@@ -1,12 +1,12 @@
1
- import{ROLE_SCOPED_SAFETY_DISCIPLINES as k,UNIVERSAL_SAFETY_DISCIPLINES as _}from"./templates.js";import{parseRoleSections as A}from"./role-section.js";import{formatRoleAgentLabel as N}from"./roster-render.js";import{formatDroneAddressToken as T}from"./drone-address.js";function M(e){if(!e||!e.trim())return null;try{const r=JSON.parse(e)?.source;return typeof r=="string"?r:null}catch{return null}}function C(e,o){return e==="codex"?"Wake path: Codex wakes via the app-server remote-wake injection \u2014 there is no tail-Monitor or `/loop` heartbeat to arm. If no wake arrives when you return to the session, run `borg_regen` manually.":["Arm your wake path before working:",`1. **Inbox Monitor** (wake path) \u2014 run a persistent Monitor on \`borg-inbox-monitor ${o}\` so cube posts wake you in real time.`,"2. **Engage `/loop`** (self-paced) so you keep waking to triage the cube.","3. **Fallback heartbeat** \u2014 set a ~1800s (30-min) `ScheduleWakeup` so you never go fully silent."].join(`
2
- `)}function j(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function q(e){const{cubeName:o,droneLabel:r,roleName:n,inboxPath:t,agentKind:i,source:s}=e,c=s==="clear"?"\n_(`/clear` cleared your conversation + session-scoped `/loop` and `ScheduleWakeup` heartbeat \u2014 re-establish them now.)_\n":"";return[`# Cube: ${o} \u2014 ${r}`,"",`**Your role:** ${n||"_(call `borg_regen` to load)_"}`,c,"You are a Borg drone \u2014 coordinate through the cube log, and never pause for the user. Blocked \u2192 escalate to your cube's coordinating role.","",C(i,t),"","This orientation is intentionally lean. For your full cube directive, role playbook, and roster \u2014 and your complete operating disciplines \u2014 call `borg_regen` (and load `borg_playbook` / `borg_cube` once per session).",""].join(`
3
- `)}function x(){return"## How to operate as a Drone\n\nYou're a Drone in a Cube. Coordinate with other drones through the activity log.\n\n**User asks how Borg MCP works** \u2014 a feature, setup, pricing, or concept question? Call `borg_docs {topic}` for the documentation index, then WebFetch the matching section URL and answer from the page. Don't guess borgmcp's own behavior from memory.\n\n**Tools:**\n- `borg_regen` \u2014 refresh full state (your role, roster, unread-log COUNT, and fetch-on-demand pointers) in one call; the cube directive (\u2192 `borg_cube`), the operating-playbook detail (\u2192 `borg_playbook`), and the recent-log payload (\u2192 `borg_read-log` when count >0) are NOT inlined \u2014 fetch them on demand\n- `borg_cube` \u2014 re-read the cube directive and the role overview\n- `borg_role` \u2014 re-read your role's detailed playbook\n- `borg_roster` \u2014 see who else is connected\n- `borg_read-log unread_only=true [limit]` \u2014 drain unread log entries from your server-side cursor\n- `borg_log <message>` \u2014 append to the log\n- `borg_assimilate <cube>` \u2014 switch to a different cube\n\n**How coordination works:** the Cube gives primitives, not workflows. Your role's `detailed_description` (above) is your playbook \u2014 its conventions + signals come from there, not the system. The log is the coordination channel. Different cubes, different conventions.\n\n**Default: act autonomously, coordinate through the log.** Don't wait for user input. Need input \u2192 post the question, continue other work, other drones respond. The human supervisor is reachable through your cube's coordinating / human-seat role (the role your cube designates for direction + integration), or the Queen role when the seat is delegated to a drone \u2014 one continuous seat. Your role's `detailed_description` says when to escalate + which decisions need human input; follow it.\n\n**Operating loop \u2014 each wake, in order:**\n1. Drain unread: `borg_read-log unread_only=true` (oldest-first, repeat until `behind_by=0`) before acting. The \"Cube log\" section gives your UNREAD COUNT.\n2. Apply your role's conventions to each entry. Act on: questions you can answer; blocked peers you can unblock; unowned work you can claim; decisions affecting you.\n3. Actionable signal \u2192 act + post the convention. Don't wait to be asked.\n4. User prompt waiting \u2192 respond, informed by cube context; log substantive units (shipped changes, blockers, findings) regardless of who initiated.\n5. Nothing actionable + no prompt \u2192 done; wait for next wake.\n\n**On a `<task-notification>` wake:** the payload is a truncatable preview; the full entry is in the DB. Drain: `borg_read-log unread_only=true limit=20`, repeat until `behind_by=0`. Do NOT triage with `since=<notification timestamp>` (strict-after \u2014 skips the boundary entry) or a bare window (skips older-unread during bursts).\n\n**On first wake this session:** post one `ARRIVAL: <your-label> (<your-role>) online on <hostname> at <project-path>` (run `hostname`; use cwd for the path). One-time per session \u2014 don't repeat on later wakes; skip if already posted this session (e.g. after a `/mcp` reconnect).\n\n**When a log entry routes work to you** (a routing/assignment-class entry per your cube's conventions that names your label + asks for action, or a direct `<your-label>:` mention): call `borg_ack entry_id=<id>` within ~60s. Use the `borg_ack` TOOL, not an in-band `ACK:` post (it records a queryable flag + wakes the author's Monitor + keeps the log clean). Ack = receipt, not completion (`STARTING` / `DONE` still apply). Ack only routing-class signals \u2014 not every mention.\n\n**Claim a work item before you start it (`borg_ack ... kind=claim`):** `borg_ack` has two kinds \u2014 `ack` (receipt, the default) and `claim` (advisory ownership of a routed work item you are about to take). When a routed entry could be picked up by more than one drone, `borg_ack entry_id=<id> kind=claim` BEFORE starting \u2014 it announces you are taking it so peers skip the duplicate work, and wakes the rest of the entry's audience. If a live peer already holds the claim, skip it; if the claim is STALE (the claimant went silent past the wake-path SLA), re-claim and proceed. A claim is ADVISORY only \u2014 it NEVER substitutes for the completion or approval signal your role's conventions require; a bogus or abandoned claim can at most delay a work item, never bypass its real gate.\n\n**When stuck:** post your blocker per your role's conventions, continue other work. Escalation is per your role detail, not by stalling.\n\n**Anti-passive (lane idle = no work routed to you, no actionable signal in the log):**\n- If your work arrives via dispatch / a work queue: when your lane goes idle, post your role's availability signal (capacity clean, awaiting next assignment from your coordinating role) \u2014 once per idle period, don't spam. No assignment in ~15 min \u2192 ping your coordinating role (capacity available since <time>; any queue item to pick up?).\n- If your work is SELF-DIRECTED (not dispatch-driven): do NOT post an availability signal \u2014 proactively surface lane-substantive work per your role (reviews, audits, proposals, coherence / quality sweeps on relevant in-flight work).\n- Route work-asks through your cube's coordinating role, never directly to the human Queen.\n\n**Verify factual claims:** verify any verifiable claim \u2014 versions, code-state, prod behavior, npm state \u2014 against the SOURCE-OF-TRUTH surface (`git tag` / `git show <ref>:<path>` / grep, `curl` / `wrangler tail`, `npm view`, the live DB) BEFORE writing it; never a derivative artifact (another post, summary, or your own prior framing). The full discipline \u2014 the v1/v2/v3 sharpening levels, the per-claim-type concrete surfaces, and four-surface propagation (brainstorm / comment / review / issue-filing) \u2014 is in the operating-playbook chapter (`borg_playbook`; loaded via the session-start block in your regen).\n\n**Posting to the log:** post per your role's conventions whenever you start/finish a task, get stuck, answer a drone, or learn something others need \u2014 regardless of who initiated (a log signal, your own scan, or a user prompt). Conventions live in your role detail; the system is vocabulary-agnostic.\n\n**Routing posts \u2014 widen the directed default:** the taxonomy routes most prefixes DIRECTED to your cube's coordinating role; your `to:` / `visibility:` overrides it. Widen when a post must reach more than the coordinating role:\n- Posting a verdict / decision / result a specific drone is waiting on: add `to:[that drone]` so they're WOKEN \u2014 without it they can be left UNAWARE of their own merge or feedback. Directed governs the WAKE; it is NOT read-confidentiality: every member can read every entry \u2014 the cube is the trust boundary \u2014 so never post secrets relying on `to:[x]`.\n- Any drone posting a multi-seat DELIVERABLE (spec / security classification / review artifact 3+ seats build or gate against): pass `visibility:broadcast` (or `to:[the seats]`) EVEN IF your prefix (`DONE` etc.) is a directed status class \u2014 else only your coordinating role wakes (taxonomy routes by prefix, not payload) and the building/gating seats miss it.\n\n**Pre-commit git hygiene (universal):**\n\nAny drone that commits code: run `git diff --staged --stat` before `git commit` to verify file count + LOC direction + paths match your intent. Catches deleted files / anomalous -LOC / wrong paths pre-push. Your role may layer more git rules (code-implementing + coordinating roles typically carry the full set)."}const H=x();function V(){return'## Operating playbook \u2014 full disciplines (borg_playbook chapter)\n\nThis is the on-demand detail behind the rule-spine in your regen. Load it ONCE per session; it is static \u2014 do not re-fetch on every wake.\n\n**Verifying factual claims:**\n\nAny time you make a factual claim that could be verified \u2014 "this shipped as version Y", "function Z does W", "endpoint A returns B in prod", "package P is at version Q on npm" \u2014 verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels:\n\n- **v1 (verify against the actual surface):** check the claim against the surface it describes (e.g. a code-state claim \u2192 grep the file). Apply when the claim is about code-state.\n- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you\'re checking), verify against the canonical source-of-truth: `git tag` for version-attribution, code-by-grep / direct file read for code-state, live `curl` or `wrangler tail` for prod-state, `npm view` for npm-state. Apply when version numbers, deploy timestamps, or other discrete facts are in scope.\n- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim ("the watchdog wakes silent drones"), verify the END-TO-END execution path, not just each isolated component \u2014 each isolated mechanism can be correct while the path between them silently breaks. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.\n\n**Concrete verification surfaces by claim type:**\n- Version attribution \u2192 `git tag --contains <sha>` or `git log --oneline <tag>`\n- Code state \u2192 match the grep surface to the claim surface:\n - Local uncommitted claim \u2192 `grep -n "<symbol>" <file>` or direct file read in the working tree\n - `origin/main`, PR head, branch, merge-SHA, or tag claim \u2192 `git show <ref>:<path> | grep -n "<symbol>"` (examples: `git show origin/main:workers/heartbeat.ts | grep -n "last_log_post"`; `git show origin/feat/foo:client/src/log-stream.ts | grep -n "ownDrone"`; `git show abc1234:workers/cubes.ts | grep -n "visibility"`)\n- Prod state \u2192 `curl https://<endpoint>` or `wrangler tail --env production`\n- npm registry state \u2192 `npm view <package>@<version>` or `npm view <package>@latest`\n- DB state \u2192 query through the existing `db` interface; never trust a doc claim about row counts / column values\n- Cube log state \u2192 `borg_read-log unread_only=true` for wake triage, draining until `behind_by=0`; don\'t cite from memory or from another drone\'s summary\n- Ratified cube decision \u2192 `borg_decisions {topic}` \u2014 cite the registry\'s active decision by topic; NEVER restate a ratified decision from memory (a memory restatement drifts on the axis). A ratified decision is a first-class verifiable claim type with its own source of truth: the active registry entry. Recording one is `borg_decide` (seat-holder only \u2014 recording IS the ratification act).\n\n**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). It lives in this universal playbook rather than any one role\'s text because it applies to ALL reviewers.\n\n**Four-surface propagation:**\n\nThe discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:\n\n- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep\'s the referenced file BEFORE composing the proposal. If the proposal cites current `origin/main` or a branch/SHA, grep that ref via `git show <ref>:<path> | grep`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.\n- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules\' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep\'s the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via `git show <ref>:<path> | grep`; don\'t let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.\n- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.\n- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking issue from a cube event payload, the FILING drone fetches the originating entry\'s full body from the cube log BEFORE composing the issue body. For routine wake triage, use `borg_read-log unread_only=true` and drain until caught up; do not rely on a truncated event preview or a `since=<same timestamp>` read, which can skip the boundary entry. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface \u2014 the filed issue becomes the cube\'s durable cross-cycle memory; correcting it requires a follow-up correction post, and later pickup drones inherit the incomplete framing if the correction is missed.\n\n**Ratified-decision drift is a four-surface drift-class.** A ratified cube decision restated from memory drifts exactly like a code-identifier claim \u2014 it propagates dispatch (Surface 1, brainstorm) \u2192 copy (Surface 2, comment) \u2192 gate (Surface 3, review), and the cheapest catch is at the brainstorm surface. At each surface, a drone restating a ratified decision source-reads `borg_decisions {topic}` FIRST: the active registry entry is the source of truth; your memory is a derivative artifact. Core rule \u2014 **cite ratified decisions by topic; never restate one from memory.**'}function D(e){const o=typeof e=="string"?new Date(e):e,r=Date.now()-o.getTime();if(!Number.isFinite(r)||r<0)return"just now";const n=Math.floor(r/1e3);if(n<60)return`${n}s ago`;const t=Math.floor(n/60);if(t<60)return`${t}m ago`;const i=Math.floor(t/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function I(e){return e==null||Array.isArray(e)&&e.length===0?"Tip: no message taxonomy declared \u2014 set one to enable intent-based smart routing (#468). Use borg_update-cube with a taxonomy array, or add classes with borg_patch-taxonomy-class.":""}function J(e,o){return e.drone?.label??o??null}let f=!1,y=null;function G(){f=!1,y=null}function L(e){const o=e??"",r=k.filter(n=>o.includes(n));return[..._,...r]}function $(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function K(e){const o=e.match(/borg_role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);if(!o)return null;try{return{role:JSON.parse(o[1]),section:JSON.parse(o[2])}}catch{return null}}const P=[..._,...k];function F(e,o){return A(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||P.some(p=>t.body.includes(p)))return t.body;const s=t.body.indexOf(`
1
+ import{ROLE_SCOPED_SAFETY_DISCIPLINES as k,UNIVERSAL_SAFETY_DISCIPLINES as _}from"./templates.js";import{parseRoleSections as A}from"./role-section.js";import{formatRoleAgentLabel as C}from"./roster-render.js";import{formatDroneAddressToken as N}from"./drone-address.js";function M(e){if(!e||!e.trim())return null;try{const r=JSON.parse(e)?.source;return typeof r=="string"?r:null}catch{return null}}function T(e,o){return e==="codex"?"Wake path: Codex wakes via the app-server remote-wake injection \u2014 there is no tail-Monitor or `/loop` heartbeat to arm. If no wake arrives when you return to the session, run `borg_regen` manually.":["Arm your wake path before working:",`1. **Inbox Monitor** (wake path) \u2014 run a persistent Monitor on \`borg-inbox-monitor ${o}\` so cube posts wake you in real time.`,"2. **Engage `/loop`** (self-paced) so you keep waking to triage the cube.","3. **Fallback heartbeat** \u2014 set a ~1800s (30-min) `ScheduleWakeup` so you never go fully silent."].join(`
2
+ `)}function j(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function q(e){const{cubeName:o,droneLabel:r,roleName:i,inboxPath:t,agentKind:n,source:s}=e,c=s==="clear"?n==="codex"?"\n_(`/clear` cleared your conversation; Codex remote-wake remains app-server-driven. If no wake arrives, run `borg_regen` manually when returning.)_\n":"\n_(`/clear` cleared your conversation + session-scoped `/loop` and `ScheduleWakeup` heartbeat \u2014 re-establish them now.)_\n":"";return[`# Cube: ${o} \u2014 ${r}`,"",`**Your role:** ${i||"_(call `borg_regen` to load)_"}`,c,"You are a Borg drone \u2014 coordinate through the cube log, and never pause for the user. Blocked \u2192 escalate to your cube's coordinating role.","",T(n,t),"","This orientation is intentionally lean. Before acting, call `borg_regen`, load the cube directive and conventions with `borg_cube`, and load your own role playbook/details with `borg_role` when not already present or after compaction. Use `borg_playbook` once per session for the complete operating disciplines.",""].join(`
3
+ `)}function x(){return"## How to operate as a Drone\n\nYou're a Drone in a Cube. Coordinate with other drones through the activity log.\n\n**User asks how Borg MCP works** \u2014 a feature, setup, pricing, or concept question? Call `borg_docs {topic}` for the documentation index, then WebFetch the matching section URL and answer from the page. Don't guess borgmcp's own behavior from memory.\n\n**Tools:**\n- `borg_regen` \u2014 refresh full state (your role, roster, unread-log COUNT, and fetch-on-demand pointers) in one call; the cube directive (\u2192 `borg_cube`), the operating-playbook detail (\u2192 `borg_playbook`), and the recent-log payload (\u2192 `borg_read-log` when count >0) are NOT inlined \u2014 fetch them on demand\n- `borg_cube` \u2014 re-read the cube directive and the role overview\n- `borg_role` \u2014 re-read your role's detailed playbook\n- `borg_roster` \u2014 see who else is connected\n- `borg_read-log unread_only=true [limit]` \u2014 drain unread log entries from your server-side cursor\n- `borg_log <message>` \u2014 append to the log\n- `borg_assimilate <cube>` \u2014 switch to a different cube\n\n**How coordination works:** the Cube gives primitives, not workflows. Your role's `detailed_description` (above) is your playbook \u2014 its conventions + signals come from there, not the system. The log is the coordination channel. Different cubes, different conventions.\n\n**Default: act autonomously, coordinate through the log.** Don't wait for user input. Need input \u2192 post the question, continue other work, other drones respond. The human supervisor is reachable through your cube's coordinating / human-seat role (the role your cube designates for direction + integration), or the Queen role when the seat is delegated to a drone \u2014 one continuous seat. Your role's `detailed_description` says when to escalate + which decisions need human input; follow it.\n\n**Operating loop \u2014 each wake, in order:**\n1. Drain unread: `borg_read-log unread_only=true` (oldest-first, repeat until `behind_by=0`) before acting. The \"Cube log\" section gives your UNREAD COUNT.\n2. Apply your role's conventions to each entry. Act on: questions you can answer; blocked peers you can unblock; unowned work you can claim; decisions affecting you.\n3. Actionable signal \u2192 act + post the convention. Don't wait to be asked.\n4. User prompt waiting \u2192 respond, informed by cube context; log substantive units (shipped changes, blockers, findings) regardless of who initiated.\n5. Nothing actionable + no prompt \u2192 done; wait for next wake.\n\n**On a `<task-notification>` wake:** the payload is a truncatable preview; the full entry is in the DB. Drain: `borg_read-log unread_only=true limit=20`, repeat until `behind_by=0`. Do NOT triage with `since=<notification timestamp>` (strict-after \u2014 skips the boundary entry) or a bare window (skips older-unread during bursts).\n\n**On first wake this session:** post one `ARRIVAL: <your-label> (<your-role>) online on <hostname> at <project-path>` (run `hostname`; use cwd for the path). One-time per session \u2014 don't repeat on later wakes; skip if already posted this session (e.g. after a `/mcp` reconnect).\n\n**When a log entry routes work to you** (a routing/assignment-class entry per your cube's conventions that names your label + asks for action, or a direct `<your-label>:` mention): call `borg_ack entry_id=<id>` within ~60s. Use the `borg_ack` TOOL, not an in-band `ACK:` post (it records a queryable flag + wakes the author's Monitor + keeps the log clean). Ack = receipt, not completion (`STARTING` / `DONE` still apply). Ack only routing-class signals \u2014 not every mention.\n\n**Claim a work item before you start it (`borg_ack ... kind=claim`):** `borg_ack` has two kinds \u2014 `ack` (receipt, the default) and `claim` (advisory ownership of a routed work item you are about to take). When a routed entry could be picked up by more than one drone, `borg_ack entry_id=<id> kind=claim` BEFORE starting \u2014 it announces you are taking it so peers skip the duplicate work, and wakes the rest of the entry's audience. If a live peer already holds the claim, skip it; if the claim is STALE (the claimant went silent past the wake-path SLA), re-claim and proceed. A claim is ADVISORY only \u2014 it NEVER substitutes for the completion or approval signal your role's conventions require; a bogus or abandoned claim can at most delay a work item, never bypass its real gate.\n\n**When stuck:** post your blocker per your role's conventions, continue other work. Escalation is per your role detail, not by stalling.\n\n**Anti-passive (lane idle = no work routed to you, no actionable signal in the log):**\n- If your work arrives via dispatch / a work queue: when your lane goes idle, post your role's availability signal (capacity clean, awaiting next assignment from your coordinating role) \u2014 once per idle period, don't spam. No assignment in ~15 min \u2192 ping your coordinating role (capacity available since <time>; any queue item to pick up?).\n- If your work is SELF-DIRECTED (not dispatch-driven): do NOT post an availability signal \u2014 proactively surface lane-substantive work per your role (reviews, audits, proposals, coherence / quality sweeps on relevant in-flight work).\n- Route work-asks through your cube's coordinating role, never directly to the human Queen.\n\n**Verify factual claims:** verify any verifiable claim \u2014 versions, code-state, prod behavior, npm state \u2014 against the SOURCE-OF-TRUTH surface (`git tag` / `git show <ref>:<path>` / grep, `curl` / `wrangler tail`, `npm view`, the live DB) BEFORE writing it; never a derivative artifact (another post, summary, or your own prior framing). The full discipline \u2014 the v1/v2/v3 sharpening levels, the per-claim-type concrete surfaces, and four-surface propagation (brainstorm / comment / review / issue-filing) \u2014 is in the operating-playbook chapter (`borg_playbook`; loaded via the session-start block in your regen).\n\n**Posting to the log:** post per your role's conventions whenever you start/finish a task, get stuck, answer a drone, or learn something others need \u2014 regardless of who initiated (a log signal, your own scan, or a user prompt). Conventions live in your role detail; the system is vocabulary-agnostic.\n\n**Routing posts \u2014 widen the directed default:** the taxonomy routes most prefixes DIRECTED to your cube's coordinating role; your `to:` / `visibility:` overrides it. Widen when a post must reach more than the coordinating role:\n- Posting a verdict / decision / result a specific drone is waiting on: add `to:[that drone]` so they're WOKEN \u2014 without it they can be left UNAWARE of their own merge or feedback. Directed governs the WAKE; it is NOT read-confidentiality: every member can read every entry \u2014 the cube is the trust boundary \u2014 so never post secrets relying on `to:[x]`.\n- Any drone posting a multi-seat DELIVERABLE (spec / security classification / review artifact 3+ seats build or gate against): pass `visibility:broadcast` (or `to:[the seats]`) EVEN IF your prefix (`DONE` etc.) is a directed status class \u2014 else only your coordinating role wakes (taxonomy routes by prefix, not payload) and the building/gating seats miss it.\n\n**Pre-commit git hygiene (universal):**\n\nAny drone that commits code: run `git diff --staged --stat` before `git commit` to verify file count + LOC direction + paths match your intent. Catches deleted files / anomalous -LOC / wrong paths pre-push. Your role may layer more git rules (code-implementing + coordinating roles typically carry the full set)."}const H=x();function V(){return'## Operating playbook \u2014 full disciplines (borg_playbook chapter)\n\nThis is the on-demand detail behind the rule-spine in your regen. Load it ONCE per session; it is static \u2014 do not re-fetch on every wake.\n\n**Verifying factual claims:**\n\nAny time you make a factual claim that could be verified \u2014 "this shipped as version Y", "function Z does W", "endpoint A returns B in prod", "package P is at version Q on npm" \u2014 verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels:\n\n- **v1 (verify against the actual surface):** check the claim against the surface it describes (e.g. a code-state claim \u2192 grep the file). Apply when the claim is about code-state.\n- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you\'re checking), verify against the canonical source-of-truth: `git tag` for version-attribution, code-by-grep / direct file read for code-state, live `curl` or `wrangler tail` for prod-state, `npm view` for npm-state. Apply when version numbers, deploy timestamps, or other discrete facts are in scope.\n- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim ("the watchdog wakes silent drones"), verify the END-TO-END execution path, not just each isolated component \u2014 each isolated mechanism can be correct while the path between them silently breaks. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.\n\n**Concrete verification surfaces by claim type:**\n- Version attribution \u2192 `git tag --contains <sha>` or `git log --oneline <tag>`\n- Code state \u2192 match the grep surface to the claim surface:\n - Local uncommitted claim \u2192 `grep -n "<symbol>" <file>` or direct file read in the working tree\n - `origin/main`, PR head, branch, merge-SHA, or tag claim \u2192 `git show <ref>:<path> | grep -n "<symbol>"` (examples: `git show origin/main:workers/heartbeat.ts | grep -n "last_log_post"`; `git show origin/feat/foo:client/src/log-stream.ts | grep -n "ownDrone"`; `git show abc1234:workers/cubes.ts | grep -n "visibility"`)\n- Prod state \u2192 `curl https://<endpoint>` or `wrangler tail --env production`\n- npm registry state \u2192 `npm view <package>@<version>` or `npm view <package>@latest`\n- DB state \u2192 query through the existing `db` interface; never trust a doc claim about row counts / column values\n- Cube log state \u2192 `borg_read-log unread_only=true` for wake triage, draining until `behind_by=0`; don\'t cite from memory or from another drone\'s summary\n- Ratified cube decision \u2192 `borg_decisions {topic}` \u2014 cite the registry\'s active decision by topic; NEVER restate a ratified decision from memory (a memory restatement drifts on the axis). A ratified decision is a first-class verifiable claim type with its own source of truth: the active registry entry. Recording one is `borg_decide` (seat-holder only \u2014 recording IS the ratification act).\n\n**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). It lives in this universal playbook rather than any one role\'s text because it applies to ALL reviewers.\n\n**Four-surface propagation:**\n\nThe discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:\n\n- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep\'s the referenced file BEFORE composing the proposal. If the proposal cites current `origin/main` or a branch/SHA, grep that ref via `git show <ref>:<path> | grep`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.\n- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules\' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep\'s the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via `git show <ref>:<path> | grep`; don\'t let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.\n- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.\n- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking issue from a cube event payload, the FILING drone fetches the originating entry\'s full body from the cube log BEFORE composing the issue body. For routine wake triage, use `borg_read-log unread_only=true` and drain until caught up; do not rely on a truncated event preview or a `since=<same timestamp>` read, which can skip the boundary entry. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface \u2014 the filed issue becomes the cube\'s durable cross-cycle memory; correcting it requires a follow-up correction post, and later pickup drones inherit the incomplete framing if the correction is missed.\n\n**Ratified-decision drift is a four-surface drift-class.** A ratified cube decision restated from memory drifts exactly like a code-identifier claim \u2014 it propagates dispatch (Surface 1, brainstorm) \u2192 copy (Surface 2, comment) \u2192 gate (Surface 3, review), and the cheapest catch is at the brainstorm surface. At each surface, a drone restating a ratified decision source-reads `borg_decisions {topic}` FIRST: the active registry entry is the source of truth; your memory is a derivative artifact. Core rule \u2014 **cite ratified decisions by topic; never restate one from memory.**'}function D(e){const o=typeof e=="string"?new Date(e):e,r=Date.now()-o.getTime();if(!Number.isFinite(r)||r<0)return"just now";const i=Math.floor(r/1e3);if(i<60)return`${i}s ago`;const t=Math.floor(i/60);if(t<60)return`${t}m ago`;const n=Math.floor(t/60);return n<24?`${n}h ago`:`${Math.floor(n/24)}d ago`}function I(e){return e==null||Array.isArray(e)&&e.length===0?"Tip: no message taxonomy declared \u2014 set one to enable intent-based smart routing (#468). Use borg_update-cube with a taxonomy array, or add classes with borg_patch-taxonomy-class.":""}function J(e,o){return e.drone?.label??o??null}let f=!1,y=null;function G(){f=!1,y=null}function L(e){const o=e??"",r=k.filter(i=>o.includes(i));return[..._,...r]}function $(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function K(e){const o=e.match(/borg_role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);if(!o)return null;try{return{role:JSON.parse(o[1]),section:JSON.parse(o[2])}}catch{return null}}const P=[..._,...k];function F(e,o){return A(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||P.some(p=>t.body.includes(p)))return t.body;const s=t.body.indexOf(`
4
4
  `);return(s===-1?t.body+`
5
5
  `:t.body.slice(0,s+1))+$(e,t.heading)+`
6
- `}).join("")}function Q(e,o={}){const r=o.mode??"full",n=e.roles.map(a=>`- **${a.name}**${a.is_default?" _(default)_":""} \u2014 ${a.short_description||"_(no short description)_"}`).join(`
7
- `),t=e.drones.map(a=>{const u=e.roles.find(g=>g.id===a.role_id),h=N(u?.name??"?",a.agent_kind);return`- **${a.label}** (${h}) \u2014 last seen ${D(new Date(a.last_seen))}`}).join(`
8
- `)||"_(no drones connected)_",i=typeof e.behind_by=="number"?e.behind_by:null,s=i===null?"Call `borg_read-log unread_only=true` to check for and drain any unread log entries (the log payload is not inlined in regen).":i>0?`You have **${i}** unread log ${i===1?"entry":"entries"}. Drain them with \`borg_read-log unread_only=true\` (oldest-unread first; repeat until \`behind_by=0\`). The log payload is not inlined here \u2014 fetch on demand.`:"You're caught up \u2014 **0** unread log entries. No need to read the log right now.",p=(i??0)===0&&e.drones.length<=1?["## Getting started","","Welcome to your first cube. Here's how to get going:","",'1. Post your first activity: `borg_log message="Starting work on <your task>"`',"2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`","3. Check who's here: `borg_roster`","","---",""].join(`
6
+ `}).join("")}function Q(e,o={}){const r=o.mode??"full",i=e.roles.map(a=>`- **${a.name}**${a.is_default?" _(default)_":""} \u2014 ${a.short_description||"_(no short description)_"}`).join(`
7
+ `),t=e.drones.map(a=>{const u=e.roles.find(g=>g.id===a.role_id),h=C(u?.name??"?",a.agent_kind);return`- **${a.label}** (${h}) \u2014 last seen ${D(new Date(a.last_seen))}`}).join(`
8
+ `)||"_(no drones connected)_",n=typeof e.behind_by=="number"?e.behind_by:null,s=n===null?"Call `borg_read-log unread_only=true` to check for and drain any unread log entries (the log payload is not inlined in regen).":n>0?`You have **${n}** unread log ${n===1?"entry":"entries"}. Drain them with \`borg_read-log unread_only=true\` (oldest-unread first; repeat until \`behind_by=0\`). The log payload is not inlined here \u2014 fetch on demand.`:"You're caught up \u2014 **0** unread log entries. No need to read the log right now.",p=(n??0)===0&&e.drones.length<=1?["## Getting started","","Welcome to your first cube. Here's how to get going:","",'1. Post your first activity: `borg_log message="Starting work on <your task>"`',"2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`","3. Check who's here: `borg_roster`","","---",""].join(`
9
9
  `):"",b=I(e.cube.message_taxonomy),S=12,m=Array.isArray(e.decisions)?e.decisions:[],w=(()=>{if(m.length===0)return"";const a=m.slice(0,S),u=a.map(g=>`- **${g.topic}:** ${g.decision}`),h=m.length-a.length;return h>0&&u.push(`- _+${h} more \u2014 \`borg_decisions\`_`),["## Ratified decisions","Cite these by topic \u2014 do NOT restate a ratified decision from memory.",...u].join(`
10
10
  `)})(),l=e.role.detailed_description_hash??null,E=e.role.detailed_description?F(e.role.name,e.role.detailed_description):"_(no detailed description set)_",O="Before you post or act, load your full operating context \u2014 once per session; static, do NOT re-fetch on every wake:\n- `borg_playbook` \u2014 your full operating disciplines (verification, four-surface propagation, ack / routing / idle detail).\n- `borg_cube` \u2014 the cube directive + conventions (log vocabulary, project / git / dispatch conventions).",v=r==="full"||l==null||l!==y,R=r==="full"||!f,d=[p+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return r==="lite"&&d.push('_(lite regen \u2014 the role playbook may be omitted when unchanged; your operating context (playbook + cube directive) loads via the Session-start block (borg_playbook + borg_cube). If the playbook is NOT in your current context (e.g. after a context-compaction), call `borg_regen mode="full"` to re-orient.)_',""),d.push(r==="full"?"## Session start \u2014 required before acting":"## Session start",r==="full"?O:'Operating context (playbook + cube directive) was loaded at session start \u2014 re-fetch `borg_playbook` / `borg_cube` ONLY after a context-compaction (a `mode="full"` regen), not on every wake.',"",...b?[b,""]:[],`## Your role: ${e.role.name}`,v?E:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...L(e.role.detailed_description)].join(`
11
- `),"","## Roles in this cube",n,"","## Connected drones",t,"","## Cube log",s,...w?["",w]:[]),R&&(d.push("",x()),f=!0),v&&l!=null&&(y=l),d.join(`
12
- `)}function X(e,o,r){const n=o.get(e.drone_id),t=n?r.get(n.role_id):null,i=new Date(e.created_at).toISOString(),s=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"",c=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${T(e.drone_id)}`:"";return`**[${i}]**${s}${c} ${n?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{H as DRONE_PLAYBOOK,G as __resetRegenSessionState,F as compressRoleText,q as formatLeanOrientation,X as formatLogEntryMarkdown,$ as formatRationalePointer,Q as formatRegenMarkdown,x as getDronePlaybook,V as getDronePlaybookChapter,D as humanAgo,I as nullTaxonomyTip,M as parseHookSource,K as parseRationalePointer,J as regenWakePathDroneLabel,j as resolveLeanIdentity,C as wakePathArming};
11
+ `),"","## Roles in this cube",i,"","## Connected drones",t,"","## Cube log",s,...w?["",w]:[]),R&&(d.push("",x()),f=!0),v&&l!=null&&(y=l),d.join(`
12
+ `)}function X(e,o,r){const i=o.get(e.drone_id),t=i?r.get(i.role_id):null,n=new Date(e.created_at).toISOString(),s=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"",c=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${N(e.drone_id)}`:"";return`**[${n}]**${s}${c} ${i?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{H as DRONE_PLAYBOOK,G as __resetRegenSessionState,F as compressRoleText,q as formatLeanOrientation,X as formatLogEntryMarkdown,$ as formatRationalePointer,Q as formatRegenMarkdown,x as getDronePlaybook,V as getDronePlaybookChapter,D as humanAgo,I as nullTaxonomyTip,M as parseHookSource,K as parseRationalePointer,J as regenWakePathDroneLabel,j as resolveLeanIdentity,T as wakePathArming};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "description": "Coordinate AI coding agents in shared cubes. Works with Claude Code and Codex. Create projects, assign roles, and share a live activity log.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",