@wirecat/cli-base 0.69.0 → 0.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/dist/dev/lint.js +7 -4
- package/dist/standards/audit.js +24 -8
- package/dist/standards/flags-mail.d.ts +2 -0
- package/dist/standards/flags-mail.js +22 -0
- package/dist/standards/flags-what.js +10 -2
- package/dist/standards/vocabulary.d.ts +5 -0
- package/dist/standards/vocabulary.js +8 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,39 @@ Versions before 0.8.0 are in the [git tags](https://github.com/WireCatLabs/cli-b
|
|
|
8
8
|
Every entry says what changed as a caller sees it, why, and what to watch for — the rules are
|
|
9
9
|
[`docs/dev/CONVENTIONS.md`](docs/dev/CONVENTIONS.md#the-changelog).
|
|
10
10
|
|
|
11
|
+
## 0.71.0 — 01.11.2026
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **A vocabulary flag can list the value names other domains give it (`otherValues`).** `--to` stays `<chat>` and also
|
|
16
|
+
takes `<address>` (mail); `--subject` stays `<ref>` and also takes `<text>`. `flag-two-meanings` accepts those and
|
|
17
|
+
reports any other value name. A CLI that declares `--to <address>` or `--subject <text>` needs no override.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **0.70.0 made every messenger CLI fail `flag-two-meanings`.** It wrote `--to` as `<chat|address>` and `--subject`
|
|
22
|
+
as `<ref|text>`, and the lint compares the value name as written, so `--to <chat>` was reported. Both go back to
|
|
23
|
+
the original value name, and mail's names are listed beside them. Do not use 0.70.0.
|
|
24
|
+
|
|
25
|
+
## 0.70.0 — 01.11.2026
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Mail words in the vocabulary: the `drafts` resource and `--cc`, `--bcc`, `--reply-all`.** mail-cli saves drafts
|
|
30
|
+
in a mailbox's Drafts folder (`mail <profile> drafts list|create|update|delete`) and addresses e-mail with copies;
|
|
31
|
+
the command lint reported each as a word it did not know.
|
|
32
|
+
- **`--to` takes a chat or an e-mail address, `--subject` takes a reference or a subject line.** The owner ruled
|
|
33
|
+
that mail shares these two words with the messengers and notes instead of renaming them. `--to` is now
|
|
34
|
+
`<chat|address>`, a `string` (was an `id`); `--subject` is `<ref|text>`. The meaning per domain is in each flag's
|
|
35
|
+
text, so `flag-two-meanings` stops reporting mail's use. Mail-cli can drop its overrides for both.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **mail is compared in its own parity group, `mail-shared`, which leaves out `search`.** mail's `search messages` is
|
|
40
|
+
cli-core's mail search, a different command from the messengers' under the same path, and `audit --complete` took
|
|
41
|
+
its twenty-odd differences as inherited. mail is compared on every other shared root, and moves back into `all`
|
|
42
|
+
once its search is aligned with the messengers' or renamed.
|
|
43
|
+
|
|
11
44
|
## 0.69.0 — 01.11.2026
|
|
12
45
|
|
|
13
46
|
### Added
|
package/dist/dev/lint.js
CHANGED
|
@@ -229,7 +229,10 @@ const CHECKS = {
|
|
|
229
229
|
}));
|
|
230
230
|
},
|
|
231
231
|
"flag-two-meanings": (document, vocabulary) => {
|
|
232
|
-
const registered = new Map(vocabulary.flags.map((flag) => [
|
|
232
|
+
const registered = new Map(vocabulary.flags.map((flag) => [
|
|
233
|
+
flag.name,
|
|
234
|
+
[flag.value?.slice(1, -1).toLowerCase() ?? "switch", ...(flag.otherValues ?? [])],
|
|
235
|
+
]));
|
|
233
236
|
const uses = new Map();
|
|
234
237
|
const note = (option, where) => {
|
|
235
238
|
const name = longName(option.flags);
|
|
@@ -246,8 +249,8 @@ const CHECKS = {
|
|
|
246
249
|
const found = [];
|
|
247
250
|
for (const [name, kinds] of [...uses].sort(([a], [b]) => byText(a, b))) {
|
|
248
251
|
const expected = registered.get(name);
|
|
249
|
-
const off = expected === undefined ? [] : [...kinds.keys()].filter((kind) => kind
|
|
250
|
-
if (kinds.size < 2
|
|
252
|
+
const off = expected === undefined ? [] : [...kinds.keys()].filter((kind) => !expected.includes(kind));
|
|
253
|
+
if (expected === undefined ? kinds.size < 2 : off.length === 0)
|
|
251
254
|
continue;
|
|
252
255
|
const described = [...kinds]
|
|
253
256
|
.map(([kind, where]) => `${kind === "switch" ? "a switch" : `<${kind}>`} in ${where.slice(0, 3).join(", ")}`)
|
|
@@ -256,7 +259,7 @@ const CHECKS = {
|
|
|
256
259
|
target: name,
|
|
257
260
|
message: expected === undefined
|
|
258
261
|
? `${name} has ${kinds.size} meanings: ${described}`
|
|
259
|
-
: `${name} is ${expected === "switch" ? "a switch" : `<${
|
|
262
|
+
: `${name} is ${expected[0] === "switch" ? "a switch" : expected.map((kind) => `<${kind}>`).join(" or ")} in the vocabulary, but: ${described}`,
|
|
260
263
|
});
|
|
261
264
|
}
|
|
262
265
|
return found;
|
package/dist/standards/audit.js
CHANGED
|
@@ -68,19 +68,35 @@ export const defineParityGroups = (input) => parseRegistry("parity groups", v.ar
|
|
|
68
68
|
.map((entry) => `${group.name}: "${entry.target}" has \`absent\`, which measures the values itself; drop \`expected\``),
|
|
69
69
|
]),
|
|
70
70
|
]);
|
|
71
|
+
const MEMO_HAS_NO_PROFILES = [
|
|
72
|
+
"mcp serve",
|
|
73
|
+
"mcp revoke",
|
|
74
|
+
"mcp server start",
|
|
75
|
+
"mcp server status",
|
|
76
|
+
"mcp server stop",
|
|
77
|
+
].map((path) => ({
|
|
78
|
+
rule: "same-path-contract",
|
|
79
|
+
target: `${path} --profiles`,
|
|
80
|
+
absent: "memo",
|
|
81
|
+
reason: "memo has no profiles: it reads other CLIs' stores and selects their accounts with --source and --account " +
|
|
82
|
+
"(https://github.com/WireCatLabs/cli-base/issues/390)",
|
|
83
|
+
}));
|
|
71
84
|
export const PARITY_GROUPS = defineParityGroups([
|
|
72
85
|
{ name: "messengers", members: ["tg", "max"], covers: "tree", differences: [] },
|
|
73
86
|
{
|
|
74
87
|
name: "all",
|
|
75
|
-
members: ["tg", "max", "memo", "drive", "zm", "wa"
|
|
88
|
+
members: ["tg", "max", "memo", "drive", "zm", "wa"],
|
|
76
89
|
covers: ["commands", "doctor", "skill", "mcp", "upgrade", "search", "notes", "meetings"],
|
|
77
|
-
differences:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
differences: MEMO_HAS_NO_PROFILES,
|
|
91
|
+
},
|
|
92
|
+
// mail's `search messages` is cli-core's mail search, not the messengers' command: --complete took their 20-odd
|
|
93
|
+
// differences as inherited (https://github.com/WireCatLabs/cli-base/issues/390). mail is compared everywhere else
|
|
94
|
+
// and moves back into "all" once its search is aligned with the messengers' or renamed.
|
|
95
|
+
{
|
|
96
|
+
name: "mail-shared",
|
|
97
|
+
members: ["tg", "max", "memo", "drive", "zm", "wa", "mail"],
|
|
98
|
+
covers: ["commands", "doctor", "skill", "mcp", "upgrade", "notes", "meetings"],
|
|
99
|
+
differences: MEMO_HAS_NO_PROFILES,
|
|
84
100
|
},
|
|
85
101
|
// gcal is compared on what it already shares with every CLI; doctor, mcp, search and skill wait until the published
|
|
86
102
|
// CLIs agree there (https://github.com/WireCatLabs/cli-base/issues/390), then gcal moves back into "all".
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const MAIL_FLAGS = [
|
|
2
|
+
{
|
|
3
|
+
name: "--cc",
|
|
4
|
+
value: "<address>",
|
|
5
|
+
role: "value",
|
|
6
|
+
type: "string",
|
|
7
|
+
meaning: "an e-mail address that gets a copy; repeat it for more",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: "--bcc",
|
|
11
|
+
value: "<address>",
|
|
12
|
+
role: "value",
|
|
13
|
+
type: "string",
|
|
14
|
+
meaning: "an e-mail address that gets a blind copy; repeat it for more",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "--reply-all",
|
|
18
|
+
role: "value",
|
|
19
|
+
type: "switch",
|
|
20
|
+
meaning: "with --reply-to, an e-mail reply also goes to everyone the message went to",
|
|
21
|
+
},
|
|
22
|
+
];
|
|
@@ -442,7 +442,14 @@ export const WHAT_FLAGS = [
|
|
|
442
442
|
meaning: "keep a token an operation returns only in this bot profile's keyring; never print it",
|
|
443
443
|
},
|
|
444
444
|
{ name: "--task", value: "<id>", role: "target", type: "id", meaning: "a task, by id" },
|
|
445
|
-
{
|
|
445
|
+
{
|
|
446
|
+
name: "--to",
|
|
447
|
+
value: "<chat>",
|
|
448
|
+
role: "target",
|
|
449
|
+
type: "id",
|
|
450
|
+
otherValues: ["address"],
|
|
451
|
+
meaning: "who it goes to: a chat in a messenger (<chat>), an e-mail address in mail (<address>, repeated for more)",
|
|
452
|
+
},
|
|
446
453
|
{
|
|
447
454
|
name: "--topic-name",
|
|
448
455
|
value: "<name>",
|
|
@@ -892,7 +899,8 @@ export const WHAT_FLAGS = [
|
|
|
892
899
|
value: "<ref>",
|
|
893
900
|
role: "value",
|
|
894
901
|
type: "string",
|
|
895
|
-
|
|
902
|
+
otherValues: ["text"],
|
|
903
|
+
meaning: "what it is about: a reference in a note or task (<ref>), the subject line in mail (<text>)",
|
|
896
904
|
replaces: ["--about"],
|
|
897
905
|
},
|
|
898
906
|
{
|
|
@@ -43,6 +43,11 @@ export declare const vocabularySchema: v.StrictObjectSchema<{
|
|
|
43
43
|
readonly type: v.PicklistSchema<readonly ["switch", "count", "number", "string", "id", "time", "duration", "choice", "list", "path", "cursor"], undefined>;
|
|
44
44
|
readonly role: v.PicklistSchema<["action" | "budget" | "mode" | "output" | "paging" | "query" | "scope" | "sort" | "target" | "value", ...("action" | "budget" | "mode" | "output" | "paging" | "query" | "scope" | "sort" | "target" | "value")[]], undefined>;
|
|
45
45
|
readonly meaning: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "must not be empty">]>;
|
|
46
|
+
/**
|
|
47
|
+
* Value names a domain other than the main one gives the flag, such as mail's `--to <address>` beside the
|
|
48
|
+
* messengers' `<chat>`. The meaning for each says which is which. `flag-two-meanings` accepts these and no others.
|
|
49
|
+
*/
|
|
50
|
+
readonly otherValues: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a value name such as address">]>, undefined>, undefined>;
|
|
46
51
|
readonly unit: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "must not be empty">]>, undefined>;
|
|
47
52
|
/** Other names for the same flag, which `flag-synonym` refuses. */
|
|
48
53
|
readonly replaces: v.OptionalSchema<v.ArraySchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a long flag">]>, undefined>, undefined>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
import { CALENDAR_FLAGS } from "./flags-calendar.js";
|
|
3
3
|
import { HOW_FLAGS } from "./flags-how.js";
|
|
4
|
+
import { MAIL_FLAGS } from "./flags-mail.js";
|
|
4
5
|
import { WHAT_FLAGS } from "./flags-what.js";
|
|
5
6
|
import { duplicates, parseRegistry, text, word } from "./registry.js";
|
|
6
7
|
export const FLAG_TYPES = [
|
|
@@ -48,6 +49,11 @@ const flagSchema = v.strictObject({
|
|
|
48
49
|
type: v.picklist(FLAG_TYPES),
|
|
49
50
|
role: v.picklist(Object.keys(FLAG_ROLES)),
|
|
50
51
|
meaning: text,
|
|
52
|
+
/**
|
|
53
|
+
* Value names a domain other than the main one gives the flag, such as mail's `--to <address>` beside the
|
|
54
|
+
* messengers' `<chat>`. The meaning for each says which is which. `flag-two-meanings` accepts these and no others.
|
|
55
|
+
*/
|
|
56
|
+
otherValues: v.optional(v.array(v.pipe(v.string(), v.regex(/^[a-z][a-z0-9-]*$/, "a value name such as address")))),
|
|
51
57
|
unit: v.optional(text),
|
|
52
58
|
/** Other names for the same flag, which `flag-synonym` refuses. */
|
|
53
59
|
replaces: v.optional(v.array(v.pipe(v.string(), v.regex(/^--[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "a long flag")))),
|
|
@@ -212,6 +218,7 @@ const collections = [
|
|
|
212
218
|
["chats", "conversations in a messenger"],
|
|
213
219
|
["contacts", "identities in a messenger's contact list"],
|
|
214
220
|
["messages", "posts in a messenger chat, or emails"],
|
|
221
|
+
["drafts", "e-mails saved in a mailbox's Drafts folder, written and not sent"],
|
|
215
222
|
["polls", "polls in a chat"],
|
|
216
223
|
["reactions", "reactions to a message"],
|
|
217
224
|
["recipients", "who a message goes to"],
|
|
@@ -359,5 +366,5 @@ export const VOCABULARY = defineVocabulary({
|
|
|
359
366
|
],
|
|
360
367
|
views: views.map(([entry, meaning]) => ({ word: entry, meaning })),
|
|
361
368
|
roots: roots.map(([entry, meaning]) => ({ word: entry, meaning })),
|
|
362
|
-
flags: [...HOW_FLAGS, ...WHAT_FLAGS, ...CALENDAR_FLAGS],
|
|
369
|
+
flags: [...HOW_FLAGS, ...WHAT_FLAGS, ...CALENDAR_FLAGS, ...MAIL_FLAGS],
|
|
363
370
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-base",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.71.0",
|
|
4
4
|
"description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|