@wirecat/cli-base 0.45.1 → 0.46.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 (330) hide show
  1. package/CHANGELOG.md +1155 -0
  2. package/README.md +17 -2
  3. package/dist/batch.d.ts +0 -1
  4. package/dist/batch.js +0 -1
  5. package/dist/codegen/index.d.ts +0 -1
  6. package/dist/codegen/index.js +0 -1
  7. package/dist/codegen/manifest.d.ts +0 -1
  8. package/dist/codegen/manifest.js +0 -1
  9. package/dist/codegen/model.d.ts +0 -1
  10. package/dist/codegen/model.js +0 -1
  11. package/dist/codegen/names.d.ts +0 -1
  12. package/dist/codegen/names.js +0 -1
  13. package/dist/codegen/pipeline.d.ts +0 -1
  14. package/dist/codegen/pipeline.js +0 -1
  15. package/dist/codegen/runtime.d.ts +0 -1
  16. package/dist/codegen/runtime.js +0 -1
  17. package/dist/codegen/tree.d.ts +0 -1
  18. package/dist/codegen/tree.js +0 -1
  19. package/dist/codegen/typescript.d.ts +0 -1
  20. package/dist/codegen/typescript.js +0 -1
  21. package/dist/codegen/valibot.d.ts +0 -1
  22. package/dist/codegen/valibot.js +0 -1
  23. package/dist/commands/document.d.ts +0 -1
  24. package/dist/commands/document.js +0 -1
  25. package/dist/commands/index.d.ts +0 -1
  26. package/dist/commands/index.js +0 -1
  27. package/dist/commands/page.d.ts +3 -2
  28. package/dist/commands/page.js +3 -2
  29. package/dist/commands/registry.d.ts +0 -1
  30. package/dist/commands/registry.js +0 -1
  31. package/dist/completion/index.d.ts +0 -1
  32. package/dist/completion/index.js +0 -1
  33. package/dist/config.d.ts +0 -1
  34. package/dist/config.js +0 -1
  35. package/dist/credentials.d.ts +0 -1
  36. package/dist/credentials.js +0 -1
  37. package/dist/deadline.d.ts +0 -1
  38. package/dist/deadline.js +0 -1
  39. package/dist/dev/audit.d.ts +0 -1
  40. package/dist/dev/audit.js +0 -1
  41. package/dist/dev/bin.d.ts +0 -1
  42. package/dist/dev/bin.js +0 -1
  43. package/dist/dev/land.d.ts +0 -1
  44. package/dist/dev/land.js +0 -1
  45. package/dist/dev/lint.d.ts +0 -1
  46. package/dist/dev/lint.js +0 -1
  47. package/dist/dev/main.d.ts +0 -1
  48. package/dist/dev/main.js +0 -1
  49. package/dist/dev/next-version.d.ts +0 -1
  50. package/dist/dev/next-version.js +0 -1
  51. package/dist/dev/slow-tests.d.ts +0 -1
  52. package/dist/dev/slow-tests.js +0 -1
  53. package/dist/dev/test-matrix.d.ts +0 -1
  54. package/dist/dev/test-matrix.js +0 -1
  55. package/dist/drain.d.ts +0 -1
  56. package/dist/drain.js +0 -1
  57. package/dist/errors.d.ts +0 -1
  58. package/dist/errors.js +0 -1
  59. package/dist/executable.d.ts +0 -1
  60. package/dist/executable.js +0 -1
  61. package/dist/exit-codes.d.ts +0 -1
  62. package/dist/exit-codes.js +0 -1
  63. package/dist/failure.d.ts +0 -1
  64. package/dist/failure.js +0 -1
  65. package/dist/http/body.d.ts +0 -1
  66. package/dist/http/body.js +0 -1
  67. package/dist/http/index.d.ts +0 -1
  68. package/dist/http/index.js +0 -1
  69. package/dist/index.d.ts +0 -1
  70. package/dist/index.js +0 -1
  71. package/dist/keyring.d.ts +0 -1
  72. package/dist/keyring.js +0 -1
  73. package/dist/logger.d.ts +0 -1
  74. package/dist/logger.js +0 -1
  75. package/dist/logging.d.ts +0 -1
  76. package/dist/logging.js +0 -1
  77. package/dist/mcp/index.d.ts +0 -1
  78. package/dist/mcp/index.js +0 -1
  79. package/dist/output-buffer.d.ts +0 -1
  80. package/dist/output-buffer.js +0 -1
  81. package/dist/paths.d.ts +0 -1
  82. package/dist/paths.js +0 -1
  83. package/dist/pretty.d.ts +0 -1
  84. package/dist/pretty.js +0 -1
  85. package/dist/query/dates.d.ts +0 -1
  86. package/dist/query/dates.js +0 -1
  87. package/dist/query/evaluate.d.ts +0 -1
  88. package/dist/query/evaluate.js +0 -1
  89. package/dist/query/fields.d.ts +0 -1
  90. package/dist/query/fields.js +0 -1
  91. package/dist/query/help.d.ts +0 -1
  92. package/dist/query/help.js +0 -1
  93. package/dist/query/index.d.ts +0 -1
  94. package/dist/query/index.js +0 -1
  95. package/dist/query/parser.d.ts +0 -1
  96. package/dist/query/parser.js +0 -1
  97. package/dist/query/registry.d.ts +0 -1
  98. package/dist/query/registry.js +0 -1
  99. package/dist/query/types.d.ts +0 -1
  100. package/dist/query/types.js +0 -1
  101. package/dist/query/validate.d.ts +0 -1
  102. package/dist/query/validate.js +0 -1
  103. package/dist/release/fold.d.ts +0 -1
  104. package/dist/release/fold.js +0 -1
  105. package/dist/release/index.d.ts +0 -1
  106. package/dist/release/index.js +0 -1
  107. package/dist/release/markdown.d.ts +0 -1
  108. package/dist/release/markdown.js +0 -1
  109. package/dist/release/structure.d.ts +0 -1
  110. package/dist/release/structure.js +0 -1
  111. package/dist/renderer.d.ts +0 -1
  112. package/dist/renderer.js +0 -1
  113. package/dist/result-fields.d.ts +0 -1
  114. package/dist/result-fields.js +0 -1
  115. package/dist/retry.d.ts +0 -1
  116. package/dist/retry.js +0 -1
  117. package/dist/sanitize.d.ts +0 -1
  118. package/dist/sanitize.js +0 -1
  119. package/dist/skill/index.d.ts +0 -1
  120. package/dist/skill/index.js +0 -1
  121. package/dist/standards/audit.d.ts +0 -1
  122. package/dist/standards/audit.js +0 -1
  123. package/dist/standards/effects.d.ts +0 -1
  124. package/dist/standards/effects.js +0 -1
  125. package/dist/standards/errors.d.ts +0 -1
  126. package/dist/standards/errors.js +0 -1
  127. package/dist/standards/fields.d.ts +0 -1
  128. package/dist/standards/fields.js +0 -1
  129. package/dist/standards/flags-how.d.ts +0 -1
  130. package/dist/standards/flags-how.js +0 -1
  131. package/dist/standards/flags-what.d.ts +0 -1
  132. package/dist/standards/flags-what.js +0 -1
  133. package/dist/standards/identifiers.d.ts +0 -1
  134. package/dist/standards/identifiers.js +0 -1
  135. package/dist/standards/index.d.ts +0 -1
  136. package/dist/standards/index.js +0 -1
  137. package/dist/standards/lint.d.ts +0 -1
  138. package/dist/standards/lint.js +0 -1
  139. package/dist/standards/log.d.ts +0 -1
  140. package/dist/standards/log.js +0 -1
  141. package/dist/standards/output.d.ts +0 -1
  142. package/dist/standards/output.js +0 -1
  143. package/dist/standards/registry.d.ts +0 -1
  144. package/dist/standards/registry.js +0 -1
  145. package/dist/standards/steps.d.ts +0 -1
  146. package/dist/standards/steps.js +0 -1
  147. package/dist/standards/tables.d.ts +0 -1
  148. package/dist/standards/tables.js +0 -1
  149. package/dist/standards/vocabulary.d.ts +0 -1
  150. package/dist/standards/vocabulary.js +1 -1
  151. package/dist/streams.d.ts +0 -1
  152. package/dist/streams.js +0 -1
  153. package/dist/testing/index.d.ts +0 -1
  154. package/dist/testing/index.js +0 -1
  155. package/dist/testing/network.d.ts +0 -1
  156. package/dist/testing/network.js +0 -1
  157. package/dist/testing/sandbox.d.ts +0 -1
  158. package/dist/testing/sandbox.js +0 -1
  159. package/dist/testing/setup.d.ts +0 -1
  160. package/dist/testing/setup.js +0 -1
  161. package/dist/time.d.ts +0 -1
  162. package/dist/time.js +0 -1
  163. package/dist/update/index.d.ts +0 -1
  164. package/dist/update/index.js +0 -1
  165. package/docs/standards/ci.md +7 -5
  166. package/docs/standards/layout.md +2 -1
  167. package/docs/standards/permissions.md +10 -7
  168. package/package.json +6 -1
  169. package/dist/batch.d.ts.map +0 -1
  170. package/dist/batch.js.map +0 -1
  171. package/dist/codegen/index.d.ts.map +0 -1
  172. package/dist/codegen/index.js.map +0 -1
  173. package/dist/codegen/manifest.d.ts.map +0 -1
  174. package/dist/codegen/manifest.js.map +0 -1
  175. package/dist/codegen/model.d.ts.map +0 -1
  176. package/dist/codegen/model.js.map +0 -1
  177. package/dist/codegen/names.d.ts.map +0 -1
  178. package/dist/codegen/names.js.map +0 -1
  179. package/dist/codegen/pipeline.d.ts.map +0 -1
  180. package/dist/codegen/pipeline.js.map +0 -1
  181. package/dist/codegen/runtime.d.ts.map +0 -1
  182. package/dist/codegen/runtime.js.map +0 -1
  183. package/dist/codegen/tree.d.ts.map +0 -1
  184. package/dist/codegen/tree.js.map +0 -1
  185. package/dist/codegen/typescript.d.ts.map +0 -1
  186. package/dist/codegen/typescript.js.map +0 -1
  187. package/dist/codegen/valibot.d.ts.map +0 -1
  188. package/dist/codegen/valibot.js.map +0 -1
  189. package/dist/commands/document.d.ts.map +0 -1
  190. package/dist/commands/document.js.map +0 -1
  191. package/dist/commands/index.d.ts.map +0 -1
  192. package/dist/commands/index.js.map +0 -1
  193. package/dist/commands/page.d.ts.map +0 -1
  194. package/dist/commands/page.js.map +0 -1
  195. package/dist/commands/registry.d.ts.map +0 -1
  196. package/dist/commands/registry.js.map +0 -1
  197. package/dist/completion/index.d.ts.map +0 -1
  198. package/dist/completion/index.js.map +0 -1
  199. package/dist/config.d.ts.map +0 -1
  200. package/dist/config.js.map +0 -1
  201. package/dist/credentials.d.ts.map +0 -1
  202. package/dist/credentials.js.map +0 -1
  203. package/dist/deadline.d.ts.map +0 -1
  204. package/dist/deadline.js.map +0 -1
  205. package/dist/dev/audit.d.ts.map +0 -1
  206. package/dist/dev/audit.js.map +0 -1
  207. package/dist/dev/bin.d.ts.map +0 -1
  208. package/dist/dev/bin.js.map +0 -1
  209. package/dist/dev/land.d.ts.map +0 -1
  210. package/dist/dev/land.js.map +0 -1
  211. package/dist/dev/lint.d.ts.map +0 -1
  212. package/dist/dev/lint.js.map +0 -1
  213. package/dist/dev/main.d.ts.map +0 -1
  214. package/dist/dev/main.js.map +0 -1
  215. package/dist/dev/next-version.d.ts.map +0 -1
  216. package/dist/dev/next-version.js.map +0 -1
  217. package/dist/dev/slow-tests.d.ts.map +0 -1
  218. package/dist/dev/slow-tests.js.map +0 -1
  219. package/dist/dev/test-matrix.d.ts.map +0 -1
  220. package/dist/dev/test-matrix.js.map +0 -1
  221. package/dist/drain.d.ts.map +0 -1
  222. package/dist/drain.js.map +0 -1
  223. package/dist/errors.d.ts.map +0 -1
  224. package/dist/errors.js.map +0 -1
  225. package/dist/executable.d.ts.map +0 -1
  226. package/dist/executable.js.map +0 -1
  227. package/dist/exit-codes.d.ts.map +0 -1
  228. package/dist/exit-codes.js.map +0 -1
  229. package/dist/failure.d.ts.map +0 -1
  230. package/dist/failure.js.map +0 -1
  231. package/dist/http/body.d.ts.map +0 -1
  232. package/dist/http/body.js.map +0 -1
  233. package/dist/http/index.d.ts.map +0 -1
  234. package/dist/http/index.js.map +0 -1
  235. package/dist/index.d.ts.map +0 -1
  236. package/dist/index.js.map +0 -1
  237. package/dist/keyring.d.ts.map +0 -1
  238. package/dist/keyring.js.map +0 -1
  239. package/dist/logger.d.ts.map +0 -1
  240. package/dist/logger.js.map +0 -1
  241. package/dist/logging.d.ts.map +0 -1
  242. package/dist/logging.js.map +0 -1
  243. package/dist/mcp/index.d.ts.map +0 -1
  244. package/dist/mcp/index.js.map +0 -1
  245. package/dist/output-buffer.d.ts.map +0 -1
  246. package/dist/output-buffer.js.map +0 -1
  247. package/dist/paths.d.ts.map +0 -1
  248. package/dist/paths.js.map +0 -1
  249. package/dist/pretty.d.ts.map +0 -1
  250. package/dist/pretty.js.map +0 -1
  251. package/dist/query/dates.d.ts.map +0 -1
  252. package/dist/query/dates.js.map +0 -1
  253. package/dist/query/evaluate.d.ts.map +0 -1
  254. package/dist/query/evaluate.js.map +0 -1
  255. package/dist/query/fields.d.ts.map +0 -1
  256. package/dist/query/fields.js.map +0 -1
  257. package/dist/query/help.d.ts.map +0 -1
  258. package/dist/query/help.js.map +0 -1
  259. package/dist/query/index.d.ts.map +0 -1
  260. package/dist/query/index.js.map +0 -1
  261. package/dist/query/parser.d.ts.map +0 -1
  262. package/dist/query/parser.js.map +0 -1
  263. package/dist/query/registry.d.ts.map +0 -1
  264. package/dist/query/registry.js.map +0 -1
  265. package/dist/query/types.d.ts.map +0 -1
  266. package/dist/query/types.js.map +0 -1
  267. package/dist/query/validate.d.ts.map +0 -1
  268. package/dist/query/validate.js.map +0 -1
  269. package/dist/release/fold.d.ts.map +0 -1
  270. package/dist/release/fold.js.map +0 -1
  271. package/dist/release/index.d.ts.map +0 -1
  272. package/dist/release/index.js.map +0 -1
  273. package/dist/release/markdown.d.ts.map +0 -1
  274. package/dist/release/markdown.js.map +0 -1
  275. package/dist/release/structure.d.ts.map +0 -1
  276. package/dist/release/structure.js.map +0 -1
  277. package/dist/renderer.d.ts.map +0 -1
  278. package/dist/renderer.js.map +0 -1
  279. package/dist/result-fields.d.ts.map +0 -1
  280. package/dist/result-fields.js.map +0 -1
  281. package/dist/retry.d.ts.map +0 -1
  282. package/dist/retry.js.map +0 -1
  283. package/dist/sanitize.d.ts.map +0 -1
  284. package/dist/sanitize.js.map +0 -1
  285. package/dist/skill/index.d.ts.map +0 -1
  286. package/dist/skill/index.js.map +0 -1
  287. package/dist/standards/audit.d.ts.map +0 -1
  288. package/dist/standards/audit.js.map +0 -1
  289. package/dist/standards/effects.d.ts.map +0 -1
  290. package/dist/standards/effects.js.map +0 -1
  291. package/dist/standards/errors.d.ts.map +0 -1
  292. package/dist/standards/errors.js.map +0 -1
  293. package/dist/standards/fields.d.ts.map +0 -1
  294. package/dist/standards/fields.js.map +0 -1
  295. package/dist/standards/flags-how.d.ts.map +0 -1
  296. package/dist/standards/flags-how.js.map +0 -1
  297. package/dist/standards/flags-what.d.ts.map +0 -1
  298. package/dist/standards/flags-what.js.map +0 -1
  299. package/dist/standards/identifiers.d.ts.map +0 -1
  300. package/dist/standards/identifiers.js.map +0 -1
  301. package/dist/standards/index.d.ts.map +0 -1
  302. package/dist/standards/index.js.map +0 -1
  303. package/dist/standards/lint.d.ts.map +0 -1
  304. package/dist/standards/lint.js.map +0 -1
  305. package/dist/standards/log.d.ts.map +0 -1
  306. package/dist/standards/log.js.map +0 -1
  307. package/dist/standards/output.d.ts.map +0 -1
  308. package/dist/standards/output.js.map +0 -1
  309. package/dist/standards/registry.d.ts.map +0 -1
  310. package/dist/standards/registry.js.map +0 -1
  311. package/dist/standards/steps.d.ts.map +0 -1
  312. package/dist/standards/steps.js.map +0 -1
  313. package/dist/standards/tables.d.ts.map +0 -1
  314. package/dist/standards/tables.js.map +0 -1
  315. package/dist/standards/vocabulary.d.ts.map +0 -1
  316. package/dist/standards/vocabulary.js.map +0 -1
  317. package/dist/streams.d.ts.map +0 -1
  318. package/dist/streams.js.map +0 -1
  319. package/dist/testing/index.d.ts.map +0 -1
  320. package/dist/testing/index.js.map +0 -1
  321. package/dist/testing/network.d.ts.map +0 -1
  322. package/dist/testing/network.js.map +0 -1
  323. package/dist/testing/sandbox.d.ts.map +0 -1
  324. package/dist/testing/sandbox.js.map +0 -1
  325. package/dist/testing/setup.d.ts.map +0 -1
  326. package/dist/testing/setup.js.map +0 -1
  327. package/dist/time.d.ts.map +0 -1
  328. package/dist/time.js.map +0 -1
  329. package/dist/update/index.d.ts.map +0 -1
  330. package/dist/update/index.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,1155 @@
