@wirecat/cli-core 0.389.0 → 0.391.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 (51) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/cli/context.js +1 -0
  3. package/dist/cli/effects.d.ts +2 -0
  4. package/dist/cli/effects.js +8 -2
  5. package/dist/cli/messenger/catch-up.d.ts +0 -19
  6. package/dist/cli/messenger/catch-up.js +0 -43
  7. package/dist/cli/messenger/context.js +6 -3
  8. package/dist/cli/messenger/inbox.js +6 -11
  9. package/dist/cli/messenger/messages-command.js +5 -15
  10. package/dist/cli/messenger/review.d.ts +1 -1
  11. package/dist/cli/messenger/review.js +5 -10
  12. package/dist/cli/messenger/store-maintenance-command.d.ts +1 -1
  13. package/dist/cli/messenger/tags-command.js +2 -2
  14. package/dist/cli/program.js +10 -0
  15. package/dist/cli/settings.d.ts +0 -4
  16. package/dist/cli/settings.js +7 -5
  17. package/dist/meetings/index.d.ts +2 -0
  18. package/dist/meetings/index.js +2 -0
  19. package/dist/meetings/links.d.ts +18 -0
  20. package/dist/meetings/links.js +1 -0
  21. package/dist/meetings/review.d.ts +45 -0
  22. package/dist/meetings/review.js +1 -0
  23. package/dist/runs/recording.d.ts +5 -0
  24. package/dist/runs/recording.js +6 -3
  25. package/dist/runs/scope.d.ts +3 -1
  26. package/dist/runs/scope.js +2 -0
  27. package/dist/search/lucene/registry.d.ts +5 -1
  28. package/dist/search/lucene/registry.js +10 -4
  29. package/dist/search/lucene/types.d.ts +8 -0
  30. package/dist/search/lucene/types.js +4 -0
  31. package/dist/search/question-plan.js +3 -3
  32. package/dist/services/excerpt.js +4 -1
  33. package/dist/services/index.d.ts +2 -0
  34. package/dist/services/index.js +2 -0
  35. package/dist/services/meeting-links.d.ts +33 -0
  36. package/dist/services/meeting-links.js +41 -0
  37. package/dist/services/meeting-review.d.ts +12 -0
  38. package/dist/services/meeting-review.js +8 -0
  39. package/dist/services/messages-search.js +2 -2
  40. package/dist/services/searches.js +3 -3
  41. package/dist/store/sqlite/lucene-fields.d.ts +3 -1
  42. package/dist/store/sqlite/lucene-fields.js +78 -11
  43. package/dist/store/sqlite/lucene.js +16 -11
  44. package/dist/store/sqlite/meeting-chat-lines.d.ts +3 -0
  45. package/dist/store/sqlite/meeting-chat-lines.js +34 -0
  46. package/dist/store/sqlite/meeting-review.d.ts +4 -0
  47. package/dist/store/sqlite/meeting-review.js +97 -0
  48. package/dist/store/sqlite/meetings.d.ts +2 -2
  49. package/dist/store/sqlite/meetings.js +4 -0
  50. package/package.json +1 -1
  51. package/parity.json +0 -11
