@wirecat/cli-core 0.410.0 → 0.412.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 (113) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/cli/bot/command.js +4 -3
  3. package/dist/cli/bot/messages-sync.d.ts +7 -0
  4. package/dist/cli/bot/{store.js → messages-sync.js} +4 -5
  5. package/dist/cli/bot/port.d.ts +4 -4
  6. package/dist/cli/bot/setup.js +11 -5
  7. package/dist/cli/commands-command.js +18 -12
  8. package/dist/cli/config-command.js +1 -0
  9. package/dist/cli/contract-evidence.d.ts +26 -0
  10. package/dist/cli/contract-evidence.js +58 -0
  11. package/dist/cli/contract-templates.d.ts +4 -0
  12. package/dist/cli/contract-templates.js +83 -0
  13. package/dist/cli/discovery-descriptions.d.ts +4 -0
  14. package/dist/cli/discovery-descriptions.js +4 -0
  15. package/dist/cli/index.d.ts +21 -55
  16. package/dist/cli/index.js +18 -55
  17. package/dist/cli/messenger/chat-answers.d.ts +1 -1
  18. package/dist/cli/messenger/chat-answers.js +1 -1
  19. package/dist/cli/messenger/chat-lists.d.ts +1 -1
  20. package/dist/cli/messenger/chats-command.js +3 -0
  21. package/dist/cli/messenger/contacts-command.js +23 -15
  22. package/dist/cli/messenger/guard-commands.d.ts +5 -1
  23. package/dist/cli/messenger/guard-commands.js +5 -3
  24. package/dist/cli/messenger/messages-press-command.js +4 -1
  25. package/dist/cli/messenger/models-command.js +3 -11
  26. package/dist/cli/messenger/records-command.js +1 -1
  27. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  28. package/dist/cli/messenger/tasks-command.js +14 -4
  29. package/dist/cli/model-catalogue-command.d.ts +15 -0
  30. package/dist/cli/model-catalogue-command.js +110 -0
  31. package/dist/cli/moved.js +1 -0
  32. package/dist/cli/program.js +2 -0
  33. package/dist/cli/runs/search-command.js +2 -3
  34. package/dist/cli/runs/search-query.d.ts +1 -1
  35. package/dist/cli/runs/search-query.js +18 -2
  36. package/dist/cli/runs/search.d.ts +2 -1
  37. package/dist/cli/runs/search.js +4 -3
  38. package/dist/cli/settings.d.ts +2 -0
  39. package/dist/cli/settings.js +5 -1
  40. package/dist/cli/shared-command-exports.d.ts +644 -0
  41. package/dist/cli/shared-command-exports.js +114 -0
  42. package/dist/cli/shared-contracts.d.ts +1 -0
  43. package/dist/cli/shared-contracts.js +1 -0
  44. package/dist/domain/providers.d.ts +25 -3
  45. package/dist/domain/providers.js +89 -5
  46. package/dist/entities/chat.d.ts +3 -3
  47. package/dist/entities/chat.js +2 -2
  48. package/dist/entities/contact-context.d.ts +8 -8
  49. package/dist/entities/contact.d.ts +4 -4
  50. package/dist/entities/index.d.ts +40 -13
  51. package/dist/entities/index.js +5 -1
  52. package/dist/entities/knowledge-lists.d.ts +28 -0
  53. package/dist/entities/knowledge-lists.js +29 -0
  54. package/dist/entities/list-results.d.ts +1 -1
  55. package/dist/entities/list-results.js +1 -1
  56. package/dist/entities/send-attempt.d.ts +0 -1
  57. package/dist/entities/send-attempt.js +0 -1
  58. package/dist/errors/reasons.d.ts +1 -1
  59. package/dist/errors/reasons.js +7 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/index.js +1 -0
  62. package/dist/meetings/cli/index.d.ts +1 -1
  63. package/dist/meetings/cli/index.js +25 -9
  64. package/dist/meetings/cli/passage.d.ts +0 -1
  65. package/dist/meetings/cli/passage.js +5 -3
  66. package/dist/meetings/cli/project-evidence.js +3 -2
  67. package/dist/meetings/cli/search.d.ts +2 -2
  68. package/dist/meetings/cli/search.js +9 -7
  69. package/dist/meetings/event-query.d.ts +1 -0
  70. package/dist/meetings/event-query.js +15 -0
  71. package/dist/meetings/index.d.ts +1 -0
  72. package/dist/meetings/index.js +1 -0
  73. package/dist/meetings/store.d.ts +2 -0
  74. package/dist/meetings/testing/memory.js +5 -0
  75. package/dist/search/account-list.d.ts +5 -2
  76. package/dist/search/account-list.js +27 -11
  77. package/dist/search/query.js +3 -1
  78. package/dist/sends/permission-aliases.js +1 -0
  79. package/dist/services/documents/knowledge.d.ts +5 -3
  80. package/dist/services/documents/knowledge.js +4 -4
  81. package/dist/services/meeting-links.d.ts +3 -1
  82. package/dist/services/meeting-links.js +2 -2
  83. package/dist/services/messages-search.js +15 -3
  84. package/dist/services/search-all.js +5 -2
  85. package/dist/services/tasks.d.ts +2 -0
  86. package/dist/services/tasks.js +6 -4
  87. package/dist/store/accounts.d.ts +3 -0
  88. package/dist/store/accounts.js +2 -0
  89. package/dist/store/exports.d.ts +1 -0
  90. package/dist/store/exports.js +1 -0
  91. package/dist/store/index.d.ts +3 -1
  92. package/dist/store/index.js +2 -0
  93. package/dist/store/knowledge-query.d.ts +6 -0
  94. package/dist/store/knowledge-query.js +34 -0
  95. package/dist/store/providers.d.ts +7 -0
  96. package/dist/store/providers.js +5 -0
  97. package/dist/store/sqlite/events.d.ts +2 -1
  98. package/dist/store/sqlite/events.js +16 -1
  99. package/dist/store/sqlite/knowledge.d.ts +5 -4
  100. package/dist/store/sqlite/knowledge.js +50 -17
  101. package/dist/store/sqlite/mail-rules.d.ts +2 -1
  102. package/dist/store/sqlite/mail-rules.js +12 -2
  103. package/dist/store/sqlite/meetings.js +3 -0
  104. package/dist/store/sqlite/notes.d.ts +11 -5
  105. package/dist/store/sqlite/notes.js +9 -2
  106. package/dist/store/sqlite/open-store.js +2 -0
  107. package/dist/store/sqlite/providers.d.ts +12 -0
  108. package/dist/store/sqlite/providers.js +45 -0
  109. package/dist/store/sqlite/tags.d.ts +1 -1
  110. package/dist/store/sqlite/tags.js +3 -3
  111. package/package.json +3 -3
  112. package/parity.json +92 -111
  113. package/dist/cli/bot/store.d.ts +0 -7
