@wirecat/cli-base 0.59.0 → 0.61.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,48 @@ 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.0 — 26.10.2026
12
+
13
+ ### Changed — may break callers
14
+
15
+ - **A run record keeps the command line as typed**
16
+ ([Security](docs/standards/security.md#wc-secr-1--private-data-and-secrets-never-reach-a-log-a-fixture-or-a-committed-document)).
17
+ The handbook said a run record never holds message text; a run's `argv` now keeps the words typed, so the owner can
18
+ read back what was run. A secret stays hidden: an option or argument marked with cli-core's `secretInput(...)`, one
19
+ named for a token, password, secret, credential, cookie, authorization or API key, a password in a URL, a long
20
+ opaque string. `record_input: false` in a CLI's config masks as before. A CLI's tests that assert a typed marker is
21
+ absent from its run folder now look outside `argv` ([Testing](docs/standards/testing.md)), and a CLI's own document
22
+ that says its run log hides what was typed needs the same change.
23
+
24
+ ## 0.60.0 — 26.10.2026
25
+
26
+ ### Added
27
+
28
+ - **`node-ci.yml` takes `test-shards` (2 to 8) to run `test:coverage` on that many runners.** The `check` job then
29
+ skips the tests; `test (i/n)` jobs each run one Vitest shard with the blob reporter, and a `coverage` job merges
30
+ them and enforces the coverage floor of the repository's Vitest config. Each shard runs with the floor at zero,
31
+ because one shard alone covers a fraction of the code. `coverage` fails, rather than being skipped, when a shard
32
+ fails, and when fewer reports than shards arrive. A repository that requires its tests to pass adds
33
+ `<job> / ci / coverage` to its required checks. With `cli: true`, `test:matrix:check` moves from `check` to
34
+ `coverage` and reads the shards' argv logs joined. Default 1: the tests stay in `check`, as before.
35
+ - **Before 1.0.0, a release with a breaking change raises the minor version**
36
+ ([Releases](docs/standards/releases.md#wc-rel-7--versions-are-xyz-before-100-a-breaking-change-raises-the-minor)).
37
+ A section with an entry under "Changed — may break callers" or "Removed", or a new feature, raises the minor; only
38
+ fixes and document changes raise the patch. npm's `^0.x.y` takes every patch of that minor, so a patch must not
39
+ break anyone. The rule used to allow a breaking change in a patch release.
40
+
41
+ ### Fixed
42
+
43
+ - **Handbook corrections from the cli-template 0.1.8 audit.**
44
+ [Kit surface](docs/standards/kit-surface.md) lists every cli-base and cli-core export the template uses, in `src/`
45
+ and `scripts/`, including `privateFiles` and `profileFree`, and the config files it extends.
46
+ [Toolchain](docs/standards/toolchain.md): the `engines` example is `^22.16.0 || >=24`, as cli-core and cli-template
47
+ pin it; `tsconfig.test.json` extends the shared test config last and sets no `exclude` of its own, since the shared
48
+ file already sets `"exclude": []`; the spell check covers `README.md` and every user page in `docs/`.
49
+ [Style](docs/standards/style.md): a cast may stay only if it was on `main` on 17.10.2026, when the rule against
50
+ casts came in. [CI](docs/standards/ci.md) says where an audit finds the shared workflows CI runs: at the commit
51
+ pinned in the repository's workflow files, not in the npm package.
52
+
11
53
  ## 0.59.0 — 26.10.2026
12
54
 
13
55
  ### Added
@@ -66,8 +66,8 @@ cli-testing, cli-docs, plus community itself.
66
66
 
67
67
  - **Why:** a pull request that lowers coverage or breaks a contract must fail before it merges; once on `main` it
68
68
  blocks every other branch and the next release. It costs 2–3 minutes per pull request in the CLIs (measured: wa
69
- 2.9, tg 2.2, max 3.0) and about 8 in cli-core, where `test:coverage` alone takes 5m46s; splitting that job into
70
- shards would need a new `node-ci.yml` input.
69
+ 2.9, tg 2.2, max 3.0) and about 5 in cli-core, where `test:coverage` alone takes about 4; `node-ci.yml`'s
70
+ `test-shards` input splits it across runners.
71
71
  - **Enforced by:** `pr.yml:validation`.
72
72
  - **Example:** `pr.yml` runs on `pull_request` and calls the repository's own `ci.yml`, the same full validation as
73
73
  `main` (WC-CI-7): lint, typecheck, `test:coverage` with the coverage floor, build, the contracts, the test matrix,
@@ -133,6 +133,10 @@ cli-core's peer range holds the installed package back; in a repository made fro
133
133
  that apply are those of the handbook in the installed package. A newer `node-ci.yml` may run a step the repository
134
134
  cannot pass yet; the `skip` input names it.
135
135
 
136
+ The shared workflows are not in the npm package, because CI runs the pinned commit, not the installed copy. To see
137
+ what CI runs, an audit reads the `uses: WireCatLabs/cli-base/.github/workflows/…@<commit>` lines in `ci.yml`,
138
+ `release.yml` and `monthly.yml`, and opens each workflow file at that commit in cli-base.
139
+
136
140
  - **Why:** one shared workflow keeps the full suite the same everywhere, and a pin keeps a cli-base change from
137
141
  breaking ten repositories at once.
138
142
  - **Enforced by:** `ci.yml:ci`.
@@ -12,18 +12,25 @@ This page is a reference, not a rule page: it assigns no rule ids. How to start
12
12
 
13
13
  ## What the template imports
14
14
 
15
- The list is what the template's `src/` imports today, type imports included.
15
+ The list is what the template's `src/` and `scripts/` import today, type imports and re-exports included. `kit/` imports
16
+ nothing from either package.
16
17
 
17
18
  | Package and path | Exports |
18
19
  |---|---|
19
20
  | `@wirecat/cli-base` | `catchCrashes`, `CliError`, `configFilePath`, `mcpErrorResult`, `resolvePaths` |
20
21
  | `@wirecat/cli-base/commands` | `annotate`, `CommandInfo`, `describeProgram` |
21
- | `@wirecat/cli-core/cli` | `AppIdentity`, `changesAuthorityAt`, `commandsCommand`, `configCommand`, `Configuration`, `createProgram`, `environmentOf`, `GlobalFlags`, `itemOf`, `LOCAL_READ`, `outputFor`, `ProgramDefinition`, `ProgramOptions`, `readOnlyLockOf`, `REMOTE_WRITE`, `renderAnswer`, `rootOf`, `run`, `RunOptions`, `runsCommand`, `settingsFor`, `skillCommand`, `upgradeCommand` |
22
+ | `@wirecat/cli-base/codegen` | `writeArtifacts` (in `scripts/`) |
23
+ | `@wirecat/cli-base/release` | `DocsRules`, `JOURNAL_IDS`, `markdownFiles`, `packContents` (in `scripts/`) |
24
+ | `@wirecat/cli-core/cli` | `AppIdentity`, `changesAuthorityAt`, `commandsCommand`, `configCommand`, `Configuration`, `createProgram`, `environmentOf`, `GlobalFlags`, `itemOf`, `LOCAL_READ`, `outputFor`, `privateFiles`, `profileFree`, `ProgramDefinition`, `ProgramOptions`, `readOnlyLockOf`, `REMOTE_WRITE`, `renderAnswer`, `rootOf`, `run`, `RunOptions`, `runsCommand`, `settingsFor`, `skillCommand`, `upgradeCommand` |
22
25
  | `@wirecat/cli-core/mcp` | `McpCommand`, `registerCommands`, `runCommandForAgent`, `toolsFromCommands` |
23
26
  | `@wirecat/cli-core/sends` | `Level` |
24
27
  | `@wirecat/cli-core/update` | `appUpdate` |
25
28
  | `@wirecat/cli-core/testing` | `ARGV_LOG`, `argvLogSetup` |
26
29
 
30
+ The template's config files also reach cli-base: `tsconfig.json` and `kit/tsconfig.json` extend
31
+ `tsconfig.base.json`, `tsconfig.test.json` and `tsconfig.scripts.json` extend the files of those names, `vitest.config.ts`
32
+ loads `@wirecat/cli-base/testing/setup`, and `.vale.ini` reads the styles in `config/vale`.
33
+
27
34
  ## When one of them changes
28
35
 
29
36
  A change that makes a CLI change its code for one of these exports ships with two things:
@@ -14,8 +14,8 @@ A "one-shot" command does one job and ends: most commands. A "long-running" comm
14
14
  `lifetime: "persistent"` (it runs until it is stopped, such as `serve` or `watch`) or `lifetime: "interactive"` (it
15
15
  waits for a person, such as a login).
16
16
 
17
- A "run" is one command's record: what it was, how it ended, and its events, never content. The rules on what a
18
- run's events may hold are [WC-SECR-1](security.md#wc-secr-1--private-data-and-secrets-never-reach-a-log-a-fixture-or-a-committed-document)
17
+ A "run" is one command's record: what it was, as typed, how it ended, and its events, which never hold content. The
18
+ rules on what a run may hold are [WC-SECR-1](security.md#wc-secr-1--private-data-and-secrets-never-reach-a-log-a-fixture-or-a-committed-document)
19
19
  and [WC-SECR-2](security.md#wc-secr-2--a-log-keeps-only-known-attribute-keys-and-hides-content-keys).
20
20
 
21
21
  ### Who follows these rules today
@@ -198,7 +198,10 @@ provider's limit as often as it ends. These three rules make the next run carry
198
198
  ### WC-LIFE-15 · Keep a run log through cli-core, and read it back with `runs`
199
199
 
200
200
  A failed command is always kept as a run, unless the person turned recording off with `--no-record` or
201
- `record: false` in the config. `--record` keeps every run of a command that does its work inside cli-core's run
201
+ `record: false` in the config. A run keeps the command line as typed, secrets hidden; `record_input: false` masks
202
+ every value that may be content
203
+ ([WC-SECR-1](security.md#wc-secr-1--private-data-and-secrets-never-reach-a-log-a-fixture-or-a-committed-document)).
204
+ `--record` keeps every run of a command that does its work inside cli-core's run
202
205
  (`context.run`, or `startRecording` in a program of its own). A run is a folder `<state>/runs/<day>/<run_id>/` with
203
206
  `run.json` and `events.jsonl`, one log record per line. `runs list` and `runs show` read it.
204
207
 
@@ -82,13 +82,14 @@ on the [CI](ci.md) page.
82
82
  - **Exceptions:** a shared library that prints no version has no `src/version.ts`.
83
83
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
84
84
 
85
- ### WC-REL-7 · Versions are `x.y.z`; before 1.0.0 the changelog, not the number, marks a breaking change
85
+ ### WC-REL-7 · Versions are `x.y.z`; before 1.0.0 a breaking change raises the minor
86
86
 
87
- - **Why:** before 1.0.0 any release may change the API, so a caller reads "Changed — may break callers" before an
88
- upgrade.
87
+ - **Why:** npm's `^0.40.4` takes every `0.40.x`, so a patch reaches callers who never read its changelog. It must not
88
+ break them.
89
89
  - **Enforced by:** review only.
90
- - **Example:** raise the minor (`0.40.4` to `0.41.0`) for a release with new features, and the patch for one with only
91
- fixes. A breaking change, in either kind of release, has an entry under "Changed — may break callers".
90
+ - **Example:** raise the minor (`0.40.4` to `0.41.0`) for a release with a new feature or with any entry under
91
+ "Changed — may break callers" or "Removed". Raise the patch (`0.40.4` to `0.40.5`) only when every entry is a fix
92
+ or a change to the documents.
92
93
  - **Exceptions:** a pre-release version (`0.41.0-rc.1`) is chosen by hand.
93
94
  - **Applies to:** all CLIs and shared libraries. **Since:** cli-base 0.41.0. **Rulings:** none.
94
95
 
@@ -21,13 +21,27 @@ Rule ids on this page are `WC-SECR-<n>`. How to read a rule, how ids work and wh
21
21
  ### WC-SECR-1 · Private data and secrets never reach a log, a fixture or a committed document
22
22
 
23
23
  Command output is the only place where a message the owner asked to read may appear. A log, a run record, a test
24
- fixture, a document and a commit message never hold message text, a token or a phone number.
24
+ fixture, a document and a commit message never hold message text, a token or a phone number — with one exception:
25
+ a run record keeps the command line the person typed.
26
+
27
+ A run record's `argv` keeps the words as typed by default, so the history of what was run can be read back. A secret
28
+ in them is always `[redacted]`: the value of an option or argument the CLI marks with cli-core's `secretInput(...)`,
29
+ one whose name says token, password, secret, credential, cookie, authorization or API key, a password inside a URL,
30
+ and a long opaque string. `record_input: false` in the CLI's config masks every value that may be content, and a
31
+ config that will not load masks the same way. A problem report sent to other people (`doctor reports create`) masks
32
+ the typed words always.
25
33
 
26
34
  - **Why:** a log or a repository is copied and kept far longer than anyone remembers; private data in it is a leak.
27
35
  - **Enforced by:** `node-ci.yml:secrets`.
28
36
  - **Example:** a log line says `{ "event": "send", "outcome": "sent", "count": 1 }`, not the text that was sent.
29
- - **Exceptions:** none. The scanners catch secrets of known shapes; message text and names are caught only in review.
30
- - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
37
+ `tg messages send Mom "see you at 5"` is kept in its run as typed; `tg bot api set-webhook --body '{…}'` keeps
38
+ `--body [redacted]`, since cli-core marks `--body` with `secretInput`. A CLI's own option that takes a secret is
39
+ declared `secretInput(new Option("--pin <code>"))` — although under
40
+ [WC-SECR-5](#wc-secr-5--a-secret-is-never-taken-on-the-command-line) such an option should not exist.
41
+ - **Exceptions:** a run record's `argv`, as above: messaging CLIs already keep every message in the store, so the
42
+ words typed are not hidden from the owner's own run log. The scanners catch secrets of known shapes; message text
43
+ and names elsewhere are caught only in review.
44
+ - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** NEED-1829.
31
45
 
32
46
  ### WC-SECR-2 · A log keeps only known attribute keys, and hides content keys
33
47
 
@@ -57,7 +57,8 @@ formatting, so this page does not repeat it.
57
57
  - **Example:** narrow with a type guard, `instanceof` or a shape check. A test fixture uses an honest type, such as
58
58
  `Omit<T, "x"> & { x: string | null }`, not `as unknown as T`.
59
59
  - **Exceptions:** `as const` is allowed. A cast that cannot be avoided, because a library is typed too loosely, keeps
60
- a comment on that line that says why. Existing casts are not yet removed; the rule holds for new code.
60
+ a comment on that line that says why. A cast already on `main` on 17.10.2026, the day cli-base 0.41.0 added this
61
+ rule, may stay until that code is changed; `git blame` shows the date of a line. A cast added later is a finding.
61
62
  - **Applies to:** all repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
62
63
 
63
64
  ### WC-STYLE-6 · In a `catch`, narrow with `instanceof` and wrap anything else
@@ -124,7 +124,8 @@ and which run after merge is in [CI](ci.md).
124
124
  - **Enforced by:** review only.
125
125
  - **Example:** test data is visibly fake: `synthetic text ${id}`, the phone number `+71234567890`, the device id
126
126
  `00000000-0000-0000-0000-000000000001`, `your-token-here`. Content that must stay hidden carries a unique marker,
127
- and the test asserts that the marker is absent from output, errors and run events, while the id is present:
127
+ and the test asserts that the marker is absent from output, errors and run events (a run's `argv` keeps what was
128
+ typed, so a marker typed on the command line is looked for outside it), while the id is present:
128
129
  `expect(err).not.toContain(token)`. The positive check stops the test from passing by recording nothing.
129
130
  - **Exceptions:** none.
130
131
  - **Applies to:** all CLIs. **Since:** cli-base 0.41.0. **Rulings:** none.
@@ -67,10 +67,12 @@ cli-testing, cli-docs, plus community itself.
67
67
  - **Why:** a folder that no `tsconfig` covers breaks without a sign, and a test that compares against a constant that
68
68
  does not exist passes for any input while CI stays green.
69
69
  - **Enforced by:** `pnpm typecheck` · `pnpm ci:quick` · `node-ci.yml:check`.
70
- - **Example:** `tsconfig.test.json` extends `@wirecat/cli-base/tsconfig.test.json`, covers all of `src/`, tests
71
- included, checks them with `noEmit` and carries `"exclude": []`. Without that line it inherits the build's
72
- `exclude`, checks no test file and still passes. `tsconfig.scripts.json` extends
73
- `@wirecat/cli-base/tsconfig.scripts.json` and covers `scripts/`. The `typecheck` script runs all three projects.
70
+ - **Example:** `tsconfig.test.json` has `"extends": ["./tsconfig.json", "@wirecat/cli-base/tsconfig.test.json"]`, in
71
+ that order, and adds at most `rootDir` and an `include` for more folders. The shared file covers all of `src/`,
72
+ tests included, checks it with `noEmit` and sets `"exclude": []`, so it must come last and the repository's file sets
73
+ no `exclude` of its own. A file that sets the build's `exclude` again checks no test file and still passes.
74
+ `tsconfig.scripts.json` extends `@wirecat/cli-base/tsconfig.scripts.json` and covers `scripts/`. The `typecheck`
75
+ script runs all three projects.
74
76
  - **Exceptions:** a new folder of TypeScript goes into one of these projects in the same pull request. memo has no
75
77
  `tsconfig.scripts.json` yet.
76
78
  - **Applies to:** the CLIs, cli-core, cli-base and cli-testing. **Since:** cli-base 0.41.0. **Rulings:** none.
@@ -81,13 +83,14 @@ cli-testing, cli-docs, plus community itself.
81
83
  the other.
82
84
  - **Enforced by:** `pnpm smoke:bun` · `node-ci.yml:bun` · `node-ci.yml:platforms` (the unit suite on Windows and
83
85
  macOS).
84
- - **Example:** `"engines": { "node": ">=22" }` in `package.json`. Full validation runs on Node 24. `bun test` cannot
85
- run the vitest suite, so `scripts/smoke.ts` runs the built exports with plain assertions, and the Bun job runs it
86
+ - **Example:** `"engines": { "node": "^22.16.0 || >=24" }` in `package.json`, as cli-core and cli-template have it:
87
+ Node 22 from 22.16.0, then 24 and later. Full validation runs on Node 24. `bun test` cannot run the vitest suite, so
88
+ `scripts/smoke.ts` runs the built exports with plain assertions, and the Bun job runs it
86
89
  (`pnpm build && pnpm smoke:bun`). A new entry point gets a line there. Node 22 runs in the Windows install matrix of
87
90
  tg, max and wa, in zoom's full validation and in cli-core's old-Node job.
88
- - **Exceptions:** cli-docs and community publish no runtime package. memo, drive and cli-base do not run Node 22 in
89
- any workflow yet. The unit suite runs on Windows and macOS for tg, max and drive (`platforms: true`); zoom and
90
- memo still fail tests there, so theirs is off.
91
+ - **Exceptions:** cli-docs and community publish no runtime package. tg needs `^22.19.0`. memo, drive and cli-base
92
+ do not run Node 22 in any workflow yet, and cli-base's own `engines` is still `>=22`. The unit suite runs on Windows
93
+ and macOS for tg, max and drive (`platforms: true`); zoom and memo still fail tests there, so theirs is off.
91
94
  - **Applies to:** the CLIs, cli-core and cli-base. **Since:** cli-base 0.41.0. **Rulings:** NEED-11.
92
95
 
93
96
  ### WC-TOOL-7 · Let Biome format the code, and do not format by hand
@@ -206,11 +209,14 @@ no-unbounded-spread ([WC-TOOL-12](#wc-tool-12--do-not-spread-a-list-of-user-data
206
209
  ### WC-TOOL-17 · Spell-check prose with the shared `cspell` config
207
210
 
208
211
  - **Why:** a typo in a page or a help text is the first thing a reader sees.
209
- - **Enforced by:** `pnpm docs:spell`.
212
+ - **Enforced by:** `pnpm docs:spell` · `pnpm docs:check`.
210
213
  - **Example:** `cspell.json` imports `./config/wirecat/cspell.json` (English, British English, Russian, Spanish and the
211
- WireCat word list) and adds only the project's own words. Code blocks and inline code are not checked.
212
- - **Exceptions:** a general word goes into the community word list, not into each project. In tg, max, wa, zoom and
213
- cli-testing, `cspell` also runs inside `docs:check` in full validation.
214
+ WireCat word list) and adds only the project's own words. A repository checks `README.md` and every user page,
215
+ `docs/*.md`, in `docs:spell` or in `docs:check`. Generated pages (`docs/commands.md`), `docs/dev/` and
216
+ `CHANGELOG.md` may be left out. Code blocks and inline code are not checked.
217
+ - **Exceptions:** a general word goes into the community word list, not into each project. cli-base checks
218
+ `README.md` and `docs/standards/`; cli-docs its site pages in `content/docs/`; community every Markdown file it
219
+ publishes. cli-core, zoom and cli-testing check only `README.md` yet.
214
220
  - **Applies to:** the ten repositories. **Since:** cli-base 0.41.0. **Rulings:** none.
215
221
 
216
222
  ### WC-TOOL-18 · Lint Markdown with `markdownlint-cli2` and the shared preset
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-base",
3
- "version": "0.59.0",
3
+ "version": "0.61.0",
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",