boxdown 1.0.0 → 1.2.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 (69) hide show
  1. package/README.md +135 -12
  2. package/assets/devcontainer/README.md +49 -17
  3. package/assets/devcontainer/devcontainer.json +24 -7
  4. package/assets/devcontainer/hooks/initialize.sh +32 -0
  5. package/assets/devcontainer/hooks/post-create.sh +59 -12
  6. package/assets/devcontainer/hooks/post-start.sh +21 -4
  7. package/assets/devcontainer/ssh-config-install.sh +12 -3
  8. package/assets/devcontainer/start.sh +721 -44
  9. package/assets/devcontainer/utils/coding-agent-cli-update.sh +267 -3
  10. package/assets/devcontainer/utils/deps-install.sh +68 -0
  11. package/assets/devcontainer/utils/git-config-bootstrap.sh +128 -0
  12. package/assets/devcontainer/utils/git-signing-bootstrap.sh +56 -0
  13. package/assets/devcontainer/utils/python-bootstrap.sh +69 -0
  14. package/assets/devcontainer/utils/ssh-bootstrap.sh +9 -0
  15. package/dist/bin/cli.cjs +1 -1
  16. package/dist/bin/cli.mjs +1 -1
  17. package/dist/main-Df4E8ARj.cjs +5498 -0
  18. package/dist/main-XMBsKjIK.mjs +5458 -0
  19. package/dist/main-XMBsKjIK.mjs.map +1 -0
  20. package/dist/main.cjs +5 -2
  21. package/dist/main.d.cts +495 -4
  22. package/dist/main.d.cts.map +1 -1
  23. package/dist/main.d.mts +495 -4
  24. package/dist/main.d.mts.map +1 -1
  25. package/dist/main.mjs +2 -2
  26. package/docs/README.md +1 -0
  27. package/docs/architecture.md +32 -0
  28. package/docs/development.md +13 -6
  29. package/docs/features/README.md +2 -0
  30. package/docs/features/commit-signing.md +23 -0
  31. package/docs/features/generated-config-and-state.md +57 -4
  32. package/docs/features/github-auth-refresh.md +12 -0
  33. package/docs/features/lifecycle.md +97 -11
  34. package/docs/features/setup.md +66 -0
  35. package/docs/features/ssh-config-and-proxy.md +228 -7
  36. package/docs/features/start-and-shell.md +40 -5
  37. package/docs/superpowers/plans/2026-07-11-default-commit-signing.md +110 -0
  38. package/docs/superpowers/plans/2026-07-11-progress-ci-regression.md +125 -0
  39. package/docs/superpowers/specs/2026-07-11-default-commit-signing-design.md +396 -0
  40. package/docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md +43 -0
  41. package/docs/testing.md +35 -2
  42. package/docs/todo.md +2 -2
  43. package/package.json +1 -1
  44. package/src/claude-app-config.ts +304 -0
  45. package/src/cli-style.ts +43 -0
  46. package/src/codex-app-config.ts +656 -0
  47. package/src/config.ts +68 -10
  48. package/src/constants.ts +5 -0
  49. package/src/devcontainer.ts +500 -64
  50. package/src/doctor.ts +195 -30
  51. package/src/git-signing.ts +87 -0
  52. package/src/interactive-prompts.ts +692 -0
  53. package/src/list.ts +16 -2
  54. package/src/logging.ts +154 -0
  55. package/src/main.ts +1213 -63
  56. package/src/metadata.ts +52 -3
  57. package/src/package-info.ts +18 -0
  58. package/src/paths.ts +50 -10
  59. package/src/process.ts +80 -2
  60. package/src/progress.ts +593 -0
  61. package/src/purge.ts +208 -0
  62. package/src/shell.ts +19 -0
  63. package/src/ssh-config.ts +134 -16
  64. package/src/ssh-install-targets.ts +111 -0
  65. package/src/ssh-key.ts +53 -13
  66. package/src/status.ts +5 -0
  67. package/dist/main-BuEptwlL.cjs +0 -1707
  68. package/dist/main-ZFTrSVgt.mjs +0 -1685
  69. package/dist/main-ZFTrSVgt.mjs.map +0 -1
