@wirecat/cli-core 0.549.0 → 0.551.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 +223 -22
- package/THIRD_PARTY_NOTICES +41 -0
- package/dist/calendar/ics.d.ts +1 -1
- package/dist/calendar/ics.js +5 -2
- package/dist/calendar/windows-zones.generated.d.ts +1 -0
- package/dist/calendar/windows-zones.generated.js +146 -0
- package/dist/cli/compact.js +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli/meeting-findings-input.js +4 -3
- package/dist/cli/messenger/backfill-command.js +28 -1
- package/dist/cli/messenger/context.js +1 -0
- package/dist/cli/messenger/history-gaps.js +4 -4
- package/dist/cli/messenger/mcp-command.js +1 -0
- package/dist/cli/messenger/messages-delete-command.js +7 -8
- package/dist/cli/messenger/projects-command.js +47 -2
- package/dist/cli/payment-query.d.ts +8 -0
- package/dist/cli/payment-query.js +27 -0
- package/dist/cli/project-status-rows.d.ts +8 -0
- package/dist/cli/project-status-rows.js +102 -0
- package/dist/cli/project-status.d.ts +132 -0
- package/dist/cli/project-status.js +248 -0
- package/dist/cli/steps.js +4 -1
- package/dist/domain/knowledge.d.ts +1 -1
- package/dist/domain/knowledge.js +1 -1
- package/dist/domain/status-words.d.ts +3 -0
- package/dist/domain/status-words.js +44 -0
- package/dist/entities/fact.d.ts +12 -1
- package/dist/entities/fact.js +13 -0
- package/dist/entities/index.d.ts +18 -2
- package/dist/entities/meeting-findings.d.ts +1 -1
- package/dist/entities/message-transfer.d.ts +16 -0
- package/dist/entities/message-transfer.js +6 -0
- package/dist/errors/reasons.js +1 -0
- package/dist/mail/partial.d.ts +9 -3
- package/dist/mail/partial.js +35 -10
- package/dist/mail/source.d.ts +7 -1
- package/dist/mail/sync.js +1 -1
- package/dist/mcp/app-server.js +1 -0
- package/dist/mcp/http/oauth.d.ts +2 -1
- package/dist/mcp/http/oauth.js +2 -2
- package/dist/mcp/http/serve.d.ts +2 -0
- package/dist/mcp/http/serve.js +2 -1
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/surface.js +1 -1
- package/dist/meetings/findings-rules.d.ts +14 -3
- package/dist/meetings/findings-rules.js +16 -2
- package/dist/meetings/index.d.ts +3 -2
- package/dist/meetings/index.js +2 -1
- package/dist/meetings/meeting-context.d.ts +2 -3
- package/dist/meetings/meeting-context.js +3 -4
- package/dist/meetings/payment.d.ts +27 -0
- package/dist/meetings/payment.js +81 -0
- package/dist/services/archive.d.ts +13 -0
- package/dist/services/archive.js +56 -5
- package/dist/services/deps.d.ts +3 -0
- package/dist/services/history-limits.d.ts +2 -0
- package/dist/services/index.d.ts +4 -1
- package/dist/services/index.js +4 -1
- package/dist/services/job-query.d.ts +5 -2
- package/dist/services/job-query.js +5 -2
- package/dist/services/link-scorers/alias-hits.d.ts +7 -0
- package/dist/services/link-scorers/alias-hits.js +54 -0
- package/dist/services/link-scorers/chat-senders.d.ts +6 -0
- package/dist/services/link-scorers/chat-senders.js +56 -0
- package/dist/services/link-scorers/chat-title.d.ts +6 -0
- package/dist/services/link-scorers/chat-title.js +44 -0
- package/dist/services/link-scorers/email-domain.d.ts +6 -0
- package/dist/services/link-scorers/email-domain.js +64 -0
- package/dist/services/link-scorers/index.d.ts +9 -0
- package/dist/services/link-scorers/index.js +8 -0
- package/dist/services/link-scorers/mail-thread.d.ts +3 -0
- package/dist/services/link-scorers/mail-thread.js +74 -0
- package/dist/services/link-scorers/member-chats.d.ts +3 -0
- package/dist/services/link-scorers/member-chats.js +40 -0
- package/dist/services/link-scorers/member-dialog.d.ts +3 -0
- package/dist/services/link-scorers/member-dialog.js +34 -0
- package/dist/services/link-scorers/scan.d.ts +41 -0
- package/dist/services/link-scorers/scan.js +244 -0
- package/dist/services/link-scorers/shared.d.ts +26 -0
- package/dist/services/link-scorers/shared.js +48 -0
- package/dist/services/link-scorers/types.d.ts +77 -0
- package/dist/services/link-scorers/types.js +1 -0
- package/dist/services/project-status/index.d.ts +71 -0
- package/dist/services/project-status/index.js +196 -0
- package/dist/services/project-status/items.d.ts +10 -0
- package/dist/services/project-status/items.js +23 -0
- package/dist/services/project-status/signals/amounts.d.ts +20 -0
- package/dist/services/project-status/signals/amounts.js +91 -0
- package/dist/services/project-status/signals/children.d.ts +13 -0
- package/dist/services/project-status/signals/children.js +28 -0
- package/dist/services/project-status/signals/coverage.d.ts +13 -0
- package/dist/services/project-status/signals/coverage.js +30 -0
- package/dist/services/project-status/signals/index.d.ts +3 -0
- package/dist/services/project-status/signals/index.js +21 -0
- package/dist/services/project-status/signals/last-message.d.ts +4 -0
- package/dist/services/project-status/signals/last-message.js +22 -0
- package/dist/services/project-status/signals/payments.d.ts +9 -0
- package/dist/services/project-status/signals/payments.js +24 -0
- package/dist/services/project-status/signals/promises.d.ts +17 -0
- package/dist/services/project-status/signals/promises.js +109 -0
- package/dist/services/project-status/signals/questions.d.ts +8 -0
- package/dist/services/project-status/signals/questions.js +38 -0
- package/dist/services/project-status/signals/reply-next.d.ts +19 -0
- package/dist/services/project-status/signals/reply-next.js +75 -0
- package/dist/services/project-status/signals/topics.d.ts +10 -0
- package/dist/services/project-status/signals/topics.js +50 -0
- package/dist/services/project-status/types.d.ts +84 -0
- package/dist/services/project-status/types.js +1 -0
- package/dist/services/proposals/index.d.ts +1 -0
- package/dist/services/proposals/index.js +1 -0
- package/dist/services/proposals/kinds/findings.d.ts +3 -0
- package/dist/services/proposals/kinds/findings.js +8 -1
- package/dist/store/decisions.d.ts +5 -1
- package/dist/store/decisions.js +5 -1
- package/dist/store/evidence-findings.js +1 -0
- package/dist/store/index.d.ts +2 -1
- package/dist/store/index.js +1 -0
- package/dist/store/project-transfer/request.d.ts +1 -1
- package/dist/store/project-transfer/schema.d.ts +2 -2
- package/dist/store/project-transfer/snapshot-model.d.ts +2 -2
- package/dist/store/sqlite/email-locations.d.ts +22 -1
- package/dist/store/sqlite/email-locations.js +29 -2
- package/dist/store/sqlite/emails.d.ts +3 -7
- package/dist/store/sqlite/emails.js +4 -2
- package/dist/store/sqlite/evidence-findings.js +5 -1
- package/dist/store/sqlite/facts.d.ts +13 -0
- package/dist/store/sqlite/facts.js +64 -6
- package/dist/store/sqlite/findings-core.js +14 -1
- package/dist/store/sqlite/knowledge.d.ts +16 -2
- package/dist/store/sqlite/knowledge.js +1 -0
- package/dist/store/sqlite/project-transfer/snapshot.d.ts +1 -1
- package/dist/store/sqlite/relation-proposals.d.ts +29 -2
- package/dist/store/sqlite/relation-proposals.js +75 -4
- package/dist/store/store.d.ts +1 -0
- package/package.json +3 -3
- package/parity.json +11 -0
|
@@ -33,6 +33,21 @@ import { stopOnSignal } from "./patience.js";
|
|
|
33
33
|
const ALL_SINCE = "90d";
|
|
34
34
|
/** What a background `--all` job is listed under; no chat argument is spelled like it. */
|
|
35
35
|
const ALL_CHATS = "--all";
|
|
36
|
+
/** Says where the history limits (history_* settings) stopped the read, so an agent does not take it for all. */
|
|
37
|
+
const limitedLine = (result, status) => {
|
|
38
|
+
if ("items" in result) {
|
|
39
|
+
const { groupTooLarge = 0, olderThanWindow = 0 } = result.limited ?? {};
|
|
40
|
+
if (groupTooLarge + olderThanWindow === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
return (`the history limits (history_* settings) left ${groupTooLarge} big group(s) unread and read ${olderThanWindow} ` +
|
|
43
|
+
`chat(s) only to the window's start — \`${status}\` lists them; \`messages sync <chat> --full-history\` reads one in full`);
|
|
44
|
+
}
|
|
45
|
+
if (result.limited === "group_too_large")
|
|
46
|
+
return "not read: a group above history_group_members_max — `--full-history` reads it and keeps it in full";
|
|
47
|
+
if (result.limited === "older_than_window")
|
|
48
|
+
return "read to the history window's start (history_dialog_days / history_group_days) — `--full-history` reads further";
|
|
49
|
+
return undefined;
|
|
50
|
+
};
|
|
36
51
|
export const syncCommand = (messenger) => {
|
|
37
52
|
const fetching = messenger.fetching ?? FETCHING;
|
|
38
53
|
return annotate(new Command("sync"), {
|
|
@@ -58,7 +73,7 @@ export const syncCommand = (messenger) => {
|
|
|
58
73
|
.option("--catch-up-chunks <n>", "at most this many local vector chunks", wholeNumber)
|
|
59
74
|
.option("--catch-up-messages <n>", "skip a graph rebuild larger than this many messages", wholeNumber)
|
|
60
75
|
.option("--catch-up-time <duration>", "local preparation time budget, 30s by default")
|
|
61
|
-
.option("--full-history", "keep this chat's
|
|
76
|
+
.option("--full-history", "read and keep this chat's history in full from now on, past the history limits (history_* settings)")
|
|
62
77
|
.option("--background", "run as a job that outlives this command; `store jobs show` follows it")
|
|
63
78
|
.option("--dry-run", "only estimate how many messages, requests and minutes a full fetch would still take — from the store, no request")
|
|
64
79
|
.action(async function (chat) {
|
|
@@ -151,6 +166,7 @@ export const syncCommand = (messenger) => {
|
|
|
151
166
|
limit,
|
|
152
167
|
pageSize,
|
|
153
168
|
pauseMs,
|
|
169
|
+
...(include ? { fullHistory: true } : {}),
|
|
154
170
|
catchUp: prepare ? preparation : false,
|
|
155
171
|
...(sinceMs === undefined ? {} : { sinceMs }),
|
|
156
172
|
...(last === undefined ? {} : { last }),
|
|
@@ -191,9 +207,20 @@ export const syncCommand = (messenger) => {
|
|
|
191
207
|
]
|
|
192
208
|
: []),
|
|
193
209
|
...(toReview > 0 ? [nextStep("mentions.review", { args: { new: true }, values: { shown } })] : []),
|
|
210
|
+
...("limited" in result && result.limited && "chat" in result && result.chat
|
|
211
|
+
? [
|
|
212
|
+
{
|
|
213
|
+
...nextStep("messages.sync", { args: { chat: result.chat, full_history: true } }),
|
|
214
|
+
why: "reads this chat past the history limits and keeps it in full from now on",
|
|
215
|
+
},
|
|
216
|
+
]
|
|
217
|
+
: []),
|
|
194
218
|
],
|
|
195
219
|
meta: window.meta,
|
|
196
220
|
}));
|
|
221
|
+
const limitedNote = limitedLine(result, lineOf(this, "store status"));
|
|
222
|
+
if (limitedNote)
|
|
223
|
+
context.renderer.note(limitedNote);
|
|
197
224
|
if (tasks?.held)
|
|
198
225
|
context.renderer.note(`the first sync left ${tasks.held} possible task(s) in ${tasks.heldChats ?? 0} chat(s) unopened ` +
|
|
199
226
|
`(config task_first_pass_per_chat) — ask the owner, then \`${lineOf(this, "tasks scan <chat>")}\``);
|
|
@@ -422,6 +422,7 @@ export const messengerContext = (command, messenger, resolving = {}) => {
|
|
|
422
422
|
// Turning recording off by name (--no-record, or record: false) keeps no search history either.
|
|
423
423
|
history: base.settings.record,
|
|
424
424
|
searchCatchUp: base.settings.searchCatchUp,
|
|
425
|
+
...(base.settings.historyLimits ? { historyLimits: base.settings.historyLimits } : {}),
|
|
425
426
|
pictures: base.settings.pictureSearch,
|
|
426
427
|
reranker: base.settings.searchReranker,
|
|
427
428
|
guard,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { nextStep } from "../steps.js";
|
|
2
2
|
const SHOWN = 5;
|
|
3
3
|
const reasonOf = (skip) => skip.reason === "group_too_large"
|
|
4
|
-
? `a group of ${skip.members ?? "many"} people, not saved`
|
|
4
|
+
? `a group of ${skip.members ?? "many"} people, not ${skip.unread ? "read" : "saved"}`
|
|
5
5
|
: `only from ${skip.keptFrom?.slice(0, 10) ?? "the window's start"}`;
|
|
6
6
|
const nameOf = (skip) => skip.title ?? skip.chatId;
|
|
7
7
|
/**
|
|
@@ -15,7 +15,7 @@ export const historyLimitGaps = (skips) => {
|
|
|
15
15
|
const messages = skips.reduce((sum, skip) => sum + skip.messages, 0);
|
|
16
16
|
const listed = skips
|
|
17
17
|
.slice(0, SHOWN)
|
|
18
|
-
.map((skip) => `${nameOf(skip)} (${skip.messages}, ${reasonOf(skip)})`)
|
|
18
|
+
.map((skip) => `${nameOf(skip)} (${skip.messages > 0 ? `${skip.messages}, ` : ""}${reasonOf(skip)})`)
|
|
19
19
|
.join("; ");
|
|
20
20
|
const more = skips.length > SHOWN ? `; and ${skips.length - SHOWN} more — \`store status\` lists them` : "";
|
|
21
21
|
return {
|
|
@@ -28,8 +28,8 @@ export const historyLimitGaps = (skips) => {
|
|
|
28
28
|
missing: [
|
|
29
29
|
{
|
|
30
30
|
field: "history_limits",
|
|
31
|
-
why: `the history limits (history_* settings) left
|
|
32
|
-
`
|
|
31
|
+
why: `the history limits (history_* settings) left history of ${skips.length} chats out of the store` +
|
|
32
|
+
`${messages > 0 ? ` (${messages} pushed messages counted)` : ""}: ${listed}${more}`,
|
|
33
33
|
step: "messages.sync",
|
|
34
34
|
},
|
|
35
35
|
],
|
|
@@ -355,6 +355,7 @@ const serveProfiles = async (self, context, host, flags, set) => {
|
|
|
355
355
|
tokenFile,
|
|
356
356
|
lockFile: httpLockFile(tokenFile),
|
|
357
357
|
version: app.version,
|
|
358
|
+
reaches: app.messenger === false ? `your ${host.name}` : "your messages",
|
|
358
359
|
onCode: (code, expires) => context.renderer.note(env[mcpHttpMarker(app.envPrefix)] === undefined
|
|
359
360
|
? `login code for a new browser app: ${code} (until ${expires.toTimeString().slice(0, 5)}; a new one after each login)`
|
|
360
361
|
: `a new login code for a browser app: \`${app.command} mcp server status --profiles ${set.option}\``),
|
|
@@ -26,14 +26,13 @@ export const deleteCommand = (messenger) => annotate(new Command("delete"), {
|
|
|
26
26
|
if (environmentOf(this).agent && (everyone || messenger.deleteForMe === false))
|
|
27
27
|
throw new CliError("permission_error", `${everyone ? "" : `${messenger.name ?? messenger.provider} deletes only for everyone, and `}an agent never ` +
|
|
28
28
|
`deletes for everyone — the owner can run \`${lineOf(this, "messages delete --for-everyone")}\``, { permission: "messages.delete", sent: false });
|
|
29
|
+
const ids = messages
|
|
30
|
+
.flatMap((one) => one.split(","))
|
|
31
|
+
.map((one) => one.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
if (ids.length === 0)
|
|
34
|
+
throw new CliError("validation_error", "no message id given — name the messages to delete", { sent: false });
|
|
29
35
|
const context = messengerContext(this, messenger);
|
|
30
|
-
const deleted = await context.withServices((services) => services.messages.delete({
|
|
31
|
-
chat,
|
|
32
|
-
messages: messages
|
|
33
|
-
.flatMap((one) => one.split(","))
|
|
34
|
-
.map((one) => one.trim())
|
|
35
|
-
.filter(Boolean),
|
|
36
|
-
forEveryone: everyone,
|
|
37
|
-
}));
|
|
36
|
+
const deleted = await context.withServices((services) => services.messages.delete({ chat, messages: ids, forEveryone: everyone }));
|
|
38
37
|
renderAnswer(this, context, itemOf(snakeKeys(deleted)));
|
|
39
38
|
});
|
|
@@ -3,12 +3,15 @@ import { annotate, annotateOption } from "@wirecat/cli-base/commands";
|
|
|
3
3
|
import { Command, Option } from "commander";
|
|
4
4
|
import { parseDuration } from "../../domain/duration.js";
|
|
5
5
|
import { FETCHING } from "../../services/archive.js";
|
|
6
|
+
import { projectStatus, STATUS_SIGNALS } from "../../services/project-status/index.js";
|
|
6
7
|
import { gatherStoredProject } from "../../services/stored-project-gathering.js";
|
|
7
|
-
import {
|
|
8
|
+
import { answerEntity } from "../answer-entity.js";
|
|
9
|
+
import { FETCH, LOCAL_READ } from "../effects.js";
|
|
8
10
|
import { itemOf, renderAnswer, snakeKeys } from "../envelope.js";
|
|
9
11
|
import { positiveCount } from "../paging.js";
|
|
10
12
|
import { projectAnswerParts, projectCutoffTime, projectEvidenceText, projectWindowOf, withSyncCommands, } from "../project-evidence.js";
|
|
11
|
-
import {
|
|
13
|
+
import { projectStatusAnswer, projectStatusRow, projectStatusSchema, projectStatusText } from "../project-status.js";
|
|
14
|
+
import { lineOf, nextStep, rootOf, stepLine } from "../steps.js";
|
|
12
15
|
import { messengerContext } from "./context.js";
|
|
13
16
|
const collect = (value, previous) => [...previous, value];
|
|
14
17
|
const upTo = (most, flag) => (value) => {
|
|
@@ -128,8 +131,50 @@ export const projectsCommand = (messenger) => {
|
|
|
128
131
|
};
|
|
129
132
|
renderAnswer(this, context, itemOf(snakeKeys(withSyncCommands(found, hints.sync)), parts), () => projectEvidenceText(found, hints));
|
|
130
133
|
}));
|
|
134
|
+
projects.addCommand(statusCommand(messenger));
|
|
131
135
|
return projects;
|
|
132
136
|
};
|
|
137
|
+
/** Where a project stands from this profile's stored messages: no model call, nothing leaves the machine. */
|
|
138
|
+
const statusCommand = (messenger) => answerEntity(annotate(new Command("status"), {
|
|
139
|
+
examples: [`${messenger.app.command} personal projects status HRB --context-query 1C`],
|
|
140
|
+
effects: LOCAL_READ,
|
|
141
|
+
partial: true,
|
|
142
|
+
})
|
|
143
|
+
.summary("where a project stands in this account's linked chats: whose move, open asks, money")
|
|
144
|
+
.description("per chat confirmed as the project's (and its child projects'), from what the store holds: the last message, " +
|
|
145
|
+
"who replies next, open questions both ways, promises, amounts with their direction, payments recorded, " +
|
|
146
|
+
"how long each --context-query has been quiet on their side, and how much of the chat is held. Amounts, " +
|
|
147
|
+
"directions and promises found by words are marked pattern: read the cited message before trusting one. " +
|
|
148
|
+
"Only this profile's account is read; the other linked chats are named, and memo projects status reads them")
|
|
149
|
+
.argument("<project>", "an existing project's id, key, exact name or alias")
|
|
150
|
+
.option("--since-time <time>", "how far back: days (60d, at most 730d) or a start date (2026-09-01)", "60d")
|
|
151
|
+
.option("--before-time <time>", "fixed exclusive end: ISO 8601 timestamp with UTC offset")
|
|
152
|
+
.option("--context-query <words>", "words that all appear in one text; reports their last mention per side. Repeat for another", collect, [])
|
|
153
|
+
.action(async function (project) {
|
|
154
|
+
const options = this.opts();
|
|
155
|
+
const context = messengerContext(this, messenger);
|
|
156
|
+
const found = await context.withStore((store, account) => projectStatus(store, {
|
|
157
|
+
project,
|
|
158
|
+
accounts: [account],
|
|
159
|
+
...projectWindowOf(options.sinceTime),
|
|
160
|
+
...(options.beforeTime === undefined ? {} : { asOf: projectCutoffTime(options.beforeTime) }),
|
|
161
|
+
contextQueries: options.contextQuery,
|
|
162
|
+
}));
|
|
163
|
+
const root = rootOf(this);
|
|
164
|
+
const hints = {
|
|
165
|
+
sync: (chat, since) => nextStep("messages.sync", { args: { chat: chat.chatId, since_time: since } }),
|
|
166
|
+
read: (chat, since) => nextStep("messages.list", { args: { chat: chat.chatId, query: `date>=${since}` } }),
|
|
167
|
+
line: (step) => (step.kind === "command" ? lineOf(this, wordsOf(root, step)) : step.why),
|
|
168
|
+
};
|
|
169
|
+
const { item, ...parts } = projectStatusAnswer(found, hints);
|
|
170
|
+
renderAnswer(this, context, itemOf(item, parts), () => projectStatusText(found, hints));
|
|
171
|
+
}), "project_status", projectStatusSchema, { row: projectStatusRow(STATUS_SIGNALS) });
|
|
172
|
+
/** A step's words after the tool and the profile, so `lineOf` names the profile the run used. */
|
|
173
|
+
const wordsOf = (root, step) => {
|
|
174
|
+
const profile = root.getOptionValue("profile");
|
|
175
|
+
const typed = typeof profile === "string" && profile !== "" ? 2 : 1;
|
|
176
|
+
return stepLine(root, step).split(" ").slice(typed).join(" ");
|
|
177
|
+
};
|
|
133
178
|
const quote = (text) => (/^[\w:.-]+$/.test(text) ? text : `"${text.replace(/(["\\$`])/g, "\\$1")}"`);
|
|
134
179
|
/** A line per saved page, counts only; a timed heartbeat with titles is the sync-progress work's (cli-core#2121). */
|
|
135
180
|
const progressNote = (note) => ({ chat, index, of, fetched }) => note(`chat ${index + 1} of ${of} · ${chat} · ${fetched} messages so far`);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PaymentFilter } from "../store/store.js";
|
|
2
|
+
/**
|
|
3
|
+
* The terms a facts list adds for payments. `state` is the payment's, not `status`, which stays the review status
|
|
4
|
+
* (proposed, accepted).
|
|
5
|
+
*/
|
|
6
|
+
export declare const PAYMENT_QUERY_FIELDS: import("@wirecat/cli-base/query").FieldSpec[];
|
|
7
|
+
/** The store's payment filter from `queryTerms`' answer; `undefined` when no payment term was given. */
|
|
8
|
+
export declare const paymentFilterOf: (terms: Map<string, string>) => PaymentFilter | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { PAYMENT_DIRECTIONS, PAYMENT_STATES } from "../meetings/index.js";
|
|
3
|
+
import { termField } from "./list-query.js";
|
|
4
|
+
/**
|
|
5
|
+
* The terms a facts list adds for payments. `state` is the payment's, not `status`, which stays the review status
|
|
6
|
+
* (proposed, accepted).
|
|
7
|
+
*/
|
|
8
|
+
export const PAYMENT_QUERY_FIELDS = [
|
|
9
|
+
termField("open", "bool", "open:true"),
|
|
10
|
+
termField("direction", "enum", "direction:to_us", { values: PAYMENT_DIRECTIONS }),
|
|
11
|
+
termField("state", "enum", "state:paid", { values: PAYMENT_STATES }),
|
|
12
|
+
];
|
|
13
|
+
/** The store's payment filter from `queryTerms`' answer; `undefined` when no payment term was given. */
|
|
14
|
+
export const paymentFilterOf = (terms) => {
|
|
15
|
+
const open = terms.get("open");
|
|
16
|
+
const direction = terms.get("direction");
|
|
17
|
+
const state = terms.get("state");
|
|
18
|
+
if (open === undefined && direction === undefined && state === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (open !== undefined && open !== "true" && open !== "false")
|
|
21
|
+
throw new CliError("validation_error", "open takes true or false");
|
|
22
|
+
return {
|
|
23
|
+
...(open === undefined ? {} : { open: open === "true" }),
|
|
24
|
+
...(direction === undefined ? {} : { direction }),
|
|
25
|
+
...(state === undefined ? {} : { state }),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { RowShape } from "./compact.js";
|
|
2
|
+
/**
|
|
3
|
+
* The compact row of each status signal's items, by `status.<signal id>`; a signal's file and its row are the two
|
|
4
|
+
* places a new signal touches. They live here because a service may not import the CLI's row type.
|
|
5
|
+
*/
|
|
6
|
+
export declare const STATUS_SIGNAL_ROWS: Readonly<Record<string, RowShape>>;
|
|
7
|
+
/** The rest of a `project_status` answer: its chats, open money, chats left out, and failed signals. */
|
|
8
|
+
export declare const STATUS_ROWS: Readonly<Record<string, RowShape>>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const where = [{ field: "ref" }, { field: ["chat_title", "chat"] }];
|
|
2
|
+
const who = { field: ["who.owner", "who.name"], tag: "you" };
|
|
3
|
+
/**
|
|
4
|
+
* The compact row of each status signal's items, by `status.<signal id>`; a signal's file and its row are the two
|
|
5
|
+
* places a new signal touches. They live here because a service may not import the CLI's row type.
|
|
6
|
+
*/
|
|
7
|
+
export const STATUS_SIGNAL_ROWS = {
|
|
8
|
+
"status.last-message": { columns: [...where, { field: "at", as: "time" }, who] },
|
|
9
|
+
"status.reply-next": {
|
|
10
|
+
columns: [
|
|
11
|
+
...where,
|
|
12
|
+
{ field: "data.next" },
|
|
13
|
+
{ field: "data.addressed.name" },
|
|
14
|
+
{ field: "data.waiting_since", as: "day" },
|
|
15
|
+
{ field: "data.overdue", tag: "overdue" },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
"status.questions": {
|
|
19
|
+
columns: [...where, { field: "at", as: "day" }, { field: "data.side" }, who],
|
|
20
|
+
text: ["data.excerpt"],
|
|
21
|
+
},
|
|
22
|
+
"status.promises": {
|
|
23
|
+
columns: [
|
|
24
|
+
...where,
|
|
25
|
+
{ field: "at", as: "day" },
|
|
26
|
+
{ field: "data.source" },
|
|
27
|
+
{ field: "data.side" },
|
|
28
|
+
{ field: "data.due", as: "day" },
|
|
29
|
+
{ field: "data.status" },
|
|
30
|
+
],
|
|
31
|
+
text: ["data.title"],
|
|
32
|
+
},
|
|
33
|
+
"status.amounts": {
|
|
34
|
+
columns: [
|
|
35
|
+
...where,
|
|
36
|
+
{ field: "data.direction" },
|
|
37
|
+
{ field: "data.amount" },
|
|
38
|
+
{ field: "data.currency" },
|
|
39
|
+
{ field: "data.state" },
|
|
40
|
+
{ field: "data.since", as: "day" },
|
|
41
|
+
{ field: "data.asks", as: "count", tag: "asks" },
|
|
42
|
+
{ field: "basis" },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
"status.payments": {
|
|
46
|
+
columns: [{ field: "ref" }, { field: "at", as: "day" }, { field: "data.status" }],
|
|
47
|
+
text: ["data.title"],
|
|
48
|
+
},
|
|
49
|
+
"status.topics": {
|
|
50
|
+
columns: [
|
|
51
|
+
...where,
|
|
52
|
+
{ field: "data.query" },
|
|
53
|
+
{ field: "data.theirs_last", as: "day" },
|
|
54
|
+
{ field: "data.yours_last", as: "day" },
|
|
55
|
+
{ field: "data.silent_since", as: "day" },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
"status.coverage": {
|
|
59
|
+
columns: [
|
|
60
|
+
{ field: "chat" },
|
|
61
|
+
{ field: "chat_title" },
|
|
62
|
+
{ field: "data.state" },
|
|
63
|
+
{ field: "data.held.messages", as: "count", tag: "held" },
|
|
64
|
+
{ field: "data.held.oldest_at", as: "day" },
|
|
65
|
+
{ field: "sync_command" },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
"status.children": {
|
|
69
|
+
columns: [
|
|
70
|
+
{ field: "data.child" },
|
|
71
|
+
{ field: "data.key" },
|
|
72
|
+
{ field: "data.name" },
|
|
73
|
+
{ field: "data.chats", as: "count", tag: "chats" },
|
|
74
|
+
{ field: "data.cut" },
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
/** The rest of a `project_status` answer: its chats, open money, chats left out, and failed signals. */
|
|
79
|
+
export const STATUS_ROWS = {
|
|
80
|
+
...STATUS_SIGNAL_ROWS,
|
|
81
|
+
"status.chat": {
|
|
82
|
+
columns: [
|
|
83
|
+
{ field: "chat" },
|
|
84
|
+
{ field: "title" },
|
|
85
|
+
{ field: "kind" },
|
|
86
|
+
{ field: "role" },
|
|
87
|
+
{ field: "attention", as: "count", tag: "attention" },
|
|
88
|
+
{ field: "why" },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
"status.money": {
|
|
92
|
+
columns: [
|
|
93
|
+
{ field: "direction" },
|
|
94
|
+
{ field: "amount" },
|
|
95
|
+
{ field: "currency" },
|
|
96
|
+
{ field: "rows", as: "count", tag: "rows" },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
"status.signal": { columns: [{ field: "id" }, { field: "version" }] },
|
|
100
|
+
"status.out-of-scope": { columns: [{ field: "chat" }, { field: "reason" }, { field: "purpose" }] },
|
|
101
|
+
"status.failed": { columns: [{ field: "signal" }], text: ["why"] },
|
|
102
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { NextStep } from "@wirecat/cli-base/standards";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import type { ProjectStatus, StatusChatSummary } from "../services/project-status/index.js";
|
|
4
|
+
import type { RowShape } from "./compact.js";
|
|
5
|
+
export interface ProjectStatusHints {
|
|
6
|
+
/** The step that syncs a chat held in part back to `since` (`YYYY-MM-DD`); null when no command here can. */
|
|
7
|
+
sync: (chat: StatusChatSummary, since: string) => NextStep | null;
|
|
8
|
+
/** The step that reads a chat's stored messages from `since`; null when no command here can. */
|
|
9
|
+
read: (chat: StatusChatSummary, since: string) => NextStep | null;
|
|
10
|
+
/** The command line a step runs, for the text form. */
|
|
11
|
+
line: (step: NextStep) => string;
|
|
12
|
+
}
|
|
13
|
+
/** The `project_status` answer drawn by `--compact`: each signal's list with that signal's row. */
|
|
14
|
+
export declare const projectStatusRow: (signals: readonly {
|
|
15
|
+
id: string;
|
|
16
|
+
}[]) => RowShape;
|
|
17
|
+
/** Where a project stands: per chat signals, open money, and what was left out. Empty lists are left out. */
|
|
18
|
+
export declare const projectStatusSchema: v.LooseObjectSchema<{
|
|
19
|
+
readonly project: v.StringSchema<undefined>;
|
|
20
|
+
readonly key: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
21
|
+
readonly name: v.StringSchema<undefined>;
|
|
22
|
+
readonly window: v.StrictObjectSchema<{
|
|
23
|
+
readonly since: v.StringSchema<undefined>;
|
|
24
|
+
readonly until: v.StringSchema<undefined>;
|
|
25
|
+
}, undefined>;
|
|
26
|
+
readonly signals: v.ArraySchema<v.StrictObjectSchema<{
|
|
27
|
+
readonly id: v.StringSchema<undefined>;
|
|
28
|
+
readonly version: v.NumberSchema<undefined>;
|
|
29
|
+
}, undefined>, undefined>;
|
|
30
|
+
readonly money: v.ArraySchema<v.StrictObjectSchema<{
|
|
31
|
+
readonly direction: v.StringSchema<undefined>;
|
|
32
|
+
readonly currency: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
33
|
+
readonly amount: v.StringSchema<undefined>;
|
|
34
|
+
readonly rows: v.NumberSchema<undefined>;
|
|
35
|
+
}, undefined>, undefined>;
|
|
36
|
+
readonly chats: v.ArraySchema<v.StrictObjectSchema<{
|
|
37
|
+
readonly chat: v.StringSchema<undefined>;
|
|
38
|
+
readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
39
|
+
readonly kind: v.StringSchema<undefined>;
|
|
40
|
+
readonly role: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
41
|
+
readonly purpose: v.StringSchema<undefined>;
|
|
42
|
+
readonly project: v.StringSchema<undefined>;
|
|
43
|
+
readonly coverage: v.StringSchema<undefined>;
|
|
44
|
+
readonly attention: v.NumberSchema<undefined>;
|
|
45
|
+
readonly why: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
46
|
+
}, undefined>, undefined>;
|
|
47
|
+
readonly out_of_scope: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
48
|
+
readonly chat: v.StringSchema<undefined>;
|
|
49
|
+
readonly reason: v.StringSchema<undefined>;
|
|
50
|
+
readonly purpose: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
51
|
+
}, undefined>, undefined>, undefined>;
|
|
52
|
+
readonly failed: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
|
|
53
|
+
readonly signal: v.StringSchema<undefined>;
|
|
54
|
+
readonly why: v.StringSchema<undefined>;
|
|
55
|
+
}, undefined>, undefined>, undefined>;
|
|
56
|
+
}, undefined>;
|
|
57
|
+
export type ProjectStatusItem = v.InferOutput<typeof projectStatusSchema>;
|
|
58
|
+
/** The answer of `projects status`: the item, two steps at most, what is missing, and the window. */
|
|
59
|
+
export declare const projectStatusAnswer: (found: ProjectStatus, hints: ProjectStatusHints) => {
|
|
60
|
+
item: {
|
|
61
|
+
project: string;
|
|
62
|
+
key: string | null;
|
|
63
|
+
name: string;
|
|
64
|
+
window: {
|
|
65
|
+
since: string;
|
|
66
|
+
until: string;
|
|
67
|
+
};
|
|
68
|
+
signals: {
|
|
69
|
+
id: string;
|
|
70
|
+
version: number;
|
|
71
|
+
}[];
|
|
72
|
+
money: {
|
|
73
|
+
direction: string;
|
|
74
|
+
currency: string | null;
|
|
75
|
+
amount: string;
|
|
76
|
+
rows: number;
|
|
77
|
+
}[];
|
|
78
|
+
chats: {
|
|
79
|
+
chat: string;
|
|
80
|
+
title: string | null;
|
|
81
|
+
kind: string;
|
|
82
|
+
role: string | null;
|
|
83
|
+
purpose: string;
|
|
84
|
+
project: string;
|
|
85
|
+
coverage: string;
|
|
86
|
+
attention: number;
|
|
87
|
+
why: string | null;
|
|
88
|
+
}[];
|
|
89
|
+
out_of_scope?: {
|
|
90
|
+
chat: string;
|
|
91
|
+
reason: string;
|
|
92
|
+
purpose?: string | undefined;
|
|
93
|
+
}[] | undefined;
|
|
94
|
+
failed?: {
|
|
95
|
+
signal: string;
|
|
96
|
+
why: string;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
} & {
|
|
99
|
+
[key: string]: unknown;
|
|
100
|
+
};
|
|
101
|
+
next: ({
|
|
102
|
+
kind: "command";
|
|
103
|
+
step: string;
|
|
104
|
+
command: string;
|
|
105
|
+
args: {
|
|
106
|
+
[x: string]: unknown;
|
|
107
|
+
};
|
|
108
|
+
line?: string | undefined;
|
|
109
|
+
why: string;
|
|
110
|
+
safe_to_auto_execute: boolean;
|
|
111
|
+
} | {
|
|
112
|
+
kind: "user_action";
|
|
113
|
+
step: string;
|
|
114
|
+
why: string;
|
|
115
|
+
safe_to_auto_execute: false;
|
|
116
|
+
})[];
|
|
117
|
+
missing: {
|
|
118
|
+
field: string;
|
|
119
|
+
why: string;
|
|
120
|
+
step?: string | undefined;
|
|
121
|
+
}[];
|
|
122
|
+
meta: {
|
|
123
|
+
window: {
|
|
124
|
+
since: string;
|
|
125
|
+
until: string;
|
|
126
|
+
};
|
|
127
|
+
complete: boolean;
|
|
128
|
+
accounts?: string[] | undefined;
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
/** The human form: header, money, a line per chat with what is open, then the steps. */
|
|
132
|
+
export declare const projectStatusText: (found: ProjectStatus, hints: ProjectStatusHints) => string;
|