@wirecat/cli-core 0.396.0 → 0.398.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 +74 -1
- package/README.md +13 -9
- package/dist/background/daily-archive-timer.d.ts +40 -0
- package/dist/background/daily-archive-timer.js +181 -0
- package/dist/background/index.d.ts +2 -0
- package/dist/background/index.js +1 -0
- package/dist/cli/bot/admins.js +2 -2
- package/dist/cli/bot/command.js +2 -2
- package/dist/cli/bot/setup.js +3 -3
- package/dist/cli/commands-command.js +1 -1
- package/dist/cli/config-command.js +1 -1
- package/dist/cli/guide.js +0 -1
- package/dist/cli/help/command-help.d.ts +9 -2
- package/dist/cli/help/command-help.js +22 -4
- package/dist/cli/help/first-screen.js +3 -3
- package/dist/cli/messenger/account-command.js +3 -1
- package/dist/cli/messenger/account-sessions-command.js +3 -1
- package/dist/cli/messenger/admin-commands.js +1 -0
- package/dist/cli/messenger/admin-contacts-command.js +9 -3
- package/dist/cli/messenger/archive-commands.js +5 -3
- package/dist/cli/messenger/attachments-command.js +17 -7
- package/dist/cli/messenger/backfill-command.js +7 -3
- package/dist/cli/messenger/catch-up-answers.d.ts +2 -0
- package/dist/cli/messenger/catch-up-answers.js +5 -1
- package/dist/cli/messenger/chats-command.js +3 -1
- package/dist/cli/messenger/chats-members-command.js +4 -0
- package/dist/cli/messenger/chats-stats-command.js +1 -0
- package/dist/cli/messenger/chats-tracking-command.js +4 -1
- package/dist/cli/messenger/complete-command.js +6 -2
- package/dist/cli/messenger/contacts-command.js +11 -4
- package/dist/cli/messenger/context.d.ts +5 -0
- package/dist/cli/messenger/conversations-command.js +18 -1
- package/dist/cli/messenger/counters-command.js +1 -0
- package/dist/cli/messenger/doctor-command.js +2 -2
- package/dist/cli/messenger/download-command.js +1 -0
- package/dist/cli/messenger/flood-command.js +2 -1
- package/dist/cli/messenger/guard-commands.js +5 -4
- package/dist/cli/messenger/inbox.js +2 -0
- package/dist/cli/messenger/lacks.d.ts +7 -0
- package/dist/cli/messenger/lacks.js +18 -1
- package/dist/cli/messenger/mcp-command.js +6 -2
- package/dist/cli/messenger/media-archive-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-command.js +72 -0
- package/dist/cli/messenger/media-archive-move-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-move-command.js +39 -0
- package/dist/cli/messenger/media-archive-timer-command.d.ts +3 -0
- package/dist/cli/messenger/media-archive-timer-command.js +70 -0
- package/dist/cli/messenger/messages-command.js +2 -1
- package/dist/cli/messenger/messages-comments-command.js +1 -0
- package/dist/cli/messenger/messages-delete-command.js +1 -1
- package/dist/cli/messenger/messages-search-command.js +1 -0
- package/dist/cli/messenger/messages-stats-command.js +1 -0
- package/dist/cli/messenger/models-command.js +8 -2
- package/dist/cli/messenger/private-people-command.js +12 -10
- package/dist/cli/messenger/rankings-command.js +2 -0
- package/dist/cli/messenger/reactions-command.js +8 -2
- package/dist/cli/messenger/replies-command.js +1 -1
- package/dist/cli/messenger/retention-command.js +1 -0
- package/dist/cli/messenger/review.js +8 -2
- package/dist/cli/messenger/schedule-command.js +3 -3
- package/dist/cli/messenger/search-command.js +7 -3
- package/dist/cli/messenger/searches-command.js +24 -7
- package/dist/cli/messenger/serve-command.js +5 -1
- package/dist/cli/messenger/serve-relay.d.ts +1 -0
- package/dist/cli/messenger/server-command.js +17 -3
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/messenger/store-maintenance-command.js +10 -3
- package/dist/cli/messenger/tags-command.js +6 -2
- package/dist/cli/messenger/tasks-command.js +10 -7
- package/dist/cli/messenger/transcribe-command.js +1 -0
- package/dist/cli/messenger/watch-command.js +6 -1
- package/dist/cli/runs/command.js +2 -3
- package/dist/cli/skill-command.js +1 -1
- package/dist/domain/models.d.ts +2 -0
- package/dist/entities/contact-read.d.ts +119 -0
- package/dist/entities/contact-read.js +50 -0
- package/dist/entities/index.d.ts +120 -1
- package/dist/entities/index.js +5 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/parity/catalogue.js +1 -1
- package/dist/parity/manifest.d.ts +3 -1
- package/dist/parity/manifest.js +2 -0
- package/dist/parity/wording.js +2 -0
- package/dist/sends/permissions.js +7 -0
- package/dist/services/deps.d.ts +1 -1
- package/dist/services/deps.js +1 -1
- package/dist/services/inbox.js +5 -5
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +2 -0
- package/dist/services/media-archive-files.d.ts +10 -0
- package/dist/services/media-archive-files.js +110 -0
- package/dist/services/media-archive-locks.d.ts +6 -0
- package/dist/services/media-archive-locks.js +71 -0
- package/dist/services/media-archive-manifest.d.ts +29 -0
- package/dist/services/media-archive-manifest.js +116 -0
- package/dist/services/media-archive-move.d.ts +21 -0
- package/dist/services/media-archive-move.js +163 -0
- package/dist/services/media-archive.d.ts +34 -0
- package/dist/services/media-archive.js +165 -0
- package/dist/services/search-kind.js +1 -1
- package/package.json +2 -2
- package/parity.json +116 -193
|
@@ -58,6 +58,7 @@ export const readProfilePhoto = (messenger, path, env) => readUpload("photo", pa
|
|
|
58
58
|
const updateCommand = (messenger) => {
|
|
59
59
|
const keys = messenger.groupSettings ?? GROUP_SETTINGS;
|
|
60
60
|
const update = annotate(new Command("update"), { effects: REMOTE_WRITE })
|
|
61
|
+
.summary("change a group or channel's profile and settings")
|
|
61
62
|
.description("rename a group or channel, change its description, or turn one of its settings on or off")
|
|
62
63
|
.argument("<chat>", messenger.chatArgument)
|
|
63
64
|
.option("--title <title>", "the new name")
|
|
@@ -10,11 +10,16 @@ const PERSON = "person id — `contacts lookup` finds one — or part of a known
|
|
|
10
10
|
/** The address book's writes, in max-cli's words. */
|
|
11
11
|
export const contactWriteCommands = (messenger) => {
|
|
12
12
|
const simple = [
|
|
13
|
-
[
|
|
13
|
+
[
|
|
14
|
+
"add",
|
|
15
|
+
"add a person to your contacts — `contacts list` still shows only people you have a dialog with",
|
|
16
|
+
"add a person to your contacts",
|
|
17
|
+
],
|
|
14
18
|
["remove", "remove a person from your contacts; the chat stays, a name you gave them may not"],
|
|
15
19
|
["block", "stop a person from writing to you — they need not be a contact"],
|
|
16
20
|
["unblock", "let a blocked person write to you again"],
|
|
17
|
-
].map(([name, description]) => annotate(new Command(name), { effects: REMOTE_WRITE })
|
|
21
|
+
].map(([name, description, summary]) => annotate(new Command(name), { effects: REMOTE_WRITE })
|
|
22
|
+
.summary(summary ?? description)
|
|
18
23
|
.description(description)
|
|
19
24
|
.argument("<person>", PERSON)
|
|
20
25
|
.action(async function (person) {
|
|
@@ -23,10 +28,11 @@ export const contactWriteCommands = (messenger) => {
|
|
|
23
28
|
renderAnswer(this, context, itemOf(snakeKeys(done)));
|
|
24
29
|
}));
|
|
25
30
|
const rename = annotate(new Command("rename"), { effects: REMOTE_WRITE })
|
|
31
|
+
.summary("change a contact's name in the messenger address book")
|
|
26
32
|
.description("rename the contact in the messenger address book; use contacts alias for a private local name")
|
|
27
33
|
.argument("<person>", PERSON)
|
|
28
34
|
.argument("<first-name>", "the name you want to see for them")
|
|
29
|
-
.argument("[last-name]")
|
|
35
|
+
.argument("[last-name]", "the last name you want to see for them")
|
|
30
36
|
.action(async function (person, firstName, lastName) {
|
|
31
37
|
const context = messengerContext(this, messenger);
|
|
32
38
|
const renamed = await context.withServices((services) => services.people.rename(person, firstName, lastName));
|
|
@@ -64,7 +64,8 @@ export const storeCommand = (messenger) => {
|
|
|
64
64
|
* What the local store holds, per chat — read from the store alone. Whether a fetch reached a
|
|
65
65
|
* chat's very first message is not recorded, so it is not claimed: the stretches held are shown.
|
|
66
66
|
*/
|
|
67
|
-
const statusCommand = (messenger) => annotate(new Command("status"), { effects: LOCAL_READ
|
|
67
|
+
const statusCommand = (messenger) => annotate(new Command("status"), { effects: LOCAL_READ })
|
|
68
|
+
.summary("what the local archive holds and what is missing")
|
|
68
69
|
.description("per chat: messages stored, the oldest and newest, and the stretches held completely")
|
|
69
70
|
.argument("[chat]", messenger.chatArgument)
|
|
70
71
|
.action(async function (chat) {
|
|
@@ -131,9 +132,10 @@ const clearCommand = (messenger) => annotate(new Command("clear"), { effects: LO
|
|
|
131
132
|
* `--format markdown` for a transcript a person reads. With `--output-dir`, any number of chats into a
|
|
132
133
|
* folder, and each later run into the same folder adds only what changed.
|
|
133
134
|
*/
|
|
134
|
-
const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { effects: LOCAL_WRITE
|
|
135
|
+
const exportCommand = (messenger) => destroysNothing(annotate(new Command("export"), { effects: LOCAL_WRITE }))
|
|
136
|
+
.summary("export stored messages to files")
|
|
135
137
|
.description("a chat's stored messages, oldest first: --jsonl one message per line, --json the list envelope of messages; never asks the messenger")
|
|
136
|
-
.argument("[
|
|
138
|
+
.argument("[chat...]", `${messenger.chatArgument}; several with --output-dir`)
|
|
137
139
|
.option("--format <format>", "jsonl: one message per line, the default with --output; markdown: a transcript with a heading per day, replies and forwards quoted")
|
|
138
140
|
.option("--since-time <time>", "only from this ISO 8601 time, or 30m / 2h / 1d ago, on")
|
|
139
141
|
.option("--output <file>", "write JSON lines, or the transcript, to this new file, readable only by you")
|
|
@@ -15,6 +15,7 @@ import { withPaging } from "../paging.js";
|
|
|
15
15
|
import { readsStdin } from "../stdin-field.js";
|
|
16
16
|
import { nextStep } from "../steps.js";
|
|
17
17
|
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
18
|
+
import { addMediaArchiveCommands } from "./media-archive-command.js";
|
|
18
19
|
import { stopOnSignal } from "./patience.js";
|
|
19
20
|
import { readAll } from "./stdin.js";
|
|
20
21
|
const count = (value) => {
|
|
@@ -38,6 +39,7 @@ const extractCommand = (messenger) => annotate(new Command("extract"), {
|
|
|
38
39
|
effects: FETCH,
|
|
39
40
|
examples: [`${messenger.app.command} personal attachments extract --chat "Book club" --limit 10`],
|
|
40
41
|
})
|
|
42
|
+
.summary("extract text from retained message files")
|
|
41
43
|
.description("read the text of downloaded files — text, PDF/DOCX text layers, ODT/ODS/XLSX/PPTX/EPUB — into the local store, where search finds them")
|
|
42
44
|
.option("--chat <chat>", `only this chat's files; ${messenger.chatArgument}`)
|
|
43
45
|
.option("--from-dir <dir>", "match files in this nonrecursive directory; needs --chat")
|
|
@@ -127,7 +129,9 @@ const row = ({ message, position, name, local_path, extraction }) => `${message}
|
|
|
127
129
|
const listCommand = (messenger) => withQuery(withPaging(annotate(new Command("list"), {
|
|
128
130
|
effects: REMOTE_READ,
|
|
129
131
|
examples: [`${messenger.app.command} personal attachments list --limit 10`],
|
|
130
|
-
})
|
|
132
|
+
})
|
|
133
|
+
.summary("stored message files, saved paths and text status")
|
|
134
|
+
.description("files of stored messages, newest first: where each was saved and whether its text is held — never the text")), ATTACHMENT_QUERY).action(async function (words) {
|
|
131
135
|
const context = messengerContext(this, messenger);
|
|
132
136
|
const query = listQuery(typedQuery(words), ATTACHMENT_QUERY, "attachments list");
|
|
133
137
|
const found = await context.withServices((services) => services.attachments.list({ ...pageOf(context.settings), ...(query ? { query } : {}) }));
|
|
@@ -175,6 +179,7 @@ const textCommand = (messenger) => {
|
|
|
175
179
|
name: "text",
|
|
176
180
|
description: "the text of the file",
|
|
177
181
|
})
|
|
182
|
+
.summary("save the text read from one message file")
|
|
178
183
|
.description("keep the text an agent read from a file — a scan, a photo — so search finds it; nothing is sent")
|
|
179
184
|
.argument("<chat>", `${messenger.chatArgument}; or a msg: locator, with no message id after it`)
|
|
180
185
|
.argument("[message]", "the message id")
|
|
@@ -196,9 +201,14 @@ const textCommand = (messenger) => {
|
|
|
196
201
|
return text;
|
|
197
202
|
};
|
|
198
203
|
/** Files of stored messages: what is in them, for `content:` in a search. Nothing here is sent. */
|
|
199
|
-
export const attachmentsCommand = (messenger) =>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
export const attachmentsCommand = (messenger) => {
|
|
205
|
+
const attachments = new Command("attachments")
|
|
206
|
+
.summary("stored message files and their retained text")
|
|
207
|
+
.description("the files of stored messages: their text in the local store, where search finds it")
|
|
208
|
+
.addCommand(extractCommand(messenger))
|
|
209
|
+
.addCommand(listCommand(messenger))
|
|
210
|
+
.addCommand(showCommand(messenger))
|
|
211
|
+
.addCommand(textCommand(messenger));
|
|
212
|
+
addMediaArchiveCommands(attachments, messenger);
|
|
213
|
+
return attachments;
|
|
214
|
+
};
|
|
@@ -39,6 +39,7 @@ export const syncCommand = (messenger) => {
|
|
|
39
39
|
effects: FETCH,
|
|
40
40
|
cooperative: true,
|
|
41
41
|
})
|
|
42
|
+
.summary("fetch and save a chat's message history")
|
|
42
43
|
.description("bring a chat's history in the local store up to date, newest first; run it again to continue; --all syncs every chat")
|
|
43
44
|
.argument("[chat]", messenger.chatArgument)
|
|
44
45
|
.option("--all", `every chat, most recently active first — what search needs; the last ${ALL_SINCE} unless --since-time or --last`)
|
|
@@ -224,6 +225,7 @@ export const jobsCommand = (messenger) => {
|
|
|
224
225
|
context.renderer.note(`no ${query ? "matching " : ""}background sync jobs for this profile`);
|
|
225
226
|
});
|
|
226
227
|
annotate(command.command("show"), { effects: LOCAL_READ })
|
|
228
|
+
.summary("one archive job's status and log")
|
|
227
229
|
.description("one background job — the newest when none is named — and what the store now holds of its chat")
|
|
228
230
|
.argument("[job]", "the job id `messages sync --background` printed")
|
|
229
231
|
.action(async function (id) {
|
|
@@ -235,7 +237,8 @@ export const jobsCommand = (messenger) => {
|
|
|
235
237
|
: await context.withServices((services) => services.archive.held(chatId)).catch(() => undefined);
|
|
236
238
|
renderAnswer(this, context, itemOf(snakeKeys({ ...jobDetails(job), ...(held ? { held } : {}), log: job.log })));
|
|
237
239
|
});
|
|
238
|
-
destroysNothing(annotate(command.command("cancel"), { effects: LOCAL_WRITE
|
|
240
|
+
destroysNothing(annotate(command.command("cancel"), { effects: LOCAL_WRITE }))
|
|
241
|
+
.summary("cancel a queued or running archive job")
|
|
239
242
|
.description("stop a running background job after its current page; a later sync resumes where it stopped")
|
|
240
243
|
.argument("<job>", "the job id")
|
|
241
244
|
.action(function (id) {
|
|
@@ -248,7 +251,8 @@ export const jobsCommand = (messenger) => {
|
|
|
248
251
|
process.kill(job.pid, "SIGTERM");
|
|
249
252
|
renderAnswer(this, context, itemOf({ job: job.id, cancelled: true }));
|
|
250
253
|
});
|
|
251
|
-
destroysNothing(annotate(command.command("retry"), { effects: LOCAL_WRITE
|
|
254
|
+
destroysNothing(annotate(command.command("retry"), { effects: LOCAL_WRITE }))
|
|
255
|
+
.summary("retry an archive job")
|
|
252
256
|
.description("start a partial, failed or died job again, as a new job; the sync resumes where the store stopped")
|
|
253
257
|
.argument("[job]", "the job id")
|
|
254
258
|
.option("--failed", "every chat whose newest job partial, failed or died")
|
|
@@ -288,7 +292,7 @@ export const jobsCommand = (messenger) => {
|
|
|
288
292
|
if (items.length === 0 && context.format === "pretty")
|
|
289
293
|
context.renderer.note("no partial, failed or died jobs to retry");
|
|
290
294
|
});
|
|
291
|
-
destroysNothing(annotate(command.command("clear"), { effects: LOCAL_WRITE
|
|
295
|
+
destroysNothing(annotate(command.command("clear"), { effects: LOCAL_WRITE }))
|
|
292
296
|
.description("forget finished jobs and remove their logs; a running job is kept")
|
|
293
297
|
.action(function () {
|
|
294
298
|
const context = messengerContext(this, messenger);
|
|
@@ -49,6 +49,8 @@ export declare const inboxAnswer: (inbox: Inbox, hearing: Hearing | undefined, r
|
|
|
49
49
|
}[][] | undefined;
|
|
50
50
|
}[];
|
|
51
51
|
}>;
|
|
52
|
+
/** What `review --offline` cannot see. */
|
|
53
|
+
export declare const OFFLINE_REVIEW = "offline: only what was already synced to the local store; run review without --offline for the rest";
|
|
52
54
|
/**
|
|
53
55
|
* A review the next one goes on from: from `until` when nothing was left out, else from the same start again.
|
|
54
56
|
* Saved points (`--new`) carry this themselves, and an `--unanswered` look is not a review to continue.
|
|
@@ -48,7 +48,7 @@ const nameOf = (chat) => chat.title ?? chat.id;
|
|
|
48
48
|
* What every catch-up answer shares: the messages, oldest first, each naming its chat; what was left out as
|
|
49
49
|
* `missing`; and a chat cut short as a `messages.list` step, after the steps the command puts first.
|
|
50
50
|
*/
|
|
51
|
-
const catchUpList = (command, found, hearing, reader, { transcribe, first = [], meta = {} }) => {
|
|
51
|
+
const catchUpList = (command, found, hearing, reader, { transcribe, first = [], meta = {}, left = [], }) => {
|
|
52
52
|
const read = found.chats
|
|
53
53
|
.flatMap((chat) => heard(chat.messages, hearing).map((message) => ({ ...message, chatTitle: chat.title })))
|
|
54
54
|
.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));
|
|
@@ -70,6 +70,7 @@ const catchUpList = (command, found, hearing, reader, { transcribe, first = [],
|
|
|
70
70
|
? [{ field: "items", why: `${found.quiet} muted or archived chats left out`, step: `${command}.all` }]
|
|
71
71
|
: []),
|
|
72
72
|
...hearingMissing(hearing, transcribe),
|
|
73
|
+
...left,
|
|
73
74
|
];
|
|
74
75
|
return listOf(command, {
|
|
75
76
|
items: read.map((message) => summaryOf(message, reader)),
|
|
@@ -81,6 +82,8 @@ const catchUpList = (command, found, hearing, reader, { transcribe, first = [],
|
|
|
81
82
|
});
|
|
82
83
|
};
|
|
83
84
|
export const inboxAnswer = (inbox, hearing, reader, transcribe, meta = {}) => catchUpList("inbox", inbox, hearing, reader, { transcribe, meta });
|
|
85
|
+
/** What `review --offline` cannot see. */
|
|
86
|
+
export const OFFLINE_REVIEW = "offline: only what was already synced to the local store; run review without --offline for the rest";
|
|
84
87
|
const quoted = (value) => `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
85
88
|
/**
|
|
86
89
|
* A review the next one goes on from: from `until` when nothing was left out, else from the same start again.
|
|
@@ -95,6 +98,7 @@ export const reviewAnswer = (found, hearing, reader, { transcribe, fresh, chat,
|
|
|
95
98
|
return catchUpList("review", found, hearing, reader, {
|
|
96
99
|
transcribe,
|
|
97
100
|
first: continues ? [nextStep("review.next", { args: { query: query.join(" ") } })] : [],
|
|
101
|
+
...(found.offline ? { left: [{ field: "items", why: OFFLINE_REVIEW }] } : {}),
|
|
98
102
|
...(meta === undefined ? {} : { meta }),
|
|
99
103
|
});
|
|
100
104
|
};
|
|
@@ -19,6 +19,7 @@ import { membersCommand } from "./chats-members-command.js";
|
|
|
19
19
|
import { markReadCommand } from "./chats-read-command.js";
|
|
20
20
|
import { trackingCommand } from "./chats-tracking-command.js";
|
|
21
21
|
import { messengerContext } from "./context.js";
|
|
22
|
+
import { hideNever } from "./lacks.js";
|
|
22
23
|
import { archiveCommands, chatDeletionCommands, chatPinCommands, markUnreadCommand, mediaCommand, muteCommands, } from "./records-command.js";
|
|
23
24
|
export const chatsCommand = (messenger) => {
|
|
24
25
|
const chats = new Command("chats").description("the account's chats");
|
|
@@ -37,6 +38,7 @@ export const chatsCommand = (messenger) => {
|
|
|
37
38
|
// Where the history is pushed, the store answers, --offline too.
|
|
38
39
|
...(messenger.history === "store" ? {} : { offline: false }),
|
|
39
40
|
})
|
|
41
|
+
.summary("stored membership and title changes")
|
|
40
42
|
.description("who joined, left, was added or removed, and by whom — from the chat's service messages; " +
|
|
41
43
|
`the last ${EVENTS_DAYS} days unless the query names time`)
|
|
42
44
|
.argument("<chat>", messenger.chatArgument), { output: { entity: "chat_event" } }), EVENTS_QUERY).action(async function (chat, words) {
|
|
@@ -152,7 +154,7 @@ export const chatsCommand = (messenger) => {
|
|
|
152
154
|
if (command)
|
|
153
155
|
annotate(command, { examples: [`${messenger.app.command} personal chats ${path} ${args}`.trimEnd()] });
|
|
154
156
|
}
|
|
155
|
-
return chats;
|
|
157
|
+
return hideNever(messenger, chats);
|
|
156
158
|
};
|
|
157
159
|
/** Where the history is pushed, the chat list and a chat's card answer from the store and never connect. */
|
|
158
160
|
const storedRead = (messenger) => (messenger.history === "store" ? LOCAL_READ : REMOTE_READ);
|
|
@@ -15,6 +15,7 @@ import { messengerContext } from "./context.js";
|
|
|
15
15
|
export const membersCommand = (messenger) => {
|
|
16
16
|
const members = new Command("members").description("who is in a group");
|
|
17
17
|
members.addCommand(annotate(withQuery(withPaging(annotate(new Command("list"), { effects: REMOTE_READ })
|
|
18
|
+
.summary("group members and their roles")
|
|
18
19
|
.description("everyone in a group, a page at a time, with their role and when they were last seen")
|
|
19
20
|
.argument("<chat>", messenger.chatArgument)), MEMBERS_QUERY), { output: { entity: "member" } }).action(async function (chat, words) {
|
|
20
21
|
const context = messengerContext(this, messenger);
|
|
@@ -23,6 +24,7 @@ export const membersCommand = (messenger) => {
|
|
|
23
24
|
renderAnswer(this, context, body, lines(({ id, name, username, role }) => [id, name ?? "", username ? `@${username}` : "", role ?? ""].join(" ")));
|
|
24
25
|
}));
|
|
25
26
|
members.addCommand(withQuery(annotate(annotate(new Command("audit"), { effects: REMOTE_READ, offline: false })
|
|
27
|
+
.summary("members that look like bots, with reasons")
|
|
26
28
|
.description("members that look like bots, each with its reasons — read from the member list and the local store; " +
|
|
27
29
|
"never one request per person, and it removes nobody")
|
|
28
30
|
.argument("<chat>", messenger.chatArgument)
|
|
@@ -54,6 +56,7 @@ export const membersCommand = (messenger) => {
|
|
|
54
56
|
].join(" ")));
|
|
55
57
|
}));
|
|
56
58
|
members.addCommand(withQuery(annotate(annotate(new Command("history"), { effects: LOCAL_READ })
|
|
59
|
+
.summary("stored membership changes")
|
|
57
60
|
.description("who joined, who left and whose profile changed, oldest first — what chats members fetch recorded in the " +
|
|
58
61
|
"local store; never asks the messenger")
|
|
59
62
|
.argument("<chat>", messenger.chatArgument), { output: { entity: "chat_event" } }), HISTORY_QUERY).action(async function (chat, words) {
|
|
@@ -69,6 +72,7 @@ export const membersCommand = (messenger) => {
|
|
|
69
72
|
].join(" ")));
|
|
70
73
|
}));
|
|
71
74
|
const fetch = annotate(new Command("fetch"), { effects: FETCH, offline: false })
|
|
75
|
+
.summary("save the group's current membership")
|
|
72
76
|
.description("read a group's whole member list into the local store's member history: who joined, who left, daily " +
|
|
73
77
|
"counts and profile changes; someone is recorded as gone only when every member was read")
|
|
74
78
|
.argument("<chat>", messenger.chatArgument);
|
|
@@ -9,6 +9,7 @@ import { messengerContext } from "./context.js";
|
|
|
9
9
|
import { chatStatsAnswer, MEMBERS_UNRECORDED, refuseJsonl } from "./stats-answers.js";
|
|
10
10
|
const PERIODS = ["day", "week"];
|
|
11
11
|
export const statsCommand = (messenger) => annotate(new Command("show"), { effects: REMOTE_READ, output: { entity: "chat_stats" } })
|
|
12
|
+
.summary("activity and participation in one chat")
|
|
12
13
|
.description("a group's or channel's numbers for a period: messages, active members, replies, reactions, questions answered, " +
|
|
13
14
|
`joins and leaves — counted from the local store; ${messenger.history === "store" ? "joins and leaves too" : "joins and leaves are asked of the messenger"}`)
|
|
14
15
|
.argument("<chat>", messenger.chatArgument)
|
|
@@ -7,7 +7,9 @@ import { lines, wholeList } from "./chat-answers.js";
|
|
|
7
7
|
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
8
8
|
/** The chats whose member lists `serve` fetches daily; kept in the local store, never sent. */
|
|
9
9
|
export const trackingCommand = (messenger) => {
|
|
10
|
-
const tracking = new Command("tracking")
|
|
10
|
+
const tracking = new Command("tracking")
|
|
11
|
+
.summary("chats whose member lists are fetched daily")
|
|
12
|
+
.description("the chats whose member lists serve fetches daily into the local store — chats members fetch --track adds one");
|
|
11
13
|
annotate(tracking.command("list"), { effects: LOCAL_READ })
|
|
12
14
|
.description("every tracked chat: since when, and its last member count")
|
|
13
15
|
.action(async function () {
|
|
@@ -22,6 +24,7 @@ export const trackingCommand = (messenger) => {
|
|
|
22
24
|
].join(" ")));
|
|
23
25
|
});
|
|
24
26
|
annotate(tracking.command("show"), { effects: LOCAL_READ })
|
|
27
|
+
.summary("one chat's member tracking and daily counts")
|
|
25
28
|
.description(`one chat: whether it is tracked, and its member count per day for the last ${TRACKED_DAYS} days`)
|
|
26
29
|
.argument("<chat>", messenger.chatArgument)
|
|
27
30
|
.action(async function (chat) {
|
|
@@ -19,9 +19,13 @@ const SHELLS = ["zsh", "bash", "fish", "powershell"];
|
|
|
19
19
|
* ⚠ **A Tab never connects and never creates anything.** Names come from the message store if it
|
|
20
20
|
* exists — no store, no names — and nothing goes to stderr: a shell shows whatever it is given.
|
|
21
21
|
*/
|
|
22
|
-
export const completeCommand = (messenger, config, options = {}) => annotate(new Command("complete"), {
|
|
22
|
+
export const completeCommand = (messenger, config, options = {}) => annotate(new Command("complete"), {
|
|
23
|
+
effects: LOCAL_READ,
|
|
24
|
+
mcp: false,
|
|
25
|
+
examples: [`${messenger.app.command} complete zsh`],
|
|
26
|
+
})
|
|
23
27
|
.description(`shell completion: \`${messenger.app.command} complete zsh\` prints the script to source`)
|
|
24
|
-
.argument("[words...]")
|
|
28
|
+
.argument("[words...]", "zsh for the shell script, or command words to complete")
|
|
25
29
|
.allowUnknownOption()
|
|
26
30
|
.helpOption(false)
|
|
27
31
|
.action(async function (words) {
|
|
@@ -19,6 +19,7 @@ import { commandAt, nextStep } from "../steps.js";
|
|
|
19
19
|
import { contactWriteCommands } from "./admin-contacts-command.js";
|
|
20
20
|
import { contactAnswer, lines, pageOf } from "./chat-answers.js";
|
|
21
21
|
import { messengerContext } from "./context.js";
|
|
22
|
+
import { hideNever } from "./lacks.js";
|
|
22
23
|
import { CONTACTS_QUERY, notesAllowed } from "./people-queries.js";
|
|
23
24
|
import { privatePeopleCommands } from "./private-people-command.js";
|
|
24
25
|
import { syncsFirst } from "./search-sync-options.js";
|
|
@@ -68,7 +69,8 @@ export const contactsCommand = (messenger) => {
|
|
|
68
69
|
}));
|
|
69
70
|
renderAnswer(this, context, itemOf(contactAnswer(account, card)));
|
|
70
71
|
}));
|
|
71
|
-
annotate(contacts.command("profile"), { effects: REMOTE_READ })
|
|
72
|
+
annotate(contacts.command("profile"), { effects: REMOTE_READ, output: { entity: "contact_profile" } })
|
|
73
|
+
.summary("a person's profile from the messenger")
|
|
72
74
|
.description("everything the messenger says about one person — handles, flags, last seen, when they registered — and " +
|
|
73
75
|
"how many of their messages the store holds in each chat you share, the first and the last, and the " +
|
|
74
76
|
"earlier names and usernames the store saw them with")
|
|
@@ -86,6 +88,7 @@ export const contactsCommand = (messenger) => {
|
|
|
86
88
|
}));
|
|
87
89
|
});
|
|
88
90
|
const contextCommand = annotate(contacts.command("context"), { effects: REMOTE_READ })
|
|
91
|
+
.summary("what the local archive knows about a person")
|
|
89
92
|
.description("what the store holds about one person, in every messenger linked to them: shared chats, the last " +
|
|
90
93
|
"messages each way, their recent messages, where others mentioned them — never connects")
|
|
91
94
|
.argument("<person>", "their id, @username, or part of their name")
|
|
@@ -167,6 +170,7 @@ export const contactsCommand = (messenger) => {
|
|
|
167
170
|
}));
|
|
168
171
|
});
|
|
169
172
|
annotate(contacts.command("timeline"), { effects: REMOTE_READ })
|
|
173
|
+
.summary("one person's activity across their linked identities")
|
|
170
174
|
.description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
|
|
171
175
|
"mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
|
|
172
176
|
.argument("<person>", "their id, @username, or part of their name")
|
|
@@ -208,6 +212,7 @@ export const contactsCommand = (messenger) => {
|
|
|
208
212
|
}));
|
|
209
213
|
});
|
|
210
214
|
annotate(contacts.command("check"), { effects: REMOTE_READ })
|
|
215
|
+
.summary("check a person's profile and messages for signs of abuse")
|
|
211
216
|
.description(registriesCover(messenger.provider)
|
|
212
217
|
? "whether one person looks like a bot, a fake or a spammer: their profile, what they wrote in the store, " +
|
|
213
218
|
`and the public ban lists (${Object.values(REGISTRIES)
|
|
@@ -235,6 +240,7 @@ export const contactsCommand = (messenger) => {
|
|
|
235
240
|
renderAnswer(this, context, itemOf(snakeKeys(answer)));
|
|
236
241
|
});
|
|
237
242
|
annotate(contacts.command("link"), { effects: LOCAL_WRITE })
|
|
243
|
+
.summary("link identities that belong to one person")
|
|
238
244
|
.description("record that two people in the store are one person — the same name is never enough")
|
|
239
245
|
.argument("<person>", "their id, @username, or part of their name")
|
|
240
246
|
.argument("<other>", "the same in another messenger of the store, as <messenger>:<person> — max:Ana; me: it is you, as zoom:<id> me")
|
|
@@ -255,7 +261,8 @@ export const contactsCommand = (messenger) => {
|
|
|
255
261
|
* ⚠ **The number is asked for or piped, never an argument**: argv is read by `ps` and kept by shell
|
|
256
262
|
* history, and a phone number is personal data.
|
|
257
263
|
*/
|
|
258
|
-
annotate(contacts.command("lookup"), { effects: REMOTE_READ })
|
|
264
|
+
annotate(contacts.command("lookup"), { effects: REMOTE_READ, output: { entity: "contact_lookup" } })
|
|
265
|
+
.summary("find an account by a phone number")
|
|
259
266
|
.description("who has this phone number — asks for it, or reads it from stdin; never an argument")
|
|
260
267
|
// Commander's own refusal would repeat the number on stderr.
|
|
261
268
|
.allowExcessArguments()
|
|
@@ -267,7 +274,7 @@ export const contactsCommand = (messenger) => {
|
|
|
267
274
|
const phone = phoneOf(await readSecret("phone number: ", { ...secretInputOf(this), echo: true }));
|
|
268
275
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.people.lookup(phone)))));
|
|
269
276
|
});
|
|
270
|
-
annotate(contacts.command("sync"), { effects: FETCH })
|
|
277
|
+
annotate(contacts.command("sync"), { effects: FETCH, output: { entity: "contact_sync" } })
|
|
271
278
|
.description("take the whole contact list from the messenger into the local store")
|
|
272
279
|
.action(async function () {
|
|
273
280
|
const context = messengerContext(this, messenger);
|
|
@@ -309,5 +316,5 @@ export const contactsCommand = (messenger) => {
|
|
|
309
316
|
if (command)
|
|
310
317
|
annotate(command, { examples: [`${messenger.app.command} personal contacts ${path} ${args}`.trimEnd()] });
|
|
311
318
|
}
|
|
312
|
-
return contacts;
|
|
319
|
+
return hideNever(messenger, contacts);
|
|
313
320
|
};
|
|
@@ -95,6 +95,11 @@ export interface Messenger extends ServiceMessenger {
|
|
|
95
95
|
* connecting, as the adapter would refuse after; one connection per device makes that connection a cost.
|
|
96
96
|
*/
|
|
97
97
|
lacks?: readonly (keyof MessengerAdapter)[];
|
|
98
|
+
/**
|
|
99
|
+
* Adapter methods this messenger can never have, where `lacks` is what it does not have yet: their commands refuse
|
|
100
|
+
* the same way and are also hidden from help, `commands` and MCP. WhatsApp: only the phone ends other sessions.
|
|
101
|
+
*/
|
|
102
|
+
never?: readonly (keyof MessengerAdapter)[];
|
|
98
103
|
/** Whether a message can be deleted for the owner alone; yes when unset. No: every delete is for everyone (WhatsApp). */
|
|
99
104
|
deleteForMe?: boolean;
|
|
100
105
|
/** Whether the messenger has forum topics: `--topic`, `topic:` in `messages list` and the `topics_*` tools; yes when unset. */
|
|
@@ -30,8 +30,11 @@ import { readAll } from "./stdin.js";
|
|
|
30
30
|
* mentions, one sender's messages in a row. From the store alone; nothing is built until asked (phase 3).
|
|
31
31
|
*/
|
|
32
32
|
export const conversationsCommand = (messenger) => {
|
|
33
|
-
const conversations = new Command("conversations")
|
|
33
|
+
const conversations = new Command("conversations")
|
|
34
|
+
.summary("group stored messages into conversations")
|
|
35
|
+
.description("the conversations inside a chat, found in the stored messages by replies, mentions and who wrote next");
|
|
34
36
|
boundOptions(reachesOtherService(annotate(conversations.command("build"), { effects: REMOTE_WRITE }))
|
|
37
|
+
.summary("build conversation links from stored messages")
|
|
35
38
|
.description("find a chat's conversations in what the store holds, replacing the last build; without --chat, every " +
|
|
36
39
|
"chat that changed since its build and every group never built; never asks the messenger")
|
|
37
40
|
.option("--chat <chat>", messenger.chatArgument)
|
|
@@ -111,6 +114,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
111
114
|
renderAnswer(this, context, itemOf(snakeKeys(built)), () => `${built.messages} messages → ${built.conversations} conversations, ${built.links} links (rules v${built.rulesVersion})\n`);
|
|
112
115
|
});
|
|
113
116
|
withQuery(annotate(conversations.command("list"), { effects: LOCAL_READ })
|
|
117
|
+
.summary("stored conversations in one chat")
|
|
114
118
|
.description("a chat's conversations, the newest first: when, how many messages, how many people")
|
|
115
119
|
.argument("<chat>", messenger.chatArgument), CONVERSATION_QUERY)
|
|
116
120
|
.option("--limit <n>", "how many", positiveCount("--limit"))
|
|
@@ -123,6 +127,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
123
127
|
context.renderer.note("no conversations match");
|
|
124
128
|
});
|
|
125
129
|
annotate(conversations.command("show"), { effects: LOCAL_READ })
|
|
130
|
+
.summary("one conversation and its messages")
|
|
126
131
|
.description("one conversation's messages, oldest first — by its id, or the one a message is in")
|
|
127
132
|
.argument("<conversation>", `a conversation id from \`conversations list\`; or ${messenger.chatArgument}, with a message`)
|
|
128
133
|
.argument("[message]", "a message id in that chat: show the conversation it is in")
|
|
@@ -151,6 +156,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
151
156
|
renderAnswer(this, context, conversationShown(account, { summary, messages }));
|
|
152
157
|
});
|
|
153
158
|
withModelOptions(annotate(conversations.command("related"), { effects: LOCAL_READ })
|
|
159
|
+
.summary("conversations nearest in meaning to a message's conversation")
|
|
154
160
|
.description("the conversations nearest in meaning to the one a message is in, in every built chat, best first — " +
|
|
155
161
|
"from the vectors `conversations embed` stored; runs no model")
|
|
156
162
|
.argument("<chat>", messenger.chatArgument)
|
|
@@ -166,6 +172,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
166
172
|
context.renderer.note(`no other conversation has vectors of ${found.model}`);
|
|
167
173
|
});
|
|
168
174
|
withModelOptions(annotate(conversations.command("status"), { effects: LOCAL_READ })
|
|
175
|
+
.summary("which chats are ready for conversation search")
|
|
169
176
|
.description("how fresh each built chat's conversations and vectors are: messages the build has not seen, chunks " +
|
|
170
177
|
"with a current, stale or missing vector")
|
|
171
178
|
.option("--chat <chat>", `only this chat: ${messenger.chatArgument}`)).action(async function () {
|
|
@@ -179,9 +186,11 @@ export const conversationsCommand = (messenger) => {
|
|
|
179
186
|
});
|
|
180
187
|
const batches = conversations
|
|
181
188
|
.command("batches")
|
|
189
|
+
.summary("batches waiting for an agent's conversation links")
|
|
182
190
|
.description("windows of a chat for your own AI agent to link: which earlier message each one answers");
|
|
183
191
|
const sizeOption = (command) => command.option("--size <n>", `messages to answer per batch, ${BATCH_SIZE.min}–${BATCH_SIZE.max}; ${BATCH_SIZE.default} by default`, positiveCount("--size"));
|
|
184
192
|
sizeOption(annotate(batches.command("status"), { effects: LOCAL_READ })
|
|
193
|
+
.summary("progress of conversation-link batches")
|
|
185
194
|
.description("how many messages still wait for an answer, in how many batches, and how much text")
|
|
186
195
|
.requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
187
196
|
const { chat, size = BATCH_SIZE.default } = this.opts();
|
|
@@ -191,6 +200,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
191
200
|
`${status.characters} characters (about ${status.tokensEstimate} tokens)\n`);
|
|
192
201
|
});
|
|
193
202
|
sizeOption(annotate(batches.command("next"), { effects: LOCAL_READ })
|
|
203
|
+
.summary("get the next batch of messages to link")
|
|
194
204
|
.description("the next window to answer, with the messages before it; message text goes to stdout only")
|
|
195
205
|
.requiredOption("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
196
206
|
const { chat, size = BATCH_SIZE.default } = this.opts();
|
|
@@ -221,6 +231,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
221
231
|
description: '{ "model", "skill"?, "answers": [{ "message", "parent", "confidence" }] }',
|
|
222
232
|
json: true,
|
|
223
233
|
})
|
|
234
|
+
.summary("save an agent's conversation-link answers")
|
|
224
235
|
.description('store your agent\'s answer to a batch, read as JSON from stdin: { "model", "answers": [{ "message", ' +
|
|
225
236
|
'"parent", "confidence" }] }; all or nothing')
|
|
226
237
|
.requiredOption("--batch <id>", "the batch id `conversations batches next` printed")
|
|
@@ -233,6 +244,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
233
244
|
renderAnswer(this, context, itemOf(stored, { next: [nextStep("conversations.build", { args: { chat: stored.chat } })] }), () => `${stored.stored} answers stored\n`);
|
|
234
245
|
});
|
|
235
246
|
annotate(links.command("clear"), { effects: LOCAL_WRITE })
|
|
247
|
+
.summary("remove an agent's saved conversation-link answers")
|
|
236
248
|
.description("drop your agent's answers for a chat, or only one model's; messages are never touched")
|
|
237
249
|
.requiredOption("--chat <chat>", messenger.chatArgument)
|
|
238
250
|
.option("--model <model>", "only the answers this model gave")
|
|
@@ -244,6 +256,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
244
256
|
renderAnswer(this, context, itemOf(cleared), () => `${cleared.cleared} answers dropped\n`);
|
|
245
257
|
});
|
|
246
258
|
const embed = withModelOptions(reachesOtherService(annotate(new Command("embed"), { effects: REMOTE_WRITE }))
|
|
259
|
+
.summary("prepare conversation vectors for meaning search")
|
|
247
260
|
.description("compute a vector for each chunk of a chat's conversations for search by meaning — on this machine, or " +
|
|
248
261
|
"with --provider through a service and your key; resumes where it stopped; without --chat, every " +
|
|
249
262
|
"built chat with chunks left, on this machine only")
|
|
@@ -290,6 +303,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
290
303
|
}), () => `${done.embedded} chunks embedded with ${done.model}${done.skipped ? `, ${done.skipped} changed since the build` : ""}\n`);
|
|
291
304
|
});
|
|
292
305
|
withModelOptions(annotate(embed.command("status"), { effects: LOCAL_READ })
|
|
306
|
+
.summary("which conversation vectors are ready or stale")
|
|
293
307
|
.description("how many chunks of a chat have a vector of the model, how many are left, and what is left costs")
|
|
294
308
|
.option("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
295
309
|
const options = this.optsWithGlobals();
|
|
@@ -303,6 +317,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
303
317
|
"\n");
|
|
304
318
|
});
|
|
305
319
|
withModelOptions(destroysNothing(annotate(embed.command("clear"), { effects: LOCAL_WRITE }))
|
|
320
|
+
.summary("remove stored conversation vectors")
|
|
306
321
|
.description("drop a chat's vectors, or only one model's; messages and conversations are never touched")
|
|
307
322
|
.option("--chat <chat>", messenger.chatArgument)).action(async function () {
|
|
308
323
|
const options = this.optsWithGlobals();
|
|
@@ -479,6 +494,7 @@ export const linksCommand = (messenger) => annotate(new Command("links"), {
|
|
|
479
494
|
examples: [`${messenger.app.command} personal messages links "Book club" 101`],
|
|
480
495
|
effects: LOCAL_READ,
|
|
481
496
|
})
|
|
497
|
+
.summary("why a message belongs to its conversation")
|
|
482
498
|
.description("why a message is in its conversation: each link it has, and the chain of answers back to the start")
|
|
483
499
|
.argument("<chat>", messenger.chatArgument)
|
|
484
500
|
.argument("<message>", "the message id")
|
|
@@ -511,6 +527,7 @@ const readinessLine = ({ chat, state, graph, pending, vectors }, model) => graph
|
|
|
511
527
|
`${vectors.missing} missing of ${vectors.chunks} chunks`;
|
|
512
528
|
/** `search conversations`: the conversations nearest to a query, in meaning and in words. */
|
|
513
529
|
export const conversationsSearchCommand = (messenger) => syncOptions(boundOptions(withModelOptions(reachesOtherService(annotate(new Command("conversations"), { effects: REMOTE_READ })))), messenger)
|
|
530
|
+
.summary("find conversations by words and meaning")
|
|
514
531
|
.description("the conversations nearest to a query in meaning and in words, best first, in one chat or every one — " +
|
|
515
532
|
"meaning after `conversations embed`; runs on this machine")
|
|
516
533
|
.argument("<query>", "what to look for, in your own words, in any language the model reads")
|
|
@@ -10,6 +10,7 @@ export const countersCommand = (messenger) => {
|
|
|
10
10
|
const parent = new Command("counters").description("per-counter observations and bounded remote refresh");
|
|
11
11
|
for (const operation of ["show", "refresh"]) {
|
|
12
12
|
const command = new Command(operation)
|
|
13
|
+
.summary("refresh message counters from the messenger")
|
|
13
14
|
.description(operation === "show"
|
|
14
15
|
? "show saved counter values and their observation freshness"
|
|
15
16
|
: "read authoritative counters for bounded messages and update their local observations")
|
|
@@ -57,7 +57,7 @@ const INCLUDES = [
|
|
|
57
57
|
const EXCLUDES = ["message text", "chat titles", "names", "phone numbers", "the session", "the app credentials"];
|
|
58
58
|
const reportCommand = (messenger) => {
|
|
59
59
|
const { app } = messenger;
|
|
60
|
-
const report = annotate(new Command("report"), { effects: REMOTE_READ
|
|
60
|
+
const report = annotate(new Command("report"), { effects: REMOTE_READ })
|
|
61
61
|
.description("what a problem report holds; writes nothing")
|
|
62
62
|
.action(function () {
|
|
63
63
|
const output = outputFor(this);
|
|
@@ -74,7 +74,7 @@ const reportCommand = (messenger) => {
|
|
|
74
74
|
`About a given run: ${create} --run <id> (ids: ${lineOf(this, "runs list")})`,
|
|
75
75
|
].join("\n"));
|
|
76
76
|
});
|
|
77
|
-
destroysNothing(annotate(report.command("create"), { effects: LOCAL_WRITE }))
|
|
77
|
+
destroysNothing(annotate(report.command("create"), { effects: LOCAL_WRITE, mcp: false }))
|
|
78
78
|
.description("write a problem report to a file, and say where to send it")
|
|
79
79
|
.option("--run <id>", "the run the report is about; the newest failed one if not given")
|
|
80
80
|
.option("--output <file>", "where to write it; a new file in this directory if not given")
|
|
@@ -21,6 +21,7 @@ export const downloadSubcommand = (messages, messenger) => annotate(messages.com
|
|
|
21
21
|
examples: [`${messenger.app.command} personal messages download "Book club" 101 --output-dir ./downloads`],
|
|
22
22
|
effects: FETCH,
|
|
23
23
|
})
|
|
24
|
+
.summary("download files attached to stored messages")
|
|
24
25
|
.description("save a message's photos, files, videos and voice notes to a folder — or a whole chat's with --all")
|
|
25
26
|
.argument("<chat>", messenger.chatArgument)
|
|
26
27
|
.argument("[message]", "the message id; left out with --all")
|
|
@@ -14,7 +14,8 @@ export const floodCommand = (messenger) => {
|
|
|
14
14
|
const name = messenger.name ?? app.command;
|
|
15
15
|
return annotate(new Command("flood"), { mcp: false })
|
|
16
16
|
.description(`the waits ${name} asked this profile to keep, and a hold on its writes`)
|
|
17
|
-
.addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE }))
|
|
17
|
+
.addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE, examples: [`${app.command} personal flood clear`] }))
|
|
18
|
+
.summary("clear a profile's stored rate-limit hold")
|
|
18
19
|
.description(`forget them, lift the hold and the profile's pace, once ${name} no longer limits the account; changes nothing there`)
|
|
19
20
|
.action(async function () {
|
|
20
21
|
const context = messengerContext(this, messenger);
|
|
@@ -14,9 +14,10 @@ import { messengerContext } from "./context.js";
|
|
|
14
14
|
* a model that a message talked into writing somewhere, not an agent set on getting around it.
|
|
15
15
|
*/
|
|
16
16
|
export const recipientsCommand = (messenger) => {
|
|
17
|
+
const tool = `${messenger.app.command} personal`;
|
|
17
18
|
const listFor = (profile, env) => new RecipientList(recipientsPathFor(messenger.app, profile, env), messenger.app.command);
|
|
18
19
|
const command = new Command("recipients").description("the chats this profile may send to, when the list is on");
|
|
19
|
-
annotate(command.command("list"), { effects: LOCAL_READ })
|
|
20
|
+
annotate(command.command("list"), { effects: LOCAL_READ, examples: [`${tool} recipients list`] })
|
|
20
21
|
.description("the chats on the list; empty and off until the first add")
|
|
21
22
|
.action(async function () {
|
|
22
23
|
const context = messengerContext(this, messenger);
|
|
@@ -28,7 +29,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
28
29
|
else if (chats.length === 0)
|
|
29
30
|
renderer.note("the recipient list is on and empty — this profile may send nowhere");
|
|
30
31
|
});
|
|
31
|
-
grantsAuthority(annotate(command.command("add"), { effects: FETCH }))
|
|
32
|
+
grantsAuthority(annotate(command.command("add"), { effects: FETCH, examples: [`${tool} recipients add "Book club"`] }))
|
|
32
33
|
.argument("<chat>", messenger.chatArgument)
|
|
33
34
|
.description("allow sending to this chat; the first add turns the list on")
|
|
34
35
|
.action(async function (chat) {
|
|
@@ -43,7 +44,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
43
44
|
});
|
|
44
45
|
renderAnswer(this, context, itemOf({ id: found.id, title: found.title, added }));
|
|
45
46
|
});
|
|
46
|
-
grantsAuthority(annotate(command.command("remove"), { effects: LOCAL_WRITE }))
|
|
47
|
+
grantsAuthority(annotate(command.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} recipients remove "Book club"`] }))
|
|
47
48
|
.argument("<chat>", "chat id, or the title as the list shows it")
|
|
48
49
|
.description("stop allowing this chat; the list stays on")
|
|
49
50
|
.action(async function (chat) {
|
|
@@ -54,7 +55,7 @@ export const recipientsCommand = (messenger) => {
|
|
|
54
55
|
throw new CliError("not_found", `${chat.trim()} is not on the recipient list of ${settings.profile}`);
|
|
55
56
|
renderAnswer(this, context, itemOf({ id: gone.id, title: gone.title, removed: true }));
|
|
56
57
|
});
|
|
57
|
-
grantsAuthority(annotate(command.command("clear"), { effects: LOCAL_WRITE }))
|
|
58
|
+
grantsAuthority(annotate(command.command("clear"), { effects: LOCAL_WRITE, examples: [`${tool} recipients clear`] }))
|
|
58
59
|
.description("delete the list, which turns it off: this profile may send to any chat again")
|
|
59
60
|
.action(async function () {
|
|
60
61
|
const context = messengerContext(this, messenger);
|