borgmcp 3.11.1 → 3.13.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 (58) hide show
  1. package/README.md +15 -0
  2. package/THIRD_PARTY_NOTICES.md +5 -0
  3. package/dist/assimilate-deps.d.ts.map +1 -1
  4. package/dist/assimilate-deps.js +11 -0
  5. package/dist/assimilate-deps.js.map +1 -1
  6. package/dist/cli-help.d.ts.map +1 -1
  7. package/dist/cli-help.js +19 -7
  8. package/dist/cli-help.js.map +1 -1
  9. package/dist/clone-cmd.d.ts +4 -1
  10. package/dist/clone-cmd.d.ts.map +1 -1
  11. package/dist/clone-cmd.js +28 -6
  12. package/dist/clone-cmd.js.map +1 -1
  13. package/dist/codex-app-wake.d.ts +3 -2
  14. package/dist/codex-app-wake.d.ts.map +1 -1
  15. package/dist/codex-app-wake.js +42 -9
  16. package/dist/codex-app-wake.js.map +1 -1
  17. package/dist/cube-activity-wake-copy.d.ts +1 -1
  18. package/dist/cube-activity-wake-copy.d.ts.map +1 -1
  19. package/dist/cube-activity-wake-copy.js +1 -1
  20. package/dist/cube-activity-wake-copy.js.map +1 -1
  21. package/dist/log-stream.d.ts +3 -3
  22. package/dist/log-stream.d.ts.map +1 -1
  23. package/dist/log-stream.js +8 -13
  24. package/dist/log-stream.js.map +1 -1
  25. package/dist/opencode-drone.d.ts +1 -1
  26. package/dist/opencode-drone.d.ts.map +1 -1
  27. package/dist/opencode-drone.js +16 -2
  28. package/dist/opencode-drone.js.map +1 -1
  29. package/dist/parse-clone-args.d.ts +3 -2
  30. package/dist/parse-clone-args.d.ts.map +1 -1
  31. package/dist/parse-clone-args.js +32 -9
  32. package/dist/parse-clone-args.js.map +1 -1
  33. package/dist/quickstart-cmd.d.ts +5 -1
  34. package/dist/quickstart-cmd.d.ts.map +1 -1
  35. package/dist/quickstart-cmd.js +17 -6
  36. package/dist/quickstart-cmd.js.map +1 -1
  37. package/dist/remote-client.d.ts +2 -21
  38. package/dist/remote-client.d.ts.map +1 -1
  39. package/dist/remote-client.js +8 -6
  40. package/dist/remote-client.js.map +1 -1
  41. package/dist/server-handshake.d.ts +6 -1
  42. package/dist/server-handshake.d.ts.map +1 -1
  43. package/dist/server-handshake.js +1 -0
  44. package/dist/server-handshake.js.map +1 -1
  45. package/docs/LOCAL_SERVER.md +4 -8
  46. package/docs/RELEASING.md +32 -308
  47. package/package.json +3 -2
  48. package/src/assimilate-deps.ts +11 -0
  49. package/src/cli-help.ts +19 -7
  50. package/src/clone-cmd.ts +36 -7
  51. package/src/codex-app-wake.ts +42 -8
  52. package/src/cube-activity-wake-copy.ts +1 -1
  53. package/src/log-stream.ts +24 -15
  54. package/src/opencode-drone.ts +14 -1
  55. package/src/parse-clone-args.ts +31 -10
  56. package/src/quickstart-cmd.ts +34 -6
  57. package/src/remote-client.ts +11 -25
  58. package/src/server-handshake.ts +4 -1
package/docs/RELEASING.md CHANGED
@@ -8,8 +8,9 @@ npm Trusted Publishing; no long-lived npm token is stored or exposed.
8
8
  ## Release Integrity
9
9
 
10
10
  Release provenance is established by protected annotated tags, GitHub build
11
- provenance, and npm Trusted Publishing. Tags and publication workflows are
12
- immutable: never move, replace, reuse, or rerun them.
11
+ provenance, and npm Trusted Publishing. Never move, replace, or reuse a tag.
12
+ A failed workflow may be rerun until npm accepts a stage; stage acceptance
13
+ consumes the version.
13
14
 
14
15
  ## Release Prerequisites
15
16
 
@@ -20,19 +21,15 @@ Before creating the release tag, independently verify all of these conditions:
20
21
  - the extraction review confirms no private backend secrets, deployment
21
22
  configuration, customer data, local state, or duplicated shared contracts
22
23
  entered the public package;
23
- - the exact audited registry dependency `borgmcp-shared@0.11.0` remains locked to
24
+ - the exact audited registry dependency `borgmcp-shared@0.12.2` remains locked to
24
25
  its canonical tarball and integrity
