@wirecat/cli-core 0.398.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 (248) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/dist/analysis/consents.d.ts +11 -6
  3. package/dist/analysis/consents.js +5 -2
  4. package/dist/cli/analysis-choice.d.ts +1 -0
  5. package/dist/cli/analysis-choice.js +3 -0
  6. package/dist/cli/bot/admins.js +14 -4
  7. package/dist/cli/bot/command.js +38 -16
  8. package/dist/cli/bot/examples.d.ts +3 -0
  9. package/dist/cli/bot/examples.js +2 -0
  10. package/dist/cli/bot/mcp.js +6 -2
  11. package/dist/cli/bot/messages.js +17 -6
  12. package/dist/cli/bot/moderation.js +16 -4
  13. package/dist/cli/bot/people.js +16 -4
  14. package/dist/cli/bot/setup.js +27 -8
  15. package/dist/cli/bot/watch.js +7 -1
  16. package/dist/cli/config-command.js +20 -1
  17. package/dist/cli/context.d.ts +2 -0
  18. package/dist/cli/context.js +1 -1
  19. package/dist/cli/effects.d.ts +4 -1
  20. package/dist/cli/effects.js +10 -2
  21. package/dist/cli/embedding-choice.d.ts +2 -1
  22. package/dist/cli/embedding-choice.js +4 -1
  23. package/dist/cli/index.d.ts +1 -1
  24. package/dist/cli/index.js +1 -1
  25. package/dist/cli/json-fields.js +12 -1
  26. package/dist/cli/messenger/account-command.js +15 -6
  27. package/dist/cli/messenger/account-profile-query.d.ts +2 -0
  28. package/dist/cli/messenger/account-profile-query.js +13 -0
  29. package/dist/cli/messenger/account-sessions-command.js +1 -0
  30. package/dist/cli/messenger/admin-commands.js +30 -21
  31. package/dist/cli/messenger/admin-contacts-command.js +6 -3
  32. package/dist/cli/messenger/admin-folders-command.js +5 -5
  33. package/dist/cli/messenger/admin-moderation-command.js +4 -4
  34. package/dist/cli/messenger/admin-statistics-command.js +6 -14
  35. package/dist/cli/messenger/analysis-consents-query.d.ts +5 -0
  36. package/dist/cli/messenger/analysis-consents-query.js +20 -0
  37. package/dist/cli/messenger/archive-commands.js +3 -3
  38. package/dist/cli/messenger/attachments-command.js +3 -0
  39. package/dist/cli/messenger/backfill-command.js +5 -4
  40. package/dist/cli/messenger/catch-up-answers.d.ts +2 -1
  41. package/dist/cli/messenger/catch-up-answers.js +6 -2
  42. package/dist/cli/messenger/chat-queries.d.ts +1 -0
  43. package/dist/cli/messenger/chat-queries.js +12 -2
  44. package/dist/cli/messenger/chats-command.js +2 -2
  45. package/dist/cli/messenger/chats-documents-command.js +7 -5
  46. package/dist/cli/messenger/chats-members-command.js +7 -3
  47. package/dist/cli/messenger/chats-read-command.js +1 -1
  48. package/dist/cli/messenger/chats-stats-command.js +1 -1
  49. package/dist/cli/messenger/chats-tracking-command.js +14 -6
  50. package/dist/cli/messenger/contacts-command.js +11 -7
  51. package/dist/cli/messenger/context.d.ts +2 -0
  52. package/dist/cli/messenger/conversations-command.js +47 -29
  53. package/dist/cli/messenger/doctor-command.js +5 -1
  54. package/dist/cli/messenger/document-links-query.d.ts +2 -0
  55. package/dist/cli/messenger/document-links-query.js +9 -0
  56. package/dist/cli/messenger/download-command.js +1 -0
  57. package/dist/cli/messenger/flood-command.d.ts +2 -2
  58. package/dist/cli/messenger/flood-command.js +3 -3
  59. package/dist/cli/messenger/gaps-command.js +2 -2
  60. package/dist/cli/messenger/guard-commands.js +35 -13
  61. package/dist/cli/messenger/hearing-command.d.ts +1 -1
  62. package/dist/cli/messenger/hearing-command.js +1 -1
  63. package/dist/cli/messenger/images-command.js +1 -0
  64. package/dist/cli/messenger/inbox.js +6 -4
  65. package/dist/cli/messenger/media-archive-command.js +2 -0
  66. package/dist/cli/messenger/media-archive-move-command.js +1 -0
  67. package/dist/cli/messenger/media-archive-timer-command.js +1 -1
  68. package/dist/cli/messenger/mentions-command.js +15 -2
  69. package/dist/cli/messenger/messages-command.js +2 -0
  70. package/dist/cli/messenger/messages-delete-command.js +1 -0
  71. package/dist/cli/messenger/messages-edit-command.js +1 -0
  72. package/dist/cli/messenger/messages-evidence-command.js +3 -1
  73. package/dist/cli/messenger/messages-forward-command.js +1 -0
  74. package/dist/cli/messenger/messages-pin-command.js +3 -0
  75. package/dist/cli/messenger/messages-send-command.js +1 -0
  76. package/dist/cli/messenger/messages-stats-command.js +1 -1
  77. package/dist/cli/messenger/metadata-command.js +7 -2
  78. package/dist/cli/messenger/models-command.js +10 -2
  79. package/dist/cli/messenger/polls-command.js +4 -0
  80. package/dist/cli/messenger/private-people-command.js +8 -6
  81. package/dist/cli/messenger/rankings-command.js +6 -11
  82. package/dist/cli/messenger/reactions-command.js +2 -0
  83. package/dist/cli/messenger/recipients-query.d.ts +5 -0
  84. package/dist/cli/messenger/recipients-query.js +18 -0
  85. package/dist/cli/messenger/records-command.js +13 -6
  86. package/dist/cli/messenger/replies-edit-command.d.ts +1 -1
  87. package/dist/cli/messenger/replies-edit-command.js +13 -5
  88. package/dist/cli/messenger/retention-command.js +1 -1
  89. package/dist/cli/messenger/review.js +6 -4
  90. package/dist/cli/messenger/search-command.js +3 -1
  91. package/dist/cli/messenger/searches-command.js +6 -2
  92. package/dist/cli/messenger/sends-query.d.ts +5 -0
  93. package/dist/cli/messenger/sends-query.js +33 -0
  94. package/dist/cli/messenger/serve-command.js +1 -0
  95. package/dist/cli/messenger/serve-relay.d.ts +1 -0
  96. package/dist/cli/messenger/statistics-query.d.ts +14 -0
  97. package/dist/cli/messenger/statistics-query.js +143 -0
  98. package/dist/cli/messenger/stats-command.js +6 -6
  99. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  100. package/dist/cli/messenger/store-maintenance-command.js +10 -10
  101. package/dist/cli/messenger/tags-command.js +18 -6
  102. package/dist/cli/messenger/tasks-command.js +47 -8
  103. package/dist/cli/messenger/transcribe-command.js +2 -1
  104. package/dist/cli/messenger/watch-answers.d.ts +6 -0
  105. package/dist/cli/messenger/watch-answers.js +69 -0
  106. package/dist/cli/messenger/watch-command.d.ts +1 -1
  107. package/dist/cli/messenger/watch-command.js +56 -7
  108. package/dist/cli/program.js +5 -1
  109. package/dist/cli/runs/command.js +25 -38
  110. package/dist/cli/runs/query.d.ts +5 -0
  111. package/dist/cli/runs/query.js +32 -0
  112. package/dist/cli/runs/search-command.d.ts +4 -0
  113. package/dist/cli/runs/search-command.js +27 -0
  114. package/dist/cli/runs/search-query.d.ts +4 -0
  115. package/dist/cli/runs/search-query.js +50 -0
  116. package/dist/cli/runs/search.d.ts +2 -1
  117. package/dist/cli/runs/search.js +6 -6
  118. package/dist/domain/attachments.js +9 -1
  119. package/dist/domain/rankings-options.js +3 -3
  120. package/dist/entities/account-change.d.ts +89 -0
  121. package/dist/entities/account-change.js +20 -0
  122. package/dist/entities/account-profile.d.ts +7 -0
  123. package/dist/entities/account-profile.js +8 -0
  124. package/dist/entities/analysis-consent.d.ts +9 -0
  125. package/dist/entities/analysis-consent.js +4 -0
  126. package/dist/entities/attachment-results.d.ts +96 -0
  127. package/dist/entities/attachment-results.js +31 -0
  128. package/dist/entities/batch-result.d.ts +27 -0
  129. package/dist/entities/batch-result.js +18 -0
  130. package/dist/entities/chat-admin.d.ts +234 -0
  131. package/dist/entities/chat-admin.js +84 -0
  132. package/dist/entities/chat-change.d.ts +25 -0
  133. package/dist/entities/chat-change.js +11 -0
  134. package/dist/entities/chat-metadata.d.ts +34 -0
  135. package/dist/entities/chat-metadata.js +15 -0
  136. package/dist/entities/chat-moderation.d.ts +44 -0
  137. package/dist/entities/chat-moderation.js +37 -0
  138. package/dist/entities/chat-tracking.d.ts +26 -0
  139. package/dist/entities/chat-tracking.js +23 -0
  140. package/dist/entities/contact-activity.d.ts +52 -0
  141. package/dist/entities/contact-activity.js +42 -0
  142. package/dist/entities/contact-context.d.ts +1568 -0
  143. package/dist/entities/contact-context.js +191 -0
  144. package/dist/entities/contact-write.d.ts +22 -0
  145. package/dist/entities/contact-write.js +6 -0
  146. package/dist/entities/conversation-preparation.d.ts +63 -0
  147. package/dist/entities/conversation-preparation.js +23 -0
  148. package/dist/entities/conversation-results.d.ts +567 -0
  149. package/dist/entities/conversation-results.js +94 -0
  150. package/dist/entities/document-link.d.ts +13 -0
  151. package/dist/entities/document-link.js +11 -0
  152. package/dist/entities/extraction-report.d.ts +95 -0
  153. package/dist/entities/extraction-report.js +34 -0
  154. package/dist/entities/image-embedding.d.ts +9 -0
  155. package/dist/entities/image-embedding.js +10 -0
  156. package/dist/entities/index.d.ts +8006 -1928
  157. package/dist/entities/index.js +174 -3
  158. package/dist/entities/listener.d.ts +2150 -0
  159. package/dist/entities/listener.js +46 -0
  160. package/dist/entities/media-archive.d.ts +43 -0
  161. package/dist/entities/media-archive.js +32 -0
  162. package/dist/entities/meeting-task-proposal.d.ts +14 -0
  163. package/dist/entities/meeting-task-proposal.js +19 -0
  164. package/dist/entities/meeting.d.ts +1 -1
  165. package/dist/entities/meeting.js +1 -1
  166. package/dist/entities/mention.d.ts +63 -0
  167. package/dist/entities/mention.js +44 -0
  168. package/dist/entities/message-change.d.ts +19 -0
  169. package/dist/entities/message-change.js +6 -0
  170. package/dist/entities/message-context.d.ts +617 -0
  171. package/dist/entities/message-context.js +20 -0
  172. package/dist/entities/message-read.d.ts +73 -0
  173. package/dist/entities/message-read.js +58 -0
  174. package/dist/entities/message-transfer.d.ts +379 -0
  175. package/dist/entities/message-transfer.js +83 -0
  176. package/dist/entities/message-write.d.ts +191 -0
  177. package/dist/entities/message-write.js +8 -0
  178. package/dist/entities/model-key.d.ts +14 -0
  179. package/dist/entities/model-key.js +10 -0
  180. package/dist/entities/poll.d.ts +40 -0
  181. package/dist/entities/poll.js +16 -0
  182. package/dist/entities/private-contact.d.ts +10 -0
  183. package/dist/entities/private-contact.js +4 -0
  184. package/dist/entities/reaction-change.d.ts +7 -0
  185. package/dist/entities/reaction-change.js +8 -0
  186. package/dist/entities/recipient.d.ts +29 -0
  187. package/dist/entities/recipient.js +11 -0
  188. package/dist/entities/recorded-run.d.ts +103 -0
  189. package/dist/entities/recorded-run.js +44 -0
  190. package/dist/entities/saved-search.d.ts +4 -0
  191. package/dist/entities/saved-search.js +2 -0
  192. package/dist/entities/send-attempt.d.ts +31 -0
  193. package/dist/entities/send-attempt.js +29 -0
  194. package/dist/entities/store-maintenance.d.ts +319 -0
  195. package/dist/entities/store-maintenance.js +145 -0
  196. package/dist/entities/store-records.d.ts +442 -0
  197. package/dist/entities/store-records.js +99 -0
  198. package/dist/entities/tag-change.d.ts +74 -0
  199. package/dist/entities/tag-change.js +31 -0
  200. package/dist/entities/task-pair.d.ts +31 -0
  201. package/dist/entities/task-pair.js +23 -0
  202. package/dist/entities/task-results.d.ts +524 -0
  203. package/dist/entities/task-results.js +142 -0
  204. package/dist/errors/reasons.js +1 -0
  205. package/dist/mcp/agent.d.ts +3 -1
  206. package/dist/mcp/agent.js +9 -6
  207. package/dist/mcp/bot/server.d.ts +2 -0
  208. package/dist/mcp/bot/server.js +1 -0
  209. package/dist/mcp/prompts.js +8 -2
  210. package/dist/mcp/server.js +3 -1
  211. package/dist/mcp/tools/attachments.js +1 -0
  212. package/dist/meetings/cli/index.js +1 -1
  213. package/dist/meetings/cli/passage.js +1 -0
  214. package/dist/meetings/index.d.ts +2 -1
  215. package/dist/meetings/index.js +1 -0
  216. package/dist/meetings/links.d.ts +16 -0
  217. package/dist/meetings/mcp/index.d.ts +15 -15
  218. package/dist/meetings/read-answers.d.ts +167 -0
  219. package/dist/meetings/read-answers.js +183 -0
  220. package/dist/runs/run.d.ts +10 -2
  221. package/dist/runs/run.js +11 -3
  222. package/dist/sends/journal.d.ts +8 -1
  223. package/dist/sends/journal.js +9 -2
  224. package/dist/sends/permissions.d.ts +5 -1
  225. package/dist/sends/permissions.js +11 -1
  226. package/dist/sends/recipients.d.ts +8 -0
  227. package/dist/sends/recipients.js +9 -0
  228. package/dist/services/admin-statistics.js +3 -3
  229. package/dist/services/chat-documents.d.ts +1 -0
  230. package/dist/services/chat-documents.js +9 -3
  231. package/dist/services/chats.d.ts +1 -1
  232. package/dist/services/chats.js +5 -2
  233. package/dist/services/meeting-links.d.ts +2 -17
  234. package/dist/store/exports.d.ts +1 -1
  235. package/dist/store/exports.js +1 -1
  236. package/dist/store/messages.d.ts +3 -1
  237. package/dist/store/messages.js +2 -2
  238. package/dist/store/roster.d.ts +4 -1
  239. package/dist/store/roster.js +2 -2
  240. package/dist/store/sqlite/chats.d.ts +12 -0
  241. package/dist/store/sqlite/chats.js +1 -0
  242. package/dist/store/sqlite/document-links.d.ts +1 -1
  243. package/dist/store/sqlite/document-links.js +5 -5
  244. package/dist/store/sqlite/involvements.js +2 -1
  245. package/dist/store/sqlite/roster.d.ts +2 -1
  246. package/dist/store/sqlite/roster.js +3 -2
  247. package/package.json +3 -3
  248. package/parity.json +76 -108
