@wirecat/cli-core 0.399.0 → 0.400.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/dist/analysis/consents.d.ts +11 -6
  3. package/dist/analysis/consents.js +5 -2
  4. package/dist/cli/bot/mcp.js +2 -1
  5. package/dist/cli/config-command.js +20 -1
  6. package/dist/cli/context.d.ts +2 -0
  7. package/dist/cli/context.js +1 -1
  8. package/dist/cli/effects.d.ts +4 -1
  9. package/dist/cli/effects.js +10 -2
  10. package/dist/cli/index.d.ts +1 -1
  11. package/dist/cli/index.js +1 -1
  12. package/dist/cli/json-fields.js +12 -1
  13. package/dist/cli/messenger/account-command.js +15 -6
  14. package/dist/cli/messenger/account-profile-query.d.ts +2 -0
  15. package/dist/cli/messenger/account-profile-query.js +13 -0
  16. package/dist/cli/messenger/account-sessions-command.js +1 -0
  17. package/dist/cli/messenger/admin-commands.js +12 -12
  18. package/dist/cli/messenger/admin-contacts-command.js +6 -3
  19. package/dist/cli/messenger/admin-folders-command.js +5 -5
  20. package/dist/cli/messenger/admin-moderation-command.js +4 -4
  21. package/dist/cli/messenger/admin-statistics-command.js +6 -14
  22. package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
  23. package/dist/cli/messenger/analysis-consents-query.js +20 -0
  24. package/dist/cli/messenger/archive-commands.js +3 -3
  25. package/dist/cli/messenger/attachments-command.js +3 -0
  26. package/dist/cli/messenger/backfill-command.js +5 -4
  27. package/dist/cli/messenger/chat-queries.d.ts +1 -0
  28. package/dist/cli/messenger/chat-queries.js +12 -2
  29. package/dist/cli/messenger/chats-command.js +2 -2
  30. package/dist/cli/messenger/chats-documents-command.js +7 -5
  31. package/dist/cli/messenger/chats-members-command.js +7 -3
  32. package/dist/cli/messenger/chats-read-command.js +1 -1
  33. package/dist/cli/messenger/chats-tracking-command.js +14 -6
  34. package/dist/cli/messenger/contacts-command.js +9 -5
  35. package/dist/cli/messenger/conversations-command.js +20 -16
  36. package/dist/cli/messenger/doctor-command.js +5 -1
  37. package/dist/cli/messenger/document-links-query.d.ts +2 -0
  38. package/dist/cli/messenger/document-links-query.js +9 -0
  39. package/dist/cli/messenger/download-command.js +1 -0
  40. package/dist/cli/messenger/flood-command.d.ts +2 -2
  41. package/dist/cli/messenger/flood-command.js +3 -3
  42. package/dist/cli/messenger/gaps-command.js +2 -2
  43. package/dist/cli/messenger/guard-commands.js +35 -13
  44. package/dist/cli/messenger/images-command.js +1 -0
  45. package/dist/cli/messenger/media-archive-command.js +2 -0
  46. package/dist/cli/messenger/media-archive-move-command.js +1 -0
  47. package/dist/cli/messenger/media-archive-timer-command.js +1 -1
  48. package/dist/cli/messenger/mentions-command.js +15 -2
  49. package/dist/cli/messenger/messages-command.js +2 -0
  50. package/dist/cli/messenger/messages-delete-command.js +1 -0
  51. package/dist/cli/messenger/messages-edit-command.js +1 -0
  52. package/dist/cli/messenger/messages-evidence-command.js +3 -1
  53. package/dist/cli/messenger/messages-forward-command.js +1 -0
  54. package/dist/cli/messenger/messages-pin-command.js +3 -0
  55. package/dist/cli/messenger/messages-send-command.js +1 -0
  56. package/dist/cli/messenger/metadata-command.js +6 -1
  57. package/dist/cli/messenger/models-command.js +10 -2
  58. package/dist/cli/messenger/polls-command.js +4 -0
  59. package/dist/cli/messenger/private-people-command.js +3 -3
  60. package/dist/cli/messenger/rankings-command.js +6 -11
  61. package/dist/cli/messenger/reactions-command.js +2 -0
  62. package/dist/cli/messenger/recipients-query.d.ts +5 -0
  63. package/dist/cli/messenger/recipients-query.js +18 -0
  64. package/dist/cli/messenger/records-command.js +13 -6
  65. package/dist/cli/messenger/search-command.js +3 -1
  66. package/dist/cli/messenger/searches-command.js +5 -1
  67. package/dist/cli/messenger/sends-query.d.ts +5 -0
  68. package/dist/cli/messenger/sends-query.js +33 -0
  69. package/dist/cli/messenger/serve-command.js +1 -0
  70. package/dist/cli/messenger/statistics-query.d.ts +14 -0
  71. package/dist/cli/messenger/statistics-query.js +143 -0
  72. package/dist/cli/messenger/stats-command.js +5 -5
  73. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  74. package/dist/cli/messenger/store-maintenance-command.js +10 -10
  75. package/dist/cli/messenger/tags-command.js +7 -1
  76. package/dist/cli/messenger/tasks-command.js +47 -8
  77. package/dist/cli/messenger/transcribe-command.js +1 -0
  78. package/dist/cli/messenger/watch-answers.d.ts +6 -0
  79. package/dist/cli/messenger/watch-answers.js +69 -0
  80. package/dist/cli/messenger/watch-command.d.ts +1 -1
  81. package/dist/cli/messenger/watch-command.js +56 -7
  82. package/dist/cli/program.js +5 -1
  83. package/dist/cli/runs/command.js +25 -38
  84. package/dist/cli/runs/query.d.ts +5 -0
  85. package/dist/cli/runs/query.js +32 -0
  86. package/dist/cli/runs/search-command.d.ts +4 -0
  87. package/dist/cli/runs/search-command.js +27 -0
  88. package/dist/cli/runs/search-query.d.ts +4 -0
  89. package/dist/cli/runs/search-query.js +50 -0
  90. package/dist/cli/runs/search.d.ts +2 -1
  91. package/dist/cli/runs/search.js +6 -6
  92. package/dist/domain/attachments.js +9 -1
  93. package/dist/domain/rankings-options.js +3 -3
  94. package/dist/entities/account-change.d.ts +89 -0
  95. package/dist/entities/account-change.js +20 -0
  96. package/dist/entities/account-profile.d.ts +7 -0
  97. package/dist/entities/account-profile.js +8 -0
  98. package/dist/entities/analysis-consent.d.ts +9 -0
  99. package/dist/entities/analysis-consent.js +4 -0
  100. package/dist/entities/attachment-results.d.ts +96 -0
  101. package/dist/entities/attachment-results.js +31 -0
  102. package/dist/entities/batch-result.d.ts +27 -0
  103. package/dist/entities/batch-result.js +18 -0
  104. package/dist/entities/chat-admin.d.ts +234 -0
  105. package/dist/entities/chat-admin.js +84 -0
  106. package/dist/entities/chat-change.d.ts +25 -0
  107. package/dist/entities/chat-change.js +11 -0
  108. package/dist/entities/chat-metadata.d.ts +34 -0
  109. package/dist/entities/chat-metadata.js +15 -0
  110. package/dist/entities/chat-moderation.d.ts +44 -0
  111. package/dist/entities/chat-moderation.js +37 -0
  112. package/dist/entities/chat-tracking.d.ts +26 -0
  113. package/dist/entities/chat-tracking.js +23 -0
  114. package/dist/entities/contact-activity.d.ts +52 -0
  115. package/dist/entities/contact-activity.js +42 -0
  116. package/dist/entities/contact-context.d.ts +1568 -0
  117. package/dist/entities/contact-context.js +191 -0
  118. package/dist/entities/contact-write.d.ts +22 -0
  119. package/dist/entities/contact-write.js +6 -0
  120. package/dist/entities/conversation-preparation.d.ts +63 -0
  121. package/dist/entities/conversation-preparation.js +23 -0
  122. package/dist/entities/conversation-results.d.ts +567 -0
  123. package/dist/entities/conversation-results.js +94 -0
  124. package/dist/entities/document-link.d.ts +13 -0
  125. package/dist/entities/document-link.js +11 -0
  126. package/dist/entities/extraction-report.d.ts +95 -0
  127. package/dist/entities/extraction-report.js +34 -0
  128. package/dist/entities/image-embedding.d.ts +9 -0
  129. package/dist/entities/image-embedding.js +10 -0
  130. package/dist/entities/index.d.ts +8006 -1928
  131. package/dist/entities/index.js +174 -3
  132. package/dist/entities/listener.d.ts +2150 -0
  133. package/dist/entities/listener.js +46 -0
  134. package/dist/entities/media-archive.d.ts +43 -0
  135. package/dist/entities/media-archive.js +32 -0
  136. package/dist/entities/meeting-task-proposal.d.ts +14 -0
  137. package/dist/entities/meeting-task-proposal.js +19 -0
  138. package/dist/entities/meeting.d.ts +1 -1
  139. package/dist/entities/meeting.js +1 -1
  140. package/dist/entities/mention.d.ts +63 -0
  141. package/dist/entities/mention.js +44 -0
  142. package/dist/entities/message-change.d.ts +19 -0
  143. package/dist/entities/message-change.js +6 -0
  144. package/dist/entities/message-context.d.ts +617 -0
  145. package/dist/entities/message-context.js +20 -0
  146. package/dist/entities/message-read.d.ts +73 -0
  147. package/dist/entities/message-read.js +58 -0
  148. package/dist/entities/message-transfer.d.ts +379 -0
  149. package/dist/entities/message-transfer.js +83 -0
  150. package/dist/entities/message-write.d.ts +191 -0
  151. package/dist/entities/message-write.js +8 -0
  152. package/dist/entities/model-key.d.ts +14 -0
  153. package/dist/entities/model-key.js +10 -0
  154. package/dist/entities/poll.d.ts +40 -0
  155. package/dist/entities/poll.js +16 -0
  156. package/dist/entities/private-contact.d.ts +10 -0
  157. package/dist/entities/private-contact.js +4 -0
  158. package/dist/entities/reaction-change.d.ts +7 -0
  159. package/dist/entities/reaction-change.js +8 -0
  160. package/dist/entities/recipient.d.ts +29 -0
  161. package/dist/entities/recipient.js +11 -0
  162. package/dist/entities/recorded-run.d.ts +103 -0
  163. package/dist/entities/recorded-run.js +44 -0
  164. package/dist/entities/saved-search.d.ts +4 -0
  165. package/dist/entities/saved-search.js +2 -0
  166. package/dist/entities/send-attempt.d.ts +31 -0
  167. package/dist/entities/send-attempt.js +29 -0
  168. package/dist/entities/store-maintenance.d.ts +319 -0
  169. package/dist/entities/store-maintenance.js +145 -0
  170. package/dist/entities/store-records.d.ts +442 -0
  171. package/dist/entities/store-records.js +99 -0
  172. package/dist/entities/tag-change.d.ts +74 -0
  173. package/dist/entities/tag-change.js +31 -0
  174. package/dist/entities/task-pair.d.ts +31 -0
  175. package/dist/entities/task-pair.js +23 -0
  176. package/dist/entities/task-results.d.ts +524 -0
  177. package/dist/entities/task-results.js +142 -0
  178. package/dist/errors/reasons.js +1 -0
  179. package/dist/mcp/agent.d.ts +3 -1
  180. package/dist/mcp/agent.js +9 -6
  181. package/dist/mcp/bot/server.d.ts +2 -0
  182. package/dist/mcp/bot/server.js +1 -0
  183. package/dist/mcp/server.js +3 -1
  184. package/dist/mcp/tools/attachments.js +1 -0
  185. package/dist/meetings/cli/index.js +1 -1
  186. package/dist/meetings/cli/passage.js +1 -0
  187. package/dist/meetings/index.d.ts +2 -1
  188. package/dist/meetings/index.js +1 -0
  189. package/dist/meetings/links.d.ts +16 -0
  190. package/dist/meetings/mcp/index.d.ts +15 -15
  191. package/dist/meetings/read-answers.d.ts +167 -0
  192. package/dist/meetings/read-answers.js +183 -0
  193. package/dist/runs/run.d.ts +10 -2
  194. package/dist/runs/run.js +11 -3
  195. package/dist/sends/journal.d.ts +8 -1
  196. package/dist/sends/journal.js +9 -2
  197. package/dist/sends/permissions.d.ts +5 -1
  198. package/dist/sends/permissions.js +11 -1
  199. package/dist/sends/recipients.d.ts +8 -0
  200. package/dist/sends/recipients.js +9 -0
  201. package/dist/services/admin-statistics.js +3 -3
  202. package/dist/services/chat-documents.d.ts +1 -0
  203. package/dist/services/chat-documents.js +9 -3
  204. package/dist/services/chats.d.ts +1 -1
  205. package/dist/services/chats.js +5 -2
  206. package/dist/services/meeting-links.d.ts +2 -17
  207. package/dist/store/exports.d.ts +1 -1
  208. package/dist/store/exports.js +1 -1
  209. package/dist/store/messages.d.ts +3 -1
  210. package/dist/store/messages.js +2 -2
  211. package/dist/store/roster.d.ts +4 -1
  212. package/dist/store/roster.js +2 -2
  213. package/dist/store/sqlite/chats.d.ts +12 -0
  214. package/dist/store/sqlite/chats.js +1 -0
  215. package/dist/store/sqlite/document-links.d.ts +1 -1
  216. package/dist/store/sqlite/document-links.js +5 -5
  217. package/dist/store/sqlite/involvements.js +2 -1
  218. package/dist/store/sqlite/roster.d.ts +2 -1
  219. package/dist/store/sqlite/roster.js +3 -2
  220. package/package.json +1 -1
  221. package/parity.json +8 -66