package/dist/cli/index.js CHANGED
@@ -32,107 +32,70 @@ export { openRequestTask } from "../services/task-rules.js";
32
32
  export { TASK_PAIR_QUERY, TASK_QUERY } from "../services/tasks.js";
33
33
  export { evaluateAgent, } from "./agent-evaluation.js";
34
34
  export { answerEntity } from "./answer-entity.js";
35
- export { botAdminsCommand, botMembersCommand } from "./bot/admins.js";
36
- export { generatedApiCommand } from "./bot/api.js";
37
35
  export { apiFlagOf, apiJson, apiOptionKey, apiPlainJson, checkApiBody, checkApiParameter, parseApiJson, readApiBody, } from "./bot/api-input.js";
38
36
  export { prepareRpcApiBody } from "./bot/api-rpc.js";
39
- export { botCommand } from "./bot/command.js";
40
37
  export { botContext, botWords } from "./bot/context.js";
41
38
  export { botCopy } from "./bot/copy.js";
42
- export { botMcpCommand } from "./bot/mcp.js";
43
- export { botCan, botIdOf, botMessagesCommand } from "./bot/messages.js";
39
+ export { botCan, botIdOf } from "./bot/messages.js";
44
40
  export { BOT_ACTIONS, } from "./bot/port.js";
45
41
  export { botFiles, botsDirectory, ChatRegistry, registryProfiles } from "./bot/registry.js";
46
42
  export { BotTokenStore } from "./bot/token.js";
47
43
  export { PressLog, UpdatesCursor } from "./bot/updates.js";
48
44
  export { commandContract, SCHEMA_DIALECT } from "./command-contract.js";
49
- export { CONTRACT, commandsCommand } from "./commands-command.js";
50
- export { configCommand, refuseUnknownKey } from "./config-command.js";
45
+ export { CONTRACT } from "./commands-command.js";
46
+ export { refuseUnknownKey } from "./config-command.js";
51
47
  export { loadSnakeCaseConfig } from "./config-keys.js";
52
48
  export { baseContext, environmentOf, outputFor, provide, secretInputOf, } from "./context.js";
49
+ export { addContractInfo, contractEvidence } from "./contract-evidence.js";
50
+ export { sharedCommandTemplate } from "./contract-templates.js";
53
51
  export { withDeadline } from "./deadline.js";
54
52
  export { DEFAULT_CONFIG, ensureDefaultConfig } from "./default-config.js";
53
+ export { DISCOVERY_DESCRIPTIONS } from "./discovery-descriptions.js";
55
54
  export { changesAuthority, changesAuthorityAt, changesAuthorityNow, destroysNothing, FETCH, grantsAuthority, isLogin, isOtherServiceCall, isStoreChange, judgedEffects, judgedTargets, LOCAL_READ, LOCAL_WRITE, logsInOrOut, NO_EFFECTS, ownerOnlyOption, REMOTE_READ, REMOTE_WRITE, reachesOtherService, refuseUnderReadOnly, } from "./effects.js";
56
55
  export { answerSchema, isBody, itemAnswer, itemOf, jsonlRecords, judgedAnswer, listOf, metaNow, printItem, renderAnswer, sealed, snakeKeys, snakeKeysKeeping, } from "./envelope.js";
57
56
  export { withAbort } from "./execution.js";
58
57
  export { askSetupQuestions, asksAtStart, LANGUAGES, setupAtStart, systemLanguage, } from "./first-run.js";
59
- export { checkGuide, checkStep, guideCommand, loadScenarios, MEMO_SCENARIOS, shellProblems, } from "./guide.js";
58
+ export { checkGuide, checkStep, loadScenarios, MEMO_SCENARIOS, shellProblems, } from "./guide.js";
60
59
  export { commandHelp, commandHelpMarkdown, commandHelpText, exampleReport, nextUsually, } from "./help/command-help.js";
61
60
  export { commandsText } from "./help/commands-text.js";
62
- export { firstScreen, installHelp, WIRECAT_TOOLS, } from "./help/first-screen.js";
61
+ export { firstScreen, installHelp, WIRECAT_TOOLS } from "./help/first-screen.js";
63
62
  export { inSection, SECTIONS } from "./help/sections.js";
64
63
  export { bufferedInput, inputPolicy, MAX_BUFFERED_INPUT, MAX_SECRET_INPUT, provideInputPolicy, } from "./input-policy.js";
65
64
  export { answerFields, checkJson, entityFields, liftJson, } from "./json-fields.js";
66
65
  export { commandPath, LIST_QUERY_GUIDE, listQuery, nativeField, nativeTerms, pagedList, pageOf, queryArgument, queryHelp, queryTerms, searchText, termField, timeBounds, typedQuery, unfilteredQuery, withQuery, } from "./list-query.js";