@@ -0,0 +1,97 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import { REVIEW_LIMIT } from "../../meetings/index.js";
3
+ import { fromJson } from "./events.js";
4
+ import { checkMeetingCancelled } from "./meeting-operation.js";
5
+ import { positiveReadInteger } from "./meeting-reads.js";
6
+ import { meetingSelectionSql } from "./meeting-selection.js";
7
+ import { read } from "./transaction.js";
8
+ const number = (value) => (value === null || value === undefined ? null : Number(value));
9
+ const text = (value) => (value === null || value === undefined ? null : String(value));
10
+ /** Talk and words come from one transcript per meeting, its earliest current one, as in meeting stats. */
11
+ export const meetingReviewOver = ({ database }) => ({
12
+ async meetingReview(input) {
13
+ checkMeetingCancelled(input.signal);
14
+ positiveReadInteger(input.accountId, "accountId");
15
+ const limit = input.limit ?? REVIEW_LIMIT.default;
16
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > REVIEW_LIMIT.most)
17
+ throw new CliError("validation_error", `a review shows 1 through ${REVIEW_LIMIT.most} meetings`);
18
+ return read(database, () => {
19
+ const { clauses, params } = meetingSelectionSql(input);
20
+ const meetings = database
21
+ .prepare(`SELECT m.id, m.account_id, m.title, m.started_at, m.duration_ms, m.participants_count FROM meetings m
22
+ WHERE ${clauses.join(" AND ")} ORDER BY m.started_at DESC, m.id DESC LIMIT ?`)
23
+ .all(...params, limit + 1);
24
+ const shown = meetings.slice(0, limit);
25
+ const items = new Map();
26
+ for (const row of shown)
27
+ items.set(Number(row.id), {
28
+ meetingId: Number(row.id),
29
+ accountId: Number(row.account_id),
30
+ title: text(row.title),
31
+ startedAt: number(row.started_at),
32
+ durationMs: number(row.duration_ms),
33
+ participantsCount: number(row.participants_count),
34
+ projects: [],
35
+ transcript: null,
36
+ speakers: [],
37
+ summary: null,
38
+ });
39
+ if (items.size) {
40
+ const ids = [...items.keys()];
41
+ const marks = ids.map(() => "?").join(",");
42
+ checkMeetingCancelled(input.signal);
43
+ const talk = database
44
+ .prepare(`WITH chosen AS (SELECT min(t.id) AS id, t.meeting_id FROM meeting_transcripts t
45
+ WHERE t.meeting_id IN (${marks}) AND t.deleted_at IS NULL AND t.superseded_at IS NULL GROUP BY t.meeting_id)
46
+ SELECT c.meeting_id, coalesce(person.name, i.name, r.speaker_name, p.display_name) AS speaker,
47
+ count(*) AS lines, sum(max(r.end_ms - r.start_ms, 0)) AS talk_ms,
48
+ sum(length(trim(r.text)) - length(replace(trim(r.text), ' ', '')) + (length(trim(r.text)) > 0)) AS words
49
+ FROM chosen c JOIN meeting_transcript_rows r ON r.meeting_transcript_id = c.id
50
+ LEFT JOIN meeting_participants p ON p.id = r.speaker_participant_id
51
+ LEFT JOIN identities i ON i.id = p.identity_id
52
+ LEFT JOIN identity_links l ON l.identity_id = p.identity_id
53
+ LEFT JOIN persons person ON person.id = l.person_id
54
+ GROUP BY c.meeting_id, speaker ORDER BY c.meeting_id, talk_ms DESC`)
55
+ .all(...ids);
56
+ for (const row of talk) {
57
+ const item = items.get(Number(row.meeting_id));
58
+ item.transcript ??= { lines: 0, words: 0 };
59
+ item.transcript.lines += Number(row.lines);
60
+ item.transcript.words += Number(row.words);
61
+ if (item.speakers.length < 3)
62
+ item.speakers.push({ name: text(row.speaker), talkMs: Number(row.talk_ms) });
63
+ }
64
+ const transcribed = database
65
+ .prepare(`SELECT DISTINCT meeting_id FROM meeting_transcripts
66
+ WHERE meeting_id IN (${marks}) AND deleted_at IS NULL AND superseded_at IS NULL`)
67
+ .all(...ids);
68
+ for (const row of transcribed) {
69
+ const item = items.get(Number(row.meeting_id));
70
+ item.transcript ??= { lines: 0, words: 0 };
71
+ }
72
+ const summaries = database
73
+ .prepare(`SELECT meeting_id, source, title, overview, next_steps, doc_url FROM meeting_summaries
74
+ WHERE meeting_id IN (${marks}) ORDER BY coalesce(external_updated_at, updated_at) DESC, id DESC`)
75
+ .all(...ids);
76
+ for (const row of summaries) {
77
+ const item = items.get(Number(row.meeting_id));
78
+ item.summary ??= {
79
+ source: String(row.source),
80
+ title: text(row.title),
81
+ overview: text(row.overview),
82
+ nextSteps: fromJson(row.next_steps) ?? [],
83
+ url: text(row.doc_url),
84
+ };
85
+ }
86
+ const projects = database
87
+ .prepare(`SELECT l.from_id, p.name FROM links l JOIN projects p ON p.id = l.to_id
88
+ WHERE l.from_type = 'meeting' AND l.from_id IN (${marks}) AND l.to_type = 'project' AND l.kind = 'member-of'
89
+ AND l.anchor IS NULL AND l.confirmed = 1 AND p.account_id IS NULL AND p.deleted_at IS NULL ORDER BY p.name`)
90
+ .all(...ids);
91
+ for (const row of projects)
92
+ items.get(Number(row.from_id)).projects.push(String(row.name));
93
+ }
94
+ return { items: [...items.values()], hasMore: meetings.length > limit };
95
+ });
96
+ },
97
+ });
@@ -1,4 +1,4 @@
1
- import type { Attachment, MeetingContextStore, MeetingParticipantSearchStore, MeetingPreparationContextStore, MeetingPreparationKnowledgeStore, MeetingPreparationNotesStore, MeetingRelatedHistoryStore, MeetingSearchIndexStore, MeetingSearchStore, MeetingSeriesHistoryStore, MeetingStatsStore, MeetingStore, MeetingTranscriptReceiptStore, MeetingTranscriptStore, NewRecord } from "../../meetings/index.js";
1
+ import type { Attachment, MeetingChatLinesStore, MeetingContextStore, MeetingParticipantSearchStore, MeetingPreparationContextStore, MeetingPreparationKnowledgeStore, MeetingPreparationNotesStore, MeetingRelatedHistoryStore, MeetingReviewStore, MeetingSearchIndexStore, MeetingSearchStore, MeetingSeriesHistoryStore, MeetingStatsStore, MeetingStore, MeetingTranscriptReceiptStore, MeetingTranscriptStore, NewRecord } from "../../meetings/index.js";
2
2
  import type { CacheDatabase } from "../driver.js";
