kibi-opencode 0.15.0 → 0.15.2
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 +4 -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 +1 -1
- package/dist/enforcement-policy.js +9 -4
- 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.js +6 -2
- 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 +1 -1
- package/dist/meaningful-change-classifier.js +1 -2
- package/dist/plugin-startup.js +0 -1
- package/dist/plugin.js +45 -395
- package/dist/prompt.d.ts +2 -6
- package/dist/prompt.js +21 -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/tui.js +1 -403
- package/dist/tui.jsx +1 -113
- package/dist/version-metadata.json +1 -1
- package/package.json +3 -5
- 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.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) {
|
|
@@ -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";
|
|
@@ -264,25 +217,16 @@ ${buildBootstrapRequiredBody(capability)}`;
|
|
|
264
217
|
riskClass &&
|
|
265
218
|
riskClass !== "safe_docs_only" &&
|
|
266
219
|
riskClass !== "safe_test_only") {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
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);
|
|
273
225
|
}
|
|
274
226
|
else {
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
context.recentCommentSuggestion) {
|
|
279
|
-
selectedBlock = buildCommentSuggestionGuidance(context.recentCommentSuggestion);
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
const block = GUIDANCE_BY_RISK[riskClass];
|
|
283
|
-
if (block)
|
|
284
|
-
selectedBlock = block;
|
|
285
|
-
}
|
|
227
|
+
const block = GUIDANCE_BY_RISK[riskClass];
|
|
228
|
+
if (block)
|
|
229
|
+
selectedBlock = block;
|
|
286
230
|
}
|
|
287
231
|
}
|
|
288
232
|
// Priority 6: Legacy path-kind fallback (when no risk class)
|
|
@@ -327,23 +271,14 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
327
271
|
}
|
|
328
272
|
}
|
|
329
273
|
} // closing brace for Priority 2-4 else block starting at 187
|
|
330
|
-
|
|
331
|
-
(riskClass === "behavior_candidate" ||
|
|
332
|
-
riskClass === "traceability_candidate") &&
|
|
333
|
-
isAuthoritativePosture(posture) &&
|
|
334
|
-
!context.maintenanceDegraded &&
|
|
335
|
-
!readyAutoBriefingAvailable) {
|
|
336
|
-
selectedBlock = insertBulletAfterHeader(selectedBlock, "- Authoritative risky edit: run `/brief-kibi` before acting.");
|
|
337
|
-
}
|
|
338
|
-
// Source-linked micro-brief: insert after header line for code risk classes
|
|
274
|
+
// Source-linked context: insert after header line for code risk classes
|
|
339
275
|
// Inserting after the header (not prepending before it) preserves the header
|
|
340
276
|
// under enforceBudget's trimming logic, which only collects non-bullet lines
|
|
341
277
|
// before the first bullet.
|
|
342
278
|
if (selectedBlock &&
|
|
343
279
|
(riskClass === "behavior_candidate" ||
|
|
344
280
|
riskClass === "traceability_candidate") &&
|
|
345
|
-
context.workspaceRoot
|
|
346
|
-
!suppressSourceLinkedBrief) {
|
|
281
|
+
context.workspaceRoot) {
|
|
347
282
|
try {
|
|
348
283
|
const focusEdit = getFocusEdit(context);
|
|
349
284
|
if (focusEdit?.path) {
|
|
@@ -358,7 +293,7 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
358
293
|
}
|
|
359
294
|
}
|
|
360
295
|
catch {
|
|
361
|
-
// Non-fatal: source-linked
|
|
296
|
+
// Non-fatal: source-linked context is best-effort
|
|
362
297
|
}
|
|
363
298
|
}
|
|
364
299
|
// ── File-operation reminder folding ─────────────────────────────────
|
|
@@ -369,7 +304,7 @@ If you're adding long explanatory comments, consider routing that knowledge to:
|
|
|
369
304
|
if (hasFileOpReminders && fileOpReminder) {
|
|
370
305
|
const foBullets = [];
|
|
371
306
|
if (fileOpReminder.lifecycleReminder) {
|
|
372
|
-
// Skip lifecycleReminder if source-linked
|
|
307
|
+
// Skip lifecycleReminder if source-linked context already shows the same IDs
|
|
373
308
|
const hasSourceLinked = selectedBlock?.includes("- Existing Kibi links:") === true;
|
|
374
309
|
const lifecycleHasEntities = fileOpReminder.lifecycleReminder.includes("Kibi entities:");
|
|
375
310
|
const overlapsSourceLinked = hasSourceLinked &&
|
|
@@ -459,7 +394,10 @@ The Kibi workspace is in a maintenance-degraded state. Guidance remains advisory
|
|
|
459
394
|
// Prepend KB freshness block when evidence is required
|
|
460
395
|
if (context.kbFreshness?.requiresEvidence) {
|
|
461
396
|
const changedListing = context.freshnessChangedPaths?.length
|
|
462
|
-
? `Changed: ${context.freshnessChangedPaths
|
|
397
|
+
? `Changed: ${context.freshnessChangedPaths
|
|
398
|
+
.slice(0, 5)
|
|
399
|
+
.map((p) => `\`${p}\``)
|
|
400
|
+
.join(", ")}`
|
|
463
401
|
: null;
|
|
464
402
|
const freshnessLines = [
|
|
465
403
|
"🧠 **Kibi freshness required**",
|
|
@@ -471,15 +409,15 @@ The Kibi workspace is in a maintenance-degraded state. Guidance remains advisory
|
|
|
471
409
|
"Resolution:",
|
|
472
410
|
"- **KB updated**: run `kb_search` for discovery, then `kb_upsert`/`kb_delete` for mutations, then `kb_check`.",
|
|
473
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`.",
|
|
474
|
-
]
|
|
412
|
+
]
|
|
413
|
+
.filter(Boolean)
|
|
414
|
+
.join("\n");
|
|
475
415
|
finalBlock = finalBlock
|
|
476
416
|
? `${freshnessLines}\n\n${finalBlock}`
|
|
477
417
|
: freshnessLines;
|
|
478
418
|
}
|
|
479
419
|
// Return: sentinel + one targeted block (or just sentinel if no block)
|
|
480
|
-
return finalBlock
|
|
481
|
-
? `${SENTINEL}\n\n${finalBlock}`
|
|
482
|
-
: SENTINEL;
|
|
420
|
+
return finalBlock ? `${SENTINEL}\n\n${finalBlock}` : SENTINEL;
|
|
483
421
|
}
|
|
484
422
|
// ── Comment suggestion guidance (legacy compat) ────────────────────────
|
|
485
423
|
function buildCommentSuggestionGuidance(suggestion) {
|
|
@@ -572,7 +510,6 @@ export function buildPrompt(context, capability = getInitKibiCommandCapability()
|
|
|
572
510
|
/**
|
|
573
511
|
* Inject prompt guidance if not already present.
|
|
574
512
|
*/
|
|
575
|
-
// implements REQ-opencode-kibi-briefing-v2
|
|
576
513
|
export function injectPrompt(current, config, context, capability = getInitKibiCommandCapability()) {
|
|
577
514
|
if (!config.prompt.enabled || !isPluginEnabled(config)) {
|
|
578
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
|
}
|