67
- export { addMeetingBatchCommand, formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
66
+ export { formatMeetingBatch, prepareMeetingBatch } from "./meeting-batch.js";
68
67
  export { meetingEvidenceItem, meetingPersonContextItem } from "./meeting-evidence-answer.js";
69
68
  export { parseEvidenceFindingsJson, parseMeetingFindingsJson } from "./meeting-findings-input.js";
70
69
  export { meetingPreparationAnswer } from "./meeting-preparation-answer.js";
71
- export { addMeetingPreparationCommand } from "./meeting-preparation-command.js";
72
70
  export { meetingPreparationInput, readMeetingPreparation, } from "./meeting-preparation-read.js";
73
- export { addMeetingProjectCandidatesCommand } from "./meeting-project-candidates-command.js";
74
71
  export { readMeetingProjectCandidates } from "./meeting-project-candidates-read.js";
75
72
  export { meetingResultItem } from "./meeting-results.js";
76
- export { addMeetingSeriesInventoryCommand } from "./meeting-series-inventory-command.js";
77
73
  export { readMeetingSeriesInventory } from "./meeting-series-inventory-read.js";
78
- export { accountCommand } from "./messenger/account-command.js";
79
74
  export { accountFileFor, profilesHere, rememberAccount } from "./messenger/accounts.js";
80
- export { storeCommand } from "./messenger/archive-commands.js";
81
- export { attachmentsCommand } from "./messenger/attachments-command.js";
82
- export { chatsCommand } from "./messenger/chats-command.js";
83
- export { markReadCommand } from "./messenger/chats-read-command.js";
84
- export { completeCommand } from "./messenger/complete-command.js";
85
- export { contactsCommand } from "./messenger/contacts-command.js";
86
75
  export { connectFor, messengerContext, } from "./messenger/context.js";
87
- export { conversationsCommand } from "./messenger/conversations-command.js";
88
- export { doctorCommand } from "./messenger/doctor-command.js";
89
- export { floodCommand } from "./messenger/flood-command.js";
90
- export { recipientsCommand, sendsCommand } from "./messenger/guard-commands.js";
91
- export { imagesCommand } from "./messenger/images-command.js";
92
- export { inboxCommand } from "./messenger/inbox.js";
93
76
  export { COMMAND_NEEDS } from "./messenger/lacks.js";
94
- export { httpTokenFile, mcpCommand, serverEntry } from "./messenger/mcp-command.js";
95
- export { mentionsCommand } from "./messenger/mentions-command.js";
77
+ export { httpTokenFile, serverEntry } from "./messenger/mcp-command.js";
96
78
  export { messageItem } from "./messenger/message-items.js";
97
- export { messagesCommand, sendCommand } from "./messenger/messages-command.js";
98
- export { deleteCommand } from "./messenger/messages-delete-command.js";
99
- export { editCommand } from "./messenger/messages-edit-command.js";
100
- export { forwardCommand } from "./messenger/messages-forward-command.js";
101
- export { pinCommand, starCommands, unpinCommand } from "./messenger/messages-pin-command.js";
102
- export { metadataCommand } from "./messenger/metadata-command.js";
103
- export { modelsCommand } from "./messenger/models-command.js";
104
- export { pollsCommand } from "./messenger/polls-command.js";
105
79
  export { privateFiles, withSqliteSidecars } from "./messenger/private-files.js";
106
- export { reactionsCommand } from "./messenger/reactions-command.js";
107
- export { callsCommand, stickersCommand } from "./messenger/records-command.js";
108
- export { repliesCommand } from "./messenger/replies-command.js";
109
80
  export { buildReport as buildDoctorReport, reportFileName as doctorReportFileName, } from "./messenger/report.js";
110
- export { reviewCommand } from "./messenger/review.js";
111
- export { scheduleCommand } from "./messenger/schedule-command.js";
112
81
  export { allAnswer, allHitItem, messageHitItem, noteHitItem, } from "./messenger/search-answers.js";
113
- export { searchCommand } from "./messenger/search-command.js";
114
- export { SEARCHES_QUERY, searchesCommand } from "./messenger/searches-command.js";
115
- export { serveCommand, servingProfiles } from "./messenger/serve-command.js";
82
+ export { SEARCHES_QUERY } from "./messenger/searches-command.js";
83
+ export { servingProfiles } from "./messenger/serve-command.js";
116
84
  export { DAY_MS, FIRST_FETCH_MS, memberFetches } from "./messenger/serve-members.js";
117
85
  export { STEMS_EVERY_MS, STEMS_SLICE_MS, stemFills } from "./messenger/serve-stems.js";
118
- export { serverCommand, } from "./messenger/server-command.js";
119
- export { statsCommand } from "./messenger/stats-command.js";
120
86
  export { storeSummary } from "./messenger/store-maintenance-command.js";
121
87
  export { stored } from "./messenger/stored.js";
122
- export { tagsCommand } from "./messenger/tags-command.js";
123
- export { tasksCommand, tasksStatsCommand } from "./messenger/tasks-command.js";
124
- export { topicsCommand } from "./messenger/topics-command.js";
125
- export { watchCommand } from "./messenger/watch-command.js";
88
+ export { MODEL_CATALOGUE_QUERY } from "./model-catalogue-command.js";
126
89
  export { window, withPaging } from "./paging.js";
127
- export { knownBeside, knownPermissionKeys, unknownPermissionKeys, } from "./permission-keys.js";
128
- export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName, } from "./profile.js";
90
+ export { knownBeside, knownPermissionKeys, unknownPermissionKeys } from "./permission-keys.js";
91
+ export { asFirstWord, commandWords, liftProfile, refuseCommandName, rootOf, usableProfileName } from "./profile.js";
129
92
  export { createProgram, run } from "./program.js";
130
93
  export { fieldsOf, projectFields } from "./result-fields.js";
131
- export { runsCommand } from "./runs/command.js";
132
94
  export { count, first, flag, fromFile, NONE_NAMED, plain, settingsFor, } from "./settings.js";
133
95
  export { readSetupAnswers, setupPermissionCeiling } from "./setup-answers.js";
134
96
  export { AGENTS, agentFor, screenFor } from "./setup-screen.js";