3
3
  import { type MeetingContextReadCapabilities, type MeetingReadCapabilities } from "./meeting-reads.js";
4
4
  import type { StoreContext } from "./open.js";
@@ -11,7 +11,7 @@ export declare const saveAttachment: (database: CacheDatabase, type: string, par
11
11
  export declare const drainMeetingIndex: (database: CacheDatabase, batch?: number) => number;
12
12
  /** Each word of the query, as a prefix, all required. */
13
13
  export declare const wordsQuery: (query: string) => string | null;
14
- export type StoredMeetings = MeetingStore & MeetingPreparationContextStore & MeetingPreparationKnowledgeStore & MeetingPreparationNotesStore & MeetingRelatedHistoryStore & MeetingSeriesHistoryStore & MeetingTranscriptStore & MeetingReadCapabilities & MeetingContextReadCapabilities & MeetingParticipantSearchStore & MeetingSearchStore & MeetingTranscriptReceiptStore & MeetingContextStore & MeetingSearchIndexStore & MeetingStatsStore;
14
+ export type StoredMeetings = MeetingStore & MeetingPreparationContextStore & MeetingPreparationKnowledgeStore & MeetingPreparationNotesStore & MeetingRelatedHistoryStore & MeetingSeriesHistoryStore & MeetingTranscriptStore & MeetingReadCapabilities & MeetingContextReadCapabilities & MeetingParticipantSearchStore & MeetingSearchStore & MeetingTranscriptReceiptStore & MeetingContextStore & MeetingSearchIndexStore & MeetingStatsStore & MeetingReviewStore & MeetingChatLinesStore;
15
15
  /** The `MeetingStore` of `src/meetings/` over the shared store. */
16
16
  export declare const meetingStoreOver: (context: StoreContext) => StoredMeetings;
17
17
  export {};
@@ -5,6 +5,7 @@ import * as eventQueries from "./events.js";
5
5
  import { fromJson, int, page, str, toJson } from "./events.js";
6
6
  import { folded } from "./folded.js";
7
7
  import { meetingIdentityPk } from "./identities.js";
8
+ import { meetingChatLinesOver } from "./meeting-chat-lines.js";
8
9
  import { meetingContextLinksOver } from "./meeting-context-links.js";
9
10
  import { meetingHitIndexOver } from "./meeting-hit-index.js";
10
11
  import { meetingHitSearchOver } from "./meeting-hit-search.js";
@@ -15,6 +16,7 @@ import { meetingPreparationKnowledgeOver } from "./meeting-preparation-knowledge
15
16
  import { meetingPreparationNotesOver } from "./meeting-preparation-notes.js";
16
17
  import { meetingReadsOver } from "./meeting-reads.js";
17
18
  import { meetingRelatedHistoryOver } from "./meeting-related-history.js";
19
+ import { meetingReviewOver } from "./meeting-review.js";
18
20
  import { meetingSeriesHistoryOver } from "./meeting-series-history.js";
19
21
  import { meetingStatsOver } from "./meeting-stats.js";
20
22
  import { invalidateMeetingStems } from "./meeting-stem-state.js";
@@ -573,6 +575,8 @@ export const meetingStoreOver = (context) => {
573
575
  ...meetingRelatedHistoryOver(context),
574
576
  ...meetingHitSearchOver(context),
575
577
  ...meetingStatsOver(context),
578
+ ...meetingReviewOver(context),
579
+ ...meetingChatLinesOver(context),
576
580
  ...meetingParticipantSearchOver(context),
577
581
  ...meetingHitIndexOver(context),
578
582
  ...meetingContextLinksOver(context),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.389.0",
3
+ "version": "0.391.0",
4
4
  "description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",
package/parity.json CHANGED
@@ -483,9 +483,6 @@
483
483
  "--local": {
484
484
  "meaning": "use the model on this machine, never the messenger"
485
485
  },
486
- "--mark-read": {
487
- "meaning": "also mark the chat read up to the newest message shown; the other person sees it"
488
- },
489
486
  "--marker": {
490
487
  "value": "<value>",
491
488
  "meaning": "Marker"
@@ -635,9 +632,6 @@
635
632
  "--no-input": {
636
633
  "meaning": "never prompt or open an interactive login; piped input remains available"
637
634
  },
638
- "--no-mark-read": {
639
- "meaning": "do not mark read, whatever the catch_up_marks_read setting says"
640
- },
641
635
  "--no-mentions-me": {
642
636
  "meaning": "do not require a mention of you or a reply to you"
643
637
  },
@@ -2876,10 +2870,8 @@
2876
2870
  "--all": "all",
2877
2871
  "--kind": "all",
2878
2872
  "--limit": "all",
2879
- "--mark-read": "all",
2880
2873
  "--model": "all",
2881
2874
  "--new": "all",
2882
- "--no-mark-read": "all",
2883
2875
  "--since": {
2884
2876
  "in": [],
2885
2877
  "planned": {
@@ -3186,7 +3178,6 @@
3186
3178
  "options": {
3187
3179
  "--cursor": "all",
3188
3180
  "--limit": "all",
3189
- "--mark-read": "all",
3190
3181
  "--model": "all",
3191
3182
  "--transcribe": "all"
3192
3183
  }
@@ -3610,10 +3601,8 @@
3610
3601
  "options": {
3611
3602
  "--all": "all",
3612
3603
  "--kind": "all",
3613
- "--mark-read": "all",
3614
3604
  "--model": "all",
3615
3605
  "--new": "all",
3616
- "--no-mark-read": "all",
3617
3606
  "--since": {
3618
3607
  "in": [],
3619
3608
  "planned": {