@@ -0,0 +1,396 @@
1
+ # Default Commit Signing Design
2
+
3
+ ## Summary
4
+
5
+ Boxdown will attempt to enable SSH commit signing by default in every newly
6
+ created environment. It will forward the host SSH agent into the container,
7
+ select a signing identity without user interaction when that choice is
8
+ unambiguous, and configure container-global Git settings for SSH signing.
9
+
10
+ Signing is best-effort. If Boxdown cannot select or use a signing identity, it
11
+ will warn, disable signing in the container-global Git configuration, and allow
12
+ unsigned commits. Signing readiness will never make `boxdown setup` fail.
13
+
14
+ This design applies only to newly created Boxdown environments. It does not
15
+ migrate existing containers or repair repository-local Git settings written by
16
+ older Boxdown versions.
17
+
18
+ ## Goals
19
+
20
+ - Sign commits by default without a separate Boxdown signing setup command.
21
+ - Keep private signing keys on the host and expose only an agent-backed signing
22
+ capability to the container.
23
+ - Provide the same signing capability through `boxdown setup`, `start`,
24
+ `codex`, `claude`, `opencode`, `antigravity`, SSH proxy, and tunnel flows.
25
+ - Continue allowing commits when signing cannot be configured or validated.
26
+ - Detect signing problems in `boxdown doctor` and the setup preflight without
27
+ making signing readiness a setup prerequisite.
28
+ - Explain the difference between a cryptographically signed commit and a
29
+ commit that GitHub displays as Verified.
30
+
31
+ ## Non-goals
32
+
33
+ - Migrating existing Boxdown environments or legacy `.git/config` values.
34
+ - Copying a private GPG or SSH key into a container or Boxdown state.
35
+ - Automatically changing the user's GitHub account or registering signing
36
+ keys without explicit user action.
37
+ - Guaranteeing that a host agent remains available after an interactive
38
+ session has started.
39
+ - Intercepting `git commit` and automatically retrying it with
40
+ `--no-gpg-sign`.
41
+
42
+ ## Approaches Considered
43
+
44
+ ### SSH signing through the Docker agent bridge
45
+
46
+ This is the selected approach. Docker Desktop exposes the host SSH agent to a
47
+ container through `/run/host-services/ssh-auth.sock`, while native Linux Docker
48
+ can bind-mount the host path in `SSH_AUTH_SOCK`. Git supports signing through
49
+ that agent with `gpg.format=ssh`.
50
+
51
+ This approach works for both SSH sessions and Boxdown's primary
52
+ `devcontainer exec` execution path. It does not copy private key material.
53
+
54
+ ### GPG-agent forwarding over SSH
55
+
56
+ GPG-agent forwarding was validated successfully, but the forwarded socket
57
+ exists only during an SSH connection. Boxdown launches shells and coding-agent
58
+ CLIs through `devcontainer exec`, so GPG forwarding would require an additional
59
+ long-lived host proxy and remote socket lifecycle management. It is not the
60
+ default design.
61
+
62
+ ### Container-owned signing key
63
+
64
+ A key persisted in Boxdown state would remain available independently of the
65
+ host agent, but the container could read and copy the private key. Every new
66
+ key would also require GitHub registration. This weakens Boxdown's isolation
67
+ boundary and is rejected.
68
+
69
+ ## Signing Policy
70
+
71
+ Signing is default-on but best-effort:
72
+
73
+ 1. Boxdown attempts to resolve one host SSH signing identity.
74
+ 2. Boxdown attempts to expose the host agent to the container.
75
+ 3. The container verifies that the selected identity is loaded and can create
76
+ a disposable signed commit.
77
+ 4. If all checks pass, container-global `commit.gpgsign` is set to `true`.
78
+ 5. If any check fails, container-global `commit.gpgsign` is set to `false` and
79
+ Boxdown reports a warning.
80
+
81
+ Boxdown must never select the first of multiple ambiguous candidates. If it
82
+ cannot identify exactly one intended key, signing is disabled and commits
83
+ continue unsigned. This behavior must be documented in the README, signing
84
+ feature documentation, `boxdown doctor` output, and setup warnings.
85
+
86
+ Boxdown will no longer write `commit.gpgsign` into the repository-local
87
+ `.git/config`. Repository-local configuration is shared with the host and is
88
+ not container-owned state.
89
+
90
+ An explicit repository-local `commit.gpgsign=true` remains the user's policy
91
+ and can override Boxdown's container-global fallback. Boxdown does not rewrite
92
+ explicit repository policy to guarantee unsigned fallback.
93
+
94
+ ## Host Signing Resolution
95
+
96
+ A new host-side module will produce a typed signing plan before generated
97
+ devcontainer configuration is written. The plan records whether signing is
98
+ enabled, the selected public key, its fingerprint, the agent mount source, and
99
+ a stable reason when signing is unavailable.
100
+
101
+ Public keys are compared by algorithm and base64 key data. Comments are not
102
+ part of identity matching.
103
+
104
+ Boxdown resolves a signing identity in this order:
105
+
106
+ 1. If the host Git configuration uses `gpg.format=ssh`, resolve its
107
+ `user.signingKey`. Select it only if the corresponding public key is loaded
108
+ in the host agent.
109
+ 2. Otherwise, when authenticated GitHub CLI access and the network are
110
+ available, intersect the loaded agent identities with the current GitHub
111
+ user's SSH authentication keys. Select the key only when exactly one loaded
112
+ identity matches.
113
+ 3. Otherwise, select the loaded identity only when the agent contains exactly
114
+ one identity.
115
+ 4. With zero or multiple unresolved identities, produce a disabled signing
116
+ plan and a warning reason. Do not guess.
117
+
118
+ Failure to execute `ssh-add`, query GitHub, parse a public key, or resolve a
119
+ single identity is non-fatal.
120
+
121
+ The selected public key is written outside the repository under:
122
+
123
+ ```text
124
+ ~/.local/share/boxdown/workspaces/<workspace-id>/git-signing/signing-key.pub
125
+ ```
126
+
127
+ The signing state directory contains public material only. It is mounted
128
+ read-only at `/opt/boxdown/state/git-signing`.
129
+
130
+ ## Agent Socket Resolution
131
+
132
+ The generated configuration uses the fixed container path:
133
+
134
+ ```text
135
+ /run/boxdown/ssh-agent.sock
136
+ ```
137
+
138
+ and sets:
139
+
140
+ ```text
141
+ SSH_AUTH_SOCK=/run/boxdown/ssh-agent.sock
142
+ ```
143
+
144
+ On Docker Desktop for macOS and Linux, the mount source is Docker Desktop's
145
+ host-service socket:
146
+
147
+ ```text
148
+ /run/host-services/ssh-auth.sock
149
+ ```
150
+
151
+ On native Linux Docker, the source is the host value of `SSH_AUTH_SOCK`.
152
+
153
+ Boxdown probes a candidate source through Docker before adding it to generated
154
+ configuration. If the probe fails, the mount and `SSH_AUTH_SOCK` container
155
+ environment value are omitted and the signing plan is disabled. This prevents
156
+ an unavailable bind source from blocking container creation.
157
+
158
+ Because mounts are create-time state, an environment created while the agent
159
+ bridge is unavailable remains unsigned until it is recreated. Boxdown warns
160
+ about this limitation; it does not mutate existing container mounts.
161
+
162
+ ## Generated Devcontainer Configuration
163
+
164
+ When signing is enabled, the generated configuration includes mounts
165
+ equivalent to:
166
+
167
+ ```json
168
+ {
169
+ "mounts": [
170
+ "type=bind,source=/run/host-services/ssh-auth.sock,target=/run/boxdown/ssh-agent.sock",
171
+ "type=bind,source=<workspace-signing-state>,target=/opt/boxdown/state/git-signing,readonly"
172
+ ],
173
+ "containerEnv": {
174
+ "SSH_AUTH_SOCK": "/run/boxdown/ssh-agent.sock"
175
+ }
176
+ }
177
+ ```
178
+
179
+ Native Linux substitutes the resolved host agent socket for the first source.
180
+ The signing mount and environment value are absent from a disabled plan.
181
+
182
+ Generated configuration accepts the resolved signing plan as input rather
183
+ than discovering host or Docker state inside the pure configuration builder.
184
+ This keeps mount generation deterministic and unit-testable.
185
+
186
+ ## Container Git Configuration
187
+
188
+ Container Git bootstrap will stop unconditionally disabling commit signing.
189
+ When the selected identity and socket validate, it will configure the writable
190
+ container-global Git configuration with:
191
+
192
+ ```ini
193
+ [gpg]
194
+ format = ssh
195
+ [user]
196
+ signingKey = /opt/boxdown/state/git-signing/signing-key.pub
197
+ [commit]
198
+ gpgSign = true
199
+ ```
200
+
201
+ The bootstrap removes incompatible copied host settings such as
202
+ `gpg.program=/opt/homebrew/bin/gpg`. It does not need a global
203
+ `gpg.ssh.allowedSignersFile` to create signatures. A disposable repository may
204
+ use a temporary allowed-signers file when validating its own signature.
205
+
206
+ When validation fails, the container-global configuration uses:
207
+
208
+ ```ini
209
+ [commit]
210
+ gpgSign = false
211
+ ```
212
+
213
+ and removes Boxdown-owned SSH signing values that would otherwise leave an
214
+ inconsistent configuration.
215
+
216
+ Tag-signing behavior is not changed beyond removing the current unconditional
217
+ `tag.gpgsign=false`. When a copied host configuration explicitly enables tag
218
+ signing, the selected SSH signing configuration can satisfy it. Commit signing
219
+ is the feature guaranteed by this design.
220
+
221
+ ## Lifecycle Consistency
222
+
223
+ The Docker agent mount makes the host agent available to all container
224
+ processes. It therefore covers both SSH access and the `devcontainer exec`
225
+ path used by `boxdown start` and coding-agent commands.
226
+
227
+ A focused container script owns signing configuration and validation. A shared
228
+ `ensureContainerGitSigning` lifecycle helper invokes it after a container is
229
+ started or reused and before control is handed to a shell, coding agent, SSH
230
+ proxy, tunnel, or setup completion.
231
+
232
+ All entry points use the same sequence:
233
+
234
+ 1. Resolve the host signing plan.
235
+ 2. Write signing-aware generated configuration.
236
+ 3. Start or reuse the container.
237
+ 4. Refresh container signing readiness.
238
+ 5. Continue normally whether signing is enabled or disabled.
239
+
240
+ The signing refresh performs a disposable signed-commit test. A failed test
241
+ changes the container-global fallback to unsigned and reports a warning. The
242
+ test does not write to the user's repository.
243
+
244
+ If the host agent or selected key disappears after an interactive process has
245
+ started, a later `git commit` can still fail. Boxdown will document this narrow
246
+ runtime limitation instead of replacing or wrapping the Git executable.
247
+
248
+ ## Doctor and Setup Preflight
249
+
250
+ Signing readiness is optional. Every signing-related doctor result has `pass`
251
+ or `warn` severity and never contributes a required setup failure.
252
+
253
+ Host and preliminary checks cover:
254
+
255
+ 1. The host SSH agent can be queried.
256
+ 2. A signing identity can be selected unambiguously.
257
+ 3. Docker can expose the candidate agent socket to a container.
258
+ 4. When GitHub CLI and the network are available, the selected key is
259
+ registered as a GitHub SSH signing key.
260
+
261
+ Container checks cover:
262
+
263
+ 1. `SSH_AUTH_SOCK` is reachable.
264
+ 2. The selected public key is present in `ssh-add -L`.
265
+ 3. A disposable Git repository can create a signed commit.
266
+ 4. The disposable commit contains a valid SSH signature from the selected
267
+ fingerprint.
268
+
269
+ `boxdown setup` runs relevant host checks during its existing preliminary
270
+ doctor stage and prints warnings before container creation. After the
271
+ container starts, the shared signing refresh supplies the full container
272
+ validation.
273
+
274
+ `boxdown doctor` runs the host checks on every invocation. It runs full
275
+ container validation in an existing Boxdown container. When no Boxdown
276
+ container exists, it may use an already available local Boxdown image for a
277
+ disposable probe; it does not pull an image solely for signing diagnostics.
278
+ If a full probe is unavailable, doctor reports that limitation as a warning.
279
+
280
+ ## GitHub Verification
281
+
282
+ A commit is cryptographically signed when Git creates and validates its SSH
283
+ signature. GitHub displays that commit as Verified only when the corresponding
284
+ public key is registered in the user's GitHub account as an SSH signing key.
285
+
286
+ GitHub treats authentication and signing registrations separately. A public
287
+ key already registered for SSH authentication must be uploaded a second time
288
+ with type `signing`.
289
+
290
+ Boxdown checks the selected key against `user/ssh_signing_keys` when possible.
291
+ If it is absent, Boxdown keeps local signing enabled and warns that GitHub will
292
+ not display Verified until the user completes the one-time registration. The
293
+ warning includes an actionable command such as:
294
+
295
+ ```bash
296
+ gh ssh-key add <public-key-file> --type signing --title "Boxdown commit signing"
297
+ ```
298
+
299
+ Boxdown does not execute this account mutation automatically. GitHub or
300
+ network unavailability never disables a locally usable signing identity after
301
+ that identity has already been selected.
302
+
303
+ ## Security Model
304
+
305
+ The agent bridge does not expose private key bytes, but any process in the
306
+ container that can access the socket can request operations from every identity
307
+ exposed by that host agent. Enabling signing therefore grants the container a
308
+ signing oracle and potentially SSH authentication capability for the lifetime
309
+ of the mount.
310
+
311
+ Documentation will recommend a dedicated signing identity or an agent that
312
+ requires confirmation for sensitive keys. The capability is default-on by
313
+ product decision, but doctor output and security documentation must describe
314
+ the exposure accurately.
315
+
316
+ ## Error Reporting
317
+
318
+ Signing warnings use stable reasons so CLI and JSON doctor output remain
319
+ testable. At minimum, reasons distinguish:
320
+
321
+ - host agent unavailable;
322
+ - no loaded identities;
323
+ - ambiguous loaded identities;
324
+ - configured host SSH signing key not loaded;
325
+ - Docker agent bridge unavailable;
326
+ - public-key snapshot failure;
327
+ - selected identity missing in the container;
328
+ - disposable signing failure;
329
+ - GitHub signing registration missing;
330
+ - GitHub registration check unavailable.
331
+
332
+ Human-readable warnings state that commits will remain unsigned but are not
333
+ blocked. A missing GitHub registration warning instead states that commits are
334
+ signed locally but will not appear Verified on GitHub.
335
+
336
+ ## Testing Strategy
337
+
338
+ ### Unit tests
339
+
340
+ - Resolve a host-configured SSH signing key that is loaded in the agent.
341
+ - Resolve exactly one loaded key that matches GitHub authentication keys.
342
+ - Resolve a single loaded key without GitHub access.
343
+ - Disable signing for zero keys, multiple ambiguous keys, malformed keys, and
344
+ command or API failures.
345
+ - Compare public keys without considering comments.
346
+ - Generate Docker Desktop and native Linux socket mounts correctly.
347
+ - Omit signing mounts and environment values for disabled plans.
348
+ - Generate the signing public-key state mount as read-only.
349
+ - Configure SSH signing after a successful container probe.
350
+ - Fall back to unsigned global configuration after every validation failure.
351
+ - Prove that post-create no longer writes repository-local
352
+ `commit.gpgsign`.
353
+ - Report all signing preflight failures as warnings rather than failures.
354
+ - Exercise `setup`, `start`, coding-agent, SSH proxy, and tunnel paths through
355
+ the shared signing lifecycle.
356
+
357
+ ### Shell-script tests
358
+
359
+ Container signing bootstrap tests use temporary Git homes, repositories, fake
360
+ agent output, and disposable public keys. They verify both the enabled Git
361
+ configuration and the non-blocking unsigned fallback.
362
+
363
+ ### Docker integration test
364
+
365
+ A gated manual or integration test mounts a real agent bridge into a disposable
366
+ container, creates an empty signed commit, and verifies the signature and
367
+ fingerprint. It is skipped when Docker or a usable agent identity is absent.
368
+ It does not push a commit or change a GitHub account.
369
+
370
+ ### Manual acceptance
371
+
372
+ Create a new Boxdown environment and verify signed commits from:
373
+
374
+ - `boxdown start`;
375
+ - `boxdown codex`;
376
+ - an SSH session through the generated Boxdown alias.
377
+
378
+ Push a test branch only when explicitly authorized, then verify GitHub's badge
379
+ before and after registering the selected public key as a signing key.
380
+
381
+ ## Documentation
382
+
383
+ Implementation updates will include:
384
+
385
+ - a dedicated commit-signing feature document;
386
+ - README behavior and security notes;
387
+ - setup and doctor documentation;
388
+ - architecture documentation for signing state, generated mounts, and trust
389
+ boundaries;
390
+ - troubleshooting for ambiguity, absent agents, Docker mount failures,
391
+ unsigned fallback, and missing GitHub verification;
392
+ - a changeset describing default-on best-effort signing.
393
+
394
+ The documentation will explicitly state that multiple ambiguous agent keys
395
+ disable signing rather than causing Boxdown to guess, and that this fallback
396
+ allows unsigned commits.
@@ -0,0 +1,43 @@
1
+ # Progress CI Regression Design
2
+
3
+ ## Goal
4
+
5
+ Restore the test suite by fixing progress checklist cleanup in every output mode
6
+ and updating stale spinner-label assertions without changing user-facing output.
7
+
8
+ ## Root Cause
9
+
10
+ `ProgressReporter.end()` stops timers before checking the output mode, but it
11
+ clears checklist state only after the interactive-only rendering guards. In
12
+ `none` and `verbose` modes, `isChecklistActive()` therefore remains true after
13
+ the reporter ends.
14
+
15
+ The SSH spinner labels remain present and are used when no checklist is active.
16
+ Their source expressions became conditional when checklist-owned output was
17
+ introduced, so the source-text test's direct-property regular expressions no
18
+ longer match them.
19
+
20
+ ## Design
21
+
22
+ Move internal checklist cleanup ahead of the output-mode and section-rendering
23
+ guards in `ProgressReporter.end()`. Ending a reporter will always clear its
24
+ steps and rendered-line count, while only interactive reporters with an open
25
+ section will write the visual terminator.
26
+
27
+ Keep the existing source-presence test because it guards friendly spinner copy,
28
+ but make the two SSH assertions check for the label text rather than requiring
29
+ a particular property-expression shape. Existing runtime tests continue to
30
+ verify that checklist mode suppresses the standalone spinners.
31
+
32
+ ## Scope
33
+
34
+ - Modify `src/progress.ts` only to make state cleanup mode-independent.
35
+ - Modify the two SSH label assertions in `__tests__/app.test.ts`.
36
+ - Do not change progress APIs, spinner copy, output formatting, or command flow.
37
+
38
+ ## Verification
39
+
40
+ Use the two CI failures as the red phase, then run the targeted progress tests,
41
+ the full test suite, lint, and build. Verification must use a supported Node 24
42
+ runtime because the current host Node 26 runtime is incompatible with one of
43
+ the installed CommonJS test dependencies.
package/docs/testing.md CHANGED
@@ -21,12 +21,18 @@ Unit tests should avoid starting Docker. Prefer pure tests for:
21
21
  - Workspace path resolution and state directory selection.
