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 +4 -2
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +21 -2
- package/dist/claude.js.map +1 -1
- package/dist/spawn.d.ts +116 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +273 -0
- package/dist/spawn.js.map +1 -0
- package/dist/stream-status.js +1 -1
- package/dist/stream-status.js.map +1 -1
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +30 -1
- package/dist/templates.js.map +1 -1
- package/dist/terminal-title.d.ts +60 -0
- package/dist/terminal-title.d.ts.map +1 -0
- package/dist/terminal-title.js +68 -0
- package/dist/terminal-title.js.map +1 -0
- package/package.json +1 -1
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
|
|
12
|
-
* borg setup
|
|
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
|
package/dist/claude.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":";AACA
|
|
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
|
|
12
|
-
* borg setup
|
|
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. `
|
package/dist/claude.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../src/claude.ts"],"names":[],"mappings":"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"}
|
package/dist/spawn.d.ts
ADDED
|
@@ -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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"}
|
package/dist/stream-status.js
CHANGED
|
@@ -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 —
|
|
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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"}
|
package/dist/templates.d.ts.map
CHANGED
|
@@ -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;
|
|
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?
|
|
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 = {
|
package/dist/templates.js.map
CHANGED
|
@@ -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
|
|
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"}
|