borgmcp 0.2.0-beta.9 → 0.3.0

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.
Files changed (48) hide show
  1. package/README.md +2 -2
  2. package/dist/auth.js +5 -5
  3. package/dist/auth.js.map +1 -1
  4. package/dist/claude.d.ts +9 -4
  5. package/dist/claude.d.ts.map +1 -1
  6. package/dist/claude.js +62 -26
  7. package/dist/claude.js.map +1 -1
  8. package/dist/config-utils.d.ts +32 -0
  9. package/dist/config-utils.d.ts.map +1 -1
  10. package/dist/config-utils.js +159 -0
  11. package/dist/config-utils.js.map +1 -1
  12. package/dist/cubes.d.ts +51 -0
  13. package/dist/cubes.d.ts.map +1 -0
  14. package/dist/cubes.js +161 -0
  15. package/dist/cubes.js.map +1 -0
  16. package/dist/inbox.d.ts +33 -0
  17. package/dist/inbox.d.ts.map +1 -0
  18. package/dist/inbox.js +125 -0
  19. package/dist/inbox.js.map +1 -0
  20. package/dist/index.d.ts +3 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +353 -25
  23. package/dist/index.js.map +1 -1
  24. package/dist/log-audit.d.ts +27 -0
  25. package/dist/log-audit.d.ts.map +1 -0
  26. package/dist/log-audit.js +161 -0
  27. package/dist/log-audit.js.map +1 -0
  28. package/dist/postinstall.d.ts +1 -1
  29. package/dist/postinstall.d.ts.map +1 -1
  30. package/dist/postinstall.js +4 -4
  31. package/dist/postinstall.js.map +1 -1
  32. package/dist/regen-format.d.ts +53 -0
  33. package/dist/regen-format.d.ts.map +1 -0
  34. package/dist/regen-format.js +164 -0
  35. package/dist/regen-format.js.map +1 -0
  36. package/dist/regen.d.ts +19 -0
  37. package/dist/regen.d.ts.map +1 -0
  38. package/dist/regen.js +35 -0
  39. package/dist/regen.js.map +1 -0
  40. package/dist/remote-client.d.ts +107 -2
  41. package/dist/remote-client.d.ts.map +1 -1
  42. package/dist/remote-client.js +141 -11
  43. package/dist/remote-client.js.map +1 -1
  44. package/dist/setup.js +40 -33
  45. package/dist/setup.js.map +1 -1
  46. package/dist/sync.js +1 -1
  47. package/dist/sync.js.map +1 -1
  48. package/package.json +6 -5