@@ -6,13 +6,13 @@ import { grantsAuthority, LOCAL_WRITE } from "../effects.js";
6
6
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
7
7
  import { messengerContext } from "./context.js";
8
8
  /**
9
- * The owner's way out of a remembered wait or a hold on writes, once they know it is over. No MCP
10
- * tool on purpose: an agent that could lift a hold would send straight into a spam limit again.
9
+ * The owner's way out of a remembered wait or a hold on writes, once they know it is over. An agent gets it only
10
+ * under `mcp.authority`: one that could lift a hold would send straight into a spam limit again.
11
11
  */
12
12
  export const floodCommand = (messenger) => {
13
13
  const { app } = messenger;
14
14
  const name = messenger.name ?? app.command;
15
- return annotate(new Command("flood"), { mcp: false })
15
+ return new Command("flood")
16
16
  .description(`the waits ${name} asked this profile to keep, and a hold on its writes`)
17
17
  .addCommand(grantsAuthority(annotate(new Command("clear"), { effects: LOCAL_WRITE, examples: [`${app.command} personal flood clear`] }))
18
18
  .summary("clear a profile's stored rate-limit hold")
@@ -21,7 +21,7 @@ const count = (value) => {
21
21
  };
22
22
  export const gapsCommand = (messenger) => {
23
23
  const group = new Command("gaps").description("inspect recorded interior coverage gaps and explicitly fetch them");
24
- annotate(group.command("plan"), { effects: LOCAL_READ })
24
+ annotate(group.command("plan"), { output: { entity: "store_gap_plan" }, effects: LOCAL_READ })
25
25
  .description("local coverage plan; missing message ids alone do not imply missing history")
26
26
  .argument("<chat>", messenger.chatArgument)
27
27
  .action(async function (chat) {
@@ -29,7 +29,7 @@ export const gapsCommand = (messenger) => {
29
29
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.gaps.plan(chat)))));
30
30
  });