25
- `sha512-I8mixCbSrLKyOAAyqEI/HZJ8cML2rz3r812Up8pr547OdAk9LxZevdCo7ojG42ZwrUmS5u7iKQPg7Vk1XvtX1g==`;
26
- - the coupled shared/server/client candidates have all been built against the
27
- same exact `borgmcp-shared` version and passed the complete local dogfood gate
28
- before any stage is approved;
26
+ `sha512-l459XEeqk0cSz1+Z8yk8cCVWik4/CX4OBTRZqj6n1SZYvDpzWJksUz82FA9k4taf//rs43Tfl1tpWXnRHAqxOQ==`;
27
+ - the coupled shared/server/client candidates use the same exact
28
+ `borgmcp-shared` version;
29
29
  - the selected stable client version is unused and the exact release commit is
30
30
  on protected `main`;
31
31
  - the repository and protected npm environment settings pass an operator audit;
32
- - the exact release source passes Code Review, Security Review, Release Quality,
33
- extraction, and package gates; and
34
- - the immutable annotated tag and publication receive explicit release
35
- authorization.
32
+ - the exact release source passes exact-SHA CI and one Code Review.
36
33
 
37
34
  `scripts/verify-release-readiness.mjs` makes the source-side blockers
38
35
  machine-checkable. A release tag created before they are resolved fails before
@@ -41,18 +38,19 @@ dependency installation or publication.
41
38
  ### Release branches
42
39
 
43
40
  Release branches use the `release/` prefix and enter protected `main` through a
44
- pull request. Direct pushes only update staging branches; publication still
45
- requires the complete protected release gate.
41
+ pull request. After exact-SHA CI and one Code Review, merge the preparation and
42
+ create its annotated version tag. The tag automatically verifies and stages the
43
+ package.
46
44
 
47
45
  ## Repository Controls
48
46
 
49
47
  Repository settings are operator-owned and are not changed by this workflow.
50
48
  Before preparing a candidate, independently verify:
51
49
 
52
- 1. The `npm-publish` environment disables administrator bypass, requires the
53
- reviewed human approver, and allows only the protected release refs. Its
54
- `NPM_EXPECTED_OWNER` variable must match the sole reviewed maintainer of the
55
- existing `borgmcp` package. It must contain no npm token.
50
+ 1. The `npm-publish` environment disables administrator bypass, has no required
51
+ reviewer, and allows only `v*.*.*` tags. Its `NPM_EXPECTED_OWNER` variable
52
+ must match the sole reviewed maintainer of the existing `borgmcp` package.
53
+ It must contain no npm token.
56
54
  2. npm Trusted Publishing is configured for organization `Byte-Ventures`,
57
55
  repository `borg-mcp-client`, workflow `publish.yml`, and environment
58
56
  `npm-publish`. Its allowed actions enable `npm stage publish` and disable
@@ -69,212 +67,8 @@ Before preparing a candidate, independently verify:
69
67
 
70
68
  ## Release Workflow
71
69
 
