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
@@ -6,7 +6,6 @@
6
6
  boxdown start
7
7
  boxdown codex
8
8
  boxdown claude
9
- boxdown cc
10
9
  boxdown opencode
11
10
  boxdown antigravity
12
11
  ```
@@ -16,18 +15,30 @@ boxdown antigravity
16
15
  ```sh
17
16
  --workspace <path>
18
17
  --recreate
18
+ --verbose
19
19
  ```
20
20
 
21
+ Use `boxdown setup` when you want to prepare the devcontainer and SSH/app
22
+ integration without opening an interactive shell.
23
+
21
24
  `boxdown shell` remains supported as an alias for `boxdown start`, but `start`
22
25
  is the canonical command used in help and documentation.
26
+ `boxdown cc` remains supported as an alias for `boxdown claude`, but `claude`
27
+ is the canonical command used in help and documentation.
23
28
 
24
29
  The coding-agent aliases start or reuse the same workspace devcontainer and
25
30
  launch the selected CLI directly:
26
31
 
27
32
  - `boxdown codex` launches `codex`.
28
- - `boxdown claude` and `boxdown cc` launch `claude`.
29
- - `boxdown opencode` launches `opencode`.
30
- - `boxdown antigravity` launches `agy`.
33
+ - `boxdown claude` launches `claude`.
34
+ - `boxdown opencode` installs/updates OpenCode when needed, then launches
35
+ `opencode`.
36
+ - `boxdown antigravity` installs/updates Antigravity when needed, then launches
37
+ `agy`.
38
+
39
+ Container bring-up eagerly installs only Codex and Claude Code. OpenCode and
40
+ Antigravity are lazy installs so projects that do not use them avoid the extra
41
+ disk usage.
31
42
 
32
43
  Pass agent-specific arguments after `--` so Boxdown options stay unambiguous:
33
44
 
@@ -42,7 +53,7 @@ boxdown claude -- --continue
42
53
  3. Generate a Boxdown-owned devcontainer config.
43
54
  4. Install or reuse the pinned Dev Containers CLI.
44
55
  5. Run `devcontainer up --workspace-folder <repo> --override-config <config>`.
45
- 6. Run container lifecycle hooks, including a best-effort coding-agent CLI refresh.
56
+ 6. Run container lifecycle hooks, including a best-effort Codex/Claude CLI refresh.
46
57
  7. Print a dynamic port hint when the configured published port is mapped.
47
58
  8. Run `devcontainer exec ... bash` to open an interactive shell.
48
59
 
@@ -50,6 +61,11 @@ Coding-agent aliases use the same startup flow but skip the port hint, run a
50
61
  best-effort refresh for the selected agent, and exec the agent binary instead of
51
62
  opening `bash`.
52
63
 
64
+ Startup progress is concise by default. Raw Docker, Dev Containers CLI,
65
+ lifecycle hook, and coding-agent install/update logs are captured; on failure,
66
+ Boxdown prints the failed step and a short output tail. Pass `--verbose` to
67
+ stream the full startup output before the interactive shell or agent takes over.
68
+
53
69
  ## Terminal Width
54
70
 
55
71
  Some terminal UIs behave poorly when a host terminal reports an extremely wide
@@ -57,6 +73,12 @@ PTY, especially from embedded terminals. Before opening the shell, Boxdown
57
73
  clamps interactive terminal width to 120 columns when the reported width is
58
74
  larger.
59
75
 
76
+ Before starting the interactive shell or a coding-agent command, Boxdown also
77
+ checks whether the container recognizes the forwarded `TERM` value. If the
78
+ container does not have terminfo for a host-specific terminal such as Ghostty,
79
+ Boxdown falls back to `xterm-256color` while preserving truecolor support with
80
+ `COLORTERM=truecolor`.
81
+
60
82
  Override the default width when you want a wider layout:
61
83
 
62
84
  ```sh
@@ -81,3 +103,16 @@ Docker `runArgs`.
81
103
  The v1 asset config publishes container port `3000` with a dynamic host port.
82
104
  After `up`, Boxdown asks Docker for the mapped host binding and prints the
83
105
  result as an HTTP URL when available.
