borgmcp 2.16.1 → 3.0.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.
Files changed (135) hide show
  1. package/README.md +5 -3
  2. package/dist/assimilate-cmd.js +43 -43
  3. package/dist/assimilate-cmd.js.map +1 -1
  4. package/dist/assimilate-deps.js +2 -2
  5. package/dist/assimilate-deps.js.map +1 -1
  6. package/dist/assimilate-guard.js +4 -4
  7. package/dist/assimilate-guard.js.map +1 -1
  8. package/dist/assimilate-welcome.js +2 -2
  9. package/dist/assimilate-welcome.js.map +1 -1
  10. package/dist/bare-launch-menu.d.ts +5 -5
  11. package/dist/bare-launch-menu.d.ts.map +1 -1
  12. package/dist/bare-launch-menu.js +2 -2
  13. package/dist/bare-launch-menu.js.map +1 -1
  14. package/dist/claude.d.ts +0 -2
  15. package/dist/claude.d.ts.map +1 -1
  16. package/dist/claude.js +5 -18
  17. package/dist/claude.js.map +1 -1
  18. package/dist/cleanup-cmd.d.ts +1 -1
  19. package/dist/cleanup-cmd.d.ts.map +1 -1
  20. package/dist/cleanup-cmd.js +3 -3
  21. package/dist/cleanup-cmd.js.map +1 -1
  22. package/dist/cli-help.d.ts +1 -2
  23. package/dist/cli-help.d.ts.map +1 -1
  24. package/dist/cli-help.js +15 -24
  25. package/dist/cli-help.js.map +1 -1
  26. package/dist/cli-platform.d.ts +4 -0
  27. package/dist/cli-platform.d.ts.map +1 -1
  28. package/dist/cli-platform.js +29 -10
  29. package/dist/cli-platform.js.map +1 -1
  30. package/dist/cubes.d.ts.map +1 -1
  31. package/dist/cubes.js +30 -12
  32. package/dist/cubes.js.map +1 -1
  33. package/dist/docs-sections.js +1 -1
  34. package/dist/docs-sections.js.map +1 -1
  35. package/dist/drone-lifecycle.d.ts.map +1 -1
  36. package/dist/drone-lifecycle.js +3 -3
  37. package/dist/drone-lifecycle.js.map +1 -1
  38. package/dist/drone-management.js +1 -1
  39. package/dist/drone-management.js.map +1 -1
  40. package/dist/evict-drone.js +2 -2
  41. package/dist/evict-drone.js.map +1 -1
  42. package/dist/first-run-server.d.ts.map +1 -1
  43. package/dist/first-run-server.js +4 -1
  44. package/dist/first-run-server.js.map +1 -1
  45. package/dist/foreign-path-reminder.js +1 -1
  46. package/dist/foreign-path-reminder.js.map +1 -1
  47. package/dist/inbox-monitor.js +2 -2
  48. package/dist/inbox-monitor.js.map +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/launch-all-cmd.js +12 -12
  52. package/dist/launch-all-cmd.js.map +1 -1
  53. package/dist/launch-all-discovery.js +1 -1
  54. package/dist/launch-all-discovery.js.map +1 -1
  55. package/dist/lifecycle-log-guard.d.ts.map +1 -1
  56. package/dist/lifecycle-log-guard.js +12 -3
  57. package/dist/lifecycle-log-guard.js.map +1 -1
  58. package/dist/opencode-seat-identity.js +5 -5
  59. package/dist/opencode-seat-identity.js.map +1 -1
  60. package/dist/regen-format.d.ts.map +1 -1
  61. package/dist/regen-format.js +2 -1
  62. package/dist/regen-format.js.map +1 -1
  63. package/dist/remote-client.d.ts.map +1 -1
  64. package/dist/remote-client.js +10 -5
  65. package/dist/remote-client.js.map +1 -1
  66. package/dist/repository-cube-init.js +1 -1
  67. package/dist/repository-cube-init.js.map +1 -1
  68. package/dist/reset-local-seat-cmd.d.ts +2 -2
  69. package/dist/reset-local-seat-cmd.d.ts.map +1 -1
  70. package/dist/reset-local-seat-cmd.js +17 -17
  71. package/dist/reset-local-seat-cmd.js.map +1 -1
  72. package/dist/seat-reattach-guard.js +1 -1
  73. package/dist/seat-reattach-guard.js.map +1 -1
  74. package/dist/seat-store.d.ts +1 -1
  75. package/dist/seat-store.js +6 -6
  76. package/dist/seat-store.js.map +1 -1
  77. package/dist/seats.js +1 -1
  78. package/dist/seats.js.map +1 -1
  79. package/dist/server-handshake.js +1 -1
  80. package/dist/server-handshake.js.map +1 -1
  81. package/dist/stream-status.js +3 -3
  82. package/dist/stream-status.js.map +1 -1
  83. package/dist/tool-manifest.js +1 -1
  84. package/dist/tool-manifest.js.map +1 -1
  85. package/dist/unknown-subcommand.d.ts +1 -1
  86. package/dist/unknown-subcommand.d.ts.map +1 -1
  87. package/dist/unknown-subcommand.js +1 -2
  88. package/dist/unknown-subcommand.js.map +1 -1
  89. package/dist/worktree-lifecycle.d.ts +0 -16
  90. package/dist/worktree-lifecycle.d.ts.map +1 -1
  91. package/dist/worktree-lifecycle.js +0 -38
  92. package/dist/worktree-lifecycle.js.map +1 -1
  93. package/docs/EXTRACTION_PROVENANCE.md +5 -3
  94. package/docs/LOCAL_SERVER.md +2 -2
  95. package/docs/SEAT_LIFECYCLE.md +19 -19
  96. package/package.json +1 -1
  97. package/src/assimilate-cmd.ts +43 -43
  98. package/src/assimilate-deps.ts +2 -2
  99. package/src/assimilate-guard.ts +4 -4
  100. package/src/assimilate-welcome.ts +2 -2
  101. package/src/bare-launch-menu.ts +6 -6
  102. package/src/claude.ts +8 -21
  103. package/src/cleanup-cmd.ts +4 -4
  104. package/src/cli-help.ts +15 -27
  105. package/src/cli-platform.ts +42 -10
  106. package/src/cubes.ts +29 -14
  107. package/src/docs-sections.ts +1 -1
  108. package/src/drone-lifecycle.ts +3 -3
  109. package/src/drone-management.ts +1 -1
  110. package/src/evict-drone.ts +2 -2
  111. package/src/first-run-server.ts +4 -1
  112. package/src/foreign-path-reminder.ts +1 -1
  113. package/src/inbox-monitor.ts +2 -2
  114. package/src/index.ts +1 -1
  115. package/src/launch-all-cmd.ts +12 -12
  116. package/src/launch-all-discovery.ts +1 -1
  117. package/src/lifecycle-log-guard.ts +15 -3
  118. package/src/opencode-seat-identity.ts +5 -5
  119. package/src/regen-format.ts +2 -1
  120. package/src/remote-client.ts +12 -6
  121. package/src/repository-cube-init.ts +1 -1
  122. package/src/reset-local-seat-cmd.ts +17 -17
  123. package/src/seat-reattach-guard.ts +1 -1
  124. package/src/seat-store.ts +6 -6
  125. package/src/seats.ts +1 -1
  126. package/src/server-handshake.ts +1 -1
  127. package/src/stream-status.ts +3 -3
  128. package/src/tool-manifest.ts +1 -1
  129. package/src/unknown-subcommand.ts +1 -2
  130. package/src/worktree-lifecycle.ts +0 -45
  131. package/dist/sync.d.ts +0 -129
  132. package/dist/sync.d.ts.map +0 -1
  133. package/dist/sync.js +0 -286
  134. package/dist/sync.js.map +0 -1
  135. package/src/sync.ts +0 -363