72
- ### Clean-environment rig lifecycle
73
-
74
- Every clean-environment verification rig has an explicit identity and an
75
- explicit end-of-life. Run it from a Borg-launched session and keep filesystem
76
- workspaces under the exact disposable scratch root Borg exports. Give each
77
- workspace a name beginning with `borg-rig-` followed by its owner, purpose, and
78
- a unique suffix. Before the first command that can create files, anchor the rig
79
- as its own npm project:
80
-
81
- ```sh
82
- BORG_SCRATCH_ROOT="${BORG_LAUNCH_SCRATCH:?run from a Borg-launched session}"
83
- RIG_OWNER="$(basename "$BORG_SCRATCH_ROOT")"
84
- RIG_NONCE="${RIG_NONCE:-$(date +%Y%m%d%H%M%S)-$$}"
85
- RIG_ID="borg-rig-${RIG_OWNER}-release-${RIG_NONCE}"
86
- RIG_ROOT="$BORG_SCRATCH_ROOT/$RIG_ID"
87
- mkdir -p "$RIG_ROOT"
88
- printf '%s\n' '{"private":true}' > "$RIG_ROOT/package.json"
89
- ```
90
-
91
- The manifest anchor is required before any `npm install`, `npm update`, or
92
- other npm command. Without it, npm can walk up from an empty scratch directory
93
- and write the operator's `package.json` instead of creating project-local
94
- state. In a source checkout, the committed `release:exercise` follows this rule
95
- for its temporary consumer; other QA scripts and manual rigs must do the same.
96
-
97
- The system temporary root is shared with the whole machine, so an unbounded
98
- listing there is not an inspectable cleanup check. Bound that leg by the
99
- invoking user and the run-window markers, which select directories whose
100
- timestamps fall within this run window without requiring a prefix inventory.
101
- New `mkdtemp` prefixes therefore require no documentation change. Keep both
102
- time predicates and the ownership predicate: removing the run-window bound
103
- turns a populated shared temp root back into an uninspectable listing.
104
-
105
- On a machine where several runs share one user account, this listing can
106
- include another run's live workspace. Removal is scoped to paths created by
107
- this run; anything else in the output is reported, not deleted. Unrelated
108
- same-user temporary directories created during the window can therefore
109
- appear in the result, so never remove a path merely because this listing found
110
- it.
111
-
112
- ```sh
113
- TEMP_ROOT="${TMPDIR:-/tmp}"
114
- TEMP_OWNER="$(id -un)"
115
- TEMP_SCAN_START="$TEMP_ROOT/.${RIG_ID}-temp-scan-start"
116
- TEMP_SCAN_END="$TEMP_ROOT/.${RIG_ID}-temp-scan-end"
117
-
118
- list_recent_owned_temp_rigs() {
119
- find "$TEMP_ROOT" -mindepth 1 -prune \
120
- -type d \
121
- -user "$TEMP_OWNER" \
122
- -newer "$TEMP_SCAN_START" \
123
- ! -newer "$TEMP_SCAN_END" \
124
- -print
125
- }
126
-
127
- touch "$TEMP_SCAN_START"
128
- ```
129
-
130
- Container-backed rigs use the same `RIG_ID` as the container name and carry
131
- both labels below. Register exact-target cleanup before launching the rig:
132
-
133
- ```sh
134
- list_owned_rig_containers() {
135
- docker container ls --all \
136
- --filter label=borg-rig=1 \
137
- --filter "label=borg-rig-owner=${RIG_OWNER:?}" \
138
- --format '{{.ID}}\t{{.Names}}\t{{.Status}}'
139
- }
140
-
141
- cleanup_done=0
142
- cleanup() {
143
- [ "$cleanup_done" -eq 0 ] || return
144
- cleanup_done=1
145
- trap - EXIT HUP INT TERM
146
- docker container rm --force "$RIG_ID" >/dev/null 2>&1 || true
147
- rm -rf -- "$RIG_ROOT"
148
- list_owned_rig_containers
149
- find "$BORG_SCRATCH_ROOT" -mindepth 1 -print
150
- find "$BORG_SCRATCH_ROOT" -name 'borg-rig-*' -print
151
- if [ -e "$TEMP_SCAN_START" ]; then
152
- touch "$TEMP_SCAN_END"
153
- list_recent_owned_temp_rigs
154
- fi
155
- rm -f -- "$TEMP_SCAN_START" "$TEMP_SCAN_END"
156
- }
157
- trap cleanup EXIT
158
- trap 'exit 129' HUP
159
- trap 'exit 130' INT
160
- trap 'exit 143' TERM
161
- ```
162
-
163
- List containers and scratch paths before starting. The filters distinguish this
164
- session's rigs from unrelated containers on a shared host:
165
-
166
- ```sh
167
- list_owned_rig_containers
168
- find "$BORG_SCRATCH_ROOT" -mindepth 1 -print
169
- find "$BORG_SCRATCH_ROOT" -name 'borg-rig-*' -print
170
- ```
171
-
172
- Only after the cleanup traps and pre-launch listings are in place, launch a
173
- container-backed rig. `--rm` is preferred; a runtime without automatic removal
174
- must remove the exact `RIG_ID` in its cleanup path and retain the labels for a
175
- bounded sweep:
176
-
177
- ```sh
178
- RIG_IMAGE="${RIG_IMAGE:?set the rig image}"
179
- docker run --rm \
180
- --name "$RIG_ID" \
181
- --label borg-rig=1 \
182
- --label "borg-rig-owner=$RIG_OWNER" \
183
- "$RIG_IMAGE"
184
- ```
185
-
186
- The unfiltered filesystem listings expose legacy names as well as conforming
187
- rigs. The bounded temporary-directory function reports same-user directories
188
- created during the run window, including unrelated work; it does not establish
189
- which process owns them. The cleanup path runs the container and scratch
190
- listings after removing this rig, then runs the bounded temporary-directory
191
- listing between the start and end markers. Inspect and report its results. If a
192
- rig creates an exact system-temporary path, record that path when it is created
193
- and remove that exact path during cleanup; never remove a path solely because
194
- the bounded listing found it.
195
-
196
- Register cleanup before launching any process, run it on success and failure,
197
- and do not deliver a verdict until the container listing and both scratch
198
- listings show no rig owned by this run, every exact system-temporary path
199
- recorded by the rig has been removed, and the bounded temporary-directory
200
- listing has been inspected and reported. For a non-`--rm` container runtime,
201
- remove only the named rig or the same owner label; never prune unrelated
202
- containers. A completed verification
203
- therefore implies zero running or stopped rig containers and no rig workspace
204
- created by this run left in either the session scratch root or any exact
205
- system-temporary path the rig recorded.
206
-
207
- ### Pre-tag composed exercise
208
-
209
- Before creating a release tag, exercise the packed client against the selected
210
- server artifact:
211
-
212
- ```sh
213
- npm run release:exercise -- \
214
- --server /absolute/path/to/borgmcp-server.tgz \
215
- --server-integrity 'sha512-...'
216
- ```
217
-
218
- The server integrity must come from the reviewed producer of that artifact. Run
219
- the command once with the published counterpart. When the client and server are
220
- being released together, run it again with the co-releasing server candidate.
221
- The default path builds and packs the current client; `--client-tarball` exists
222
- only for reproducing a reviewed packed client or running a negative control.
223
-
224
- This harness exercises reviewed release candidates and their published
225
- counterparts. It is not a containment sandbox for hostile or otherwise untrusted
226
- packages and must not be used as one. The temporary install and data directories
227
- isolate the reviewed exercise from normal product state; they are not an
228
- operating-system security boundary.
229
-
230
- The harness requires Node.js 22, npm 11.18.0, Python 3, and macOS or Linux. It
231
- installs both tarballs into a private temporary project, bootstraps isolated
232
- server data, and starts the installed server directly. It never uses
233
- `borg server setup`, because registry-backed activation would select the
234
- previously published server instead of a candidate.
235
-
236
- Both composed terminal journeys run under a real PTY:
237
-
238
- - `borg server dashboard` must render the attached-viewer footer, exit cleanly
239
- on Ctrl-C, restore the cursor and alternate screen, and leave the server's
240
- pinned-TLS process healthy at `GET /healthz` (204, empty body).
241
- - `borg server start` must render the foreground-server footer, exit cleanly on
242
- Ctrl-C, restore the terminal, stop the server, and release its health endpoint.
243
-
244
- The client facade resolves `borg-mcp-server` by bare name. The harness therefore
245
- uses a controlled shim only to arrange resolution, then verifies the outcome:
246
- before trusting a frame, it reads the live process command and requires the
247
- absolute installed server entry. Every client or server executable must resolve
248
- inside the installed package root covered by the SRI cited for that role. Its
249
- JSON report records the absolute path, version, and independently supplied
250
- integrity for the client, dashboard listener, dashboard viewer, and foreground
251
- listener/viewer roles. PATH ordering alone is never accepted as identity evidence.
252
-
253
- The harness is a fail-closed release gate. A missing frame, non-PTY execution,
254
- wrong journey footer, substituted artifact, nonzero exit, absent terminal
255
- restore sequence, failed post-exit health assertion, timeout, or oversized
256
- transcript fails the command. Its controls must remain demonstrably bidirectional:
257
- the current client/server pairing passes; and a deliberately wrong server
258
- integrity fails before either journey starts.
259
-
260
- The terminal-restore negative control is specifically the packed pre-#146 client
261
- from commit `81da7b970ffb4e76a35c7bc551c419fec702a3b6` composed with published
262
- `borgmcp-server@0.2.0`, registry integrity
263
- `sha512-squb0+vdy0q7l/4FeV7OTvSm7OiFWGsAjGhcVEXYrQc9K/8jJYduqPS90VBwaJRT6z221Gxf3xQ5SeZe/Qoncw==`.
264
- That composition must fail on the missing cursor-restore sequence. A newer server
265
- candidate can mask the old client's defect and is not a valid counterpart for
266
- this negative control.
267
-
268
- ```sh
269
- npm run release:exercise -- \
270
- --server borgmcp-server@0.2.0 \
271
- --server-integrity 'sha512-squb0+vdy0q7l/4FeV7OTvSm7OiFWGsAjGhcVEXYrQc9K/8jJYduqPS90VBwaJRT6z221Gxf3xQ5SeZe/Qoncw==' \
272
- --client-tarball /absolute/path/to/pre-146-borgmcp-2.1.1.tgz
273
- ```
274
-
275
70
  The only trigger is a protected annotated `v<package version>` tag. Manual