1
+ # Changelog
2
+
3
+ Notable changes to `@wirecat/cli-base` (published as `@wirecat/cli-core` up to 0.20.0), one section per version, newest
4
+ first. Versions follow [semantic versioning](https://semver.org/); before `1.0.0` a minor release may change
5
+ the API.
6
+ Versions before 0.8.0 are in the [git tags](https://github.com/WireCatLabs/cli-base/tags).
7
+
8
+ Every entry says what changed as a caller sees it, why, and what to watch for — the rules are
9
+ [`docs/dev/CONVENTIONS.md`](docs/dev/CONVENTIONS.md#the-changelog).
10
+
11
+ ## 0.46.0 — 18.10.2026
12
+
13
+ ### Added
14
+
15
+ - **`commandsPage({ …, details })`** closes each command's section with the Markdown paragraphs `details(command)`
16
+ returns. cli-core's `commandHelpMarkdown` gives the Returns, Next usually and Example lines that `--help` shows,
17
+ so `docs/commands.md` says the same as the terminal. Without `details` the page is unchanged.
18
+ - **`guide` is a root word in the vocabulary**: a top-level `<tool> guide [name]` (cli-core's scenarios command)
19
+ passes `cli-dev lint` without a baseline entry.
20
+
21
+ ### Fixed
22
+
23
+ - **The README says which platforms CI covers here:** Linux, plus a non-blocking macOS run of the path tests; no
24
+ Windows job. It read as if the keyring were tested on every platform it lists. `CLEANUP.md` is no longer tracked.
25
+ - **Commander is needed at run time by `/skill`,** not only as a type, and the README says so. Full validation
26
+ takes a `platforms` input to run the suite on Windows and macOS by hand; Windows fails 14 tests today.
27
+ - **The package no longer ships source maps.** They pointed at `src/`, which is not published, so vitest in every
28
+ consumer printed "Sourcemap … points to missing source files" for each file it loaded from here. Stack traces now
29
+ show the `dist/` JavaScript, which is close to the source.
30
+ - **The package now holds `CHANGELOG.md`,** and its npm page links the repository's issues and README.
31
+ - **The README gives the install command** and says the package is ESM only (`require()` works from Node 22.12).
32
+ - **WC-PERM-12 says that any command saves incoming history under the read-only lock**, not only `serve` and
33
+ `watch`: on a messenger whose history lives in the store and whose adapter has the optional `listen`, cli-core
34
+ 0.343.0 saves what arrives during a one-shot command or an MCP session. The lock does not stop it. No messenger
35
+ has `listen` yet.
36
+ - **WC-CI-12 now names the required checks in every repository.** The `main` rulesets of whatsapp-cli, cli-core,
37
+ tg-cli, max-cli, cli-base and cli-memo require the pull request validation jobs besides `standards`, so a red pull
38
+ request cannot merge there. drive-cli, zoom-cli, cli-testing and cli-docs have no `pr.yml` and require only
39
+ `standards`. WC-CI-5 lists cli-memo among the repositories with `pr.yml`.
40
+
41
+ ## 0.45.1 — 18.10.2026
42
+
43
+ ### Fixed
44
+
45
+ - **WC-CI-5 now lists who runs pull request validation and what it costs, as measured.** whatsapp-cli, tg-cli,
46
+ max-cli and cli-core have `pr.yml` beside cli-base; cli-memo, drive-cli, zoom-cli and cli-testing still lack it. A
47
+ run takes 2–3 minutes in the CLIs and about 8 in cli-core, not the two and a half minutes the rule guessed.
48
+ - **WC-CI-12 now names what enforces it.** The `main` rulesets of whatsapp-cli and cli-core require the pull request
49
+ validation jobs besides `standards`, so a red pull request cannot merge there; tg-cli, max-cli and cli-base require
50
+ only `standards` and still rely on review.
51
+
52
+ ## 0.45.0 — 18.10.2026
53
+
54
+ ### Changed — may break callers
55
+
56
+ - **The lint, audit and vocabulary tables moved to `@wirecat/cli-base/standards/tables`.** `VOCABULARY`, `LINT_RULES`,
57
+ `AUDIT_RULES`, `PARITY_GROUPS`, `REQUIRED_ROOTS`, `IDENTIFIERS`, `FIELD_RULES` and their `define…` helpers and types
58
+ are no longer exported from `/standards`, which keeps the run-time contract only: effects, errors, output envelopes,
59
+ the log record and next steps. Every CLI loads `/standards` on start and parsed six dev-only tables it never read
60
+ (82 KB, about 7–12 ms; [measurement](https://github.com/WireCatLabs/cli-testing/tree/main/performance/startup/standards-split-before/README.md)).
61
+ Import the tables from `/standards/tables`; run-time names need no change.
62
+ - **Every pull request now runs the full Linux validation, coverage floor included (WC-CI-5).** A `pr.yml` calls the
63
+ repository's own `ci.yml` on `pull_request`, with platform suites skipped and superseded runs cancelled; before,
64
+ tests, coverage, contracts and parity ran only after the merge, so a pull request that lowered coverage passed and
65
+ then broke `main`. WC-CI-4, WC-CI-7, WC-CI-10, WC-TEST-11 and WC-TEST-13 say the same, and cli-base has its own
66
+ `pr.yml`. A CLI adds the workflow shown in WC-CI-5; it costs about two and a half minutes per pull request. Windows,
67
+ macOS and install checks still run only at release, monthly or on request.
68
+
69
+ ## 0.44.5 — 18.10.2026
70
+
71
+ ### Added
72
+
73
+ - **`node-ci.yml` runs a CLI's whole contract by name**: `cli: true` runs `contracts:check`, `contracts:upgrade`,
74
+ `security:no-leak`, `version:check`, `generate:check`, `test:matrix:check`, `docs:check`, `package:check` and
75
+ `lint:commands` as separate steps after the build (`parity: true` adds `parity:check`); a missing script fails its
76
+ step, and `skip` lists the ones a CLI cannot have yet. `platforms: true` runs the unit suite and the built `bin`
77
+ on Windows and macOS, with `platform-run` for extra commands. All are off by default, so today's callers see no
78
+ change.
79
+
80
+ ## 0.44.4 — 18.10.2026
81
+
82
+ ### Added
83
+
84
+ - **The handbook catches up with wa**: WC-LIFE-8 lets a one-shot command end a wait it entered with cli-core's
85
+ `untilStopped()` with exit 0, not 130, since stopping is how that wait ends. WC-REL-10 counts whatsapp-cli's
86
+ `bin/release` among the ones that check a signed release report. WC-PERM-18 is stricter: a standing hold (frozen,
87
+ limited, banned) holds every remote write, reactions and read receipts too, and a ban with no end date holds until
88
+ it is lifted.
89
+ - **`archive`, `unarchive` and `mark-unread` are vocabulary verbs.** They name the messenger CLIs' commands that
90
+ move a chat into the messenger's own archive and back, and mark a chat unread for the owner alone
91
+ (`chats archive`, `chats unarchive`, `chats mark-unread`). `cli-dev lint` no longer reports them as
92
+ `unknown-verb`. `--backend archive`, the local archive of messages, keeps its meaning. Watch for: once a CLI takes
93
+ this release, `cli-dev lint` fails on its `unknown-verb` baseline entries for these commands as stale until they
94
+ are deleted.
95
+ - **`star` and `unstar` are vocabulary verbs.** They name the messenger CLIs' `messages star` and `messages unstar`:
96
+ WhatsApp's starred messages, a mark only the owner sees. Watch for: once a CLI takes this release, `cli-dev lint`
97
+ fails on its `unknown-verb` baseline entries for them as stale until they are deleted.
98
+ - **The vocabulary has the words for privacy settings and muted chats.** `privacy` is a resource (an account has one),
99
+ `mute` and `unmute` are verbs, and `account privacy set`'s options (`--find-by-phone`, `--phone-number`, `--calls`,
100
+ `--chat-invites`, `--hide-online`, `--read-receipts`, `--profile-photo`, `--about-text`, `--last-seen`) and
101
+ `chats mute --until` have the role `value`. Why: every messenger CLI that turned these commands on carried up to 13
102
+ command-lint findings in its baseline (`unknown-resource`, `unknown-verb`, `option-role`). Watch for: once a CLI
103
+ takes this release, `cli-dev lint` fails on those baseline entries as stale until they are deleted.
104
+
105
+ ### Changed — may break callers
106
+
107
+ - **WC-PERM-12 now says what the read-only lock refuses in cli-core 0.337.0**: every write, and every local change
108
+ not marked `destroysNothing`, so a new local write fails closed. Syncs, `serve` and `watch` keep receiving; rebuilt
109
+ indexes, backups, exports, charts and the timed task writes still run; local embedding still runs while text for an
110
+ outside service is refused. A CLI's own local write must be marked, or the lock refuses it.
111
+
112
+ ## 0.44.3 — 18.10.2026
113
+
114
+ ### Added
115
+
116
+ - **The commands page says when a command reaches another service, not the CLI's own.**
117
+ `annotate(command, { reaches: "other_service" })` marks a command whose remote side is a service other than the
118
+ CLI's own, such as an embedding provider or a model host. The command description and `commands --json` carry
119
+ `reaches: "other_service"`, and `commandsPage` labels it "Sends text to an outside service" (remote write) or "Reads
120
+ from an outside service" (remote read) instead of the CLI's `mutates` line, which said such a command changed
121
+ something in the messenger. The labels come with `COMMANDS_PAGE_LABELS.en` and `.ru`; a CLI's own labels object
122
+ may leave them out, and then the command gets no line. Commands without the field are described as before;
123
+ regenerate `docs/commands.md` once a command sets it.
124
+ - **WC-STORE-13 says why keys stay out of the shared store, and allows a per-file media key or reference**: the
125
+ store is what gets backed up, exported, copied and read by agents, so a key there would open an account or a device
126
+ from any copy. A per-file media key (WhatsApp's `mediaKey`) or a per-file reference (a MAX bot's attachment
127
+ `token`) may now be kept in the attachment's `providerRef`: each opens only that one file. Device, session and
128
+ identity keys, account tokens and passwords still stay out.
129
+
130
+ ## 0.44.2 — 18.10.2026
131
+
132
+ ### Added
133
+
134
+ - `--meeting-inventory` is a registered MCP mode switch for hosts that explicitly bind the shared read-only series inventory tool. Candidate dates stay unverified; the flag does not add a provider connection or widen a default server on its own.
135
+
136
+ ## 0.44.1 — 18.10.2026
137
+
138
+ ### Added
139
+
140
+ - **Meeting project suggestions have a distinct MCP opt-in.** `--meeting-project-candidates` is registered as a mode switch for hosts that bind the shared read-only candidate SDK. It selects that capability explicitly rather than widening an existing MCP server; consumer hosts still supply the implementation.
141
+
142
+ ## 0.44.0 — 18.10.2026
143
+
144
+ ### Added
145
+
146
+ - **`cli-dev lint` fails a command with no `effects`** (`missing-command-effects`, WC-CMD-17): every leaf, generated
147
+ ones included, and every group that runs on its own must declare them; a command without them counts as a write,
148
+ so its failures are never retried and no next step runs it unasked. The commands document now says
149
+ `runnable: true` on a group with an action of its own, so the rule can tell it from a group that only prints its
150
+ help — a CLI's groups are checked from the release that picks up this version. tg, max, wa, memo, drive and zoom
151
+ pass it at their current `main`. A reader of `commands --json` that rejects unknown keys must accept `runnable`,
152
+ and an exact comparison of `describeProgram` output gains it on groups such as `doctor`, `mcp` and `commands`.
153
+ - **`offline: false` marks a command that cannot answer offline**: `annotate(command, { offline: false })` on a
154
+ command or a group, and `describeProgram` and `commands --json` say `offline: false` on it and on every command
155
+ below it, like `mcp: false`. cli-core's agent catalogue offers an `offline` input on every command that reads the
156
+ service, and a sync or an `inbox` that refuses `--offline` had no way to say so. Nothing changes until a command
157
+ sets it; a reader of `commands --json` that rejects unknown keys must accept the new one.
158
+ - **`writeSecurely` writes bytes as well as text**: `contents` takes a `string` or a `Uint8Array` (a `Buffer` is one).
159
+ A login QR image can now be written atomically with mode `0600`, like the credentials file, instead of with
160
+ `writeFileSync` and a `chmod` after it. Callers that pass a string see no change.
161
+ - **The vocabulary knows `setup` and `--agent`**: `setup`, the guided first run that logs in and then installs the
162
+ agent skill, is a registered root word, and `setup --agent <agent>` has the `action` role. `cli-dev lint` no
163
+ longer reports them in tg and wa; their `lint/baseline` entries for them turn stale and can be removed.
164
+ - **The audit baseline holds wa's shared findings**: `cli-dev audit --for wa --lint wa=lint`, run with the other
165
+ CLIs' command trees, starts with no new finding. What wa shares with the other CLIs was already in the baseline
166
+ under the other CLIs' names.
167
+ - **The handbook covers a CLI with a Go part and a messenger with no test accounts**: a new toolchain rule pins Go
168
+ from `go.mod` and runs gofmt, vet and the Go tests; Go keeps its own file names, and the 1,000-line cap is
169
+ TypeScript only. Live checks may use the owner's own chat where no test account exists, no test may open a real
170
+ credential store of any kind, and a release checks every package it publishes. Logging in and out answer without
171
+ an operation id, a flag counts as naming a command in a lower layer, `SECURITY.md` is a user page, and the
172
+ `replies` rules apply only where the messenger has replies. Read the toolchain, layout, style, testing, releases,
173
+ output, architecture, lifecycle, security, permissions and documents pages before the next audit.
174
+
175
+ ### Fixed
176
+
177
+ - **A query whose `/` opens a regular expression that never closes now says to quote the value.** `/` starts a
178
+ regular expression in the query language, so `text/plain` or `mime:(text/plain)` failed with
179
+ `query.unclosed-literal` and no hint; the message and `details.alternative` now read
180
+ `/ starts a regular expression; put the value in quotes: "text/plain"`. `mime:text/plain` keeps its existing hint,
181
+ `mime:"text/plain"`. The error code and reason are unchanged.
182
+ - **`--json=item.<field>` keeps that field**: `projectFields` read `items.` as each listed thing but not `item.` as
183
+ the one answered thing, so `--json=item.profile` printed `item: {}`. It now prints `item: { profile: … }`, as
184
+ `--json=profile` already did.
185
+ - **`cli-dev audit` leaves a baseline entry alone when the run lacks one of the CLIs it names**: such an entry was
186
+ counted as fixed, and `--write-baseline` dropped it, so a run over fewer CLIs wiped what a wider run had recorded.
187
+ It now stays in the baseline and is not counted.
188
+
189
+ ## 0.43.5 — 18.10.2026
190
+
191
+ ### Fixed
192
+
193
+ - **The shared test sandbox no longer fails a test file on Windows when a test left a file open**: the folder is
194
+ retried at exit and otherwise left for the system's temporary-file cleanup, with one line on stderr.
195
+
196
+ ## 0.43.4 — 18.10.2026
197
+
198
+ ### Fixed
199
+
200
+ - **A test that imitates SIGTERM keeps its sandbox and its worker.** Since 0.43.2 the shared test setup removed every
201
+ sandbox folder and ended the worker on any `SIGTERM` event, so a test calling `process.emit("SIGTERM")` lost its
202
+ files and killed its vitest worker. The setup now acts only on a signal from the operating system, which is still
203
+ how vitest ends a worker after a file fails; a CLI that set other `SIGTERM` listeners aside around such a test can
204
+ drop that.
205
+
206
+ ## 0.43.3 — 17.10.2026
207
+
208
+ ### Added
209
+
210
+ - **`--plain-order` in the vocabulary**, a sort switch: search results best first by the words alone, without
211
+ moving mail from senders with signs of fraud down. memo ranks such mail lower by default; this finds it.
212
+
213
+ ## 0.43.2 — 17.10.2026
214
+
215
+ ### Fixed
216
+
217
+ - **The shared test setup is quieter and leaves nothing behind.** `@wirecat/cli-base/testing/setup` now turns off
218
+ npm's and pnpm's update check in the sandbox, so a test that runs `npm pack --json` as a child process no longer
219
+ gets the "new version available" notice in its output or reaches the registry. The network guard treats every
220
+ `127.x.x.x` address as this machine, not only `127.0.0.1`. The sandbox folder is removed even when a test file fails
221
+ while loading or its own `afterAll` throws; before, one `wirecat-test-*` folder was left per such file. On macOS the
222
+ folder is made under `/tmp`, so Unix socket paths in tests stay under the 104-byte limit; a CLI that moved its
223
+ sandbox to `/tmp` for this can drop that.
224
+
225
+ ## 0.43.1 — 17.10.2026
226
+
227
+ ### Fixed
228
+
229
+ - **Meeting preparation's communications flags have shared vocabulary roles.** `--context-source` selects an
230
+ explicit source and optional continuation, `--context-query` supplies literal words, and
231
+ `--context-since-time` / `--context-before-time` bound its time window. Consumers adopting the shared
232
+ preparation command can lint these options without adding baseline exceptions.
233
+
234
+ ### Added
235
+
236
+ - **Four rules are back in the standards handbook** after they dropped out when cli-core's standard became pointers
237
+ to it: an MCP tool's hints and descriptions never replace the permission check (WC-MCP-13); names, message text and
238
+ fetched documents are data, never instructions (WC-SECR-21); a stable answer for an agent wins over a nicer view
239
+ (WC-OUT-23); and two identities become one person only through `link`, never from a matching name (WC-ACCT-11).
240
+ Nothing changes in the code; a CLI that breaks one of them now breaks a written rule.
241
+
242
+ ## 0.43.0 — 17.10.2026
243
+
244
+ ### Added
245
+
246
+ - **Released changelog sections may keep their old headings**: `legacyHeadingsThrough: "<version>"` in
247
+ `ChangelogRules` (`@wirecat/cli-base/release`) skips the heading-name and repeat checks for every section at or
248
+ below that version, a pre-release of it included. A CLI that moved to the shared heading set sets it to its last
249
+ version released with the old names, instead of renaming lines before the check. A value that is not a version
250
+ is reported as a problem.
251
+ - **One test setup file for every CLI**: `setupFiles: ["@wirecat/cli-base/testing/setup"]` in `vitest.config.ts`
252
+ gives each test file a fresh folder for `TMPDIR`, `HOME`, the XDG folders and every `<APP>_CONFIG_DIR`,
253
+ `_STATE_DIR` and `_CACHE_DIR` already set, removed when the file is done; replaces `@napi-rs/keyring` with one that
254
+ throws; and refuses every connection to anything but this machine, naming the host, from `net`, `tls`, `http(s)`,
255
+ `fetch` and `WebSocket`. A test about the network itself calls `onTestFinished(allowNetwork())`. It needs `vitest`,
256
+ now an optional peer dependency. A CLI's own setup file, listed after it, keeps only what is the CLI's own.
257
+
258
+ ### Changed — may break callers
259
+
260
+ - **`cli-dev audit` compares wa with the other CLIs** on the shared roots of the `all` parity group (`commands`,
261
+ `doctor`, `skill`, `mcp`, `upgrade`, `search`, `notes`, `meetings`). An audit run that passes `--cli` for wa may
262
+ find differences it did not report before; the weekly scorecard adds wa once wa is published.
263
+
264
+ ## 0.42.0 — 17.10.2026
265
+
266
+ ### Added
267
+
268
+ - **`mcp: false` works on a single option**: `annotateOption(option, { mcp: false })` from
269
+ `@wirecat/cli-base/commands` marks it, and `commands --json` carries `mcp: false` on that option and nothing on the
270
+ rest, so an MCP tool can leave it out of its input while the command stays offered. The first use is
271
+ `messages delete --for-everyone`: agents never delete for everyone. No MCP server reads it yet.
272
+
273
+ ## 0.41.0 — 17.10.2026
274
+
275
+ ### Added
276
+
277
+ - **The WireCat CLI standards handbook ships in the package**, at `docs/standards/README.md`: an index, 16 area
278
+ pages with 254 rules, from the toolchain to releases, and a checklist for a new CLI. Each rule has an id, a reason,
279
+ the check that enforces it or "review only", an example, its exceptions and the CLIs it applies to. A rule lives
280
+ on one page; a rule that moved or merged keeps its old id as a pointer, so a citation still resolves.
281
+ `pnpm docs:check` checks the ids, the rule counts in the index, and that every "Enforced by" entry names a real
282
+ check: one in this repository, or one in another WireCat repository written as `<repo>: <check>`.
283
+
284
+ ## 0.40.4 — 17.10.2026
285
+
286
+ ### Added
287
+
288
+ - **`scan` is a known verb**: read stored messages for what a rule finds, such as tasks, and list it; record
289
+ it only with `--yes`. For `tasks scan <chat>` in cli-core, the name the owner chose over `tasks apply`.
290
+ - **`mcp: false` keeps a command off MCP servers**: set on a command or a whole group with `annotate`, it covers every
291
+ command below it. `commands --json` carries `mcp: false` on each command it reaches and nothing on the rest, so MCP
292
+ tool lists can be built from the command tree. Agents running the CLI itself are not affected.
293
+
294
+ ## 0.40.3 — 17.10.2026
295
+
296
+ ### Added
297
+
298
+ - Release source checks support Go through its pinned toolchain and CodeQL autobuild. Callers
299
+ opt in with `go` in `languages` and set `go-version-file` for a nested module; interpreted
300
+ language checks retain their existing build mode and critical severity policy.
301
+
302
+ - **Spreadsheet structure uses shared `insert` and `format` verbs.** `insert` adds bounded rows,
303
+ columns or cells before an explicit position; `format` changes selected cell presentation
304
+ without replacing values. Consumers can remove these dated vocabulary exceptions after adoption.
305
+
306
+ ## 0.40.2 — 17.10.2026
307
+
308
+ ### Fixed
309
+
310
+ - **An upgrade through pnpm installs the newest version.** pnpm 11 installs only versions at least a day old
311
+ (`minimumReleaseAge` 1440), so `pnpm add -g <package>@latest` could install a day-old release — on 2026-10-17 an
312
+ older `tg` than the one installed — while the update notice named the newest. `updateCommand("pnpm", …)` now adds
313
+ `--config.minimumReleaseAgeExclude=<scope>/*` for the package's own scope; every other dependency keeps pnpm's wait.
314
+
315
+
316
+ ## 0.40.1 — 17.10.2026
317
+
318
+ ### Added
319
+
320
+ - **Spreadsheet commands have shared vocabulary.** `sheets` names remote spreadsheets;
321
+ `--values <json>` supplies cell rows, `--input <mode>` selects input parsing and
322
+ `--render <mode>` selects returned value representation. Drive uses the existing `info`,
323
+ `show`, `update`, `add` and `clear` verbs. Consumers can remove corresponding dated lint
324
+ exceptions after adopting this version; existing baselines need no additions.
325
+ - **`places` is a view noun**: the street addresses confirmed as places, most mentioned first, as in
326
+ `tg <profile> mentions places` (cli-core 0.308.0). The `unknown-verb` lint no longer reports it; tg and max can
327
+ drop that baseline line after adopting this version.
328
+
329
+ - **`--certainty <n>` is a known flag** (role `value`): how sure an agent is, 0 to 1, kept with what it wrote, as
330
+ `tasks close --certainty` in cli-core takes it. tg and max listed it in their `option-role` lint baselines; after
331
+ they update cli-base those lines are stale and go.
332
+
333
+ ### Fixed
334
+
335
+ - **The shared changelog guard can restore a historical section to its exact immutable tag.** This repairs notes that raced publication without permitting arbitrary historical edits; missing tag content still fails closed.
336
+
337
+ - **The changelog guard refuses a pull request that repeats a `###` heading in a section not yet released**
338
+ (`### Fixed` twice under `## Unreleased`). Each app's shape check refuses it too, but only in full validation,
339
+ so it reached main and stopped cli-core's 0.307.0 release. A repeat already on the base does not fail the guard.
340
+ Each app moves its guard pin to take it.
341
+
342
+ - **`bin/release` restarts itself once when the release brings a newer `bin/release`**: folding the changelog and
343
+ raising the version both fast-forward `main`, and the shell went on reading the old script from the byte it had
344
+ reached, so the rest of the run was a mix of old and new text. It now starts the new script over with the same
345
+ arguments; a second change during that run stops it with `run it again`. Only the repository's own release is
346
+ affected; the published package does not change.
347
+ - **`bin/release` restarts by its path in the repository after updating `main`**: it restarted as `$0` after
348
+ changing to the repository root, which pointed nowhere when the script was started from another folder by a relative
349
+ path. Only the repository's own release is affected.
350
+
351
+ ## 0.40.0 — 16.10.2026
352
+
353
+ ### Fixed
354
+
355
+ - **An email address reads as one term, unquoted**: `from:alice@example.com` used to fail with
356
+ `query.unsupported-operator — boost and minimum-should-match are not supported`, because `@` is Lucene's
357
+ minimum-should-match operator. Inside a word, `@` followed by a name with a dot (`user@host.tld`) now stays part of
358
+ the word. `(a OR b)@2` and `word@2` still fail as before.
359
+
360
+ ## 0.39.0 — 16.10.2026
361
+
362
+ ### Added
363
+
364
+ - **A query that fails on an unquoted value with an operator in it says to quote it.** `url:a.com/x?y=1` used to
365
+ fail with only `query.invalid-syntax at position 14`: `/`, `?`, `=` and `:` are query syntax. The error now ends
366
+ with `put the value in quotes: url:"a.com/x?y=1"`, also in `details.alternative`. A failure that already carries
367
+ an alternative keeps its own.
368
+
369
+ ## 0.38.0 — 16.10.2026
370
+
371
+ ### Added
372
+
373
+ - **A command says how long it runs**: `CommandMeta.lifetime` (`persistent` until stopped, `interactive` until a person
374
+ answers; unset is bounded by the default timeout) and `cooperative` (stops at its own pace when aborted from outside),
375
+ set with `annotate` and printed in `commands --json`. A runner reads them with `metaOf` instead of matching command
376
+ names.
377
+
378
+ ### Fixed
379
+
380
+ - **Release scripts report observed lock holders separately from stale release labels.** Shared
381
+ diagnostics no longer attribute a live lock to a previous run; wait for the holders to finish, then
382
+ retry.
383
+
384
+ ## 0.37.0 — 16.10.2026
385
+
386
+ ### Added
387
+
388
+ - **The vocabulary registers `apply`, and `inspect` covers a mailbox.** `apply` runs the owner's saved rules over
389
+ what is already stored and changes what they match, saying only what would change unless `--yes` (memo's
390
+ `mail rules apply`); `test` stays the read-only twin that never changes anything. `inspect` now means looking at
391
+ a source before taking anything from it — a chat link without joining, a mailbox without importing (memo's
392
+ `mail probe` becomes `mail inspect`). Both were `unknown-verb` findings in memo's command lint baseline.
393
+
394
+ ## 0.36.0 — 16.10.2026
395
+
396
+ ### Added
397
+
398
+ - **`cli-dev changelog fold --land` lands the fold through a pull request and redoes it once when main moves.**
399
+ bin/release used to fold, open the pull request and wait on its checks; a session that merged a change to
400
+ CHANGELOG.md meanwhile left the pull request unmergeable and the release stopped (cli-core#1335). With
401
+ `--land` the fold is made on a fresh `origin/main`; a pull request that conflicts — found before waiting on checks,
402
+ since a conflicting one gets none, or when the merge is refused — is closed and the fold made again on the new
403
+ main, once. It prints the line and the pull request when it landed, nothing when there was nothing to fold (another
404
+ session already did), and fails after a second conflict or on failed checks. It needs `gh` and a clean `main`.
405
+ - **`mergeRepeatedHeadings` (`@wirecat/cli-base/release`) and `cli-dev changelog fold` merge a `###` heading the
406
+ dated section repeats.** A pull request branched before a fold adds under the unreleased heading; git applies the
407
+ hunk under the dated heading beside the section's own copy, and docs:check refuses "Added twice" (cli-core 0.294.0
408
+ after #1327). With no unreleased section on top, the fold now merges the dated section's repeats and prints
409
+ `merged repeated headings in <version>`.
410
+
411
+ ### Fixed
412
+
413
+ - **The fold no longer leaves a blank line inside a merged list.** When a section already repeated a heading, the
414
+ blank line before its second copy ended up between the merged entries. Headings that differ only by trailing
415
+ spaces now merge too.
416
+
417
+ ## 0.35.0 — 15.10.2026
418
+
419
+ ### Added
420
+
421
+ - **Bounded batches share parsing, correlation and sequential execution.** `prepareBatch`, `readBatchInput` and
422
+ `runBatch` accept explicit registered handlers, cap input/count/read reservations and retained output, and keep
423
+ a result for every input on budget refusal. Failed reads never refund their reserved ceiling; it is not a byte
424
+ consumption measurement. Hosts retain account authorization, cancellation, final rendering and resource cleanup.
425
+ `projectBatchAnswers` selects nested answer fields while retaining ids, errors and completeness.
426
+
427
+ ## 0.34.0 — 15.10.2026
428
+
429
+ ### Added
430
+
431
+ - **`cli-dev changelog fold` folds `## Unreleased` into the dated section below it.** One session dates the version
432
+ and sets package.json; before anyone publishes, another merges a new `## Unreleased` above it, and the release
433
+ refuses until someone folds by hand (cli-core#1300 fixed it for cli-core alone). The command rewrites CHANGELOG.md
434
+ when the section below the unreleased one is `## <version> — ` (`--version`, package.json's by default): same-named
435
+ `###` headings merge, the unreleased entries first, the date stays. It prints `folded Unreleased into <version>`
436
+ when it folded and nothing otherwise, and fails on text under the unreleased section before its first `###`
437
+ rather than lose it. `--unreleased «Не выпущено»` names another title. It does not ask npm: run it in
438
+ bin/release only while the version is not published, or it adds entries to released notes. `foldUnreleased` is
439
+ exported from `@wirecat/cli-base/release` for the same job in code.
440
+
441
+ ## 0.33.0 — 15.10.2026
442
+
443
+ ### Changed — may break callers
444
+
445
+ - **The shared vocabulary registers the words of cli-core's task, mention and picture commands.** Verbs `claim`,
446
+ `release`, `merge`, `unmerge`, `defer` and `embed`; views `agenda`, `categories` and `domains`; resources `images`
447
+ and `duplicates`; flags `--into <id>`, `--all-in-chat`, `--due-within <duration>`, `--unanswered <duration>`,
448
+ `--waiting <duration>`, `--category <name>`, `--holder <name>` and `--lease <duration>`. `tasks plan` stays
449
+ unregistered: `plan` is a synonym of `update`. A filter is still not registered, so `tasks agenda --tag`,
450
+ `--claimed` and `tasks duplicates --unsure` keep their `option-role` finding. A CLI that moves to this version
451
+ must remove the baseline entries these clear, because a stale entry fails: on tg, for example, `unknown-verb`
452
+ `tasks claim`, `option-role` `tasks agenda --holder` and `unknown-resource` `images`.
453
+
454
+ ## 0.32.0 — 15.10.2026
455
+
456
+ ### Added
457
+
458
+ - **The shared vocabulary registers 13 more flags that apps carried without a role.** Rule fields `--sender`,
459
+ `--domain`, `--list-id` and `--do <actions>` (what a rule does, the same list tg and max's reply rules take);
460
+ `--clear`, `--ignore`, `--subfolder`, `--occurred-time`, `--grant <scopes>`, `--min-similarity`, `--domain-age`,
461
+ `--retry-attachments` and `--all-versions`. `--occurred` is now a synonym of `--occurred-time`, and
462
+ `--target-accounts` of `--source`. A CLI that moves to this version must drop the `option-role` baseline entries
463
+ these clear, because a stale entry fails, and rename the flags `flag-synonym` now names.
464
+
465
+ ### Fixed
466
+
467
+ - **Changelog checks reject duplicate release and pending headings.** Repeated versions could hide
468
+ changed notes behind an unchanged copy. Keep one section per version and one pending section.
469
+
470
+ ## 0.31.1 — 15.10.2026
471
+
472
+ ### Fixed
473
+
474
+ - **Nested field selection traverses arrays and retains report coverage.** Item/list envelopes, continuation, missing data and operation identifiers are handled by one shared projector; consumers no longer need to unwrap item answers.
475
+
476
+ ## 0.31.0 — 15.10.2026
477
+
478
+ ### Changed — may break callers
479
+
480
+ - **The shared vocabulary registers 158 more flags with a role, and names 20 more flags as synonyms.** These are the
481
+ options tg, max, memo, drive and zm already use, such as `--chat`, `--model`, `--title`, `--sync-time` and
482
+ `--max-bytes`. A filter is still not registered: it belongs in the list's query. On 2026-10-14 this clears 226 of
483
+ tg's 287 `option-role` baseline entries, 216 of max's 281, 114 of memo's 146, 39 of drive's 53 and 74 of zm's 88.
484
+ A CLI that moves to this version must remove those entries from its baseline, because a stale entry fails. It
485
+ also gets new findings to fix:
486
+ - `flag-synonym`: for example `--refresh` is `--sync-first`, `--budget` is `--max-pages`, `--estimate` is
487
+ `--dry-run`, `--offset` is `--cursor` and `--destination` is `--output-dir`.
488
+ - `flag-two-meanings`: a use whose value name differs from the registered one, such as zm's
489
+ `--title <text>`, which is `--title <title>` in the vocabulary.
490
+ - **A flag's value name may contain `=` or `:`, and may end in `...`.** So `<key=level>`, `<hh:mm>` and `<ref...>`,
491
+ which commands already show, can be registered.
492
+
493
+ - **`node-ci.yml` no longer leaves the job token in the checkout.** Its `check`, `bun` and `secrets` jobs check out
494
+ with `persist-credentials: false`, so nothing a dependency runs in those jobs can read the token from `.git/config`.
495
+ A caller whose `checks` lines push or call `git` against the remote with that token needs its own job for it.
496
+ - **`release-checks.yml` asks for less.** Only the `source` job requests `actions: read` and
497
+ `security-events: write`; the dependency audit reads the lockfile without `pnpm install`, so no
498
+ lifecycle script runs; the secret scan no longer keeps the checkout token. Callers granting
499
+ `contents: read`, `actions: read` and `security-events: write` need no change.
500
+
501
+ ### Fixed
502
+
503
+ - **The shared `release-checks.yml` gate now blocks critical CodeQL findings.** CodeQL 2.27 lists its
504
+ rules under `tool.extensions`, not `tool.driver`, so every finding scored 0 and the gate always
505
+ passed. Rules are now resolved through each result's `rule.toolComponent`, and a finding whose rule
506
+ is not in the report fails the gate instead of passing it. Callers get this when they bump their pin.
507
+
508
+ ## 0.30.0 — 14.10.2026
509
+
510
+ ### Fixed
511
+
512
+ - **`cli-dev audit` no longer reports a command as missing from a whole-tree pair when neither member has it.** A
513
+ path only another CLI has (memo's `catalog`, `mail`, `meetings projects`) came out as `max+tg catalog: presence:
514
+ only in ` with nobody named, 21 false findings on 2026-10-14. A path counts as missing only when at least one
515
+ member of the group has it.
516
+
517
+ ### Added
518
+
519
+ - The shared command vocabulary defines `prepare` as bounded cited context assembly and classifies
520
+ `--meetings`, `--cues` and `--context-limit` as work budgets, `--history-since-time` as a target
521
+ history window and `--meeting-preparation` as opt-in tool exposure. Preparation commands reuse
522
+ `--since-time` and `--before-time` for an inclusive start and exclusive end. Command lint can
523
+ check these workflows without new baseline entries or per-tool overrides.
524
+
525
+ ## 0.29.0 — 14.10.2026
526
+
527
+ ### Changed — may break callers
528
+
529
+ - **`updateNotice` names `upgrade`, and stays silent only when the upgrade is the command being run.** The line said
530
+ `` `<cli> update` installs it `` while every CLI's command is `upgrade`, the standard's required root, and it went
531
+ silent whenever the word `update` appeared anywhere, so `tasks update` never saw a notice. The word is now
532
+ `upgradeCommand`, default `"upgrade"`, and the notice skips only when it or `complete` is one of the first two words
533
+ that are not options (a profile may come first). A CLI that patched the line with `.replace(...)` or skipped on
534
+ `argv.includes("upgrade")` itself can drop both.
535
+ - **`skill show` and `skill install` carry their effects.** `show` reads this machine, `install` writes to it, and
536
+ neither touches the service; before, they had none and counted as writes. A host that annotated them itself can stop.
537
+
538
+ ### Removed
539
+
540
+ - **`legacyMacPaths` and `migrateLegacyMacPaths` are gone, with the `LegacyMove` and `MigrateLegacyPathsOptions`
541
+ types.** Every machine that ran a release with `~/Library` folders has moved them. A CLI that calls the migration at
542
+ start drops the call.
543
+ - **`noopLogger` and `isTransportFailure` are gone.** Nothing used them.
544
+
545
+ ## 0.28.2 — 14.10.2026
546
+
547
+ ### Added
548
+
549
+ - **`readJsonResponse` reads UTF-8 JSON within an explicit byte budget.** HTTP adapters can reject
550
+ oversized or malformed bodies before parsing, even without a trustworthy Content-Length. The
551
+ `/http` helper supports cancellation and releases its reader; callers still own status handling
552
+ and provider validation. It adds no HTTP dependency to the root export.
553
+
554
+ ## 0.28.1 — 14.10.2026
555
+
556
+ ### Changed — may break callers
557
+
558
+ - **`--operation-id` replaces `--send-id`, and `operation_id` replaces `send_id`.** One id per meaning: the flag
559
+ that repeats a write after an unknown outcome names the same attempt as the answer's `operation_id`. The flag row
560
+ lists `--send-id` in `replaces`, so `flag-synonym` reports a CLI that still takes it, and `identifier-name` reports
561
+ a `send_id` key. `projectFields` keeps only `operation_id` beside the projected fields.
562
+
563
+ ### Added
564
+
565
+ - **Vocabulary rows for `--all`, `--as-owner` and `--problems`.** `--all` (paging): every item at once, instead of one
566
+ page or one target. `--as-owner` (mode): record an action as the owner's when an agent's terminal runs the tool.
567
+ `--problems` (output): only failures, warnings and crashes (`runs show`). cli-core already ships all three, so apps'
568
+ `cli-dev lint` stopped reporting them as unknown flags.
569
+
570
+ ## 0.28.0 — 14.10.2026
571
+
572
+ ### Changed — may break callers
573
+
574
+ - **`effects` replaces `mutates` and `local` on commands.** `annotate(command, { effects: { remote, local } })`, each
575
+ `none | read | write`; `commands --json` prints `effects`. `mutates: true` is `remote: "write"`, `mutates + local` is
576
+ `local: "write"`. A command without `effects` counts as a write, so annotate every read command, or its failures are
577
+ never safe to retry and steps to it are never safe to run unasked. MCP's `readOnlyHint` is `!writes(effects)`.
578
+ - **The error object carries `retry: { safe, after_ms? }`, always.** `errorObjectOf` derives it from the code, the
579
+ failed command's `effects` (new `FailureContext.effects`) and the new `sent` detail: a transient failure (`timeout`,
580
+ `rate_limited`, `network_error`, `provider_unavailable`, `provider_error` with `retryable: true`) of a command that
581
+ writes nothing, or of a write with `sent: false`, is safe; `outcome_unknown` never is. `retryable` and
582
+ `retry_after_ms` are read into `retry` and no longer copied into `details`. `operation_id`, one write attempt, is a
583
+ common detail now, so an unknown outcome names the attempt to look up before trying again.
584
+ - **A next step has `kind` and `safe_to_auto_execute`.** `kind: "command"` keeps `command` and `args`;
585
+ `kind: "user_action"` (defined with `{ id, kind: "user_action", why }`) has neither and is never safe. A command
586
+ step is safe only when its target is known to write nothing. At most two steps per answer (`MAX_NEXT_STEPS`, was
587
+ three): the one to take, then one alternative.
588
+ - **`stepOf` checks that a step binds.** Argument values must be scalars, or lists of them, on one line — a paragraph
589
+ read out of a message never becomes a step. With `targets` (`stepTargets(program)` from `/commands`, or
590
+ `FailureContext.targets`), the command must exist and take every argument named, the `*` step included; a
591
+ command takes its own options, its groups' and the root's, each named by its long flag (`--no-input` is
592
+ `no_input`).
593
+ - **`meta.complete` and `meta.issues` replace `meta.skipped`.** `complete` is required on every envelope: `false` when
594
+ a source failed or was skipped, with `issues: [{ code, reason?, source }]` (`code` is an error code, so a skipped
595
+ account is `authentication_error`, not free text). `truncated` (a size cap cut the answer) and `page.has_more`
596
+ (another page exists) keep their own meanings. A command that may answer partially declares
597
+ `annotate(command, { partial: true })`, shown in `commands --json`; `exitCodeOfAnswer(meta, partial)` exits 0
598
+ only then, else with the first issue's code.
599
+ - **`page` says how the source pages.** `{ mode: "page", limit, has_more, next_page? }` or
600
+ `{ mode: "cursor", limit, has_more, next_cursor? }`; `cursor` is gone. A cursor source never shows page numbers.
601
+ - **An override names its owner and a review date.** Each `lint/overrides.json` entry needs `owner` (a GitHub handle
602
+ or team, `@leemour`) and `review_after` (`YYYY-MM-DD`); `cli-dev lint` and `cli-dev audit` fail one past its date.
603
+ `overridesProblems(entries, today)` takes the date. `cli-dev audit` names a rule overridden more than three times
604
+ across the CLIs it reads (`rulesForReview`, `OVERRIDE_RULE_REVIEW`), without failing; the per-repo budget of 10
605
+ stays.
606
+ - **`api_operation` replaces `operation_id` on commands.** `annotate(generated, { api_operation })` and the commands
607
+ document key: the catalog operation a generated command was made from. `operation_id` now means one write attempt
608
+ only, as `IDENTIFIERS` says.
609
+ - **A log record's `body` is its event id, never a sentence.** `createRecordLog` and `logRecordOf` write the
610
+ `event` attribute as `body` and drop the message passed beside it (`log.error({ event }, "text")`), which could
611
+ quote content. `errorRecordOf` names its record `command.failed` and moves the message into `attributes.message`.
612
+ Give every record a registered dotted `event`, or its `body` is empty.
613
+
614
+ ### Added
615
+
616
+ - **`Streams.drain(signal?)`.** A caller that wrote a lot of output can wait until stdout and stderr have flushed, or
617
+ give up when the signal aborts. Optional on `Streams`; `processStreams` implements it.
618
+
619
+
620
+ - **JSON lines with an end line:** `jsonlOf(envelope)` prints `{"type":"item","data":…}` per item, then
621
+ `{"type":"end","complete":…,"issues"?:…,"page":…}`, so a reader tells a finished stream from a cut one;
622
+ `jsonlItemSchema` and `jsonlEndSchema` describe them, and `key-casing` checks both.
623
+ - **`missing` entries may have no `step`**, for a field nothing can fill.
624
+ - **The `absent-null` field rule and the `nullable-list` lint rule** (medium): absent is not asked or not known,
625
+ `null` is known to have no value, `[]` is known empty, `0` is measured. The lint fails a list in a registered
626
+ JSON Schema that may be `null`. Run `cli-dev lint --write-baseline` once to record today's.
627
+ - **Flag synonyms.** A vocabulary flag lists its other names in `replaces` (`--limit`: `--max`, `--count`, `--take`,
628
+ `--max-results`); the `flag-synonym` lint rule (medium) reports them, and `option-role` leaves them to it.
629
+ - **The identifiers glossary**, `IDENTIFIERS` and `defineIdentifiers`: `run_id` (one invocation), `operation_id` (one
630
+ write attempt), `request_id` (the provider's reference), `trace_id` (tracing). The `identifier-name` lint rule
631
+ (high) reports `execution_id`, `invocation_id`, `log_id` and `diagnostic_id` in a registered schema or the commands
632
+ document.
633
+ - **Registered log events.** `defineLogEvents([{ id, meaning, keys }])` — a dotted id such as
634
+ `provider.request.failed` and the attribute keys each record carries; `LOG_EVENTS` holds cli-base's own
635
+ (`command.failed`). `logEventProblems(record, events)` names an unknown event or a missing key, and
636
+ `expectRegisteredEvents(lines, events)` from `/testing` throws on either, so a test fails on an event nobody
637
+ registered.
638
+
639
+ ### Fixed
640
+
641
+ - **A config file that exists but cannot be read is an error, not an empty config.** Only a missing file falls back
642
+ to defaults; a directory, a file used as a parent folder, or an I/O failure reports the filesystem error, so a bad
643
+ path no longer silently resets every setting.
644
+
645
+ ## 0.27.2 — 14.10.2026
646
+
647
+ ### Added
648
+
649
+ - **`--scan-limit <n>` is a shared read-budget option.** Commands can bound candidate inspection separately from returned matches and byte reads. Adopting consumers remove any now-stale lint baseline entries for this option.
650
+
651
+ ## 0.27.1 — 14.10.2026
652
+
653
+ ### Added
654
+
655
+ - **`cli-dev audit`**: two or more CLIs, from their `commands --json`, against every lint rule and against each other
656
+ — required roots, the same contract on a shared path, the same meaning for a flag, one schema per output entity, one
657
+ name per failure. It prints a scorecard (a table, or the list envelope with `--json`) and fails on a finding the
658
+ baseline in `audit/baseline/` does not hold. `/standards` adds `AUDIT_RULES`, `PARITY_GROUPS`, `REQUIRED_ROOTS` and
659
+ `AUDIT_BLOCKS`, the switch that makes a new cross-CLI divergence fail a release (`"new"`, today) or only show
660
+ (`"never"`). A weekly workflow audits the five latest published CLIs and rewrites one pinned issue.
661
+
662
+ ### Fixed
663
+
664
+ - The shared `--bytes` budget uses `<bytes>`, matching existing evidence commands and preventing a false two-meanings lint failure.
665
+
666
+ ## 0.27.0 — 14.10.2026
667
+
668
+ ### Changed — may break callers
669
+
670
+ - **A log record carries only allow-listed attribute keys.** `createRecordLog` writes a top-level attribute key that is
671
+ not in `LOG_ATTRIBUTE_KEYS` or the new `allow` option as `"[not allowed]"`, and redacts any key that names content
672
+ (`isContentKey`: `text`, `chatTitle`, `errorMessage`, `accessToken`, `phoneNumber`, …) at any depth. Pass every key
673
+ your writers log in `allow`, or `runs show` loses those fields. A writer that builds its own lines calls
674
+ `logRecordOf`, the same builder.
675
+
676
+ ### Added
677
+
678
+ - **`log-attribute-key` lint rule** (high): no key of the CLI's log allow-list names message text, a token or a phone
679
+ number. Pass `logAttributesJsonSchema(allow)` as `log_attributes` in `cli-dev lint --schemas`; without it the rule
680
+ checks cli-base's own list. `key-casing` skips that entry, as log attribute keys are camelCase for now.
681
+ - **`traceIdOf(traceparent)`** in `/standards`: the trace id of a W3C `traceparent`, or undefined for one the spec
682
+ says to ignore — for an MCP caller's `_meta.traceparent` or a `TRACEPARENT` variable.
683
+
684
+ ## 0.26.1 — 14.10.2026
685
+
686
+ ### Added
687
+
688
+ - **The shared vocabulary names an offline source catalog.** `catalog` is a stored file/folder metadata resource, `tree` its hierarchy view, and `--depth <n>` a traversal budget. Consumers can expose offline browsing without provider-specific naming exceptions; list filters still use the shared query language.
689
+
690
+ - The `passage` view names a cited transcript anchor with bounded surrounding lines. Evidence budgets `--bytes` / `--max-read-bytes` and `--expected-fingerprint` are registered; the existing `--before-n` / `--after-n` counts also describe transcript lines.
691
+
692
+ - **The verb `rebuild`**: compute again, from what the store already holds, what the tool derives from it, keeping
693
+ the owner's decisions. cli-messaging's `mentions rebuild` (phones, e-mail and links found in stored text) is
694
+ the first command to use it.
695
+ - **The resource `mentions`**: phones, e-mail addresses, links and documents found in stored text, each where it was
696
+ written.
697
+
698
+ ### Changed — may break callers
699
+
700
+ - **`identities` now means every way to reach a person or an organization** — an account in a messenger or mailbox,
701
+ an e-mail address, a phone — not only messenger and mailbox accounts. Only the description changes; no rule does.
702
+
703
+ ## 0.26.0 — 13.10.2026
704
+
705
+ ### Changed — may break callers
706
+
707
+ - **116 more vocabulary flags with a role**, from the flags tg, max, memo, drive, zoom and cli-messaging share:
708
+ output (`--json`, `--jsonl`, `--fields`, `--brief`, `--full`, `--quiet`, …), sort (`--measure`, `--score`,
709
+ `--newest`, `--order`, `--weights`), mode, action, budget (`--max-*` with their units), target and value rows.
710
+ `option-role` drops in every app: memo 244 → 141, drive 66 → 56, max 562 → 317, tg 576 → 329, zoom 157 → 106,
711
+ cli-messaging's chat app 366 → 151; no other rule rises. Filter flags get no row: they become query fields.
712
+ `cli-dev lint` fails on the now-stale `option-role` baseline entries until the app runs `--write-baseline` at its
713
+ bump; regenerate cli-messaging's STANDARD flag table too.
714
+ - **The parsed query tree is snake_case.** A range predicate carries `lower_inclusive` and `upper_inclusive`, and a
715
+ negated clause has `occur: "must_not"`; `DateRange` from `dateRange` has `lower_inclusive` and `upper_inclusive`.
716
+ The tree is JSON an agent sends back (an MCP `ast`, a saved search), so it follows the one casing for every key.
717
+ A tree stored in the old form fails the new `queryAstSchema`: rewrite stored trees before reading them.
718
+
719
+ ### Added
720
+
721
+ - **`queryAstSchema` and `queryNodeSchema`** in `/query`: the tree's valibot schema, so a caller validates an `ast`
722
+ it receives instead of keeping its own. `key-casing` checks it as the base schema `query_ast`.
723
+
724
+ ## 0.25.0 — 13.10.2026
725
+
726
+ ### Changed — may break callers
727
+
728
+ - **Every JSON key the standard defines is snake_case.** The envelope: `page.has_more`, `meta.run_id`,
729
+ `meta.duration_ms`. The error object: `error.run_id`; error details `retry_after_ms`, `request_id`,
730
+ `timeout_ms`, `max_bytes`, `buffered_bytes`, `partial_output`. The log record: `run_id`, and `trace_id` as
731
+ OpenTelemetry spells it for JSON log lines; `thrown_message` in an error record's attributes.
732
+ `commands --json`: `global_options`, `inherited_options`, `exit_codes`, `takes_value`, `operation_id`, and
733
+ `implies` keyed by long flag (`--refresh`) instead of Commander's attribute name. One casing for every key, so
734
+ an agent never guesses. Reasons' `details`, step argument names and `{placeholders}` must now be snake_case,
735
+ or `defineReasons` and `defineSteps` throw at start-up; `annotate` takes `operation_id`; `projectFields` keeps
736
+ `operation_id` and `send_id`. Bump your `contract`.
737
+ - **The field rule `camel-case` is now `snake-case`**, and a duration key ends in `_ms`.
738
+ - **Every vocabulary flag has a `role`**, one of `FLAG_ROLES`, and `defineVocabulary` refuses a flag without one.
739
+ There is no filter role: a filter is a query field.
740
+ - **Query error messages start with the command that ran**, `chats list: query.unknown-field at position 8`, in
741
+ place of `search:`; the guide link is in `details.guide` only when the caller passes one. The `~` hints no
742
+ longer name `--language legacy`, a messages-search option.
743
+
744
+ ### Added
745
+
746
+ - **`--account`, `--provider` and `--source` in the vocabulary** with role `scope`: they choose the accounts a
747
+ command reads, so `filter-flag` and `option-role` no longer report them.
748
+ - **One query language for every list: `@wirecat/cli-base/query`.** The Lucene 9.12.3 parser port moves here
749
+ from cli-messaging with its date handling. `queryFieldsOf` derives a list's fields from its entity schema,
750
+ `validateQuery` checks a query against them, `fieldsHelp` prints the Fields section, `defineQuery` refuses a
751
+ field whose example does not run, and `QUERY_REASONS` registers the shared `query.*` errors, with
752
+ `query.bare-word`, `query.invalid-number` and `query.unsupported-placement` new. `evaluate` judges one item
753
+ against a query, for tests: a list filters in its store or at the provider, never in memory.
754
+ - **`annotate` takes `output: { entity, view? }` and `query`, and `commands --json` carries both**, so an agent
755
+ sees what a list answers with and which fields it filters by.
756
+ - **`reference` joins `isoTime` in `/standards`**: the schema of a typed name such as `note:42`.
757
+ - **Four lint rules for lists.** `filter-flag` (an option of a read-only command named after a field of its
758
+ entity, scope flags exempt), `option-role` (an option with no role in the vocabulary), `list-without-query` and
759
+ `query-example`. Today's violations go to your baseline with `--write-baseline`.
760
+ - **`cli-dev lint` checks key casing.** The new rule `key-casing` fails on a key that is not snake_case in
761
+ cli-base's schemas, the commands document as the CLI printed it, and the schemas given with
762
+ `--schemas <file>`. A CLI with camelCase keys left baselines them with `--write-baseline`.
763
+
764
+ ### Security
765
+
766
+ - **Log redaction also hides `refresh_token` and `verify_code`**, so snake_case events are covered.
767
+
768
+ ## 0.24.0 — 13.10.2026
769
+
770
+ ### Changed — may break callers
771
+
772
+ - **`commands --json` names exit code 1 `internal_error`**, the code a crash reports, in place of
773
+ `generic_failure`. A reader that looked the key up by its old name reads `internal_error`.
774
+
775
+ ### Added
776
+
777
+ - **`--debug` in the vocabulary:** on failure, show the full error record, cause and stack.
778
+
779
+ ## 0.23.0 — 13.10.2026
780
+
781
+ ### Added
782
+
783
+ - **`createRecordLog` writes the standard log record.** Each call appends one `LogRecord` as a flat JSON line —
784
+ `time`, `severity`, `body`, `runId`, `traceId`, `command`, `attributes`, `resource` — to a file (mode 0600) or
785
+ hands it to a function, so a log written to stderr gets the same record. The event becomes `attributes`;
786
+ `body` is the message when given, else the event's `event` name. Lines are appended synchronously, so an exit
787
+ never drops the tail.
788
+ - **One failure handler: `errorObjectOf`, `renderError`, `errorRecordOf`, `mcpErrorResult`, `catchCrashes`.**
789
+ Any thrown value becomes the one error object `{ error: { code, reason, message, param?, details?, next?,
790
+ runId? } }`. The reason comes from `details.reason` when the package registered it, else from the code's
791
+ base reason (`BASE_REASONS`, one per code). Only the reason's detail keys and `COMMON_DETAILS` survive;
792
+ `cause` never does. The next step is chosen by the reason's `step`, never by the message text. A person sees
793
+ `✗ message`, one `→ next step` line and `details: <cli> runs show <runId>`; `--debug` callers pass the
794
+ record from `errorRecordOf` instead, the only place a cause or stack is shown. The CLI's machine output and
795
+ MCP's `structuredContent` are the same value. `catchCrashes` reports a throw before a CLI's own handler.
796
+ - **`CRASH_CODE` (`internal_error`) and `CRASH_REASON` (`command.crashed`)** for a failure that is not a
797
+ `CliError`: exit 1, a fixed message — the thrown text never reaches the user. It stays outside `errorCodes`,
798
+ so no command can raise it.
799
+ - `CliErrorDetails` names `reason` and `param`.
800
+ - `FailureContext.details`: detail keys a package allows on every reason until each is registered on its own.
801
+ - **Two `cli-dev lint` rules for help examples**, ported from the Azure CLI linter. `command-example-count`: every
802
+ handwritten leaf has one to three `examples`. `faulty-help-example-parameters`: each example starts with the
803
+ tool, may name a profile, runs the exact command path, gives the arguments that command takes and only its own,
804
+ its parents' and the global options. The check reads `commands --json`, so it needs no live Commander tree.
805
+ Generated commands are exempt: their examples would restate the API they mirror.
806
+ - **Vocabulary for drive, memo and zm.** Verb `run`; resources `files`, `comments`, `meetings`, `recordings`,
807
+ `jobs`, `actions`, `aliases` and the singulars `vocabulary` and `api`; views `transcript` and `evidence`; flag
808
+ `--until-time <time>`.
809
+
810
+ ### Changed — may break callers
811
+
812
+ - **`errorObjectSchema.runId` is optional** until every CLI keeps a run for each failure.
813
+ - **Reasons cli-base raises are dotted and registered:** `command_timeout` → `command.timed-out`,
814
+ `output_limit` → `output.too-large`, `projection_unavailable` → `output.fields-unavailable`. A script that
815
+ matched the old strings has to switch.
816
+ - The handler also recognises a `CliError` from a cli-base copy older than 0.22.0 by name and code, since
817
+ those carry no brand.
818
+ - **`unknown-verb` accepts `search <resource>`.** A leaf under `search` (`search messages`, `bot search messages`)
819
+ passes when its name is a resource noun or `all`, as STANDARD's search hierarchy places it. A CLI that adopts
820
+ this must delete the now-stale baseline entries and overrides, and runs the two example rules for the first
821
+ time: `--write-baseline` records the examples it still lacks.
822
+ - **Redaction applies at every depth, arrays included.** It used to stop two levels down.
823
+
824
+ ### Removed
825
+
826
+ - **`createFileLogger`, `FileLogger` and `FileLoggerOptions`, and the `pino` dependency.** Use `createRecordLog`
827
+ with a `runId`, `command` and `resource`; there is no `close()` to await any more.
828
+
829
+ ## 0.22.0 — 13.10.2026
830
+
831
+ ### Added
832
+
833
+ - **`/standards`: the registries one standard for every CLI is built from.** `VOCABULARY` (verbs, resource nouns,
834
+ view nouns, root words, flags with one meaning each), the answer envelope (`listEnvelopeSchema`,
835
+ `itemEnvelopeSchema`, `pageSchema`, `metaSchema`, `nextStepSchema`, `missingSchema`, `FIELD_RULES`), the error
836
+ object and reasons (`errorObjectSchema`, `defineReasons`), next steps (`STEPS`, `defineSteps`, `stepOf`), the
837
+ log record (`logRecordSchema`) and the lint rules (`LINT_RULES`). Each is a Valibot schema; `jsonSchemaOf`
838
+ emits JSON Schema 2020-12. A malformed entry throws a `RegistryError` naming every problem. Nothing uses them
839
+ yet: the output, error and log streams adopt them in later releases.
840
+ - **`commandsDocument` in `/commands`: one `commands --json` shape for every CLI**, with
841
+ `commandsDocumentSchema`, `commandsDocumentJsonSchema()` and `parseCommandsDocument`. memo, drive and zoom
842
+ print their own shapes today; `cli-dev lint` refuses them until they switch.
843
+ - **`cli-dev lint`** judges a CLI's command tree by the lint rules — verbs from the vocabulary, resources,
844
+ one meaning per flag, summaries present and at most 80 characters, every argument and option described —
845
+ against a shrink-only baseline in `lint/baseline/<rule-id>.json` and counted overrides in
846
+ `lint/overrides.json`.
847
+
848
+ - **`refuseJoinedPath` in `/commands` refuses a command path given as one argument.** `tool "projects add" --help` —
849
+ what a zsh loop over `$c` passes — made Commander print the root help, and an agent read it as a missing
850
+ command. Called on the words before `parseAsync`, it throws a `validation_error` naming the words to type. A
851
+ CLI adopts it at its next bump; memo has the same check of its own until then.
852
+
853
+ ### Fixed
854
+
855
+ - **`isCliError` recognises an error made by another copy of the package.** It checked `instanceof`, which is
856
+ false when a CLI and one of its dependencies each load their own cli-base during a pin bump. It
857
+ now checks a brand set by the constructor; `deadline` and `createOutputBuffer` use it too. Both copies must be
858
+ this release or later: an error made by 0.21.0 carries no brand. Use `isCliError(error)` instead of
859
+ `error instanceof CliError`.
860
+
861
+ ## 0.21.0 — 11.10.2026
862
+
863
+ ### Changed — may break callers
864
+
865
+ - **The package is now `@wirecat/cli-base`, and the repository `WireCatLabs/cli-base`.** The code is the same as
866
+ 0.20.0. Change every `@wirecat/cli-core` import, `extends` (`biome.json`, `tsconfig*.json`), lefthook,
867
+ cspell, rumdl and vale path, and every `uses: WireCatLabs/cli-core/.github/workflows/…` to `cli-base`. The
868
+ name `@wirecat/cli-core` is not deprecated: the messenger layer, now `@wirecat/cli-messaging`, takes it over
869
+ in a later release, with versions above 0.24x, so a pin such as `>=0.20.0 <0.21.0` never crosses over. The
870
+ code generator's default imports are now `@wirecat/cli-base/codegen` and `/codegen/runtime`; pass
871
+ `coreImport` and `runtimeImport` to keep the old ones.
872
+
873
+ ## 0.20.0 — 11.10.2026
874
+
875
+ ### Changed — may break callers
876
+
877
+ - **`resolvePaths` gives the XDG layout on macOS too** — `~/.config/<app>`, `~/.local/share/<app>` and
878
+ `~/.cache/<app>`, or the `XDG_*` variables when set — instead of `~/Library/Preferences`,
879
+ `~/Library/Application Support` and `~/Library/Caches`. One layout on every Unix is one set of
880
+ documentation and one place to look. Linux and Windows paths do not change, and the `*_DIR` overrides
881
+ still win. It now reads `HOME`, the `XDG_*` variables, `USERPROFILE`, `APPDATA` and `LOCALAPPDATA`
882
+ from the `env` it is given, not from the global `process.env`, and takes an optional `platform`.
883
+ **On macOS, a CLI that adopts this release finds none of its existing files** until it calls
884
+ `migrateLegacyMacPaths` at start. A keyring service name built from the config path changes with it.
885
+
886
+ ### Added
887
+
888
+ - **`migrateLegacyMacPaths` moves an older release's `~/Library` folders to the new ones**, once.
889
+ On macOS, with no `*_DIR` override set, it moves each old folder whose new one is missing or empty,
890
+ all of them or none, and returns what moved. It never merges into a folder that has files, and
891
+ across file systems it copies, checks the copy and keeps the old folder. It throws a
892
+ `configuration_error` naming the processes when lsof shows a file open in a pending folder — stop a
893
+ running `serve` or MCP server and run again. lsof sees only files open at that moment, so a caller
894
+ that knows its own lock files should report their holders through `holders`. Call it at program start, before anything resolves a
895
+ path; with nothing to move it costs one `lstat` per folder. `legacyMacPaths` gives the old folders.
896
+
897
+ ### Removed
898
+
899
+ - **The `env-paths` dependency.** The paths it gave on Windows are kept as they were.
900
+
901
+ ## 0.19.4 — 11.10.2026
902
+
903
+ ### Fixed
904
+
905
+ - Package references, documentation and fixtures use the WireCat namespace throughout.
906
+
907
+
908
+ - The owner-migration helper covers meetings, testing, Zoom and the shared organization repositories, so their links and package names migrate with the original CLIs.
909
+
910
+ ## 0.19.3 — 10.10.2026
911
+
912
+ ### Fixed
913
+
914
+ - **Zero command deadlines abort synchronously** before a body can execute, matching the documented
915
+ immediate deadline instead of scheduling a timer that could lose to a fast command. They create
916
+ no sleep or timer; an already aborted parent retains its cancellation reason.
917
+
918
+ ## 0.19.2 — 10.10.2026
919
+
920
+ ### Added
921
+
922
+ - **Portable command control helpers** expose `fieldsOf`, `projectFields`, `createOutputBuffer`
923
+ and `createDeadline` from the root export. Field projection retains list and operation metadata;
924
+ a bounded UTF-8 buffer delays data until hosts finish cleanup. Deadlines use injected sleep and
925
+ cancellation, waiting for the body to settle before callers close resources. Callers must propagate
926
+ the signal, dispose the deadline and discard buffered output on failure; no provider/store lifecycle
927
+ or automatic retries are imported.
928
+
929
+ ### Fixed
930
+
931
+ - Windows maintenance commands support portable relative PATH entries and custom or sanitized command-processor environments while preserving separate arguments.
932
+
933
+ ### Security
934
+
935
+ - Release checks include source and secret analysis; critical production advisories and critical source findings block publication. Workflow findings are reported for review. PR security remains the fast secret scan.
936
+
937
+ ## 0.19.0 — 10.10.2026
938
+
939
+ ### Fixed
940
+
941
+ - Windows updater and MCP setup commands resolve through absolute PATH entries and use an explicit command processor, avoiding accidental execution from the current folder. POSIX resolution is unchanged.
942
+
943
+ ### Added
944
+
945
+ - `/update` exports `executableOnPath` and `executableEnvironment` for consumers using the same Windows command resolution.
946
+
947
+ ## 0.18.2 — 10.10.2026
948
+
949
+ ### Changed — may break callers
950
+
951
+ - **The project is now licensed under Apache License 2.0.** See `LICENSE` for the terms.
952
+
953
+ ## 0.18.1 — 10.10.2026
954
+
955
+ ### Fixed
956
+
957
+ - **The shared spelling list knows `wirecat`.** `cspell` no longer flags the new package scope in READMEs.
958
+
959
+ ## 0.18.0 — 10.10.2026
960
+
961
+ ### Changed — may break callers
962
+
963
+ - **The package is now `@wirecat/cli-core`, and the repository is `WireCatLabs/cli-core`.** Install
964
+ `@wirecat/cli-core` and change imports from `@wirecat/cli-core` (and its subpaths) to `@wirecat/cli-core`;
965
+ the code is the same as 0.17.3. `@wirecat/cli-core` gets no new versions. Call the reusable workflows
966
+ as `WireCatLabs/cli-core/.github/workflows/...`.
967
+
968
+ ## 0.17.3 — 09.10.2026
969
+
970
+ ### Changed — may break callers
971
+
972
+ - **Production dependency and workflow security checks run before releases.** Call the reusable
973
+ `release-checks.yml` workflow from each release workflow and require it before building. `node-ci.yml`
974
+ keeps lint, typecheck, coverage, build, Bun and secret scanning on changes.
975
+
976
+ ### Fixed
977
+
978
+ - **Display sanitization makes Unicode tags and byte-order marks visible.** Subdivision flag emoji
979
+ stay intact. CLI machine output continues to preserve original strings.
980
+
981
+ ## 0.17.2 — 07.10.2026
982
+
983
+ ### Added
984
+
985
+ - **`indent(text, spaces)` shifts every line of a block right.** A step's body under its heading, or a
986
+ QR code, can sit indented in a setup screen. Empty lines stay empty, so no trailing spaces are printed.
987
+
988
+ ## 0.17.1 — 04.10.2026
989
+
990
+ ### Fixed
991
+
992
+ - **`cli-dev docs-check` and `markdownFiles` skip what is not a file or a folder.** A device or a
993
+ pipe named `*.md` used to be read, and the check crashed. Claude Code's Bash sandbox lays devices
994
+ over names in the working folder (`.claude/loop.md`, `.claude/skills`), so the check failed in
995
+ every agent session with the sandbox on. A symbolic link to a file is still listed; a link to a
996
+ folder is still not followed.
997
+
998
+ ## 0.17.0 — 03.10.2026
999
+
1000
+ ### Changed — may break callers
1001
+
1002
+ - Consumers switching exhaustively over `SchemaNode` or API parameter locations must handle explicit unions and RPC body parameters. Existing HTTP models generate the same artifacts.
1003
+
1004
+ ### Added
1005
+
1006
+ - API generators preserve explicit unions and Boolean literals, validate nested references, and emit schema definitions for shared command input traversal. RPC parameters can belong to the JSON body; multipart references retain their format. Source adapters can mark secret fields so consumers keep them out of generated CLI arguments.
1007
+
1008
+ ## 0.16.0 — 02.10.2026
1009
+
1010
+ ### Added
1011
+
1012
+ - **`@wirecat/cli-core/mcp` installs and checks local stdio MCP entries.** A CLI can add its existing
1013
+ MCP server to Codex or Claude Code through those clients' own commands and probe the handshake and
1014
+ tool list without calling a tool. Existing entries are refused, so callers must remove one in the
1015
+ client before changing it.
1016
+ Client commands use `cross-spawn` so npm's Windows `.cmd` shims can be started.
1017
+
1018
+ ## 0.15.0 — 01.10.2026
1019
+
1020
+ ### Added
1021
+
1022
+ - **`annotate(command, { mutates: true, local: true })`** marks a write that changes only this
1023
+ machine — a file, the keyring — and never the service. `CommandInfo.local` carries it, and
1024
+ `mutates` stays true, so a caller that tells reads from writes still sees a write.
1025
+ - **`commandsPage` text takes `mutatesLocal`**, the line shown under such a command instead of
1026
+ `mutates`. Without it a local write gets no line. Before this, tg-cli labelled `config set` as
1027
+ changing Telegram.
1028
+
1029
+ ## 0.14.0 — 01.10.2026
1030
+
1031
+ ### Added
1032
+
1033
+ - **Shared settings for checking docs**: `@wirecat/cli-core/cspell` (spelling in English, British
1034
+ English and Russian, with the shared word list; code and links are not checked),
1035
+ `config/rumdl.toml` (Markdown form) and `config/vale/` (prose: filler words and long sentences in
1036
+ both languages, and tg's tone). A repository installs the tools itself — [README](docs/TOOLING.md).
1037
+ - **`cli-dev docs-check --pages`** also checks `docs/` against `docs/meta.json`, the docs portal's
1038
+ page structure.
1039
+
1040
+ ## 0.13.0 — 01.10.2026
1041
+
1042
+ ### Added
1043
+
1044
+ - **`skillCommand(app, skill, environment, more)`**: `more` names skills a library ships for every CLI
1045
+ built on it, and `skill show <name>` prints one, with `{{command}}` turned into the CLI's command.
1046
+ cli-messaging ships one for linking conversations. They are printed, never installed — two CLIs
1047
+ would install the same name. Without `more`, `skill show` takes no argument, as before.
1048
+ - **The shared pre-push hook runs `parity:check` after `typecheck`**, when the project has that
1049
+ script. tg-cli and max-cli check their commands and pages against cli-messaging's parity manifest
1050
+ before a push instead of a minute later in CI. A project without the script sees no change. Why in
1051
+ one command: the check builds into the same `dist/` that typecheck writes, and two `tsc` runs side
1052
+ by side race on its state file.
1053
+
1054
+ ## 0.12.0 — 01.10.2026
1055
+
1056
+ ### Changed — may break callers
1057
+
1058
+ - **`commandsPage` writes what tg-cli's own copy of it already did**: each description ends as a
1059
+ sentence, the allowed values follow it ("One of: `new`, `old`."), an argument shows its default
1060
+ as an option does, and a `~` is escaped — `~~struck~~` in a description struck out the rest of
1061
+ the row. `CommandsPageLabels` gains `oneOf` (`COMMANDS_PAGE_LABELS` has it in both languages).
1062
+ What to watch for: a regenerated `commands.md` changes on almost every row; a caller with its
1063
+ own labels adds `oneOf`.
1064
+
1065
+ ## 0.11.0 — 01.10.2026
1066
+
1067
+ ### Added
1068
+
1069
+ - **Shared configuration a CLI extends instead of copying.** `@wirecat/cli-core/tsconfig.base.json`
1070
+ (compiler options), `/tsconfig.test.json` and `/tsconfig.scripts.json` (the no-emit overlays for
1071
+ tests and `scripts/`), `/biome` (formatter, linter, the `scripts/` console rule) and
1072
+ `config/lefthook.yml` (the git hooks). Paths, file lists and import rules stay in each repository.
1073
+ What to watch for: in lefthook, the extended file overrides your own `lefthook.yml`, not the other
1074
+ way round. How to adopt each: [README](docs/TOOLING.md).
1075
+ - **`cli-dev`, a command for the development scripts every CLI carried a copy of:** `slow-tests`,
1076
+ `next-version`, `version`, `docs-check` and `test-matrix`. Each prints what the script it replaces
1077
+ printed; the test matrix page names `cli-dev` in its first line, so a repository regenerates and
1078
+ commits that page once when it switches.
1079
+ - **`.github/workflows/node-ci.yml`, a reusable workflow:** install, lint, typecheck, test with
1080
+ coverage, build, the extra checks a repository passes in, the Bun run and a gitleaks scan of the
1081
+ whole history.
1082
+
1083
+ ## 0.10.0 — 01.10.2026
1084
+
1085
+ ### Added
1086
+
1087
+ - **`@wirecat/cli-core/skill` installs a CLI's SKILL.md and tells agents when to.** `skillCommand`
1088
+ is the `skill` command, moved down from cli-messaging, with a new `install [--for claude|agents|all]`
1089
+ that writes the file to `~/.claude/skills/<appName>/` and `~/.agents/skills/<appName>/` and records
1090
+ the CLI's version as `metadata.version` in its frontmatter — the Agent Skills specification has no
1091
+ top-level `version`. It refuses a SKILL.md whose `name` differs from `<appName>`, the directory it
1092
+ writes to, which the specification requires to match. `skillHint` is a once-a-day line for an agent whose copy is
1093
+ missing or older than the CLI; `skillResource` serves the file to an MCP client as `<command>://skill`.
1094
+ An agent used to learn of the skill only by reading `--help`.
1095
+ What to watch for: `skillCommand` takes a third argument, the host's renderer, streams and `env`;
1096
+ it imports Commander at run time, so a CLI using `/skill` needs Commander installed, as all four do.
1097
+ - **The update state file keeps a second field, `skillHintAt`,** and `writeUpdateState` now merges
1098
+ into the file as it is when written, instead of replacing it, so the two daily notices do not erase
1099
+ each other. It accepts a partial state; `readNoticeState` reads the file whether or not npm was asked.
1100
+ - **`commandsPage` in `@wirecat/cli-core/commands` writes a CLI's whole `docs/commands.md`** from
1101
+ the command tree: every command at any depth, its usage, arguments and options with defaults, and
1102
+ the exit codes. The table labels come in English and Russian (`COMMANDS_PAGE_LABELS`); the CLI
1103
+ passes its own title, introduction and closing words. It is max-cli's generator, moved here so
1104
+ tg-cli gets the same page from the same code; max-cli's page comes out byte for byte the same.
1105
+ - **`structureProblems` in `@wirecat/cli-core/release` checks a docs folder against its
1106
+ `meta.json`**, the sidebar the docs portal reads: every listed page exists, every page is listed
1107
+ once, the required pages are there (`REQUIRED_PAGES`), `meta.json` holds only Fumadocs' keys,
1108
+ and each page has exactly one `# ` heading, first. `README.md` in the folder is left out — it is
1109
+ the contributors' index.
1110
+
1111
+ ### Fixed
1112
+
1113
+ - **`Credentials.write` refuses to replace a credentials file it cannot parse.** It used to read a
1114
+ broken file as empty, and the next write replaced it, losing every other secret stored in it. Now
1115
+ it throws a `CliError` with code `configuration_error` that names the file and never quotes it.
1116
+ `read` still treats such a file as holding nothing, and `remove` leaves it alone.
1117
+ What to watch for: `write` can now throw when the keyring is unavailable and the file is broken —
1118
+ the user fixes the file or moves it aside. An empty file counts as broken.
1119
+ - **A generated Valibot schema accepts a 64-bit integer enum.** The values are matched as the
1120
+ decimal strings a 64-bit integer becomes, as the generated type already said; before, no input
1121
+ passed.
1122
+ - **Generation stops, naming the schema, when an `allOf` reaches its own schema**, directly or
1123
+ through others. It used to overflow the stack.
1124
+
1125
+ ## 0.9.0 — 30.09.2026
1126
+
1127
+ ### Added
1128
+
1129
+ - **`@wirecat/cli-core/release` holds the release checks that max-cli and tg-cli share.** A runner,
1130
+ `releaseCheck`, that runs every check and prints one `ok` or `FAIL` line each, and the checks a
1131
+ program can decide: a command that must exit 0, the version not yet on npm, the package contents,
1132
+ the changelog's shape, links and anchors in the documents, and the version in `package.json` and
1133
+ `src/version.ts` in step. Until now each CLI kept its own copy, and the copies had drifted apart.
1134
+ Each caller passes its own changelog headings, id prefixes and user pages.
1135
+
1136
+ ## 0.8.0 — 27.09.2026
1137
+
1138
+ ### Added
1139
+
1140
+ - **`@wirecat/cli-core/codegen` generates an API catalog from an official description, at build
1141
+ time.** It reads a format-neutral `ApiModel` — each operation with an `http` or `rpc` binding, an
1142
+ effect (`read`, `write`, `destructive`) and how far its source can be trusted — and writes
1143
+ TypeScript types, Valibot schemas, an operation manifest and a coverage page. `writeArtifacts`
1144
+ writes them, or with `check` only reports which are stale.
1145
+ Why: max-cli's bot commands are generated from the MAX Bot API description, and the same machinery
1146
+ serves any CLI that has such a description.
1147
+ What to watch for: generation stops, naming the problem, on colliding names, an operation not
1148
+ classified as a read or a write, an override that matches nothing, an unknown reference or a
1149
+ construct it cannot express — it never drops an operation or falls back to `any`. No parser ships:
1150
+ the OpenAPI or Postman adapter lives in the CLI that owns the source.
1151
+ - **`@wirecat/cli-core/codegen/runtime`**, the few number helpers the generated schemas import at run
1152
+ time. A 64-bit integer comes out as its exact decimal string from a `lossless-json` number; any
1153
+ other integer that does not fit a JS number fails instead of rounding.
1154
+ What to watch for: the schemas decode, they do not encode — validate a request with the schema,
1155
+ then send the original lossless value.