@@ -17,7 +17,7 @@ Only two seat states are persisted:
17
17
  `revoked`, `superseded`, and `evicted` are server verdicts, not values written
18
18
  to `seats.json`. Revocation and supersession do not silently delete local
19
19
  state. An authoritative eviction is the sole verdict that permits the terminal
20
- assimilation flow to replace a rejected seat. `borg reset-local-seat` is the
20
+ assimilation flow to replace a rejected seat. `borg reset-local-connection` is the
21
21
  explicit offline deletion path.
22
22
 
23
23
  Seat sessions do not expire. A legacy `expiresAt` field is ignored, and current
@@ -26,11 +26,11 @@ invitation can expire; recovery is to request a new invitation. An expired
26
26
  stream cursor is also separate from seat identity and is recovered by resetting
27
27
  the cursor and reconnecting.
28
28
 
29
- The whole seat store fails closed if any persisted record is malformed. The
29
+ The whole private store fails closed if any persisted record is malformed. The
30
30
  store reader raises:
31
31
 
32
32
  ```text
33
- Borg seat store is malformed or has an unsupported version; refusing to read it
33
+ Borg private store is malformed or has an unsupported version; refusing to read it
34
34
  ```
35
35
 
36
36
  The file is not rewritten and no bearer material is included in the error.
@@ -74,7 +74,7 @@ probes it. A live seat is reattached with the identical bearer; its saved role
74
74
  is authoritative, and no new drone is minted. The success line is:
75
75
 
76
76
  ```text
77
- re-attached to existing seat <drone-label> (same session, no new drone minted)
77
+ re-attached as <drone-label> (same session, no new drone minted)
78
78
  ```
79
79
 
80
80
  Before relaunch, Borg checks the seat's inbox-monitor PID. A live holder refuses
@@ -82,7 +82,7 @@ the relaunch, names its PID, and gives both the safe fresh-worktree path and the
82
82
  explicit one-time override:
83
83
 
84
84
  ```text
85
- This worktree's Borg seat already has a live session (inbox monitor pid <pid>).
85
+ This worktree's Borg drone already has a live session (inbox monitor pid <pid>).
86
86
  No agent was launched. Stop the existing session or use a fresh worktree with `borg assimilate --worktree <name>`. If the live monitor is wedged, override once with `borg assimilate --here --force`.
87
87
  ```
88
88
 
@@ -100,7 +100,7 @@ If a second monitor reaches the singleton after launch, it exits successfully
100
100
  without tailing and prints:
101
101
 
102
102
  ```text
103
- borg-inbox-monitor: seat inbox "<path>" is already monitored by a live instance (pid <pid>); yielding — another session likely holds this seat.
103
+ borg-inbox-monitor: inbox "<path>" is already monitored by a live instance (pid <pid>); yielding — another session likely holds this worktree's connection.
104
104
  ```
105
105
 
106
106
  The existing monitor remains the sole inbox reader.
@@ -114,7 +114,7 @@ worktree to another cube.
114
114
  With no saved identity, it returns:
115
115
 
116
116
  ```text
117
- ◼ This session has no drone seat for this worktree, and in-session borg_assimilate is re-attach-only (it never creates seats — gh#780). To create a seat for cube "<cube>", run `borg assimilate` in a terminal — it spawns the worktree, persists the identity, and launches the agent in one step.
117
+ ◼ This session has no drone for this worktree, and in-session borg_assimilate is re-attach-only (it never creates drones — gh#780). To create a drone for cube "<cube>", run `borg assimilate` in a terminal — it spawns the worktree, persists the identity, and launches the agent in one step.
118
118
  ```
