borgmcp 1.0.41 → 1.0.43
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/dist/assimilate-cmd.js +32 -32
- package/dist/claude.js +13 -13
- package/dist/codex-launch.d.ts +11 -0
- package/dist/codex-launch.js +2 -1
- package/dist/regen-format.d.ts +93 -0
- package/dist/regen-format.js +11 -9
- package/dist/regen.js +3 -3
- package/package.json +1 -1
package/dist/assimilate-cmd.js
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import{dirname as re,basename as E}from"node:path";import{randomUUID as ne}from"node:crypto";import{roleSlug as oe,matchRoleByName as ie,pickDefaultRole as ae}from"./role-resolver.js";import{deriveCubeName as
|
|
1
|
+
import{dirname as re,basename as E}from"node:path";import{randomUUID as ne}from"node:crypto";import{roleSlug as oe,matchRoleByName as ie,pickDefaultRole as ae}from"./role-resolver.js";import{deriveCubeName as le,parseGitRemote as se,sanitizeRemoteUrl as ce}from"./cube-name.js";import{validateName as G}from"./name-validator.js";import{renderAssimilationWelcome as ue}from"./assimilate-welcome.js";import{shellEscape as me}from"./shell-escape.js";import{withCodexCwdArg as de}from"./codex-remote.js";import{buildAgentKickoffPrompt as fe,buildKickoffWakePathClause as he,recordCodexWakeTarget as ge,socketPathFromRemoteArgs as we}from"./codex-launch.js";import{perWorktreeBranchName as K,adoptWorktree as be,computeWorktreePath as q}from"./worktree-lifecycle.js";import{DroneEvictedError as ke}from"./drone-lifecycle.js";import{codexBorgSessionConfigArgs as ye}from"./launch-gate.js";import{resolveLaunchEnv as ve,resolveOllamaBaseUrl as $e,parseModel as pe}from"./model-presets.js";async function Ye(r,e){if(r.role!==void 0){const t=G(r.role);if(!t.ok)return e.stderr(t.error+`
|
|
2
2
|
`),1}if(r.flags.worktree!==void 0){const t=G(r.flags.worktree);if(!t.ok)return e.stderr(t.error+`
|
|
3
|
-
`),1}let i=await e.getCachedAuth();if(!i){if(!e.isTTY()&&!r.flags.yes)return e.stderr("borg setup required and stdin is non-interactive. Run `borg setup` first in an interactive terminal, then `borg assimilate`.\n"),1;i=await e.runSetup()}const a=e.findProjectRoot(e.cwd());let n;if(r.flags.cubeName)n=r.flags.cubeName;else{const t=e.runSync("git",["remote","get-url","origin"],a),o=t.status===0?t.stdout:null;if(n=
|
|
4
|
-
`)}}let
|
|
5
|
-
`);let
|
|
6
|
-
`),i=await e.runSetup(),
|
|
7
|
-
`),1;let
|
|
3
|
+
`),1}let i=await e.getCachedAuth();if(!i){if(!e.isTTY()&&!r.flags.yes)return e.stderr("borg setup required and stdin is non-interactive. Run `borg setup` first in an interactive terminal, then `borg assimilate`.\n"),1;i=await e.runSetup()}const a=e.findProjectRoot(e.cwd());let n;if(r.flags.cubeName)n=r.flags.cubeName;else{const t=e.runSync("git",["remote","get-url","origin"],a),o=t.status===0?t.stdout:null;if(n=le(a,o),o){const u=ce(o),m=u?se(u):null;u&&!m&&n&&e.stderr(`couldn't parse git remote '${u}' \u2014 using directory name '${n}' as cube name
|
|
4
|
+
`)}}let l=null;if(n&&n.includes("@")&&n.includes(":")){const t=n.lastIndexOf(":");l={ownerEmail:n.substring(0,t),cubeName:n.substring(t+1)},n=l.cubeName}const C=e.cwd();e.stderr(`Checking your cubes\u2026
|
|
5
|
+
`);let _;try{_=await e.listCubes(i.apiUrl,i.token)}catch(t){const o=t instanceof Error?t.message:String(t);if(o.includes("Authentication required")||o.includes("Authentication expired"))e.stderr(`Re-authenticating...
|
|
6
|
+
`),i=await e.runSetup(),_=await e.listCubes(i.apiUrl,i.token);else throw t}const A=_.find(t=>t.name===n);if(!A&&l)return e.stderr(`No cube named '${l.cubeName}' accessible to you owned by '${l.ownerEmail}'. Did you accept their invite? See borgmcp.ai/dashboard.
|
|
7
|
+
`),1;let s,N;if(A)s=await e.getCube(i.apiUrl,i.token,A.id),N=!1;else{let t;if(r.flags.template)t=r.flags.template;else if(r.flags.noTemplate)t=void 0;else if(e.isTTY())if(r.flags.yes)t="starter";else{const o=await e.listTemplates(i.apiUrl,i.token),u=["First drone joining a new cube. Apply a template?"];o.forEach((v,$)=>{const S=$===0?" (default)":"";u.push(` ${$+1}) ${v.name}${S} \u2014 ${v.description}`)}),u.push(` ${o.length+1}) skip \u2014 no template`);const m=(await e.prompt(u.join(`
|
|
8
8
|
`)+`
|
|
9
9
|
[1]: `)).trim(),y=m===""?1:parseInt(m,10);if(Number.isNaN(y)||y<1||y>o.length+1)return e.stderr(`invalid choice "${m}"
|
|
10
10
|
`),1;t=y<=o.length?o[y-1].name:void 0}else{if(!r.flags.yes)return e.stderr(`cube creation needs a template choice but stdin is non-interactive.
|
|
11
11
|
Pass --template <name>, --no-template, or --yes (defaults to starter).
|
|
12
12
|
`),1;t="starter"}e.stderr(n?`Creating cube '${n}'\u2026
|
|
13
13
|
`:`Creating your cube\u2026
|
|
14
|
-
`),
|
|
14
|
+
`),s=await e.createCube(i.apiUrl,i.token,t?{name:n??void 0,template:t}:{name:n??void 0}),N=!0}let d;if(r.role!==void 0){if(d=ie(s.roles,r.role),!d){const t=s.roles.map(m=>m.name).join(", "),o=Se(r.role,s.roles.map(m=>m.name)),u=o?` Did you mean "${o}"?`:"";return e.stderr(`no role matching "${r.role}" in cube "${s.name}". Available: ${t}.${u}
|
|
15
15
|
(Use --template <name> on first-drone setup or run \`borg_create-role\` from inside Claude.)
|
|
16
|
-
`),1}}else if(d=ae(
|
|
17
|
-
`),1;const
|
|
18
|
-
`),1;const
|
|
19
|
-
`),1}const
|
|
20
|
-
`);let c;try{c=await e.assimilate(i.apiUrl,i.token,{cube_id:
|
|
16
|
+
`),1}}else if(d=ae(s.roles,{isFirstDrone:N}),!d)return e.stderr(`cube "${s.name}" has no default or human-seat role; cannot infer a role. Either pass a role argument explicitly (e.g. \`borg assimilate builder\`) or run \`borg_create-role\` from inside Claude to set up roles.
|
|
17
|
+
`),1;const p=await e.getActiveCube();let x;if(p&&r.flags.here)if(p.cubeId===s.id)x=p.droneId;else return e.stderr(`this directory already hosts an active drone; remove --here or run from a fresh worktree
|
|
18
|
+
`),1;const O=r.flags.worktree!==void 0||p!==null&&!r.flags.here,J=x??p?.droneId??null,P=O?null:await e.getLaunchModel(s.id,a,J),w=r.flags.model??P?.model??d.default_model??null,T=$e(process.env,w!=null&&w===P?.model?P?.ollamaBaseUrl:void 0);if(w){const t=await e.checkModelReachable(w,e.fetch,T);if(!t.ok)return e.stderr(`${t.message}
|
|
19
|
+
`),1}const f=await e.resolveCli(r.flags.cli);e.stderr(`Joining cube '${s.name}' as ${d.name}\u2026
|
|
20
|
+
`);let c;try{c=await e.assimilate(i.apiUrl,i.token,{cube_id:s.id,role_id:d.id,hostname:e.getHostname(),agent_kind:f,model:w,...x?{prior_drone_id:x}:{}})}catch(t){if(t instanceof ke&&x!=null)return e.stderr(`seat evicted \u2014 this worktree's saved seat was evicted from the cube. Re-assimilate fresh from a terminal, or remove this worktree.
|
|
21
21
|
`),1;const o=t instanceof Error?t.message:String(t);return e.stderr(`assimilate failed: ${o}
|
|
22
|
-
`),1}const b=
|
|
22
|
+
`),1}const b=s.roles.find(t=>t.id===c.role_id)??d;c.reattached?e.stderr(`re-attached to existing seat ${c.drone_label} (session token rotated, no new drone minted)
|
|
23
23
|
`):b.id!==d.id&&e.stderr(`Note: your invite didn't grant the "${d.name}" role \u2014 assimilated as "${b.name}" instead.
|
|
24
|
-
`);const
|
|
24
|
+
`);const V=O;let h=null;if(V){const t=e.runSync("git",["rev-parse","--verify","HEAD"],a);if(t.status!==0)return e.stderr(`sibling worktree spawn requires HEAD pointing at a commit.
|
|
25
25
|
Fix: create at least one commit (\`git commit --allow-empty -m "initial"\`)
|
|
26
26
|
OR: pass --here to skip the sibling spawn and use the current directory
|
|
27
27
|
`),1;e.runSync("git",["fetch","origin"],a);let o="origin/main";e.runSync("git",["rev-parse","--verify","origin/main"],a).status!==0&&e.runSync("git",["rev-parse","--verify","origin/master"],a).status===0&&(o="origin/master");const m=t.stdout.trim(),y=e.runSync("git",["rev-parse",o],a).stdout.trim();m!==y&&e.stderr(`note: local HEAD (${m.slice(0,7)}) differs from ${o} (${y.slice(0,7)}); new worktree will start on ${o}
|
|
28
|
-
`);const v=E(a)
|
|
29
|
-
`),1;const
|
|
30
|
-
`),1;e.stderr(`spawned sibling worktree at ${k} on branch ${
|
|
31
|
-
`),e.chdir(k),e.stderr(
|
|
32
|
-
`),
|
|
33
|
-
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${
|
|
34
|
-
`)}return 1}e.setTerminalTitle(c.drone_label,
|
|
35
|
-
`)}if(!
|
|
36
|
-
`),e.stderr(
|
|
37
|
-
`)}await e.probeMcpReady()||e.stderr(`warning: borg-mcp readiness probe did not complete within the timeout; launching ${
|
|
38
|
-
`);const X=e.getInboxPath(c.cube_id,c.drone_id),
|
|
39
|
-
`),
|
|
40
|
-
Agent exited. You were working in ${
|
|
28
|
+
`);const v=E(a),$=r.flags.worktree??oe(b.name);if($.length===0)return e.stderr(`cannot derive a worktree name from role "${b.name}"; pass an explicit --worktree <name>
|
|
29
|
+
`),1;const S=e.homedir();let k=q(S,v,$),F=2;for(;e.pathExists(k)||Re(e,a,k);)k=q(S,v,$,F),F++;e.mkdirp(re(k));const W=K(E(k),v),Y=e.runSync("git",["worktree","add","-b",W,k,o],a);if(Y.status!==0)return e.stderr(`git worktree add failed: ${z(Y.stderr)}
|
|
30
|
+
`),1;e.stderr(`spawned sibling worktree at ${k} on branch ${W} (${o}); original dir is registered as active (edit ~/.config/borgmcp/cubes.json if stale).
|
|
31
|
+
`),e.chdir(k),e.stderr(xe(k,W,a)),h=e.cwd()}try{await e.setActiveCube({cubeId:c.cube_id,droneId:c.drone_id,name:s.name,sessionToken:c.session_token,droneLabel:c.drone_label,apiUrl:i.apiUrl,roleName:b.name,isHumanSeat:b.is_human_seat,...b.role_class?{roleClass:b.role_class}:{}})}catch(t){const o=t instanceof Error?t.message:String(t);if(e.stderr(`setActiveCube failed: ${o}
|
|
32
|
+
`),h){const u=e.runSync("git",["worktree","remove","--force",h],a);u.status===0?e.stderr(`rolled back spawned worktree at ${h}
|
|
33
|
+
`):e.stderr(`manual cleanup needed: \`git worktree remove --force ${h}\` (rollback attempt failed: ${z(u.stderr).trim()||"unknown"})
|
|
34
|
+
`)}return 1}e.setTerminalTitle(c.drone_label,s.name);const Q=e.isTTY()&&!process.env.NO_COLOR&&!process.env.CI;e.stdout(ue(b.name,s.name,Q));const g=e.cwd();try{e.installProjectSessionHook(g)}catch{e.stderr(`warning: could not install the project-local SessionStart hook in ${g}; it will be re-attempted on the next borg launch
|
|
35
|
+
`)}if(!h){e.runSync("git",["fetch","origin","--prune"],g);const t=K(E(g),E(a)),o=be(e.runSync,g,t,"origin/main");o.action==="adopted"?(e.stderr(`worktree: adopted branch ${t} at origin/main
|
|
36
|
+
`),e.stderr(Ce(g,t))):o.message&&e.stderr(`worktree sync: ${o.message}
|
|
37
|
+
`)}await e.probeMcpReady()||e.stderr(`warning: borg-mcp readiness probe did not complete within the timeout; launching ${f} anyway \u2014 the kickoff prompt's ToolSearch fallback will recover if the MCP server takes longer to start.
|
|
38
|
+
`);const X=e.getInboxPath(c.cube_id,c.drone_id),I=f==="codex"?`borg-wake-${ne()}`:null,Z=he(f==="codex"?"codex":"claude",f==="claude"?X:null);let U,H=[],R,D=null,L=null;const M=e.findProjectRoot(g);w?await e.setLaunchModel(c.cube_id,M,{model:w,ollamaBaseUrl:pe(w).kind==="ollama"?T:null}):await e.clearLaunchModel(c.cube_id,M);const B=ve(w,T),j={...process.env,...B.set,BORG_SESSION:"1"};for(const t of B.unset)delete j[t];if(f==="codex"){const t=await e.prepareCodexRemoteLaunch();t.warning?(e.stderr(`warning: ${t.warning}
|
|
39
|
+
`),U="\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."):U="Codex wake-path capability check passed: remote-control socket established for this session.",H=t.args,Object.keys(t.env).length>0&&Object.assign(j,t.env),D=we(t.args),L=t.server?.cleanup??null}R=[fe({cli:f,codexWakeNonce:I,monitorClause:Z,codexWakePathClause:U})],f==="codex"&&(R=[...ye(),...H,...de(R,g)]);const ee=e.exec(f,R,g,j);f==="codex"&&D&&I&&ge({deps:e,cubeId:c.cube_id,droneId:c.drone_id,socketPath:D,cwd:g,previewNeedle:I,launchedAtSeconds:Math.floor(Date.now()/1e3)});const te=await ee;if(L)try{L()}catch{}return h&&C!==h&&e.stderr(`
|
|
40
|
+
Agent exited. You were working in ${h}; your shell is back in ${C}.
|
|
41
41
|
To return:
|
|
42
|
-
cd ${me(
|
|
43
|
-
`),te}function
|
|
42
|
+
cd ${me(h)}
|
|
43
|
+
`),te}function xe(r,e,i){return`
|
|
44
44
|
WORKTREE STEERING: You are in worktree ${r} on branch ${e}. Do ALL work HERE \u2014 cut your feature branch (fix/.../feat/...) off ${e} in THIS worktree, use relative paths / your cwd. NEVER \`git -C ${i}\` or operate on the primary checkout ${i}: the same branch can't be checked out in two worktrees, so work created in the primary won't reach your wt-branch without manual surgery (cherry-pick/merge).
|
|
45
|
-
`}function
|
|
45
|
+
`}function Ce(r,e){return`
|
|
46
46
|
WORKTREE STEERING: This checkout is now on branch ${e}. Do ALL work HERE in ${r} \u2014 cut your feature branch (fix/.../feat/...) off ${e}, use relative paths / your cwd.
|
|
47
|
-
`}function
|
|
48
|
-
`).some(n=>n===`worktree ${i}`)}function Se(r,e){if(e.length===0)return null;const i=r.toLowerCase();let a=null;for(const n of e){const
|
|
47
|
+
`}function z(r){return r.replace(/[\x00-\x1F\x7F]/g,"")}function Re(r,e,i){const a=r.runSync("git",["worktree","list","--porcelain"],e);return a.status!==0?!1:a.stdout.split(`
|
|
48
|
+
`).some(n=>n===`worktree ${i}`)}function Se(r,e){if(e.length===0)return null;const i=r.toLowerCase();let a=null;for(const n of e){const l=Ee(i,n.toLowerCase());l<=2&&(a===null||l<a.distance)&&(a={name:n,distance:l})}return a?a.name:null}function Ee(r,e){if(r===e)return 0;if(r.length===0)return e.length;if(e.length===0)return r.length;const i=new Array(e.length+1),a=new Array(e.length+1);for(let n=0;n<=e.length;n++)i[n]=n;for(let n=1;n<=r.length;n++){a[0]=n;for(let l=1;l<=e.length;l++){const C=r[n-1]===e[l-1]?0:1;a[l]=Math.min(a[l-1]+1,i[l]+1,i[l-1]+C)}for(let l=0;l<=e.length;l++)i[l]=a[l]}return i[e.length]}export{Ye as runAssimilate,z as safeStderr,Se as suggestRoleName};
|
package/dist/claude.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawn as
|
|
3
|
-
`)})();if((process.argv[2]==="--help"||process.argv[2]==="-h")&&(process.stdout.write(
|
|
4
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=re(),i=await ee({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=
|
|
5
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await
|
|
6
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=await Q({},e.options);process.exit(r)}if(process.argv[2]==="launch-all"){const e=A(process.argv.slice(3));e.ok||(process.stderr.write(
|
|
7
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const r=x(),i=await I(e.args,r);process.exit(i)}const
|
|
8
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1)),we(await fe()!==null,await ge()!==null)){const e=
|
|
9
|
-
`)),process.stderr.write("Run `borg --help` for usage.\n"),process.exit(1));const
|
|
10
|
-
\u25FC Failed to launch ${
|
|
11
|
-
`)}`)):console.error(`${o()}${
|
|
12
|
-
\u25FC Failed to launch ${
|
|
13
|
-
`)}`),process.exit(1)}),
|
|
14
|
-
\u25FC Error: ${
|
|
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{parseSyncArgs as X,runSync as z}from"./sync.js";import{parseCleanupArgs as J,runCleanup as Q}from"./cleanup-cmd.js";import{parseAssimilateArgs as Z}from"./parse-assimilate-args.js";import{runAssimilate as ee}from"./assimilate-cmd.js";import{buildDefaultAssimilateDeps as re}from"./assimilate-deps.js";import{parseLaunchAllArgs as A}from"./parse-launch-all-args.js";import{unknownSubcommand as oe}from"./unknown-subcommand.js";import{runLaunchAll as I}from"./launch-all-cmd.js";import{buildDefaultLaunchAllDeps as x}from"./launch-all-deps.js";import{discoverDroneCandidates as se}from"./launch-all-discovery.js";import{runBareLaunchMenu as te,shouldShowLaunchMenu as ae}from"./bare-launch-menu.js";import{setTerminalTitle as ie}from"./terminal-title.js";import{initConsolePrefix as ce,consolePrefix as o}from"./console-prefix.js";import{initDebugFromArgv as ne}from"./debug.js";import{fetchLatestBorgmcpVersion as le,compareVersionsForStaleness as de}from"./stale-version-check.js";import{defaultCliChoiceDeps as pe,detectCliAvailability as C,installedCliNames as P,parseCliFlag as ue,resolveCliChoice as me}from"./cli-platform.js";import{getRefreshToken as fe,getIdToken as ge}from"./config.js";import{composeGetStarted as he,shouldShowGetStarted as we}from"./get-started.js";import{prepareCodexRemoteLaunch as xe,withCodexCwdArg as Ce,defaultCodexRemoteDeps as ke,checkCodexBridgeHealthy as ve}from"./codex-remote.js";import{findLoadedCodexThread as be}from"./codex-app-server.js";import{buildAgentKickoffPrompt as $e,buildKickoffWakePathClause as Se,recordCodexWakeTarget as ye,socketPathFromRemoteArgs as R}from"./codex-launch.js";import{codexBorgSessionConfigArgs as Te}from"./launch-gate.js";import{addCodexMcpServer as Ae,addCodexSessionStartHook as Ie,addCodexUserPromptSubmitHook as Pe,addMcpServer as Re,addProjectSessionStartHook as De,addUserPromptSubmitHook as Le,isCodexMcpServerConfigured as Ee,isMcpServerConfigured as Me,removeSessionStartHook as Oe}from"./config-utils.js";async function He(){ne(process.argv),U(),await ce();const n=(async()=>{if(!process.stderr.isTTY)return;const e=h(),r=await le();if(!r)return;const i=de(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=Z(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=re(),i=await ee({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=X(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 z({},e.options);process.exit(r)}if(process.argv[2]==="cleanup"){const e=J(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 Q({},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=x(),i=await I(e.args,r);process.exit(i)}const c=oe(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)),we(await fe()!==null,await ge()!==null)){const e=P(C()).length>0;process.stdout.write(he(e)),process.exit(0)}const f=ue(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 k=async e=>{const r=H({input:process.stdin,output:process.stdout});try{return await r.question(e)}finally{r.close()}};let s=await me(f.cli,pe(k,()=>process.stdin.isTTY===!0));Fe();const a=await F();if(ae({extraArgs:process.argv.slice(2),stdinIsTTY:process.stdin.isTTY===!0,stdoutIsTTY:process.stdout.isTTY===!0})){const e=P(C()).find(m=>m!==s)??null;let r=!1;a&&(r=(await se({targetCubeId:a.cubeId},x())).length>0);const i=await te({defaultCli:s,otherInstalledCli:e,hasLaunchAllTargets:r},k);if(i.kind==="launch-all"){const m=A([]),L=m.ok?await I(m.args,x()):1;process.exit(L)}s=i.cli}const l=f.rest;ie(a?{label:a.droneLabel,cubeName:a.name}:null,O(process.cwd()));const D=Se(s==="codex"?"codex":"claude",a&&s==="claude"?B(a.cubeId,a.droneId):null);await Promise.race([n,new Promise(e=>setTimeout(e,2e3))]);const v=s==="codex"?`borg-wake-${M()}`:null;let g,b=[],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 xe(ke());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.",b=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 $=$e({cli:s,codexWakeNonce:v,monitorClause:D,codexWakePathClause:g});let w=[...l,$];s==="codex"&&(w=[...Te(),...b,...Ce(w,process.cwd())]),console.error(`${o()}${t.blue(`\u25FC Launching ${s==="claude"?"Claude Code":"Codex"}\u2026`)}`);const S=E(s,w,{stdio:"inherit",shell:!1,env:d});s==="codex"&&a&&p&&(ye({deps:{setCodexWakeTarget:_,findLoadedCodexThread:be},cubeId:a.cubeId,droneId:a.droneId,socketPath:p,passthroughArgs:l,previewNeedle:v??$.slice(0,120),cwd:process.cwd(),launchedAtSeconds:Math.floor(Date.now()/1e3)}),W(e=>ve(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(`
|
|
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 Fe(){const n=C();if(n.claude)try{Me()||Re(),De(y(process.cwd())),Oe(),Le()}catch(c){console.error(`${o()}${t.yellow(`warning: Claude Code integration check failed: ${c?.message??c}`)}`)}if(n.codex)try{Ee()||Ae(),Ie(),Pe()}catch(c){console.error(`${o()}${t.yellow(`warning: Codex integration check failed: ${c?.message??c}`)}`)}}He().catch(n=>{console.error(`${o()}${t.red(`
|
|
14
|
+
\u25FC Error: ${n.message}
|
|
15
15
|
`)}`),process.exit(1)});
|
package/dist/codex-launch.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { BorgCli } from './cubes.js';
|
|
2
|
+
import { type AgentKind } from './regen-format.js';
|
|
3
|
+
/**
|
|
4
|
+
* The claude kickoff prompt's wake-path section (gh#929) — the SAME shared
|
|
5
|
+
* `wakePathArming` the SessionStart hook + /clear orientation use (one place,
|
|
6
|
+
* not three), plus a one-line NEVER-TaskStop safety reminder preserved from
|
|
7
|
+
* the pre-gh#929 monitorClause. Built by both launch call sites
|
|
8
|
+
* (claude.ts + assimilate-cmd.ts) and passed to `buildAgentKickoffPrompt` as
|
|
9
|
+
* `monitorClause`. Codex wakes via the app-server (no tail-Monitor / `/loop`
|
|
10
|
+
* to arm) → empty; no active cube (no inboxPath) → empty.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildKickoffWakePathClause(agentKind: AgentKind, inboxPath: string | null): string;
|
|
2
13
|
export interface CodexWakeTargetDeps {
|
|
3
14
|
setCodexWakeTarget: (cubeId: string, droneId: string, target: {
|
|
4
15
|
threadId: string;
|
package/dist/codex-launch.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import{wakePathArming as n}from"./regen-format.js";function s(e,t){return e!=="claude"||!t?"":n("claude",t)+`
|
|
2
|
+
Keep this Monitor armed for the whole session \u2014 NEVER TaskStop it (the generic /loop "TaskStop any Monitor you armed" step does NOT apply to the cube inbox Monitor; it targets throwaway loop-scratch watches only). The sole exception is a confirmed terminal eviction (410 DRONE_EVICTED). `}function d(e){const t=e.codexWakeNonce?`Wake target nonce: ${e.codexWakeNonce}. `:"",r=e.codexWakePathClause??"Codex Borg wakeups use remote-control when available; if no wake arrives, run borg_regen manually when returning to the session.";return`${e.cli==="claude"?"/loop ":""}Call borg_regen and follow the playbook in its response. `+t+'Note: at session start the borg MCP server is still spinning up in parallel \u2014 if a system reminder claims "MCP server disconnected" or the borg tools are not yet registered, do NOT bail. Recover via `ToolSearch({query: "select:mcp__borg__borg_regen,mcp__borg__borg_log,Monitor", max_results: 3})` to load the bootstrap tools in one call, then call borg_regen. The server typically becomes available within a few seconds. '+(e.cli==="claude"?e.monitorClause:r)}function i(e){const t=e.indexOf("--remote");if(t<0)return null;const r=e[t+1];return r?.startsWith("unix://")?r.slice(7):null}function c(e){if(e[0]==="resume"&&e[1]&&!e[1].startsWith("-"))return e[1];const t=e.indexOf("--resume");return t>=0&&e[t+1]?e[t+1]:null}async function u(e){try{const t=e.passthroughArgs?c(e.passthroughArgs):null;if(t){await e.deps.setCodexWakeTarget(e.cubeId,e.droneId,{threadId:t,socketPath:e.socketPath});return}const r=Date.now()+15e3;for(;Date.now()<r;){const a=await e.deps.findLoadedCodexThread({socketPath:e.socketPath,cwd:e.cwd,previewIncludes:e.previewNeedle,updatedAfter:e.launchedAtSeconds-5});if(a){await e.deps.setCodexWakeTarget(e.cubeId,e.droneId,{threadId:a,socketPath:e.socketPath});return}await new Promise(o=>setTimeout(o,500))}}catch{}}export{d as buildAgentKickoffPrompt,s as buildKickoffWakePathClause,u as recordCodexWakeTarget,i as socketPathFromRemoteArgs,c as threadIdFromPassthroughArgs};
|
package/dist/regen-format.d.ts
CHANGED
|
@@ -5,6 +5,99 @@
|
|
|
5
5
|
* Lives in its own module so regen.ts can import these without pulling in
|
|
6
6
|
* index.ts's stdio MCP server bootstrap.
|
|
7
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Extract the SessionStart `source` from a Claude Code hook payload (gh#926).
|
|
10
|
+
*
|
|
11
|
+
* SessionStart hooks receive a JSON object on stdin whose `source` field is
|
|
12
|
+
* one of `startup` / `resume` / `clear` / `compact`. The `borg-regen`
|
|
13
|
+
* SessionStart hook uses this to detect a `/clear` re-orientation, which is
|
|
14
|
+
* the FIRST time the hook is the SOLE orientation path (the launch kickoff
|
|
15
|
+
* prompt is gone) AND the moment Claude Code clears the session-scoped
|
|
16
|
+
* `/loop` + `ScheduleWakeup` — so the re-injected orientation must instruct
|
|
17
|
+
* an operational re-arm.
|
|
18
|
+
*
|
|
19
|
+
* Best-effort + total: empty input (manual / TTY run with no stdin),
|
|
20
|
+
* malformed JSON, a missing `source`, or a non-string `source` all return
|
|
21
|
+
* `null` so the caller falls back to the default (full-regen) behavior. A
|
|
22
|
+
* hook bin must never throw on unexpected stdin.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseHookSource(raw: string): string | null;
|
|
25
|
+
/** The agent runtime a session runs under — drives the wake-path branch. */
|
|
26
|
+
export type AgentKind = 'claude' | 'codex';
|
|
27
|
+
/**
|
|
28
|
+
* The agent-branched WAKE-PATH ARMING sub-block (gh#929/gh#927) — the single
|
|
29
|
+
* shared "re-establish your wake path" instruction reused by the launch
|
|
30
|
+
* kickoff prompt, the lean SessionStart-hook orientation, and the /clear
|
|
31
|
+
* re-orient. Factored to ONE place so the most load-bearing (and most
|
|
32
|
+
* drift-prone) liveness instruction can never diverge across the three
|
|
33
|
+
* surfaces.
|
|
34
|
+
*
|
|
35
|
+
* Agent-branched on the existing env-agnostic signal (BORG_SESSION-style
|
|
36
|
+
* `isCodexRemoteWakeEnabled`), NOT on a mutable server-recorded field:
|
|
37
|
+
* - claude: arm the inbox-file tail Monitor + engage `/loop` + set a ~1800s
|
|
38
|
+
* `ScheduleWakeup` fallback heartbeat (three independent wake mechanisms).
|
|
39
|
+
* - codex: wakes via the app-server remote-wake injection — it has NO
|
|
40
|
+
* tail-Monitor and NO `/loop` heartbeat to arm, so instructing either would
|
|
41
|
+
* be wrong; the recovery is a manual `borg_regen` on return.
|
|
42
|
+
*
|
|
43
|
+
* `inboxPath` is the deterministic client-generated UUID path
|
|
44
|
+
* (`~/.config/borgmcp/inboxes/<cubeId>/<droneId>.log`) — no user-controlled
|
|
45
|
+
* metacharacters, same source as today's launch monitorClause.
|
|
46
|
+
*/
|
|
47
|
+
export declare function wakePathArming(agentKind: AgentKind, inboxPath: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the lean-orientation identity (gh#927), preferring the fresh
|
|
50
|
+
* network `regen()` result and falling back per-field to the local
|
|
51
|
+
* `getActiveCube` state. When `result` is null — the net-free fallback path
|
|
52
|
+
* taken on a `regen()` network failure — identity comes entirely from local
|
|
53
|
+
* state, so a weak drone that hits a SessionStart network blip still gets
|
|
54
|
+
* oriented (with its wake-path arming) instead of left dormant.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveLeanIdentity(active: {
|
|
57
|
+
name: string;
|
|
58
|
+
droneLabel: string;
|
|
59
|
+
roleName?: string | null;
|
|
60
|
+
}, result: {
|
|
61
|
+
cube?: any;
|
|
62
|
+
drone?: any;
|
|
63
|
+
role?: any;
|
|
64
|
+
} | null): {
|
|
65
|
+
cubeName: string;
|
|
66
|
+
droneLabel: string;
|
|
67
|
+
roleName: string | null;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The canonical LEAN orientation core (gh#929/gh#927) — the single shared
|
|
71
|
+
* "minimal operational orientation" rendered for a drone at launch, on every
|
|
72
|
+
* SessionStart source (startup/resume/clear/compact), and on /clear. It
|
|
73
|
+
* SUPERSEDES the per-surface variants: the SessionStart hook renders this
|
|
74
|
+
* instead of the full ~20.7KB `formatRegenMarkdown` (which the harness
|
|
75
|
+
* truncates to a ~2KB preview, leaving weak models partially oriented), and
|
|
76
|
+
* the /clear re-orient is just this with `source: 'clear'`.
|
|
77
|
+
*
|
|
78
|
+
* Three load-bearing parts, all kept (per the SEC/PM/CR rails):
|
|
79
|
+
* - IDENTITY: cube + drone label + role, so a weak model knows who it is.
|
|
80
|
+
* - WAKE-PATH ARMING: the shared `wakePathArming` block (liveness — correct
|
|
81
|
+
* to carry pre-`borg_regen`).
|
|
82
|
+
* - `borg_regen` POINTER: the path to the FULL context AND the safety floor
|
|
83
|
+
* (NEVER-TaskStop / 410-EVICTED / 423-FROZEN rules live in role-text,
|
|
84
|
+
* reachable only via this pointer). Kept in EVERY render.
|
|
85
|
+
*
|
|
86
|
+
* Template-agnostic (#921): the escalation target is "your cube's coordinating
|
|
87
|
+
* role" — NEVER a hardcoded `coordinator` / `drone-1` (this is the
|
|
88
|
+
* single most-rendered instruction surface in the product, and the first
|
|
89
|
+
* thing a weak model on a NON-sw-dev template reads). `roleName` is optional
|
|
90
|
+
* so the net-free fallback can render from local `getActiveCube` state when a
|
|
91
|
+
* `regen()` network call is unavailable.
|
|
92
|
+
*/
|
|
93
|
+
export declare function formatLeanOrientation(args: {
|
|
94
|
+
cubeName: string;
|
|
95
|
+
droneLabel: string;
|
|
96
|
+
roleName?: string | null;
|
|
97
|
+
inboxPath: string;
|
|
98
|
+
agentKind: AgentKind;
|
|
99
|
+
source?: string | null;
|
|
100
|
+
}): string;
|
|
8
101
|
/**
|
|
9
102
|
* Build the universal drone playbook.
|
|
10
103
|
*
|
package/dist/regen-format.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import{ROLE_SCOPED_SAFETY_DISCIPLINES as m,UNIVERSAL_SAFETY_DISCIPLINES as f}from"./templates.js";import{parseRoleSections as O}from"./role-section.js";import{formatRoleAgentLabel as S}from"./roster-render.js";function y(){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**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**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 $=y();function F(){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\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.'}function R(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 T(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 U(e,o){return e.drone?.label??o??null}let d=!1,h=null;function Y(){d=!1,h=null}function A(e){const o=e??"",r=m.filter(n=>o.includes(n));return[...f,...r]}function D(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function B(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 C=[...f,...m];function N(e,o){return O(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||C.some(u=>t.body.includes(u)))return t.body;const s=t.body.indexOf(`
|
|
2
|
-
`);return(s
|
|
3
|
-
`:t.body.slice(0,s+1))+D(e,t.heading)+`
|
|
4
|
-
`
|
|
5
|
-
|
|
6
|
-
`)
|
|
7
|
-
`)
|
|
8
|
-
`),"","
|
|
9
|
-
`)
|
|
1
|
+
import{ROLE_SCOPED_SAFETY_DISCIPLINES as f,UNIVERSAL_SAFETY_DISCIPLINES as y}from"./templates.js";import{parseRoleSections as O}from"./role-section.js";import{formatRoleAgentLabel as S}from"./roster-render.js";function F(e){if(!e||!e.trim())return null;try{const r=JSON.parse(e)?.source;return typeof r=="string"?r:null}catch{return null}}function R(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 Y(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function B(e){const{cubeName:o,droneLabel:r,roleName:n,inboxPath:t,agentKind:i,source:a}=e,u=a==="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)_"}`,u,"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.","",R(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 b(){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**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**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 U=b();function M(){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\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.'}function N(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 T(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 W(e,o){return e.drone?.label??o??null}let h=!1,g=null;function j(){h=!1,g=null}function A(e){const o=e??"",r=f.filter(n=>o.includes(n));return[...y,...r]}function C(e,o){return`rationale \u2192 borg_role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function H(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 D=[...y,...f];function I(e,o){return O(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||D.some(d=>t.body.includes(d)))return t.body;const a=t.body.indexOf(`
|
|
4
|
+
`);return(a===-1?t.body+`
|
|
5
|
+
`:t.body.slice(0,a+1))+C(e,t.heading)+`
|
|
6
|
+
`}).join("")}function q(e,o={}){const r=o.mode??"full",n=e.roles.map(s=>`- **${s.name}**${s.is_default?" _(default)_":""} \u2014 ${s.short_description||"_(no short description)_"}`).join(`
|
|
7
|
+
`),t=e.drones.map(s=>{const _=e.roles.find(E=>E.id===s.role_id),x=S(_?.name??"?",s.agent_kind);return`- **${s.label}** (${x}) \u2014 last seen ${N(new Date(s.last_seen))}`}).join(`
|
|
8
|
+
`)||"_(no drones connected)_",i=typeof e.behind_by=="number"?e.behind_by:null,a=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.",d=(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(`
|
|
9
|
+
`):"",p=T(e.cube.message_taxonomy),l=e.role.detailed_description_hash??null,v=e.role.detailed_description?I(e.role.name,e.role.detailed_description):"_(no detailed description set)_",w="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).",m=r==="full"||l==null||l!==g,k=r==="full"||!h,c=[d+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return r==="lite"&&c.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.)_',""),c.push(r==="full"?"## Session start \u2014 required before acting":"## Session start",r==="full"?w:'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.',"",...p?[p,""]:[],`## Your role: ${e.role.name}`,m?v:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...A(e.role.detailed_description)].join(`
|
|
10
|
+
`),"","## Roles in this cube",n,"","## Connected drones",t,"","## Cube log",a),k&&(c.push("",b()),h=!0),m&&l!=null&&(g=l),c.join(`
|
|
11
|
+
`)}function V(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(),a=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"";return`**[${i}]**${a} ${n?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{U as DRONE_PLAYBOOK,j as __resetRegenSessionState,I as compressRoleText,B as formatLeanOrientation,V as formatLogEntryMarkdown,C as formatRationalePointer,q as formatRegenMarkdown,b as getDronePlaybook,M as getDronePlaybookChapter,N as humanAgo,T as nullTaxonomyTip,F as parseHookSource,H as parseRationalePointer,W as regenWakePathDroneLabel,Y as resolveLeanIdentity,R as wakePathArming};
|
package/dist/regen.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{regen as
|
|
3
|
-
`)}async function
|
|
2
|
+
import{regen as i,listCubes as a}from"./remote-client.js";import{getActiveCube as c,inboxPathForDrone as u}from"./cubes.js";import{parseHookSource as h,formatLeanOrientation as l,resolveLeanIdentity as p}from"./regen-format.js";import{isCodexRemoteWakeEnabled as b}from"./codex-app-wake.js";import{handleVersionFlag as d}from"./version.js";import{gateAllowsActivation as g}from"./launch-gate.js";async function m(){if(process.stdin.isTTY)return"";try{const e=[];for await(const t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}catch{return""}}async function f(){if(d(),!g("borg-regen SessionStart hook"))return;const e=h(await m()),t=await c();if(!t){await w();return}const o=u(t.cubeId,t.droneId),n=b()?"codex":"claude";let s=null;try{s=await i(t.sessionToken,t.apiUrl)}catch{s=null}const r=p(t,s);process.stdout.write(l({...r,inboxPath:o,agentKind:n,source:e})+`
|
|
3
|
+
`)}async function w(){const e=[];e.push("# Borg MCP \u2014 not connected to a cube"),e.push(""),e.push("The borg MCP server is installed in this project but no cube has been joined here yet."),e.push(""),e.push("**Drone instructions:**"),e.push("1. **Tell the user** which cubes exist on their account (see the list below) and ask them which one to join, or whether to create a new cube. This is the first thing you should surface in this session."),e.push("2. **Do not** call `borg_assimilate`, `borg_create-cube`, or any other state-changing borg tool until the user has chosen."),e.push("3. The user may also decline to use borg in this project at all \u2014 that's a valid choice; just stop suggesting it."),e.push("");try{const{cubes:t}=await a();if(t.length>0){e.push("## Cubes on your account");for(const o of t)e.push(`- **${o.name}** (id: ${o.id})`)}else e.push("## Cubes on your account"),e.push("_(none yet \u2014 offer to create one via `borg_create-cube` once the user confirms)_")}catch(t){e.push("## Cubes on your account"),e.push("_(could not list \u2014 "+(t?.message??String(t))+")_")}e.push(""),e.push("## Tools you can call once the user has chosen"),e.push('- Join an existing cube: `borg_assimilate cube_name="<their choice>"`'),e.push('- Create a new cube: `borg_create-cube name="<name>" cube_directive="<markdown>"` (optionally `template="software-dev"`)'),e.push("- See available templates: `borg_list-templates`"),process.stdout.write(e.join(`
|
|
4
4
|
`)+`
|
|
5
|
-
`)}
|
|
5
|
+
`)}f().catch(e=>{const t=e?.message??String(e);process.stderr.write(`borg-regen: ${t}
|
|
6
6
|
`),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borgmcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.43",
|
|
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",
|