22
22
  - Generated devcontainer config shape.
23
23
  - SSH config block creation and idempotent replacement.
24
+ - SSH install target parsing and prompt behavior, including explicit target
25
+ flags, prompt selection, prompt skip/cancel, and non-TTY fallback.
26
+ - Codex app/global-state target installation, legacy path migration,
27
+ and idempotent project injection.
24
28
  - Lifecycle status and doctor output formatting.
25
29
  - Workspace metadata and list output formatting.
26
30
  - Safety invariants, such as not packaging `.ssh/` key material.
27
31
 
28
32
  Use temporary directories for workspace and state tests. Do not write to the
29
- user's real SSH config in unit tests.
33
+ user's real SSH config or Codex app config in unit tests. Use
34
+ `BOXDOWN_CODEX_APP_CONFIG` or direct helper path overrides for Codex config
35
+ fixtures.
30
36
 
31
37
  ## Build and CLI Smoke Tests
32
38
 
@@ -49,15 +55,42 @@ Confirm `assets/devcontainer/**` is included and `.ssh/` is not.
49
55
  Manual Docker acceptance is heavier and should be done intentionally:
50
56
 
51
57
  ```sh
58
+ boxdown setup --workspace /path/to/repo
59
+ boxdown setup --workspace /path/to/repo --target codex
60
+ boxdown setup --workspace /path/to/repo --target claude
52
61
  boxdown start --workspace /path/to/repo
53
62
  boxdown list
54
63
  boxdown list --json
55
64
  boxdown status --workspace /path/to/repo
56
65
  boxdown status --workspace /path/to/repo --json
57
66
  boxdown doctor --workspace /path/to/repo
58
- boxdown ssh-config install --workspace /path/to/repo
67
+ boxdown ssh install --workspace /path/to/repo
68
+ boxdown ssh install --workspace /path/to/repo --target codex
69
+ boxdown ssh install --workspace /path/to/repo --target claude
70
+ CI=1 boxdown ssh install --workspace /path/to/repo
59
71
  ssh <repo-name>-devcontainer 'whoami && pwd'
72
+ boxdown down --workspace /path/to/repo-a --workspace /path/to/repo-b
73
+ boxdown purge --workspace /path/to/disposable-repo
60
74
  ```