276
- dispatch is intentionally absent so a second run cannot rebuild or publish an
277
- existing tag. The workflow rejects reruns, root `.npmrc`
71
+ dispatch is intentionally absent. The workflow rejects root `.npmrc`
278
72
  configuration, non-tag events, lightweight or malformed tags, version mismatch,
279
73
  source/tag mismatch, and tags whose commits are not on protected `main`.
280
74
 
@@ -283,7 +77,8 @@ The unprivileged `verify` job performs one sequence:
283
77
  1. Verify the public-source boundary, extraction readiness, exact shared-package
284
78
  pin, and canonical registry lock metadata.
285
79
  2. Install the lockfile once with lifecycle scripts disabled and audit it.
286
- 3. Run type checks, tests, and one readable build; reject generated `dist` drift.
80
+ 3. Run one readable build and reject generated `dist` drift. Tests already ran
81
+ in CI on the exact source.
287
82
  4. Produce one npm tarball.
288
83
  5. Verify that tarball once for package identity, license/notice, source and map
289
84
  completeness, executable bins, archive safety, dependency integrity, and
@@ -294,14 +89,6 @@ The unprivileged `verify` job performs one sequence:
294
89
  7. Upload only the tarball and its verifier-generated report as the same-run
295
90
  release artifact.
296
91
 
