@wirecat/cli-base 0.61.1 → 0.61.3

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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,36 @@ Versions before 0.8.0 are in the [git tags](https://github.com/WireCatLabs/cli-b
8
8
  Every entry says what changed as a caller sees it, why, and what to watch for — the rules are
9
9
  [`docs/dev/CONVENTIONS.md`](docs/dev/CONVENTIONS.md#the-changelog).
10
10
 
11
+ ## 0.61.3 — 26.10.2026
12
+
13
+ ### Fixed
14
+
15
+ - **Handbook corrections from the cli-template 0.6.0 audit.** [CI](docs/standards/ci.md): the shared `node-ci.yml`
16
+ runs a CLI's contract, leak, version, generate, test matrix, docs, package and command lint steps itself with
17
+ `cli: true`; the repository's `ci.yml` names a step it cannot run yet in `skip` and puts only extra checks in
18
+ `checks`. The page used to say the repository listed those steps in `checks`.
19
+ [Architecture](docs/standards/architecture.md): a command may fall back to `process.env` when the environment leaves
20
+ `env` unset, as in `environmentOf(this).env ?? process.env`; the two rules on the command environment no longer
21
+ disagree. [Testing](docs/standards/testing.md): tests that only read may share a profile; a test that writes config
22
+ or the store uses its own profile or undoes the write in a `finally` block. The upgrade check defines local state:
23
+ data the CLI's own code writes and reads back, not cli-core's config file and run records.
24
+ [Documents](docs/standards/docs.md): an empty `## Unreleased` is not kept, and a developer page anchors claims about
25
+ behaviour, measurements and findings, not instructions. [New CLI](docs/standards/new-cli.md): the copy tool under
26
+ `kit/` follows the rules for code and the repository but not those for a CLI's commands and runtime, and the
27
+ `CLIDEV_` prefix is a placeholder like `clidev`. The repository lists on the CI, Toolchain, Security and Documents
28
+ pages now include cli-template.
29
+
30
+ ## 0.61.2 — 26.10.2026
31
+
32
+ ### Fixed
33
+
34
+ - **A failing test shard in `node-ci.yml` names the failed test.** With `test-shards` above 1, each shard wrote only a
35
+ blob report, so its log said a shard failed but not which test; the shard now also prints Vitest's default report
36
+ and a GitHub annotation with the test's file and line. Its blob report is uploaded even when its tests fail, so it
37
+ can be downloaded from the run. The `coverage` job still fails when any shard fails. A caller gets this when it moves
38
+ its `node-ci.yml` pin to the release that carries it; a caller without shards already had both from Vitest's
39
+ defaults.
40
+
11
41
  ## 0.61.1 — 26.10.2026
12
42
 
13
43
  ### Fixed
@@ -144,7 +144,8 @@ at start-up and the `serve` relay are in [Lifecycle](lifecycle.md); the store's
144
144
  - **Why:** two registries of the same environment must both be filled on every run, and one is always forgotten.
145
145
  - **Enforced by:** review only.
146
146
  - **Example:** a CLI extends `BaseEnvironment`, hands it to cli-core's `provide(program, environment)`, and its
147
- commands read it with `environmentOf<Environment>(command)`, never with `process.*`.
147
+ commands read it with `environmentOf<Environment>(command)`. `process.*` appears only as the default for a field
148
+ the environment leaves unset, as WC-ARCH-10 says: `environmentOf(this).env ?? process.env`.
148
149
  - **Exceptions:** a CLI that does not use cli-core's command skeleton keeps one environment of its own.
149
150
  - **Applies to:** CLIs built on cli-core's command skeleton. **Since:** cli-base 0.41.0. **Rulings:** none.
150
151
 
@@ -11,7 +11,8 @@ Rule ids on this page are `WC-CI-<n>`. How to read a rule, how ids work and whic
11
11
  [Security page](security.md).
12
12
 
13
13
  "The ten repositories" on this page means the six CLIs (tg, max, memo, drive, zoom, wa), cli-base, cli-core,
14
- cli-testing, cli-docs, plus community itself.
14
+ cli-testing, cli-docs, plus community itself. cli-template follows every rule here as a CLI does
15
+ ([New CLI](new-cli.md#the-template-repository-itself)).
15
16
 
16
17
  ## Rules
17
18
 
@@ -51,7 +52,7 @@ cli-testing, cli-docs, plus community itself.
51
52
  - **Why:** it is the first build-level answer on a pull request, so it must stay fast enough that nobody wants it gone.
52
53
  - **Enforced by:** `standards.yml:standards` · `pnpm ci:quick`.
53
54
  - **Example:** what each repository runs today:
54
- - tg, max, memo and wa: `pnpm typecheck && pnpm build && pnpm lint:commands`;
55
+ - tg, max, memo, wa and cli-template: `pnpm typecheck && pnpm build && pnpm lint:commands`;
55
56
  - drive: `pnpm typecheck && pnpm lint:commands`, which builds first;
56
57
  - zoom, cli-base and cli-testing: `pnpm typecheck`;
57
58
  - cli-core: `pnpm typecheck` and its command lint test;
@@ -95,8 +96,8 @@ cli-testing, cli-docs, plus community itself.
95
96
  cli-base `macos.yml` for the path code. cli-docs and community run `security-pr.yml`, a new-commit secret scan;
96
97
  cli-docs validates its site before each deployment instead, and community has no build. Third-party security apps
97
98
  may post their own checks.
98
- - **Applies to:** the CLIs, cli-base, cli-core and cli-testing. cli-base, whatsapp-cli, tg-cli, max-cli, cli-core
99
- cli-memo, zoom-cli and drive-cli have `pr.yml`; cli-testing does not have it yet. **Since:**
99
+ - **Applies to:** the CLIs, cli-base, cli-core and cli-testing. cli-base, whatsapp-cli, tg-cli, max-cli, cli-core,
100
+ cli-memo, zoom-cli, drive-cli and cli-template have `pr.yml`; cli-testing does not have it yet. **Since:**
100
101
  cli-base 0.41.0 (pull request validation since cli-base 0.45.0). **Rulings:** none.
101
102
 
102
103
  ### WC-CI-6 · Run the changelog guard on every pull request
@@ -115,9 +116,12 @@ cli-testing, cli-docs, plus community itself.
115
116
  catches that merge skew the same hour, not at the next release.
116
117
  - **Enforced by:** `main.yml:validation` · `node-ci.yml:check` · `node-ci.yml:bun` · `node-ci.yml:secrets`.
117
118
  - **Example:** `main.yml` calls the repository's own `ci.yml`, which calls cli-base `node-ci.yml`. That runs
118
- `pnpm lint`, `pnpm typecheck`, `pnpm test:coverage`, `pnpm build`, then the project's `checks` lines (for max:
119
- contracts, the no-leak scan, the version check, `pnpm generate` and `git diff --exit-code`, the test matrix,
120
- `docs:check` and parity), a Bun job, and a gitleaks scan of the whole history.
119
+ `pnpm lint`, `pnpm typecheck`, `pnpm test:coverage` and `pnpm build`. With `cli: true` it then runs one named step
120
+ per CLI script: `contracts:check`, `contracts:upgrade`, `security:no-leak`, `version:check`, `generate:check`,
121
+ `test:matrix:check`, `docs:check`, `package:check`, `lint:commands`, and `parity:check` with `parity: true`. Then
122
+ the project's `checks` lines, a Bun job, and a gitleaks scan of the whole history. The shared workflow owns the CLI
123
+ steps: the repository's `ci.yml` names a step it cannot run yet in `skip`, with the reason beside it, and puts only
124
+ its extra checks in `checks` (tg and max: `pnpm check:agent-docs`).
121
125
  - **Exceptions:** platform suites (Windows, macOS, install checks) are skipped here and run at release, monthly or on
122
126
  request. Nothing in this run publishes or tags. cli-docs runs its full validation before each deployment and monthly
123
127
  instead; community has no build to validate.
@@ -140,8 +144,8 @@ what CI runs, an audit reads the `uses: WireCatLabs/cli-base/.github/workflows/
140
144
  - **Why:** one shared workflow keeps the full suite the same everywhere, and a pin keeps a cli-base change from
141
145
  breaking ten repositories at once.
142
146
  - **Enforced by:** `ci.yml:ci`.
143
- - **Example:** `uses: WireCatLabs/cli-base/.github/workflows/node-ci.yml@<commit> # v0.31.0`, with the project's own
144
- checks in the `checks` input and `bun-run` for the Bun job. A job that only one repository needs stays beside it in
147
+ - **Example:** `uses: WireCatLabs/cli-base/.github/workflows/node-ci.yml@<commit> # v0.31.0`, with `cli: true` for
148
+ the CLI steps (WC-CI-7), the project's extra checks in the `checks` input and `bun-run` for the Bun job. A job that only one repository needs stays beside it in
145
149
  that repository's `ci.yml`.
146
150
  - **Exceptions:** never pin to `main`. cli-base itself calls `./.github/workflows/node-ci.yml`. cli-docs has its own
147
151
  full workflow for the site, and community has none.
@@ -182,8 +186,8 @@ what CI runs, an audit reads the `uses: WireCatLabs/cli-base/.github/workflows/
182
186
  - **Why:** a red merge breaks `main` for every parallel branch, and an admin merge skips the only gate there is.
183
187
  - **Enforced by:** `whatsapp-cli: main ruleset required checks` · `cli-core: main ruleset required checks` ·
184
188
  `tg-cli: main ruleset required checks` · `max-cli: main ruleset required checks` ·
185
- `cli-memo: main ruleset required checks` · `drive-cli: main ruleset required checks`, plus the main ruleset of
186
- cli-base itself. In each repository with pr.yml, the ruleset requires the pull request validation jobs besides the
189
+ `cli-memo: main ruleset required checks` · `drive-cli: main ruleset required checks`, plus the main rulesets of
190
+ cli-base and cli-template. In each repository with pr.yml, the ruleset requires the pull request validation jobs besides the
187
191
  standards job. cli-testing and cli-docs have no pr.yml, so their rulesets require only the standards job, and
188
192
  review covers the rest. zoom-cli has pr.yml,
189
193
  but its ruleset still requires only the standards job.
@@ -27,8 +27,8 @@ Rule ids on this page are `WC-DOC-<n>`. How to read a rule, how ids work and whi
27
27
  - **Enforced by:** `tg-cli: pnpm docs:check` · `max-cli: pnpm docs:check` · `whatsapp-cli: pnpm docs:check` ·
28
28
  `zoom-cli: pnpm docs:check`.
29
29
  - **Example:** write "`--limit` defaults to 50", not "~~20~~ 50 (correction 2026-10-01, NEED-12)".
30
- - **Exceptions:** none. `cli-dev docs-check` checks this when the CLI's rules module sets `userPage`. tg, max, wa
31
- and zoom set it; memo, drive and cli-base's default check do not.
30
+ - **Exceptions:** none. `cli-dev docs-check` checks this when the CLI's rules module sets `userPage`. tg, max, wa,
31
+ zoom and cli-template set it; memo, drive and cli-base's default check do not.
32
32
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
33
33
 
34
34
  ### WC-DOC-3 · Every document states current facts; rewrite a wrong sentence in place
@@ -110,7 +110,9 @@ Rule ids on this page are `WC-DOC-<n>`. How to read a rule, how ids work and whi
110
110
  - **Enforced by:** review only.
111
111
  - **Example:** cite `src/store/open.ts:42` for our code, a URL for anything outside, a commit for history. Write "ran
112
112
  X, saw Y" for what you ran, and "the docs describe X" for what you only read. Say at the top what is not verified.
113
- - **Exceptions:** user pages state behaviour and do not carry code anchors.
113
+ - **Exceptions:** an instruction or a rule ("run `pnpm test`", "one store per profile") needs no anchor; a claim about
114
+ how the code or a service behaves, a measurement and a finding do. A file path with no line number is enough when
115
+ the claim is about the whole file. User pages state behaviour and do not carry code anchors.
114
116
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
115
117
 
116
118
  ### WC-DOC-12 · A runnable command in a document has no placeholder in it
@@ -136,7 +138,9 @@ Rule ids on this page are `WC-DOC-<n>`. How to read a rule, how ids work and whi
136
138
  - **Why:** the GitHub release and the release scripts read the changelog by its headings.
137
139
  - **Enforced by:** `pnpm docs:check` · `changelog-guard.yml`.
138
140
  - **Example:** `## Unreleased` at the top while work is merged; a release dates it as `## 0.41.0 — 17.10.2026`. Each
139
- version heading and the pending heading appear once.
141
+ version heading and the pending heading appear once. The pending section exists only while it has entries: after a
142
+ release the changelog opens on the dated section, and the next entry adds `## Unreleased` back. Never keep an empty
143
+ one.
140
144
  - **Exceptions:** max-cli names the pending section `## Не выпущено`.
141
145
  - **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
142
146
 
@@ -114,8 +114,12 @@ upgrade, and no script or agent reads its answers.
114
114
  | [WC-LAY-2](layout.md) | The skeleton keeps it in `src/bin/clidev.ts`. The package's `"bin"` names the copy tool, `kit/dist/main.js`. | `new` writes the CLI's own `"bin"` entry into the copy. |
115
115
  | [WC-OUT-18](output.md#wc-out-18--exit-with-cli-bases-code-for-each-kind-of-failure), [WC-OUT-19](output.md#wc-out-19--report-a-wrong-command-line-as-validation_error-exit-2) | The copy tool is exempt: it exits 1 with a plain sentence. The skeleton follows them. | People run the copy tool by hand; nothing branches on its exit code or parses its errors (owner, ASK-349). |
116
116
  | [WC-TOOL-4](toolchain.md#wc-tool-4--extend-the-shared-typescript-config-and-do-not-loosen-it), [WC-TEST-11](testing.md#wc-test-11--keep-a-coverage-floor-and-raise-it-never-lower-it) | Both programs follow them: `kit/tsconfig.json` extends the shared base, and `kit/src` has its own coverage floor. | The copy tool writes into a person's new repository, so it gets the same build-time and test checks (owner, ASK-349). |
117
- | [WC-TEST-14](testing.md#wc-test-14--check-the-upgrade-from-the-previous-published-version) | Exempt: `ci.yml` skips `contracts:upgrade`. | The skeleton is never published under its own name, so there is no earlier package to upgrade from. A CLI made from the template turns the step on after its first release. |
118
- | [WC-DOC-12](docs.md#wc-doc-12--a-runnable-command-in-a-document-has-no-placeholder-in-it) | Exempt only for the placeholder names `clidev`, `clidev-cli`, `@wirecat/clidev-cli` and `WireCatLabs/clidev-cli`. | `new` replaces each of them with the CLI's own name. Any other placeholder is still a gap. |
117
+ | [WC-TEST-14](testing.md#wc-test-14--check-the-upgrade-from-the-previous-published-version) | Exempt: `ci.yml` skips `contracts:upgrade`. | The skeleton has no local state of its own: its config file and run records are written by cli-core. A CLI made from the template turns the step on once it has local state and a published version to upgrade from. |
118
+ | [WC-DOC-12](docs.md#wc-doc-12--a-runnable-command-in-a-document-has-no-placeholder-in-it) | Exempt only for the placeholder names `clidev`, `clidev-cli`, `@wirecat/clidev-cli` and `WireCatLabs/clidev-cli`, and the upper-case prefix `CLIDEV_` (as in `CLIDEV_PROFILE`). | `new` replaces each of them with the CLI's own name. Any other placeholder is still a gap. |
119
119
  | [WC-DOC-17](docs.md#wc-doc-17--each-entry-says-what-changed-for-the-caller-why-and-what-to-watch-for) | A changelog entry may name a file path. | The template's readers are CLI authors, who edit those files or take them with `upgrade`; for them a path is what they see. |
120
120
 
121
- Every other rule that applies to all CLIs, or to the ten repositories, applies to cli-template as it is written.
121
+ Every other rule that applies to all CLIs, or to the ten repositories, applies to cli-template as it is written,
122
+ with one limit for the copy tool. It follows the rules for code and the repository: Toolchain, Layout, Style,
123
+ Testing, Security and Documents. It has no profile, no machine mode, no MCP server, no store and no agent reading its
124
+ answers, so the rules for a CLI's commands and runtime do not reach it: Commands, Output, MCP, Accounts, Permissions,
125
+ Architecture, Lifecycle, Store, Data model and Time. Those apply to the skeleton.
@@ -31,7 +31,10 @@ inside a URL, and a long opaque string. Inside any typed word cli-core also find
31
31
  only the secret: a token of a known format (GitHub `ghp_…`/`github_pat_…`, `sk-…`, Slack `xox…-`, AWS `AKIA…`, Google
32
32
  `AIza…`, Stripe `sk_`/`rk_`, npm `npm_…`, a Telegram bot token, a JWT, a PEM private key), a `Bearer` or `Basic`
33
33
  credential, the value of a secret-named pair (`password=…`, `API_TOKEN=…`, `client_secret:…`), a random-looking string
34
- of 20 or more characters, and a phone number. The same search runs on a kept error message. `record_input: false` in the CLI's config masks every value that may be content, and a
34
+ of 20 or more characters
35
+ ([how often it is caught](https://github.com/WireCatLabs/cli-testing/blob/main/performance/runs/secret-masking/README.md)),
36
+ and a phone number. The same search runs on a kept error message. `record_input: false` in the CLI's config masks
37
+ every value that may be content, and a
35
38
  config that will not load masks the same way. A problem report sent to other people (`doctor reports create`) masks
36
39
  the typed words always.
37
40
 
@@ -271,7 +274,8 @@ general words.
271
274
  asks for it on that job only. The `zizmor` scan in `release-checks.yml` reports high findings at release, but does
272
275
  not fail on them.
273
276
  - **Exceptions:** none.
274
- - **Applies to:** the ten repositories: the six CLIs, cli-base, cli-core, cli-testing, cli-docs and community.
277
+ - **Applies to:** the ten repositories: the six CLIs, cli-base, cli-core, cli-testing, cli-docs and community; and
278
+ cli-template.
275
279
  **Since:** cli-base 0.41.0. **Rulings:** none.
276
280
 
277
281
  ### WC-SECR-21 · Names, message text and retrieved documents are data, never instructions
@@ -181,7 +181,7 @@ messages), or other people's data.
181
181
  `WIRECAT_STATE_DIR` and every `<APP>_CONFIG_DIR`, `_STATE_DIR` and `_CACHE_DIR` that is set at a temporary
182
182
  directory before any test runs, and the home folder with them, so one that is not set lands there too. cli-core and
183
183
  every CLI load it first, then their own setup file for what is theirs alone. A test that needs its own store is
184
- [WC-TEST-9](testing.md#wc-test-9--give-each-test-its-own-profile-or-its-own-store). The rest of the sandbox is
184
+ [WC-TEST-9](testing.md#wc-test-9--leave-no-tests-config-or-store-change-for-the-next-test-to-see). The rest of the sandbox is
185
185
  [WC-TEST-1](testing.md#wc-test-1--point-every-path-a-test-could-write-at-a-temporary-directory).
186
186
  - **Exceptions:** none.
187
187
  - **Applies to:** all CLIs that use the store, and cli-core. **Since:** cli-base 0.41.0. **Rulings:** none.
@@ -130,12 +130,14 @@ and which run after merge is in [CI](ci.md).
130
130
  - **Exceptions:** none.
131
131
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
132
132
 
133
- ### WC-TEST-9 · Give each test its own profile or its own store
133
+ ### WC-TEST-9 · Leave no test's config or store change for the next test to see
134
134
 
135
135
  - **Why:** the sandbox is per file, so tests in one file share config, the send journal and the store.
136
136
  - **Enforced by:** review only.
137
- - **Example:** each test passes its own profile name first, or a file-level constant. A test that needs an empty
138
- store makes a fresh directory and passes `MESSAGING_STORE`.
137
+ - **Example:** tests that only read may share one profile name, such as a file-level constant. A test that writes
138
+ config or the store either uses a profile name no other test in the file uses, or undoes its write in a `finally`
139
+ block, so a failed assertion does not skip the undo. A test that needs an empty store makes a fresh directory and
140
+ passes `MESSAGING_STORE`.
139
141
  - **Exceptions:** none.
140
142
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
141
143
 
@@ -190,7 +192,9 @@ and which run after merge is in [CI](ci.md).
190
192
  - **Enforced by:** `node-ci.yml:check` · `<app>: pnpm contracts:upgrade`.
191
193
  - **Example:** `pnpm contracts:upgrade` runs the previous published package and this build on the same synthetic
192
194
  state and checks that the data survives. Each store migration also has its own upgrade test (WC-STORE-9).
193
- - **Exceptions:** a CLI with no local state.
195
+ - **Exceptions:** a CLI with no local state, or with no published version yet. Local state is data the CLI's own
196
+ code writes and reads back in a later run, such as a store, a session or a cache. A config file and run records
197
+ that only cli-core writes do not count.
194
198
  - **Applies to:** all CLIs with local state. wa has no upgrade contract yet. **Since:** cli-base 0.41.0. **Rulings:** none.
195
199
 
196
200
  ### WC-TEST-15 · A refactor keeps `commands --json` and the MCP tool list as they were
@@ -16,7 +16,8 @@ one source of the presets. Each repository holds a copy of them in `config/wirec
16
16
  `adopt-standards.py`.
17
17
 
18
18
  "The ten repositories" on this page means the six CLIs (tg, max, memo, drive, zoom, wa), cli-base, cli-core,
19
- cli-testing, cli-docs, plus community itself.
19
+ cli-testing, cli-docs, plus community itself. cli-template follows every rule here as a CLI does
20
+ ([New CLI](new-cli.md#the-template-repository-itself)).
20
21
 
21
22
  ## Rules
22
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-base",
3
- "version": "0.61.1",
3
+ "version": "0.61.3",
4
4
  "description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",