kibi-opencode 0.14.0 → 0.15.1
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 +18 -12
- package/dist/config.d.ts +0 -3
- package/dist/config.js +0 -9
- package/dist/e2e-coverage-signals.js +1 -3
- package/dist/enforcement-policy.d.ts +2 -0
- package/dist/enforcement-policy.js +18 -3
- package/dist/enforcement-scope.js +1 -3
- package/dist/file-entity-links.js +2 -6
- package/dist/file-operation-reminders.d.ts +4 -4
- package/dist/file-operation-state.js +4 -2
- package/dist/init-kibi-alias.js +0 -1
- package/dist/init-kibi-capability.js +1 -5
- package/dist/kb-freshness-state.d.ts +62 -0
- package/dist/kb-freshness-state.js +215 -0
- package/dist/kibi-checkpoint-runner.d.ts +1 -1
- package/dist/kibi-checkpoint-runner.js +6 -3
- package/dist/knowledge-classifier.js +2 -1
- package/dist/meaningful-change-classifier.d.ts +30 -0
- package/dist/meaningful-change-classifier.js +103 -0
- package/dist/plugin-startup.js +0 -1
- package/dist/plugin.js +101 -391
- package/dist/prompt.d.ts +6 -5
- package/dist/prompt.js +48 -84
- package/dist/reconcile-engine.d.ts +15 -1
- package/dist/reconcile-engine.js +1 -3
- package/dist/scheduler.js +16 -4
- package/dist/session-edit-state.js +2 -1
- package/dist/source-linked-guidance.js +3 -1
- package/dist/startup-notifier.d.ts +7 -1
- package/dist/startup-notifier.js +51 -17
- package/dist/tui.js +1 -403
- package/dist/tui.jsx +1 -113
- package/dist/version-metadata.d.ts +13 -0
- package/dist/version-metadata.js +78 -0
- package/dist/version-metadata.json +1 -0
- package/package.json +5 -7
- package/dist/brief-delivery-reasons.d.ts +0 -12
- package/dist/brief-delivery-reasons.js +0 -132
- package/dist/brief-intent.d.ts +0 -41
- package/dist/brief-intent.js +0 -142
- package/dist/briefing-runtime.d.ts +0 -24
- package/dist/briefing-runtime.js +0 -277
- package/dist/graph-narrator.d.ts +0 -25
- package/dist/graph-narrator.js +0 -408
- package/dist/idle-brief-audit.d.ts +0 -36
- package/dist/idle-brief-audit.js +0 -186
- package/dist/idle-brief-paths.d.ts +0 -6
- package/dist/idle-brief-paths.js +0 -120
- package/dist/idle-brief-reader.d.ts +0 -37
- package/dist/idle-brief-reader.js +0 -163
- package/dist/idle-brief-runtime.d.ts +0 -55
- package/dist/idle-brief-runtime.js +0 -496
- package/dist/idle-brief-store.d.ts +0 -113
- package/dist/idle-brief-store.js +0 -262
- package/dist/session-fingerprint.d.ts +0 -11
- package/dist/session-fingerprint.js +0 -21
- package/dist/tui-brief-delivery.d.ts +0 -67
- package/dist/tui-brief-delivery.js +0 -279
- package/dist/tui-brief-view-model.d.ts +0 -63
- package/dist/tui-brief-view-model.js +0 -209
- package/dist/utils/brief-marker.d.ts +0 -19
- package/dist/utils/brief-marker.js +0 -101
package/dist/prompt.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { CommentAnalysisResult } from "./comment-analysis.js";
|
|
2
|
-
import type { BriefingRuntimeResult } from "./briefing-runtime.js";
|
|
3
2
|
import type { KibiConfig } from "./config.js";
|
|
4
|
-
import { type InitKibiCommandCapability } from "./init-kibi-capability.js";
|
|
5
3
|
import type { GuidanceCache } from "./guidance-cache.js";
|
|
4
|
+
import { type InitKibiCommandCapability } from "./init-kibi-capability.js";
|
|
5
|
+
import type { KbFreshnessEvaluation } from "./kb-freshness-state.js";
|
|
6
6
|
import type { PathKind } from "./path-kind.js";
|
|
7
7
|
import type { RepoPosture } from "./repo-posture.js";
|
|
8
8
|
import type { RiskClass } from "./risk-classifier.js";
|
|
9
9
|
import type { WorkspaceHealth } from "./workspace-health.js";
|
|
10
10
|
declare const SENTINEL = "<!-- kibi-opencode -->";
|
|
11
|
-
export declare function buildAutoBriefingGuidance(autoBriefResult: BriefingRuntimeResult | undefined, completionReminder: boolean): string | null;
|
|
12
11
|
export interface PromptContext {
|
|
13
12
|
recentEdits: Array<{
|
|
14
13
|
path: string;
|
|
@@ -39,8 +38,6 @@ export interface PromptContext {
|
|
|
39
38
|
degradedMode?: "warn-once" | "structured-only";
|
|
40
39
|
/** Whether to show the degraded advisory block this invocation */
|
|
41
40
|
showDegradedAdvisory?: boolean;
|
|
42
|
-
/** Stored auto-brief runtime result for the current fingerprint */
|
|
43
|
-
autoBriefResult?: BriefingRuntimeResult;
|
|
44
41
|
/** File-operation reminder from lifecycle and e2e coverage signals */
|
|
45
42
|
fileOperationReminder?: {
|
|
46
43
|
path: string;
|
|
@@ -53,6 +50,10 @@ export interface PromptContext {
|
|
|
53
50
|
remainingCount: number;
|
|
54
51
|
reason?: string;
|
|
55
52
|
};
|
|
53
|
+
/** KB freshness evaluation from evidence store */
|
|
54
|
+
kbFreshness?: KbFreshnessEvaluation;
|
|
55
|
+
/** Paths changed for freshness evaluation (capped at 5) */
|
|
56
|
+
freshnessChangedPaths?: string[];
|
|
56
57
|
}
|
|
57
58
|
export declare function postureGuidance(posture: RepoPosture, capability?: InitKibiCommandCapability): string | null;
|
|
58
59
|
/**
|
package/dist/prompt.js
CHANGED
|
@@ -5,13 +5,7 @@ import { getSourceLinkedRequirementIds } from "./source-linked-guidance.js";
|
|
|
5
5
|
const SENTINEL = "<!-- kibi-opencode -->";
|
|
6
6
|
// ── Token budget enforcement ───────────────────────────────────────────
|
|
7
7
|
const MAX_BULLETS = 5;
|
|
8
|
-
const MAX_AUTO_BRIEF_BULLETS_WITH_REMINDER = 4;
|
|
9
8
|
const MAX_WORDS = 117; // Reserve 3 words for sentinel so total injected prompt stays ≤ 120
|
|
10
|
-
const AUTO_BRIEF_HEADER = "🧠 **Kibi briefing available**";
|
|
11
|
-
const AUTHORITATIVE_POSTURES = [
|
|
12
|
-
"root_active",
|
|
13
|
-
"hybrid_root_plus_vendored",
|
|
14
|
-
];
|
|
15
9
|
function countWords(text) {
|
|
16
10
|
return text.split(/\s+/).filter(Boolean).length;
|
|
17
11
|
}
|
|
@@ -55,50 +49,12 @@ function insertBulletAfterHeader(block, bullet) {
|
|
|
55
49
|
return `${block}\n${bullet}`;
|
|
56
50
|
return `${block.slice(0, headerEnd + 1)}${bullet}\n${block.slice(headerEnd + 1)}`;
|
|
57
51
|
}
|
|
58
|
-
// implements REQ-opencode-kibi-briefing-v2
|
|
59
|
-
export function buildAutoBriefingGuidance(autoBriefResult, completionReminder) {
|
|
60
|
-
if (!autoBriefResult)
|
|
61
|
-
return null;
|
|
62
|
-
// Defensive: idle-brief results are persisted to .kb/briefs/, never injected into prompts.
|
|
63
|
-
// This function only handles auto-briefs from the file.edited risk-classification flow.
|
|
64
|
-
if (typeof autoBriefResult === "object" &&
|
|
65
|
-
autoBriefResult !== null &&
|
|
66
|
-
("briefId" in autoBriefResult || "schemaVersion" in autoBriefResult)) {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
if (autoBriefResult.state === "ready") {
|
|
70
|
-
const promptBlock = autoBriefResult.promptBlock.trim();
|
|
71
|
-
if (!promptBlock)
|
|
72
|
-
return null;
|
|
73
|
-
const maxBullets = completionReminder
|
|
74
|
-
? MAX_AUTO_BRIEF_BULLETS_WITH_REMINDER
|
|
75
|
-
: MAX_BULLETS;
|
|
76
|
-
const briefingLines = promptBlock
|
|
77
|
-
.split("\n")
|
|
78
|
-
.map((line) => line.trim())
|
|
79
|
-
.filter((line) => line.startsWith("-"))
|
|
80
|
-
.slice(0, maxBullets);
|
|
81
|
-
if (briefingLines.length === 0)
|
|
82
|
-
return null;
|
|
83
|
-
return `${AUTO_BRIEF_HEADER}\n${briefingLines.join("\n")}`;
|
|
84
|
-
}
|
|
85
|
-
if (autoBriefResult.state === "tldr_fallback") {
|
|
86
|
-
const promptBlock = autoBriefResult.promptBlock.trim();
|
|
87
|
-
if (!promptBlock)
|
|
88
|
-
return null;
|
|
89
|
-
return `${AUTO_BRIEF_HEADER}\n${promptBlock}`;
|
|
90
|
-
}
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
function isAuthoritativePosture(posture) {
|
|
94
|
-
return AUTHORITATIVE_POSTURES.includes(posture);
|
|
95
|
-
}
|
|
96
52
|
// ── File bucket derivation ─────────────────────────────────────────────
|
|
97
53
|
function deriveFileBucket(pathKind) {
|
|
98
54
|
return pathKind;
|
|
99
55
|
}
|
|
100
56
|
function getFocusEdit(context) {
|
|
101
|
-
return context.focusEdit ?? context.recentEdits[context.recentEdits.length - 1];
|
|
57
|
+
return (context.focusEdit ?? context.recentEdits[context.recentEdits.length - 1]);
|
|
102
58
|
}
|
|
103
59
|
function buildInitKibiBootstrapReference(capability = getInitKibiCommandCapability()) {
|
|
104
60
|
if (capability.supported) {
|
|
@@ -110,7 +66,7 @@ function buildBootstrapRequiredBody(capability = getInitKibiCommandCapability())
|
|
|
110
66
|
const commandBullet = capability.supported
|
|
111
67
|
? "- When the Kibi OpenCode plugin is active and native injection is supported, use `/init-kibi` as the canonical short alias; `/kibi:init-kibi:mcp` remains the namespaced MCP fallback."
|
|
112
68
|
: "- This host does not support native `/init-kibi` injection. Kibi must fail closed and does not register a fake native alias; use `/kibi:init-kibi:mcp` instead.";
|
|
113
|
-
return `This repository does not appear to have Kibi initialized. Agents should:\n${commandBullet}\n- The workflow uses \`kb_autopilot_generate\` for read-only synthesis; always preview and get approval before writes.\n- Ask the user/operator to run setup or repair outside this session if bootstrap is insufficient.\n\nUse public MCP tools only: \`kb_autopilot_generate\`, \`kb_search\`, \`kb_query\`, \`kb_status\`, \`kb_find_gaps\`, \`kb_coverage\`, \`kb_graph\`, \`kb_upsert\`, \`kb_delete\`, \`kb_check\`.`;
|
|
69
|
+
return `This repository does not appear to have Kibi initialized. Agents should:\n${commandBullet}\n- The workflow uses \`kb_autopilot_generate\` for read-only synthesis; always preview and get approval before writes.\n- Ask the user/operator to run setup or repair outside this session if bootstrap is insufficient.\n- For comprehensive Kibi usage guidance, use \`kb_skills_load\` with skill id \`kibi-usage\`.\n\nUse public MCP tools only: \`kb_autopilot_generate\`, \`kb_search\`, \`kb_query\`, \`kb_status\`, \`kb_find_gaps\`, \`kb_coverage\`, \`kb_graph\`, \`kb_upsert\`, \`kb_delete\`, \`kb_check\`, \`kb_skills_load\`.`;
|
|
114
70
|
}
|
|
115
71
|
function buildHardGateBlock(block) {
|
|
116
72
|
const pathLines = block.shownPaths.map((path) => `- \`${path}\``);
|
|
@@ -198,9 +154,6 @@ function buildContextualGuidance(context, capability = getInitKibiCommandCapabil
|
|
|
198
154
|
}
|
|
199
155
|
const posture = context.posture ?? "root_active";
|
|
200
156
|
const riskClass = context.riskClass;
|
|
201
|
-
const readyAutoBriefingAvailable = context.autoBriefResult?.showManualCue === false;
|
|
202
|
-
const suppressSourceLinkedBrief = context.autoBriefResult?.state === "ready" ||
|
|
203
|
-
context.autoBriefResult?.state === "tldr_fallback";
|
|
204
157
|
const showDegraded = context.showDegradedAdvisory === true &&
|
|
205
158
|
context.maintenanceDegraded === true &&
|
|
206
159
|
context.degradedMode === "warn-once";
|
|
@@ -240,6 +193,7 @@ ${buildBootstrapRequiredBody(capability)}`;
|
|
|
240
193
|
context.cache &&
|
|
241
194
|
context.workspaceRoot &&
|
|
242
195
|
context.branch &&
|
|
196
|
+
!context.kbFreshness?.requiresEvidence &&
|
|
243
197
|
riskClass) {
|
|
244
198
|
const focusEdit = getFocusEdit(context);
|
|
245
199
|
const key = {
|
|
@@ -263,25 +217,16 @@ ${buildBootstrapRequiredBody(capability)}`;
|
|
|
263
217
|
riskClass &&
|
|
264
218
|
riskClass !== "safe_docs_only" &&
|
|
265
219
|
riskClass !== "safe_test_only") {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
selectedBlock = autoBriefBlock;
|
|
220
|
+
// For behavior/traceability with comment suggestions, use suggestion guidance
|
|
221
|
+
if ((riskClass === "behavior_candidate" ||
|
|
222
|
+
riskClass === "traceability_candidate") &&
|
|
223
|
+
context.recentCommentSuggestion) {
|
|
224
|
+
selectedBlock = buildCommentSuggestionGuidance(context.recentCommentSuggestion);
|
|
272
225
|
}
|
|
273
226
|
else {
|
|
274
|
-
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
context.recentCommentSuggestion) {
|
|
278
|
-
selectedBlock = buildCommentSuggestionGuidance(context.recentCommentSuggestion);
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
const block = GUIDANCE_BY_RISK[riskClass];
|
|
282
|
-
if (block)
|
|
283
|
-
selectedBlock = block;
|
|
284
|
-
}
|
|
227
|
+
const block = GUIDANCE_BY_RISK[riskClass];
|
|
228
|
+
if (block)
|
|
229
|
+
selectedBlock = block;
|
|
285
230
|
}
|
|
286
231
|
}
|
|
287
232
|
// Priority 6: Legacy path-kind fallback (when no risk class)
|
|
@@ -326,23 +271,14 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
326
271
|
}
|
|
327
272
|
}
|
|
328
273
|
} // closing brace for Priority 2-4 else block starting at 187
|
|
329
|
-
|
|
330
|
-
(riskClass === "behavior_candidate" ||
|
|
331
|
-
riskClass === "traceability_candidate") &&
|
|
332
|
-
isAuthoritativePosture(posture) &&
|
|
333
|
-
!context.maintenanceDegraded &&
|
|
334
|
-
!readyAutoBriefingAvailable) {
|
|
335
|
-
selectedBlock = insertBulletAfterHeader(selectedBlock, "- Authoritative risky edit: run `/brief-kibi` before acting.");
|
|
336
|
-
}
|
|
337
|
-
// Source-linked micro-brief: insert after header line for code risk classes
|
|
274
|
+
// Source-linked context: insert after header line for code risk classes
|
|
338
275
|
// Inserting after the header (not prepending before it) preserves the header
|
|
339
276
|
// under enforceBudget's trimming logic, which only collects non-bullet lines
|
|
340
277
|
// before the first bullet.
|
|
341
278
|
if (selectedBlock &&
|
|
342
279
|
(riskClass === "behavior_candidate" ||
|
|
343
280
|
riskClass === "traceability_candidate") &&
|
|
344
|
-
context.workspaceRoot
|
|
345
|
-
!suppressSourceLinkedBrief) {
|
|
281
|
+
context.workspaceRoot) {
|
|
346
282
|
try {
|
|
347
283
|
const focusEdit = getFocusEdit(context);
|
|
348
284
|
if (focusEdit?.path) {
|
|
@@ -357,7 +293,7 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
357
293
|
}
|
|
358
294
|
}
|
|
359
295
|
catch {
|
|
360
|
-
// Non-fatal: source-linked
|
|
296
|
+
// Non-fatal: source-linked context is best-effort
|
|
361
297
|
}
|
|
362
298
|
}
|
|
363
299
|
// ── File-operation reminder folding ─────────────────────────────────
|
|
@@ -368,7 +304,7 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
368
304
|
if (hasFileOpReminders && fileOpReminder) {
|
|
369
305
|
const foBullets = [];
|
|
370
306
|
if (fileOpReminder.lifecycleReminder) {
|
|
371
|
-
// Skip lifecycleReminder if source-linked
|
|
307
|
+
// Skip lifecycleReminder if source-linked context already shows the same IDs
|
|
372
308
|
const hasSourceLinked = selectedBlock?.includes("- Existing Kibi links:") === true;
|
|
373
309
|
const lifecycleHasEntities = fileOpReminder.lifecycleReminder.includes("Kibi entities:");
|
|
374
310
|
const overlapsSourceLinked = hasSourceLinked &&
|
|
@@ -455,10 +391,33 @@ The Kibi workspace is in a maintenance-degraded state. Guidance remains advisory
|
|
|
455
391
|
posture !== "root_partial") {
|
|
456
392
|
finalBlock = `${finalBlock}\n- Kibi impact evidence is required before completion/commit: run \`kb_check\` before completing this task.`;
|
|
457
393
|
}
|
|
394
|
+
// Prepend KB freshness block when evidence is required
|
|
395
|
+
if (context.kbFreshness?.requiresEvidence) {
|
|
396
|
+
const changedListing = context.freshnessChangedPaths?.length
|
|
397
|
+
? `Changed: ${context.freshnessChangedPaths
|
|
398
|
+
.slice(0, 5)
|
|
399
|
+
.map((p) => `\`${p}\``)
|
|
400
|
+
.join(", ")}`
|
|
401
|
+
: null;
|
|
402
|
+
const freshnessLines = [
|
|
403
|
+
"🧠 **Kibi freshness required**",
|
|
404
|
+
changedListing,
|
|
405
|
+
`State: ${context.kbFreshness.state}. ${context.kbFreshness.reason}`,
|
|
406
|
+
...(context.kbFreshness.missingEvidence.length > 0
|
|
407
|
+
? [`Missing: ${context.kbFreshness.missingEvidence.join(", ")}`]
|
|
408
|
+
: []),
|
|
409
|
+
"Resolution:",
|
|
410
|
+
"- **KB updated**: run `kb_search` for discovery, then `kb_upsert`/`kb_delete` for mutations, then `kb_check`.",
|
|
411
|
+
"- **No KB impact**: include a no-impact rationale in your final report after source-linked discovery via `kb_search`/`kb_query(sourceFile=...)` and `kb_check`.",
|
|
412
|
+
]
|
|
413
|
+
.filter(Boolean)
|
|
414
|
+
.join("\n");
|
|
415
|
+
finalBlock = finalBlock
|
|
416
|
+
? `${freshnessLines}\n\n${finalBlock}`
|
|
417
|
+
: freshnessLines;
|
|
418
|
+
}
|
|
458
419
|
// Return: sentinel + one targeted block (or just sentinel if no block)
|
|
459
|
-
return finalBlock
|
|
460
|
-
? `${SENTINEL}\n\n${finalBlock}`
|
|
461
|
-
: SENTINEL;
|
|
420
|
+
return finalBlock ? `${SENTINEL}\n\n${finalBlock}` : SENTINEL;
|
|
462
421
|
}
|
|
463
422
|
// ── Comment suggestion guidance (legacy compat) ────────────────────────
|
|
464
423
|
function buildCommentSuggestionGuidance(suggestion) {
|
|
@@ -531,7 +490,13 @@ Dogfood note for this repo: OpenCode here uses local built \`kibi-mcp\` and \`ki
|
|
|
531
490
|
6. **Validate**: Run kb_check after KB mutations to catch violations early.
|
|
532
491
|
7. **Before completion/commit**: Kibi impact evidence is required before completion/commit. If extraction output changes, refresh documentation/symbols.yaml and do not revert that update as scope creep.
|
|
533
492
|
|
|
534
|
-
**Public Kibi tools only:** kb_autopilot_generate, kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check
|
|
493
|
+
**Public Kibi tools only:** kb_autopilot_generate, kb_search, kb_query, kb_status, kb_find_gaps, kb_coverage, kb_graph, kb_upsert, kb_delete, kb_check, kb_skills_load.
|
|
494
|
+
|
|
495
|
+
For comprehensive Kibi usage guidance (relationships, fact lanes, workflows), use \`kb_skills_load\` with skill id \`kibi-usage\`.
|
|
496
|
+
|
|
497
|
+
Do not invoke Kibi CLI commands directly from the agent.
|
|
498
|
+
|
|
499
|
+
${buildInitKibiBootstrapReference(capability)}`;
|
|
535
500
|
}
|
|
536
501
|
/**
|
|
537
502
|
* Build prompt with contextual guidance based on posture, risk class, and cache state.
|
|
@@ -545,7 +510,6 @@ export function buildPrompt(context, capability = getInitKibiCommandCapability()
|
|
|
545
510
|
/**
|
|
546
511
|
* Inject prompt guidance if not already present.
|
|
547
512
|
*/
|
|
548
|
-
// implements REQ-opencode-kibi-briefing-v2
|
|
549
513
|
export function injectPrompt(current, config, context, capability = getInitKibiCommandCapability()) {
|
|
550
514
|
if (!config.prompt.enabled || !isPluginEnabled(config)) {
|
|
551
515
|
return current;
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
export interface AuditEntityPayload {
|
|
2
|
+
kind: "entity";
|
|
3
|
+
entityType: string;
|
|
4
|
+
changeKind?: "created" | "updated";
|
|
5
|
+
title?: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
textRef?: string;
|
|
8
|
+
properties: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface AuditEntry {
|
|
11
|
+
timestamp: string;
|
|
12
|
+
operation: "upsert" | "delete" | "upsert_rel";
|
|
13
|
+
entityId: string;
|
|
14
|
+
payload?: AuditEntityPayload | Record<string, unknown>;
|
|
15
|
+
}
|
|
2
16
|
export interface EntityChangeItem {
|
|
3
17
|
id: string;
|
|
4
18
|
type: string;
|
package/dist/reconcile-engine.js
CHANGED
|
@@ -46,9 +46,7 @@ function toChangeItem(payload, entityId) {
|
|
|
46
46
|
function compareChangeItems(left, right) {
|
|
47
47
|
return left.type.localeCompare(right.type) || left.id.localeCompare(right.id);
|
|
48
48
|
}
|
|
49
|
-
export function reconcileAuditEntries(
|
|
50
|
-
// implements REQ-opencode-kibi-briefing-v6
|
|
51
|
-
entries) {
|
|
49
|
+
export function reconcileAuditEntries(entries) {
|
|
52
50
|
const states = new Map();
|
|
53
51
|
let relationshipsChanged = 0;
|
|
54
52
|
for (const entry of [...entries].sort((left, right) => left.timestamp.localeCompare(right.timestamp))) {
|
package/dist/scheduler.js
CHANGED
|
@@ -34,7 +34,9 @@ class WorktreeSyncScheduler {
|
|
|
34
34
|
if (!this.config.sync.enabled)
|
|
35
35
|
return;
|
|
36
36
|
// Treat file.created, file.edited, and file.deleted same relevance-wise
|
|
37
|
-
const isFileLifecycle = reason === "file.edited" ||
|
|
37
|
+
const isFileLifecycle = reason === "file.edited" ||
|
|
38
|
+
reason === "file.created" ||
|
|
39
|
+
reason === "file.deleted";
|
|
38
40
|
if (isFileLifecycle) {
|
|
39
41
|
if (!filePath)
|
|
40
42
|
return;
|
|
@@ -181,7 +183,11 @@ class WorktreeSyncScheduler {
|
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
185
|
isIdle() {
|
|
184
|
-
return !this.inFlight &&
|
|
186
|
+
return (!this.inFlight &&
|
|
187
|
+
!this.timer &&
|
|
188
|
+
!this.pending &&
|
|
189
|
+
!this.dirty &&
|
|
190
|
+
!this.trailing);
|
|
185
191
|
}
|
|
186
192
|
resolveFlushWaitersIfIdle() {
|
|
187
193
|
if (!this.isIdle())
|
|
@@ -245,7 +251,9 @@ async function runKibiSync(worktree) {
|
|
|
245
251
|
const truncatedOut = truncateSyncOutput(stdout || undefined);
|
|
246
252
|
const truncatedErr = truncateSyncOutput(stderr || undefined);
|
|
247
253
|
const signal = error.signal ? ` (signal: ${error.signal})` : "";
|
|
248
|
-
const errorMessage = error.message
|
|
254
|
+
const errorMessage = error.message
|
|
255
|
+
? `${error.message}${signal}`
|
|
256
|
+
: signal || undefined;
|
|
249
257
|
resolve({
|
|
250
258
|
exitCode: error.code ?? 1,
|
|
251
259
|
syncCommand: "kibi sync",
|
|
@@ -261,7 +269,11 @@ async function runKibiSync(worktree) {
|
|
|
261
269
|
}
|
|
262
270
|
catch (err) {
|
|
263
271
|
const message = err instanceof Error ? err.message : String(err);
|
|
264
|
-
resolve({
|
|
272
|
+
resolve({
|
|
273
|
+
exitCode: 1,
|
|
274
|
+
syncCommand: "kibi sync",
|
|
275
|
+
syncErrorMessage: message,
|
|
276
|
+
});
|
|
265
277
|
}
|
|
266
278
|
});
|
|
267
279
|
}
|
|
@@ -125,7 +125,8 @@ export function createSessionEditState(opts) {
|
|
|
125
125
|
if (edits.length === 0)
|
|
126
126
|
return null;
|
|
127
127
|
// edits are sorted ascending by lastReconciledAt, so last = most recent
|
|
128
|
-
|
|
128
|
+
const focusEdit = edits.at(-1);
|
|
129
|
+
return focusEdit ?? null;
|
|
129
130
|
}
|
|
130
131
|
function hasSessionEdits() {
|
|
131
132
|
for (const [, entry] of tracked) {
|
|
@@ -12,5 +12,7 @@ import { getFileLinkedTargetsByType } from "./file-entity-links.js";
|
|
|
12
12
|
export function getSourceLinkedRequirementIds(worktree, editedAbsolutePath) {
|
|
13
13
|
// Delegate to the shared file-entity-links resolver with implements-only filter.
|
|
14
14
|
// implements relationships always target REQ- IDs, so no additional filtering needed.
|
|
15
|
-
return getFileLinkedTargetsByType(worktree, editedAbsolutePath, [
|
|
15
|
+
return getFileLinkedTargetsByType(worktree, editedAbsolutePath, [
|
|
16
|
+
"implements",
|
|
17
|
+
]).slice(0, 3);
|
|
16
18
|
}
|
|
@@ -6,8 +6,14 @@ export type StartupNotifierClient = ToastCapableClient & {
|
|
|
6
6
|
};
|
|
7
7
|
};
|
|
8
8
|
export type StartupNotifierConfig = {
|
|
9
|
-
|
|
9
|
+
versions?: {
|
|
10
|
+
opencode?: string;
|
|
11
|
+
mcp?: string;
|
|
12
|
+
cli?: string;
|
|
13
|
+
core?: string;
|
|
14
|
+
};
|
|
10
15
|
suppressToast?: boolean;
|
|
11
16
|
directory?: string;
|
|
17
|
+
versionMetadataSource?: string;
|
|
12
18
|
};
|
|
13
19
|
export declare function notifyStartup(client: StartupNotifierClient, cfg: StartupNotifierConfig): void;
|
package/dist/startup-notifier.js
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
import { sendToast, } from "./toast.js";
|
|
2
|
+
const VERSION_ORDER = ["opencode", "mcp", "cli", "core"];
|
|
2
3
|
// implements REQ-opencode-kibi-plugin-v1
|
|
3
4
|
export function notifyStartup(client, cfg) {
|
|
4
5
|
const message = "kibi-opencode started";
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
// Build toast message from known versions in order
|
|
7
|
+
const versionParts = [];
|
|
8
|
+
if (cfg.versions) {
|
|
9
|
+
for (const key of VERSION_ORDER) {
|
|
10
|
+
const val = cfg.versions[key];
|
|
11
|
+
if (val !== undefined) {
|
|
12
|
+
versionParts.push(`${key} v${val}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const displayMessage = versionParts.length > 0
|
|
17
|
+
? `${message} (${versionParts.join(", ")})`
|
|
7
18
|
: message;
|
|
19
|
+
// Compute unknown versions (keys from VERSION_ORDER not present in cfg.versions)
|
|
20
|
+
const unknownVersions = [];
|
|
21
|
+
if (cfg.versions) {
|
|
22
|
+
for (const key of VERSION_ORDER) {
|
|
23
|
+
if (cfg.versions[key] === undefined) {
|
|
24
|
+
unknownVersions.push(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
8
28
|
const toastPayload = {
|
|
9
29
|
variant: "success",
|
|
10
30
|
title: "Kibi OpenCode",
|
|
@@ -18,31 +38,36 @@ export function notifyStartup(client, cfg) {
|
|
|
18
38
|
...(cfg.directory ? { directory: cfg.directory } : {}),
|
|
19
39
|
};
|
|
20
40
|
if (result.status === "delivered") {
|
|
21
|
-
void client.app
|
|
41
|
+
void client.app
|
|
42
|
+
.log({
|
|
22
43
|
body: {
|
|
23
44
|
...base,
|
|
24
45
|
level: "info",
|
|
25
46
|
message: "startup toast delivered",
|
|
26
47
|
transport: result.transport,
|
|
27
48
|
},
|
|
28
|
-
})
|
|
49
|
+
})
|
|
50
|
+
.catch(() => {
|
|
29
51
|
// Advisory log failure stays silent
|
|
30
52
|
});
|
|
31
53
|
}
|
|
32
54
|
else if (result.status === "unavailable") {
|
|
33
|
-
void client.app
|
|
55
|
+
void client.app
|
|
56
|
+
.log({
|
|
34
57
|
body: {
|
|
35
58
|
...base,
|
|
36
59
|
level: "info",
|
|
37
60
|
message: "startup toast unavailable",
|
|
38
61
|
reason: result.reason,
|
|
39
62
|
},
|
|
40
|
-
})
|
|
63
|
+
})
|
|
64
|
+
.catch(() => {
|
|
41
65
|
// Advisory log failure stays silent
|
|
42
66
|
});
|
|
43
67
|
}
|
|
44
68
|
else if (result.status === "failed") {
|
|
45
|
-
void client.app
|
|
69
|
+
void client.app
|
|
70
|
+
.log({
|
|
46
71
|
body: {
|
|
47
72
|
...base,
|
|
48
73
|
level: "warn",
|
|
@@ -51,21 +76,30 @@ export function notifyStartup(client, cfg) {
|
|
|
51
76
|
reason: result.reason,
|
|
52
77
|
...(result.error ? { error: result.error } : {}),
|
|
53
78
|
},
|
|
54
|
-
})
|
|
79
|
+
})
|
|
80
|
+
.catch(() => {
|
|
55
81
|
// Advisory log failure stays silent
|
|
56
82
|
});
|
|
57
83
|
}
|
|
58
84
|
});
|
|
59
85
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
// Structured startup log
|
|
87
|
+
const logBody = {
|
|
88
|
+
service: "kibi-opencode",
|
|
89
|
+
level: "info",
|
|
90
|
+
message,
|
|
91
|
+
};
|
|
92
|
+
if (cfg.versions && Object.keys(cfg.versions).length > 0) {
|
|
93
|
+
logBody.versions = cfg.versions;
|
|
94
|
+
logBody.unknownVersions = unknownVersions;
|
|
95
|
+
}
|
|
96
|
+
if (cfg.versionMetadataSource) {
|
|
97
|
+
logBody.versionMetadataSource = cfg.versionMetadataSource;
|
|
98
|
+
}
|
|
99
|
+
if (cfg.directory) {
|
|
100
|
+
logBody.directory = cfg.directory;
|
|
101
|
+
}
|
|
102
|
+
void Promise.resolve(client.app.log({ body: logBody })).catch(() => {
|
|
69
103
|
// Advisory log failure stays silent
|
|
70
104
|
});
|
|
71
105
|
}
|