106
+
107
+ For dev servers that choose another port after the container is already running,
108
+ use the SSH tunnel command instead. Interactive terminals can omit `--port` to
109
+ be prompted for the mapping:
110
+
111
+ ```sh
112
+ boxdown tunnel
113
+ boxdown tunnel --port 3030
114
+ boxdown tunnel --workspace /path/to/project --port 3030
115
+ ```
116
+
117
+ This is the preferred path for host browsers and the Codex in-app browser when a
118
+ server is listening on container-local `localhost`.
@@ -0,0 +1,110 @@
1
+ # Default Commit Signing Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Make new Boxdown environments sign commits through the host SSH agent by default, while retaining an unsigned, warning-only fallback.
6
+
7
+ **Architecture:** A host-side resolver selects one unambiguous public SSH key and snapshots only that key into workspace state. The generated devcontainer config mounts the host agent and the public-key snapshot. A container bootstrap validates a disposable SSH-signed commit and configures global Git for SSH signing or disables it without touching repository-local Git config.
8
+
9
+ **Tech Stack:** Node.js 24, TypeScript, Bash, Git, OpenSSH, Docker/Dev Containers CLI, Node test runner.
10
+
11
+ ## Global Constraints
12
+
13
+ - Never copy private signing-key material into Boxdown state or the container.
14
+ - Multiple ambiguous agent identities disable signing and warn; never select one arbitrarily.
15
+ - Signing diagnostics are `warn`, never a setup-preflight failure.
16
+ - Never write `commit.gpgsign` in the repository-local `.git/config`.
17
+ - Docker integration is optional and must not pull images or push commits.
18
+
19
+ ---
20
+
21
+ ### Task 1: Resolve and persist a signing plan
22
+
23
+ **Files:**
24
+
25
+ - Create: `src/git-signing.ts`
26
+ - Modify: `src/constants.ts`, `src/paths.ts`, `src/config.ts`
27
+ - Test: `__tests__/app.test.ts`
28
+
29
+ **Interfaces:**
30
+
31
+ ```ts
32
+ export type GitSigningReason = 'agent-unavailable' | 'no-identities' | 'ambiguous-identities' | 'configured-key-not-loaded' | 'agent-mount-unavailable'
33
+ export interface GitSigningPlan { enabled: boolean, reason?: GitSigningReason, publicKey?: string, agentSocketSource?: string }
34
+ export function parseSshPublicKey(value: string): string | undefined
35
+ export function selectGitSigningKey(identities: string[], configuredKey?: string, githubKeys?: string[]): { key?: string, reason?: GitSigningReason }
36
+ export function writeGitSigningPublicKey(context: WorkspaceContext, key: string): void
37
+ ```
38
+
39
+ - [ ] Write focused failing tests for parsing keys without comments, configured-key selection, a unique GitHub-key match, single-key fallback, ambiguous fallback, and config mount generation.
40
+ - [ ] Run `node --import tsx --test --test-name-pattern='git signing|devcontainer config generation' __tests__/app.test.ts` and confirm the new assertions fail because the module and mounts do not exist.
41
+ - [ ] Add state paths and container constants, implement the pure resolver and public-key snapshot, and let `buildGeneratedDevcontainerConfig(context, signingPlan)` add read-only key-state and agent mounts only for enabled plans.
42
+ - [ ] Rerun the focused tests and confirm they pass.
43
+
44
+ ### Task 2: Configure and validate container-side Git signing
45
+
46
+ **Files:**
47
+
48
+ - Create: `assets/devcontainer/utils/git-signing-bootstrap.sh`
49
+ - Modify: `assets/devcontainer/utils/git-config-bootstrap.sh`, `assets/devcontainer/hooks/post-create.sh`, `assets/devcontainer/hooks/post-start.sh`
50
+ - Test: `__tests__/app.test.ts`
51
+
52
+ **Interfaces:**
53
+
54
+ ```bash
55
+ bash /opt/boxdown/devcontainer/utils/git-signing-bootstrap.sh
56
+ # Reads BOXDOWN_GIT_SIGNING_ENABLED and BOXDOWN_GIT_SIGNING_KEY_PATH.
57
+ # Exits 0 in both signed and unsigned fallback modes.
58
+ ```
59
+
60
+ - [ ] Write failing shell-hook tests for successful SSH configuration, absent-agent fallback, removal of incompatible `gpg.program`, and absence of repository-local `commit.gpgsign` writes.
61
+ - [ ] Run the matching `devcontainer git config hooks` tests and confirm the new assertions fail.
62
+ - [ ] Implement a temporary-repository signing probe using `ssh-add -L`, a temporary allowed-signers file, and `git commit --allow-empty`; set global SSH signing only after it succeeds, otherwise set global `commit.gpgsign=false` and print a warning.
63
+ - [ ] Remove unconditional global signing disablement and all post-create local `commit.gpgsign` mutations; call the bootstrap after create and start.
64
+ - [ ] Rerun the hook tests and confirm they pass.
65
+
66
+ ### Task 3: Use one signing-aware lifecycle for every command
67
+
68
+ **Files:**
69
+
70
+ - Modify: `src/devcontainer.ts`, `src/main.ts`
71
+ - Test: `__tests__/app.test.ts`
72
+
73
+ **Interfaces:**
74
+
75
+ ```ts
76
+ export async function resolveGitSigningPlan(context: WorkspaceContext): Promise<GitSigningPlan>
77
+ export async function ensureContainerGitSigning(context: WorkspaceContext, plan: GitSigningPlan, options?: ContainerCommandOptions): Promise<void>
78
+ ```
79
+
80
+ - [ ] Write failing command-flow tests proving `startDevcontainer` writes a signing-aware config and invokes the non-fatal container signing refresh for fresh and reused containers.
81
+ - [ ] Run the relevant `CLI execution` and `coding-agent command mapping` tests and confirm they fail on the missing signing lifecycle.
82
+ - [ ] Resolve the plan before writing generated config, pass plan state to the lifecycle script, and call the refresh from shared startup so setup, shell, coding agents, SSH proxy, and tunnels all inherit it.
83
+ - [ ] Ensure refresh failures only warn and do not change the command exit code.
84
+ - [ ] Rerun focused lifecycle tests and confirm they pass.
85
+
86
+ ### Task 4: Add doctor/preflight warnings and user documentation
87
+
88
+ **Files:**
89
+
90
+ - Modify: `src/doctor.ts`, `src/main.ts`, `README.md`, `docs/features/setup.md`, `docs/architecture.md`, `docs/features/README.md`
91
+ - Create: `docs/features/commit-signing.md`, `.changeset/<generated-name>.md`
92
+ - Test: `__tests__/app.test.ts`
93
+
94
+ - [ ] Write failing doctor tests for unavailable agent, ambiguous identities, unusable mount, missing GitHub signing registration, and warning-only setup behavior.
95
+ - [ ] Run the `doctor output` and setup-preflight tests and confirm the new checks are absent.
96
+ - [ ] Add warning-only host signing checks, including an optional GitHub signing-key query; do not add account mutations or required preflight failures.
97
+ - [ ] Document automatic selection order, unsigned fallback, Docker mount limitations, agent security exposure, and GitHub's one-time separate signing-key registration.
98
+ - [ ] Add a changeset for default-on best-effort commit signing.
99
+ - [ ] Rerun focused doctor/setup tests and lint changed Markdown.
100
+
101
+ ### Task 5: Verify the full feature
102
+
103
+ **Files:**
104
+
105
+ - Test: `__tests__/app.test.ts`
106
+
107
+ - [ ] Run `node --import tsx --test __tests__/**/*.test.ts`; record the two known unrelated baseline failures and require no additional failures.
108
+ - [ ] Run `npm run build` and `npm run lint` using local project tooling where available.
109
+ - [ ] With Docker and a loaded agent available, create a disposable container using the generated mount, make an empty signed commit, and verify its SSH fingerprint; do not push it.
110
+ - [ ] Review `git diff --check`, changed docs, and the implementation against `docs/superpowers/specs/2026-07-11-default-commit-signing-design.md`.
@@ -0,0 +1,125 @@
1
+ # Progress CI Regression Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Restore CI by making progress checklist cleanup mode-independent and updating stale spinner-label source assertions.
6
+
7
+ **Architecture:** Preserve the existing `ProgressReporter` API and output behavior. Move only internal cleanup ahead of rendering guards, then loosen two source-presence assertions while leaving runtime checklist-output coverage intact.
8
+
9
+ **Tech Stack:** TypeScript, Node.js 24 test runner, `node:assert`, pnpm, ESLint, tsdown
10
+
11
+ ## Global Constraints
12
+
13
+ - Do not change progress APIs, spinner copy, output formatting, or command flow.
14
+ - Use Node.js 24 for tests because the current Node.js 26 host runtime is incompatible with an installed CommonJS test dependency.
15
+ - Commit documentation separately from the implementation, as explicitly requested.
16
+
17
+ ---
18
+
19
+ ### Task 1: Commit the approved documentation
20
+
21
+ **Files:**
22
+
23
+ - Create: `docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md`
24
+ - Create: `docs/superpowers/plans/2026-07-11-progress-ci-regression.md`
25
+
26
+ **Interfaces:**
27
+
28
+ - Consumes: The user-approved diagnosis and design.
29
+ - Produces: Durable design and execution context for the CI fix.
30
+
31
+ - [ ] **Step 1: Verify documentation formatting**
32
+
33
+ Run:
34
+
35
+ ```bash
36
+ git diff --check
37
+ fnm exec --using v24.15.0 node ../../node_modules/markdownlint-cli/markdownlint.js \
38
+ -c .github/.markdownlint.yml -i 'apm_modules/**' -i '.git' -i '__tests__' \
39
+ -i '.github' -i '.changeset' -i 'CODE_OF_CONDUCT.md' -i 'CHANGELOG.md' \
40
+ -i 'node_modules' -i 'dist' '**/**.md'
41
+ ```
42
+
43
+ Expected: PASS with no whitespace or Markdown lint errors.
44
+
45
+ - [ ] **Step 2: Commit documentation only**
46
+
47
+ ```bash
48
+ git add docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md docs/superpowers/plans/2026-07-11-progress-ci-regression.md
49
+ git commit -m "docs: design progress CI regression fix"
50
+ ```
51
+
52
+ Expected: A commit containing only the design specification and implementation plan.
53
+
54
+ ### Task 2: Fix progress cleanup and stale assertions
55
+
56
+ **Files:**
57
+
58
+ - Modify: `src/progress.ts:147`
59
+ - Test: `__tests__/app.test.ts:3080`
60
+ - Test: `__tests__/app.test.ts:3427`
61
+
62
+ **Interfaces:**
63
+
64
+ - Consumes: `ProgressReporter.end()` and `ProgressReporter.isChecklistActive()`.
65
+ - Produces: Mode-independent reporter cleanup without API changes.
66
+
67
+ - [ ] **Step 1: Verify the existing tests reproduce both CI failures**
68
+
69
+ Run:
70
+
71
+ ```bash
72
+ fnm exec --using v24.15.0 node --import tsx --test \
73
+ --test-name-pattern='reports whether a checklist is active|hidden command helpers use friendly spinner labels' \
74
+ __tests__/app.test.ts
75
+ ```
76
+
77
+ Expected: FAIL because checklist state remains active in `none` mode and the SSH label regular expressions require an outdated direct-property expression.
78
+
79
+ - [ ] **Step 2: Make cleanup independent of rendering mode**
80
+
81
+ In `ProgressReporter.end()`, clear internal state immediately after stopping timers:
82
+
83
+ ```ts
84
+ this.#steps = []
85
+ this.#renderedStepLineCount = 0
86
+ ```
87
+
88
+ Remove the existing duplicate cleanup after the interactive section terminator.
89
+
90
+ - [ ] **Step 3: Make the source-presence assertions expression-shape independent**
91
+
92
+ Replace the two SSH assertions with:
93
+
94
+ ```ts
95
+ assert.match(sshKeySource, /Generating Boxdown SSH identity/)
96
+ assert.match(sshKeySource, /Writing Boxdown SSH public key/)
97
+ ```
98
+
99
+ - [ ] **Step 4: Verify the focused regression tests pass**
100
+
101
+ Run the Step 1 command again.
102
+
103
+ Expected: PASS for both selected tests.
104
+
105
+ - [ ] **Step 5: Run full verification**
106
+
107
+ Run:
108
+
109
+ ```bash
110
+ fnm exec --using v24.15.0 node ../../node_modules/c8/bin/c8.js node --import tsx --test __tests__/**/*.test.ts
111
+ fnm exec --using v24.15.0 node ../../node_modules/eslint/bin/eslint.js .
112
+ fnm exec --using v24.15.0 node ../../node_modules/typescript/bin/tsc
113
+ fnm exec --using v24.15.0 node ../../node_modules/tsdown/dist/run.mjs
114
+ ```
115
+
116
+ Expected: All tests pass, ESLint reports no errors, TypeScript compilation succeeds, and tsdown builds successfully.
117
+
118
+ - [ ] **Step 6: Commit the implementation separately**
119
+
120
+ ```bash
121
+ git add src/progress.ts __tests__/app.test.ts
122
+ git commit -m "fix(progress): clear checklist state in every mode"
123
+ ```
124
+
125
+ Expected: A second commit containing only the production fix and updated assertions.