135
- export { skillCommand } from "./skill-command.js";
97
+ export { accountCommand, addMeetingBatchCommand, addMeetingPreparationCommand, addMeetingProjectCandidatesCommand, addMeetingSeriesInventoryCommand, attachmentsCommand, botAdminsCommand, botCommand, botMcpCommand, botMembersCommand, botMessagesCommand, callsCommand, chatsCommand, commandsCommand, completeCommand, configCommand, contactsCommand, conversationsCommand, deleteCommand, doctorCommand, editCommand, floodCommand, forwardCommand, generatedApiCommand, guideCommand, imagesCommand, inboxCommand, markReadCommand, mcpCommand, mentionsCommand, messagesCommand, metadataCommand, modelCatalogueCommand, modelsCommand, pinCommand, pollsCommand, reactionsCommand, recipientsCommand, repliesCommand, reviewCommand, runsCommand, scheduleCommand, searchCommand, searchesCommand, sendCommand, sendsCommand, serveCommand, serverCommand, skillCommand, starCommands, statsCommand, stickersCommand, storeCommand, tagsCommand, tasksCommand, tasksStatsCommand, topicsCommand, unpinCommand, upgradeCommand, watchCommand, } from "./shared-command-exports.js";
98
+ export { assertSharedContracts, customizeSharedCommand, hasSharedContract, overrideSharedContract, sharedContractOverrides, sharedContracts, } from "./shared-contracts.js";
136
99
  export { runnable, withSafety } from "./steps.js";
137
100
  export { changeStoreSetting, isStoreSetting, STORE_SETTINGS, storeSettings } from "./store-settings.js";
138
- export { restartServers, upgradeCommand } from "./upgrade-command.js";
101
+ export { restartServers } from "./upgrade-command.js";
@@ -101,7 +101,7 @@ export declare const chatShown: (account: AccountKey, card: Parameters<typeof ch
101
101
  check?: {
102
102
  [x: string]: unknown;
103
103
  } | undefined;
104
- }[] | null | undefined;
104
+ }[] | undefined;
105
105
  }>;
106
106
  /** The lowest score `score>=n` asks for; the audit's own floor when the query names none. */
107
107
  export declare const scoreFloor: (ast: QueryAst | undefined) => number;
@@ -38,7 +38,7 @@ export const chatAnswer = (account, chat) => defined({
38
38
  description: chat.description,
39
39
  invite_link: chat.link,
40
40
  settings: chat.settings ? settingsOf(chat.settings) : undefined,
41
- members: chat.members === undefined ? undefined : (chat.members?.map(memberAnswer) ?? null),
41
+ members: chat.members?.map(memberAnswer),
42
42
  });
43
43
  export const memberAnswer = (member) => {
44
44
  const group = member;
@@ -61,7 +61,7 @@ export declare const chatsList: (services: Services, query: QueryWords, paging:
61
61
  check?: {
62
62
  [x: string]: unknown;
63
63
  } | undefined;
64
- }[] | null | undefined;
64
+ }[] | undefined;
65
65
  }>>;
66
66
  export declare const chatEvents: (services: Services, chat: string, query: QueryWords) => Promise<import("../envelope.js").ListBody<{
67
67
  chat_id: string;
@@ -109,6 +109,8 @@ export const chatsCommand = (messenger) => {
109
109
  "admins add": `"Book club" "Alex Example"${messenger.pickAdminRights === false ? "" : ` --can ${messenger.adminRights?.[0] ?? "info"}`}`,
110
110
  "admins remove": '"Book club" "Alex Example"',
111
111
  create: '"Book club" "Alex Example"',
112
+ app: '"Example Bot"',
113
+ start: '"Example Bot"',
112
114
  "documents list": '"Project room" --limit 10',
113
115
  events: '"Book club"',
114
116
  "folders create": '"Reading" --chat "Book club"',
@@ -125,6 +127,7 @@ export const chatsCommand = (messenger) => {
125
127
  "links list": '"Book club"',
126
128
  "links reset": '"Book club"',
127
129
  "links remove": '"Book club" https://example.com/invite/book-club',
130
+ "links update": '"Book club" https://example.com/invite/book-club --max-uses 25',
128
131
  "links show": '"Book club"',
129
132
  list: "--limit 10",
130
133
  "mark-read": '"Book club" --until-id 101',
@@ -1,5 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { annotate } from "@wirecat/cli-base/commands";
3
+ import { defineQuery } from "@wirecat/cli-base/query";
3
4
  import { Command } from "commander";
4
5
  import { REGISTRIES, registriesCover } from "../../botcheck/registries.js";
5
6
  import { levelFor } from "../../sends/permissions.js";
@@ -12,7 +13,7 @@ import { readSecret } from "../../terminal/prompt.js";
12
13
  import { secretInputOf } from "../context.js";
13
14
  import { FETCH, LOCAL_WRITE, REMOTE_READ } from "../effects.js";
14
15
  import { itemOf, listOf, renderAnswer, snakeKeys } from "../envelope.js";
15
- import { listQuery, nativeTerms, typedQuery, withQuery } from "../list-query.js";
16
+ import { listQuery, nativeField, nativeTerms, termField, timeBounds, typedQuery, withQuery } from "../list-query.js";
16
17
  import { positiveCount, window, withPaging } from "../paging.js";
17
18
  import { casKey } from "../registry-keys.js";
18
19
  import { commandAt, nextStep } from "../steps.js";
@@ -23,6 +24,19 @@ import { hideNever } from "./lacks.js";
23
24
  import { CONTACTS_QUERY, notesAllowed } from "./people-queries.js";
24
25
  import { privatePeopleCommands } from "./private-people-command.js";
25
26
  import { syncsFirst } from "./search-sync-options.js";
27
+ const CONTACT_ACTIVITY_QUERY = defineQuery({
28
+ entity: "contact_activity",
29
+ backend: "native",
30
+ fields: [
31
+ termField("scope", "enum", "scope:work", { values: SCOPES }),
32
+ nativeField({
33
+ name: "at",
34
+ type: "time",
35
+ operators: ["term", "phrase", "range"],
36
+ example: "at:[2026-10-01 TO 2026-10-31]",
37
+ }),
38
+ ],
39
+ });
26
40
  /** People this account has a one-to-one chat with, as the people service counts them. */
27
41
  const collect = (value, previous = []) => [...previous, value];
28
42
  export const contactsCommand = (messenger) => {
@@ -173,28 +187,24 @@ export const contactsCommand = (messenger) => {
173
187
  meta: { ...window.meta, ...(hasMore ? { truncated: true } : {}) },
174
188
  }));
175
189
  });
