borgmcp 1.0.45 → 1.0.47
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 +40 -40
- package/dist/cleanup-cmd.js +14 -13
- package/dist/config-utils.d.ts +17 -0
- package/dist/config-utils.js +3 -3
- package/dist/cubes.js +5 -5
- package/dist/index.js +12 -12
- package/dist/launch-all-cmd.js +30 -26
- package/dist/log-stream.d.ts +39 -3
- package/dist/log-stream.js +9 -9
- package/dist/regen-format.js +6 -6
- package/dist/remote-client.d.ts +1 -1
- package/dist/remote-client.js +1 -1
- package/dist/setup-confirm.d.ts +31 -0
- package/dist/setup-confirm.js +2 -2
- package/dist/setup.js +28 -28
- package/dist/templates.js +2 -1
- package/package.json +1 -1
package/dist/regen-format.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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";import{formatDroneAddressToken as R}from"./drone-address.js";function B(e){if(!e||!e.trim())return null;try{const r=JSON.parse(e)?.source;return typeof r=="string"?r:null}catch{return null}}function
|
|
2
|
-
`)}function U(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function
|
|
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 W=b();function j(){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 T(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 A(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 H(e,o){return e.drone?.label??o??null}let h=!1,g=null;function q(){h=!1,g=null}function D(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 V(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 I=[...y,...f];function L(e,o){return O(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||I.some(d=>t.body.includes(d)))return t.body;const a=t.body.indexOf(`
|
|
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";import{formatDroneAddressToken as R}from"./drone-address.js";function B(e){if(!e||!e.trim())return null;try{const r=JSON.parse(e)?.source;return typeof r=="string"?r:null}catch{return null}}function A(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 U(e,o){return{cubeName:o?.cube?.name??e.name,droneLabel:o?.drone?.label??e.droneLabel,roleName:o?.role?.name??e.roleName??null}}function W(e){const{cubeName:o,droneLabel:r,roleName:n,inboxPath:t,agentKind:i,source:a}=e,l=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)_"}`,l,"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.","",A(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**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 M=b();function j(){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 q(e,o){return e.drone?.label??o??null}let h=!1,g=null;function H(){h=!1,g=null}function D(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 V(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 I=[...y,...f];function L(e,o){return O(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||I.some(d=>t.body.includes(d)))return t.body;const a=t.body.indexOf(`
|
|
4
4
|
`);return(a===-1?t.body+`
|
|
5
5
|
`:t.body.slice(0,a+1))+C(e,t.heading)+`
|
|
6
6
|
`}).join("")}function J(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 ${
|
|
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
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=
|
|
9
|
+
`):"",p=T(e.cube.message_taxonomy),c=e.role.detailed_description_hash??null,w=e.role.detailed_description?L(e.role.name,e.role.detailed_description):"_(no detailed description set)_",v="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"||c==null||c!==g,k=r==="full"||!h,u=[d+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return r==="lite"&&u.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.)_',""),u.push(r==="full"?"## Session start \u2014 required before acting":"## Session start",r==="full"?v:'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?w:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...D(e.role.detailed_description)].join(`
|
|
10
10
|
`),"","## Roles in this cube",n,"","## Connected drones",t,"","## Cube log",a),k&&(u.push("",b()),h=!0),m&&c!=null&&(g=c),u.join(`
|
|
11
|
-
`)}function G(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}]`:"",l=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${R(e.drone_id)}`:"";return`**[${i}]**${a}${l} ${n?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{
|
|
11
|
+
`)}function G(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}]`:"",l=typeof e.drone_id=="string"&&e.drone_id.length>0?` ${R(e.drone_id)}`:"";return`**[${i}]**${a}${l} ${n?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{M as DRONE_PLAYBOOK,H as __resetRegenSessionState,L as compressRoleText,W as formatLeanOrientation,G as formatLogEntryMarkdown,C as formatRationalePointer,J as formatRegenMarkdown,b as getDronePlaybook,j as getDronePlaybookChapter,N as humanAgo,T as nullTaxonomyTip,B as parseHookSource,V as parseRationalePointer,q as regenWakePathDroneLabel,U as resolveLeanIdentity,A as wakePathArming};
|
package/dist/remote-client.d.ts
CHANGED
|
@@ -179,7 +179,7 @@ export declare function readLog(sessionToken: string, apiUrl: string, opts?: {
|
|
|
179
179
|
* flag on activity_log_acks. Idempotent — the server INSERT uses ON
|
|
180
180
|
* CONFLICT DO NOTHING. 204 No Content on success.
|
|
181
181
|
*/
|
|
182
|
-
export declare function ackLogEntry(sessionToken: string, apiUrl: string, entryId: string): Promise<void>;
|
|
182
|
+
export declare function ackLogEntry(sessionToken: string, apiUrl: string, entryId: string, kind?: 'ack' | 'claim'): Promise<void>;
|
|
183
183
|
/**
|
|
184
184
|
* Regen: one-shot composite of everything a drone needs to be oriented.
|
|
185
185
|
*
|
package/dist/remote-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getIdToken as f,getRefreshToken as h,clearTokens as m}from"./config.js";import{refreshIdToken as R,RefreshTokenInvalidError as g,RefreshTransientError as T}from"./auth.js";import{consolePrefix as S}from"./console-prefix.js";import{debugLog as b}from"./debug.js";import{assertUuidShape as E}from"./evict-drone.js";import{DroneEvictedError as C,DroneFrozenError as k,DRONE_EVICTED_CODE as P,DRONE_FROZEN_CODE as O,errorCodeFromBody as I}from"./drone-lifecycle.js";import{MODEL_DESCRIPTOR_REGEX as D}from"./model-presets.js";const L=process.env.BORG_API_URL||"https://api.borgmcp.ai",A=3,v=6e4;let l=null;function x(e){return l||(l=R(e).finally(()=>{l=null}),l)}function G(e){if(e==null)return null;const n=e.trim();return/^\d+$/.test(n)?parseInt(n,10)*1e3:null}function J(e,n,t=v,o=()=>Math.random()*500){const s=e??1e3*(n+1);return Math.min(s,t)+o()}function M(e){const n=(t,o)=>`${t}${/[.:!?]$/.test(t)?"":":"} ${o}`;try{const t=JSON.parse(e);if(typeof t?.error=="string")return typeof t.details=="string"?n(t.error,t.details):t.error;if(t?.error&&typeof t.error=="object"){const o=t.error.message,s=t.error.details??t.details;if(typeof o=="string"&&typeof s=="string")return n(o,s);if(typeof o=="string")return o}if(typeof t?.message=="string"&&typeof t?.details=="string")return n(t.message,t.details);if(typeof t?.message=="string")return t.message}catch{}return e}async function U(e,n,t){const o=t.maxRetries??A;let s=e,a=0;for(;s.status===429&&a<o;){const d=J(G(s.headers.get("Retry-After")),a,t.capMs,t.jitter);t.log?.(`rate limited (429); retrying in ${Math.round(d)}ms (attempt ${a+1}/${o})`),await t.sleep(d),a++,s=await n()}return s}function H(e){return new Promise(n=>setTimeout(n,e))}async function q(){let e=await f();if(!e){const n=await h(),t=n!=null;if(n)try{await x(n),e=await f()}catch(o){if(o instanceof g)await m();else throw o instanceof T?o:new T(`Token refresh failed unexpectedly (your saved login was NOT cleared \u2014 retry; if it persists, restart the borg session): ${o?.message??"unknown"}`)}if(!e)throw new Error(t?"Authentication expired \u2014 your saved login has expired. Run: borg setup":"Authentication required \u2014 you are not signed in. Run: borg setup")}return e}async function B(){const e=await h();if(!e)return null;try{return await x(e),await f()}catch(n){if(n instanceof g&&await m(),n instanceof T)throw n;return null}}async function Q(){if(await f())return"valid";const n=await h();if(!n)return"dead";try{return await x(n),await f()?"valid":"transient"}catch(t){return t instanceof g?(await m(),"dead"):"transient"}}async function r(e,n={}){let t=await q();const{droneSession:o,apiUrl:s,headers:a,...d}=n,$=s??L,y=(d.method??"GET").toUpperCase(),w=async c=>{const p={Authorization:`Bearer ${c}`,...a};o&&(p["X-Drone-Session"]=o),b(`\u2192 ${y} ${e}`);const u=await fetch(`${$}${e}`,{...d,headers:p});return b(`\u2190 ${u.status} ${y} ${e}`),u};let i=await w(t);if(i.status===401){const c=await B();c&&(t=c,i=await w(t))}if(i.status===401)throw new Error("Authentication required. Run: borg setup");if(i.status===429&&(i=await U(i,()=>w(t),{sleep:H,log:c=>console.error(`${S()}${c}`)})),!i.ok){const c=await i.text();b(`\u2717 ${i.status} ${y} ${e}: ${c}`);const p=M(c),u=I(c);if(i.status===410&&u===P)throw new C(p);if(i.status===423&&u===O)throw new k(p);if(i.status===429){const _=i.headers.get("Retry-After"),j=_?` (retry after ${_}s)`:"";throw new Error(`HTTP 429: rate limited${j}: ${p}`)}throw new Error(`HTTP ${i.status}: ${p}`)}return i}async function Y(e,n,t,o){const s={hostname:t??null};if((o==="claude"||o==="codex")&&(s.agent_kind=o),typeof e=="string")s.cube_name=e;else if(e.cube_id&&(s.cube_id=e.cube_id),e.cube_name&&(s.cube_name=e.cube_name),e.role_id&&(s.role_id=e.role_id),e.role_name&&(s.role_name=e.role_name),e.prior_drone_id&&(s.prior_drone_id=e.prior_drone_id),e.model!=null){if(!D.test(e.model))throw new Error(`Invalid model descriptor: "${e.model}" (expected "<claude|ollama>:<model>").`);s.model=e.model}return await(await r("/api/assimilate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s),apiUrl:n})).json()}async function K(e,n){return await(await r("/api/drone/cube",{method:"GET",droneSession:e,apiUrl:n})).json()}async function ee(e,n){return await(await r("/api/drone/role",{method:"GET",droneSession:e,apiUrl:n})).json()}async function te(e,n){return await(await r("/api/drone/whoami",{method:"GET",droneSession:e,apiUrl:n})).json()}async function ne(e,n,t){const o=t?`?since=${encodeURIComponent(t)}`:"";return await(await r(`/api/drone/roster${o}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function oe(e,n,t={}){const o=new URLSearchParams;t.since&&o.set("since",t.since),t.limit!==void 0&&o.set("limit",String(t.limit)),t.unreadOnly&&o.set("unread_only","true");const s=o.toString();return await(await r(`/api/drone/log${s?`?${s}`:""}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function se(e,n,t){await r(`/api/drone/log/${t}/ack`,{method:"POST",body:JSON.stringify({kind:
|
|
1
|
+
import{getIdToken as f,getRefreshToken as h,clearTokens as m}from"./config.js";import{refreshIdToken as R,RefreshTokenInvalidError as g,RefreshTransientError as T}from"./auth.js";import{consolePrefix as S}from"./console-prefix.js";import{debugLog as b}from"./debug.js";import{assertUuidShape as E}from"./evict-drone.js";import{DroneEvictedError as C,DroneFrozenError as k,DRONE_EVICTED_CODE as P,DRONE_FROZEN_CODE as O,errorCodeFromBody as I}from"./drone-lifecycle.js";import{MODEL_DESCRIPTOR_REGEX as D}from"./model-presets.js";const L=process.env.BORG_API_URL||"https://api.borgmcp.ai",A=3,v=6e4;let l=null;function x(e){return l||(l=R(e).finally(()=>{l=null}),l)}function G(e){if(e==null)return null;const n=e.trim();return/^\d+$/.test(n)?parseInt(n,10)*1e3:null}function J(e,n,t=v,o=()=>Math.random()*500){const s=e??1e3*(n+1);return Math.min(s,t)+o()}function M(e){const n=(t,o)=>`${t}${/[.:!?]$/.test(t)?"":":"} ${o}`;try{const t=JSON.parse(e);if(typeof t?.error=="string")return typeof t.details=="string"?n(t.error,t.details):t.error;if(t?.error&&typeof t.error=="object"){const o=t.error.message,s=t.error.details??t.details;if(typeof o=="string"&&typeof s=="string")return n(o,s);if(typeof o=="string")return o}if(typeof t?.message=="string"&&typeof t?.details=="string")return n(t.message,t.details);if(typeof t?.message=="string")return t.message}catch{}return e}async function U(e,n,t){const o=t.maxRetries??A;let s=e,a=0;for(;s.status===429&&a<o;){const d=J(G(s.headers.get("Retry-After")),a,t.capMs,t.jitter);t.log?.(`rate limited (429); retrying in ${Math.round(d)}ms (attempt ${a+1}/${o})`),await t.sleep(d),a++,s=await n()}return s}function H(e){return new Promise(n=>setTimeout(n,e))}async function q(){let e=await f();if(!e){const n=await h(),t=n!=null;if(n)try{await x(n),e=await f()}catch(o){if(o instanceof g)await m();else throw o instanceof T?o:new T(`Token refresh failed unexpectedly (your saved login was NOT cleared \u2014 retry; if it persists, restart the borg session): ${o?.message??"unknown"}`)}if(!e)throw new Error(t?"Authentication expired \u2014 your saved login has expired. Run: borg setup":"Authentication required \u2014 you are not signed in. Run: borg setup")}return e}async function B(){const e=await h();if(!e)return null;try{return await x(e),await f()}catch(n){if(n instanceof g&&await m(),n instanceof T)throw n;return null}}async function Q(){if(await f())return"valid";const n=await h();if(!n)return"dead";try{return await x(n),await f()?"valid":"transient"}catch(t){return t instanceof g?(await m(),"dead"):"transient"}}async function r(e,n={}){let t=await q();const{droneSession:o,apiUrl:s,headers:a,...d}=n,$=s??L,y=(d.method??"GET").toUpperCase(),w=async c=>{const p={Authorization:`Bearer ${c}`,...a};o&&(p["X-Drone-Session"]=o),b(`\u2192 ${y} ${e}`);const u=await fetch(`${$}${e}`,{...d,headers:p});return b(`\u2190 ${u.status} ${y} ${e}`),u};let i=await w(t);if(i.status===401){const c=await B();c&&(t=c,i=await w(t))}if(i.status===401)throw new Error("Authentication required. Run: borg setup");if(i.status===429&&(i=await U(i,()=>w(t),{sleep:H,log:c=>console.error(`${S()}${c}`)})),!i.ok){const c=await i.text();b(`\u2717 ${i.status} ${y} ${e}: ${c}`);const p=M(c),u=I(c);if(i.status===410&&u===P)throw new C(p);if(i.status===423&&u===O)throw new k(p);if(i.status===429){const _=i.headers.get("Retry-After"),j=_?` (retry after ${_}s)`:"";throw new Error(`HTTP 429: rate limited${j}: ${p}`)}throw new Error(`HTTP ${i.status}: ${p}`)}return i}async function Y(e,n,t,o){const s={hostname:t??null};if((o==="claude"||o==="codex")&&(s.agent_kind=o),typeof e=="string")s.cube_name=e;else if(e.cube_id&&(s.cube_id=e.cube_id),e.cube_name&&(s.cube_name=e.cube_name),e.role_id&&(s.role_id=e.role_id),e.role_name&&(s.role_name=e.role_name),e.prior_drone_id&&(s.prior_drone_id=e.prior_drone_id),e.model!=null){if(!D.test(e.model))throw new Error(`Invalid model descriptor: "${e.model}" (expected "<claude|ollama>:<model>").`);s.model=e.model}return await(await r("/api/assimilate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s),apiUrl:n})).json()}async function K(e,n){return await(await r("/api/drone/cube",{method:"GET",droneSession:e,apiUrl:n})).json()}async function ee(e,n){return await(await r("/api/drone/role",{method:"GET",droneSession:e,apiUrl:n})).json()}async function te(e,n){return await(await r("/api/drone/whoami",{method:"GET",droneSession:e,apiUrl:n})).json()}async function ne(e,n,t){const o=t?`?since=${encodeURIComponent(t)}`:"";return await(await r(`/api/drone/roster${o}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function oe(e,n,t={}){const o=new URLSearchParams;t.since&&o.set("since",t.since),t.limit!==void 0&&o.set("limit",String(t.limit)),t.unreadOnly&&o.set("unread_only","true");const s=o.toString();return await(await r(`/api/drone/log${s?`?${s}`:""}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function se(e,n,t,o="ack"){await r(`/api/drone/log/${t}/ack`,{method:"POST",body:JSON.stringify({kind:o}),droneSession:e,apiUrl:n})}async function re(e,n,t={}){const o=new URLSearchParams;t.since&&o.set("since",t.since);const s=o.toString();return await(await r(`/api/drone/regen${s?`?${s}`:""}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function ae(e,n,t,o){const s=new URLSearchParams({role:t,section:o});return await(await r(`/api/drone/role-rationale?${s.toString()}`,{method:"GET",droneSession:e,apiUrl:n})).json()}async function ie(e,n,t,o={}){const s={message:t,...o.visibility?{visibility:o.visibility}:{},...o.recipientDroneIds?{recipientDroneIds:o.recipientDroneIds}:{},...o.class?{class:o.class}:{},...o.to?{to:o.to}:{}};return await(await r("/api/drone/log",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:n,body:JSON.stringify(s)})).json()}async function ce(e,n,t){const o={kind:t.kind??"friction",message:t.message,...t.metadata?{metadata:t.metadata}:{}};return await(await r("/api/drone/report",{method:"POST",headers:{"Content-Type":"application/json"},droneSession:e,apiUrl:n,body:JSON.stringify(o)})).json()}async function pe(){return await(await r("/api/cubes",{method:"GET"})).json()}async function de(){return await(await r("/api/templates",{method:"GET"})).json()}async function ue(e,n,t){const o={cube_directive:n};e&&(o.name=e),t?.template&&(o.template=t.template),t&&Object.prototype.hasOwnProperty.call(t,"message_taxonomy")&&(o.message_taxonomy=t.message_taxonomy??null);const a=await(await r("/api/cubes",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})).json();return a.cube?{...a.cube,roles:a.roles??[],drones:a.drones??[]}:a}async function fe(e,n){return await(await r(`/api/cubes/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).json()}async function le(e,n){return await(await r(`/api/cubes/${e}/taxonomy-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).json()}async function ye(e){await r(`/api/cubes/${e}`,{method:"DELETE"})}async function we(e,n){return await(await r(`/api/cubes/${e}/roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).json()}async function he(e,n){return await(await r(`/api/roles/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).json()}async function me(e,n){return await(await r(`/api/roles/${e}/section-patch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).json()}async function ge(e){await r(`/api/roles/${e}`,{method:"DELETE"})}async function Te(e,n){return E(e,"drone_id"),await(await r(`/api/drones/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({role_id:n})})).json()}async function be(e){E(e,"drone_id"),await r(`/api/drones/${e}`,{method:"DELETE"})}async function xe(e){const t=await(await r(`/api/cubes/${e}`,{method:"GET"})).json();return t.cube?{...t.cube,roles:t.roles??[],drones:t.drones??[]}:t}async function _e(e,n){return await(await r(`/api/cubes/${e}/apply-template`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:n})})).json()}async function Ee(){return await(await r("/api/subscription/status",{method:"GET"})).json()}async function $e(e,n="software-dev",t=!1,o){return await(await r(`/api/cubes/${e}/sync-roles`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({template_name:n,apply:t,...o?{decisions:o}:{}})})).json()}async function je(){const n=await(await r("/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!n.checkout_url)throw new Error("No checkout URL in response");return n.checkout_url}async function Re(){const n=await(await r("/api/subscription/portal",{method:"POST",headers:{"Content-Type":"application/json"}})).json();if(!n.portal_url)throw new Error(n.message||"No portal URL in response");return n.portal_url}export{L as API_URL,se as ackLogEntry,ie as appendLog,_e as applyTemplate,Y as assimilate,Ee as checkSubscriptionStatus,Re as createBillingPortalSession,ue as createCube,we as createRole,je as createSubscription,ye as deleteCube,ge as deleteRole,be as evictDrone,M as extractHttpErrorMessage,xe as getCube,K as getCubeInfo,ee as getRoleInfo,ne as getRoster,q as getValidToken,pe as listCubes,de as listTemplates,G as parseRetryAfterMs,me as patchRoleSection,le as patchTaxonomyClass,Q as probeSession,J as rateLimitWaitMs,oe as readLog,Te as reassignDrone,re as regen,U as retryOn429,ae as roleRationale,ce as submitReport,$e as syncRoles,fe as updateCube,he as updateRole,te as whoami};
|
package/dist/setup-confirm.d.ts
CHANGED
|
@@ -70,4 +70,35 @@ export declare function confirmConfigMutation(deps: ConfirmConfigMutationDeps):
|
|
|
70
70
|
* --no-browser/--device scan in setup.ts).
|
|
71
71
|
*/
|
|
72
72
|
export declare function parseYesFlag(argv: string[]): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* gh#844 — whether Step-1 has ANY config mutation worth disclosing.
|
|
75
|
+
*
|
|
76
|
+
* The disclosure + confirm prompt exists to obtain informed consent for the
|
|
77
|
+
* config writes Step-1 performs (gh#818). On a pure refresh — the normal
|
|
78
|
+
* OAuth-refresh re-run where every DETECTED agent CLI already has the full
|
|
79
|
+
* borg setup (MCP server registered AND every hook write already applied) —
|
|
80
|
+
* there is no mutation to consent to, so the prompt is redundant and skipped.
|
|
81
|
+
*
|
|
82
|
+
* CRITICAL (gh#844 SR finding 8d9c732e): the gate must cover the FULL disclosed
|
|
83
|
+
* mutation set — MCP registration AND every hook write (claude UserPromptSubmit
|
|
84
|
+
* + the legacy SessionStart removal; codex SessionStart + UserPromptSubmit) —
|
|
85
|
+
* not MCP registration alone. Otherwise an MCP-configured user with a pending
|
|
86
|
+
* hook write (e.g. a pre-gh#673 upgrader whose legacy global hook must be
|
|
87
|
+
* removed) would have settings.json mutated with consent silently skipped.
|
|
88
|
+
* Caller derives `claudeHookPending`/`codexHookPending` from the SAME peeks
|
|
89
|
+
* that gate the individual writers, so the gate and the writers cannot drift.
|
|
90
|
+
*
|
|
91
|
+
* Scoped to DETECTED CLIs: a claude-only user is never gated on codex config
|
|
92
|
+
* state (and vice-versa). A naive `!isMcpServerConfigured() ||
|
|
93
|
+
* !isCodexMcpServerConfigured()` gate would mis-fire for single-CLI users —
|
|
94
|
+
* the absent CLI's config is unconfigured, so the OR would always be true.
|
|
95
|
+
*/
|
|
96
|
+
export declare function setupMutationPending(deps: {
|
|
97
|
+
claude: boolean;
|
|
98
|
+
codex: boolean;
|
|
99
|
+
claudeMcpConfigured: boolean;
|
|
100
|
+
codexMcpConfigured: boolean;
|
|
101
|
+
claudeHookPending: boolean;
|
|
102
|
+
codexHookPending: boolean;
|
|
103
|
+
}): boolean;
|
|
73
104
|
//# sourceMappingURL=setup-confirm.d.ts.map
|
package/dist/setup-confirm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
2
|
-
`)}async function
|
|
1
|
+
function r(e){const o=[];return e.claude&&(o.push({file:"~/.claude.json",change:"registers the borg MCP server"}),o.push({file:"~/.claude/settings.json",change:"adds a UserPromptSubmit hook"})),e.codex&&(o.push({file:"~/.codex/config.toml",change:"registers the borg MCP server"}),o.push({file:"~/.codex/hooks.json",change:"adds SessionStart + UserPromptSubmit hooks"})),o}function t(e){const o=[];o.push("borg setup will register the borg MCP server in your agent config:");for(const n of e)o.push(` \u2022 ${n.file} (${n.change})`);return o.push('These changes are additive and reversible \u2014 remove the "borg" entries to undo.'),o.join(`
|
|
2
|
+
`)}async function i(e){return!e.isTTY||e.yes||await e.confirm()?"proceed":"abort"}function s(e){return e.includes("--yes")||e.includes("-y")}function c(e){return e.claude&&(!e.claudeMcpConfigured||e.claudeHookPending)||e.codex&&(!e.codexMcpConfigured||e.codexHookPending)}export{r as configMutationTargets,i as confirmConfigMutation,t as formatConfigMutationDisclosure,s as parseYesFlag,c as setupMutationPending};
|
package/dist/setup.js
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
\u25FC Borg MCP Setup Wizard \u25FC`));const c=process.argv.includes("--no-browser")||process.argv.includes("--device");let t=null,n=null;try{t=
|
|
2
|
+
import k from"prompts";import e from"chalk";import w from"open";import C from"which";import{authenticateWithGoogle as F}from"./auth.js";import{checkSubscriptionStatus as i,createSubscription as P,probeSession as $}from"./remote-client.js";import{setupActionForSession as A}from"./setup-action.js";import{confirmConfigMutation as H,configMutationTargets as M,formatConfigMutationDisclosure as R,parseYesFlag as U,setupMutationPending as T}from"./setup-confirm.js";import{retrySubscriptionCheck as S}from"./subscription-retry.js";import{addMcpServer as D,addUserPromptSubmitHook as q,addCodexMcpServer as I,addCodexSessionStartHook as L,addCodexUserPromptSubmitHook as Y,isMcpServerConfigured as N,isCodexMcpServerConfigured as W,isSessionStartHookRegistered as j,isUserPromptSubmitHookRegistered as B,isCodexSessionStartHookRegistered as E,isCodexUserPromptSubmitHookRegistered as G,removeSessionStartHook as O}from"./config-utils.js";import{handleVersionFlag as z}from"./version.js";import{initDebugFromArgv as Q}from"./debug.js";async function V(){Q(process.argv),z(),console.log(e.blue.bold(`
|
|
3
|
+
\u25FC Borg MCP Setup Wizard \u25FC`));const c=process.argv.includes("--no-browser")||process.argv.includes("--device");let t=null,n=null;try{t=C.sync("claude")}catch{}try{n=C.sync("codex")}catch{}t&&console.log(e.gray(`Found Claude CLI: ${t}`)),n&&console.log(e.gray(`Found Codex CLI: ${n}`)),(t||n)&&console.log(""),!t&&!n&&(console.error(e.red(`\u25FC No supported agent CLI found
|
|
4
4
|
`)),console.error(e.yellow("Please install Claude Code or Codex first:")),console.error(e.gray(" Claude Code: https://claude.ai/download")),console.error(e.gray(` Codex: https://developers.openai.com/codex
|
|
5
|
-
`)),process.exit(1)),console.log(e.blue("\u25FC Agent CLI Integration"));const
|
|
5
|
+
`)),process.exit(1)),console.log(e.blue("\u25FC Agent CLI Integration"));const v=U(process.argv),a=t!==null,l=n!==null,d=N(),p=W(),h=a&&j(),m=a&&!B(),b=l&&!E(),f=l&&!G();if(T({claude:a,codex:l,claudeMcpConfigured:d,codexMcpConfigured:p,claudeHookPending:h||m,codexHookPending:b||f})&&(console.log(R(M({claude:a,codex:l}))),await H({isTTY:process.stdin.isTTY===!0,yes:v,confirm:async()=>{const{proceed:o}=await k({type:"confirm",name:"proceed",message:"Continue with these changes?",initial:!0});return o===!0}})==="abort"&&(console.log(e.yellow(`
|
|
6
6
|
\u25FC Setup cancelled \u2014 no changes made.
|
|
7
|
-
`)),process.exit(0)),console.log(""),t)try{
|
|
8
|
-
\u25FC Failed to configure Claude Code: ${
|
|
9
|
-
`)),process.exit(1)}if(n)try{
|
|
10
|
-
\u25FC Failed to configure Codex: ${
|
|
11
|
-
`)),process.exit(1)}console.log(""),console.log(e.blue("\u25FC Google Authentication"));const
|
|
12
|
-
`));else if(
|
|
13
|
-
\u25FC Could not reach Google to verify your session (network issue).`)),console.error(e.yellow("Re-run `borg setup` when your connection is back.\n")),process.exit(1);else try{await
|
|
14
|
-
\u25FC Authentication failed: ${
|
|
15
|
-
`)),console.error(e.yellow("Re-run `borg setup` to try again.\n")),process.exit(1)}console.log(e.blue("\u25FC Subscription Check"));let s;try{s=await i()}catch(
|
|
16
|
-
\u25FC Subscription check failed: ${
|
|
17
|
-
`)),s={hasAccess:!1}}if(s=await
|
|
7
|
+
`)),process.exit(0))),console.log(""),t)try{d||D(),h&&O(),m&&q(),console.log(e.green("\u25FC borg configured for Claude Code"))}catch(r){console.error(e.red(`
|
|
8
|
+
\u25FC Failed to configure Claude Code: ${r.message}
|
|
9
|
+
`)),process.exit(1)}if(n)try{p||I(),b&&L(),f&&Y(),console.log(e.green("\u25FC borg configured for Codex"))}catch(r){console.error(e.red(`
|
|
10
|
+
\u25FC Failed to configure Codex: ${r.message}
|
|
11
|
+
`)),process.exit(1)}console.log(""),console.log(e.blue("\u25FC Google Authentication"));const y=A(await $());if(y==="skip")console.log(e.green(`\u25FC Already signed in
|
|
12
|
+
`));else if(y==="retry")console.error(e.yellow(`
|
|
13
|
+
\u25FC Could not reach Google to verify your session (network issue).`)),console.error(e.yellow("Re-run `borg setup` when your connection is back.\n")),process.exit(1);else try{await F(c?{noBrowser:!0}:void 0)}catch(r){console.error(e.red(`
|
|
14
|
+
\u25FC Authentication failed: ${r.message}
|
|
15
|
+
`)),console.error(e.yellow("Re-run `borg setup` to try again.\n")),process.exit(1)}console.log(e.blue("\u25FC Subscription Check"));let s;try{s=await i()}catch(r){console.error(e.yellow(`
|
|
16
|
+
\u25FC Subscription check failed: ${r.message}`)),console.error(e.gray(`\u25FC Retrying before falling back to the Free tier...
|
|
17
|
+
`)),s={hasAccess:!1}}if(s=await S(s,{check:i,sleep:r=>new Promise(o=>setTimeout(o,r)),onRetry:(r,o)=>console.log(e.gray(`\u25FC Checking subscription... (attempt ${r}/${o})`))}),s.hasAccess)if(console.log(e.green("\u25FC Active subscription found")),s.expiresAt){const r=new Date(s.expiresAt);console.log(e.gray(` Expires: ${r.toLocaleDateString()}
|
|
18
18
|
`))}else console.log("");else{console.log(e.green("\u25FC You're on the Free tier \u2014 permanent, no card needed: 1 cube + 3 agent sessions + 100 req/hr.")),console.log(e.gray(`\u25FC Start using borgmcp right now. Upgrade any time: $1/month per cube, each cube adds 8 pooled agent sessions + 1000 req/hr.
|
|
19
|
-
`));const{subscribeMethod:
|
|
19
|
+
`));const{subscribeMethod:r}=await k({type:"select",name:"subscribeMethod",message:"You're ready on the Free tier. Want to do more?",choices:[{title:"\u25FC Continue on the Free tier (recommended)",value:"skip",description:"Start now \u2014 1 cube, 3 agent sessions, 100 req/hr. No payment required."},{title:"\u25FC Upgrade to Cube tier \u2014 $1/month per cube",value:"web",description:"Each cube adds 8 pooled agent sessions + 1000 req/hr. Opens the subscribe page in your browser."},{title:"\u25FC Quick Stripe checkout",value:"stripe",description:"Fast upgrade checkout in the browser"},{title:"\u25FC I already subscribed \u2014 re-check",value:"recheck",description:"Re-check now \u2014 a just-completed subscription can take a moment to activate"}]});switch(r===void 0&&console.log(e.yellow(`
|
|
20
20
|
\u25FC No subscription option selected \u2014 continuing on the Free tier.
|
|
21
|
-
`)),
|
|
22
|
-
\u25FC Opening: https://borgmcp.ai/subscribe`));try{await
|
|
23
|
-
`)),await
|
|
24
|
-
\u25FC ${
|
|
25
|
-
`))}break;case"stripe":try{const
|
|
26
|
-
\u25FC Opening Stripe: ${
|
|
27
|
-
`)),await
|
|
28
|
-
\u25FC Failed to create checkout: ${
|
|
21
|
+
`)),r){case"web":console.log(e.blue(`
|
|
22
|
+
\u25FC Opening: https://borgmcp.ai/subscribe`));try{await w("https://borgmcp.ai/subscribe"),console.log(e.gray(`\u25FC Waiting for subscription (checking every 5s for 2 min)...
|
|
23
|
+
`)),await x()}catch(o){console.error(e.yellow(`
|
|
24
|
+
\u25FC ${o.message}`)),console.log(e.green(`\u25FC Continuing on the Free tier. Upgrade any time from https://borgmcp.ai/subscribe.
|
|
25
|
+
`))}break;case"stripe":try{const o=await P();console.log(e.blue(`
|
|
26
|
+
\u25FC Opening Stripe: ${o}`)),await w(o),console.log(e.gray(`\u25FC Waiting for subscription...
|
|
27
|
+
`)),await x()}catch(o){console.error(e.red(`
|
|
28
|
+
\u25FC Failed to create checkout: ${o.message}
|
|
29
29
|
`)),console.log(e.green(`\u25FC Continuing on the Free tier. Upgrade any time from https://borgmcp.ai/subscribe.
|
|
30
|
-
`))}break;case"recheck":try{let
|
|
30
|
+
`))}break;case"recheck":try{let o;try{o=await i()}catch{o={hasAccess:!1}}o=await S(o,{check:i,sleep:g=>new Promise(u=>setTimeout(u,g)),onRetry:(g,u)=>console.log(e.gray(`\u25FC Re-checking subscription... (attempt ${g}/${u})`))}),o.hasAccess?console.log(e.green(`
|
|
31
31
|
\u25FC Subscription found!
|
|
32
32
|
`)):console.log(e.yellow(`
|
|
33
33
|
\u25FC No subscription found \u2014 continuing on the Free tier.
|
|
34
|
-
`))}catch(
|
|
35
|
-
\u25FC Failed to recheck: ${
|
|
34
|
+
`))}catch(o){console.error(e.red(`
|
|
35
|
+
\u25FC Failed to recheck: ${o.message}
|
|
36
36
|
`)),console.log(e.green(`\u25FC Continuing on the Free tier.
|
|
37
37
|
`))}break;case"skip":console.log(e.green(`
|
|
38
38
|
\u25FC You're all set on the Free tier: 1 cube, 3 agent sessions, 100 req/hr.
|
|
39
39
|
`));break}}console.log(e.green.bold(`Setup complete!
|
|
40
40
|
`)),console.log(e.yellow(`\u{1F504} Restart Claude Code/Codex (or open a new session) for the changes to take effect.
|
|
41
41
|
`)),console.log(e.gray("\u25FC Next steps:")),console.log(e.gray('1. cd into your project, then run "borg assimilate" to join a cube')),console.log(e.gray(" (this creates/joins the cube and launches your agent)")),console.log(e.gray(`2. Manage cubes and subscription at https://borgmcp.ai/dashboard
|
|
42
|
-
`))}async function
|
|
43
|
-
`));return}}catch{}}throw new Error("Timeout - Run setup again after subscribing")}
|
|
42
|
+
`))}async function x(){for(let t=0;t<24;t++){await new Promise(n=>setTimeout(n,5e3));try{if((await i()).hasAccess){console.log(e.green(`\u25FC Subscription activated!
|
|
43
|
+
`));return}}catch{}}throw new Error("Timeout - Run setup again after subscribing")}V().catch(c=>{console.error(e.red(`
|
|
44
44
|
\u25FC Setup failed: ${c.message}
|
|
45
45
|
`)),process.exit(1)});
|
package/dist/templates.js
CHANGED
|
@@ -164,6 +164,7 @@ Your job:
|
|
|
164
164
|
- Read the activity log on every regen. Decide what work is pending, what's stalled, what's done.
|
|
165
165
|
- When a new drone connects, look at pending log signals and assign it to the right role using \`borg_reassign-drone\`. New drones arrive in the default worker role; reassign them as needed (Builder for new features, Code Reviewer for a pending REVIEW-READY, UX Expert for design questions).
|
|
166
166
|
- **Merge approved branches to the primary branch, run production deploys, and initiate releases.** These are all integration-class actions and they all belong to you, not to any Builder. When you see \`REVIEW-APPROVED: <branch>\` (and \`QA-PASS:\` where applicable), fetch, merge, push, and log a \`DONE: merged <branch>\` entry. When the Queen authorizes a production deploy or a release, you run the command yourself from your terminal (you're on the Queen's machine where the credentials live) \u2014 you do NOT dispatch deploy/release commands to Builders, who lack the operator-level auth. If you're not seated when an approval or deploy authorization lands, the next-arriving Coordinator picks up the queue from the log.
|
|
167
|
+
- **Let reviewers self-claim their gates \u2014 don't pre-assign a canonical reviewer per branch.** Reviewers \`borg_ack ... kind=claim\` a \`REVIEW-READY:\` before starting, so a gate has a visible owner and peers skip the double-review without you splitting branch-sets by hand. Intervene only when a gate is **unclaimed past the SLA** (assign it explicitly to a named reviewer) or a **claim has gone stale** (the claimant went silent past the wake-path SLA \u2014 reassign the gate or re-open it). A claim is advisory ownership only; merge eligibility stays keyed on \`REVIEW-APPROVED\`, never on a claim.
|
|
167
168
|
- **Communicate clearly with the Queen.** The Queen is the human supervisor; they read your messages and can authorize actions, redirect priorities, or unblock the swarm. Clarity rules:
|
|
168
169
|
- **CRITICAL: present plans, decisions, and asks to the human Queen in plain conversation text \u2014 NOT only in the cube log.** The human Queen does NOT read the cube log directly. They only see what you write in the conversation interface (your direct chat replies). Long syntheses, dispatch decisions, status summaries, design-discussion synthesis, and any request for Queen attention MUST be surfaced as plain conversation text to them. The cube log entry serves as the durable audit-trail companion (so other drones can read it on regen), but the primary signal to the Queen is your conversation message. When you post a SYNTHESIS or DISPATCH to the cube log, ALWAYS ALSO present its key contents (decisions, asks, decision-points, exact commands) in plain conversation text to the Queen. Assume the Queen sees ONLY your direct conversation responses \u2014 never the cube log entries \u2014 unless they explicitly say otherwise.
|
|
169
170
|
- **Lead with the ask, not the context.** If you need authorization or a decision, put the ask in the first line. Context comes after. Don't bury the question.
|
|
@@ -216,7 +217,7 @@ Project conventions:
|
|
|
216
217
|
- Tests must pass and any build-verification or deploy-dry-run gate the cube specifies must succeed before claiming DONE.${r}${i}${o}${l}${u}${e}`},{name:"Code Reviewer",can_broadcast:!0,short_description:"Reviews completed branches before merge. Verifies correctness + implementation quality + readability. Posts REVIEW-FEEDBACK or REVIEW-APPROVED.",detailed_description:`You review branches that Builders mark \`REVIEW-READY:\`. Autonomous \u2014 coordinate through the log.
|
|
217
218
|
|
|
218
219
|
Workflow:
|
|
219
|
-
- On regen, scan the log for unanswered \`REVIEW-READY:\` signals. Pick the oldest
|
|
220
|
+
- On regen, scan the log for unanswered \`REVIEW-READY:\` signals. Pick the oldest one whose claim is free or stale and **claim it before reviewing**: \`borg_ack entry_id=<id> kind=claim\` announces you are taking the gate so a peer reviewer skips the double-review. If a live peer already holds the claim, skip that one and pick another; if the claim is STALE (the claimant went silent past the wake-path SLA), re-claim and proceed. The claim is ADVISORY \u2014 it kills the double-review race without a hard lock; merge eligibility stays keyed on \`REVIEW-APPROVED\`, NEVER on a claim. Then post \`STARTING: review of <branch>\` and pull the diff.
|
|
220
221
|
- **Before reviewing, sync your local checkout.** \`git fetch origin <branch>\` \u2192 \`git checkout <branch>\` \u2192 \`git pull --ff-only\`. Verify \`git rev-parse HEAD\` matches the merge-base SHA the Builder quoted in their REVIEW-READY post. The merge-base in their post tells you which base branch the work derives from \u2014 match that, don't assume. Reviewing stale code is the canonical "I reviewed an old version" failure class.
|
|
221
222
|
- Verify correctness: does the code do what the commit message claims? Tests pass? Bundle size acceptable? Follows project conventions?
|
|
222
223
|
- **Verify implementation quality + suggest refactors when appropriate.** Beyond "does it work," ask: is the code clean and readable? Specific things to call out \u2014 duplicated logic that could share a helper, dense or clever code that hides intent, unclear naming, missing abstractions for repeated non-trivial patterns, complex conditionals that would flatten, magic numbers, overly long functions, dead code, inconsistent in-file style. **Balance against "don't over-engineer"**: per the project's standing rule, three similar lines is better than a premature abstraction. Refactors should reduce real complexity, not add layers for hypothetical future cases. Refactor suggestions are typically NIT-class \u2014 post as \`REVIEW-FEEDBACK (nit: suggested refactor): <branch> <observation>\` and DO NOT block \`REVIEW-APPROVED\` on them; block only on patterns that compound technical debt or harm readability materially. Bigger refactors needing their own scope \u2192 file as a deferred-work issue rather than expanding the PR.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "borgmcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
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",
|