@@ -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)
@@ -12,7 +12,7 @@ export const messagesStatsCommand = (messenger) => syncOptions(annotate(new Comm
12
12
  .summary("count stored messages by chat, sender, day or hour")
13
13
  .description(`how many stored messages match, by chat, sender, day or hour — the local store only${syncsFirst(messenger) ? "; optionally fetches new messages with --sync-first" : ""}`)
14
14
  .argument("[query...]", "a strict Lucene query, as for search messages; none counts every stored message; with --saved, more words AND-ed to it")
15
- .option("--by <chat|sender|day|hour>", "what to count by (default: chat)", groupingOf)
15
+ .option("--by <group>", "count by chat, sender, day or hour (default: chat)", groupingOf)
16
16
  .option("--chat <chat>", `only this chat — the same as chat: in the query; ${messenger.chatArgument}`)
17
17
  .option("--source <accounts>", "every account of this messenger held in the store; personal, bots or all — the same as in: in the query")
18
18
  .option("--limit <n>", "how many rows", positiveCount("--limit"))
@@ -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,13 +21,14 @@ 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
  })
23
28
  .description("fetch group and channel descriptions again and cache them")
24
29
  .option("--chat <chat>", "stored group/channel; repeat for several", (value, previous) => [...previous, value], [])
25
30
  .option("--only-missing", "only chats with no metadata yet; without --chat, every stored group/channel")
