@wirecat/cli-core 0.394.0 → 0.396.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +77 -0
- package/dist/cli/commands-command.js +6 -0
- package/dist/cli/config-command.js +5 -2
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/input-fields.d.ts +3 -0
- package/dist/cli/input-fields.js +27 -0
- package/dist/cli/messenger/account-command.js +8 -3
- package/dist/cli/messenger/account-sessions-command.js +7 -2
- package/dist/cli/messenger/attachments-command.js +16 -4
- package/dist/cli/messenger/conversation-answers.d.ts +5 -0
- package/dist/cli/messenger/conversation-examples.d.ts +2 -0
- package/dist/cli/messenger/conversation-examples.js +25 -0
- package/dist/cli/messenger/conversations-command.js +2 -0
- package/dist/cli/messenger/mcp-command.js +12 -3
- package/dist/cli/messenger/message-answers.d.ts +5 -0
- package/dist/cli/messenger/message-writes.d.ts +5 -0
- package/dist/cli/messenger/models-command.js +15 -6
- package/dist/cli/messenger/polls-command.js +20 -5
- package/dist/cli/messenger/records-command.js +10 -2
- package/dist/cli/messenger/search-answers.js +2 -0
- package/dist/cli/messenger/search-command.js +12 -0
- package/dist/cli/messenger/searches-command.d.ts +2 -0
- package/dist/cli/messenger/searches-command.js +2 -1
- package/dist/cli/messenger/server-command.js +19 -7
- package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
- package/dist/cli/messenger/tasks-command.js +18 -6
- package/dist/cli/program.js +5 -2
- package/dist/cli/runs/command.js +11 -4
- package/dist/cli/skill-command.js +6 -1
- package/dist/entities/evidence.d.ts +40 -0
- package/dist/entities/index.d.ts +72 -2
- package/dist/entities/index.js +2 -0
- package/dist/entities/message.d.ts +9 -0
- package/dist/entities/message.js +6 -0
- package/dist/entities/saved-search.d.ts +1 -1
- package/dist/entities/saved-search.js +1 -1
- package/dist/errors/reasons.d.ts +1 -0
- package/dist/errors/reasons.js +16 -0
- package/dist/meetings/cli/search.js +1 -1
- package/dist/meetings/hit-query.d.ts +10 -0
- package/dist/meetings/hit-query.js +85 -1
- package/dist/meetings/index.d.ts +1 -1
- package/dist/meetings/index.js +1 -1
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/search/lucene/registry.js +2 -2
- package/dist/services/index.d.ts +3 -2
- package/dist/services/index.js +2 -1
- package/dist/services/meeting-combined.d.ts +1 -1
- package/dist/services/meeting-combined.js +4 -3
- package/dist/services/meeting-hit-search.d.ts +1 -1
- package/dist/services/meeting-hit-search.js +4 -2
- package/dist/services/meeting-searches.d.ts +25 -0
- package/dist/services/meeting-searches.js +35 -0
- package/dist/services/messages-search.js +13 -2
- package/dist/services/messages.d.ts +5 -0
- package/dist/services/search-record.d.ts +1 -1
- package/dist/services/searches.d.ts +1 -0
- package/dist/services/searches.js +17 -4
- package/dist/store/search-index.d.ts +4 -1
- package/dist/store/search-index.js +10 -1
- package/dist/store/searches.d.ts +5 -4
- package/dist/store/searches.js +4 -4
- package/dist/store/sqlite/hit-places.d.ts +12 -0
- package/dist/store/sqlite/hit-places.js +76 -0
- package/dist/store/sqlite/lucene-fields.d.ts +8 -0
- package/dist/store/sqlite/lucene-fields.js +6 -4
- package/dist/store/sqlite/lucene.js +25 -70
- package/dist/store/sqlite/manifest.js +2 -0
- package/dist/store/sqlite/migrations.generated.js +19 -0
- package/dist/store/sqlite/note-index.d.ts +11 -1
- package/dist/store/sqlite/note-index.js +18 -6
- package/dist/store/sqlite/schema.d.ts +39 -0
- package/dist/store/sqlite/schema.js +4 -0
- package/dist/store/sqlite/searches.d.ts +4 -4
- package/dist/store/sqlite/searches.js +21 -6
- package/dist/store/sqlite/transcript-query.d.ts +40 -0
- package/dist/store/sqlite/transcript-query.js +108 -0
- package/dist/store/sqlite/words.d.ts +6 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,83 @@ Notable changes to `@wirecat/cli-core` (`@wirecat/cli-messaging` up to 0.261.0,
|
|
|
5
5
|
minor version may break callers, and says how under "Changed — may break callers". `pnpm docs:check` checks the
|
|
6
6
|
shape of this file.
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## 0.396.0 — 23.10.2026
|
|
11
|
+
|
|
12
|
+
### Changed — may break callers
|
|
13
|
+
|
|
14
|
+
- **Command discovery uses `--json`.** `commands`, scoped discovery and `commands schema` refuse `--jsonl` with `output.one-object`, instead of printing a line with no stream end record. Use `--json` for the discovery document.
|
|
15
|
+
|
|
16
|
+
- cli-core requires cli-base 0.50.1 or newer within the 0.50 line for profile-preserving command corrections and concise skill summaries.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Unknown-command explanations are separated.** The explanation of a word read as a profile follows the command error with a dash, keeping both parts readable on one line.
|
|
21
|
+
|
|
22
|
+
- **Input-field errors name the command's flags.** Adapters can report unsupported or conflicting domain fields through `input.field-unsupported` or `input.field-conflict`. The CLI maps declared fields to flags in the human message and structured parameter, while SDK errors stay independent of command syntax. Unknown fields are kept without guessing.
|
|
23
|
+
|
|
24
|
+
- **Joined command hints keep the chosen profile.** A command path passed as one argument is refused with a correction that preserves a separate explicit profile, including when global options come first.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- `SEARCHES_QUERY` (`@wirecat/cli-core/cli`): the query `searches list` takes, for another CLI's own saved searches.
|
|
29
|
+
|
|
30
|
+
- **A voice note's transcript is found in any form of a word, and widened to the spellings speech recognition
|
|
31
|
+
garbles (store version 29).** «риелтор» finds a transcript saying «риелтору», and «реелтора»: always, not only when
|
|
32
|
+
nothing matched, by meeting search's rules — one stem edit from a word the Russian dictionary does not know, never
|
|
33
|
+
a number word, never a phrase or under `exact:`. A hit found only through a garbled spelling has `match:
|
|
34
|
+
"corrected"` and `corrections`, and the answer's `corrections` lists them. New tables `transcript_words`, its
|
|
35
|
+
vocabulary, `transcript_stems` and `transcript_index_pending`, filled before each search; existing transcripts are
|
|
36
|
+
queued on upgrade.
|
|
37
|
+
|
|
38
|
+
- Attachment commands include concrete examples in help and command discovery.
|
|
39
|
+
|
|
40
|
+
## 0.395.0 — 23.10.2026
|
|
41
|
+
|
|
42
|
+
### Changed — may break callers
|
|
43
|
+
|
|
44
|
+
- `searches list`, `searches history` and `searches clear` cover only message searches. The table is shared by
|
|
45
|
+
every CLI on the store; a saved meeting search shown, run or deleted from a messenger is refused with
|
|
46
|
+
`details.reason: "search.kind"`. `SearchesStore.savedSearches`, `searchHistory` and `clearSearchHistory` take an
|
|
47
|
+
optional list of kinds.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `search meetings` takes `date:`, `project:`, `tag:` and `topic:` in the query: `budget project:RES date>=30d`. They
|
|
52
|
+
choose meetings as `--since-time`/`--until-time`, `--project`, `--tag` and `--topic` do, join the words with AND,
|
|
53
|
+
and are refused inside OR, NOT and groups (`query.unsupported-field`) or when the same filter is also an option.
|
|
54
|
+
Search by meaning embeds only the words. `liftMeetingFields` (`@wirecat/cli-core/meetings`) does the split.
|
|
55
|
+
|
|
56
|
+
- `meetingSearchesService` (`@wirecat/cli-core/services`): saved meeting searches, kind `meetings-search`, in the
|
|
57
|
+
same table: create, list, show, delete and `run`, which counts the run.
|
|
58
|
+
|
|
59
|
+
- MCP setup and diagnostic commands include examples in help and command discovery.
|
|
60
|
+
|
|
61
|
+
- Account commands include concrete examples in help and command discovery.
|
|
62
|
+
|
|
63
|
+
- Search commands include concrete examples in help and command discovery.
|
|
64
|
+
|
|
65
|
+
- Configuration commands include concrete examples in help and command discovery.
|
|
66
|
+
|
|
67
|
+
- Skill commands include concrete examples in help and command discovery.
|
|
68
|
+
|
|
69
|
+
- Task commands include concrete examples in help and command discovery.
|
|
70
|
+
|
|
71
|
+
- Chat deletion and history clearing include examples in help and command discovery.
|
|
72
|
+
|
|
73
|
+
- Poll commands include concrete examples in help and command discovery.
|
|
74
|
+
|
|
75
|
+
- Listing poll voters includes an example in help and command discovery.
|
|
76
|
+
|
|
77
|
+
- Model commands include concrete examples in help and command discovery.
|
|
78
|
+
|
|
79
|
+
- Recorded-run commands include concrete examples in help and command discovery.
|
|
80
|
+
|
|
81
|
+
- Conversation commands include examples in help and command discovery, with guidance for submitting a batch answer.
|
|
82
|
+
|
|
83
|
+
- Server commands include concrete examples in help and command discovery.
|
|
84
|
+
|
|
8
85
|
## 0.394.0 — 23.10.2026
|
|
9
86
|
|
|
10
87
|
### Added
|
|
@@ -20,6 +20,12 @@ export const CONTRACT = 3;
|
|
|
20
20
|
export const commandsCommand = (app) => {
|
|
21
21
|
const command = annotate(new Command("commands"), { effects: NO_EFFECTS })
|
|
22
22
|
.description("every command by section, one line each; --json: the contract, one path per call")
|
|
23
|
+
.hook("preAction", (_command, action) => {
|
|
24
|
+
if (action.optsWithGlobals().jsonl)
|
|
25
|
+
throw new CliError("validation_error", "command discovery is one JSON object — use --json", {
|
|
26
|
+
reason: "output.one-object",
|
|
27
|
+
});
|
|
28
|
+
})
|
|
23
29
|
.argument("[path...]", "one command path, for example: search messages; inspect other groups in separate calls")
|
|
24
30
|
.action(function (path) {
|
|
25
31
|
const root = rootOf(this);
|
|
@@ -16,7 +16,7 @@ import { changeStoreSetting, isStoreSetting, STORE_SETTINGS, storeSettings } fro
|
|
|
16
16
|
*/
|
|
17
17
|
export const configCommand = (app, config, { permissionKey } = {}) => {
|
|
18
18
|
const command = annotate(new Command("config"), { mcp: false }).description("the settings in force, and where each one came from");
|
|
19
|
-
annotate(command.command("show"), { effects: LOCAL_READ })
|
|
19
|
+
annotate(command.command("show"), { effects: LOCAL_READ, examples: [`${app.command} personal config show`] })
|
|
20
20
|
.description("the profile, the profiles that exist, and each setting with where it came from")
|
|
21
21
|
.option("--bot", "the settings a bot command on this profile gets, rather than the personal account's")
|
|
22
22
|
.action(async function () {
|
|
@@ -42,7 +42,10 @@ export const configCommand = (app, config, { permissionKey } = {}) => {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
for (const action of ["set", "unset"]) {
|
|
45
|
-
const sub = grantsAuthority(annotate(command.command(action), {
|
|
45
|
+
const sub = grantsAuthority(annotate(command.command(action), {
|
|
46
|
+
effects: LOCAL_WRITE,
|
|
47
|
+
examples: [`${app.command} personal config ${action} limit${action === "set" ? " 20" : ""}`],
|
|
48
|
+
}))
|
|
46
49
|
.argument("<setting>",
|
|
47
50
|
// default_profile is only removed now: `config unset` still takes it, `config set` refuses it.
|
|
48
51
|
`one of: ${[...config.allSettings.filter((one) => action === "unset" || one !== "default_profile"), ...STORE_SETTINGS, "account_groups.<name>"].join(", ")}`)
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ export { reviewCommand } from "./messenger/review.js";
|
|
|
108
108
|
export { scheduleCommand } from "./messenger/schedule-command.js";
|
|
109
109
|
export { allHitItem, type HitItem, type MessageHitItem, messageHitItem, type NoteHitItem, noteHitItem, } from "./messenger/search-answers.js";
|
|
110
110
|
export { searchCommand } from "./messenger/search-command.js";
|
|
111
|
-
export { searchesCommand } from "./messenger/searches-command.js";
|
|
111
|
+
export { SEARCHES_QUERY, searchesCommand } from "./messenger/searches-command.js";
|
|
112
112
|
export { serveCommand, servingProfiles } from "./messenger/serve-command.js";
|
|
113
113
|
export { DAY_MS, FIRST_FETCH_MS, type MemberFetches, memberFetches } from "./messenger/serve-members.js";
|
|
114
114
|
export { STEMS_EVERY_MS, STEMS_SLICE_MS, type StemFills, stemFills } from "./messenger/serve-stems.js";
|
package/dist/cli/index.js
CHANGED
|
@@ -103,7 +103,7 @@ export { reviewCommand } from "./messenger/review.js";
|
|
|
103
103
|
export { scheduleCommand } from "./messenger/schedule-command.js";
|
|
104
104
|
export { allHitItem, messageHitItem, noteHitItem, } from "./messenger/search-answers.js";
|
|
105
105
|
export { searchCommand } from "./messenger/search-command.js";
|
|
106
|
-
export { searchesCommand } from "./messenger/searches-command.js";
|
|
106
|
+
export { SEARCHES_QUERY, searchesCommand } from "./messenger/searches-command.js";
|
|
107
107
|
export { serveCommand, servingProfiles } from "./messenger/serve-command.js";
|
|
108
108
|
export { DAY_MS, FIRST_FETCH_MS, memberFetches } from "./messenger/serve-members.js";
|
|
109
109
|
export { STEMS_EVERY_MS, STEMS_SLICE_MS, stemFills } from "./messenger/serve-stems.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { INPUT_FIELD_REASONS } from "../errors/reasons.js";
|
|
2
|
+
export const showInputFields = (object, command) => {
|
|
3
|
+
if (!INPUT_FIELD_REASONS.some((reason) => reason.reason === object.error.reason))
|
|
4
|
+
return;
|
|
5
|
+
const fields = object.error.details?.fields;
|
|
6
|
+
if (!Array.isArray(fields) || fields.length === 0 || !fields.every((field) => typeof field === "string"))
|
|
7
|
+
return;
|
|
8
|
+
const options = new Map();
|
|
9
|
+
for (let at = command; at; at = at.parent)
|
|
10
|
+
for (const option of at.options)
|
|
11
|
+
if (!option.hidden && option.long && !options.has(option.attributeName()))
|
|
12
|
+
options.set(option.attributeName(), option.long);
|
|
13
|
+
const flags = [];
|
|
14
|
+
for (const field of fields) {
|
|
15
|
+
const flag = options.get(field);
|
|
16
|
+
if (!flag)
|
|
17
|
+
return;
|
|
18
|
+
if (!flags.includes(flag))
|
|
19
|
+
flags.push(flag);
|
|
20
|
+
}
|
|
21
|
+
const first = flags[0];
|
|
22
|
+
if (first === undefined)
|
|
23
|
+
return;
|
|
24
|
+
object.error.message = `${flags.join(", ")}: ${object.error.message}`;
|
|
25
|
+
object.error.param = first;
|
|
26
|
+
object.error.details = { ...object.error.details, fields: flags };
|
|
27
|
+
};
|
|
@@ -14,9 +14,10 @@ import { accountAnswer, renderItem, wholeList } from "./chat-answers.js";
|
|
|
14
14
|
import { messengerContext } from "./context.js";
|
|
15
15
|
import { privacyCommand } from "./records-command.js";
|
|
16
16
|
export const accountCommand = (messenger) => {
|
|
17
|
+
const tool = `${messenger.app.command} personal`;
|
|
17
18
|
const account = new Command("account")
|
|
18
19
|
.description("the logged-in account")
|
|
19
|
-
.addCommand(annotate(new Command("list"), { effects: REMOTE_READ })
|
|
20
|
+
.addCommand(annotate(new Command("list"), { effects: REMOTE_READ, examples: [`${tool} account list`] })
|
|
20
21
|
.description("every profile on this computer, and the account each is logged in as; asks the messenger nothing")
|
|
21
22
|
.action(async function () {
|
|
22
23
|
const context = messengerContext(this, messenger, { needsProfile: false });
|
|
@@ -45,7 +46,11 @@ export const accountCommand = (messenger) => {
|
|
|
45
46
|
await store?.close();
|
|
46
47
|
}
|
|
47
48
|
}))
|
|
48
|
-
.addCommand(annotate(new Command("show"), {
|
|
49
|
+
.addCommand(annotate(new Command("show"), {
|
|
50
|
+
effects: REMOTE_READ,
|
|
51
|
+
output: { entity: "account" },
|
|
52
|
+
examples: [`${tool} account show`],
|
|
53
|
+
})
|
|
49
54
|
.description("who this profile is logged in as; the phone number shows its last four digits")
|
|
50
55
|
.option("--show-phone", "print the whole phone number")
|
|
51
56
|
.action(async function () {
|
|
@@ -54,7 +59,7 @@ export const accountCommand = (messenger) => {
|
|
|
54
59
|
const account = await context.withMessenger((connection) => connection.me());
|
|
55
60
|
renderAnswer(this, context, itemOf(accountAnswer(messenger.provider, whole ? account : maskedAccount(account))));
|
|
56
61
|
}))
|
|
57
|
-
.addCommand(annotate(new Command("update"), { effects: REMOTE_WRITE })
|
|
62
|
+
.addCommand(annotate(new Command("update"), { effects: REMOTE_WRITE, examples: [`${tool} account update --first-name Alex`] })
|
|
58
63
|
.description("change the name, the description or the photo everyone sees on your profile")
|
|
59
64
|
.option("--first-name <name>", "your first name")
|
|
60
65
|
.option("--last-name <name>", "your last name")
|
|
@@ -11,8 +11,9 @@ import { messengerContext } from "./context.js";
|
|
|
11
11
|
const SESSIONS_QUERY = unfilteredQuery("account_session", accountSession);
|
|
12
12
|
/** `account sessions …` — where else the account is logged in, not this tool's own `session`. */
|
|
13
13
|
export const accountSessionsCommand = (messenger) => {
|
|
14
|
+
const tool = `${messenger.app.command} personal`;
|
|
14
15
|
const sessions = new Command("sessions").description(`where else this account is logged in — not \`${messenger.app.command} session\`, which is this tool's own login`);
|
|
15
|
-
withQuery(annotate(sessions.command("list"), { effects: REMOTE_READ }), SESSIONS_QUERY)
|
|
16
|
+
withQuery(annotate(sessions.command("list"), { effects: REMOTE_READ, examples: [`${tool} account sessions list`] }), SESSIONS_QUERY)
|
|
16
17
|
.description("every device and app logged in to this account; nothing is ended")
|
|
17
18
|
.action(async function (words) {
|
|
18
19
|
const context = messengerContext(this, messenger);
|
|
@@ -21,7 +22,11 @@ export const accountSessionsCommand = (messenger) => {
|
|
|
21
22
|
renderAnswer(this, context, wholeList("account sessions list", found.map(sessionAnswer)));
|
|
22
23
|
});
|
|
23
24
|
// No MCP tool, at any permission level: it logs the owner out of the phone.
|
|
24
|
-
sessions.addCommand(annotate(new Command("end"), {
|
|
25
|
+
sessions.addCommand(annotate(new Command("end"), {
|
|
26
|
+
effects: REMOTE_WRITE,
|
|
27
|
+
mcp: false,
|
|
28
|
+
examples: [`${tool} account sessions end --others`],
|
|
29
|
+
})
|
|
25
30
|
.description("log out every other device, your phone included; this one stays")
|
|
26
31
|
.option("--others", "every session but this one")
|
|
27
32
|
.action(async function () {
|
|
@@ -34,7 +34,10 @@ const summary = (run) => [
|
|
|
34
34
|
]
|
|
35
35
|
.filter(Boolean)
|
|
36
36
|
.join(", ");
|
|
37
|
-
const extractCommand = (messenger) => annotate(new Command("extract"), {
|
|
37
|
+
const extractCommand = (messenger) => annotate(new Command("extract"), {
|
|
38
|
+
effects: FETCH,
|
|
39
|
+
examples: [`${messenger.app.command} personal attachments extract --chat "Book club" --limit 10`],
|
|
40
|
+
})
|
|
38
41
|
.description("read the text of downloaded files — text, PDF/DOCX text layers, ODT/ODS/XLSX/PPTX/EPUB — into the local store, where search finds them")
|
|
39
42
|
.option("--chat <chat>", `only this chat's files; ${messenger.chatArgument}`)
|
|
40
43
|
.option("--from-dir <dir>", "match files in this nonrecursive directory; needs --chat")
|
|
@@ -121,13 +124,19 @@ const extractCommand = (messenger) => annotate(new Command("extract"), { effects
|
|
|
121
124
|
});
|
|
122
125
|
const row = ({ message, position, name, local_path, extraction }) => `${message} #${position} ${name ?? ""} ${extraction ? `${extraction.origin} ${extraction.chars}` : "no text"} ` +
|
|
123
126
|
(local_path ?? "not downloaded");
|
|
124
|
-
const listCommand = (messenger) => withQuery(withPaging(annotate(new Command("list"), {
|
|
127
|
+
const listCommand = (messenger) => withQuery(withPaging(annotate(new Command("list"), {
|
|
128
|
+
effects: REMOTE_READ,
|
|
129
|
+
examples: [`${messenger.app.command} personal attachments list --limit 10`],
|
|
130
|
+
}).description("files of stored messages, newest first: where each was saved and whether its text is held — never the text")), ATTACHMENT_QUERY).action(async function (words) {
|
|
125
131
|
const context = messengerContext(this, messenger);
|
|
126
132
|
const query = listQuery(typedQuery(words), ATTACHMENT_QUERY, "attachments list");
|
|
127
133
|
const found = await context.withServices((services) => services.attachments.list({ ...pageOf(context.settings), ...(query ? { query } : {}) }));
|
|
128
134
|
renderAnswer(this, context, pagedList("attachments list", context.settings, found), () => found.items.length === 0 ? "" : `${found.items.map(row).join("\n")}\n`);
|
|
129
135
|
});
|
|
130
|
-
const showCommand = (messenger) => annotate(new Command("show"), {
|
|
136
|
+
const showCommand = (messenger) => annotate(new Command("show"), {
|
|
137
|
+
effects: REMOTE_READ,
|
|
138
|
+
examples: [`${messenger.app.command} personal attachments show "Book club" 101 --attachment 1`],
|
|
139
|
+
})
|
|
131
140
|
.description("read a bounded chunk of one retained attachment; JSON includes base64 bytes")
|
|
132
141
|
.argument("<chat>", `${messenger.chatArgument}; or a msg: locator alone`)
|
|
133
142
|
.argument("[message]", "the message id")
|
|
@@ -159,7 +168,10 @@ const showCommand = (messenger) => annotate(new Command("show"), { effects: REMO
|
|
|
159
168
|
});
|
|
160
169
|
const textCommand = (messenger) => {
|
|
161
170
|
const text = new Command("text").description("the text of one file, as an agent read it");
|
|
162
|
-
readsStdin(annotate(text.command("set"), {
|
|
171
|
+
readsStdin(annotate(text.command("set"), {
|
|
172
|
+
effects: LOCAL_WRITE,
|
|
173
|
+
examples: [`${messenger.app.command} personal attachments text set "Book club" 101 --text-file ./transcript.txt`],
|
|
174
|
+
}), {
|
|
163
175
|
name: "text",
|
|
164
176
|
description: "the text of the file",
|
|
165
177
|
})
|
|
@@ -54,6 +54,11 @@ export declare const conversationShown: (account: AccountKey, { summary, message
|
|
|
54
54
|
excerpt?: string | undefined;
|
|
55
55
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
56
56
|
score?: number | undefined;
|
|
57
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
58
|
+
corrections?: {
|
|
59
|
+
from: string;
|
|
60
|
+
to: string[];
|
|
61
|
+
}[] | undefined;
|
|
57
62
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
58
63
|
discovery?: {
|
|
59
64
|
coverage: number;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { annotate } from "@wirecat/cli-base/commands";
|
|
2
|
+
import { commandAt } from "../steps.js";
|
|
3
|
+
export const conversationExamples = (root, tool) => {
|
|
4
|
+
const examples = {
|
|
5
|
+
"batches next": '--chat "Book club"',
|
|
6
|
+
"batches status": '--chat "Book club"',
|
|
7
|
+
build: '--chat "Book club"',
|
|
8
|
+
"consents list": "",
|
|
9
|
+
"consents revoke": '--chat "Book club"',
|
|
10
|
+
"embed clear": '--chat "Book club"',
|
|
11
|
+
"embed status": '--chat "Book club"',
|
|
12
|
+
"links add": '--batch "$batch"',
|
|
13
|
+
"links clear": '--chat "Book club"',
|
|
14
|
+
list: '"Book club"',
|
|
15
|
+
related: '"Book club" 101',
|
|
16
|
+
show: '"Book club" 101',
|
|
17
|
+
status: "",
|
|
18
|
+
};
|
|
19
|
+
for (const [path, args] of Object.entries(examples)) {
|
|
20
|
+
const command = commandAt(root, path);
|
|
21
|
+
if (command)
|
|
22
|
+
annotate(command, { examples: [`${tool} personal conversations ${path} ${args}`.trimEnd()] });
|
|
23
|
+
}
|
|
24
|
+
commandAt(root, "links add")?.addHelpText("after", "\nSet batch to the id from conversations batches next; supply the JSON answer to that batch on stdin.\n");
|
|
25
|
+
};
|
|
@@ -21,6 +21,7 @@ import { nextStep, rootOf, runnable, stepLine } from "../steps.js";
|
|
|
21
21
|
import { answerOf as askOwner } from "./ask.js";
|
|
22
22
|
import { messengerContext, refuseLocalWrite } from "./context.js";
|
|
23
23
|
import { conversationShown, foundConversations, readinessList, refreshedAnswer } from "./conversation-answers.js";
|
|
24
|
+
import { conversationExamples } from "./conversation-examples.js";
|
|
24
25
|
import { messageItems } from "./message-items.js";
|
|
25
26
|
import { syncOptions, syncRequest } from "./search-sync-options.js";
|
|
26
27
|
import { readAll } from "./stdin.js";
|
|
@@ -363,6 +364,7 @@ export const conversationsCommand = (messenger) => {
|
|
|
363
364
|
renderAnswer(this, context, itemOf({ revoked: true }));
|
|
364
365
|
});
|
|
365
366
|
conversations.addCommand(embed);
|
|
367
|
+
conversationExamples(conversations, messenger.app.command);
|
|
366
368
|
return conversations;
|
|
367
369
|
};
|
|
368
370
|
/**
|
|
@@ -52,7 +52,10 @@ export const mcpCommand = (messenger) => {
|
|
|
52
52
|
const { serveOverStdio } = await import("../../mcp/server.js");
|
|
53
53
|
await serveOverStdio(this, context, messenger, {});
|
|
54
54
|
});
|
|
55
|
-
command.addCommand(withFlags(annotate(new Command("config"), {
|
|
55
|
+
command.addCommand(withFlags(annotate(new Command("config"), {
|
|
56
|
+
effects: LOCAL_READ,
|
|
57
|
+
examples: [`${app.command} personal mcp config`],
|
|
58
|
+
}).description("print the mcpServers entry for Claude Desktop, Cursor and others, with full paths; writes nothing")).action(async function () {
|
|
56
59
|
const flags = this.optsWithGlobals();
|
|
57
60
|
const context = messengerContext(this, messenger);
|
|
58
61
|
const { settings, env } = context;
|
|
@@ -67,7 +70,10 @@ export const mcpCommand = (messenger) => {
|
|
|
67
70
|
// Pasted into a file, so a person gets the client's own JSON, indented; a script finds it under `config`.
|
|
68
71
|
renderAnswer(this, context, itemOf({ config: entry.config }, { next: entry.nodeBound ? [nextStep("mcp.config", { args: {} })] : [] }), () => JSON.stringify(entry.config, null, 2));
|
|
69
72
|
}));
|
|
70
|
-
command.addCommand(withFlags(grantsAuthority(annotate(new Command("setup"), {
|
|
73
|
+
command.addCommand(withFlags(grantsAuthority(annotate(new Command("setup"), {
|
|
74
|
+
effects: LOCAL_WRITE,
|
|
75
|
+
examples: [`${app.command} personal mcp setup codex --allow-writes`],
|
|
76
|
+
}))
|
|
71
77
|
.description("add this profile's local MCP server to Codex or Claude Code")
|
|
72
78
|
.argument("<client>", "codex or claude-code")
|
|
73
79
|
.option("--allow-writes", "acknowledge that this profile offers writing tools")).action(async function (client) {
|
|
@@ -98,7 +104,10 @@ export const mcpCommand = (messenger) => {
|
|
|
98
104
|
throw new CliError("validation_error", error instanceof Error ? error.message : "MCP setup failed");
|
|
99
105
|
}
|
|
100
106
|
}));
|
|
101
|
-
command.addCommand(withFlags(annotate(new Command("doctor"), {
|
|
107
|
+
command.addCommand(withFlags(annotate(new Command("doctor"), {
|
|
108
|
+
effects: LOCAL_READ,
|
|
109
|
+
examples: [`${app.command} personal mcp doctor`],
|
|
110
|
+
}).description("check this profile's local MCP handshake and tool list")).action(async function () {
|
|
102
111
|
const context = messengerContext(this, messenger);
|
|
103
112
|
const { settings, env } = context;
|
|
104
113
|
const given = environmentOf(this).mcp ?? {};
|
|
@@ -31,6 +31,11 @@ export declare const threadAnswer: ({ items, ...thread }: ThreadContext, reader:
|
|
|
31
31
|
excerpt?: string | undefined;
|
|
32
32
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
33
33
|
score?: number | undefined;
|
|
34
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
35
|
+
corrections?: {
|
|
36
|
+
from: string;
|
|
37
|
+
to: string[];
|
|
38
|
+
}[] | undefined;
|
|
34
39
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
35
40
|
discovery?: {
|
|
36
41
|
coverage: number;
|
|
@@ -33,6 +33,11 @@ export declare const writtenAnswer: ({ operationId, sendId, message, scheduledFo
|
|
|
33
33
|
excerpt?: string | undefined;
|
|
34
34
|
matched_in?: "context" | "file" | "filename" | "picture" | "text" | "transcript" | undefined;
|
|
35
35
|
score?: number | undefined;
|
|
36
|
+
match?: "beginnings" | "corrected" | "words" | undefined;
|
|
37
|
+
corrections?: {
|
|
38
|
+
from: string;
|
|
39
|
+
to: string[];
|
|
40
|
+
}[] | undefined;
|
|
36
41
|
found_in?: "archive" | "both" | "server" | undefined;
|
|
37
42
|
discovery?: {
|
|
38
43
|
coverage: number;
|
|
@@ -57,9 +57,10 @@ const downloaded = (body) => "item" in body ? `${body.item.id}: downloaded and w
|
|
|
57
57
|
const modelLine = (item, width) => `${item.default ? "*" : " "} ${item.id.padEnd(width)} ${megabytes(item.size_bytes).padStart(7)} ` +
|
|
58
58
|
(item.downloaded ? "downloaded" : "—".padEnd(10));
|
|
59
59
|
export const modelsCommand = (messenger) => {
|
|
60
|
+
const tool = `${messenger.app.command} personal`;
|
|
60
61
|
const models = annotate(new Command("models"), { mcp: false }).description("models that run on this machine");
|
|
61
62
|
const audio = models.command("audio").description("speech models for transcribing voice messages");
|
|
62
|
-
withQuery(annotate(audio.command("list"), { effects: LOCAL_READ }), MODELS_QUERY)
|
|
63
|
+
withQuery(annotate(audio.command("list"), { effects: LOCAL_READ, examples: [`${tool} models audio list`] }), MODELS_QUERY)
|
|
63
64
|
.description("the speech models, most suitable first, which are downloaded, and which one is the default")
|
|
64
65
|
.action(async function (words) {
|
|
65
66
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -73,7 +74,11 @@ export const modelsCommand = (messenger) => {
|
|
|
73
74
|
context.renderer.note(`kept in ${directory}`);
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
|
-
reachesOtherService(annotate(audio.command("download"), {
|
|
77
|
+
reachesOtherService(annotate(audio.command("download"), {
|
|
78
|
+
effects: FETCH,
|
|
79
|
+
output: { entity: "model" },
|
|
80
|
+
examples: [`${tool} models audio download parakeet-v3`],
|
|
81
|
+
}))
|
|
77
82
|
.argument("<model>", "a model id from `models audio list`")
|
|
78
83
|
.description("download a speech model once, checked against the sha256 this version expects")
|
|
79
84
|
.action(async function (id) {
|
|
@@ -101,7 +106,7 @@ export const modelsCommand = (messenger) => {
|
|
|
101
106
|
}, { unbounded: true });
|
|
102
107
|
});
|
|
103
108
|
const text = models.command("text").description("embedding models for searching conversations by meaning");
|
|
104
|
-
withQuery(annotate(text.command("list"), { effects: LOCAL_READ }), MODELS_QUERY)
|
|
109
|
+
withQuery(annotate(text.command("list"), { effects: LOCAL_READ, examples: [`${tool} models text list`] }), MODELS_QUERY)
|
|
105
110
|
.description("the embedding models, most suitable first, which are downloaded, and which one is the default")
|
|
106
111
|
.action(async function (words) {
|
|
107
112
|
const context = baseContext(this, messenger.resolveSettings);
|
|
@@ -115,7 +120,11 @@ export const modelsCommand = (messenger) => {
|
|
|
115
120
|
context.renderer.note(`kept in ${directory}`);
|
|
116
121
|
});
|
|
117
122
|
});
|
|
118
|
-
reachesOtherService(annotate(text.command("download"), {
|
|
123
|
+
reachesOtherService(annotate(text.command("download"), {
|
|
124
|
+
effects: FETCH,
|
|
125
|
+
output: { entity: "model" },
|
|
126
|
+
examples: [`${tool} models text download ${DEFAULT_TEXT_MODEL}`],
|
|
127
|
+
}))
|
|
119
128
|
.argument("<model>", "a model id from `models text list`")
|
|
120
129
|
.addOption(new Option("--accept-terms", "accept the model's licence terms, for a model that has its own"))
|
|
121
130
|
.description("download an embedding model once, checked against the sha256 this version expects")
|
|
@@ -131,7 +140,7 @@ export const modelsCommand = (messenger) => {
|
|
|
131
140
|
}, { unbounded: true });
|
|
132
141
|
});
|
|
133
142
|
const key = text.command("key").description("API keys for embedding and analysis providers");
|
|
134
|
-
grantsAuthority(annotate(key.command("set"), { effects: LOCAL_WRITE }))
|
|
143
|
+
grantsAuthority(annotate(key.command("set"), { effects: LOCAL_WRITE, examples: [`${tool} models text key set openai`] }))
|
|
135
144
|
.argument("<provider>", "openai, anthropic, or the host of a --base-url server that wants a key")
|
|
136
145
|
.description("store a key, typed at a hidden prompt or piped on stdin — never as an argument")
|
|
137
146
|
.action(async function (provider) {
|
|
@@ -145,7 +154,7 @@ export const modelsCommand = (messenger) => {
|
|
|
145
154
|
renderAnswer(this, context, itemOf({ provider, stored: source }));
|
|
146
155
|
});
|
|
147
156
|
});
|
|
148
|
-
grantsAuthority(annotate(key.command("remove"), { effects: LOCAL_WRITE }))
|
|
157
|
+
grantsAuthority(annotate(key.command("remove"), { effects: LOCAL_WRITE, examples: [`${tool} models text key remove openai`] }))
|
|
149
158
|
.argument("<provider>", "openai, anthropic, or a server's host")
|
|
150
159
|
.description("forget a stored key")
|
|
151
160
|
.action(async function (provider) {
|
|
@@ -22,7 +22,10 @@ export const pollsCommand = (messenger) => {
|
|
|
22
22
|
const polls = new Command("polls").description(messenger.pollClose === false
|
|
23
23
|
? "read a poll, vote in it, create one"
|
|
24
24
|
: "read a poll, vote in it, close your own, create one");
|
|
25
|
-
annotate(polls.command("show"), {
|
|
25
|
+
annotate(polls.command("show"), {
|
|
26
|
+
effects: REMOTE_READ,
|
|
27
|
+
examples: [`${messenger.app.command} personal polls show "Book club" 101`],
|
|
28
|
+
})
|
|
26
29
|
.description("a poll and its answer ids, as the message carries it now")
|
|
27
30
|
.argument("<chat>", messenger.chatArgument)
|
|
28
31
|
.argument("<message>", "the id of the message that carries the poll")
|
|
@@ -35,7 +38,10 @@ export const pollsCommand = (messenger) => {
|
|
|
35
38
|
renderAnswer(this, context, itemOf(snakeKeys(poll)));
|
|
36
39
|
});
|
|
37
40
|
if (messenger.pollVoters)
|
|
38
|
-
polls.addCommand(withQuery(annotate(annotate(new Command("voters"), {
|
|
41
|
+
polls.addCommand(withQuery(annotate(annotate(new Command("voters"), {
|
|
42
|
+
effects: REMOTE_READ,
|
|
43
|
+
examples: [`${messenger.app.command} personal polls voters "Book club" 101 answers:0 --limit 10`],
|
|
44
|
+
})
|
|
39
45
|
.description(messenger.pollAnonymous === false
|
|
40
46
|
? "who voted for what, newest first"
|
|
41
47
|
: "who voted for what, newest first; not in an anonymous poll")
|
|
@@ -60,7 +66,10 @@ export const pollsCommand = (messenger) => {
|
|
|
60
66
|
args: { chat, message, ...(words.length > 0 ? { query: words.join(" ") } : {}) },
|
|
61
67
|
}), lines(({ voted_at, person_id, name, answers }) => [voted_at, name ?? person_id, answers.join(",")].join(" ")));
|
|
62
68
|
}));
|
|
63
|
-
annotate(polls.command("vote"), {
|
|
69
|
+
annotate(polls.command("vote"), {
|
|
70
|
+
effects: REMOTE_WRITE,
|
|
71
|
+
examples: [`${messenger.app.command} personal polls vote "Book club" 101 0`],
|
|
72
|
+
})
|
|
64
73
|
.description(messenger.pollAnonymous === false
|
|
65
74
|
? "vote in a poll, or take your vote back; the others see it"
|
|
66
75
|
: "vote in a poll, or take your vote back; the others see it unless the poll is anonymous")
|
|
@@ -85,7 +94,10 @@ export const pollsCommand = (messenger) => {
|
|
|
85
94
|
renderAnswer(this, context, itemOf(snakeKeys(voted)));
|
|
86
95
|
});
|
|
87
96
|
if (messenger.pollClose !== false)
|
|
88
|
-
annotate(polls.command("close"), {
|
|
97
|
+
annotate(polls.command("close"), {
|
|
98
|
+
effects: REMOTE_WRITE,
|
|
99
|
+
examples: [`${messenger.app.command} personal polls close "Book club" 101`],
|
|
100
|
+
})
|
|
89
101
|
.description("close your own poll; nobody can vote after that, and it cannot be reopened")
|
|
90
102
|
.argument("<chat>", messenger.chatArgument)
|
|
91
103
|
.argument("<message>", "the id of your own message that carries the poll")
|
|
@@ -94,7 +106,10 @@ export const pollsCommand = (messenger) => {
|
|
|
94
106
|
const closed = await context.withMessenger((connection) => guardedClose(context.guard, connection, { chat, message: message.trim() }));
|
|
95
107
|
renderAnswer(this, context, itemOf(snakeKeys(closed)));
|
|
96
108
|
});
|
|
97
|
-
const create = annotate(polls.command("create"), {
|
|
109
|
+
const create = annotate(polls.command("create"), {
|
|
110
|
+
effects: REMOTE_WRITE,
|
|
111
|
+
examples: [`${messenger.app.command} personal polls create "Book club" "Which day?" Monday Tuesday`],
|
|
112
|
+
})
|
|
98
113
|
.description(messenger.pollAnonymous === false
|
|
99
114
|
? "send a poll to a chat, as a message of its own; everyone sees who voted for what"
|
|
100
115
|
: "send a poll to a chat, as a message of its own; public unless --anonymous")
|
|
@@ -82,7 +82,11 @@ export const callsCommand = (messenger) => {
|
|
|
82
82
|
};
|
|
83
83
|
/** `chats delete` and `chats clear` — for this account only; at the default level they ask first. */
|
|
84
84
|
export const chatDeletionCommands = (messenger) => [
|
|
85
|
-
annotate(new Command("delete"), {
|
|
85
|
+
annotate(new Command("delete"), {
|
|
86
|
+
effects: REMOTE_WRITE,
|
|
87
|
+
mcp: false,
|
|
88
|
+
examples: [`${messenger.app.command} personal chats delete "Book club"`],
|
|
89
|
+
})
|
|
86
90
|
.description("delete a chat from this account; the others in it keep it and its messages")
|
|
87
91
|
.argument("<chat>", messenger.chatArgument)
|
|
88
92
|
.addOption(ownerOnlyOption("--allow-dangerous", "go ahead without the question an ask level puts before a deletion"))
|
|
@@ -90,7 +94,11 @@ export const chatDeletionCommands = (messenger) => [
|
|
|
90
94
|
const context = messengerContext(this, messenger);
|
|
91
95
|
renderAnswer(this, context, itemOf(snakeKeys(await context.withServices((services) => services.admin.deleteChat(chat)))));
|
|
92
96
|
}),
|
|
93
|
-
annotate(new Command("clear"), {
|
|
97
|
+
annotate(new Command("clear"), {
|
|
98
|
+
effects: REMOTE_WRITE,
|
|
99
|
+
mcp: false,
|
|
100
|
+
examples: [`${messenger.app.command} personal chats clear "Book club"`],
|
|
101
|
+
})
|
|
94
102
|
.description("delete every message in a chat for this account; the others in it keep theirs")
|
|
95
103
|
.argument("<chat>", messenger.chatArgument)
|
|
96
104
|
.addOption(ownerOnlyOption("--allow-dangerous", "go ahead without the question an ask level puts before a deletion"))
|
|
@@ -21,6 +21,8 @@ export const messageHitItem = (hit, query = "") => {
|
|
|
21
21
|
excerpt: hit.excerpt ?? excerpt(hit.text, query),
|
|
22
22
|
matched_in: hit.matchedIn,
|
|
23
23
|
score: hit.score ?? undefined,
|
|
24
|
+
match: hit.match,
|
|
25
|
+
corrections: hit.corrections,
|
|
24
26
|
found_in: hit.source,
|
|
25
27
|
discovery: hit.discovery
|
|
26
28
|
? defined({
|
|
@@ -6,6 +6,7 @@ import { searchNotesOf } from "../../services/notes-search.js";
|
|
|
6
6
|
import { LOCAL_READ, REMOTE_READ } from "../effects.js";
|
|
7
7
|
import { renderAnswer } from "../envelope.js";
|
|
8
8
|
import { positiveCount } from "../paging.js";
|
|
9
|
+
import { commandAt } from "../steps.js";
|
|
9
10
|
import { messengerContext } from "./context.js";
|
|
10
11
|
import { conversationsSearchCommand } from "./conversations-command.js";
|
|
11
12
|
import { messagesSearchCommand } from "./messages-search-command.js";
|
|
@@ -157,5 +158,16 @@ export const searchCommand = (messenger, { topics = false } = {}) => {
|
|
|
157
158
|
.addCommand(conversationsSearchCommand(messenger));
|
|
158
159
|
if (topics)
|
|
159
160
|
search.addCommand(topicsSearchCommand(messenger));
|
|
161
|
+
for (const [path, args] of Object.entries({
|
|
162
|
+
all: "draft",
|
|
163
|
+
conversations: '"Reviewing the project draft"',
|
|
164
|
+
messages: "draft --limit 10",
|
|
165
|
+
notes: "draft --limit 10",
|
|
166
|
+
topics: '"Book club" draft',
|
|
167
|
+
})) {
|
|
168
|
+
const command = commandAt(search, path);
|
|
169
|
+
if (command)
|
|
170
|
+
annotate(command, { examples: [`${messenger.app.command} personal search ${path} ${args}`] });
|
|
171
|
+
}
|
|
160
172
|
return search;
|
|
161
173
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { type Messenger } from "./context.js";
|
|
3
|
+
/** `searches list` takes a query over the saved search fields, none of them a filter: the list is short and whole. */
|
|
4
|
+
export declare const SEARCHES_QUERY: import("@wirecat/cli-base/query").QueryRegistry;
|
|
3
5
|
/** Saved searches and the history of `search messages` and `stats messages show`, in the local store only. */
|
|
4
6
|
export declare const searchesCommand: (messenger: Messenger) => Command;
|
|
@@ -20,7 +20,8 @@ const line = ({ id, name, command, params, runs, lastRunAt }) => [
|
|
|
20
20
|
`${runs} runs`,
|
|
21
21
|
lastRunAt?.slice(0, 16).replace("T", " ") ?? "never run",
|
|
22
22
|
].join(" ");
|
|
23
|
-
|
|
23
|
+
/** `searches list` takes a query over the saved search fields, none of them a filter: the list is short and whole. */
|
|
24
|
+
export const SEARCHES_QUERY = unfilteredQuery("saved_search", savedSearch);
|
|
24
25
|
/** Saved searches and the history of `search messages` and `stats messages show`, in the local store only. */
|
|
25
26
|
export const searchesCommand = (messenger) => {
|
|
26
27
|
const searches = new Command("searches").description("saved searches and the history of search messages and stats messages show, kept in the local store; --saved runs one");
|