297
- After `verify` succeeds, the designated Queen operator alone approves the
298
- `npm-publish` environment. There is no separate pre-stage exact-artifact
299
- Security gate: the verify job is the mechanical authority for the exact bytes
300
- that the stage job consumes. Environment approval authorizes submitting those
301
- bytes to npm's private staged-publishing service; it does not make the version
302
- public, permit a rerun or rebuilt artifact, or authorize approval by another
303
- actor.
304
-
305
92
  The protected `publish` job alone receives `id-token: write`. It downloads the
306
93
  same-run artifact and rejects a report whose package name or version differs
307
94
  from the release, a version that already exists, an unclaimed package, or an
@@ -313,48 +100,15 @@ package.
313
100
 
314
101
  Successful completion of the workflow means npm accepted the immutable staged
315
102
  tarball. It does not mean the version is public. Stage acceptance consumes the
316
- tagged attempt and version under the burned-version rule, but must not trigger
103
+ version, but must not trigger
317
104
  release announcements, issue closure, consumer pins, site synchronization, or
318
105
  claims that the version was published. There is no workflow registry readback:
319
106
  stage inspection and approval require an interactive npm identity and cannot use
320
107
  the workflow's OIDC credential.
321
108
 
322
- ### Coupled stage approval
323
-
324
- Before approving anything, the operator requires successful stage workflows for
325
- `borgmcp-shared`, `borgmcp-server`, and `borgmcp`. Use authenticated
326
- `npm stage list` and `npm stage view <UUID>` to record and inspect all three stage
327
- UUIDs and verify each package, version, and eventual `latest` tag. For every
328
- package, `npm stage download <UUID>` is mandatory; compute its SHA-512 SRI and
329
- require an exact match with that workflow run's same-run artifact report. Bind
330
- the source annotated tag object, commit, and workflow run separately from GitHub
331
- evidence, because npm stage inspection does not expose them. Exercise the exact
332
- downloaded coupled set before approval. Confirm that public `latest`, public
333
- `versions`, and client update resolution still expose the prior coherent release
334
- set. If a pending stage appears on any public surface, approve nothing and halt
335
- this mechanism before consumers can observe it.
336
-
337
- Approve the verified UUIDs in one operator session, using interactive 2FA, in
338
- this exact order:
339
-
340
- 1. `npm stage approve <shared-stage-uuid>`
341
- 2. `npm stage approve <server-stage-uuid>`
342
- 3. `npm stage approve <client-stage-uuid>`
343
-
344
- The approvals are not atomic. Shared approval leaves existing client and server
345
- pins coherent. Server approval opens a bounded mismatch window between public
346
- server `latest` and client `latest`; client approval closes it. After an approval
347
- returns successfully, continue only while the next stage remains valid. For an
348
- ambiguous result, inspect authenticated stage state and canonical public package
349
- version/integrity before acting; never repeat an approval blindly.
350
-
351
- The terminal release boundary is successful interactive stage approval followed
352
- by canonical registry visibility, integrity verification, and registry
353
- provenance-attestation inspection. Stage inspection cannot establish provenance
354
- before approval; Trusted Publishing carries it to the live package, where the
355
- registry attestation is authoritative. Only then may the release be announced,
356
- issues closed, consumer pins or site data synchronized, or the version described
357
- as published.
109
+ Inspect the npm stage, then approve it interactively. This stage approval is the
110
+ sole human publication boundary. Confirm canonical live package visibility and
111
+ integrity before announcing the release or updating dependent packages.
358
112
 
359
113
  After all three packages cross that boundary, create their GitHub Releases in
360
114
  the same shared → server → client operator session. In each package repository,
@@ -364,46 +118,16 @@ run:
364
118
  GITHUB_TOKEN="$(gh auth token)" node scripts/create-github-release.mjs <version>