package/dist/cubes.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Per-project active-cube persistence for Borg MCP client
3
+ *
4
+ * Stores the currently-assimilated cube (name + drone session token + drone
5
+ * label + apiUrl) PER PROJECT in ~/.config/borgmcp/cubes.json. The "project"
6
+ * is identified by walking up from cwd to find a .git directory; if none is
7
+ * found, cwd itself is used as the project key.
8
+ *
9
+ * The session token is a bearer credential for drone-scoped REST endpoints,
10
+ * but it is not as sensitive as the OAuth tokens (which still live in the
11
+ * OS keychain via config.ts) — it only authorizes access within the cube
12
+ * the user has already been admitted to.
13
+ *
14
+ * apiUrl is captured at assimilate time so subprocess invocations (e.g. the
15
+ * SessionStart hook firing borg-regen) don't need BORG_API_URL in their env
16
+ * to know which worker to talk to.
17
+ */
18
+ import { existsSync } from 'node:fs';
19
+ import { mkdir, readFile, writeFile, unlink } from 'node:fs/promises';
20
+ import { homedir } from 'node:os';
21
+ import { dirname, join, resolve } from 'node:path';
22
+ const CUBES_DIR = join(homedir(), '.config', 'borgmcp');
23
+ const CUBES_FILE = join(CUBES_DIR, 'cubes.json');
24
+ const INBOX_DIR = join(CUBES_DIR, 'inboxes');
25
+ /**
26
+ * Walk up from cwd looking for a .git directory. If found, return that
27
+ * directory. If not found by filesystem root, return the original cwd.
28
+ * The returned absolute path is the "project key" used to scope cube state.
29
+ */
30
+ export function findProjectRoot(cwd = process.cwd()) {
31
+ let dir = resolve(cwd);
32
+ while (true) {
33
+ if (existsSync(join(dir, '.git')))
34
+ return dir;
35
+ const parent = dirname(dir);
36
+ if (parent === dir)
37
+ return resolve(cwd); // hit root, fall back to cwd
38
+ dir = parent;
39
+ }
40
+ }
41
+ /**
42
+ * Per-(cube, drone) inbox file path. Each drone gets its own file so that
43
+ * multiple drones in the same cube don't trample each other's writes when
44
+ * they each receive the same long-poll batch. The file lives under a
45
+ * per-cube subdir keyed by cube ID, then by drone ID (a UUID, globally
46
+ * unique).
47
+ *
48
+ * Validates cubeId/droneId as UUIDs before using them in a filesystem
49
+ * path. The values come from cubes.json (populated from server response),
50
+ * so the input is trusted in normal operation — but a regex guard is
51
+ * cheap defense against a corrupted file or future bug that would
52
+ * otherwise let `../` slip through into the inbox path.
53
+ */
54
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
55
+ export function inboxPathForDrone(cubeId, droneId) {
56
+ if (!UUID_RE.test(cubeId))
57
+ throw new Error(`Invalid cubeId: ${cubeId}`);
58
+ if (!UUID_RE.test(droneId))
59
+ throw new Error(`Invalid droneId: ${droneId}`);
60
+ return join(INBOX_DIR, cubeId, `${droneId}.log`);
61
+ }
62
+ /**
63
+ * Type guard: true iff the parsed JSON looks like the new schema. Anything
64
+ * else (old single-cube schema, malformed, missing) is treated as "no state".
65
+ */
66
+ function isCubesFile(data) {
67
+ return (data !== null &&
68
+ typeof data === 'object' &&
69
+ typeof data.projects === 'object' &&
70
+ data.projects !== null &&
71
+ !Array.isArray(data.projects));
72
+ }
73
+ /**
74
+ * Read the cubes.json file. Returns null if the file does not exist, is
75
+ * unparseable, or is in the old single-cube schema (per the project's no-
76
+ * backward-compat stance, the old shape is treated as absent — it will be
77
+ * overwritten the next time setActiveCube() runs).
78
+ */
79
+ async function readCubesFile() {
80
+ let raw;
81
+ try {
82
+ raw = await readFile(CUBES_FILE, 'utf8');
83
+ }
84
+ catch (error) {
85
+ if (error?.code === 'ENOENT')
86
+ return null;
87
+ throw error;
88
+ }
89
+ let parsed;
90
+ try {
91
+ parsed = JSON.parse(raw);
92
+ }
93
+ catch {
94
+ return null;
95
+ }
96
+ if (!isCubesFile(parsed))
97
+ return null;
98
+ return parsed;
99
+ }
100
+ /**
101
+ * Write the cubes.json file, ensuring the parent directory exists.
102
+ */
103
+ async function writeCubesFile(data) {
104
+ await mkdir(dirname(CUBES_FILE), { recursive: true });
105
+ await writeFile(CUBES_FILE, JSON.stringify(data, null, 2) + '\n', {
106
+ mode: 0o600,
107
+ });
108
+ }
109
+ /**
110
+ * Get the currently-active cube for the current project, or null if not
111
+ * assimilated in this project. Entries written by older client versions
112
+ * that lack the `cubeId` field are treated as absent — re-assimilate to
113
+ * refresh.
114
+ */
115
+ export async function getActiveCube() {
116
+ const data = await readCubesFile();
117
+ if (!data)
118
+ return null;
119
+ const key = findProjectRoot();
120
+ const entry = data.projects[key];
121
+ if (!entry || typeof entry.cubeId !== 'string' || !entry.cubeId)
122
+ return null;
123
+ if (typeof entry.droneId !== 'string' || !entry.droneId)
124
+ return null;
125
+ return entry;
126
+ }
127
+ /**
128
+ * Set the active cube for the current project. Preserves entries for all
129
+ * other projects.
130
+ */
131
+ export async function setActiveCube(active) {
132
+ const existing = (await readCubesFile()) ?? { projects: {} };
133
+ existing.projects[findProjectRoot()] = active;
134
+ await writeCubesFile(existing);
135
+ }
136
+ /**
137
+ * Clear the active cube for the current project. If the projects map
138
+ * becomes empty as a result, remove the file entirely rather than leave
139
+ * an empty {projects:{}} skeleton.
140
+ */
141
+ export async function clearActiveCube() {
142
+ const existing = await readCubesFile();
143
+ if (!existing)
144
+ return;
145
+ const key = findProjectRoot();
146
+ if (!(key in existing.projects))
147
+ return;
148
+ delete existing.projects[key];
149
+ if (Object.keys(existing.projects).length === 0) {
150
+ try {
151
+ await unlink(CUBES_FILE);
152
+ }
153
+ catch (error) {
154
+ if (error?.code !== 'ENOENT')
155
+ throw error;
156
+ }
157
+ return;
158
+ }
159
+ await writeCubesFile(existing);
160
+ }
161
+ //# sourceMappingURL=cubes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cubes.js","sourceRoot":"","sources":["../src/cubes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Real-time drone wakeup via long-poll + inbox file.
3
+ *
4
+ * The MCP client spawns a background poller that long-polls the worker's
5
+ * /api/drone/wait endpoint. When new activity from OTHER drones arrives
6
+ * in the cube, we append a one-line summary per entry to a per-(cube,
7
+ * drone) inbox file (see inboxPathForDrone in cubes.ts). One file per
8
+ * drone session avoids the duplicate-write problem that would happen
9
+ * if multiple drones in the same cube shared a file.
10
+ *
11
+ * Own-drone entries are filtered out — there's no point waking a drone
12
+ * on its own post.
13
+ *
14
+ * The launcher kickoff (see claude.ts) arms a Monitor on the same path
15
+ * so Claude wakes the moment a line is appended — no waiting for the
16
+ * next /loop heartbeat.
17
+ *
18
+ * Dedup by entry id is layered in as defense against any precision /
19
+ * timestamp-cursor edge cases on the server side. Without it, a single
20
+ * sub-millisecond gap between the DB column and our cursor string would
21
+ * have the worker return the same row forever.
22
+ *
23
+ * The poller is fire-and-forget. If it dies, the launcher's fallback
24
+ * heartbeat (ScheduleWakeup, ~30 minutes) still keeps the drone in sync.
25
+ */
26
+ /**
27
+ * Spawn the background long-poll loop. Idempotent: if no active cube
28
+ * is configured for this project, the loop sleeps and re-checks. The
29
+ * loop runs until process exit. Errors are logged to stderr (so they
30
+ * don't pollute the MCP stdio channel) and the loop continues.
31
+ */
32
+ export declare function startInboxPoller(): void;
33
+ //# sourceMappingURL=inbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAOH;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}
package/dist/inbox.js ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Real-time drone wakeup via long-poll + inbox file.
3
+ *
4
+ * The MCP client spawns a background poller that long-polls the worker's
5
+ * /api/drone/wait endpoint. When new activity from OTHER drones arrives
6
+ * in the cube, we append a one-line summary per entry to a per-(cube,
7
+ * drone) inbox file (see inboxPathForDrone in cubes.ts). One file per
8
+ * drone session avoids the duplicate-write problem that would happen
9
+ * if multiple drones in the same cube shared a file.
10
+ *
11
+ * Own-drone entries are filtered out — there's no point waking a drone
12
+ * on its own post.
13
+ *
14
+ * The launcher kickoff (see claude.ts) arms a Monitor on the same path
15
+ * so Claude wakes the moment a line is appended — no waiting for the
16
+ * next /loop heartbeat.
17
+ *
18
+ * Dedup by entry id is layered in as defense against any precision /
19
+ * timestamp-cursor edge cases on the server side. Without it, a single
20
+ * sub-millisecond gap between the DB column and our cursor string would
21
+ * have the worker return the same row forever.
22
+ *
23
+ * The poller is fire-and-forget. If it dies, the launcher's fallback
24
+ * heartbeat (ScheduleWakeup, ~30 minutes) still keeps the drone in sync.
25
+ */
26
+ import { promises as fs } from 'node:fs';
27
+ import path from 'node:path';
28
+ import { waitForActivity } from './remote-client.js';
29
+ import { getActiveCube, inboxPathForDrone } from './cubes.js';
30
+ /**
31
+ * Spawn the background long-poll loop. Idempotent: if no active cube
32
+ * is configured for this project, the loop sleeps and re-checks. The
33
+ * loop runs until process exit. Errors are logged to stderr (so they
34
+ * don't pollute the MCP stdio channel) and the loop continues.
35
+ */
36
+ export function startInboxPoller() {
37
+ void runLoop().catch((err) => {
38
+ process.stderr.write(`[borg-mcp inbox poller] fatal: ${err?.message ?? err}\n`);
39
+ });
40
+ }
41
+ async function runLoop() {
42
+ // Initialize lastSeen to "now" so we don't replay history that was
43
+ // already in the regen() snapshot included in the session-start
44
+ // orientation. We only want NEW activity from here forward.
45
+ let lastSeen = new Date().toISOString();
46
+ let currentCubeId = null;
47
+ let ownDroneId = null;
48
+ // Ring buffer of recently-written entry ids — protects against any
49
+ // server-side cursor drift that would otherwise re-emit the same row.
50
+ const recentIds = [];
51
+ const RECENT_CAP = 500;
52
+ while (true) {
53
+ const active = await getActiveCube();
54
+ if (!active) {
55
+ // No active cube — sleep and re-check. The user might assimilate
56
+ // mid-session.
57
+ await sleep(5000);
58
+ continue;
59
+ }
60
+ // Reset the cursor and dedup state when the active cube changes so
61
+ // we don't try to pull "since lastSeen" entries from the wrong
62
+ // timeline.
63
+ if (active.cubeId !== currentCubeId) {
64
+ currentCubeId = active.cubeId;
65
+ ownDroneId = active.droneId;
66
+ lastSeen = new Date().toISOString();
67
+ recentIds.length = 0;
68
+ }
69
+ try {
70
+ const { entries, drones, roles } = await waitForActivity(active.sessionToken, active.apiUrl, lastSeen);
71
+ if (entries.length > 0) {
72
+ const seenSet = new Set(recentIds);
73
+ const fresh = entries.filter((e) => {
74
+ if (seenSet.has(e.id))
75
+ return false;
76
+ // Filter out our own posts — we already know what we wrote.
77
+ if (e.drone_id === ownDroneId)
78
+ return false;
79
+ return true;
80
+ });
81
+ if (fresh.length > 0) {
82
+ await appendToInbox(active.cubeId, active.droneId, fresh, drones, roles);
83
+ for (const e of fresh)
84
+ recentIds.push(e.id);
85
+ while (recentIds.length > RECENT_CAP)
86
+ recentIds.shift();
87
+ }
88
+ // Advance the cursor against ALL returned entries (including
89
+ // own / already-seen). Otherwise we'd long-poll forever for
90
+ // entries we've intentionally skipped.
91
+ const newest = entries.reduce((a, b) => new Date(a.created_at) > new Date(b.created_at) ? a : b);
92
+ lastSeen = typeof newest.created_at === 'string'
93
+ ? newest.created_at
94
+ : new Date(newest.created_at).toISOString();
95
+ }
96
+ // Whether empty or not, immediately re-poll. The server already
97
+ // held the request for up to ~25s; no client-side sleep needed.
98
+ }
99
+ catch (err) {
100
+ // Network blip, auth refresh, etc — back off briefly so we don't
101
+ // hot-loop on a persistent failure.
102
+ process.stderr.write(`[borg-mcp inbox poller] ${err?.message ?? err}\n`);
103
+ await sleep(5000);
104
+ }
105
+ }
106
+ }
107
+ async function appendToInbox(cubeId, droneId, entries, drones, roles) {
108
+ const droneById = new Map(drones.map((d) => [d.id, d]));
109
+ const roleById = new Map(roles.map((r) => [r.id, r]));
110
+ const lines = entries.map((e) => {
111
+ const d = droneById.get(e.drone_id);
112
+ const r = d ? roleById.get(d.role_id) : null;
113
+ const ts = typeof e.created_at === 'string'
114
+ ? new Date(e.created_at).toISOString()
115
+ : new Date(e.created_at).toISOString();
116
+ return `${ts} ${d?.label ?? '?'} (${r?.name ?? '?'}): ${e.message}`;
117
+ });
118
+ const p = inboxPathForDrone(cubeId, droneId);
119
+ await fs.mkdir(path.dirname(p), { recursive: true });
120
+ await fs.appendFile(p, lines.join('\n') + '\n', 'utf-8');
121
+ }
122
+ function sleep(ms) {
123
+ return new Promise((resolve) => setTimeout(resolve, ms));
124
+ }
125
+ //# sourceMappingURL=inbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,KAAK,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kCAAkC,GAAG,EAAE,OAAO,IAAI,GAAG,IAAI,CAC1D,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,mEAAmE;IACnE,gEAAgE;IAChE,4DAA4D;IAC5D,IAAI,QAAQ,GAAW,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChD,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,mEAAmE;IACnE,sEAAsE;IACtE,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,GAAG,CAAC;IAEvB,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;QACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,iEAAiE;YACjE,eAAe;YACf,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QAED,mEAAmE;QACnE,+DAA+D;QAC/D,YAAY;QACZ,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;YACpC,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;YAC9B,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;YAC5B,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACpC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QACvB,CAAC;QAED,IAAI,CAAC;YACH,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,eAAe,CACtD,MAAM,CAAC,YAAY,EACnB,MAAM,CAAC,MAAM,EACb,QAAQ,CACT,CAAC;YACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;gBACnC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE;oBACtC,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;wBAAE,OAAO,KAAK,CAAC;oBACpC,4DAA4D;oBAC5D,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU;wBAAE,OAAO,KAAK,CAAC;oBAC5C,OAAO,IAAI,CAAC;gBACd,CAAC,CAAC,CAAC;gBACH,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,MAAM,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;oBACzE,KAAK,MAAM,CAAC,IAAI,KAAK;wBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBAC5C,OAAO,SAAS,CAAC,MAAM,GAAG,UAAU;wBAAE,SAAS,CAAC,KAAK,EAAE,CAAC;gBAC1D,CAAC;gBACD,6DAA6D;gBAC7D,4DAA4D;gBAC5D,uCAAuC;gBACvC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE,CAC/C,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACxD,CAAC;gBACF,QAAQ,GAAG,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ;oBAC9C,CAAC,CAAC,MAAM,CAAC,UAAU;oBACnB,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;YAChD,CAAC;YACD,gEAAgE;YAChE,gEAAgE;QAClE,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,iEAAiE;YACjE,oCAAoC;YACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2BAA2B,GAAG,EAAE,OAAO,IAAI,GAAG,IAAI,CACnD,CAAC;YACF,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,MAAc,EACd,OAAe,EACf,OAAc,EACd,MAAa,EACb,KAAY;IAEZ,MAAM,SAAS,GAAG,IAAI,GAAG,CAAc,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAc,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QAC9B,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7C,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ;YACzC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE;YACtC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QACzC,OAAO,GAAG,EAAE,IAAI,CAAC,EAAE,KAAK,IAAI,GAAG,KAAK,CAAC,EAAE,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IACtE,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/dist/index.d.ts CHANGED
@@ -6,7 +6,9 @@
6
6
  * 1. Connects to Claude Code via stdio transport
7
7
  * 2. Authenticates via Google OAuth device flow
8
8
  * 3. Proxies MCP tools to remote server at api.borgmcp.ai
9
- * 4. Provides borg:regen tool for centralized context retrieval
9
+ * 4. Provides the borg: cube tool surface (assimilate / cube / role /
10
+ * roster / read-log) so Claude can act as a Drone in a hive of
11
+ * collaborating sessions.
10
12
  */
11
13
  export {};
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}