borgmcp 0.6.1 → 0.6.2

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/claude.d.ts CHANGED
@@ -8,8 +8,10 @@
8
8
  * "look at the log and act" directive never executes.
9
9
  *
10
10
  * Commands:
11
- * borg → Launch Claude with kickoff prompt
12
- * borg setup → Re-route to the setup wizard
11
+ * borg → Launch Claude with kickoff prompt
12
+ * borg setup → Re-route to the setup wizard
13
+ * borg spawn <name> → Create a sibling git worktree + launch a
14
+ * fresh drone inside it (see spawn.ts)
13
15
  */
14
16
  export {};
15
17
  //# sourceMappingURL=claude.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
package/dist/claude.js CHANGED
@@ -8,13 +8,18 @@
8
8
  * "look at the log and act" directive never executes.
9
9
  *
10
10
  * Commands:
11
- * borg → Launch Claude with kickoff prompt
12
- * borg setup → Re-route to the setup wizard
11
+ * borg → Launch Claude with kickoff prompt
12
+ * borg setup → Re-route to the setup wizard
13
+ * borg spawn <name> → Create a sibling git worktree + launch a
14
+ * fresh drone inside it (see spawn.ts)
13
15
  */
14
16
  import { spawn } from 'child_process';
17
+ import { basename } from 'node:path';
15
18
  import chalk from 'chalk';
16
19
  import { getActiveCube, inboxPathForDrone } from './cubes.js';
17
20
  import { handleVersionFlag } from './version.js';