365
119
  ```
366
120
 
367
- The script binds the annotated tag, merged release pull request, successful tag
368
- workflow artifact, and live npm integrity before creating the Release. It copies
369
- the merged release pull request body verbatim and refuses an existing Release.
370
-
371
- Separately, once the release is installable from the canonical registry, install
372
- it into an isolated prefix and exercise the real user update path end to end.
373
- This is product verification, not candidate validation: failure routes a new
374
- reviewed fix and never invalidates, rebuilds, retags, or reruns the immutable
375
- release. Do not repeat byte comparisons, packed-version checks, source-tree
376
- verification, dist-tag readback, or provenance reconstruction that the
377
- exact-artifact `verify` and stage jobs already completed. The canonical
378
- live-version integrity check is required because it establishes the approval
379
- boundary; it is not a duplicate candidate verification.
380
-
381
- No separate checksum file is needed: the tarball verifier records canonical
382
- SHA-512 SRI in the artifact report. GitHub's same-run artifact transport and the
383
- report bind the reviewed candidate without repeated SHA512 choreography.
384
-
385
- Rely on npm Trusted Publishing. Perform the one authoritative registry
386
- provenance-attestation inspection at the approved-live boundary; do not
387
- reconstruct DSSE, in-toto, SLSA, workflow-ref, or builder statements locally or
388
- add redundant provenance readback.
389
- Do not add cross-run tuple variables, cross-run artifact selection, duplicate
390
- builds, duplicate package verification, checksum bundles, or SBOM ceremony.
121
+ The script binds the annotated tag, live npm package and integrity, and Release
122
+ absence before creating the Release. It reads
123
+ the curated `docs/releases/<version>.md` notes from the exact tagged commit,
124
+ renders them under `News and fixes`, and refuses missing or blank notes and an
125
+ existing Release.
391
126
 
392
127
  ## Stop And Recovery Conditions
393
128
 
394
- Stop when source, settings, ownership, tag, artifact, test, audit, review, or
395
- authorization evidence is missing or inconsistent. Never move or reuse a failed
396
- tag, rerun a failed release workflow, overwrite an npm version, unpublish to hide
397
- a failure, or substitute a local rebuild. Recovery starts from a fresh reviewed
398
- source change and, after any registry mutation, a separately authorized version.
399
-
400
- Before any approval, missing or inconsistent stage evidence means approve none.
401
- Reject all three stages only when abandoning the coupled candidate; Borg treats
402
- every rejected or unusable tagged stage as burned, even though npm permits some
403
- re-staging. Replacement versions require fresh review, tags, and stage runs.
404
- After shared approval but before server approval, stopping is compatibility-safe
405
- for the existing client/server pair, but shared is immutable and any repair uses
406
- fresh versions. After server approval, prioritize the already-verified client
407
- approval because the public pair is mismatched. If that client stage cannot be
408
- approved, do not reject it or rerun the tag; prepare newly reviewed matching
409
- server/client recovery versions and state the live mismatch window explicitly.
129
+ Stop when source, settings, ownership, tag, artifact, test, audit, or review
130
+ evidence is missing or inconsistent. Never move or reuse a tag,
131
+ overwrite an npm version, unpublish to hide a failure, or substitute a local
132
+ rebuild. A pre-stage workflow failure may be rerun against the same immutable
133
+ tag. After npm accepts a stage, recovery uses a fresh reviewed version.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "borgmcp",
3
- "version": "3.11.1",
3
+ "version": "3.13.0",
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",
@@ -48,6 +48,7 @@
48
48
  "README.md",
49
49
  "LICENSE",
50
50
  "NOTICE",
51
+ "THIRD_PARTY_NOTICES.md",
51
52
  "SECURITY.md",
52
53
  "CONTRIBUTING.md"
53
54
  ],
@@ -71,7 +72,7 @@
71
72
  },
72
73
  "dependencies": {
73
74
  "@modelcontextprotocol/sdk": "^1.0.4",
74
- "borgmcp-shared": "0.11.0",
75
+ "borgmcp-shared": "0.12.2",
75
76
  "chalk": "^5.3.0",
76
77
  "prompts": "^2.4.2",
77
78
  "which": "^4.0.0"
@@ -40,6 +40,7 @@ import {
40
40
  resumeLocalBorgServerEnrollment,
41
41
  sendBorgServerAttach,
42
42
  } from './server-handshake.js';
43
+ import { advanceLocalServerCursor } from './local-server-cursor.js';
43
44
  import {
44
45
  findIncompleteSiblingAttempt,
45
46
  observeSeat,
@@ -483,6 +484,16 @@ export function buildDefaultAssimilateDeps(
483
484
  );
484
485
  }
485
486
  }
487
+ if (prepared.initialLogCursor) {
488
+ const binding = {
489
+ origin: apiUrl,
490
+ trustIdentity: serverTrustIdentity,
491
+ cubeId: prepared.cube.id,
492
+ droneId: prepared.drone.id,
493
+ };
494
+ await advanceLocalServerCursor(binding, prepared.initialLogCursor);
495
+ await advanceLocalServerCursor({ ...binding, purpose: 'stream' }, prepared.initialLogCursor);
496
+ }
486
497
  return {
487
498
  cube_id: prepared.cube.id,
488
499
  drone_id: prepared.drone.id,
package/src/cli-help.ts CHANGED
@@ -57,12 +57,15 @@ export function quickstartHelpText(version: string): string {
57
57
  `Usage:\n` +
58
58
  ` borg quickstart [options]\n\n` +
59
59
  `Options:\n` +
60
- ` --template ${NEW_CUBE_TEMPLATE_OPTIONS} Choose the new-cube template without prompting\n` +
60
+ ` --template <name> Choose without prompting:\n` +
61
+ ` ${NEW_CUBE_TEMPLATE_OPTIONS}\n` +
61
62
  ` --role <slug>[:<count>] Fully specify the roster (repeatable)\n` +
62
63
  ` --yes, -y Accept the displayed plan\n` +
63
64
  ` --help, -h Show this help\n\n` +
64
- `Quickstart requires a running Borg server and never starts one. Rerun the same\n` +
65
- `command after a partial failure; existing drones are kept and skipped.\n`
65
+ `For the current repository, quickstart creates and launches a full roster.\n` +
66
+ `Use --role to replace that roster with an explicit selection. Quickstart\n` +
67
+ `requires a running Borg server and never starts one. Rerun the same command\n` +
68
+ `after a partial failure; existing drones are kept and skipped.\n`
66
69
  );
67
70
  }
68
71
 
@@ -70,10 +73,17 @@ export function cloneHelpText(version: string): string {
70
73
  return (
71
74
  `borg clone (borgmcp ${version}) — clone a repository and run quickstart\n\n` +
72
75
  `Usage:\n` +
73
- ` borg clone <repository-url> [directory] [--no-launch]\n\n` +
76
+ ` borg clone <repository-url> [directory] [options]\n\n` +
74
77
  `Options:\n` +
75
- ` --no-launch Stop after the checkout is ready; do not create or launch a cube\n` +
76
- ` --help, -h Show this help\n\n` +
78
+ ` --template <name> Choose without prompting:\n` +
79
+ ` ${NEW_CUBE_TEMPLATE_OPTIONS}\n` +
80
+ ` --role <slug>[:<count>] Fully specify the roster (repeatable)\n` +
81
+ ` --yes, -y Accept the displayed plan\n` +
82
+ ` --checkout-only Stop after the checkout is ready\n` +
83
+ ` --no-launch Same as --checkout-only\n` +
84
+ ` --help, -h Show this help\n\n` +
85
+ `Clone checks out a new repository, then delegates the complete setup directly to\n` +
86
+ `quickstart. Non-interactive full setup requires both --yes and --template.\n` +
77
87
  `A repeated command reuses a checkout only when its origin matches. Default\n` +
78
88
  `destination names avoid non-repository collisions. Credential-bearing URLs are\n` +
79
89
  `refused; use a Git credential helper or SSH configuration instead.\n`
@@ -162,7 +172,7 @@ export function topLevelHelpText(version: string): string {
162
172
  ` borg doctor Check agent hook commands, versions, configs, and the OpenCode plugin\n` +
163
173
  ` borg clone <url> [dir] Clone a repository, then create and launch its cube\n` +
164
174
  ` borg quickstart Create a cube and a drone for every role, then launch them\n` +
165
- ` borg assimilate [role] Join or create a cube\n` +
175
+ ` borg assimilate [role] Join or create a cube with one drone under one role\n` +
166
176
  ` borg assimilate --host <host> Join or create on an explicit server\n` +
167
177
  ` borg assimilate --worktree <name> Spawn a worktree drone (in ~/.borg/worktrees/<repo>/<name>)\n` +
168
178
  ` borg server cube init Initialize this repository's cube without creating a drone\n` +
