@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 +42 -0
- package/docs/standards/ci.md +6 -2
- package/docs/standards/kit-surface.md +9 -2
- package/docs/standards/lifecycle.md +6 -3
- package/docs/standards/releases.md +6 -5
- package/docs/standards/security.md +17 -3
- package/docs/standards/style.md +2 -1
- package/docs/standards/testing.md +2 -1
- package/docs/standards/toolchain.md +19 -13
- package/package.json +1 -1
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
|
package/docs/standards/ci.md
CHANGED
|
@@ -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
|
|
70
|
-
shards
|
|
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/`
|
|
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-
|
|
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
|
|
18
|
-
run
|
|
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.
|
|
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
|
|
85
|
+
### WC-REL-7 · Versions are `x.y.z`; before 1.0.0 a breaking change raises the minor
|
|
86
86
|
|
|
87
|
-
- **Why:**
|
|
88
|
-
|
|
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
|
|
91
|
-
|
|
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
|
-
|
|
30
|
-
-
|
|
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
|
|
package/docs/standards/style.md
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
|
71
|
-
|
|
72
|
-
`exclude`,
|
|
73
|
-
|
|
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": ">=
|
|
85
|
-
|
|
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
|
|
89
|
-
|
|
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.
|
|
212
|
-
|
|
213
|
-
|
|
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.
|
|
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",
|