31
31
  const fetching = messenger.fetching ?? FETCHING;
32
- annotate(group.command("repair"), { effects: FETCH })
32
+ annotate(group.command("repair"), { output: { entity: "store_gap_repair" }, effects: FETCH })
33
33
  .description("fetch bounded interior gaps and recheck coverage; never delete unseen messages")
34
34
  .argument("<chat>", messenger.chatArgument)
35
35
  .option("--limit <n>", "total messages in this repair, 500 by default", count)
@@ -5,10 +5,12 @@ import { SendJournal, sendsPathFor } from "../../sends/journal.js";
5
5
  import { RecipientList, recipientsPathFor } from "../../sends/recipients.js";
6
6
  import { FETCH, grantsAuthority, LOCAL_READ, LOCAL_WRITE } from "../effects.js";
7
7
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
8
- import { pagedList } from "../list-query.js";
8
+ import { pagedList, typedQuery, withQuery } from "../list-query.js";
9
9
  import { withPaging } from "../paging.js";
10
10
  import { wholeList } from "./chat-answers.js";
11
11
  import { messengerContext } from "./context.js";
12
+ import { RECIPIENTS_QUERY, recipientFilter } from "./recipients-query.js";
13
+ import { SENDS_QUERY, sendFilter } from "./sends-query.js";
12
14
  /**
13
15
  * The chats this profile may send to. Off until the first `add`; `clear` deletes it, which turns it off again. It stops
14
16
  * a model that a message talked into writing somewhere, not an agent set on getting around it.
@@ -17,19 +19,26 @@ export const recipientsCommand = (messenger) => {
17
19
  const tool = `${messenger.app.command} personal`;
18
20
  const listFor = (profile, env) => new RecipientList(recipientsPathFor(messenger.app, profile, env), messenger.app.command);
19
21
  const command = new Command("recipients").description("the chats this profile may send to, when the list is on");
20
- annotate(command.command("list"), { effects: LOCAL_READ, examples: [`${tool} recipients list`] })
22
+ withQuery(annotate(command.command("list"), { examples: [`${tool} recipients list`], effects: LOCAL_READ }), RECIPIENTS_QUERY)
21
23
  .description("the chats on the list; empty and off until the first add")
22
- .action(async function () {
24
+ .action(async function (words) {
25
+ const filter = recipientFilter(words);
23
26
  const context = messengerContext(this, messenger);
24
27
  const { settings, renderer, env } = context;
25
- const chats = listFor(settings.profile, env).read();
26
- renderAnswer(this, context, wholeList("recipients list", (chats ?? []).map(snakeKeys)));
27
- if (!chats)
28
+ const found = listFor(settings.profile, env).list(filter);
29
+ renderAnswer(this, context, wholeList("recipients list", found.items.map(snakeKeys)));
30
+ if (found.state === "off")
28
31
  renderer.note("the recipient list is off — this profile may send to any chat");
29
- else if (chats.length === 0)
32
+ else if (found.state === "empty")
30
33
  renderer.note("the recipient list is on and empty — this profile may send nowhere");
34
+ else if (found.items.length === 0)
35
+ renderer.note("no recipients match the query");
31
36
  });
32
- grantsAuthority(annotate(command.command("add"), { effects: FETCH, examples: [`${tool} recipients add "Book club"`] }))
37
+ grantsAuthority(annotate(command.command("add"), {
38
+ examples: [`${tool} recipients add "Book club"`],
39
+ effects: FETCH,
40
+ output: { entity: "recipient_change", view: "added" },
41
+ }))
33
42
  .argument("<chat>", messenger.chatArgument)
34
43
  .description("allow sending to this chat; the first add turns the list on")
35
44
  .action(async function (chat) {
@@ -44,7 +53,11 @@ export const recipientsCommand = (messenger) => {
44
53
  });
45
54
  renderAnswer(this, context, itemOf({ id: found.id, title: found.title, added }));
46
55
  });
47
- grantsAuthority(annotate(command.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} recipients remove "Book club"`] }))
56
+ grantsAuthority(annotate(command.command("remove"), {
57
+ examples: [`${tool} recipients remove "Book club"`],
58
+ effects: LOCAL_WRITE,
59
+ output: { entity: "recipient_change", view: "removed" },
60
+ }))
48
61
  .argument("<chat>", "chat id, or the title as the list shows it")
49
62
  .description("stop allowing this chat; the list stays on")
50
63
  .action(async function (chat) {
@@ -55,7 +68,11 @@ export const recipientsCommand = (messenger) => {
55
68
  throw new CliError("not_found", `${chat.trim()} is not on the recipient list of ${settings.profile}`);
56
69
  renderAnswer(this, context, itemOf({ id: gone.id, title: gone.title, removed: true }));
57
70
  });
58
- grantsAuthority(annotate(command.command("clear"), { effects: LOCAL_WRITE, examples: [`${tool} recipients clear`] }))
71
+ grantsAuthority(annotate(command.command("clear"), {
72
+ examples: [`${tool} recipients clear`],
73
+ effects: LOCAL_WRITE,
74
+ output: { entity: "recipient_change", view: "off" },
75
+ }))
59
76
  .description("delete the list, which turns it off: this profile may send to any chat again")
60
77
  .action(async function () {
61
78
  const context = messengerContext(this, messenger);
@@ -65,16 +82,21 @@ export const recipientsCommand = (messenger) => {
65
82
  return command;
66
83
  };
67
84
  /** Every attempt to send from this profile, kept whatever `--record` says. Never the text. */