26
- .option("--limit <number>", "process at most 1–500 chats", positiveCount("--limit"), 50)
31
+ .option("--limit <n>", "process at most 1–500 chats", positiveCount("--limit"), 50)
27
32
  .action(async function () {
28
33
  const context = messengerContext(this, messenger);
29
34
  refuseLocalWrite(context, messenger.app.command, "metadata.refresh");
@@ -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) {
@@ -54,10 +54,11 @@ export const privatePeopleCommands = (messenger) => {
54
54
  output: { entity: "contact_note" },
55
55
  }), { name: "text", description: "the note's text" })
56
56
  .description(name === "add"
57
- ? "add a private note on a person from stdin or --file; never sent"
57
+ ? "add a private note on a person from stdin or --text-file; never sent"
58
58
  : "replace a note's text; --revision must match the one you read")
59
59
  .argument("<person>", "the person's id, reference or known name")
60
- .addOption(ownerOnlyOption("--file <path>", "read note text from a file; omitted or - reads stdin"));
60
+ .addOption(ownerOnlyOption("--text-file <path>", "read note text from a file; omitted or - reads stdin").conflicts("file"))
61
+ .addOption(ownerOnlyOption("--file <path>", "compatibility alias for --text-file").hideHelp().conflicts("textFile"));
61
62
  if (name === "edit")