176
- annotate(contacts.command("events"), {
190
+ withQuery(annotate(contacts.command("events"), {
177
191
  effects: REMOTE_READ,
178
192
  output: { entity: "contact_activity" },
179
193
  })
180
194
  .summary("one person's activity across their linked identities")
181
195
  .description("everything one person took part in, in every messenger linked to them — messages they wrote or were " +
182
196
  "mentioned in, chats, mail, meetings, tasks — newest first, from the store; never connects")
183
- .argument("<person>", "their id, @username, or part of their name")
184
- .option("--scope <scope>", "only what belongs to personal or to work accounts: personal or work")
185
- .option("--since-time <time>", "nothing older than this ISO 8601 time, or 2h / 1d ago")
186
- .option("--until-time <time>", "through this ISO 8601 time, or 2h / 1d ago")
197
+ .argument("<person>", "their id, @username, or part of their name"), CONTACT_ACTIVITY_QUERY)
187
198
  .option("--limit <n>", `at most this many; ${TIMELINE_ITEMS} if not given`, positiveCount("--limit"))
188
199
  .option("--cursor <cursor>", "go on from where the last page stopped; page.next gives it, with the same filters")
189
- .action(async function (person) {
190
- const { scope, sinceTime, untilTime, limit, cursor } = this.opts();
200
+ .action(async function (person, words) {
201
+ const terms = nativeTerms(listQuery(typedQuery(words), CONTACT_ACTIVITY_QUERY, "contacts events"), "contacts events");
202
+ const scope = terms.get("scope")?.value.toLowerCase();
203
+ const { limit, cursor } = this.opts();
191
204
  if (scope !== undefined && !SCOPES.includes(scope))
192
205
  throw new CliError("validation_error", `--scope is personal or work, not "${scope}"`);
193
206
  const context = messengerContext(this, messenger);
194
- const window = dateWindow({
195
- ...(sinceTime === undefined ? {} : { sinceTime }),
196
- ...(untilTime === undefined ? {} : { untilTime }),
197
- });
207
+ const window = timeBounds(terms.get("at"));
198
208
  const found = await context.withServices((services) => services.people.timeline(person, {
199
209
  ...(scope === undefined ? {} : { scope: scope }),
200
210
  ...(window.since === undefined ? {} : { since: window.since }),
@@ -211,9 +221,7 @@ export const contactsCommand = (messenger) => {
211
221
  ...(found.cursor === undefined ? {} : { cursor: found.cursor }),
212
222
  args: {
213
223
  person,
214
- ...(scope === undefined ? {} : { scope }),
215
- ...(sinceTime === undefined ? {} : { since_time: sinceTime }),
216
- ...(untilTime === undefined ? {} : { until_time: untilTime }),
224
+ ...(words.length ? { query: typedQuery(words) } : {}),
217
225
  },
218
226
  meta: { ...window.meta, ...(pending > 0 ? { truncated: true } : {}) },
219
227
  }));
@@ -1,9 +1,13 @@
1
1
  import { Command } from "commander";
2
+ import { type SendEntry } from "../../sends/journal.js";
2
3
  import { type Messenger } from "./context.js";
3
4
  /**
4
5
  * The chats this profile may send to. Off until the first `add`; `clear` deletes it, which turns it off again. It stops
5
6
  * a model that a message talked into writing somewhere, not an agent set on getting around it.
6
7
  */
7
- export declare const recipientsCommand: (messenger: Messenger) => Command;
8
+ export declare const recipientsCommand: (messenger: Messenger, options?: {
9
+ resolve?: "chat-card";
10
+ }) => Command;
8
11
  /** Every attempt to send from this profile, kept whatever `--record` says. Never the text. */
9
12
  export declare const sendsCommand: (messenger: Messenger) => Command;
13
+ export declare const sendAttemptItem: ({ sendId, operationId, ...entry }: SendEntry) => unknown;
@@ -15,7 +15,7 @@ import { SENDS_QUERY, sendFilter } from "./sends-query.js";
15
15
  * The chats this profile may send to. Off until the first `add`; `clear` deletes it, which turns it off again. It stops
16
16
  * a model that a message talked into writing somewhere, not an agent set on getting around it.
17
17
  */
18
- export const recipientsCommand = (messenger) => {
18
+ export const recipientsCommand = (messenger, options = {}) => {
19
19
  const tool = `${messenger.app.command} personal`;
20
20
  const listFor = (profile, env) => new RecipientList(recipientsPathFor(messenger.app, profile, env), messenger.app.command);
21
21
  const command = new Command("recipients").description("the chats this profile may send to, when the list is on");
@@ -43,7 +43,8 @@ export const recipientsCommand = (messenger) => {
43
43
  .description("allow sending to this chat; the first add turns the list on")
44
44
  .action(async function (chat) {
45
45
  const context = messengerContext(this, messenger);
46
- const found = await context.withMessenger((connection) => connection.resolve(chat));
46
+ // Some transports resolve an id without reading its title or dialog partner.
47
+ const found = await context.withMessenger((connection) => options.resolve === "chat-card" ? connection.chat(chat) : connection.resolve(chat));
47
48
  const partnerId = messenger.partnerOf?.(found);
48
49
  const added = await listFor(context.settings.profile, context.env).addAsync({
49
50
  id: found.id,
@@ -93,10 +94,11 @@ export const sendsCommand = (messenger) => new Command("sends").description("eve
93
94
  const from = settings.all ? 0 : (settings.page - 1) * settings.limit;
94
95
  const shown = settings.all ? entries : entries.slice(from, from + settings.limit);
95
96
  renderAnswer(this, context, pagedList("sends list", settings, {
96
- items: shown.map(snakeKeys),
97
+ items: shown.map(sendAttemptItem),
97
98
  args: words.length ? { query: typedQuery(words) } : {},
98
99
  has_more: from + shown.length < entries.length,
99
100
  }));
100
101
  if (entries.length === 0)
101
102
  renderer.note(filter ? "no send attempts match the query" : `profile ${settings.profile} has not tried to send anything`);
102
103
  }));
104
+ export const sendAttemptItem = ({ sendId, operationId, ...entry }) => snakeKeys({ ...entry, ...((operationId ?? sendId) === undefined ? {} : { operationId: operationId ?? sendId }) });
@@ -5,7 +5,10 @@ import { REMOTE_WRITE } from "../effects.js";
5
5
  import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
6
6
  import { messengerContext } from "./context.js";
7
7
  /** `messages press` — one of a bot's callback buttons; the bot sees who pressed it. */
8
- export const pressCommand = (messenger) => annotate(new Command("press"), { effects: REMOTE_WRITE })
8
+ export const pressCommand = (messenger) => annotate(new Command("press"), {
9
+ effects: REMOTE_WRITE,
10
+ examples: [`${messenger.app.command} personal messages press "Example Bot" 101 1`],
11
+ })
9
12
  .description("press a bot's button under a message; the bot sees that you pressed it")
10
13
  .argument("<chat>", messenger.chatArgument)
11
14
  .argument("<message>", "the id of the message with the buttons")
@@ -1,7 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { CliError } from "@wirecat/cli-base";
3
3
  import { annotate } from "@wirecat/cli-base/commands";
4
- import { defineQuery } from "@wirecat/cli-base/query";
5
4
  import { Command } from "commander";
6
5
  import { isTextModelInstalled, textModelsDirectory } from "../../embeddings/embed.js";
7
6
  import { embeddingKeys } from "../../embeddings/keys.js";
@@ -16,19 +15,12 @@ import { readSecret } from "../../terminal/prompt.js";
16
15
  import { baseContext, environmentOf } from "../context.js";
17
16
  import { FETCH, grantsAuthority, LOCAL_READ, LOCAL_WRITE, reachesOtherService } from "../effects.js";
18
17
  import { itemOf, listOf, renderAnswer } from "../envelope.js";
19
- import { listQuery, nativeTerms, termField, typedQuery, unfilteredQuery, withQuery } from "../list-query.js";
18
+ import { listQuery, nativeTerms, typedQuery, unfilteredQuery, withQuery } from "../list-query.js";
19
+ import { MODEL_CATALOGUE_QUERY } from "../model-catalogue-command.js";
20
20
  import { nextStep } from "../steps.js";
21
21
  /** A model list is the catalog this version ships. */
22
22
  const MODELS_QUERY = unfilteredQuery("model", modelEntity);
23
- const ROOT_MODELS_QUERY = defineQuery({
24
- entity: "model",
25
- backend: "native",
26
- fields: [
27
- termField("kind", "enum", "kind:audio", { values: ["audio", "text"] }),
28
- termField("id", "kw", "id:e5-small"),
29
- termField("downloaded", "bool", "downloaded:true"),
30
- ],
31
- });
23
+ const ROOT_MODELS_QUERY = MODEL_CATALOGUE_QUERY;
32
24
  /** Every model this version knows: a short, fixed list, never paged. */
33
25
  const everyModel = (command, items, next = []) => listOf(command, { items, limit: Math.max(1, items.length), has_more: false, next });
34
26
  const audioItem = (model, directory, chosen) => ({
@@ -22,7 +22,7 @@ const kindsOf = (typed) => {
22
22
  export const mediaCommand = (messenger) => annotate(new Command("media"), { effects: REMOTE_READ })
23
23
  .description("a chat's photos, videos, files, audio and links, from the messenger's server; reading marks nothing")
24
24
  .argument("<chat>", messenger.chatArgument)
25
- .option("--type <names>", `only these kinds, comma-separated: ${MEDIA_KINDS.join(", ")}`)
25
+ .option("--type <type>", `only these kinds, comma-separated: ${MEDIA_KINDS.join(", ")}`)
26
26
  .option("--limit <n>", "how many to show", positiveCount("--limit"))
27
27
  .option("--cursor <cursor>", "go on from where page.next says the last page stopped")
28
28
  .action(async function (chat) {
@@ -3,9 +3,9 @@ import type { CacheDatabase } from "../../store/driver.js";
3
3
  import type { Messenger } from "./context.js";
4
4
  /** `doctor`'s short summary of the store. Must answer for a broken file, so an error is a field. */
5
5
  export declare const storeSummary: (env: NodeJS.ProcessEnv) => Promise<{
6
+ error?: undefined;
6
7
  path: string;
7
8
  exists: boolean;
8
- error?: undefined;
9
9
  } | {
10
10
  path: string;
11
11
  exists: boolean;
@@ -108,6 +108,10 @@ const holderOf = (command) => holderIn(environmentOf(command).env ?? process.env
108
108
  const HOLDER_HELP = "who you are for claims: a session id or a chat id; default WIRECAT_HOLDER, else Claude Code's session, else the agent's name";
109
109
  const planFrom = (options) => {
110
110
  const plan = {};
111
+ if (options.priority !== undefined)
112
+ plan.priority = options.priority;
113
+ if (options.parent !== undefined)
114
+ plan.parent = options.parent === "none" ? null : options.parent;
111
115
  if (options.category !== undefined)
112
116
  plan.category = options.category;
113
117
  if (options.title !== undefined)
@@ -190,15 +194,21 @@ export const tasksCommand = (messenger) => {
190
194
  effects: LOCAL_WRITE,
191
195
  examples: [`${tool} personal tasks update DOC-12 --title "Review draft"`],
192
196
  }))
193
- .summary("set a task's title, description, due date, next check, category and assignee")
194
- .description("set a task's title, description, due date, next check, category and assignee; nothing reaches the messenger")
197
+ .summary("update a task's text, schedule, category, assignees, priority or parent")
198
+ .description("update a task's text, schedule, category, assignees, priority or parent; nothing reaches the messenger")
195
199
  .argument("<task>", "the task's id or key, as tasks list shows it")
196
- .option("--category <name>", `${categoryHelp}; a change is kept in the task's history`), "send Alex the API token").action(async function (id) {
200
+ .option("--category <name>", `${categoryHelp}; a change is kept in the task's history`)
201
+ .option("--priority <n>", "priority from 0 (urgent) through 4 (low)", (value) => {
202
+ if (!/^[0-4]$/.test(value))
203
+ throw new CliError("validation_error", "--priority takes 0 through 4");
204
+ return Number(value);
205
+ })
206
+ .option("--parent <id>", "parent task in the same project; none clears it"), "send Alex the API token").action(async function (id) {
197
207
  const context = messengerContext(this, messenger);
198
208
  refuseLocalWrite(context, messenger.app.command, "tasks.update");
199
209
  const plan = planFrom(this.opts());
200
210
  if (Object.keys(plan).length === 0)
201
- throw new CliError("validation_error", "give at least one of --title, --description, --due, --remind-time, --category");
211
+ throw new CliError("validation_error", "give at least one of --title, --description, --due, --remind-time, --category, --priority, --parent");
202
212
  const planned = await context.withServices((services) => services.tasks.plan(id, plan, writerOf(this)));
203
213
  renderAnswer(this, context, itemOf(snakeKeys(planned)), () => `${card(planned)}\n`);
204
214
  });
@@ -0,0 +1,15 @@
1
+ import { Command } from "commander";
2
+ import type { AppIdentity } from "../ports/app.js";
3
+ import { type Body } from "./envelope.js";
4
+ export declare const MODEL_CATALOGUE_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
5
+ export interface ModelCatalogueOptions {
6
+ kinds: readonly ("audio" | "text")[];
7
+ env: () => NodeJS.ProcessEnv;
8
+ output: (answer: Body, command: Command) => void;
9
+ diagnostic: (line: string) => void;
10
+ jsonOption?: boolean;
11
+ offline?: () => boolean;
12
+ defaultAudio?: () => string;
13
+ }
14
+ /** Provider composition supplies I/O and supported kinds; parsing, receipts and download policy stay shared. */
15
+ export declare const modelCatalogueCommand: (app: AppIdentity, options: ModelCatalogueOptions) => Command;
@@ -0,0 +1,110 @@
1
+ import { join } from "node:path";
2
+ import { CliError } from "@wirecat/cli-base";
3
+ import { annotate } from "@wirecat/cli-base/commands";
4
+ import { defineQuery } from "@wirecat/cli-base/query";
5
+ import { Command } from "commander";
6
+ import { isTextModelInstalled, textModelsDirectory } from "../embeddings/embed.js";
7
+ import { DEFAULT_TEXT_MODEL, TEXT_MODELS } from "../embeddings/models.js";
8
+ import { downloadTextModel } from "../services/model-downloads.js";
9
+ import { install, installedBytes, isInstalled, modelsDirectory } from "../speech/install.js";
10
+ import { DEFAULT_ORDER, orderedModels, speechModel } from "../speech/models.js";
11
+ import { FETCH, LOCAL_READ, NO_EFFECTS } from "./effects.js";
12
+ import { itemOf, listOf } from "./envelope.js";
13
+ import { listQuery, nativeTerms, termField, typedQuery, withQuery } from "./list-query.js";
14
+ export const MODEL_CATALOGUE_QUERY = defineQuery({
15
+ entity: "model",
16
+ backend: "native",
17
+ fields: [
18
+ termField("kind", "enum", "kind:audio", { values: ["audio", "text"] }),
19
+ termField("id", "kw", "id:e5-small"),
20
+ termField("downloaded", "bool", "downloaded:true"),
21
+ ],
22
+ });
23
+ /** Provider composition supplies I/O and supported kinds; parsing, receipts and download policy stay shared. */
24
+ export const modelCatalogueCommand = (app, options) => {
25
+ if (options.kinds.length === 0)
26
+ throw new CliError("configuration_error", "declare at least one supported model kind");
27
+ const audio = (id, env) => {
28
+ const model = speechModel(id);
29
+ const directory = modelsDirectory(env);
30
+ return {
31
+ id,
32
+ kind: "audio",
33
+ title: model.title,
34
+ languages: model.languages,
35
+ size_bytes: installedBytes(model),
36
+ downloaded: isInstalled(model, directory),
37
+ default: id === (options.defaultAudio?.() ?? DEFAULT_ORDER[0]),
38
+ path: join(directory, id),
39
+ };
40
+ };
41
+ const text = (id, env) => {
42
+ const model = TEXT_MODELS.find((one) => one.id === id);
43
+ if (!model)
44
+ throw new CliError("validation_error", `unknown text model ${id}`);
45
+ const directory = textModelsDirectory(env);
46
+ return {
47
+ id,
48
+ kind: "text",
49
+ title: model.title,
50
+ languages: model.languages,
51
+ licence: model.licence,
52
+ size_bytes: model.files.reduce((sum, file) => sum + file.bytes, 0),
53
+ downloaded: isTextModelInstalled(model, directory),
54
+ default: id === DEFAULT_TEXT_MODEL,
55
+ path: join(directory, id),
56
+ };
57
+ };
58
+ const models = annotate(new Command("models"), { effects: NO_EFFECTS }).description("models that run on this machine");
59
+ withQuery(annotate(models.command("list"), {
60
+ effects: LOCAL_READ,
61
+ output: { entity: "model" },
62
+ examples: [`${app.command} models list`, `${app.command} models list kind:${options.kinds[0]}`],
63
+ }), MODEL_CATALOGUE_QUERY)
64
+ .description("speech and embedding models, their download status and the default of each kind")
65
+ .action(function (words) {
66
+ const terms = nativeTerms(listQuery(typedQuery(words), MODEL_CATALOGUE_QUERY, "models list"), "models list");
67
+ const env = options.env();
68
+ const items = [
69
+ ...(options.kinds.includes("audio") ? orderedModels().map((model) => audio(model.id, env)) : []),
70
+ ...(options.kinds.includes("text") ? TEXT_MODELS.map((model) => text(model.id, env)) : []),
71
+ ].filter((item) => (terms.get("kind") === undefined || item.kind === terms.get("kind")?.value) &&
72
+ (terms.get("id") === undefined || item.id === terms.get("id")?.value) &&
73
+ (terms.get("downloaded") === undefined || item.downloaded === (terms.get("downloaded")?.value === "true")));
74
+ options.output(listOf("models list", { items, limit: Math.max(1, items.length), has_more: false }), this);
75
+ });
76
+ annotate(models.command("download"), {
77
+ effects: FETCH,
78
+ output: { entity: "model" },
79
+ examples: [
80
+ `${app.command} models download ${options.kinds.includes("text") ? DEFAULT_TEXT_MODEL : DEFAULT_ORDER[0]}`,
81
+ ],
82
+ })
83
+ .description("download and verify one speech or embedding model")
84
+ .argument("<model>", "a model id from models list")
85
+ .option("--offline", "refuse a download that needs the network")
86
+ .option("--accept-terms", "accept the model's licence terms, for a model that has its own")
87
+ .action(async function (id, given) {
88
+ if (options.offline?.() || this.optsWithGlobals().offline)
89
+ throw new CliError("permission_error", "downloading a model needs the network; --offline was requested", {
90
+ reason: "model.offline",
91
+ });
92
+ const env = options.env();
93
+ if (TEXT_MODELS.some((model) => model.id === id)) {
94
+ if (!options.kinds.includes("text"))
95
+ throw new CliError("validation_error", "this tool does not use text models");
96
+ await downloadTextModel(id, { env, acceptTerms: given.acceptTerms === true, progress: options.diagnostic });
97
+ options.output(itemOf(text(id, env)), this);
98
+ }
99
+ else {
100
+ if (!options.kinds.includes("audio"))
101
+ throw new CliError("validation_error", "this tool does not use audio models");
102
+ await install(speechModel(id), modelsDirectory(env), { verifyExisting: true, progress: options.diagnostic });
103
+ options.output(itemOf(audio(id, env)), this);
104
+ }
105
+ });
106
+ if (options.jsonOption)
107
+ for (const command of models.commands)
108
+ command.option("--json", "print JSON");
109
+ return models;
110
+ };
package/dist/cli/moved.js CHANGED
@@ -32,6 +32,7 @@ export const RENAMED_COMMANDS = {
32
32
  "mcp setup": "mcp add",
33
33
  "mcp doctor": "mcp check",
34
34
  "commands schema": "commands info",
35
+ "bot store fetch": "bot messages sync",
35
36
  "store gaps plan": "store gaps show",
36
37
  "store reindex": "store rebuild",
37
38
  "stats messages counters refresh": "stats messages counters sync",
@@ -30,6 +30,7 @@ import { PreviewComplete, preview } from "./preview.js";
30
30
  import { commandWords, liftProfile, PROFILE_FREE, valuedOptions } from "./profile.js";
31
31
  import { absolutePaths, relayTarget } from "./relay-route.js";
32
32
  import { settingsFor } from "./settings.js";
33
+ import { assertSharedContracts } from "./shared-contracts.js";
33
34
  import { commandAt, lineOf, withLines } from "./steps.js";
34
35
  /**
35
36
  * The command tree, built fresh on each call. Commander is global state by default —
@@ -66,6 +67,7 @@ export const createProgram = ({ app, commands, configure, firstScreen }, { out,
66
67
  for (const command of commands())
67
68
  program.addCommand(command);
68
69
  configure?.(program);
70
+ assertSharedContracts(program);
69
71
  installHelp(program, { pitch: app.description, ...firstScreen });
70
72
  // Commander builds a usage line from the command tree, where the profile is not a word.
71
73
  for (const top of program.commands) {
@@ -12,7 +12,6 @@ import { RUN_SEARCH_QUERY, runSearchFilter } from "./search-query.js";
12
12
  export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
13
13
  .summary("search recorded diagnostic metadata and events")
14
14
  .description("search recorded diagnostic metadata and events; literal words and field:value terms joined by AND; no message contents")
15
- .option("--since-time <time>", "runs starting on or after an ISO time or 2h / 1d ago")
16
15
  .option("--limit <n>", "matches per page, at most 100", positiveCount("--limit"), 20)
17
16
  .option("--page <n>", "result page", positiveCount("--page"), 1), RUN_SEARCH_QUERY), {
18
17
  effects: LOCAL_READ,
@@ -22,6 +21,6 @@ export const runsSearchCommand = (app) => annotate(withQuery(new Command("runs")
22
21
  const filter = runSearchFilter(words);
23
22
  const options = this.opts();
24
23
  const dir = runsDirFor(app, environmentOf(this).env ?? process.env);
25
- const found = searchRuns(dir, { ...filter, ...options, since: options.sinceTime });
26
- renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words), since_time: options.sinceTime }));
24
+ const found = searchRuns(dir, { ...filter, ...options });
25
+ renderAnswer(this, outputFor(this), runsFound(found, { query: typedQuery(words) }));
27
26
  });
@@ -1,4 +1,4 @@
1
1
  import { type QueryWords } from "../list-query.js";
2
2
  import type { RunSearch } from "./search.js";
3
3
  export declare const RUN_SEARCH_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
4
- export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile">;
4
+ export declare const runSearchFilter: (words: QueryWords) => Pick<RunSearch, "terms" | "status" | "errorCode" | "operation" | "profile" | "since" | "until">;