68
- export const sendsCommand = (messenger) => new Command("sends").description("every attempt to send from this profile — never the text").addCommand(withPaging(annotate(new Command("list"), { effects: LOCAL_READ }).description("attempts to send, newest first: sent, refused, failed, or not known")).action(async function () {
85
+ export const sendsCommand = (messenger) => new Command("sends").description("every attempt to send from this profile — never the text").addCommand(withQuery(withPaging(annotate(new Command("list"), {
86
+ effects: LOCAL_READ,
87
+ examples: [`${messenger.app.command} personal sends list --limit 10`],
88
+ }).description("attempts to send, newest first: sent, refused, failed, or not known")), SENDS_QUERY).action(async function (words) {
89
+ const filter = sendFilter(words);
69
90
  const context = messengerContext(this, messenger);
70
91
  const { settings, renderer, env } = context;
71
- const entries = new SendJournal(sendsPathFor(messenger.app, settings.profile, env)).entries().reverse();
92
+ const entries = new SendJournal(sendsPathFor(messenger.app, settings.profile, env)).entries(filter).reverse();
72
93
  const from = settings.all ? 0 : (settings.page - 1) * settings.limit;
73
94
  const shown = settings.all ? entries : entries.slice(from, from + settings.limit);
74
95
  renderAnswer(this, context, pagedList("sends list", settings, {
75
96
  items: shown.map(snakeKeys),
97
+ args: words.length ? { query: typedQuery(words) } : {},
76
98
  has_more: from + shown.length < entries.length,
77
99
  }));
78
100
  if (entries.length === 0)
79
- renderer.note(`profile ${settings.profile} has not tried to send anything`);
101
+ renderer.note(filter ? "no send attempts match the query" : `profile ${settings.profile} has not tried to send anything`);
80
102
  }));
@@ -13,6 +13,7 @@ const DEFAULT_LIMIT = 100;
13
13
  export const imagesCommand = (messenger) => {
14
14
  const images = new Command("images").description("search pictures by what they show, with a model on this machine");
15
15
  annotate(images.command("embed"), {
16
+ output: { entity: "image_embedding" },
16
17
  effects: FETCH,
17
18
  offline: false,
18
19
  examples: [`${messenger.app.command} images embed`, `${messenger.app.command} images embed --limit 500`],
@@ -13,6 +13,7 @@ import { addMediaArchiveTimers } from "./media-archive-timer-command.js";
13
13
  import { stopOnSignal } from "./patience.js";
14
14
  export const addMediaArchiveCommands = (archive, messenger) => {
15
15
  annotate(archive.command("download"), {
16
+ output: { entity: "media_archive" },
16
17
  effects: FETCH,
17
18
  examples: [`${messenger.app.command} attachments download ./media-archive`],
18
19
  })
@@ -54,6 +55,7 @@ export const addMediaArchiveCommands = (archive, messenger) => {
54
55
  }
55
56
  });
56
57
  annotate(archive.command("check"), {
58
+ output: { entity: "media_archive_check" },
57
59
  effects: LOCAL_READ,
58
60
  examples: [`${messenger.app.command} attachments check ./media-archive`],
59
61
  })
@@ -8,6 +8,7 @@ import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
8
8
  import { messengerContext, refuseLocalWrite } from "./context.js";
9
9
  export const addMediaArchiveMove = (attachments, messenger) => {
10
10
  annotate(attachments.command("move"), {
11
+ output: { entity: "media_archive_move" },
11
12
  effects: LOCAL_WRITE,
12
13
  examples: [
13
14
  `${messenger.app.command} attachments move ./media-archive ./older-images --before-time 2026-10-01T00:00:00Z --dry-run`,
@@ -14,8 +14,8 @@ export const addMediaArchiveTimers = (attachments, messenger) => {
14
14
  const archive = attachments.command("jobs").description("daily media archive timers for this profile");
15
15
  for (const action of ["install", "start", "stop", "status", "uninstall"]) {
16
16
  const command = annotate(archive.command(action), {
17
+ output: { entity: "media_archive_timer" },
17
18
  effects: action === "status" ? LOCAL_READ : LOCAL_WRITE,
18
- ...(action === "status" ? {} : { mcp: false }),
19
19
  examples: [`${messenger.app.command} attachments jobs ${action} ./media-archive`],
20
20
  })
21
21
  .description({
@@ -16,6 +16,7 @@ export const mentionsCommand = (messenger) => {
16
16
  const tool = messenger.app.command;
17
17
  const mentions = new Command("mentions").description("phones, e-mail, links, documents, names and addresses in stored messages");
18
18
  annotate(mentions.command("list"), {
19
+ output: { entity: "mention" },
19
20
  effects: LOCAL_READ,
20
21
  query: MENTION_QUERY,
21
22
  examples: [
@@ -55,6 +56,7 @@ export const mentionsCommand = (messenger) => {
55
56
  context.renderer.note("more remain — a larger --limit shows them all and moves the --new point");
56
57
  });
57
58
  annotate(mentions.command("domains"), {
59
+ output: { entity: "mention_domain" },
58
60
  effects: LOCAL_READ,
59
61
  query: MENTION_QUERY,
60
62
  examples: [`${tool} mentions domains`, `${tool} mentions domains status:found`],
@@ -77,7 +79,11 @@ export const mentionsCommand = (messenger) => {
77
79
  `${firstAt.slice(0, 10)}…${lastAt.slice(0, 10)}\n`)
78
80
  .join(""));
79
81
  });
80
- annotate(mentions.command("places"), { effects: LOCAL_READ, examples: [`${tool} mentions places`] })
82
+ annotate(mentions.command("places"), {
83
+ output: { entity: "place" },
84
+ effects: LOCAL_READ,
85
+ examples: [`${tool} mentions places`],
86
+ })
81
87
  .summary("confirmed street addresses, most mentioned first")
82
88
  .description("confirmed addresses, in every account, most mentioned first; `mentions confirm <id>` adds one")
83
89
  .option("--limit <n>", "how many to show", positiveCount("--limit"))
@@ -90,7 +96,11 @@ export const mentionsCommand = (messenger) => {
90
96
  has_more: places.hasMore,
91
97
  }), () => places.items.map(({ id, address, mentions }) => `${id} ${address} ${mentions} mentioned\n`).join(""));
92
98
  });
93
- annotate(mentions.command("show"), { effects: LOCAL_READ, examples: [`${tool} mentions show 12`] })
99
+ annotate(mentions.command("show"), {
100
+ output: { entity: "mention", view: "details" },
101
+ effects: LOCAL_READ,
102
+ examples: [`${tool} mentions show 12`],
103
+ })
94
104
  .description("one mention, the text around it, and how many other places hold its value")
95
105
  .argument("<id>", "the mention's id, from mentions list", positiveCount("<id>"))
96
106
  .action(async function (id) {
@@ -101,6 +111,7 @@ export const mentionsCommand = (messenger) => {
101
111
  (found.candidates.length > 0 ? `may be ${found.candidates.join(", ")}\n` : ""));
102
112
  });
103
113
  annotate(mentions.command("confirm"), {
114
+ output: { entity: "mention_decision" },
104
115
  effects: LOCAL_WRITE,
105
116
  examples: [
106
117
  `${tool} mentions confirm 12 person:7`,
@@ -123,6 +134,7 @@ export const mentionsCommand = (messenger) => {
123
134
  renderAnswer(this, context, itemOf(snakeKeys(decided)));
124
135
  });
125
136
  annotate(mentions.command("reject"), {
137
+ output: { entity: "mention_decision" },
126
138
  effects: LOCAL_WRITE,
127
139
  examples: [`${tool} mentions reject 12`],
128
140
  })
@@ -140,6 +152,7 @@ export const mentionsCommand = (messenger) => {
140
152
  renderAnswer(this, context, itemOf(snakeKeys(decided)));
141
153
  });
142
154
  destroysNothing(annotate(mentions.command("rebuild"), {
155
+ output: { entity: "mention_rebuild" },
143
156
  effects: LOCAL_WRITE,
144
157
  examples: [`${tool} mentions rebuild`, `${tool} mentions rebuild person-name`],
145
158
  }))
@@ -110,6 +110,7 @@ export const messagesCommand = (messenger) => {
110
110
  };
111
111
  messages.addCommand(sendCommand(messenger));
112
112
  annotate(messages.command("show"), {
113
+ output: { entity: "message" },
113
114
  examples: [`${messenger.app.command} personal messages show "Book club" 101`],
114
115
  effects: REMOTE_READ,
115
116
  })
@@ -122,6 +123,7 @@ export const messagesCommand = (messenger) => {
122
123
  await readWindow(this, chat, message, { before: 0, after: 0 });
123
124
  });
124
125
  threadOptions(annotate(messages.command("context"), {
126
+ output: { entity: "message_context" },
125
127
  examples: [`${messenger.app.command} personal messages context "Book club" 101 --before-n 3 --after-n 3`],
126
128
  effects: REMOTE_READ,
127
129
  }))
@@ -14,6 +14,7 @@ import { messengerContext } from "./context.js";
14
14
  export const deleteCommand = (messenger) => annotate(new Command("delete"), {
15
15
  examples: [`${messenger.app.command} personal messages delete "Book club" 101`],
16
16
  effects: REMOTE_WRITE,
17
+ output: { entity: "message_deletion" },
17
18
  })
18
19
  .description("delete messages for you only; with --for-everyone, for everyone in the chat")
19
20
  .argument("<chat>", messenger.chatArgument)
@@ -11,6 +11,7 @@ export const editCommand = (messenger) => {
11
11
  const edit = annotate(new Command("edit"), {
12
12
  examples: [`${messenger.app.command} personal messages edit "Book club" 101 "The meeting starts at 11."`],
13
13
  effects: REMOTE_WRITE,
14
+ output: { entity: "message_write" },
14
15
  })
15
16
  .description("change the text of your own message; the other side may have read it already")
16
17
  .argument("<chat>", messenger.chatArgument)
@@ -7,10 +7,12 @@ import { positiveCount } from "../paging.js";
7
7
  import { messengerContext } from "./context.js";
8
8
  import { evidenceAnswer } from "./message-answers.js";
9
9
  export const evidenceCommand = (messenger) => annotate(new Command("evidence"), {
10
+ output: { entity: "evidence_packet" },
10
11
  examples: [`${messenger.app.command} personal messages evidence "Book club" --limit 10`],
11
12
  effects: LOCAL_READ,
12
13
  })
13
- .description("a bounded evidence packet from stored messages, newest first")
14
+ .summary("a recent-chat evidence packet from stored messages")
15
+ .description("general chat context, newest first, with references, coverage and fingerprints for verification")
14
16
  .argument("<chat>", messenger.chatArgument)
15
17
  .option("--limit <n>", "how many, 1–100", positiveCount("--limit"))
16
18
  .option("--cursor <cursor>", "go on from where page.next says the last packet stopped")
@@ -9,6 +9,7 @@ export const forwardCommand = (messenger) => {
9
9
  const forward = annotate(new Command("forward"), {
10
10
  examples: [`${messenger.app.command} personal messages forward "Book club" 101 --to "Project room"`],
11
11
  effects: REMOTE_WRITE,
12
+ output: { entity: "message_write" },
12
13
  })
13
14
  .description("forward one message to another chat")
14
15
  .argument("<chat>", `the chat the message is in: ${messenger.chatArgument}`)
@@ -10,6 +10,7 @@ export const pinCommand = (messenger) => {
10
10
  const durations = messenger.pinDurations?.length ? messenger.pinDurations : undefined;
11
11
  const command = annotate(new Command("pin"), {
12
12
  effects: REMOTE_WRITE,
13
+ output: { entity: "message_pin" },
13
14
  examples: [
14
15
  `${tool} messages pin <chat> <message>`,
15
16
  ...(quiet ? [`${tool} messages pin <chat> <message> --notify`] : []),
@@ -38,6 +39,7 @@ export const pinCommand = (messenger) => {
38
39
  };
39
40
  export const unpinCommand = (messenger) => annotate(new Command("unpin"), {
40
41
  effects: REMOTE_WRITE,
42
+ output: { entity: "message_pin" },
41
43
  examples: [`${messenger.app.command} messages unpin <chat> <message>`],
42
44
  })
43
45
  .description("unpin a message in a chat")
@@ -53,6 +55,7 @@ export const starCommands = (messenger) => [true, false].map((starred) => {
53
55
  const name = starred ? "star" : "unstar";
54
56
  return annotate(new Command(name), {
55
57
  effects: REMOTE_WRITE,
58
+ output: { entity: "message_star" },
56
59
  examples: [`${messenger.app.command} messages ${name} <chat> <message>`],
57
60
  })
58
61
  .description(starred ? "star a message, so it is easy to find again; only you see the star" : "take the star off a message")
@@ -19,6 +19,7 @@ export const sendCommand = (messenger) => {
19
19
  const send = annotate(new Command("send"), {
20
20
  examples: [`${messenger.app.command} personal messages send "Book club" "The meeting starts at 10."`],
21
21
  effects: REMOTE_WRITE,
22
+ output: { entity: "message_write" },
22
23
  })
23
24
  .description("send a text message; without [text], the text is read from stdin")
24
25
  .argument("<chat>", messenger.chatArgument)
@@ -8,7 +8,11 @@ import { messengerContext, refuseLocalWrite } from "./context.js";
8
8
  export const metadataCommand = (messenger) => {
9
9
  const tool = messenger.app.command;
10
10
  const command = new Command("metadata").description("cached group/channel descriptions for local automatic tags");
11
- annotate(command.command("get"), { effects: LOCAL_READ, examples: [`${tool} metadata get --chat 7`] })
11
+ annotate(command.command("get"), {
12
+ output: { entity: "chat_metadata" },
13
+ effects: LOCAL_READ,
14
+ examples: [`${tool} metadata get --chat 7`],
15
+ })
12
16
  .description("a stored chat's cached description, as automatic tags read it")
13
17
  .requiredOption("--chat <chat>", "a stored chat")
14
18
  .action(async function () {
@@ -17,6 +21,7 @@ export const metadataCommand = (messenger) => {
17
21
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.metadata.get(chat)))));
18
22
  });
19
23
  annotate(command.command("refresh"), {
24
+ output: { entity: "chat_metadata_refresh" },
20
25
  effects: FETCH,
21
26
  examples: [`${tool} metadata refresh --chat 7 --chat 8`, `${tool} metadata refresh --only-missing --limit 20`],
22
27
  })
@@ -145,7 +145,11 @@ export const modelsCommand = (messenger) => {
145
145
  }, { unbounded: true });
146
146
  });
147
147
  const key = text.command("key").description("API keys for embedding and analysis providers");
148
- grantsAuthority(annotate(key.command("set"), { effects: LOCAL_WRITE, examples: [`${tool} models text key set openai`] }))
148
+ grantsAuthority(annotate(key.command("set"), {
149
+ effects: LOCAL_WRITE,
150
+ output: { entity: "model_key", view: "stored" },
151
+ examples: [`${tool} models text key set openai`],
152
+ }))
149
153
  .argument("<provider>", "openai, anthropic, or the host of a --base-url server that wants a key")
150
154
  .description("store a key, typed at a hidden prompt or piped on stdin — never as an argument")
151
155
  .action(async function (provider) {
@@ -159,7 +163,11 @@ export const modelsCommand = (messenger) => {
159
163
  renderAnswer(this, context, itemOf({ provider, stored: source }));
160
164
  });
161
165
  });
162
- grantsAuthority(annotate(key.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} models text key remove openai`] }))
166
+ grantsAuthority(annotate(key.command("remove"), {
167
+ effects: LOCAL_WRITE,
168
+ output: { entity: "model_key", view: "removed" },
169
+ examples: [`${tool} models text key remove openai`],
170
+ }))
163
171
  .argument("<provider>", "openai, anthropic, or a server's host")
164
172
  .description("forget a stored key")
165
173
  .action(async function (provider) {
@@ -23,6 +23,7 @@ export const pollsCommand = (messenger) => {
23
23
  ? "read a poll, vote in it, create one"
24
24
  : "read a poll, vote in it, close your own, create one");
25
25
  annotate(polls.command("show"), {
26
+ output: { entity: "poll" },
26
27
  effects: REMOTE_READ,
27
28
  examples: [`${messenger.app.command} personal polls show "Book club" 101`],
28
29
  })
@@ -67,6 +68,7 @@ export const pollsCommand = (messenger) => {
67
68
  }), lines(({ voted_at, person_id, name, answers }) => [voted_at, name ?? person_id, answers.join(",")].join(" ")));
68
69
  }));
69
70
  annotate(polls.command("vote"), {
71
+ output: { entity: "poll_change" },
70
72
  effects: REMOTE_WRITE,
71
73
  examples: [`${messenger.app.command} personal polls vote "Book club" 101 0`],
72
74
  })
@@ -95,6 +97,7 @@ export const pollsCommand = (messenger) => {
95
97
  });
96
98
  if (messenger.pollClose !== false)
97
99
  annotate(polls.command("close"), {
100
+ output: { entity: "poll_change" },
98
101
  effects: REMOTE_WRITE,
99
102
  examples: [`${messenger.app.command} personal polls close "Book club" 101`],
100
103
  })
@@ -107,6 +110,7 @@ export const pollsCommand = (messenger) => {
107
110
  renderAnswer(this, context, itemOf(snakeKeys(closed)));
108
111
  });
109
112
  const create = annotate(polls.command("create"), {
113
+ output: { entity: "message_write" },
110
114
  effects: REMOTE_WRITE,
111
115
  examples: [`${messenger.app.command} personal polls create "Book club" "Which day?" Monday Tuesday`],
112
116
  })
@@ -14,7 +14,7 @@ const NOTES_QUERY = unfilteredQuery("contact_note", contactNote);
14
14
  const everyNote = (command, items) => listOf(command, { items, limit: Math.max(1, items.length), has_more: false });
15
15
  export const privatePeopleCommands = (messenger) => {
16
16
  const alias = new Command("alias").description("a private local display name in the selected account");
17
- annotate(alias.command("set"), { effects: LOCAL_WRITE })
17
+ annotate(alias.command("set"), { effects: LOCAL_WRITE, output: { entity: "contact_alias" } })
18
18
  .description("your own name for a person in this account; kept on this machine, never sent")
19
19
  .argument("<person>", "the person's id, reference or known name")
20
20
  .argument("<alias>", "the private name to use for this person")
@@ -23,7 +23,7 @@ export const privatePeopleCommands = (messenger) => {
23
23
  refuseLocalWrite(context, messenger.app.command, "contacts.alias.set");
24
24
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.privatePeople.alias(person, value)))));
25
25
  });
26
- annotate(alias.command("rm"), { effects: LOCAL_WRITE })
26
+ annotate(alias.command("rm"), { effects: LOCAL_WRITE, output: { entity: "contact_alias" } })
27
27
  .description("drop your local name for a person; their own name shows again")
28
28
  .argument("<person>", "the person's id, reference or known name")
29
29
  .action(async function (person) {
@@ -78,7 +78,7 @@ export const privatePeopleCommands = (messenger) => {
78
78
  renderAnswer(this, context, itemOf(saved));
79
79
  });
80
80
  }
81
- annotate(notes.command("remove"), { effects: LOCAL_WRITE })
81
+ annotate(notes.command("remove"), { effects: LOCAL_WRITE, output: { entity: "contact_note_removal" } })
82
82
  .description("delete one private note on a person")
83
83
  .argument("<person>", "the person's id, reference or known name")
84
84
  .argument("<id>", "the note id from contacts notes list")
@@ -12,6 +12,7 @@ import { renderAnswer } from "../envelope.js";
12
12
  import { positiveCount } from "../paging.js";
13
13
  import { messengerContext } from "./context.js";
14
14
  import { syncOptions, syncRequest } from "./search-sync-options.js";
15
+ import { statisticsQuery, withStatisticsQuery } from "./statistics-query.js";
15
16
  import { evidenceAnswer, rankingAnswer, refuseJsonl } from "./stats-answers.js";
16
17
  const selectionOf = (value) => {
17
18
  if (Buffer.byteLength(value) > RANKING_SELECTION_BYTES)
@@ -35,28 +36,22 @@ export const rankingsTopCommand = (messenger, target) => {
35
36
  "engaging",
36
37
  ]))
37
38
  .option("--weights <json>", "the complete component weights; replaces preset weights")
38
- .addOption(new Option("--message-kind <kind>", "select proven all, posts or comments before ranking").choices([
39
- "all",
40
- "posts",
41
- "comments",
42
- ]))
43
- .option("--chat <chat>", `only this chat; ${messenger.chatArgument}`)
44
39
  .option("--source <accounts>", "every held account of this messenger; personal, bots or all")
45
40
  .option("--timezone <zone>", "the IANA timezone for dates and active days")
46
- .option("--exact", "bare words match exact forms rather than stems")
47
41
  .option("--limit <n>", "ranked rows, 1–100", positiveCount("--limit"))
48
42
  .option("--saved <name|id>", "run a saved query or ranking run; typed options replace stored options");
49
- if (target === "contacts")
50
- command.option("--min-messages <n>", "minimum selected messages per author; 1, or 5 for engaging", positiveCount("--min-messages"));
43
+ const report = target === "contacts" ? "contacts-top" : "messages-top";
44
+ withStatisticsQuery(command, report);
51
45
  command.action(async function (words) {
46
+ const filters = statisticsQuery(words, report);
52
47
  const context = messengerContext(this, messenger);
53
48
  refuseJsonl(context.format);
54
49
  const options = this.opts();
55
50
  const { saved, ...typed } = options;
56
- let params = { ...typed, ...(words.length ? { text: words.join(" ") } : {}) };
51
+ let params = { ...typed, ...filters };
57
52
  const found = await context.withServices(async (services) => {
58
53
  if (saved !== undefined)
59
- params = (await services.searches.resolve(saved, { ...params, text: words.join(" ") })).params;
54
+ params = (await services.searches.resolve(saved, { ...params, text: filters.text ?? "" })).params;
60
55
  if (params.target !== undefined && params.target !== target)
61
56
  throw new CliError("validation_error", "saved ranking belongs to a different target");
62
57
  if (params.newest || params.context)
@@ -8,6 +8,7 @@ export const reactionsCommand = (messenger) => {
8
8
  const reactions = new Command("reactions").description("react to messages");
9
9
  annotate(reactions.command("add"), {
10
10
  effects: REMOTE_WRITE,
11
+ output: { entity: "reaction_change" },
11
12
  examples: [`${messenger.app.command} personal reactions add "Book club" 101 👍`],
12
13
  })
13
14
  .description("put your reaction on a message; it replaces the one you had")
@@ -21,6 +22,7 @@ export const reactionsCommand = (messenger) => {
21
22
  });
22
23
  annotate(reactions.command("remove"), {
23
24
  effects: REMOTE_WRITE,
25
+ output: { entity: "reaction_change" },
24
26
  examples: [`${messenger.app.command} personal reactions remove "Book club" 101`],
25
27
  })
26
28
  .description("take your reaction off a message")
@@ -0,0 +1,5 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ import type { RecipientFilter } from "../../sends/recipients.js";
3
+ import { type QueryWords } from "../list-query.js";
4
+ export declare const RECIPIENTS_QUERY: QueryRegistry;
5
+ export declare const recipientFilter: (words: QueryWords) => RecipientFilter;
@@ -0,0 +1,18 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { recipient } from "../../entities/index.js";
3
+ import { queryTerms } from "../list-query.js";
4
+ export const RECIPIENTS_QUERY = {
5
+ entity: "recipient",
6
+ backend: "native",
7
+ fields: queryFieldsOf(recipient, {
8
+ native: ["id", "title"],
9
+ examples: { id: "id:7", title: 'title:"Book club"' },
10
+ }).map((field) => field.native
11
+ ? { ...field, operators: field.operators.filter((operator) => operator === "term" || operator === "phrase") }
12
+ : field),
13
+ };
14
+ export const recipientFilter = (words) => {
15
+ const terms = queryTerms(words, RECIPIENTS_QUERY, "recipients list");
16
+ const id = terms.get("id"), title = terms.get("title");
17
+ return { ...(id === undefined ? {} : { id }), ...(title === undefined ? {} : { title }) };
18
+ };
@@ -83,6 +83,7 @@ export const callsCommand = (messenger) => {
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
85
  annotate(new Command("delete"), {
86
+ output: { entity: "chat_removal" },
86
87
  effects: REMOTE_WRITE,
87
88
  mcp: false,
88
89
  examples: [`${messenger.app.command} personal chats delete "Book club"`],
@@ -95,6 +96,7 @@ export const chatDeletionCommands = (messenger) => [
95
96
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.admin.deleteChat(chat)))));
96
97
  }),
97
98
  annotate(new Command("clear"), {
99
+ output: { entity: "chat_removal" },
98
100
  effects: REMOTE_WRITE,
99
101
  mcp: false,
100
102
  examples: [`${messenger.app.command} personal chats clear "Book club"`],
@@ -110,6 +112,7 @@ export const chatDeletionCommands = (messenger) => [
110
112
  /** `chats mute` and `unmute` — the owner's own notifications for one chat; nobody in it is told. */
111
113
  export const muteCommands = (messenger) => [
112
114
  annotate(new Command("mute"), {
115
+ output: { entity: "chat_muting" },
113
116
  effects: REMOTE_WRITE,
114
117
  examples: [
115
118
  `${messenger.app.command} chats mute "Book club"`,
@@ -126,6 +129,7 @@ export const muteCommands = (messenger) => [
126
129
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.account.mute(chat, when)))));
127
130
  }),
128
131
  annotate(new Command("unmute"), {
132
+ output: { entity: "chat_muting" },
129
133
  effects: REMOTE_WRITE,
130
134
  examples: [`${messenger.app.command} chats unmute "Book club"`],
131
135
  })
@@ -136,7 +140,8 @@ export const muteCommands = (messenger) => [
136
140
  renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.account.mute(chat, null)))));
137
141
  }),
138
142
  ];
139
- const onChatCommand = (messenger, name, description, change) => annotate(new Command(name), {
143
+ const onChatCommand = (messenger, name, description, entity, change) => annotate(new Command(name), {
144
+ output: { entity },
140
145
  effects: REMOTE_WRITE,
141
146
  examples: [`${messenger.app.command} chats ${name} 123456789`],
142
147
  })
@@ -148,16 +153,16 @@ const onChatCommand = (messenger, name, description, change) => annotate(new Com
148
153
  });
149
154
  /** `chats archive` and `unarchive` — the messenger's archive of chats, not the local store. */
150
155
  export const archiveCommands = (messenger) => [
151
- onChatCommand(messenger, "archive", "move a chat into the archive; nobody in it is told", (services, chat) => services.account.archive(chat, true)),
152
- onChatCommand(messenger, "unarchive", "bring an archived chat back to the chat list", (services, chat) => services.account.archive(chat, false)),
156
+ onChatCommand(messenger, "archive", "move a chat into the archive; nobody in it is told", "chat_archive", (services, chat) => services.account.archive(chat, true)),
157
+ onChatCommand(messenger, "unarchive", "bring an archived chat back to the chat list", "chat_archive", (services, chat) => services.account.archive(chat, false)),
153
158
  ];
154
159
  /** `chats pin` and `unpin` — where the chat sits in the owner's list; `messages pin` pins a message for everyone. */
155
160
  export const chatPinCommands = (messenger) => [
156
- onChatCommand(messenger, "pin", "keep a chat at the top of your chat list; nobody in it is told", (services, chat) => services.account.pinChat(chat, true)),
157
- onChatCommand(messenger, "unpin", "let a pinned chat move down the chat list again", (services, chat) => services.account.pinChat(chat, false)),
161
+ onChatCommand(messenger, "pin", "keep a chat at the top of your chat list; nobody in it is told", "chat_pin", (services, chat) => services.account.pinChat(chat, true)),
162
+ onChatCommand(messenger, "unpin", "let a pinned chat move down the chat list again", "chat_pin", (services, chat) => services.account.pinChat(chat, false)),
158
163
  ];
159
164
  /** `chats mark-unread` — undone by `chats mark-read`, which the other side does see. */
160
- export const markUnreadCommand = (messenger) => onChatCommand(messenger, "mark-unread", "mark a chat unread on your own devices; nobody in it is told", (services, chat) => services.account.markUnread(chat));
165
+ export const markUnreadCommand = (messenger) => onChatCommand(messenger, "mark-unread", "mark a chat unread on your own devices; nobody in it is told", "chat_unread", (services, chat) => services.account.markUnread(chat));
161
166
  const AUDIENCES = ["everyone", "contacts", "nobody"];
162
167
  const audienceOf = (flag, value) => {
163
168
  if (!AUDIENCES.includes(value))
@@ -202,6 +207,7 @@ export const privacyCommand = (messenger) => {
202
207
  const keys = messenger.privacySettings ?? PRIVACY_SETTINGS;
203
208
  const privacy = new Command("privacy").description("who may find, call or add the account");
204
209
  annotate(privacy.command("show"), {
210
+ output: { entity: "account_privacy" },
205
211
  effects: REMOTE_READ,
206
212
  examples: [`${tool} account privacy show`, `${tool} account privacy show --json`],
207
213
  })
@@ -214,6 +220,7 @@ export const privacyCommand = (messenger) => {
214
220
  const audience = keys.find((key) => !SWITCHES.has(key));
215
221
  const toggle = keys.find((key) => SWITCHES.has(key));
216
222
  const set = annotate(new Command("set"), {
223
+ output: { entity: "account_privacy_change" },
217
224
  effects: REMOTE_WRITE,
218
225
  examples: [audience, toggle].flatMap((key) => (key === undefined ? [] : [exampleOf(tool, key)])),
219
226
  }).description("change who may find, call or add the account; the settings not named stay");