@@ -280,6 +290,8 @@ export function assimilateHelpText(version: string): string {
280
290
  ` --cli claude|codex|opencode Agent CLI to launch\n` +
281
291
  ` --model claude:<model> Legacy Claude model override (configure models in the agent CLI)\n` +
282
292
  ` --yes, -y Accept new-cube defaults; never adopt by name\n\n` +
293
+ `Assimilate adds or resumes one drone under one role; use quickstart when you want\n` +
294
+ `the repository's full roster.\n` +
283
295
  `Creation shows repository context, name, template, and one confirmation. An existing\n` +
284
296
  `repository association skips all prompts. One accessible exact-name legacy cube requires\n` +
285
297
  `explicit interactive adoption; ambiguous matches fail closed. An enrolled owner client may\n` +
package/src/clone-cmd.ts CHANGED
@@ -4,7 +4,12 @@ import { basename, dirname, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { hasCloneCredentials, redactCloneSecrets } from './clone-security.js';
6
6
  import type { CloneArgs } from './parse-clone-args.js';
7
- import { buildDefaultQuickstartDeps, runQuickstart } from './quickstart-cmd.js';
7
+ import type { QuickstartArgs } from './parse-quickstart-args.js';
8
+ import {
9
+ buildDefaultQuickstartDeps,
10
+ runQuickstart,
11
+ type QuickstartRunOptions,
12
+ } from './quickstart-cmd.js';
8
13
  import { shellEscape } from './shell-escape.js';
9
14
 
10
15
  export interface GitRunResult {
@@ -22,7 +27,8 @@ export interface CloneDeps {
22
27
  readDirectory: (path: string) => string[];
23
28
  createDirectory: (path: string) => boolean;
24
29
  removeTree: (path: string) => void;
25
- quickstart: (cwd: string) => Promise<number>;
30
+ isTTY: () => boolean;
31
+ quickstart: (cwd: string, args: QuickstartArgs, options: QuickstartRunOptions) => Promise<number>;
26
32
  stdout: (text: string) => void;
27
33
  stderr: (text: string) => void;
28
34
  }
@@ -61,9 +67,10 @@ export function buildDefaultCloneDeps(): CloneDeps {
61
67
  }
62
68
  },
63
69
  removeTree: (path) => rmSync(path, { recursive: true, force: true }),
64
- quickstart: async (cwd) => {
70
+ isTTY: () => process.stdin.isTTY === true,
71
+ quickstart: async (cwd, args, options) => {
65
72
  process.chdir(cwd);
66
- return runQuickstart({ roles: [], yes: false }, buildDefaultQuickstartDeps());
73
+ return runQuickstart(args, buildDefaultQuickstartDeps(), options);
67
74
  },
68
75
  stdout: (text) => process.stdout.write(text),
69
76
  stderr: (text) => process.stderr.write(text),
@@ -177,6 +184,13 @@ export async function runClone(args: CloneArgs, rawDeps: CloneDeps): Promise<num
177
184
  deps.stderr(`borg clone: ${valid.error}.\n`);
178
185
  return 1;
179
186
  }
187
+ if (!args.checkoutOnly && !deps.isTTY() && (!args.yes || !args.template)) {
188
+ deps.stderr(
189
+ 'borg clone: non-interactive full setup requires both --yes and --template; ' +
190
+ 'use --checkout-only to clone without setup.\n',
191
+ );
192
+ return 1;
193
+ }
180
194
  const selected = chooseDestination(deps, args);
181
195
  if ('error' in selected) {
182
196
  deps.stderr(`borg clone: ${selected.error}.\n`);
@@ -224,7 +238,7 @@ export async function runClone(args: CloneArgs, rawDeps: CloneDeps): Promise<num
224
238
  deps.stdout(`Cloned ${sourceName(args.repositoryUrl)} into ${destination}.\n`);
225
239
  }
226
240
 
227
- if (args.noLaunch) {
241
+ if (args.checkoutOnly) {
228
242
  deps.stdout(
229
243
  `Checkout ready at ${destination}. No cube or drone was created.\n` +
230
244
  `Next: cd ${shellEscape(destination)} && borg quickstart\n`,
@@ -232,8 +246,23 @@ export async function runClone(args: CloneArgs, rawDeps: CloneDeps): Promise<num
232
246
  return 0;
233
247
  }
234
248
  deps.chdir(destination);
235
- const code = await deps.quickstart(destination);
236
- if (code !== 0) {
249
+ const quickstartArgs: QuickstartArgs = {
250
+ ...(args.template === undefined ? {} : { template: args.template }),
251
+ roles: args.roles,
252
+ yes: args.yes,
253
+ };
254
+ let cancelled = false;
255
+ const code = await deps.quickstart(destination, quickstartArgs, {
256
+ onCancelled: (kind) => {
257
+ cancelled = true;
258
+ const message =
259
+ `${kind === 'interrupted' ? '\n' : ''}borg clone: cancelled. Checkout remains at ${destination}.\n` +
260
+ `Resume: cd ${shellEscape(destination)} && borg quickstart\n`;
261
+ if (kind === 'interrupted') deps.stderr(message);
262
+ else deps.stdout(message);
263
+ },
264
+ });
265
+ if (code !== 0 && !cancelled) {
237
266
  deps.stderr(
238
267
  `The checkout${cloned ? '' : ' you already had'} is ready at ${destination}, but quickstart did not finish. ` +
239
268
  `It was left untouched; cd there and run \`borg quickstart\` again.\n`,