@wirecat/cli-core 0.394.0 → 0.395.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 +47 -0
- package/dist/cli/config-command.js +5 -2
- package/dist/cli/messenger/account-command.js +8 -3
- package/dist/cli/messenger/account-sessions-command.js +7 -2
- package/dist/cli/messenger/conversation-examples.d.ts +2 -0
- package/dist/cli/messenger/conversation-examples.js +25 -0
- package/dist/cli/messenger/conversations-command.js +2 -0
- package/dist/cli/messenger/mcp-command.js +12 -3
- package/dist/cli/messenger/models-command.js +15 -6
- package/dist/cli/messenger/polls-command.js +20 -5
- package/dist/cli/messenger/records-command.js +10 -2
- package/dist/cli/messenger/search-command.js +12 -0
- package/dist/cli/messenger/server-command.js +19 -7
- package/dist/cli/messenger/tasks-command.js +18 -6
- package/dist/cli/runs/command.js +11 -4
- package/dist/cli/skill-command.js +6 -1
- package/dist/entities/index.d.ts +1 -1
- package/dist/entities/saved-search.d.ts +1 -1
- package/dist/entities/saved-search.js +1 -1
- package/dist/errors/reasons.js +1 -0
- package/dist/meetings/cli/search.js +1 -1
- package/dist/meetings/hit-query.d.ts +10 -0
- package/dist/meetings/hit-query.js +85 -1
- package/dist/meetings/index.d.ts +1 -1
- package/dist/meetings/index.js +1 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +2 -1
- package/dist/services/meeting-combined.d.ts +1 -1
- package/dist/services/meeting-combined.js +4 -3
- package/dist/services/meeting-hit-search.d.ts +1 -1
- package/dist/services/meeting-hit-search.js +4 -2
- package/dist/services/meeting-searches.d.ts +25 -0
- package/dist/services/meeting-searches.js +35 -0
- package/dist/services/search-record.d.ts +1 -1
- package/dist/services/searches.d.ts +1 -0
- package/dist/services/searches.js +17 -4
- package/dist/store/searches.d.ts +5 -4
- package/dist/store/searches.js +4 -4
- package/dist/store/sqlite/searches.d.ts +4 -4
- package/dist/store/sqlite/searches.js +21 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,53 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
|
|
|
5
5
|
minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
|
|
6
6
|
shape of this file.
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## 0.395.0 — 23.10.2026
|
|
11
|
+
|
|
12
|
+
### Changed — may break callers
|
|
13
|
+
|
|
14
|
+
- `searches list`, `searches history` and `searches clear` cover only message searches. The table is shared by
|
|
15
|
+
every CLI on the store; a saved meeting search shown, run or deleted from a messenger is refused with
|
|
16
|
+
`details.reason: "search.kind"`. `SearchesStore.savedSearches`, `searchHistory` and `clearSearchHistory` take an
|
|
17
|
+
optional list of kinds.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `search meetings` takes `date:`, `project:`, `tag:` and `topic:` in the query: `budget project:RES date>=30d`. They
|
|
22
|
+
choose meetings as `--since-time`/`--until-time`, `--project`, `--tag` and `--topic` do, join the words with AND,
|
|
23
|
+
and are refused inside OR, NOT and groups (`query.unsupported-field`) or when the same filter is also an option.
|
|
24
|
+
Search by meaning embeds only the words. `liftMeetingFields` (`@wirecat/cli-core/meetings`) does the split.
|
|
25
|
+
|
|
26
|
+
- `meetingSearchesService` (`@wirecat/cli-core/services`): saved meeting searches, kind `meetings-search`, in the
|
|
27
|
+
same table: create, list, show, delete and `run`, which counts the run.
|
|
28
|
+
|
|
29
|
+
- MCP setup and diagnostic commands include examples in help and command discovery.
|
|
30
|
+
|
|
31
|
+
- Account commands include concrete examples in help and command discovery.
|
|
32
|
+
|
|
33
|
+
- Search commands include concrete examples in help and command discovery.
|
|
34
|
+
|
|
35
|
+
- Configuration commands include concrete examples in help and command discovery.
|
|
36
|
+
|
|
37
|
+
- Skill commands include concrete examples in help and command discovery.
|
|
38
|
+
|
|
39
|
+
- Task commands include concrete examples in help and command discovery.
|
|
40
|
+
|
|
41
|
+
- Chat deletion and history clearing include examples in help and command discovery.
|
|
42
|
+
|
|
43
|
+
- Poll commands include concrete examples in help and command discovery.
|
|
44
|
+
|
|
45
|
+
- Listing poll voters includes an example in help and command discovery.
|
|
46
|
+
|
|
47
|
+
- Model commands include concrete examples in help and command discovery.
|
|
48
|
+
|
|
49
|
+
- Recorded-run commands include concrete examples in help and command discovery.
|
|
50
|
+
|
|
51
|
+
- Conversation commands include examples in help and command discovery, with guidance for submitting a batch answer.
|
|
52
|
+
|
|
53
|
+
- Server commands include concrete examples in help and command discovery.
|
|
54
|
+
|
|
8
55
|
## 0.394.0 — 23.10.2026
|
|
9
56
|
|
|
10
57
|
### Added
|
|
@@ -16,7 +16,7 @@ import { changeStoreSetting, isStoreSetting, STORE_SETTINGS, storeSettings } fro
|
|
|
16
16
|
*/
|
|
17
17
|
export const configCommand = (app, config, { permissionKey } = {}) => {
|
|
18
18
|
const command = annotate(new Command("config"), { mcp: false }).description("the settings in force, and where each one came from");
|
|
19
|
-
annotate(command.command("show"), { effects: LOCAL_READ })
|
|
19
|
+
annotate(command.command("show"), { effects: LOCAL_READ, examples: [`${app.command} personal config show`] })
|
|
20
20
|
.description("the profile, the profiles that exist, and each setting with where it came from")
|
|
21
21
|
.option("--bot", "the settings a bot command on this profile gets, rather than the personal account's")
|
|
22
22
|
.action(async function () {
|
|
@@ -42,7 +42,10 @@ export const configCommand = (app, config, { permissionKey } = {}) => {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
for (const action of ["set", "unset"]) {
|
|
45
|
-
const sub = grantsAuthority(annotate(command.command(action), {
|
|
45
|
+
const sub = grantsAuthority(annotate(command.command(action), {
|
|
46
|
+
effects: LOCAL_WRITE,
|
|
47
|
+
examples: [`${app.command} personal config ${action} limit${action === "set" ? " 20" : ""}`],
|
|
48
|
+
}))
|
|
46
49
|
.argument("<setting>",
|
|
47
50
|
// default_profile is only removed now: `config unset` still takes it, `config set` refuses it.
|
|
48
51
|
`one of: ${[...config.allSettings.filter((one) => action === "unset" || one !== "default_profile"), ...STORE_SETTINGS, "account_groups.<name>"].join(", ")}`)
|
|
@@ -14,9 +14,10 @@ import { accountAnswer, renderItem, wholeList } from "./chat-answers.js";
|
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
15
|
import { privacyCommand } from "./records-command.js";
|
|
16
16
|
export const accountCommand = (messenger) => {
|
|
17
|
+
const tool = `${messenger.app.command} personal`;
|
|
17
18
|
const account = new Command("account")
|
|
18
19
|
.description("the logged-in account")
|
|
19
|
-
.addCommand(annotate(new Command("list"), { effects: REMOTE_READ })
|
|
20
|
+
.addCommand(annotate(new Command("list"), { effects: REMOTE_READ, examples: [`${tool} account list`] })
|
|
20
21
|
.description("every profile on this computer, and the account each is logged in as; asks the messenger nothing")
|
|
21
22
|
.action(async function () {
|
|
22
23
|
const context = messengerContext(this, messenger, { needsProfile: false });
|
|
@@ -45,7 +46,11 @@ export const accountCommand = (messenger) => {
|
|
|
45
46
|
await store?.close();
|
|
46
47
|
}
|
|
47
48
|
}))
|
|
48
|
-
.addCommand(annotate(new Command("show"), {
|
|
49
|
+
.addCommand(annotate(new Command("show"), {
|
|
50
|
+
effects: REMOTE_READ,
|
|
51
|
+
output: { entity: "account" },
|
|
52
|
+
examples: [`${tool} account show`],
|
|
53
|
+
})
|
|
49
54
|
.description("who this profile is logged in as; the phone number shows its last four digits")
|
|
50
55
|
.option("--show-phone", "print the whole phone number")
|
|
51
56
|
.action(async function () {
|
|
@@ -54,7 +59,7 @@ export const accountCommand = (messenger) => {
|
|
|
54
59
|
const account = await context.withMessenger((connection) => connection.me());
|
|
55
60
|
renderAnswer(this, context, itemOf(accountAnswer(messenger.provider, whole ? account : maskedAccount(account))));
|
|
56
61
|
}))
|
|
57
|
-
.addCommand(annotate(new Command("update"), { effects: REMOTE_WRITE })
|
|
62
|
+
.addCommand(annotate(new Command("update"), { effects: REMOTE_WRITE, examples: [`${tool} account update --first-name Alex`] })
|
|
58
63
|
.description("change the name, the description or the photo everyone sees on your profile")
|
|
59
64
|
.option("--first-name <name>", "your first name")
|
|
60
65
|
.option("--last-name <name>", "your last name")
|
|
@@ -11,8 +11,9 @@ import { messengerContext } from "./context.js";
|
|
|
11
11
|
const SESSIONS_QUERY = unfilteredQuery("account_session", accountSession);
|
|
12
12
|
/** `account sessions …` — where else the account is logged in, not this tool's own `session`. */
|
|
13
13
|
export const accountSessionsCommand = (messenger) => {
|
|
14
|
+
const tool = `${messenger.app.command} personal`;
|
|
14
15
|
const sessions = new Command("sessions").description(`where else this account is logged in — not \`${messenger.app.command} session\`, which is this tool's own login`);
|
|
15
|
-
withQuery(annotate(sessions.command("list"), { effects: REMOTE_READ }), SESSIONS_QUERY)
|
|
16
|
+
withQuery(annotate(sessions.command("list"), { effects: REMOTE_READ, examples: [`${tool} account sessions list`] }), SESSIONS_QUERY)
|
|
16
17
|
.description("every device and app logged in to this account; nothing is ended")
|
|
17
18
|
.action(async function (words) {
|
|
18
19
|
const context = messengerContext(this, messenger);
|
|
@@ -21,7 +22,11 @@ export const accountSessionsCommand = (messenger) => {
|
|
|
21
22
|
renderAnswer(this, context, wholeList("account sessions list", found.map(sessionAnswer)));
|
|
22
23
|
});
|
|
23
24
|
// No MCP tool, at any permission level: it logs the owner out of the phone.
|
|
24
|
-
sessions.addCommand(annotate(new Command("end"), {
|
|
25
|
+
sessions.addCommand(annotate(new Command("end"), {
|
|
26
|
+
effects: REMOTE_WRITE,
|
|
27
|
+
mcp: false,
|
|
28
|
+
examples: [`${tool} account sessions end --others`],
|
|
29
|
+
})
|
|
25
30
|
.description("log out every other device, your phone included; this one stays")
|
|
26
31
|
.option("--others", "every session but this one")
|
|
27
32
|
.action(async function () {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
2
|
+
import { commandAt } from "../steps.js";
|
|
3
|
+
export const conversationExamples = (root, tool) => {
|
|
4
|
+
const examples = {
|
|
5
|
+
"batches next": '--chat "Book club"',
|
|
6
|
+
"batches status": '--chat "Book club"',
|
|
7
|
+
build: '--chat "Book club"',
|
|
8
|
+
"consents list": "",
|
|
9
|
+
"consents revoke": '--chat "Book club"',
|
|
10
|
+
"embed clear": '--chat "Book club"',
|
|
11
|
+
"embed status": '--chat "Book club"',
|
|
12
|
+
"links add": '--batch "$batch"',
|
|
13
|
+
"links clear": '--chat "Book club"',
|
|
14
|
+
list: '"Book club"',
|
|
15
|
+
related: '"Book club" 101',
|
|
16
|
+
show: '"Book club" 101',
|
|
17
|
+
status: "",
|
|
18
|
+
};
|
|
19
|
+
for (const [path, args] of Object.entries(examples)) {
|
|
20
|
+
const command = commandAt(root, path);
|
|
21
|
+
if (command)
|
|
22
|
+
annotate(command, { examples: [`${tool} personal conversations ${path} ${args}`.trimEnd()] });
|
|
23
|
+
}
|
|
24
|
+
commandAt(root, "links add")?.addHelpText("after", "\nSet batch to the id from conversations batches next; supply the JSON answer to that batch on stdin.\n");
|
|
25
|
+
};
|
|
@@ -21,6 +21,7 @@ import { nextStep, rootOf, runnable, stepLine } from "../steps.js";
|
|
|
21
21
|
import { answerOf as askOwner } from "./ask.js";
|
|
22
22
|
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
23
23
|
import { conversationShown, foundConversations, readinessList, refreshedAnswer } from "./conversation-answers.js";
|
|
24
|
+
import { conversationExamples } from "./conversation-examples.js";
|
|
24
25
|
import { messageItems } from "./message-items.js";
|
|
25
26
|
import { syncOptions, syncRequest } from "./search-sync-options.js";
|
|
26
27
|
import { readAll } from "./stdin.js";
|
|
@@ -363,6 +364,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
363
364
|
renderAnswer(this, context, itemOf({ revoked: true }));
|
|
364
365
|
});
|
|
365
366
|
conversations.addCommand(embed);
|
|
367
|
+
conversationExamples(conversations, messenger.app.command);
|
|
366
368
|
return conversations;
|
|
367
369
|
};
|
|
368
370
|
/**
|
|
@@ -52,7 +52,10 @@ export const mcpCommand = (messenger) => {
|
|
|
52
52
|
const { serveOverStdio } = await import("../../mcp/server.js");
|
|
53
53
|
await serveOverStdio(this, context, messenger, {});
|
|
54
54
|
});
|
|
55
|
-
command.addCommand(withFlags(annotate(new Command("config"), {
|
|
55
|
+
command.addCommand(withFlags(annotate(new Command("config"), {
|
|
56
|
+
effects: LOCAL_READ,
|
|
57
|
+
examples: [`${app.command} personal mcp config`],
|
|
58
|
+
}).description("print the mcpServers entry for Claude Desktop, Cursor and others, with full paths; writes nothing")).action(async function () {
|
|
56
59
|
const flags = this.optsWithGlobals();
|
|
57
60
|
const context = messengerContext(this, messenger);
|
|
58
61
|
const { settings, env } = context;
|
|
@@ -67,7 +70,10 @@ export const mcpCommand = (messenger) => {
|
|
|
67
70
|
// Pasted into a file, so a person gets the client's own JSON, indented; a script finds it under `config`.
|
|
68
71
|
renderAnswer(this, context, itemOf({ config: entry.config }, { next: entry.nodeBound ? [nextStep("mcp.config", { args: {} })] : [] }), () => JSON.stringify(entry.config, null, 2));
|
|
69
72
|
}));
|
|
70
|
-
command.addCommand(withFlags(grantsAuthority(annotate(new Command("setup"), {
|
|
73
|
+
command.addCommand(withFlags(grantsAuthority(annotate(new Command("setup"), {
|
|
74
|
+
effects: LOCAL_WRITE,
|
|
75
|
+
examples: [`${app.command} personal mcp setup codex --allow-writes`],
|
|
76
|
+
}))
|
|
71
77
|
.description("add this profile's local MCP server to Codex or Claude Code")
|
|
72
78
|
.argument("<client>", "codex or claude-code")
|
|
73
79
|
.option("--allow-writes", "acknowledge that this profile offers writing tools")).action(async function (client) {
|
|
@@ -98,7 +104,10 @@ export const mcpCommand = (messenger) => {
|
|
|
98
104
|
throw new CliError("validation_error", error instanceof Error ? error.message : "MCP setup failed");
|
|
99
105
|
}
|
|
100
106
|
}));
|
|
101
|
-
command.addCommand(withFlags(annotate(new Command("doctor"), {
|
|
107
|
+
command.addCommand(withFlags(annotate(new Command("doctor"), {
|
|
108
|
+
effects: LOCAL_READ,
|
|
109
|
+
examples: [`${app.command} personal mcp doctor`],
|
|
110
|
+
}).description("check this profile's local MCP handshake and tool list")).action(async function () {
|
|
102
111
|
const context = messengerContext(this, messenger);
|
|
103
112
|
const { settings, env } = context;
|
|
104
113
|
const given = environmentOf(this).mcp ?? {};
|
|
@@ -57,9 +57,10 @@ const downloaded = (body) => "item" in body ? `${body.item.id}: downloaded and w
|
|
|
57
57
|
const modelLine = (item, width) => `${item.default ? "*" : " "} ${item.id.padEnd(width)} ${megabytes(item.size_bytes).padStart(7)} ` +
|
|
58
58
|
(item.downloaded ? "downloaded" : "—".padEnd(10));
|
|
59
59
|
export const modelsCommand = (messenger) => {
|
|
60
|
+
const tool = `${messenger.app.command} personal`;
|
|
60
61
|
const models = annotate(new Command("models"), { mcp: false }).description("models that run on this machine");
|
|
61
62
|
const audio = models.command("audio").description("speech models for transcribing voice messages");
|
|
62
|
-
withQuery(annotate(audio.command("list"), { effects: LOCAL_READ }), MODELS_QUERY)
|
|
63
|
+
withQuery(annotate(audio.command("list"), { effects: LOCAL_READ, examples: [`${tool} models audio list`] }), MODELS_QUERY)
|
|
63
64
|
.description("the speech models, most suitable first, which are downloaded, and which one is the default")
|
|
64
65
|
.action(async function (words) {
|
|
65
66
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -73,7 +74,11 @@ export const modelsCommand = (messenger) => {
|
|
|
73
74
|
context.renderer.note(`kept in ${directory}`);
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
|
-
reachesOtherService(annotate(audio.command("download"), {
|
|
77
|
+
reachesOtherService(annotate(audio.command("download"), {
|
|
78
|
+
effects: FETCH,
|
|
79
|
+
output: { entity: "model" },
|
|
80
|
+
examples: [`${tool} models audio download parakeet-v3`],
|
|
81
|
+
}))
|
|
77
82
|
.argument("<model>", "a model id from `models audio list`")
|
|
78
83
|
.description("download a speech model once, checked against the sha256 this version expects")
|
|
79
84
|
.action(async function (id) {
|
|
@@ -101,7 +106,7 @@ export const modelsCommand = (messenger) => {
|
|
|
101
106
|
}, { unbounded: true });
|
|
102
107
|
});
|
|
103
108
|
const text = models.command("text").description("embedding models for searching conversations by meaning");
|
|
104
|
-
withQuery(annotate(text.command("list"), { effects: LOCAL_READ }), MODELS_QUERY)
|
|
109
|
+
withQuery(annotate(text.command("list"), { effects: LOCAL_READ, examples: [`${tool} models text list`] }), MODELS_QUERY)
|
|
105
110
|
.description("the embedding models, most suitable first, which are downloaded, and which one is the default")
|
|
106
111
|
.action(async function (words) {
|
|
107
112
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -115,7 +120,11 @@ export const modelsCommand = (messenger) => {
|
|
|
115
120
|
context.renderer.note(`kept in ${directory}`);
|
|
116
121
|
});
|
|
117
122
|
});
|
|
118
|
-
reachesOtherService(annotate(text.command("download"), {
|
|
123
|
+
reachesOtherService(annotate(text.command("download"), {
|
|
124
|
+
effects: FETCH,
|
|
125
|
+
output: { entity: "model" },
|
|
126
|
+
examples: [`${tool} models text download ${DEFAULT_TEXT_MODEL}`],
|
|
127
|
+
}))
|
|
119
128
|
.argument("<model>", "a model id from `models text list`")
|
|
120
129
|
.addOption(new Option("--accept-terms", "accept the model's licence terms, for a model that has its own"))
|
|
121
130
|
.description("download an embedding model once, checked against the sha256 this version expects")
|
|
@@ -131,7 +140,7 @@ export const modelsCommand = (messenger) => {
|
|
|
131
140
|
}, { unbounded: true });
|
|
132
141
|
});
|
|
133
142
|
const key = text.command("key").description("API keys for embedding and analysis providers");
|
|
134
|
-
grantsAuthority(annotate(key.command("set"), { effects: LOCAL_WRITE }))
|
|
143
|
+
grantsAuthority(annotate(key.command("set"), { effects: LOCAL_WRITE, examples: [`${tool} models text key set openai`] }))
|
|
135
144
|
.argument("<provider>", "openai, anthropic, or the host of a --base-url server that wants a key")
|
|
136
145
|
.description("store a key, typed at a hidden prompt or piped on stdin — never as an argument")
|
|
137
146
|
.action(async function (provider) {
|
|
@@ -145,7 +154,7 @@ export const modelsCommand = (messenger) => {
|
|
|
145
154
|
renderAnswer(this, context, itemOf({ provider, stored: source }));
|
|
146
155
|
});
|
|
147
156
|
});
|
|
148
|
-
grantsAuthority(annotate(key.command("remove"), { effects: LOCAL_WRITE }))
|
|
157
|
+
grantsAuthority(annotate(key.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} models text key remove openai`] }))
|
|
149
158
|
.argument("<provider>", "openai, anthropic, or a server's host")
|
|
150
159
|
.description("forget a stored key")
|
|
151
160
|
.action(async function (provider) {
|
|
@@ -22,7 +22,10 @@ export const pollsCommand = (messenger) => {
|
|
|
22
22
|
const polls = new Command("polls").description(messenger.pollClose === false
|
|
23
23
|
? "read a poll, vote in it, create one"
|
|
24
24
|
: "read a poll, vote in it, close your own, create one");
|
|
25
|
-
annotate(polls.command("show"), {
|
|
25
|
+
annotate(polls.command("show"), {
|
|
26
|
+
effects: REMOTE_READ,
|
|
27
|
+
examples: [`${messenger.app.command} personal polls show "Book club" 101`],
|
|
28
|
+
})
|
|
26
29
|
.description("a poll and its answer ids, as the message carries it now")
|
|
27
30
|
.argument("<chat>", messenger.chatArgument)
|
|
28
31
|
.argument("<message>", "the id of the message that carries the poll")
|
|
@@ -35,7 +38,10 @@ export const pollsCommand = (messenger) => {
|
|
|
35
38
|
renderAnswer(this, context, itemOf(snakeKeys(poll)));
|
|
36
39
|
});
|
|
37
40
|
if (messenger.pollVoters)
|
|
38
|
-
polls.addCommand(withQuery(annotate(annotate(new Command("voters"), {
|
|
41
|
+
polls.addCommand(withQuery(annotate(annotate(new Command("voters"), {
|
|
42
|
+
effects: REMOTE_READ,
|
|
43
|
+
examples: [`${messenger.app.command} personal polls voters "Book club" 101 answers:0 --limit 10`],
|
|
44
|
+
})
|
|
39
45
|
.description(messenger.pollAnonymous === false
|
|
40
46
|
? "who voted for what, newest first"
|
|
41
47
|
: "who voted for what, newest first; not in an anonymous poll")
|
|
@@ -60,7 +66,10 @@ export const pollsCommand = (messenger) => {
|
|
|
60
66
|
args: { chat, message, ...(words.length > 0 ? { query: words.join(" ") } : {}) },
|
|
61
67
|
}), lines(({ voted_at, person_id, name, answers }) => [voted_at, name ?? person_id, answers.join(",")].join(" ")));
|
|
62
68
|
}));
|
|
63
|
-
annotate(polls.command("vote"), {
|
|
69
|
+
annotate(polls.command("vote"), {
|
|
70
|
+
effects: REMOTE_WRITE,
|
|
71
|
+
examples: [`${messenger.app.command} personal polls vote "Book club" 101 0`],
|
|
72
|
+
})
|
|
64
73
|
.description(messenger.pollAnonymous === false
|
|
65
74
|
? "vote in a poll, or take your vote back; the others see it"
|
|
66
75
|
: "vote in a poll, or take your vote back; the others see it unless the poll is anonymous")
|
|
@@ -85,7 +94,10 @@ export const pollsCommand = (messenger) => {
|
|
|
85
94
|
renderAnswer(this, context, itemOf(snakeKeys(voted)));
|
|
86
95
|
});
|
|
87
96
|
if (messenger.pollClose !== false)
|
|
88
|
-
annotate(polls.command("close"), {
|
|
97
|
+
annotate(polls.command("close"), {
|
|
98
|
+
effects: REMOTE_WRITE,
|
|
99
|
+
examples: [`${messenger.app.command} personal polls close "Book club" 101`],
|
|
100
|
+
})
|
|
89
101
|
.description("close your own poll; nobody can vote after that, and it cannot be reopened")
|
|
90
102
|
.argument("<chat>", messenger.chatArgument)
|
|
91
103
|
.argument("<message>", "the id of your own message that carries the poll")
|
|
@@ -94,7 +106,10 @@ export const pollsCommand = (messenger) => {
|
|
|
94
106
|
const closed = await context.withMessenger((connection) => guardedClose(context.guard, connection, { chat, message: message.trim() }));
|
|
95
107
|
renderAnswer(this, context, itemOf(snakeKeys(closed)));
|
|
96
108
|
});
|
|
97
|
-
const create = annotate(polls.command("create"), {
|
|
109
|
+
const create = annotate(polls.command("create"), {
|
|
110
|
+
effects: REMOTE_WRITE,
|
|
111
|
+
examples: [`${messenger.app.command} personal polls create "Book club" "Which day?" Monday Tuesday`],
|
|
112
|
+
})
|
|
98
113
|
.description(messenger.pollAnonymous === false
|
|
99
114
|
? "send a poll to a chat, as a message of its own; everyone sees who voted for what"
|
|
100
115
|
: "send a poll to a chat, as a message of its own; public unless --anonymous")
|
|
@@ -82,7 +82,11 @@ export const callsCommand = (messenger) => {
|
|
|
82
82
|
};
|
|
83
83
|
/** `chats delete` and `chats clear` — for this account only; at the default level they ask first. */
|
|
84
84
|
export const chatDeletionCommands = (messenger) => [
|
|
85
|
-
annotate(new Command("delete"), {
|
|
85
|
+
annotate(new Command("delete"), {
|
|
86
|
+
effects: REMOTE_WRITE,
|
|
87
|
+
mcp: false,
|
|
88
|
+
examples: [`${messenger.app.command} personal chats delete "Book club"`],
|
|
89
|
+
})
|
|
86
90
|
.description("delete a chat from this account; the others in it keep it and its messages")
|
|
87
91
|
.argument("<chat>", messenger.chatArgument)
|
|
88
92
|
.addOption(ownerOnlyOption("--allow-dangerous", "go ahead without the question an ask level puts before a deletion"))
|
|
@@ -90,7 +94,11 @@ export const chatDeletionCommands = (messenger) => [
|
|
|
90
94
|
const context = messengerContext(this, messenger);
|
|
91
95
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.admin.deleteChat(chat)))));
|
|
92
96
|
}),
|
|
93
|
-
annotate(new Command("clear"), {
|
|
97
|
+
annotate(new Command("clear"), {
|
|
98
|
+
effects: REMOTE_WRITE,
|
|
99
|
+
mcp: false,
|
|
100
|
+
examples: [`${messenger.app.command} personal chats clear "Book club"`],
|
|
101
|
+
})
|
|
94
102
|
.description("delete every message in a chat for this account; the others in it keep theirs")
|
|
95
103
|
.argument("<chat>", messenger.chatArgument)
|
|
96
104
|
.addOption(ownerOnlyOption("--allow-dangerous", "go ahead without the question an ask level puts before a deletion"))
|
|
@@ -6,6 +6,7 @@ import { searchNotesOf } from "../../services/notes-search.js";
|
|
|
6
6
|
import { LOCAL_READ, REMOTE_READ } from "../effects.js";
|
|
7
7
|
import { renderAnswer } from "../envelope.js";
|
|
8
8
|
import { positiveCount } from "../paging.js";
|
|
9
|
+
import { commandAt } from "../steps.js";
|
|
9
10
|
import { messengerContext } from "./context.js";
|
|
10
11
|
import { conversationsSearchCommand } from "./conversations-command.js";
|
|
11
12
|
import { messagesSearchCommand } from "./messages-search-command.js";
|
|
@@ -157,5 +158,16 @@ export const searchCommand = (messenger, { topics = false } = {}) => {
|
|
|
157
158
|
.addCommand(conversationsSearchCommand(messenger));
|
|
158
159
|
if (topics)
|
|
159
160
|
search.addCommand(topicsSearchCommand(messenger));
|
|
161
|
+
for (const [path, args] of Object.entries({
|
|
162
|
+
all: "draft",
|
|
163
|
+
conversations: '"Reviewing the project draft"',
|
|
164
|
+
messages: "draft --limit 10",
|
|
165
|
+
notes: "draft --limit 10",
|
|
166
|
+
topics: '"Book club" draft',
|
|
167
|
+
})) {
|
|
168
|
+
const command = commandAt(search, path);
|
|
169
|
+
if (command)
|
|
170
|
+
annotate(command, { examples: [`${messenger.app.command} personal search ${path} ${args}`] });
|
|
171
|
+
}
|
|
160
172
|
return search;
|
|
161
173
|
};
|
|
@@ -181,21 +181,24 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
181
181
|
return ["--idle", idle];
|
|
182
182
|
};
|
|
183
183
|
const withIdle = (sub) => options.idle ? sub.option("--idle <duration>", "stop after this long with nobody using it — 15m, 1h") : sub;
|
|
184
|
-
withIdle(destroysNothing(annotate(command.command("start"), { effects: LOCAL_WRITE })).description("start serve in the background — through the unit if one is installed — and answer once it connects")).action(async function () {
|
|
184
|
+
withIdle(destroysNothing(annotate(command.command("start"), { effects: LOCAL_WRITE, examples: [`${app.command} personal server start`] })).description("start serve in the background — through the unit if one is installed — and answer once it connects")).action(async function () {
|
|
185
185
|
await start(prepare(this), idleArgs(this));
|
|
186
186
|
});
|
|
187
|
-
destroysNothing(annotate(command.command("stop"), { effects: LOCAL_WRITE }))
|
|
187
|
+
destroysNothing(annotate(command.command("stop"), { effects: LOCAL_WRITE, examples: [`${app.command} personal server stop`] }))
|
|
188
188
|
.description("stop this profile's serve — through the unit if it runs under one")
|
|
189
189
|
.action(async function () {
|
|
190
190
|
await stop(prepare(this));
|
|
191
191
|
});
|
|
192
|
-
withIdle(destroysNothing(annotate(command.command("restart"), {
|
|
192
|
+
withIdle(destroysNothing(annotate(command.command("restart"), {
|
|
193
|
+
effects: LOCAL_WRITE,
|
|
194
|
+
examples: [`${app.command} personal server restart`],
|
|
195
|
+
})).description("stop it and start it again")).action(async function () {
|
|
193
196
|
const args = idleArgs(this);
|
|
194
197
|
const prepared = prepare(this);
|
|
195
198
|
await stop({ ...prepared, say: () => { } });
|
|
196
199
|
await start(prepared, args);
|
|
197
200
|
});
|
|
198
|
-
annotate(command.command("status"), { effects: LOCAL_READ })
|
|
201
|
+
annotate(command.command("status"), { effects: LOCAL_READ, examples: [`${app.command} personal server status`] })
|
|
199
202
|
.description("whether serve runs for this profile, since when, who started it, and the unit if there is one")
|
|
200
203
|
.action(async function () {
|
|
201
204
|
const prepared = prepare(this);
|
|
@@ -261,7 +264,10 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
261
264
|
...flood.deadlines.map((one) => `Asked to wait before ${one.operation}${one.chatId ? ` in chat ${one.chatId}` : ""} until ${clock(one.until)}.`),
|
|
262
265
|
], outdated ? [nextStep("server.restart", { args: {} })] : []);
|
|
263
266
|
});
|
|
264
|
-
annotate(command.command("logs"), {
|
|
267
|
+
annotate(command.command("logs"), {
|
|
268
|
+
effects: LOCAL_READ,
|
|
269
|
+
examples: [`${app.command} personal server logs --limit 20`],
|
|
270
|
+
})
|
|
265
271
|
.description("serve's latest log lines — from the journal under systemd, else its log file")
|
|
266
272
|
.option("-n, --limit <n>", "how many lines", (value) => wholeNumber(value), 50)
|
|
267
273
|
.action(async function () {
|
|
@@ -272,7 +278,10 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
272
278
|
context.renderer.note(`no log yet for profile ${unit.profile}`);
|
|
273
279
|
renderAnswer(this, context, wholeList("server logs", text.split("\n").filter(Boolean)), () => text ? readableLog(text.replace(/\n$/, "")) : "");
|
|
274
280
|
});
|
|
275
|
-
grantsAuthority(annotate(command.command("install"), {
|
|
281
|
+
grantsAuthority(annotate(command.command("install"), {
|
|
282
|
+
effects: LOCAL_WRITE,
|
|
283
|
+
examples: [`${app.command} personal server install`],
|
|
284
|
+
}))
|
|
276
285
|
.description("write a systemd user unit or a launchd agent for this profile; starts nothing")
|
|
277
286
|
.action(function () {
|
|
278
287
|
const { context, platform, unit, say } = prepare(this);
|
|
@@ -287,7 +296,10 @@ export const serverCommand = (messenger, options = {}) => {
|
|
|
287
296
|
`A daily follow-up review is off. To turn it on: ${app.command} ${unit.profile} schedule agent follow-ups claude "${followUpsPrompt(app.command)}" --at 08:30`,
|
|
288
297
|
]);
|
|
289
298
|
});
|
|
290
|
-
grantsAuthority(annotate(command.command("uninstall"), {
|
|
299
|
+
grantsAuthority(annotate(command.command("uninstall"), {
|
|
300
|
+
effects: LOCAL_WRITE,
|
|
301
|
+
examples: [`${app.command} personal server uninstall`],
|
|
302
|
+
}))
|
|
291
303
|
.description("remove this profile's unit; stop it first")
|
|
292
304
|
.action(async function () {
|
|
293
305
|
const { context, platform, unit, say } = prepare(this);
|
|
@@ -130,7 +130,10 @@ export const tasksCommand = (messenger) => {
|
|
|
130
130
|
? "agent"
|
|
131
131
|
: "owner";
|
|
132
132
|
const tasks = new Command("tasks").description("what waits on you — unanswered questions, mentions, requests, promises — kept in the local store; review and serve add them");
|
|
133
|
-
withQuery(annotate(tasks.command("list"), {
|
|
133
|
+
withQuery(annotate(tasks.command("list"), {
|
|
134
|
+
effects: LOCAL_READ,
|
|
135
|
+
examples: [`${tool} personal tasks list --limit 10`],
|
|
136
|
+
}).description("tasks, oldest first, with their message or note source"), TASK_QUERY)
|
|
134
137
|
.option("--limit <n>", "how many", positiveCount("--limit"))
|
|
135
138
|
.action(async function (words) {
|
|
136
139
|
const context = messengerContext(this, messenger);
|
|
@@ -146,7 +149,10 @@ export const tasksCommand = (messenger) => {
|
|
|
146
149
|
if (found.items.length === 0 && context.format === "pretty")
|
|
147
150
|
context.renderer.note("no tasks — review, inbox and messages sync add them as questions arrive");
|
|
148
151
|
});
|
|
149
|
-
planOptions(destroysNothing(annotate(tasks.command("add"), {
|
|
152
|
+
planOptions(destroysNothing(annotate(tasks.command("add"), {
|
|
153
|
+
effects: LOCAL_WRITE,
|
|
154
|
+
examples: [`${tool} personal tasks add note:101 --type promise --category documents --title "Review draft"`],
|
|
155
|
+
}))
|
|
150
156
|
.description("add a task for a stored message or note — a promise, a request")
|
|
151
157
|
.argument("<message>", "a message locator, msg:<provider>/<account>/<chat>/<message>, or note:<id>")
|
|
152
158
|
.requiredOption("--type <type>", "the task's type: question, request, mention or promise", taskTypeOf)
|
|
@@ -176,7 +182,10 @@ export const tasksCommand = (messenger) => {
|
|
|
176
182
|
else if (followUpOf && context.format === "pretty")
|
|
177
183
|
context.renderer.note(`${task.key ?? task.id} follows up ${followUpOf.key} (${followUpOf.status}), which stays closed: linked follow-up-of`);
|
|
178
184
|
});
|
|
179
|
-
planOptions(destroysNothing(annotate(tasks.command("plan"), {
|
|
185
|
+
planOptions(destroysNothing(annotate(tasks.command("plan"), {
|
|
186
|
+
effects: LOCAL_WRITE,
|
|
187
|
+
examples: [`${tool} personal tasks plan DOC-12 --title "Review draft"`],
|
|
188
|
+
}))
|
|
180
189
|
.summary("set a task's title, description, due date, next check, category and assignee")
|
|
181
190
|
.description("set a task's title, description, due date, next check, category and assignee; nothing reaches the messenger")
|
|
182
191
|
.argument("<task>", "the task's id or key, as tasks list shows it")
|
|
@@ -189,7 +198,10 @@ export const tasksCommand = (messenger) => {
|
|
|
189
198
|
const planned = await context.withServices((services) => services.tasks.plan(id, plan, writerOf(this)));
|
|
190
199
|
renderAnswer(this, context, itemOf(snakeKeys(planned)), () => `${card(planned)}\n`);
|
|
191
200
|
});
|
|
192
|
-
destroysNothing(annotate(tasks.command("link"), {
|
|
201
|
+
destroysNothing(annotate(tasks.command("link"), {
|
|
202
|
+
effects: LOCAL_WRITE,
|
|
203
|
+
examples: [`${tool} personal tasks link DOC-12 https://example.com/draft`],
|
|
204
|
+
}))
|
|
193
205
|
.description("link a task to messages, chats, people or URLs — or to another task: task:<id>")
|
|
194
206
|
.argument("<task>", "the task's id or key, as tasks list shows it")
|
|
195
207
|
.argument("[targets...]", "msg: locators, person:<id>, document:<id>, note:<id>, a URL; or task:<id>, which links two project tasks")
|
|
@@ -209,7 +221,7 @@ export const tasksCommand = (messenger) => {
|
|
|
209
221
|
if (!("relations" in linked) && linked.review && context.format === "pretty")
|
|
210
222
|
context.renderer.note(`${linked.review} possible duplicate pair(s) went to tasks duplicates`);
|
|
211
223
|
});
|
|
212
|
-
annotate(tasks.command("show"), { effects: LOCAL_READ })
|
|
224
|
+
annotate(tasks.command("show"), { effects: LOCAL_READ, examples: [`${tool} personal tasks show DOC-12`] })
|
|
213
225
|
.description("one task: its plan and everything linked to it")
|
|
214
226
|
.argument("<task>", "the task's id or key, as tasks list shows it")
|
|
215
227
|
.action(async function (id) {
|
|
@@ -307,7 +319,7 @@ export const tasksCommand = (messenger) => {
|
|
|
307
319
|
const unmerged = await context.withServices((services) => services.tasks.unmerge(dup, writerOf(this)));
|
|
308
320
|
renderAnswer(this, context, itemOf(snakeKeys(unmerged)), () => `${card(unmerged.task)}\n`);
|
|
309
321
|
});
|
|
310
|
-
withQuery(annotate(tasks.command("agenda"), { effects: LOCAL_READ })
|
|
322
|
+
withQuery(annotate(tasks.command("agenda"), { effects: LOCAL_READ, examples: [`${tool} personal tasks agenda`] })
|
|
311
323
|
.summary("what needs a look today: tasks due, chats you owe or wait on, what you are owed")
|
|
312
324
|
.description("what needs a look today, from the local store and without message text: tasks overdue, due or to check, " +
|
|
313
325
|
"chats where you owe an answer, chats where you wait for one, open tasks someone else does"), AGENDA_QUERY)
|
package/dist/cli/runs/command.js
CHANGED
|
@@ -18,7 +18,11 @@ export const runsCommand = (app) => {
|
|
|
18
18
|
throw new CliError("not_found", `no recorded run "${id}" — \`${lineOf(from, "runs list")}\` shows them`);
|
|
19
19
|
return found;
|
|
20
20
|
};
|
|
21
|
-
annotate(command.command("list"), {
|
|
21
|
+
annotate(command.command("list"), {
|
|
22
|
+
effects: LOCAL_READ,
|
|
23
|
+
mcp: false,
|
|
24
|
+
examples: [`${app.command} runs list --limit 10`],
|
|
25
|
+
})
|
|
22
26
|
.description("recorded runs, newest first")
|
|
23
27
|
.option("--limit <n>", "how many to show", positiveCount("--limit"), 20)
|
|
24
28
|
.option("--page <n>", "which page, starting at 1", positiveCount("--page"), 1)
|
|
@@ -32,7 +36,7 @@ export const runsCommand = (app) => {
|
|
|
32
36
|
if (runs.length === 0)
|
|
33
37
|
renderer.note("nothing recorded — a run is kept with `--record`, or when it fails");
|
|
34
38
|
});
|
|
35
|
-
annotate(command.command("show"), { effects: LOCAL_READ, mcp: false })
|
|
39
|
+
annotate(command.command("show"), { effects: LOCAL_READ, mcp: false, examples: [`${app.command} runs show run-101`] })
|
|
36
40
|
.argument("<run-id>", `an id from \`${app.command} runs list\``)
|
|
37
41
|
.description("one run: what it was, how it ended, and every event it logged")
|
|
38
42
|
.option("--problems", "only failed responses, warnings and crashes")
|
|
@@ -53,7 +57,10 @@ export const runsCommand = (app) => {
|
|
|
53
57
|
if (shown.length < events.length)
|
|
54
58
|
renderer.note(`${events.length - shown.length} other events — \`${lineOf(this, `runs show ${id}`)}\` shows them all`);
|
|
55
59
|
});
|
|
56
|
-
annotate(command.command("search"), {
|
|
60
|
+
annotate(command.command("search"), {
|
|
61
|
+
effects: LOCAL_READ,
|
|
62
|
+
examples: [`${app.command} runs search timeout --limit 10`],
|
|
63
|
+
})
|
|
57
64
|
.description("search recorded diagnostic metadata and events; no message contents")
|
|
58
65
|
.argument("[query]", "case-insensitive literal diagnostic text")
|
|
59
66
|
.addOption(new Option("--status <status>", "run outcome").choices(["success", "failed", "partial", "running"]))
|
|
@@ -79,7 +86,7 @@ export const runsCommand = (app) => {
|
|
|
79
86
|
since_time: options.sinceTime,
|
|
80
87
|
}));
|
|
81
88
|
});
|
|
82
|
-
annotate(command.command("path"), { effects: NO_EFFECTS, mcp: false })
|
|
89
|
+
annotate(command.command("path"), { effects: NO_EFFECTS, mcp: false, examples: [`${app.command} runs path run-101`] })
|
|
83
90
|
.argument("<run-id>", `an id from \`${app.command} runs list\``)
|
|
84
91
|
.description("the directory holding one run")
|
|
85
92
|
.action(function (id) {
|
|
@@ -13,8 +13,13 @@ export const skillCommand = (app, skill) => {
|
|
|
13
13
|
}, SHARED_SKILLS);
|
|
14
14
|
// An installed skill is instructions every later agent follows.
|
|
15
15
|
const install = command.commands.find((sub) => sub.name() === "install");
|
|
16
|
-
if (install)
|
|
16
|
+
if (install) {
|
|
17
17
|
grantsAuthority(install);
|
|
18
|
+
annotate(install, { examples: [`${app.command} skill install --for agents`] });
|
|
19
|
+
}
|
|
20
|
+
const show = command.commands.find((sub) => sub.name() === "show");
|
|
21
|
+
if (show)
|
|
22
|
+
annotate(show, { examples: [`${app.command} skill show`, `${app.command} skill show follow-ups`] });
|
|
18
23
|
return annotate(command, { mcp: false });
|
|
19
24
|
};
|
|
20
25
|
/** Skills this package ships for every CLI on it, printed by `skill show <name>`: `skills/` beside `dist/` and `src/`. */
|
package/dist/entities/index.d.ts
CHANGED
|
@@ -2857,7 +2857,7 @@ export declare const ENTITIES: {
|
|
|
2857
2857
|
readonly saved_search: v.StrictObjectSchema<{
|
|
2858
2858
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
2859
2859
|
readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
2860
|
-
readonly command: v.PicklistSchema<["search", "stats", "message-top", "author-top", "admin-statistics"], undefined>;
|
|
2860
|
+
readonly command: v.PicklistSchema<["search", "stats", "message-top", "author-top", "admin-statistics", "meetings-search"], undefined>;
|
|
2861
2861
|
readonly params: v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>;
|
|
2862
2862
|
readonly language: v.StringSchema<undefined>;
|
|
2863
2863
|
readonly version: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
@@ -4,7 +4,7 @@ export declare const savedSearch: v.StrictObjectSchema<{
|
|
|
4
4
|
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
5
5
|
/** `null` for a run in the history. */
|
|
6
6
|
readonly name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
7
|
-
readonly command: v.PicklistSchema<["search", "stats", "message-top", "author-top", "admin-statistics"], undefined>;
|
|
7
|
+
readonly command: v.PicklistSchema<["search", "stats", "message-top", "author-top", "admin-statistics", "meetings-search"], undefined>;
|
|
8
8
|
/** The query and the options it runs with, as the command took them. */
|
|
9
9
|
readonly params: v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>;
|
|
10
10
|
readonly language: v.StringSchema<undefined>;
|
|
@@ -5,7 +5,7 @@ export const savedSearch = v.strictObject({
|
|
|
5
5
|
id,
|
|
6
6
|
/** `null` for a run in the history. */
|
|
7
7
|
name: v.nullable(text),
|
|
8
|
-
command: v.picklist(["search", "stats", "message-top", "author-top", "admin-statistics"]),
|
|
8
|
+
command: v.picklist(["search", "stats", "message-top", "author-top", "admin-statistics", "meetings-search"]),
|
|
9
9
|
/** The query and the options it runs with, as the command took them. */
|
|
10
10
|
params: v.record(v.string(), v.unknown()),
|
|
11
11
|
language: text,
|
package/dist/errors/reasons.js
CHANGED
|
@@ -51,6 +51,7 @@ const OTHERS = [
|
|
|
51
51
|
validation("ranking.invalid", "the ranking is not valid"),
|
|
52
52
|
validation("selection.changed", "the stored selection changed meanwhile"),
|
|
53
53
|
validation("selection.invalid", "the selection is not valid"),
|
|
54
|
+
validation("search.kind", "the saved search belongs to another kind of search: messages or meetings"),
|
|
54
55
|
["selection.out-of-scope", "permission_error", "the selection reaches outside what this profile may read"],
|
|
55
56
|
validation("stemmer.invalid", "the stemmer is not valid for that script"),
|
|
56
57
|
validation("stemmer.newer", "the stems were built by a newer tool"),
|