119
119
 
120
120
  For another cube, it returns:
@@ -129,15 +129,15 @@ A successful in-session re-attach begins:
129
129
  # Re-attached to cube: <cube>
130
130
 
131
131
  **Drone label:** <drone-label>
132
- **Seat:** existing identity reused — no new drone minted (gh#780)
132
+ **Drone:** existing identity reused — no new drone minted (gh#780)
133
133
  ```
134
134
 
135
135
  If the server no longer accepts the saved seat, the tool fails rather than
136
136
  reminting:
137
137
 
138
138
  ```text
139
- ◼ Re-attach failed — this worktree's saved seat is unreachable (likely evicted or its session was revoked). Server said: <server-error>
140
- Recover by running `borg assimilate` in a terminal to create a fresh seat; in-session borg_assimilate never re-mints (gh#780).
139
+ ◼ Re-attach failed — this worktree's saved connection is unreachable (likely evicted or its session was revoked). Server said: <server-error>
140
+ Recover by running `borg assimilate` in a terminal to create a fresh drone; in-session borg_assimilate never re-mints (gh#780).
141
141
  ```
142
142
 
143
143
  ## Terminal verdicts and recovery
@@ -149,12 +149,12 @@ does not mutate the saved seat. The exact output is one of:
149
149
 
150
150
  ```text
151
151
  Local session was revoked.
152
- Next: run borg reset-local-seat, then borg assimilate --host <server> --enroll.
152
+ Next: run borg reset-local-connection, then borg assimilate --host <server> --enroll.
153
153
  ```
154
154
 
155
155
  ```text
156
156
  Local session was superseded by a newer enrollment.
157
- Next: run borg reset-local-seat, then borg assimilate --host <server> --enroll.
157
+ Next: run borg reset-local-connection, then borg assimilate --host <server> --enroll.
158
158
  ```
159
159
 
160
160
  Run the named offline reset, obtain a new invitation, and enroll again.
@@ -171,7 +171,7 @@ If the attach itself returns the eviction after re-attach began, Borg does not
171
171
  claim recovery. It prints:
172
172
 
173
173
  ```text
174
- This worktree's saved seat on <server> was evicted. Remove this worktree, or from a fresh worktree run `borg assimilate --host <server>`.
174
+ This worktree's drone on <server> was evicted. Remove this worktree, or from a fresh worktree run `borg assimilate --host <server>`.
175
175
  ```
176
176
 
177
177
  The stream path also treats typed `DRONE_EVICTED` as terminal: it marks that
@@ -188,20 +188,20 @@ The enrollment invitation for <server> was rejected or expired. Ask the server o
188
188
 
189
189
  ## Offline reset
190
190
 
191
- `borg reset-local-seat` clears only the current worktree's saved credential and
191
+ `borg reset-local-connection` clears only the current worktree's saved credential and
192
192
  cube binding. It makes no network call, revokes nothing server-side, and leaves
193
193
  the server, trust anchor, cube, and sibling worktrees unchanged.
194
194
 
195
195
  Its shipped help describes the command as:
196
196
 
197
197
  ```text
198
- borg reset-local-seat (borgmcp <version>) — clear ONLY this worktree's saved local seat
198
+ borg reset-local-connection (borgmcp <version>) — clear ONLY this worktree's saved connection to its cube
199
199
 
200
200
  Usage:
201
- borg reset-local-seat Reset this worktree's saved seat (TTY confirms [y/N])
202
- borg reset-local-seat --host <host> No-op unless this worktree's seat is on <host>
203
- borg reset-local-seat --yes Reset without a prompt (required when non-interactive)
204
- borg reset-local-seat --help Show this help
201
+ borg reset-local-connection Reset this worktree's saved connection (TTY confirms [y/N])
202
+ borg reset-local-connection --host <host> No-op unless this worktree connects to <host>
203
+ borg reset-local-connection --yes Reset without a prompt (required when non-interactive)
204
+ borg reset-local-connection --help Show this help
205
205
  ```
206
206
 
207
207
  The command snapshots the exact binding and token-safe bearer observation,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "2.16.1",
3
+ "version": "3.0.2",
4
4
  "description": "Coordinate AI coding agents in shared cubes. Works with Claude Code, Codex, and OpenCode.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -615,7 +615,7 @@ function reportServerFailure(
615
615
  );
616
616
  return 1;
617
617
  }
618
- if (/seat store lock file .* is stale/i.test(message)) {
618
+ if (/(?:private|seat) store lock file .* is stale/i.test(message)) {
619
619
  // RULED option (b): a lock whose recorded holder is DEAD (or whose payload is
620
620
  // corrupt) is NEVER auto-removed. Surface the fail-closed guidance verbatim —
621
621
  // it already names the exact lockfile path and the delete-only-if-no-borg
@@ -626,16 +626,16 @@ function reportServerFailure(
626
626
  );
627
627
  return 1;
628
628
  }
629
- if (/(?:seat|credential) store is busy/i.test(message)) {
629
+ if (/(?:private|seat|credential) store is busy/i.test(message)) {
630
630
  deps.stderr(
631
- `Borg's local seat store is busy for ${apiUrl} because another Borg process is ` +
632
- `creating or resuming saved seat state. Wait for it to finish, then rerun ${retryCommand}.\n`,
631
+ `Borg's private store is busy for ${apiUrl} because another Borg process is ` +
632
+ `creating or resuming saved connection state. Wait for it to finish, then rerun ${retryCommand}.\n`,
633
633
  );
634
634
  return 1;
635
635
  }
636
- if (/(?:local )?seat store|(?:secure )?credential (?:store|storage)/i.test(message)) {
636
+ if (/(?:(?:local )?(?:private|seat) store|(?:secure )?credential (?:store|storage))/i.test(message)) {
637
637
  deps.stderr(
638
- `Borg could not access its local seat store for ${apiUrl}. ` +
638
+ `Borg could not access its private store for ${apiUrl}. ` +
639
639
  `Ensure its directory on this machine is readable and writable, then rerun ${retryCommand}.\n`,
640
640
  );
641
641
  return 1;
@@ -680,7 +680,7 @@ function reportServerFailure(
680
680
  }
681
681
 
682
682
  function resetLocalSeatCommand(apiUrl: string): string {
683
- return `\`borg reset-local-seat --host ${apiUrl}\``;
683
+ return `\`borg reset-local-connection --host ${apiUrl}\``;
684
684
  }
685
685
 
686
686
  // Pin-matched terminal session diagnosis. This is intentionally output-only:
@@ -699,7 +699,7 @@ function diagnoseSessionTermination(
699
699
  : `borg assimilate --host ${apiUrl} --enroll`;
700
700
  deps.stderr(
701
701
  `${message}\n` +
702
- `Next: run borg reset-local-seat, then ${recovery}.\n`,
702
+ `Next: run borg reset-local-connection, then ${recovery}.\n`,
703
703
  );
704
704
  return 1;
705
705
  }
@@ -1287,9 +1287,9 @@ export async function runAssimilate(
1287
1287
  }
1288
1288
  } else {
1289
1289
  deps.stderr(
1290
- `This worktree has saved seat metadata for ${authority.apiUrl}, but its local session ` +
1291
- 'credential could not be loaded from the local seat store. No new seat was created. Run ' +
1292
- `${resetLocalSeatCommand(authority.apiUrl)} to clear this worktree's saved seat, then ` +
1290
+ `This worktree has a saved connection to ${authority.apiUrl}, but its local session ` +
1291
+ 'credential could not be loaded from the private store. No drone was created. Run ' +
1292
+ `${resetLocalSeatCommand(authority.apiUrl)} to clear this worktree's saved connection, then ` +
1293
1293
  `ask the operator for a new invitation and rerun ${localAssimilateCommand(authority.apiUrl, true)}.\n`,
1294
1294
  );
1295
1295
  return 1;
@@ -1301,8 +1301,8 @@ export async function runAssimilate(
1301
1301
  existing.serverTrustIdentity !== auth.serverTrustIdentity)
1302
1302
  ) {
1303
1303
  deps.stderr(
1304
- `This worktree's saved seat does not match ${authority.apiUrl}. ` +
1305
- 'No new seat was created. Restore the expected server identity or use a fresh ' +
1304
+ `This worktree's saved connection does not match ${authority.apiUrl}. ` +
1305
+ 'No drone was created. Restore the expected server identity or use a fresh ' +
1306
1306
  `worktree, then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`,
1307
1307
  );
1308
1308
  return 1;
@@ -1323,7 +1323,7 @@ export async function runAssimilate(
1323
1323
  );
1324
1324
  // Canonical rotated/revoked path: a pin-matched 401 on THIS worktree's
1325
1325
  // saved bearer. PURE DIAGNOSIS — attach never mutates local state on a
1326
- // rejection; it points at the offline `borg reset-local-seat` command.
1326
+ // rejection; it points at the offline `borg reset-local-connection` command.
1327
1327
  // Distinct from unreachable/404/5xx/trust-mismatch, which stay
1328
1328
  // indeterminate below.
1329
1329
  if (status === 'revoked') {
@@ -1338,7 +1338,7 @@ export async function runAssimilate(
1338
1338
  // The saved SESSION bearer was rejected WITHOUT the typed takeover code
1339
1339
  // (a bare/other 401). Non-destructive: re-enroll, never a seat reset.
1340
1340
  deps.stderr(
1341
- `The saved enrollment for ${authority.apiUrl} was rejected. No new seat was created ` +
1341
+ `The saved enrollment for ${authority.apiUrl} was rejected. No drone was created ` +
1342
1342
  `and nothing was changed. Re-enroll with ${localAssimilateCommand(authority.apiUrl, true)} ` +
1343
1343
  'from the operator’s terminal.\n',
1344
1344
  );
@@ -1349,7 +1349,7 @@ export async function runAssimilate(
1349
1349
  // fix it — verify this is the expected server / re-initialization.
1350
1350
  deps.stderr(
1351
1351
  `Borg could not verify the expected server identity for ${authority.apiUrl}. ` +
1352
- 'No new seat was created. Verify that this is the expected server; if it was ' +
1352
+ 'No drone was created. Verify that this is the expected server; if it was ' +
1353
1353
  're-initialized, restore the expected identity, then rerun ' +
1354
1354
  `${localAssimilateCommand(authority.apiUrl)}.\n`,
1355
1355
  );
@@ -1361,7 +1361,7 @@ export async function runAssimilate(
1361
1361
  // fix it; align versions. Non-destructive: no seat created, nothing reset.
1362
1362
  deps.stderr(
1363
1363
  `Borg reached ${authority.apiUrl} but it did not recognize this worktree's drone ` +
1364
- 'endpoint — the client and server versions are likely incompatible. No new seat ' +
1364
+ 'endpoint — the client and server versions are likely incompatible. No drone ' +
1365
1365
  'was created and nothing was changed. Update the Borg client and/or server so ' +
1366
1366
  `their versions match, then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`,
1367
1367
  );
@@ -1372,7 +1372,7 @@ export async function runAssimilate(
1372
1372
  // check the server, then retry. Non-destructive.
1373
1373
  deps.stderr(
1374
1374
  `Borg reached ${authority.apiUrl} but it returned a server error while verifying ` +
1375
- "this worktree's saved seat. No new seat was created. Check the server (its logs / " +
1375
+ "this worktree's saved connection. No drone was created. Check the server (its logs / " +
1376
1376
  `\`borg-mcp-server start\`), then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`,
1377
1377
  );
1378
1378
  return 1;
@@ -1381,16 +1381,16 @@ export async function runAssimilate(
1381
1381
  // CR5: transport failure / timeout (unreachable) or a genuinely ambiguous
1382
1382
  // failure (indeterminate) — both transient. Start or restart the server.
1383
1383
  deps.stderr(
1384
- `Borg could not verify this worktree's saved seat on ${authority.apiUrl}. ` +
1385
- 'No new seat was created. Start or restart the server with ' +
1384
+ `Borg could not verify this worktree's saved connection to ${authority.apiUrl}. ` +
1385
+ 'No drone was created. Start or restart the server with ' +
1386
1386
  `\`borg-mcp-server start\`, then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`,
1387
1387
  );
1388
1388
  return 1;
1389
1389
  }
1390
1390
  if (status === 'live' && !savedLocalRole) {
1391
1391
  deps.stderr(
1392
- `Borg verified this worktree's saved seat on ${authority.apiUrl}, but its saved ` +
1393
- 'role is unavailable. No new seat was created. Ask the server operator to restore ' +
1392
+ `Borg verified this worktree's saved connection to ${authority.apiUrl}, but its saved ` +
1393
+ 'role is unavailable. No drone was created. Ask the server operator to restore ' +
1394
1394
  `the role, then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`,
1395
1395
  );
1396
1396
  return 1;
@@ -1401,8 +1401,8 @@ export async function runAssimilate(
1401
1401
  } else if (existing && args.flags.here) {
1402
1402
  if (existing.serverTrustIdentity !== undefined || existing.apiUrl !== auth.apiUrl) {
1403
1403
  deps.stderr(
1404
- 'This worktree\'s saved seat belongs to a different Borg authority. ' +
1405
- 'No new seat was created; use a fresh worktree.\n',
1404
+ 'This worktree\'s saved connection belongs to a different Borg authority. ' +
1405
+ 'No drone was created; use a fresh worktree.\n',
1406
1406
  );
1407
1407
  return 1;
1408
1408
  }
@@ -1583,7 +1583,7 @@ export async function runAssimilate(
1583
1583
  // a non-reattach DroneEvictedError falls through to the generic message.
1584
1584
  if (err instanceof DroneEvictedError && reattachPriorId != null) {
1585
1585
  deps.stderr(
1586
- `This worktree's saved seat on ${authority.apiUrl} was evicted. ` +
1586
+ `This worktree's drone on ${authority.apiUrl} was evicted. ` +
1587
1587
  `Remove this worktree, or from a fresh worktree run ` +
1588
1588
  `${localAssimilateCommand(authority.apiUrl)}.\n`,
1589
1589
  );
@@ -1593,7 +1593,7 @@ export async function runAssimilate(
1593
1593
  // Reached only after a successful pinned-TLS attach, so it is pin-matched by
1594
1594
  // construction — a pin mismatch throws a distinct trust error and never
1595
1595
  // enters this branch. Attach mutates NOTHING; it recommends the offline
1596
- // `borg reset-local-seat` command.
1596
+ // `borg reset-local-connection` command.
1597
1597
  if (err instanceof BorgServerError && reattachPriorId != null) {
1598
1598
  if (err.code === 'SESSION_REVOKED') {
1599
1599
  return diagnoseSessionTermination(deps, authority.apiUrl, 'revoked');
@@ -1616,7 +1616,7 @@ export async function runAssimilate(
1616
1616
  // concurrent offline reset, or a competing enroll). No FS/network mutation
1617
1617
  // happened; never silently recreate.
1618
1618
  deps.stderr(
1619
- `This worktree's saved local seat on ${authority.apiUrl} changed before the attach ` +
1619
+ `This worktree's saved connection to ${authority.apiUrl} changed before the attach ` +
1620
1620
  '(a concurrent reset or enroll); no credential was created or sent and nothing was ' +
1621
1621
  `changed. Re-run ${localAssimilateCommand(authority.apiUrl)} to attach against the ` +
1622
1622
  'current state.\n',
@@ -1641,16 +1641,16 @@ export async function runAssimilate(
1641
1641
  const assignedRole =
1642
1642
  cubeDetail.roles.find((r) => r.id === result.role_id) ?? resolvedRole;
1643
1643
  if (result.result === 'reused') {
1644
- // The seat's existing role is authoritative on an idempotent reattach —
1644
+ // The drone's existing role is authoritative on an idempotent reattach —
1645
1645
  // a role difference is expected, not a grant fallback. The bearer is
1646
1646
  // reused, not rotated: no new drone minted.
1647
1647
  deps.stderr(
1648
- `re-attached to existing seat ${result.drone_label} (same session, no new drone minted)\n`
1648
+ `re-attached as ${result.drone_label} (same session, no new drone minted)\n`
1649
1649
  );
1650
1650
  } else if (assignedRole.id !== resolvedRole.id) {
1651
1651
  deps.stderr(
1652
1652
  `The requested role "${resolvedRole.name}" was unavailable; ` +
1653
- `attached to the "${assignedRole.name}" seat instead.\n`
1653
+ `attached under the "${assignedRole.name}" role instead.\n`
1654
1654
  );
1655
1655
  }
1656
1656
 
@@ -1814,7 +1814,7 @@ export async function runAssimilate(
1814
1814
  }
1815
1815
  deps.stderr(
1816
1816
  `spawned sibling worktree at ${candidate} on branch ${wtBranch} (${startRef}); ` +
1817
- `the original dir keeps its active drone binding — run \`borg reset-local-seat\` there if that binding is stale.\n`
1817
+ `the original dir keeps its active drone binding — run \`borg reset-local-connection\` there if that binding is stale.\n`
1818
1818
  );
1819
1819
  deps.chdir(candidate);
1820
1820
  deps.stderr(renderWorktreeSteeringNote(candidate, wtBranch, projectRoot));
@@ -1903,7 +1903,7 @@ export async function runAssimilate(
1903
1903
  // EXACT prior binding with its live-bearer digest; eviction remint = EXACT ref
1904
1904
  // only, bearer intentionally replaced; fresh/sibling = ABSENT).
1905
1905
  if (result.finalize === undefined || deps.finalizeServerSeat === undefined) {
1906
- deps.stderr('Local Borg server session metadata is incomplete; no seat was saved.\n');
1906
+ deps.stderr('Local Borg server session metadata is incomplete; no connection was saved.\n');
1907
1907
  rollbackWorktree();
1908
1908
  return 1;
1909
1909
  }
@@ -1959,12 +1959,12 @@ export async function runAssimilate(
1959
1959
  if (bindOutcome === 'bound') {
1960
1960
  // The worktree now owns a durable locator (the bound-pending record points
1961
1961
  // here). PRESERVE it. Truthful convergence copy: a rerun FROM here re-sends
1962
- // the identical bearer (no duplicate), and `reset-local-seat` from here now
1962
+ // the identical bearer (no duplicate), and `reset-local-connection` from here now
1963
1963
  // discovers + clears the bound-pending record.
1964
1964
  deps.stderr(
1965
1965
  `This worktree's secure session on ${auth.apiUrl} did not finish activating, but ` +
1966
- 'its resumable seat state was PRESERVED here. This worktree was NOT removed. From ' +
1967
- `here, re-run ${localAssimilateCommand(auth.apiUrl)} to converge (the identical seat ` +
1966
+ 'its resumable connection state was PRESERVED here. This worktree was NOT removed. From ' +
1967
+ `here, re-run ${localAssimilateCommand(auth.apiUrl)} to converge (the identical connection ` +
1968
1968
  `is reused — no duplicate is minted), or run ${resetLocalSeatCommand(auth.apiUrl)} to ` +
1969
1969
  'clear it.\n',
1970
1970
  );
@@ -1977,17 +1977,17 @@ export async function runAssimilate(
1977
1977
  // that can silently create a duplicate server seat (#35).
1978
1978
  const bindFailure =
1979
1979
  bindOutcome === 'missing'
1980
- ? 'the exact pending seat record went missing locally before it could be bound'
1980
+ ? 'the exact pending connection record went missing locally before it could be bound'
1981
1981
  : bindOutcome === 'replaced'
1982
- ? 'the exact pending seat record was replaced locally before it could be bound; the replacement was left untouched'
1982
+ ? 'the exact pending connection record was replaced locally before it could be bound; the replacement was left untouched'
1983
1983
  : bindOutcome === 'threw'
1984
- ? 'the local seat store could not be read or written while preserving the pending seat'
1985
- : 'this client did not receive a pending-seat preservation handle';
1984
+ ? 'the private store could not be read or written while preserving the pending connection'
1985
+ : 'this client did not receive a pending-connection preservation handle';
1986
1986
  deps.stderr(
1987
1987
  `This worktree's secure session on ${auth.apiUrl} did not finish activating: ` +
1988
1988
  `${bindFailure}. The spawned worktree will be removed. No client-only command can ` +
1989
- 'prove reuse or safely clear the possibly accepted server-side seat; ask the server ' +
1990
- 'operator to inspect that seat before retrying.\n',
1989
+ 'prove reuse or safely clear the possibly accepted server-side drone; ask the server ' +
1990
+ 'operator to inspect that drone before retrying.\n',
1991
1991
  );
1992
1992
  rollbackWorktree();
1993
1993
  return 1;
@@ -1997,8 +1997,8 @@ export async function runAssimilate(
1997
1997
  // reset or enroll). The composite scrubbed only our own pending record — no
1998
1998
  // orphan ACTIVE credential — so a just-spawned worktree is safe to remove.
1999
1999
  deps.stderr(
2000
- `This worktree's saved local seat on ${auth.apiUrl} changed during attach ` +
2001
- '(a concurrent reset or enroll); no seat was created and nothing was overwritten. ' +
2000
+ `This worktree's saved connection to ${auth.apiUrl} changed during attach ` +
2001
+ '(a concurrent reset or enroll); no drone was created and nothing was overwritten. ' +
2002
2002
  `Re-run ${localAssimilateCommand(auth.apiUrl)} to attach against the current state.\n`,
2003
2003
  );
2004
2004
  rollbackWorktree();
@@ -474,13 +474,13 @@ export function buildDefaultAssimilateDeps(
474
474
  if (params.remint_invalid_prior) {
475
475
  if (prepared.result !== 'created' || prepared.drone.id === params.prior_drone_id) {
476
476
  await prepared.scrubPending();
477
- throw new Error('Borg server did not remint a fresh seat after eviction');
477
+ throw new Error('Borg server did not remint a fresh drone after eviction');
478
478
  }
479
479
  } else if (prepared.result !== 'reused' || prepared.drone.id !== params.prior_drone_id) {
480
480
  await prepared.scrubPending();
481
481
  throw new BorgServerError(
482
482
  'ATTACH_CONFLICT',
483
- 'Borg server did not reattach the saved seat',
483
+ 'Borg server did not reattach the saved connection',
484
484
  );
485
485
  }
486
486
  }
@@ -48,8 +48,8 @@ export function reattachOnlyRefusal(
48
48
  ): string {
49
49
  if (decision.kind === 'no-identity') {
50
50
  return (
51
- `◼ This session has no drone seat for this worktree, and in-session borg_assimilate is ` +
52
- `re-attach-only (it never creates seats — gh#780). To create a seat for cube ` +
51
+ `◼ This session has no drone for this worktree, and in-session borg_assimilate is ` +
52
+ `re-attach-only (it never creates drones — gh#780). To create a drone for cube ` +
53
53
  `"${requestedCubeName}", run \`borg assimilate\` in a terminal — it spawns the worktree, ` +
54
54
  `persists the identity, and launches the agent in one step.`
55
55
  );
@@ -76,9 +76,9 @@ export function reattachFailureMessage(error: {
76
76
  }): string {
77
77
  const detail = error.message ?? String(error);
78
78
  return (
79
- `◼ Re-attach failed — this worktree's saved seat is unreachable (likely evicted or its ` +
79
+ `◼ Re-attach failed — this worktree's saved connection is unreachable (likely evicted or its ` +
80
80
  `session was revoked). Server said: ${detail}\n` +
81
- `Recover by running \`borg assimilate\` in a terminal to create a fresh seat; ` +
81
+ `Recover by running \`borg assimilate\` in a terminal to create a fresh drone; ` +
82
82
  `in-session borg_assimilate never re-mints (gh#780).`
83
83
  );
84
84
  }
@@ -31,8 +31,8 @@ export function renderAssimilationWelcome(
31
31
  return [
32
32
  `${check} Attached \`${droneLabel}\` to \`${roleName}\` in cube \`${cubeName}\`.`,
33
33
  ``,
34
- `In the launched agent, run \`borg_whoami\` and \`borg_roster\` to verify the seat`,
35
- `and begin coordinating.`,
34
+ `In the launched agent, run \`borg_whoami\` and \`borg_roster\``,
35
+ `to verify the connection and begin coordinating.`,
36
36
  `Launch future sessions in this repository with \`borg\`.`,
37
37
  ...teammateLines,
38
38
  ``,
@@ -4,13 +4,13 @@
4
4
  * When `borg` is run with NO arguments in a TTY, offer a small launch selector
5
5
  * instead of launching immediately:
6
6
  * 1. Launch (default) — the configured agent (Enter selects).
7
- * 2. Launch with <other> instead — the OTHER installed agent, ONE-SHOT
7
+ * 2. Launch with <other> instead — the OTHER configured agent, ONE-SHOT
8
8
  * (does NOT persist the preference).
9
9
  * 3. Launch all — runLaunchAll for the active cube.
10
10
  *
11
11
  * The option-set, the selection→action mapping, and the show/collapse decision
12
12
  * are pure functions so they're unit-testable without a real TTY. claude.ts
13
- * main() is thin glue: it computes the inputs (default cli, other-installed cli,
13
+ * main() is thin glue: it computes the inputs (default cli, other-configured cli,
14
14
  * launch-all targets), gates on shouldShowLaunchMenu, runs the orchestrator with
15
15
  * the real readline prompt, then dispatches the returned action.
16
16
  *
@@ -33,8 +33,8 @@ export interface LaunchMenuOption {
33
33
  export interface LaunchMenuInputs {
34
34
  /** The configured/resolved current agent (option 1). */
35
35
  defaultCli: BorgCli;
36
- /** All installed agents that are NOT the default, in display order. */
37
- otherInstalledClis: BorgCli[];
36
+ /** All configured agents that are NOT the default, in display order. */
37
+ otherConfiguredClis: BorgCli[];
38
38
  /** True iff there's an active cube with >=1 discoverable drone (option 3). */
39
39
  hasLaunchAllTargets: boolean;
40
40
  }
@@ -44,7 +44,7 @@ export interface LaunchMenuInputs {
44
44
  *
45
45
  * This callback deliberately runs after the one-shot menu choice. A bare
46
46
  * launch may resolve one default CLI first and then launch a different
47
- * installed CLI for this invocation; only the final CLI may be self-healed.
47
+ * configured CLI for this invocation; only the final CLI may be self-healed.
48
48
  */
49
49
  export function configureSelectedLaunchCli(
50
50
  defaultCli: BorgCli,
@@ -105,7 +105,7 @@ export function buildLaunchMenuOptions(inputs: LaunchMenuInputs): LaunchMenuOpti
105
105
  action: { kind: 'launch', cli: inputs.defaultCli },
106
106
  },
107
107
  ];
108
- for (const cli of inputs.otherInstalledClis) {
108
+ for (const cli of inputs.otherConfiguredClis) {
109
109
  options.push({
110
110
  key: String(options.length + 1),
111
111
  label: `Launch with ${PRETTY[cli]} (one-shot)`,
package/src/claude.ts CHANGED
@@ -12,8 +12,6 @@
12
12
  * borg setup → Re-route to the setup wizard
13
13
  * borg spawn <name> → Create a sibling git worktree + launch a
14
14
  * fresh drone inside it (see spawn.ts)
15
- * borg sync → Advance the current worktree across the 5
16
- * lifecycle states (see sync.ts, gh#33)
17
15
  * borg server <cmd> → Forward a lifecycle command to borg-mcp-server
18
16
  */
19
17
 
@@ -31,7 +29,6 @@ import { handleVersionFlag, getPackageVersion } from './version.js';
31
29
  import { clientSubcommandHelpText, topLevelHelpText } from './cli-help.js';
32
30
  import { runSpawn } from './spawn.js';
33
31
  import { buildClaudeLaunchArgs } from './claude-launch-args.js';
34
- import { parseSyncArgs, runSync } from './sync.js';
35
32
  import { parseCleanupArgs, runCleanup } from './cleanup-cmd.js';
36
33
  import { parseAssimilateArgs } from './parse-assimilate-args.js';
37
34
  import { runAssimilate } from './assimilate-cmd.js';
@@ -58,7 +55,7 @@ import {
58
55
  import { setTerminalTitle } from './terminal-title.js';
59
56
  import { initConsolePrefix, consolePrefix } from './console-prefix.js';
60
57
  import { initDebugFromArgv } from './debug.js';
61
- import { defaultCliChoiceDeps, detectCliAvailability, installedCliNames, parseCliFlag, resolveCliChoice } from './cli-platform.js';
58
+ import { configuredCliNames, defaultCliChoiceDeps, detectCliAvailability, detectCliConfiguration, parseCliFlag, resolveCliChoice } from './cli-platform.js';
62
59
  import { prepareCodexRemoteLaunch, resolveCodexLaunchCwd, withCodexCwdArg, defaultCodexRemoteDeps, checkCodexBridgeHealthy } from './codex-remote.js';
63
60
  import {
64
61
  BORG_CODEX_REMOTE_WAKE_ENV,
@@ -216,11 +213,11 @@ async function main() {
216
213
  const code = await runAssimilateEntry(process.argv.slice(3));
217
214
  process.exit(code);
218
215
  }
219
- if (process.argv[2] === 'reset-local-seat') {
216
+ if (process.argv[2] === 'reset-local-connection') {
220
217
  const parsed = parseResetLocalSeatArgs(process.argv.slice(3));
221
218
  if (!parsed.ok) {
222
219
  process.stderr.write(
223
- chalk.red(`${consolePrefix()}◼ borg reset-local-seat: ${parsed.error}\n`)
220
+ chalk.red(`${consolePrefix()}◼ borg reset-local-connection: ${parsed.error}\n`)
224
221
  );
225
222
  process.stderr.write(`Run \`borg --help\` for usage.\n`);
226
223
  process.exit(1);
@@ -254,18 +251,6 @@ async function main() {
254
251
  const code = await runSpawn();
255
252
  process.exit(code);
256
253
  }
257
- if (process.argv[2] === 'sync') {
258
- const parsed = parseSyncArgs(process.argv.slice(3));
259
- if (!parsed.ok) {
260
- process.stderr.write(
261
- chalk.red(`${consolePrefix()}◼ borg sync: ${parsed.error}\n`)
262
- );
263
- process.stderr.write(`Run \`borg --help\` for usage.\n`);
264
- process.exit(1);
265
- }
266
- const code = await runSync({}, parsed.options);
267
- process.exit(code);
268
- }
269
254
  if (process.argv[2] === 'cleanup') {
270
255
  const parsed = parseCleanupArgs(process.argv.slice(3));
271
256
  if (!parsed.ok) {
@@ -372,10 +357,12 @@ async function main() {
372
357
  stdoutIsTTY,
373
358
  })
374
359
  ) {
375
- const otherInstalledClis =
376
- installedCliNames(detectCliAvailability()).filter((c) => c !== cli);
360
+ const otherConfiguredClis = configuredCliNames(
361
+ detectCliAvailability(),
362
+ detectCliConfiguration(),
363
+ ).filter((c) => c !== cli);
377
364
  const action = await runBareLaunchMenu(
378
- { defaultCli: cli, otherInstalledClis, hasLaunchAllTargets: await hasLaunchAllTargets() },
365
+ { defaultCli: cli, otherConfiguredClis, hasLaunchAllTargets: await hasLaunchAllTargets() },
379
366
  prompt
380
367
  );
381
368
  if (action.kind === 'launch-all') {
@@ -74,7 +74,7 @@ export type CleanupReason =
74
74
  | 'SURVIVES-live'
75
75
  | 'SURVIVES-self'
76
76
  | 'UNKNOWN-indeterminate'
77
- | 'UNKNOWN-no-seat'
77
+ | 'UNKNOWN-no-connection'
78
78
  | 'LEGACY-manual-review';
79
79
 
80
80
  export interface CleanupRow {
@@ -350,7 +350,7 @@ async function classifyWorktree(
350
350
  case 'revoked':
351
351
  return { reason: 'SURVIVES-revoked', detail: 'session revoked — recoverable via re-enroll, never delete' };
352
352
  case 'live':
353
- return { reason: 'SURVIVES-live', detail: 'seat resolves (drone alive)' };
353
+ return { reason: 'SURVIVES-live', detail: 'connection resolves (drone alive)' };
354
354
  case 'indeterminate':
355
355
  default:
356
356
  return {
@@ -431,7 +431,7 @@ export async function buildCleanupReport(
431
431
  // sibling → REPORT for manual review, never auto-prune (S5). No seat →
432
432
  // an arbitrary user worktree we never touch or list.
433
433
  if (seat) {
434
- rows.push({ worktreePath: wt, wtBranch: null, reason: 'LEGACY-manual-review', detail: 'borg seat outside worktreesHome (pre-gh#556 sibling)' });
434
+ rows.push({ worktreePath: wt, wtBranch: null, reason: 'LEGACY-manual-review', detail: 'borg connection outside worktreesHome (pre-gh#556 sibling)' });
435
435
  }
436
436
  continue;
437
437
  }
@@ -439,7 +439,7 @@ export async function buildCleanupReport(
439
439
  // Under worktreesHome but no saved seat → not eviction-orphaned
440
440
  // (pre-assimilate or hand-made). Report, never auto-prune.
441
441
  if (!seat) {
442
- rows.push({ worktreePath: wt, wtBranch: null, reason: 'UNKNOWN-no-seat', detail: 'no saved seat — manual review' });
442
+ rows.push({ worktreePath: wt, wtBranch: null, reason: 'UNKNOWN-no-connection', detail: 'no saved connection — manual review' });
443
443
  continue;
444
444
  }
445
445