kibi-opencode 0.9.0 → 0.11.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/README.md +38 -13
- package/dist/brief-delivery-reasons.d.ts +12 -0
- package/dist/brief-delivery-reasons.js +132 -0
- package/dist/brief-intent.d.ts +15 -4
- package/dist/brief-intent.js +78 -25
- package/dist/briefing-runtime.js +2 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +9 -0
- package/dist/e2e-coverage-signals.d.ts +6 -0
- package/dist/e2e-coverage-signals.js +186 -0
- package/dist/file-entity-links.d.ts +15 -0
- package/dist/file-entity-links.js +254 -0
- package/dist/file-operation-reminders.d.ts +24 -0
- package/dist/file-operation-reminders.js +55 -0
- package/dist/file-operation-state.d.ts +29 -0
- package/dist/file-operation-state.js +113 -0
- package/dist/idle-brief-audit.d.ts +36 -0
- package/dist/idle-brief-audit.js +186 -0
- package/dist/idle-brief-paths.d.ts +6 -0
- package/dist/idle-brief-paths.js +120 -0
- package/dist/idle-brief-reader.d.ts +37 -0
- package/dist/idle-brief-reader.js +163 -0
- package/dist/idle-brief-runtime.d.ts +48 -0
- package/dist/idle-brief-runtime.js +478 -0
- package/dist/idle-brief-store.d.ts +113 -0
- package/dist/idle-brief-store.js +262 -0
- package/dist/index.d.ts +2 -39
- package/dist/index.js +1 -492
- package/dist/init-kibi-alias.d.ts +14 -0
- package/dist/init-kibi-alias.js +38 -0
- package/dist/init-kibi-capability.d.ts +32 -0
- package/dist/init-kibi-capability.js +202 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +17 -4
- package/dist/plugin-startup.d.ts +1 -0
- package/dist/plugin-startup.js +11 -2
- package/dist/plugin.d.ts +52 -0
- package/dist/plugin.js +1068 -0
- package/dist/prompt.d.ts +15 -3
- package/dist/prompt.js +106 -36
- package/dist/reconcile-engine.d.ts +15 -0
- package/dist/reconcile-engine.js +112 -0
- package/dist/scheduler.d.ts +13 -2
- package/dist/scheduler.js +86 -7
- package/dist/session-edit-state.d.ts +25 -0
- package/dist/session-edit-state.js +177 -0
- package/dist/session-fingerprint.d.ts +11 -0
- package/dist/session-fingerprint.js +21 -0
- package/dist/source-linked-guidance.d.ts +1 -2
- package/dist/source-linked-guidance.js +5 -168
- package/dist/startup-notifier.js +42 -31
- package/dist/toast.d.ts +23 -22
- package/dist/toast.js +36 -14
- package/dist/tui-brief-delivery.d.ts +67 -0
- package/dist/tui-brief-delivery.js +279 -0
- package/dist/tui-brief-view-model.d.ts +63 -0
- package/dist/tui-brief-view-model.js +209 -0
- package/dist/tui.d.ts +8 -0
- package/dist/tui.js +413 -0
- package/dist/tui.jsx +120 -0
- package/package.json +13 -4
package/dist/toast.d.ts
CHANGED
|
@@ -4,29 +4,30 @@ export type ToastPayload = {
|
|
|
4
4
|
message: string;
|
|
5
5
|
duration?: number;
|
|
6
6
|
};
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
export type SendToastResult = {
|
|
8
|
+
status: "delivered";
|
|
9
|
+
transport: "legacy" | "sdk";
|
|
10
|
+
} | {
|
|
11
|
+
status: "unavailable";
|
|
12
|
+
reason: "missing-capability";
|
|
13
|
+
} | {
|
|
14
|
+
status: "failed";
|
|
15
|
+
transport: "legacy" | "sdk";
|
|
16
|
+
reason: string;
|
|
17
|
+
error?: string;
|
|
15
18
|
};
|
|
16
19
|
export type ToastCapableClient = {
|
|
17
|
-
tui?:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
showToast:
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
tui?: {
|
|
21
|
+
/** Legacy direct TUI toast (works in plugin context) */
|
|
22
|
+
toast?: (payload: ToastPayload) => void | Promise<void>;
|
|
23
|
+
/** SDK toast - receives { body: ToastPayload } */
|
|
24
|
+
showToast?: (payload: {
|
|
25
|
+
body: ToastPayload;
|
|
26
|
+
}) => void | Promise<void>;
|
|
27
|
+
/** SDK command bridge - invoke TUI command */
|
|
28
|
+
executeCommand?: (command: string, args?: object) => void | Promise<void>;
|
|
29
|
+
clearPrompt?: () => void | Promise<void>;
|
|
30
|
+
submitPrompt?: () => void | Promise<void>;
|
|
27
31
|
};
|
|
28
32
|
};
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function hasLegacyToast(client: ToastCapableClient): client is ClientWithLegacyToast;
|
|
31
|
-
export declare function sendToast(client: ToastCapableClient, payload: ToastPayload): Promise<void>;
|
|
32
|
-
export {};
|
|
33
|
+
export declare function sendToast(client: ToastCapableClient, payload: ToastPayload): Promise<SendToastResult>;
|
package/dist/toast.js
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
// implements REQ-opencode-kibi-plugin-v1
|
|
2
|
-
export function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
export async function sendToast(client, payload) {
|
|
3
|
+
if (typeof client.tui?.toast === "function") {
|
|
4
|
+
try {
|
|
5
|
+
await client.tui.toast(payload);
|
|
6
|
+
return { status: "delivered", transport: "legacy" };
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
return {
|
|
10
|
+
status: "failed",
|
|
11
|
+
transport: "legacy",
|
|
12
|
+
reason: "rejected",
|
|
13
|
+
error: err instanceof Error ? err.message : String(err),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
13
16
|
}
|
|
14
|
-
if (
|
|
15
|
-
|
|
17
|
+
if (typeof client.tui?.showToast === "function") {
|
|
18
|
+
try {
|
|
19
|
+
const result = client.tui.showToast({ body: payload });
|
|
20
|
+
if (result && typeof result.then === "function") {
|
|
21
|
+
const timeout = new Promise((_, reject) => {
|
|
22
|
+
setTimeout(() => reject(new Error("showToast timed out")), 3000);
|
|
23
|
+
});
|
|
24
|
+
await Promise.race([result, timeout]);
|
|
25
|
+
}
|
|
26
|
+
return { status: "delivered", transport: "sdk" };
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return {
|
|
30
|
+
status: "failed",
|
|
31
|
+
transport: "sdk",
|
|
32
|
+
reason: err instanceof Error && err.message === "showToast timed out"
|
|
33
|
+
? "timed-out"
|
|
34
|
+
: "rejected",
|
|
35
|
+
error: err instanceof Error ? err.message : String(err),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
16
38
|
}
|
|
17
|
-
return
|
|
39
|
+
return { status: "unavailable", reason: "missing-capability" };
|
|
18
40
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ToastCapableClient as SendToastCapableClient } from "./toast.js";
|
|
2
|
+
import type { IdleBriefEnvelope } from "./idle-brief-store.js";
|
|
3
|
+
export type ToastPayload = {
|
|
4
|
+
variant?: "info" | "success" | "warning" | "error";
|
|
5
|
+
title?: string;
|
|
6
|
+
message: string;
|
|
7
|
+
duration?: number;
|
|
8
|
+
};
|
|
9
|
+
export type ToastCapableClient = {
|
|
10
|
+
tui?: {
|
|
11
|
+
showToast?: (payload: {
|
|
12
|
+
body: ToastPayload;
|
|
13
|
+
}) => void | Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type SharedBriefPolicy = {
|
|
17
|
+
briefs: {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
channels: {
|
|
20
|
+
tui: boolean;
|
|
21
|
+
vscode: boolean;
|
|
22
|
+
};
|
|
23
|
+
tui: {
|
|
24
|
+
toast: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
export type LocalBriefConfig = {
|
|
29
|
+
autoSubmit: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type DeliverResult = {
|
|
32
|
+
delivered: boolean;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Delivers a Kibi briefing to the TUI via toast notification.
|
|
36
|
+
*
|
|
37
|
+
* Uses the REAL OpenCode plugin API:
|
|
38
|
+
* - client.tui.showToast(payload) — primary (and only) delivery mechanism
|
|
39
|
+
*
|
|
40
|
+
* The toast contains a rich summary from the envelope and is displayed
|
|
41
|
+
* for 8 seconds so users can read the content.
|
|
42
|
+
*
|
|
43
|
+
* @param client - OpenCode client with optional TUI capabilities
|
|
44
|
+
* @param envelope - Idle brief envelope containing briefing content
|
|
45
|
+
* @param sharedPolicy - Shared brief policy from `.kb/config.json`
|
|
46
|
+
* @param localConfig - Local OpenCode config
|
|
47
|
+
*/
|
|
48
|
+
export declare function deliverBriefTui(client: ToastCapableClient, envelope: IdleBriefEnvelope, sharedPolicy: SharedBriefPolicy, _localConfig: LocalBriefConfig): Promise<DeliverResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Client type for announcement-only TUI delivery.
|
|
51
|
+
* Extends toast capability with the SDK command bridge.
|
|
52
|
+
*/
|
|
53
|
+
export type AnnouncementClient = SendToastCapableClient;
|
|
54
|
+
export type AnnouncementResult = {
|
|
55
|
+
toastDelivered: boolean;
|
|
56
|
+
commandPublished: boolean;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Announcement-only TUI delivery coordinator.
|
|
60
|
+
*
|
|
61
|
+
* Sends the summary toast and invokes the official SDK bridge
|
|
62
|
+
* (`executeCommand`) but does NOT mutate read/seen state.
|
|
63
|
+
* The caller is responsible for any state transitions after the
|
|
64
|
+
* TUI route confirms render success.
|
|
65
|
+
*/
|
|
66
|
+
export declare function announceBriefTui(// implements REQ-opencode-kibi-briefing-v6
|
|
67
|
+
client: AnnouncementClient, envelope: IdleBriefEnvelope, sharedPolicy: SharedBriefPolicy): Promise<AnnouncementResult>;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
* Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*/
|
|
10
|
+
import { sendToast } from "./toast.js";
|
|
11
|
+
import { renderToastSummary } from "./brief-delivery-reasons.js";
|
|
12
|
+
import * as logger from "./logger.js";
|
|
13
|
+
function firstNonEmpty(...values) {
|
|
14
|
+
for (const value of values) {
|
|
15
|
+
const trimmed = value?.trim();
|
|
16
|
+
if (trimmed) {
|
|
17
|
+
return trimmed;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
function buildTuiBriefMessage(envelope) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
const briefing = envelope.briefing;
|
|
25
|
+
const deliveryReasons = briefing.deliveryReasons;
|
|
26
|
+
const renderedToast = deliveryReasons?.items?.length
|
|
27
|
+
? renderToastSummary(deliveryReasons)
|
|
28
|
+
: undefined;
|
|
29
|
+
const whatChanged = renderedToast
|
|
30
|
+
? [renderedToast.summary]
|
|
31
|
+
: envelope.schemaVersion === "2.0"
|
|
32
|
+
? envelope.briefing.changeNarrative.map((line) => line.trim()).filter(Boolean).filter((line) => !line.includes(".sisyphus/"))
|
|
33
|
+
: [];
|
|
34
|
+
lines.push("## What changed");
|
|
35
|
+
if (whatChanged.length > 0) {
|
|
36
|
+
lines.push(...whatChanged.slice(0, 2));
|
|
37
|
+
}
|
|
38
|
+
else if (envelope.schemaVersion === "2.0") {
|
|
39
|
+
const fallbackEntity = envelope.changes.entities.modified[0] ?? envelope.changes.entities.added[0];
|
|
40
|
+
if (fallbackEntity) {
|
|
41
|
+
const action = envelope.changes.entities.modified[0] ? "Modified" : "Added";
|
|
42
|
+
lines.push(`${action} ${fallbackEntity.id}: ${fallbackEntity.title ?? "Untitled"}`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const fallback = firstNonEmpty(envelope.summary, envelope.briefing.tldr);
|
|
46
|
+
if (fallback)
|
|
47
|
+
lines.push(fallback);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const fallback = firstNonEmpty(envelope.summary, envelope.briefing.tldr);
|
|
52
|
+
if (fallback)
|
|
53
|
+
lines.push(fallback);
|
|
54
|
+
}
|
|
55
|
+
lines.push("");
|
|
56
|
+
const whyItMatters = firstNonEmpty(deliveryReasons?.items?.length ? renderedToast?.whyItMatters : undefined);
|
|
57
|
+
if (whyItMatters) {
|
|
58
|
+
lines.push("## Why it matters");
|
|
59
|
+
lines.push(whyItMatters);
|
|
60
|
+
lines.push("");
|
|
61
|
+
}
|
|
62
|
+
const hasKnowledgeImpact = envelope.briefing.citations.length > 0 ||
|
|
63
|
+
(envelope.briefing.constraints?.length ?? 0) > 0 ||
|
|
64
|
+
(envelope.briefing.regressionRisks?.length ?? 0) > 0;
|
|
65
|
+
if (hasKnowledgeImpact) {
|
|
66
|
+
lines.push("## Project knowledge impact");
|
|
67
|
+
if (envelope.briefing.citations.length > 0) {
|
|
68
|
+
for (const citation of envelope.briefing.citations) {
|
|
69
|
+
lines.push(`- **${citation.id}**${citation.title ? `: ${citation.title}` : ""}${citation.source ? ` (${citation.source})` : ""}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if ((envelope.briefing.constraints?.length ?? 0) > 0) {
|
|
73
|
+
for (const constraint of envelope.briefing.constraints ?? []) {
|
|
74
|
+
lines.push(`- ${constraint.statement}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if ((envelope.briefing.regressionRisks?.length ?? 0) > 0) {
|
|
78
|
+
for (const risk of envelope.briefing.regressionRisks ?? []) {
|
|
79
|
+
lines.push(`- ${risk.statement}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
lines.push("");
|
|
83
|
+
}
|
|
84
|
+
const hasMissingEvidence = (envelope.briefing.missingEvidence?.length ?? 0) > 0;
|
|
85
|
+
if (envelope.validation.count > 0 || hasMissingEvidence) {
|
|
86
|
+
lines.push("## Interpretation note");
|
|
87
|
+
if (envelope.validation.count > 0) {
|
|
88
|
+
lines.push(`Validation checks reported unresolved items: ${envelope.validation.count} issue(s).`);
|
|
89
|
+
}
|
|
90
|
+
if (hasMissingEvidence) {
|
|
91
|
+
lines.push("This brief includes unresolved evidence notes:");
|
|
92
|
+
for (const item of envelope.briefing.missingEvidence ?? []) {
|
|
93
|
+
lines.push(`- ${item.statement}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
lines.push("");
|
|
97
|
+
}
|
|
98
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
99
|
+
lines.pop();
|
|
100
|
+
}
|
|
101
|
+
const result = lines.join("\n");
|
|
102
|
+
if (result === "## What changed") {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
function buildTuiBriefToastPayload(envelope) {
|
|
108
|
+
const message = buildTuiBriefMessage(envelope);
|
|
109
|
+
if (message === undefined) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
variant: envelope.type === "warning" ? "warning" : "info",
|
|
114
|
+
title: "Kibi Knowledge Update",
|
|
115
|
+
message,
|
|
116
|
+
duration: 8000,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function hasSignificantBriefingImpact(envelope) {
|
|
120
|
+
const briefing = envelope.briefing;
|
|
121
|
+
return !(briefing.citations.length === 0 &&
|
|
122
|
+
(!briefing.constraints || briefing.constraints.length === 0) &&
|
|
123
|
+
(!briefing.regressionRisks || briefing.regressionRisks.length === 0) &&
|
|
124
|
+
(!briefing.missingEvidence || briefing.missingEvidence.length === 0));
|
|
125
|
+
}
|
|
126
|
+
function isNoOpBriefEnvelope(envelope) {
|
|
127
|
+
const counts = envelope.counts;
|
|
128
|
+
const zeroCounts = "relationshipsChanged" in counts
|
|
129
|
+
? counts.entitiesAdded === 0 &&
|
|
130
|
+
counts.entitiesModified === 0 &&
|
|
131
|
+
counts.entitiesRemoved === 0 &&
|
|
132
|
+
counts.relationshipsChanged === 0
|
|
133
|
+
: counts.requirementsAdded === 0 &&
|
|
134
|
+
counts.relationshipsAdded === 0 &&
|
|
135
|
+
counts.entitiesDeleted === 0;
|
|
136
|
+
const briefing = envelope.briefing;
|
|
137
|
+
const hasDeliveryReasons = (briefing.deliveryReasons?.items.length ?? 0) > 0;
|
|
138
|
+
if (hasDeliveryReasons) {
|
|
139
|
+
const toast = briefing.deliveryReasons ? renderToastSummary(briefing.deliveryReasons) : undefined;
|
|
140
|
+
if (toast === undefined)
|
|
141
|
+
return true; // all operational → no-op
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// Suppress legacy (no deliveryReasons) envelopes only when all three conditions hold:
|
|
145
|
+
// zero change counts, no validation issues, and no significant briefing impact.
|
|
146
|
+
// Matching summary/tldr alone is not sufficient — a domain-specific brief may legitimately
|
|
147
|
+
// have the same value in both fields.
|
|
148
|
+
return (zeroCounts &&
|
|
149
|
+
envelope.validation.count === 0 &&
|
|
150
|
+
!hasSignificantBriefingImpact(envelope));
|
|
151
|
+
}
|
|
152
|
+
function getEnvelopeChangeTotal(envelope) {
|
|
153
|
+
const counts = envelope.counts;
|
|
154
|
+
return "relationshipsChanged" in counts
|
|
155
|
+
? counts.entitiesAdded +
|
|
156
|
+
counts.entitiesModified +
|
|
157
|
+
counts.entitiesRemoved +
|
|
158
|
+
counts.relationshipsChanged
|
|
159
|
+
: counts.requirementsAdded + counts.relationshipsAdded + counts.entitiesDeleted;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Delivers a Kibi briefing to the TUI via toast notification.
|
|
163
|
+
*
|
|
164
|
+
* Uses the REAL OpenCode plugin API:
|
|
165
|
+
* - client.tui.showToast(payload) — primary (and only) delivery mechanism
|
|
166
|
+
*
|
|
167
|
+
* The toast contains a rich summary from the envelope and is displayed
|
|
168
|
+
* for 8 seconds so users can read the content.
|
|
169
|
+
*
|
|
170
|
+
* @param client - OpenCode client with optional TUI capabilities
|
|
171
|
+
* @param envelope - Idle brief envelope containing briefing content
|
|
172
|
+
* @param sharedPolicy - Shared brief policy from `.kb/config.json`
|
|
173
|
+
* @param localConfig - Local OpenCode config
|
|
174
|
+
*/
|
|
175
|
+
// implements REQ-opencode-kibi-briefing-v4
|
|
176
|
+
export async function deliverBriefTui(client, envelope, sharedPolicy, _localConfig) {
|
|
177
|
+
// Early exit if TUI delivery is disabled
|
|
178
|
+
if (!sharedPolicy.briefs.channels.tui) {
|
|
179
|
+
logger.info("TUI brief delivery disabled by shared policy");
|
|
180
|
+
return { delivered: false };
|
|
181
|
+
}
|
|
182
|
+
const tui = client.tui;
|
|
183
|
+
// Toast is the primary delivery mechanism
|
|
184
|
+
if (sharedPolicy.briefs.tui.toast && typeof tui?.showToast === "function") {
|
|
185
|
+
if (isNoOpBriefEnvelope(envelope)) {
|
|
186
|
+
return { delivered: false };
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const message = buildTuiBriefMessage(envelope);
|
|
190
|
+
if (message === undefined) {
|
|
191
|
+
return { delivered: false };
|
|
192
|
+
}
|
|
193
|
+
await tui.showToast({
|
|
194
|
+
body: {
|
|
195
|
+
variant: envelope.type === "warning" ? "warning" : "info",
|
|
196
|
+
title: "Kibi Knowledge Update",
|
|
197
|
+
message,
|
|
198
|
+
duration: 8000,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
return { delivered: true };
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
logger.error("Failed to deliver brief toast", {
|
|
205
|
+
event: "idle_brief_toast_failed",
|
|
206
|
+
error: err instanceof Error ? err.message : String(err),
|
|
207
|
+
});
|
|
208
|
+
return { delivered: false };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
logger.info("TUI showToast API unavailable, brief not delivered");
|
|
213
|
+
return { delivered: false };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Announcement-only TUI delivery coordinator.
|
|
218
|
+
*
|
|
219
|
+
* Sends the summary toast and invokes the official SDK bridge
|
|
220
|
+
* (`executeCommand`) but does NOT mutate read/seen state.
|
|
221
|
+
* The caller is responsible for any state transitions after the
|
|
222
|
+
* TUI route confirms render success.
|
|
223
|
+
*/
|
|
224
|
+
export async function announceBriefTui(// implements REQ-opencode-kibi-briefing-v6
|
|
225
|
+
client, envelope, sharedPolicy) {
|
|
226
|
+
if (!sharedPolicy.briefs.channels.tui) {
|
|
227
|
+
logger.info("TUI brief delivery disabled by shared policy");
|
|
228
|
+
return { toastDelivered: false, commandPublished: false };
|
|
229
|
+
}
|
|
230
|
+
const briefing = envelope.briefing;
|
|
231
|
+
if (!envelope.unread &&
|
|
232
|
+
isNoOpBriefEnvelope(envelope) &&
|
|
233
|
+
!(briefing.deliveryReasons?.items.length ?? 0)) {
|
|
234
|
+
return { toastDelivered: false, commandPublished: false };
|
|
235
|
+
}
|
|
236
|
+
const totalChanges = getEnvelopeChangeTotal(envelope);
|
|
237
|
+
const hasDeliveryReasons = (briefing.deliveryReasons?.items.length ?? 0) > 0;
|
|
238
|
+
if (!envelope.unread &&
|
|
239
|
+
totalChanges === 0 &&
|
|
240
|
+
envelope.validation.count === 0 &&
|
|
241
|
+
!hasDeliveryReasons &&
|
|
242
|
+
isNoOpBriefEnvelope(envelope)) {
|
|
243
|
+
return { toastDelivered: false, commandPublished: false };
|
|
244
|
+
}
|
|
245
|
+
let toastDelivered = false;
|
|
246
|
+
let commandPublished = false;
|
|
247
|
+
if (sharedPolicy.briefs.tui.toast) {
|
|
248
|
+
const payload = buildTuiBriefToastPayload(envelope);
|
|
249
|
+
if (payload !== undefined) {
|
|
250
|
+
const toastResult = await sendToast(client, payload);
|
|
251
|
+
if (toastResult.status === "delivered") {
|
|
252
|
+
toastDelivered = true;
|
|
253
|
+
}
|
|
254
|
+
else if (toastResult.status === "failed") {
|
|
255
|
+
logger.error("Failed to deliver brief toast", {
|
|
256
|
+
event: "idle_brief_toast_failed",
|
|
257
|
+
error: toastResult.error ?? toastResult.reason,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
logger.info("TUI showToast API unavailable, brief not delivered");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Step 2: Invoke the SDK command bridge to open the brief in the TUI
|
|
266
|
+
if (typeof client.tui?.executeCommand === "function") {
|
|
267
|
+
try {
|
|
268
|
+
await client.tui.executeCommand("kibi.open_latest_brief", {});
|
|
269
|
+
commandPublished = true;
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
logger.error("Failed to publish open_latest_brief command", {
|
|
273
|
+
event: "idle_brief_command_failed",
|
|
274
|
+
error: err instanceof Error ? err.message : String(err),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return { toastDelivered, commandPublished };
|
|
279
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { IdleBriefCitation, IdleBriefEnvelope, IdleBriefStatement } from "./idle-brief-store.js";
|
|
2
|
+
export interface TuiBriefViewModel {
|
|
3
|
+
briefId: string;
|
|
4
|
+
schemaVersion: "1.0" | "2.0";
|
|
5
|
+
branch: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
type: "success" | "warning";
|
|
8
|
+
unread: boolean;
|
|
9
|
+
contentHash: string;
|
|
10
|
+
/** Short human-readable title derived from the envelope */
|
|
11
|
+
title: string;
|
|
12
|
+
/** "What changed" section content */
|
|
13
|
+
whatChanged: string[];
|
|
14
|
+
/** "Why it matters" section content */
|
|
15
|
+
whyItMatters: string | undefined;
|
|
16
|
+
/** Project knowledge impact section (citations, constraints, risks) */
|
|
17
|
+
knowledgeImpact: {
|
|
18
|
+
citations: IdleBriefCitation[];
|
|
19
|
+
constraints: IdleBriefStatement[];
|
|
20
|
+
regressionRisks: IdleBriefStatement[];
|
|
21
|
+
};
|
|
22
|
+
/** Interpretation note section (validation + missing evidence) */
|
|
23
|
+
interpretationNote: {
|
|
24
|
+
validationCount: number;
|
|
25
|
+
missingEvidence: IdleBriefStatement[];
|
|
26
|
+
};
|
|
27
|
+
/** Summary counts (schema-aware) */
|
|
28
|
+
counts: {
|
|
29
|
+
schemaVersion: "1.0";
|
|
30
|
+
requirementsAdded: number;
|
|
31
|
+
relationshipsAdded: number;
|
|
32
|
+
entitiesDeleted: number;
|
|
33
|
+
} | {
|
|
34
|
+
schemaVersion: "2.0";
|
|
35
|
+
entitiesAdded: number;
|
|
36
|
+
entitiesModified: number;
|
|
37
|
+
entitiesRemoved: number;
|
|
38
|
+
relationshipsChanged: number;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a structured view model from a persisted brief envelope.
|
|
43
|
+
*
|
|
44
|
+
* Derives all route-rendering data (title, sections, citations, counts) from
|
|
45
|
+
* the envelope without regenerating any content. Supports both schema 1.0 and
|
|
46
|
+
* 2.0 during the migration window.
|
|
47
|
+
*
|
|
48
|
+
* @param envelope - The persisted brief envelope
|
|
49
|
+
* @returns A deterministic view model suitable for route rendering
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildTuiBriefViewModel(// implements REQ-opencode-kibi-briefing-v6
|
|
52
|
+
envelope: IdleBriefEnvelope): TuiBriefViewModel;
|
|
53
|
+
/**
|
|
54
|
+
* Build a short summary text from a persisted brief envelope.
|
|
55
|
+
*
|
|
56
|
+
* Reuses the same section-building logic as `buildTuiBriefMessage` from
|
|
57
|
+
* `tui-brief-delivery.ts`, producing a deterministic plain-text summary
|
|
58
|
+
* suitable for TUI route rendering or server-side summary generation.
|
|
59
|
+
*
|
|
60
|
+
* @param envelope - The persisted brief envelope
|
|
61
|
+
* @returns A multi-line summary string
|
|
62
|
+
*/
|
|
63
|
+
export declare function buildTuiBriefSummary(envelope: IdleBriefEnvelope): string;
|