21
+ import { parseSpawnArgs, runSpawn } from './spawn.js';
22
+ import { setTerminalTitle } from './terminal-title.js';
18
23
  async function main() {
19
24
  // Honor `--version` / `-v` before any other work.
20
25
  handleVersionFlag();
@@ -23,6 +28,15 @@ async function main() {
23
28
  await import('./setup.js');
24
29
  return;
25
30
  }
31
+ if (process.argv[2] === 'spawn') {
32
+ const parsed = parseSpawnArgs(process.argv.slice(3));
33
+ if (!parsed.ok) {
34
+ process.stderr.write(chalk.red(`◼ borg spawn: ${parsed.error}\n`));
35
+ process.exit(1);
36
+ }
37
+ const code = await runSpawn(parsed.args);
38
+ process.exit(code);
39
+ }
26
40
  // Forward any user-supplied flags (e.g. --resume <id>, --cwd, etc.) to
27
41
  // claude unchanged, except for --autonomous which we intercept and
28
42
  // translate into BORG_MODE=autonomous on the spawned env.
@@ -47,6 +61,11 @@ async function main() {
47
61
  // no signal. The user can assimilate and relaunch to engage real-time
48
62
  // wake; the /loop heartbeat alone covers the meantime.
49
63
  const active = await getActiveCube();
64
+ // Set the terminal title so sibling drone sessions are
65
+ // distinguishable in Cmd-Tab / tab bars / Mission Control. No-op
66
+ // when stdout isn't a TTY (piped invocation, CI). Claude Code does
67
+ // not set its own title, so this persists for the session.
68
+ setTerminalTitle(active ? { label: active.droneLabel, cubeName: active.name } : null, basename(process.cwd()));
50
69
  const monitorClause = active
51
70
  ? `If you haven't yet, arm a persistent Monitor that watches the file ${inboxPathForDrone(active.cubeId, active.droneId)} ` +
52
71
  `for new lines so you wake immediately when other drones post to the cube. `
@@ -1 +1 @@
1
- {"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * `borg spawn` — create a new git worktree for a sibling drone and
3
+ * (by default) launch a fresh Claude Code session inside it.
4
+ *
5
+ * Why this exists: the cube's multi-drone pattern wants each drone to
6
+ * have its own worktree so parallel branches don't step on each other,
7
+ * but the manual ceremony (`git worktree add ../foo-name`, then
8
+ * `cd ../foo-name && borg`) is enough friction that drones drift back
9
+ * to working in one worktree and serializing. The subcommand collapses
10
+ * that to `borg spawn <name>`.
11
+ *
12
+ * Layout:
13
+ * Given the current worktree's primary at `/path/to/borg-mcp`, a
14
+ * `borg spawn builder` call creates `/path/to/borg-mcp-builder` as
15
+ * a sibling directory and runs `git worktree add` to register it.
16
+ * Default checkout is detached HEAD at the current HEAD so the new
17
+ * drone starts on a clean reference point without competing with
18
+ * the source worktree's branch state.
19
+ *
20
+ * Flags:
21
+ * --no-launch Create the worktree but don't run `borg` inside.
22
+ * Useful for setting up a tree to come back to later.
23
+ * --branch <ref> Check out a specific branch instead of detached
24
+ * HEAD. If <ref> is already checked out in another
25
+ * worktree, git refuses and we surface that error.
26
+ *
27
+ * Edge cases:
28
+ * - Not inside a git repo → friendly error, exit 1
29
+ * - Target path already exists → friendly error, exit 1
30
+ * - Empty name / name with slashes → format-hint error, exit 1
31
+ * - git worktree add fails → surface git's stderr verbatim, exit 1
32
+ */
33
+ export interface SpawnArgs {
34
+ name: string;
35
+ noLaunch: boolean;
36
+ branch: string | null;
37
+ }
38
+ export type ParseResult = {
39
+ ok: true;
40
+ args: SpawnArgs;
41
+ } | {
42
+ ok: false;
43
+ error: string;
44
+ };
45
+ /**
46
+ * Parse `argv.slice(2)` (i.e. arguments to `borg`, with `spawn` already
47
+ * stripped) into a structured SpawnArgs or a friendly error.
48
+ *
49
+ * Supported shapes:
50
+ * borg spawn <name>
51
+ * borg spawn <name> --no-launch
52
+ * borg spawn <name> --branch <ref>
53
+ * borg spawn <name> --branch <ref> --no-launch
54
+ * (flag order doesn't matter; positional `<name>` may appear before
55
+ * or after the flags)
56
+ *
57
+ * Unknown flags and missing/duplicate positional name are caller-
58
+ * friendly errors with format hints.
59
+ */
60
+ export declare function parseSpawnArgs(rawArgs: string[]): ParseResult;
61
+ export declare function validateName(name: string): {
62
+ ok: true;
63
+ } | {
64
+ ok: false;
65
+ error: string;
66
+ };
67
+ /**
68
+ * Given the absolute path to the primary worktree and a validated
69
+ * spawn name, return the absolute path of the new sibling worktree.
70
+ *
71
+ * Naming convention: `<sibling-parent>/<primary-basename>-<name>`.
72
+ * Example: primary `/home/x/borg-mcp` + name `builder` →
73
+ * `/home/x/borg-mcp-builder`. Keeps related worktrees alphabetically
74
+ * adjacent in `ls`, which matters when a drone-operator has a dozen
75
+ * sibling trees.
76
+ *
77
+ * Pure. Tested directly.
78
+ */
79
+ export declare function computeWorktreePath(primaryWorktreeAbs: string, name: string): string;
80
+ export interface SpawnDeps {
81
+ /** Synchronous spawn — used for `git ...` invocations. */
82
+ runSync?: (cmd: string, args: string[], cwd?: string) => SpawnSyncResult;
83
+ /** Path existence — used to bail on collisions. */
84
+ pathExists?: (p: string) => boolean;
85
+ /** Async spawn — used to launch the in-worktree `borg`. */
86
+ runDetached?: (cmd: string, args: string[], cwd: string) => Promise<number>;
87
+ /** Inject for tests; defaults to `process.cwd()`. */
88
+ cwd?: () => string;
89
+ /** stderr sink — overridable for tests. */
90
+ stderr?: (line: string) => void;
91
+ }
92
+ export interface SpawnSyncResult {
93
+ status: number | null;
94
+ stdout: string;
95
+ stderr: string;
96
+ }
97
+ /**
98
+ * Run the `borg spawn` flow against the given args. Returns the
99
+ * desired process exit code. Pure side-effects flow through `deps`
100
+ * so tests can inject mocks for every external interaction.
101
+ *
102
+ * Flow:
103
+ * 1. Validate name
104
+ * 2. Resolve primary worktree path via `git rev-parse --show-toplevel`
105
+ * (run from the operator's cwd — handles nested worktrees by
106
+ * asking git to find the top, which always points at the current
107
+ * worktree's root; for our naming convention we want the
108
+ * `--git-common-dir` parent which IS the primary worktree's
109
+ * filesystem dir, NOT the current worktree's. Resolve below.)
110
+ * 3. Compute target path; abort if exists
111
+ * 4. Run `git worktree add <target> [<ref>|--detach HEAD]`
112
+ * 5. Print stderr nudge
113
+ * 6. If !noLaunch, exec `borg` inside the new worktree
114
+ */
115
+ export declare function runSpawn(args: SpawnArgs, deps?: SpawnDeps): Promise<number>;
116
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAWH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,CA0C7D;AAkBD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAatF;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIpF;AAMD,MAAM,WAAW,SAAS;IACxB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,eAAe,CAAC;IACzE,mDAAmD;IACnD,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACpC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5E,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,SAAS,EACf,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,MAAM,CAAC,CAwGjB"}
package/dist/spawn.js ADDED
@@ -0,0 +1,273 @@
1
+ /**
2
+ * `borg spawn` — create a new git worktree for a sibling drone and
3
+ * (by default) launch a fresh Claude Code session inside it.
4
+ *
5
+ * Why this exists: the cube's multi-drone pattern wants each drone to
6
+ * have its own worktree so parallel branches don't step on each other,
7
+ * but the manual ceremony (`git worktree add ../foo-name`, then
8
+ * `cd ../foo-name && borg`) is enough friction that drones drift back
9
+ * to working in one worktree and serializing. The subcommand collapses
10
+ * that to `borg spawn <name>`.
11
+ *
12
+ * Layout:
13
+ * Given the current worktree's primary at `/path/to/borg-mcp`, a
14
+ * `borg spawn builder` call creates `/path/to/borg-mcp-builder` as
15
+ * a sibling directory and runs `git worktree add` to register it.
16
+ * Default checkout is detached HEAD at the current HEAD so the new
17
+ * drone starts on a clean reference point without competing with
18
+ * the source worktree's branch state.
19
+ *
20
+ * Flags:
21
+ * --no-launch Create the worktree but don't run `borg` inside.
22
+ * Useful for setting up a tree to come back to later.
23
+ * --branch <ref> Check out a specific branch instead of detached
24
+ * HEAD. If <ref> is already checked out in another
25
+ * worktree, git refuses and we surface that error.
26
+ *
27
+ * Edge cases:
28
+ * - Not inside a git repo → friendly error, exit 1
29
+ * - Target path already exists → friendly error, exit 1
30
+ * - Empty name / name with slashes → format-hint error, exit 1
31
+ * - git worktree add fails → surface git's stderr verbatim, exit 1
32
+ */
33
+ import { spawnSync, spawn } from 'node:child_process';
34
+ import { existsSync } from 'node:fs';
35
+ import { basename, dirname, join, resolve } from 'node:path';
36
+ import chalk from 'chalk';
37
+ /**
38
+ * Parse `argv.slice(2)` (i.e. arguments to `borg`, with `spawn` already
39
+ * stripped) into a structured SpawnArgs or a friendly error.
40
+ *
41
+ * Supported shapes:
42
+ * borg spawn <name>
43
+ * borg spawn <name> --no-launch
44
+ * borg spawn <name> --branch <ref>
45
+ * borg spawn <name> --branch <ref> --no-launch
46
+ * (flag order doesn't matter; positional `<name>` may appear before
47
+ * or after the flags)
48
+ *
49
+ * Unknown flags and missing/duplicate positional name are caller-
50
+ * friendly errors with format hints.
51
+ */
52
+ export function parseSpawnArgs(rawArgs) {
53
+ let name = null;
54
+ let noLaunch = false;
55
+ let branch = null;
56
+ for (let i = 0; i < rawArgs.length; i += 1) {
57
+ const arg = rawArgs[i];
58
+ if (arg === '--no-launch') {
59
+ noLaunch = true;
60
+ }
61
+ else if (arg === '--branch') {
62
+ const next = rawArgs[i + 1];
63
+ if (typeof next !== 'string' || next.length === 0) {
64
+ return {
65
+ ok: false,
66
+ error: `--branch requires a ref argument (e.g. \`--branch main\`)`,
67
+ };
68
+ }
69
+ branch = next;
70
+ i += 1;
71
+ }
72
+ else if (arg.startsWith('--')) {
73
+ return {
74
+ ok: false,
75
+ error: `unknown flag: ${arg}. Supported: --no-launch, --branch <ref>`,
76
+ };
77
+ }
78
+ else {
79
+ if (name !== null) {
80
+ return {
81
+ ok: false,
82
+ error: `unexpected extra argument: ${arg} (already have name "${name}")`,
83
+ };
84
+ }
85
+ name = arg;
86
+ }
87
+ }
88
+ if (name === null) {
89
+ return {
90
+ ok: false,
91
+ error: `missing required argument <name>. Usage: borg spawn <name> [--branch <ref>] [--no-launch]`,
92
+ };
93
+ }
94
+ return { ok: true, args: { name, noLaunch, branch } };
95
+ }
96
+ // ------------------------------------------------------------------
97
+ // Name validation
98
+ // ------------------------------------------------------------------
99
+ /**
100
+ * Worktree names get tacked onto the parent directory's path. They
101
+ * must therefore be filesystem-safe AND short enough that the
102
+ * resulting path doesn't make `ls` unreadable. Reject anything with
103
+ * slashes, parent-directory tokens, leading dashes (would parse as
104
+ * a flag), or shell-metacharacter punctuation.
105
+ *
106
+ * Allowed: lowercase ASCII letters, digits, hyphens, underscores.
107
+ * Length 1–48. Anchored. No leading hyphen.
108
+ */
109
+ const NAME_RE = /^[a-z0-9_][a-z0-9_-]{0,47}$/;
110
+ export function validateName(name) {
111
+ if (name.length === 0) {
112
+ return { ok: false, error: `name must not be empty` };
113
+ }
114
+ if (!NAME_RE.test(name)) {
115
+ return {
116
+ ok: false,
117
+ error: `invalid name "${name}". Use lowercase letters, digits, hyphens, or ` +
118
+ `underscores; max 48 chars; must not start with a hyphen.`,
119
+ };
120
+ }
121
+ return { ok: true };
122
+ }
123
+ // ------------------------------------------------------------------
124
+ // Path computation (pure)
125
+ // ------------------------------------------------------------------
126
+ /**
127
+ * Given the absolute path to the primary worktree and a validated
128
+ * spawn name, return the absolute path of the new sibling worktree.
129
+ *
130
+ * Naming convention: `<sibling-parent>/<primary-basename>-<name>`.
131
+ * Example: primary `/home/x/borg-mcp` + name `builder` →
132
+ * `/home/x/borg-mcp-builder`. Keeps related worktrees alphabetically
133
+ * adjacent in `ls`, which matters when a drone-operator has a dozen
134
+ * sibling trees.
135
+ *
136
+ * Pure. Tested directly.
137
+ */
138
+ export function computeWorktreePath(primaryWorktreeAbs, name) {
139
+ const parent = dirname(primaryWorktreeAbs);
140
+ const base = basename(primaryWorktreeAbs);
141
+ return join(parent, `${base}-${name}`);
142
+ }
143
+ const defaultDeps = {
144
+ runSync: (cmd, args, cwd) => {
145
+ const r = spawnSync(cmd, args, { cwd, encoding: 'utf-8' });
146
+ return {
147
+ status: r.status,
148
+ stdout: r.stdout ?? '',
149
+ stderr: r.stderr ?? '',
150
+ };
151
+ },
152
+ pathExists: (p) => existsSync(p),
153
+ runDetached: (cmd, args, cwd) => new Promise((resolveExit, rejectExit) => {
154
+ const child = spawn(cmd, args, { cwd, stdio: 'inherit', shell: false });
155
+ child.on('error', rejectExit);
156
+ child.on('exit', (code) => resolveExit(code ?? 0));
157
+ }),
158
+ cwd: () => process.cwd(),
159
+ stderr: (line) => process.stderr.write(line),
160
+ };
161
+ /**
162
+ * Run the `borg spawn` flow against the given args. Returns the
163
+ * desired process exit code. Pure side-effects flow through `deps`
164
+ * so tests can inject mocks for every external interaction.
165
+ *
166
+ * Flow:
167
+ * 1. Validate name
168
+ * 2. Resolve primary worktree path via `git rev-parse --show-toplevel`
169
+ * (run from the operator's cwd — handles nested worktrees by
170
+ * asking git to find the top, which always points at the current
171
+ * worktree's root; for our naming convention we want the
172
+ * `--git-common-dir` parent which IS the primary worktree's
173
+ * filesystem dir, NOT the current worktree's. Resolve below.)
174
+ * 3. Compute target path; abort if exists
175
+ * 4. Run `git worktree add <target> [<ref>|--detach HEAD]`
176
+ * 5. Print stderr nudge
177
+ * 6. If !noLaunch, exec `borg` inside the new worktree
178
+ */
179
+ export async function runSpawn(args, deps = {}) {
180
+ const { runSync, pathExists, runDetached, cwd, stderr } = {
181
+ ...defaultDeps,
182
+ ...deps,
183
+ };
184
+ // (1) Name validation
185
+ const nameCheck = validateName(args.name);
186
+ if (!nameCheck.ok) {
187
+ stderr(chalk.red(`◼ borg spawn: ${nameCheck.error}\n`));
188
+ return 1;
189
+ }
190
+ // (2) Primary worktree resolution.
191
+ //
192
+ // `git rev-parse --git-common-dir` returns the path to the SHARED
193
+ // `.git` directory (the primary worktree's `.git`). The PRIMARY
194
+ // WORKTREE itself is that directory's parent — which is what we
195
+ // want to use as the basis for the sibling worktree's path. This
196
+ // resolution works correctly even when `borg spawn` is invoked
197
+ // from inside a non-primary worktree (e.g. the operator already
198
+ // cd'd into a sibling drone tree and wants to spin up another).
199
+ const cwdValue = cwd();
200
+ const gitCommonDir = runSync('git', ['rev-parse', '--git-common-dir'], cwdValue);
201
+ if (gitCommonDir.status !== 0) {
202
+ stderr(chalk.red(`◼ borg spawn: not in a git repository (cwd: ${cwdValue})\n`));
203
+ return 1;
204
+ }
205
+ const commonDirRaw = gitCommonDir.stdout.trim();
206
+ // `--git-common-dir` may print a relative path (e.g. `.git`); resolve
207
+ // against cwd to get an absolute reference.
208
+ const commonDirAbs = resolve(cwdValue, commonDirRaw);
209
+ // Strip the trailing `.git` segment to get the primary worktree root.
210
+ // commonDirAbs ends in `/.git` for a regular checkout, or in
211
+ // `/.git/worktrees/<name>` for a non-primary worktree's perspective
212
+ // (but `--common-dir` always returns the SHARED `.git`, so the suffix
213
+ // is just `/.git`). dirname() peels the `.git` segment off.
214
+ const primaryWorktree = basename(commonDirAbs) === '.git' ? dirname(commonDirAbs) : commonDirAbs;
215
+ // (3) Compute + collision-check target path
216
+ const targetPath = computeWorktreePath(primaryWorktree, args.name);
217
+ if (pathExists(targetPath)) {
218
+ stderr(chalk.red(`◼ borg spawn: target path already exists: ${targetPath}\n`));
219
+ return 1;
220
+ }
221
+ // (4) Create the worktree
222
+ const worktreeArgs = args.branch
223
+ ? ['worktree', 'add', targetPath, args.branch]
224
+ : ['worktree', 'add', '--detach', targetPath, 'HEAD'];
225
+ const createResult = runSync('git', worktreeArgs, primaryWorktree);
226
+ if (createResult.status !== 0) {
227
+ const gitErr = createResult.stderr.trim() || createResult.stdout.trim();
228
+ stderr(chalk.red(`◼ borg spawn: git worktree add failed:\n${gitErr}\n`));
229
+ return 1;
230
+ }
231
+ // (5) Stderr nudge.
232
+ //
233
+ // Even though `git worktree add` already prints its own confirmation
234
+ // line, we add a short summary that names the next steps the operator
235
+ // typically wants — particularly the "checkout a branch and pull"
236
+ // reminder for the default detached case, which catches users who
237
+ // would otherwise start work on a stale HEAD without realising.
238
+ stderr(chalk.blue(`◼ Spawned worktree at ${targetPath}\n`));
239
+ if (!args.branch) {
240
+ const shortSha = readShortSha(runSync, targetPath);
241
+ stderr(chalk.gray(`◼ Detached HEAD at ${shortSha}\n`));
242
+ stderr(chalk.gray(`◼ Run \`git checkout main && git pull\` before starting work\n`));
243
+ }
244
+ else {
245
+ stderr(chalk.gray(`◼ Checked out ${args.branch}\n`));
246
+ }
247
+ // (6) Optional drone launch.
248
+ if (args.noLaunch) {
249
+ stderr(chalk.gray(`◼ --no-launch: skipping borg launch\n`));
250
+ stderr(chalk.gray(` cd ${targetPath} && borg\n`));
251
+ return 0;
252
+ }
253
+ stderr(chalk.blue(`◼ Launching Claude Code in ${targetPath}…\n`));
254
+ try {
255
+ const code = await runDetached('borg', [], targetPath);
256
+ return code;
257
+ }
258
+ catch (err) {
259
+ stderr(chalk.red(`◼ borg spawn: failed to launch borg in new worktree: ${err?.message ?? err}\n`));
260
+ return 1;
261
+ }
262
+ }
263
+ /**
264
+ * Best-effort short SHA of the new worktree's HEAD. Used only for the
265
+ * cosmetic "Detached HEAD at <sha>" line; failure here is non-fatal.
266
+ */
267
+ function readShortSha(runSync, worktreePath) {
268
+ const r = runSync('git', ['rev-parse', '--short', 'HEAD'], worktreePath);
269
+ if (r.status === 0)
270
+ return r.stdout.trim();
271
+ return '(unknown)';
272
+ }
273
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
@@ -138,7 +138,7 @@ export function renderStreamStatus(inputs) {
138
138
  if (inboxPath && droneLabel && cubeName) {
139
139
  lines.push('');
140
140
  lines.push('## Real-time wake-up (do this now)');
141
- lines.push('No process is tailing this drone\'s inbox file — your `/loop` will only wake on its fallback heartbeat and will miss live coordination from other drones. Arm an inbox Monitor:');
141
+ lines.push('No process is tailing this drone\'s inbox file — this session won\'t wake on real-time cube activity, only on its fallback timer, and will miss live coordination from other drones. Arm an inbox Monitor:');
142
142
  lines.push('');
143
143
  lines.push(`> Monitor command: \`tail -n 0 -F ${inboxPath}\` — persistent, 1h timeout, description "borg inbox for ${droneLabel} on cube ${cubeName}".`);
144
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stream-status.js","sourceRoot":"","sources":["../src/stream-status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stream-status.js","sourceRoot":"","sources":["../src/stream-status.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAiID,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAE9C,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEzD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AA8JD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAE9C,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEzD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
package/dist/templates.js CHANGED
@@ -72,8 +72,9 @@ Project conventions:
72
72
 
73
73
  Workflow:
74
74
  - On regen, scan the log for unanswered \`REVIEW-READY:\` signals. Pick the oldest unowned one. Post \`STARTING: review of <branch>\` and pull the diff.
75
- - Verify: does the code do what the commit message claims? Tests pass? Bundle size acceptable? No security or RLS regressions? Follows project conventions?
75
+ - Verify: does the code do what the commit message claims? Tests pass? Bundle size acceptable? Follows project conventions?
76
76
  - **Replaced-module behavioral diff.** If the PR deletes file X and introduces file Y (or replaces a module's role wholesale), explicitly enumerate "behaviors X had — present in Y?" before approval. Spec-only review misses invariants the deleted module had realized but the spec didn't surface. The 0.5.0 SSE cutover lost the silent-self filter exactly this way; checking the new \`log-stream.ts\` against the deleted \`inbox.ts:87-88\` directly would have caught it pre-merge.
77
+ - **Security review is Security Auditor's lane, not yours.** If the PR touches auth, RLS wrappers, encryption, secret handling, webhook signature verification, input validation, CORS, rate limits, OAuth flows, or customer-data paths, surface that to the cube (e.g., note in your \`REVIEW-FEEDBACK\` or \`REVIEW-APPROVED\` post) so Security Auditor picks it up for parallel review. Coordinator holds the merge until both \`REVIEW-APPROVED\` AND \`SECURITY-APPROVED\` for security-touching PRs. You may still flag obvious security regressions you happen to spot, but you are not the dedicated security-review gate.
77
78
  - For each finding worth flagging, post \`REVIEW-FEEDBACK: <branch> <observation>\` — high-confidence issues only. Sort blockers from nits explicitly.
78
79
  - When done, post either \`REVIEW-APPROVED: <branch>\` (clean) or expect the Builder to address feedback and re-post \`REVIEW-READY:\`.
79
80
 
@@ -137,6 +138,34 @@ Operating principles:
137
138
 
138
139
  Read the log first on every regen — including older entries that may have surfaced retrospectives you can build on.`,
139
140
  },
141
+ {
142
+ name: 'Security Auditor',
143
+ short_description: 'Reviews security-touching changes for vulnerability classes, auth/RLS/crypto correctness, and adherence to documented security expectations. Continuous low-grade vigilance.',
144
+ detailed_description: `You are the cube's security specialist — the dedicated owner of the security expectations the project documents but no other role enforces. Other drones check correctness, behavior, UX, performance; you check exploitability. Autonomous — coordinate through the log.
145
+
146
+ Your job:
147
+ - Review security-touching code changes for vulnerability classes: OWASP top 10, command injection, XSS, SQL injection, auth bypass, data leaks, path traversal, SSRF, race conditions in auth/billing paths.
148
+ - Audit security-critical surfaces: auth flows (JWT/OAuth verification, JWKS caching), RLS wrappers (\`withUserId()\` and equivalents — any function that gates user-data access by session identity), encryption (algorithms, key handling, IV/nonce uniqueness, secret storage), webhook signature verification (HMAC), input validation at API boundaries (Zod schemas), CORS / origin allowlists, rate limiters, dependency hygiene (CVE checks on dependency bumps), customer-data and billing paths.
149
+ - Run periodic full-codebase sweeps separate from per-PR review — walk the documented security expectations (CLAUDE.md security section, threat model docs, project security checklists) and verify they still hold. Cadence: once per minor release or every ~2 weeks, whichever comes first. Catches the "we documented it but stopped enforcing it" failure mode.
150
+
151
+ When you engage on a PR:
152
+ - On regen, scan the log for \`REVIEW-READY:\` signals on branches touching security surface (auth, RLS, encryption, webhooks, input validation, CORS, rate limits, OAuth, customer-data paths, dependency bumps).
153
+ - For non-security-relevant changes (UX copy, version bumps, test infra, internal refactors of non-security code), DON'T gate. Code Reviewer alone is the merge gate for those.
154
+ - Post \`STARTING: security review of <branch>\` and pull the diff.
155
+
156
+ For each finding, post \`SECURITY-FINDING: <branch> <severity>: <observation> — remediation: <fix>\` using these severity classes:
157
+ - **CRITICAL** — data leak, auth bypass, RCE potential → block merge
158
+ - **HIGH** — significant exposure under realistic conditions → fix before merge
159
+ - **MEDIUM** — limited exposure or requires unusual conditions → fix this sprint
160
+ - **LOW** — defense-in-depth, hardening → track for follow-up
161
+ - **INFORMATIONAL** — pattern note, best-practice suggestion → non-blocking
162
+
163
+ When done, post \`SECURITY-APPROVED: <branch>\` (clean), or \`SECURITY-DEFER: <branch> <issue> — track: <where>\` for a known issue acceptable for this release but documented for follow-up. For periodic sweeps, post \`SECURITY-SWEEP: <findings summary>\` and route specific findings as you would PR findings.
164
+
165
+ Don't merge yourself — \`SECURITY-APPROVED\` is the signal; the Coordinator does the actual merge. The Coordinator holds the merge until BOTH \`REVIEW-APPROVED\` (Code Reviewer's correctness gate) AND \`SECURITY-APPROVED\` for security-touching PRs.
166
+
167
+ You DON'T do: correctness review (Code Reviewer's lane), QA testing (QA Tester's lane), UX evaluation (UX Expert's lane), merging, or releasing. Your output is \`SECURITY-FINDING:\` / \`SECURITY-APPROVED:\` / \`SECURITY-DEFER:\` / \`SECURITY-SWEEP:\` signals on the log.`,
168
+ },
140
169
  ],
141
170
  };
142
171
  export const TEMPLATES = {
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgBH,MAAM,YAAY,GAAa;IAC7B,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,yIAAyI;IAC3I,KAAK,EAAE;QACL;YACE,IAAI,EAAE,aAAa;YACnB,cAAc,EAAE,IAAI;YACpB,iBAAiB,EACf,qFAAqF;YACvF,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;mEAuBuC;SAC9D;QACD;YACE,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,IAAI;YAChB,iBAAiB,EAAE,2FAA2F;YAC9G,oBAAoB,EAAE;;;;;;;;;;;uFAW2D;SAClF;QACD;YACE,IAAI,EAAE,eAAe;YACrB,iBAAiB,EAAE,oFAAoF;YACvG,oBAAoB,EAAE;;;;;;;;;yGAS6E;SACpG;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,6FAA6F;YAChH,oBAAoB,EAAE;;;;;;;;;;iNAUqL;SAC5M;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,+EAA+E;YAClG,oBAAoB,EAAE;;;;;;;;2KAQ+I;SACtK;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,qIAAqI;YACxJ,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;oHAyBwF;SAC/G;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAA6B;IACjD,cAAc,EAAE,YAAY;CAC7B,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAgBH,MAAM,YAAY,GAAa;IAC7B,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,yIAAyI;IAC3I,KAAK,EAAE;QACL;YACE,IAAI,EAAE,aAAa;YACnB,cAAc,EAAE,IAAI;YACpB,iBAAiB,EACf,qFAAqF;YACvF,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;mEAuBuC;SAC9D;QACD;YACE,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,IAAI;YAChB,iBAAiB,EAAE,2FAA2F;YAC9G,oBAAoB,EAAE;;;;;;;;;;;uFAW2D;SAClF;QACD;YACE,IAAI,EAAE,eAAe;YACrB,iBAAiB,EAAE,oFAAoF;YACvG,oBAAoB,EAAE;;;;;;;;;;yGAU6E;SACpG;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,6FAA6F;YAChH,oBAAoB,EAAE;;;;;;;;;;iNAUqL;SAC5M;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,+EAA+E;YAClG,oBAAoB,EAAE;;;;;;;;2KAQ+I;SACtK;QACD;YACE,IAAI,EAAE,WAAW;YACjB,iBAAiB,EAAE,qIAAqI;YACxJ,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;oHAyBwF;SAC/G;QACD;YACE,IAAI,EAAE,kBAAkB;YACxB,iBAAiB,EAAE,8KAA8K;YACjM,oBAAoB,EAAE;;;;;;;;;;;;;;;;;;;;;;;+QAuBmP;SAC1Q;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAA6B;IACjD,cAAc,EAAE,YAAY;CAC7B,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Terminal-title setter for borg drone sessions.
3
+ *
4
+ * Multiple Claude Code sessions across sibling worktrees are visually
5
+ * indistinguishable in Cmd-Tab / tab bars / Mission Control on macOS,
6
+ * and likewise on most Linux terminal emulators. Setting the terminal
7
+ * title via the OSC 0 / OSC 2 escape gives each window a free per-
8
+ * session identity.
9
+ *
10
+ * Format (Queen-specified):
11
+ * - Assimilated drone session: `borg · <label> · <cubeName>`
12
+ * - Unassimilated session: `borg · <repo-basename>`
13
+ *
14
+ * Why OSC 0 (`\x1b]0;…\x07`): sets both window title AND icon name on
15
+ * most terminals. OSC 2 sets only window title; OSC 1 sets only icon
16
+ * name. OSC 0 is the maximally-portable choice — works in iTerm2,
17
+ * macOS Terminal, kitty, alacritty, ghostty, GNOME Terminal, xterm.
18
+ *
19
+ * Lifetime: the escape is emitted once, before spawning Claude Code.
20
+ * Claude Code itself does not set its own window title (verified
21
+ * 2026-05-11), so the borg-set title persists for the whole session.
22
+ *
23
+ * Limitations (acceptable for v1; flagged for future):
24
+ * - Title doesn't update mid-session on `borg:assimilate`. The
25
+ * borgmcp client process can't write the escape post-spawn
26
+ * because stdio is owned by Claude Code at that point (and stdio
27
+ * to Claude is JSON-RPC — terminal escapes would be parsed as
28
+ * invalid messages).
29
+ * - Falls back to repo-basename for the unassimilated case; loses
30
+ * drone identity until the cube is joined. Typical pattern is
31
+ * "drone has been around long enough that cubes.json is already
32
+ * populated," so the assimilated path is the common case.
33
+ */
34
+ /**
35
+ * Pure: compose the title string for a session. Exported so tests can
36
+ * exercise every branch without TTY / process / fs dependencies.
37
+ *
38
+ * @param activeDrone — `{label, cubeName}` if this project is
39
+ * assimilated to a cube, null otherwise.
40
+ * @param repoBasename — fallback identity for the unassimilated case
41
+ * (typically `basename(process.cwd())`).
42
+ */
43
+ export declare function composeTerminalTitle(activeDrone: {
44
+ label: string;
45
+ cubeName: string;
46
+ } | null, repoBasename: string): string;
47
+ /**
48
+ * Side-effecting: emit the OSC 0 escape to stdout, but only if stdout
49
+ * is a TTY. When stdout is piped (CI, redirection, scripted
50
+ * invocation), emitting the raw escape would pollute the captured
51
+ * output without doing anything useful — so we no-op.
52
+ *
53
+ * Returns the title string that WOULD have been emitted, regardless of
54
+ * TTY state, so callers can log it independently for diagnostics.
55
+ */
56
+ export declare function setTerminalTitle(activeDrone: {
57
+ label: string;
58
+ cubeName: string;
59
+ } | null, repoBasename: string, stdout?: NodeJS.WriteStream): string;
60
+ //# sourceMappingURL=terminal-title.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-title.d.ts","sourceRoot":"","sources":["../src/terminal-title.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EACvD,YAAY,EAAE,MAAM,GACnB,MAAM,CAKR;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EACvD,YAAY,EAAE,MAAM,EACpB,MAAM,GAAE,MAAM,CAAC,WAA4B,GAC1C,MAAM,CASR"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Terminal-title setter for borg drone sessions.
3
+ *
4
+ * Multiple Claude Code sessions across sibling worktrees are visually
5
+ * indistinguishable in Cmd-Tab / tab bars / Mission Control on macOS,
6
+ * and likewise on most Linux terminal emulators. Setting the terminal
7
+ * title via the OSC 0 / OSC 2 escape gives each window a free per-
8
+ * session identity.
9
+ *
10
+ * Format (Queen-specified):
11
+ * - Assimilated drone session: `borg · <label> · <cubeName>`
12
+ * - Unassimilated session: `borg · <repo-basename>`
13
+ *
14
+ * Why OSC 0 (`\x1b]0;…\x07`): sets both window title AND icon name on
15
+ * most terminals. OSC 2 sets only window title; OSC 1 sets only icon
16
+ * name. OSC 0 is the maximally-portable choice — works in iTerm2,
17
+ * macOS Terminal, kitty, alacritty, ghostty, GNOME Terminal, xterm.
18
+ *
19
+ * Lifetime: the escape is emitted once, before spawning Claude Code.
20
+ * Claude Code itself does not set its own window title (verified
21
+ * 2026-05-11), so the borg-set title persists for the whole session.
22
+ *
23
+ * Limitations (acceptable for v1; flagged for future):
24
+ * - Title doesn't update mid-session on `borg:assimilate`. The
25
+ * borgmcp client process can't write the escape post-spawn
26
+ * because stdio is owned by Claude Code at that point (and stdio
27
+ * to Claude is JSON-RPC — terminal escapes would be parsed as
28
+ * invalid messages).
29
+ * - Falls back to repo-basename for the unassimilated case; loses
30
+ * drone identity until the cube is joined. Typical pattern is
31
+ * "drone has been around long enough that cubes.json is already
32
+ * populated," so the assimilated path is the common case.
33
+ */
34
+ /**
35
+ * Pure: compose the title string for a session. Exported so tests can
36
+ * exercise every branch without TTY / process / fs dependencies.
37
+ *
38
+ * @param activeDrone — `{label, cubeName}` if this project is
39
+ * assimilated to a cube, null otherwise.
40
+ * @param repoBasename — fallback identity for the unassimilated case
41
+ * (typically `basename(process.cwd())`).
42
+ */
43
+ export function composeTerminalTitle(activeDrone, repoBasename) {
44
+ if (activeDrone) {
45
+ return `borg · ${activeDrone.label} · ${activeDrone.cubeName}`;
46
+ }
47
+ return `borg · ${repoBasename}`;
48
+ }
49
+ /**
50
+ * Side-effecting: emit the OSC 0 escape to stdout, but only if stdout
51
+ * is a TTY. When stdout is piped (CI, redirection, scripted
52
+ * invocation), emitting the raw escape would pollute the captured
53
+ * output without doing anything useful — so we no-op.
54
+ *
55
+ * Returns the title string that WOULD have been emitted, regardless of
56
+ * TTY state, so callers can log it independently for diagnostics.
57
+ */
58
+ export function setTerminalTitle(activeDrone, repoBasename, stdout = process.stdout) {
59
+ const title = composeTerminalTitle(activeDrone, repoBasename);
60
+ if (stdout.isTTY) {
61
+ // OSC 0: ESC ] 0 ; <title> BEL
62
+ // (BEL terminator is honored by all OSC-supporting terminals;
63
+ // ST = `ESC \` is an alternative but BEL has wider compat.)
64
+ stdout.write(`\x1b]0;${title}\x07`);
65
+ }
66
+ return title;
67
+ }
68
+ //# sourceMappingURL=terminal-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-title.js","sourceRoot":"","sources":["../src/terminal-title.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAAuD,EACvD,YAAoB;IAEpB,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,UAAU,WAAW,CAAC,KAAK,MAAM,WAAW,CAAC,QAAQ,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,UAAU,YAAY,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAC9B,WAAuD,EACvD,YAAoB,EACpB,SAA6B,OAAO,CAAC,MAAM;IAE3C,MAAM,KAAK,GAAG,oBAAoB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IAC9D,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,+BAA+B;QAC/B,8DAA8D;QAC9D,4DAA4D;QAC5D,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Multi-agent coordination for Claude Code — cubes, drones, and a shared activity log.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",