61
75
 
76
+ The plain `ssh install` command should show the optional target selector when
77
+ run in an interactive terminal. The explicit `--target codex` and
78
+ `--target claude` commands verify scriptable target installation, and the `CI=1`
79
+ command verifies the non-interactive skip path without blocking.
80
+
81
+ The plain `tunnel` command should prompt for ports in an interactive terminal.
82
+ Use `boxdown tunnel --workspace /path/to/repo --port 3030` when testing the
83
+ non-interactive or fully explicit path.
84
+
85
+ When checking browser access, start a dev server inside the container and keep a
86
+ foreground tunnel open from the host:
87
+
88
+ ```sh
89
+ boxdown tunnel --workspace /path/to/repo
90
+ boxdown tunnel --workspace /path/to/repo --port 3030
91
+ ```
92
+
93
+ Confirm `http://localhost:3030/` works, then stop the tunnel with Ctrl-C.
94
+
62
95
  Run this from at least two repositories when changing workspace isolation,
63
96
  container lookup, SSH config generation, or generated config behavior.
package/docs/todo.md CHANGED
@@ -35,11 +35,11 @@ Acceptance commands:
35
35
 
36
36
  ```sh
37
37
  boxdown start --workspace ~/projects/repos/gh-cp
38
- boxdown ssh-config install --workspace ~/projects/repos/gh-cp
38
+ CI=1 boxdown ssh install --workspace ~/projects/repos/gh-cp
39
39
  ssh gh-cp-devcontainer 'whoami && pwd'
40
40
 
41
41
  boxdown start --workspace ~/projects/repos/lirantaldotcom
42
- boxdown ssh-config install --workspace ~/projects/repos/lirantaldotcom
42
+ CI=1 boxdown ssh install --workspace ~/projects/repos/lirantaldotcom
43
43
  ssh lirantaldotcom-devcontainer 'whoami && pwd'
44
44
  ```
45
45
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "boxdown",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Run a reusable devcontainer sandbox for any local project",
5
5
  "types": "dist/main.d.mts",
6
6
  "type": "module",