62
63
  command
63
64
  .argument("<id>", "the note id from contacts notes list")
@@ -65,7 +66,8 @@ export const privatePeopleCommands = (messenger) => {
65
66
  command.action(async function (person, id) {
66
67
  const context = messengerContext(this, messenger);
67
68
  refuseLocalWrite(context, messenger.app.command, `contacts.notes.${name}`);
68
- const { file, revision } = this.opts();
69
+ const { textFile, file: legacyFile, revision, } = this.opts();
70
+ const file = textFile ?? legacyFile;
69
71
  const text = file === undefined || file === "-" ? await readAll(context.stdin) : await readFile(file, "utf8");
70
72
  const version = Number(revision);
71
73
  if (name === "edit" && (!Number.isSafeInteger(version) || version < 1))
@@ -76,7 +78,7 @@ export const privatePeopleCommands = (messenger) => {
76
78
  renderAnswer(this, context, itemOf(saved));
77
79
  });
78
80
  }
79
- annotate(notes.command("remove"), { effects: LOCAL_WRITE })
81
+ annotate(notes.command("remove"), { effects: LOCAL_WRITE, output: { entity: "contact_note_removal" } })
80
82
  .description("delete one private note on a person")
81
83
  .argument("<person>", "the person's id, reference or known name")
82
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");
@@ -1,5 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import { type Messenger } from "./context.js";
3
- export declare const REPLY_EDIT_OPTIONS: readonly [readonly ["--do <actions>", "actions: reply, task, or both, comma-separated"], readonly ["--kinds <kinds>", "chat kinds: dialog, group; comma-separated, empty for any"], readonly ["--allow-chats <ids>", "only these chat ids, comma-separated; empty for any"], readonly ["--deny-chats <ids>", "leave these chat ids out, comma-separated; empty clears"], readonly ["--words <words>", "match any of these whole words, comma-separated; empty clears"], readonly ["--question", "match only questions"], readonly ["--no-question", "do not require a question"], readonly ["--mentions-me", "require a mention of you or a reply to you"], readonly ["--no-mentions-me", "do not require a mention of you or a reply to you"], readonly ["--allow-people <ids>", "only these sender ids, comma-separated; empty for any"], readonly ["--deny-people <ids>", "leave these sender ids out, comma-separated; empty clears"], readonly ["--contacts-only", "match only contacts"], readonly ["--no-contacts-only", "do not require a contact"], readonly ["--template <text>", "the reply template"], readonly ["--model <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"], readonly ["--as-reply", "send as a reply to the matched message"], readonly ["--no-as-reply", "send without linking to the matched message"], readonly ["--per-chat <limit>", "at most this many per chat, such as 1/12h"], readonly ["--per-person <limit>", "at most this many per person, such as 1/1d"], readonly ["--outside <hours>", "answer outside this 24-hour window, such as 09:00-19:00"], readonly ["--days <days>", "days of the working window, such as mon-fri or sat,sun"], readonly ["--timezone <zone>", "the IANA timezone for the working window"], readonly ["--no-hours", "clear the working window"]];
3
+ export declare const REPLY_EDIT_OPTIONS: readonly [readonly ["--do <actions>", "actions: reply, task, or both, comma-separated"], readonly ["--kinds <kinds>", "chat kinds: dialog, group; comma-separated, empty for any"], readonly ["--allow-chats <ids>", "only these chat ids, comma-separated; empty for any"], readonly ["--deny-chats <ids>", "leave these chat ids out, comma-separated; empty clears"], readonly ["--words <words>", "match any of these whole words, comma-separated; empty clears"], readonly ["--question", "match only questions"], readonly ["--no-question", "do not require a question"], readonly ["--mentions-me", "require a mention of you or a reply to you"], readonly ["--no-mentions-me", "do not require a mention of you or a reply to you"], readonly ["--allow-people <ids>", "only these sender ids, comma-separated; empty for any"], readonly ["--deny-people <ids>", "leave these sender ids out, comma-separated; empty clears"], readonly ["--contacts-only", "match only contacts"], readonly ["--no-contacts-only", "do not require a contact"], readonly ["--template <text>", "the reply template"], readonly ["--template-mode <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"], readonly ["--as-reply", "send as a reply to the matched message"], readonly ["--no-as-reply", "send without linking to the matched message"], readonly ["--per-chat <limit>", "at most this many per chat, such as 1/12h"], readonly ["--per-person <limit>", "at most this many per person, such as 1/1d"], readonly ["--outside <hours>", "answer outside this 24-hour window, such as 09:00-19:00"], readonly ["--days <days>", "days of the working window, such as mon-fri or sat,sun"], readonly ["--timezone <zone>", "the IANA timezone for the working window"], readonly ["--no-hours", "clear the working window"]];
4
4
  export declare const REPLY_AUDIENCE_OPTIONS: readonly [readonly ["--reply <mode>", "answer all or only listed senders and chats: all, listed"], readonly ["--allow-people <ids>", "replace allowed sender ids, comma-separated; empty clears"], readonly ["--allow-chats <ids>", "replace allowed chat ids, comma-separated; empty clears"], readonly ["--deny-people <ids>", "replace denied sender ids, comma-separated; empty clears; deny wins"], readonly ["--deny-chats <ids>", "replace denied chat ids, comma-separated; empty clears; deny wins"]];
5
5
  export declare const addReplyEditors: (replies: Command, messenger: Messenger) => void;
@@ -1,5 +1,5 @@
1
1
  import { annotate } from "@wirecat/cli-base/commands";
2
- import { Command } from "commander";
2
+ import { Command, Option } from "commander";
3
3
  import { addRule, editAudience, editRule, switchRule } from "../../replies/edit.js";
4
4
  import { audienceWarnings, repliesPathFor } from "../../replies/rules.js";
5
5
  import { grantsAuthority, LOCAL_WRITE } from "../effects.js";
@@ -20,7 +20,7 @@ export const REPLY_EDIT_OPTIONS = [
20
20
  ["--contacts-only", "match only contacts"],
21
21
  ["--no-contacts-only", "do not require a contact"],
22
22
  ["--template <text>", "the reply template"],
23
- ["--model <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"],
23
+ ["--template-mode <mode>", "legacy template mode: fill-only or may-reword; use ai blocks instead"],
24
24
  ["--as-reply", "send as a reply to the matched message"],
25
25
  ["--no-as-reply", "send without linking to the matched message"],
26
26
  ["--per-chat <limit>", "at most this many per chat, such as 1/12h"],
@@ -59,8 +59,13 @@ export const addReplyEditors = (replies, messenger) => {
59
59
  const edit = grantsAuthority(annotate(new Command("edit"), { effects: LOCAL_WRITE }))
60
60
  .description("change only the named fields of a reply rule; lists replace the whole list")
61
61
  .argument("<id>", "the rule's id");
62
- for (const [flag, help] of REPLY_EDIT_OPTIONS)
63
- edit.option(flag, help);
62
+ for (const [flag, help] of REPLY_EDIT_OPTIONS) {
63
+ const option = new Option(flag, help);
64
+ if (flag === "--template-mode <mode>")
65
+ option.conflicts("model");
66
+ edit.addOption(option);
67
+ }
68
+ edit.addOption(new Option("--model <mode>", "compatibility alias for --template-mode").hideHelp().conflicts("templateMode"));
64
69
  // Commander gives negated-only flags a true default; omission must preserve the saved field.
65
70
  edit.action(function (id) {
66
71
  const options = this.opts();
@@ -68,7 +73,10 @@ export const addReplyEditors = (replies, messenger) => {
68
73
  delete options.hours;
69
74
  const context = messengerContext(this, messenger);
70
75
  const { settings, env } = context;
71
- renderItem(this, context, editRule(repliesPathFor(messenger.app, settings.profile, env), messenger.provider, id, options));
76
+ renderItem(this, context, editRule(repliesPathFor(messenger.app, settings.profile, env), messenger.provider, id, {
77
+ ...options,
78
+ model: options.templateMode ?? options.model,
79
+ }));
72
80
  });
73
81
  replies.addCommand(edit);
74
82
  const audience = grantsAuthority(annotate(new Command("audience"), { effects: LOCAL_WRITE })).description("show the reply audience, who the rules may answer, or replace its named fields; a new file answers everyone a rule matches");
@@ -13,7 +13,7 @@ export const retentionCommand = (messenger) => annotate(new Command("retention")
13
13
  .option("--until-time <time>", "joining period ends at this time; now by default")
14
14
  .option("--checkpoints <durations>", "up to 10 increasing joining ages, comma separated; 1d,7d,30d by default")
15
15
  .option("--within <duration>", "activity and early departure window after joining; 7d by default")
16
- .addOption(new Option("--by <day|week>", "group joining dates by calendar day or Monday week").choices(["day", "week"]))
16
+ .addOption(new Option("--by <group>", "group joining dates by calendar day or Monday week").choices(["day", "week"]))
17
17
  .option("--timezone <zone>", "IANA timezone for joining cohorts")
18
18
  .option("--limit <n>", "cohorts and member evidence, 1–100", positiveCount("--limit"))
19
19
  .action(async function (chat) {
@@ -1,6 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
- import { Command } from "commander";
3
+ import { Command, Option } from "commander";
4
4
  import { parseDuration } from "../../domain/duration.js";
5
5
  import { renderMessages } from "../../render/messages.js";
6
6
  import { CHAT_KINDS } from "../../services/chats.js";
@@ -34,7 +34,7 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
34
34
  .description("every message, yours too, in chats that changed since a point — for reviewing who owes what")
35
35
  .addHelpText("after", queryHelp(REVIEW_QUERY))
36
36
  .argument("[query...]", `where the last review ended and which chat: date>2h, chat:"Book club"; ${REVIEW_DAYS} days ago if no date`)
37
- .option("--kind <kinds>", `only chats of these kinds, comma-separated: ${CHAT_KINDS.join(", ")}`)
37
+ .addOption(new Option("--kind <kinds>", `compatibility spelling for kind:; ${CHAT_KINDS.join(", ")}`).hideHelp())
38
38
  .option("--unanswered [duration]", `only questions to you or a group's admins that nobody answered, asked at least this long ago — 4h, 1d; ${UNANSWERED_HOURS}h if not given`)
39
39
  .option("--all", "muted and archived chats too — left out unless they mention you or reply to you")
40
40
  .option(...TRANSCRIBE_OPTION)
@@ -47,7 +47,9 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
47
47
  const hearWith = modelWith(options.transcribe, options.model);
48
48
  const context = messengerContext(this, messenger);
49
49
  const { settings, renderer, format, env } = context;
50
- const { since: from, chat, meta } = catchUpQuery(query, REVIEW_QUERY, "review");
50
+ const { since: from, chat, meta, kinds: queriedKinds } = catchUpQuery(query, REVIEW_QUERY, "review");
51
+ if (options.kind !== undefined && queriedKinds !== undefined)
52
+ throw new CliError("validation_error", "give kind: or its legacy --kind spelling, not both");
51
53
  if (options.new && (from !== undefined || options.unanswered !== undefined)) {
52
54
  // An open question stays open: moving past it would drop it from the next review.
53
55
  throw new CliError("validation_error", "--new keeps its own point — not with a date or --unanswered");
@@ -62,7 +64,7 @@ export const reviewCommand = (messenger) => annotate(new Command("review"), {
62
64
  : undefined;
63
65
  const since = from ?? saved?.first ?? reviewStart();
64
66
  const hours = options.unanswered === undefined ? undefined : unansweredHours(options.unanswered);
65
- const kinds = options.kind === undefined ? undefined : kindsOf(options.kind);
67
+ const kinds = queriedKinds ?? (options.kind === undefined ? undefined : kindsOf(options.kind));
66
68
  let hearing;
67
69
  const transcribe = options.transcribe === true;
68
70
  const { found, reader } = await context.withServices(async (services, connect) => {
@@ -6,6 +6,7 @@ import { searchNotesOf } from "../../services/notes-search.js";
6
6
  import { LOCAL_READ, REMOTE_READ } from "../effects.js";
7
7
  import { renderAnswer } from "../envelope.js";
8
8
  import { positiveCount } from "../paging.js";
9
+ import { runsSearchCommand } from "../runs/search-command.js";
9
10
  import { commandAt } from "../steps.js";
10
11
  import { messengerContext } from "./context.js";
11
12
  import { conversationsSearchCommand } from "./conversations-command.js";
@@ -159,7 +160,8 @@ export const searchCommand = (messenger, { topics = false } = {}) => {
159
160
  .addCommand(allCommand(messenger))
160
161
  .addCommand(messagesSearchCommand(messenger))
161
162
  .addCommand(notesCommand(messenger))
162
- .addCommand(conversationsSearchCommand(messenger));
163
+ .addCommand(conversationsSearchCommand(messenger))
164
+ .addCommand(runsSearchCommand(messenger.app));
163
165
  if (topics)
164
166
  search.addCommand(topicsSearchCommand(messenger));
165
167
  for (const [path, args] of Object.entries({
@@ -45,7 +45,7 @@ export const searchesCommand = (messenger) => {
45
45
  .option("--exact", "bare words and quotes match their exact form only, as exact:word does; text: still matches every form")
46
46
  .option("--context <n>", "messages before and after each hit", wholeCount)
47
47
  .option("--timezone <zone>", "the IANA timezone for calendar date boundaries")
48
- .option("--by <chat|sender|day|hour>", "what stats messages show --saved counts by", groupingOf)
48
+ .option("--by <group>", "count by chat, sender, day or hour for stats messages show --saved", groupingOf)
49
49
  .option("--selection <json>", "save the resolved parent ranking query and options from a drilldown")
50
50
  .option("--replace", "overwrite a saved search of the same name")
51
51
  .action(async function (name, words) {
@@ -124,7 +124,11 @@ export const searchesCommand = (messenger) => {
124
124
  const deleted = await context.withServices((services) => services.searches.delete(reference));
125
125
  renderAnswer(this, context, itemOf(savedSearchItem(deleted)), () => `deleted ${deleted.name ?? `run ${deleted.id}`}\n`);
126
126
  });
127
- annotate(searches.command("clear"), { effects: LOCAL_WRITE, examples: [`${tool} searches clear`] })
127
+ annotate(searches.command("clear"), {
128
+ output: { entity: "search_history_clear" },
129
+ effects: LOCAL_WRITE,
130
+ examples: [`${tool} searches clear`],
131
+ })
128
132
  .description("empty the history; saved searches stay")
129
133
  .action(async function () {
130
134
  const context = messengerContext(this, messenger);
@@ -0,0 +1,5 @@
1
+ import { type QueryRegistry } from "@wirecat/cli-base/query";
2
+ import type { SendFilter } from "../../sends/journal.js";
3
+ import { type QueryWords } from "../list-query.js";
4
+ export declare const SENDS_QUERY: QueryRegistry;
5
+ export declare const sendFilter: (words: QueryWords) => SendFilter | undefined;
@@ -0,0 +1,33 @@
1
+ import { queryFieldsOf } from "@wirecat/cli-base/query";
2
+ import { sendAttempt } from "../../entities/index.js";
3
+ import { queryTerms } from "../list-query.js";
4
+ export const SENDS_QUERY = {
5
+ entity: "send_attempt",
6
+ backend: "native",
7
+ fields: queryFieldsOf(sendAttempt, {
8
+ native: ["outcome", "chat_id", "operation_id", "error_code", "key"],
9
+ examples: {
10
+ outcome: "outcome:failed",
11
+ chat_id: "chat_id:7",
12
+ operation_id: "operation_id:op-1",
13
+ error_code: "error_code:timeout",
14
+ key: "key:messages.send",
15
+ },
16
+ }).map((field) => field.native
17
+ ? { ...field, operators: field.operators.filter((operator) => operator === "term" || operator === "phrase") }
18
+ : field),
19
+ };
20
+ export const sendFilter = (words) => {
21
+ const terms = queryTerms(words, SENDS_QUERY, "sends list");
22
+ if (terms.size === 0)
23
+ return undefined;
24
+ const outcome = sendAttempt.entries.outcome.options.find((value) => value === terms.get("outcome"));
25
+ const chatId = terms.get("chat_id"), operationId = terms.get("operation_id"), errorCode = terms.get("error_code"), key = terms.get("key");
26
+ return {
27
+ ...(outcome === undefined ? {} : { outcome }),
28
+ ...(chatId === undefined ? {} : { chatId }),
29
+ ...(operationId === undefined ? {} : { operationId }),
30
+ ...(errorCode === undefined ? {} : { errorCode }),
31
+ ...(key === undefined ? {} : { key }),
32
+ };
33
+ };
@@ -31,6 +31,7 @@ export { lockPath, readLock, servingProfiles } from "../../background/lock.js";
31
31
  */
32
32
  export const serveCommand = (messenger) => {
33
33
  const command = annotate(new Command("serve"), {
34
+ output: { entity: "serve_summary" },
34
35
  effects: REMOTE_WRITE,
35
36
  lifetime: "persistent",
36
37
  examples: [`${messenger.app.command} personal serve`],
@@ -78,6 +78,7 @@ export declare const serveRelay: (command: Command, messenger: Messenger) => {
78
78
  inviteLinkRevoke?: boolean;
79
79
  inviteLinkUpdate?: boolean;
80
80
  joinRequestsAll?: boolean;
81
+ joinRequestsByLink?: boolean;
81
82
  channelComments?: boolean;
82
83
  addsWithHistory?: boolean;
83
84
  adminRights?: readonly import("../../domain/models.js").AdminRight[];
@@ -0,0 +1,14 @@
1
+ import type { Command } from "commander";
2
+ import { type QueryWords } from "../list-query.js";
3
+ export type StatisticsQuery = "contacts-top" | "messages-top" | "discussion" | "unanswered" | "responses";
4
+ export declare const withStatisticsQuery: (command: Command, report: StatisticsQuery) => Command;
5
+ /** Report predicates become the existing source/aggregate parameters; the message query keeps its syntax. */
6
+ export interface StatisticsFilters {
7
+ text?: string;
8
+ messageKind?: string;
9
+ minMessages?: number;
10
+ minViews?: number;
11
+ maxReplies?: number;
12
+ olderThan?: string;
13
+ }
14
+ export declare const statisticsQuery: (words: QueryWords, report: StatisticsQuery) => StatisticsFilters;