kibi-opencode 0.16.0 → 0.18.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 +4 -2
- package/dist/enforcement-policy.js +8 -0
- package/dist/scheduler.d.ts +3 -1
- package/dist/scheduler.js +76 -2
- package/dist/version-metadata.json +1 -1
- package/package.json +3 -5
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ The plugin now uses a posture-aware, low-token smart-enforcement model before em
|
|
|
56
56
|
- **Start-task risky cue**: authoritative risky edits also add a compact Kibi context cue so agents can start with explicit source-linked guidance before acting, while staying inside the same single prompt block and token budget.
|
|
57
57
|
- **Effective mode gating**: `advisory` (default) never blocks; `strict` escalates checks and reminders for `root_active` and `hybrid_root_plus_vendored` postures when `requireRootKbForStrict` is enabled; `hard` fails closed for authoritative roots and linked git worktrees, injecting a stop-state with MCP recovery steps until the Kibi checkpoint passes. `maintenanceDegraded` overrides everything back to `advisory`.
|
|
58
58
|
- **Low-token prompt policy**: docs-only and test-only edits avoid unnecessary discovery prompts; vendored-only repos suppress operational bootstrap nudges; at most one contextual block is injected per prompt (≤120 words, ≤5 bullets)
|
|
59
|
-
- **Completion reminder**: when `completionReminder` is enabled, risky code edits append a single prompt-visible `kb_check` reminder exactly once per cached context
|
|
59
|
+
- **Completion reminder**: when `completionReminder` is enabled, risky code edits append a single prompt-visible impact-enabled `kb_check` reminder exactly once per cached context
|
|
60
60
|
- **Runtime maintenance overlay**: static `maintenanceDegraded` from posture is merged with a latched runtime overlay (sync disabled/unavailable/failing) so degraded state is consistently reflected in prompts, logs, and mode decisions
|
|
61
61
|
- **Three-mode enforcement**: `advisory` by default keeps guidance non-blocking; opt-in `strict` adds elevated validation cues; `hard` blocks the prompt for authoritative workspaces until the checkpoint cycle succeeds. Git hooks and KB validation checks remain the durable enforcement boundary for all modes.
|
|
62
62
|
- **Structured observability**: posture, risk, cache, degraded-mode, targeted-check, and guidance events flow through structured plugin logs
|
|
@@ -75,11 +75,13 @@ modified, the plugin evaluates KB freshness evidence and surfaces a visible
|
|
|
75
75
|
discovery via `kb_search` or `kb_query(sourceFile=...)` and `kb_check`.
|
|
76
76
|
- **Deferred/Failed**: Do not claim task completion.
|
|
77
77
|
|
|
78
|
+
For meaningful source edits, the first gate is MCP `kb_check` with impact options, for example `kb_check({sourceFiles:["src/feature.ts"], includeImpactDiagnostics:true, includeWorkingTreeDiff:true})`. These diagnostics guide symbol granularity, requirement ownership, and semantic review of linked requirements/tests; they do not prove source behavior matches prose.
|
|
79
|
+
|
|
78
80
|
### Dynamic Contextual Guidance
|
|
79
81
|
|
|
80
82
|
The plugin provides context-aware prompt guidance based on recent edits and workspace state:
|
|
81
83
|
|
|
82
|
-
- **Code edits**: Guidance for querying Kibi by sourceFile, preferring Kibi over comments, and adding
|
|
84
|
+
- **Code edits**: Guidance for running impact-enabled `kb_check`, querying Kibi by sourceFile, preferring Kibi over comments, and adding or updating symbol traceability
|
|
83
85
|
- **Requirement edits**: Guidance for maintaining separate REQ/SCEN/TEST artifacts and avoiding embedded scenarios
|
|
84
86
|
- **KB doc edits**: Guidance for proper entity relationships and validation
|
|
85
87
|
- **Bootstrap needed**: Detection and nudges for uninitialized repos
|
|
@@ -25,6 +25,7 @@ const ADVISORY_EDIT_KINDS = new Set([
|
|
|
25
25
|
"symbol",
|
|
26
26
|
"kb",
|
|
27
27
|
]);
|
|
28
|
+
const SOURCE_IMPACT_KINDS = new Set(["code"]);
|
|
28
29
|
function normalizeCheckpointEvidence(evidence) {
|
|
29
30
|
if (typeof evidence === "boolean") {
|
|
30
31
|
return evidence;
|
|
@@ -126,6 +127,13 @@ function advisoryText(event) {
|
|
|
126
127
|
return "- New file detected. Add or update the necessary Kibi entities and traceability before completing this task.";
|
|
127
128
|
}
|
|
128
129
|
if (event.lifecycle === "edited") {
|
|
130
|
+
if (SOURCE_IMPACT_KINDS.has(event.pathKind)) {
|
|
131
|
+
const impactCheckCall = `kb_check({sourceFiles:${JSON.stringify([event.normalizedPath])}, includeImpactDiagnostics:true, includeWorkingTreeDiff:true})`;
|
|
132
|
+
return [
|
|
133
|
+
`- Edited source file detected. Run \`${impactCheckCall}\` while the edit context is fresh.`,
|
|
134
|
+
" Use the impact diagnostics to review symbol granularity, requirement ownership, and semantic review of linked requirements/tests before completing this task.",
|
|
135
|
+
].join("\n");
|
|
136
|
+
}
|
|
129
137
|
return `- Edited file detected. Review Kibi traceability for ${event.normalizedPath} before completing this task.`;
|
|
130
138
|
}
|
|
131
139
|
const ids = event.linkedEntityResult.ids;
|
package/dist/scheduler.d.ts
CHANGED
|
@@ -24,7 +24,9 @@ export type SyncRunnerResult = {
|
|
|
24
24
|
};
|
|
25
25
|
export type SyncRunner = (worktree: string) => Promise<SyncRunnerResult>;
|
|
26
26
|
export type CheckRunner = (worktree: string, rules: string[]) => Promise<{
|
|
27
|
-
exitCode: number;
|
|
27
|
+
readonly exitCode: number;
|
|
28
|
+
readonly stdout?: string;
|
|
29
|
+
readonly stderr?: string;
|
|
28
30
|
}>;
|
|
29
31
|
export interface SchedulerOptions {
|
|
30
32
|
worktree: string;
|
package/dist/scheduler.js
CHANGED
|
@@ -154,6 +154,7 @@ class WorktreeSyncScheduler {
|
|
|
154
154
|
}
|
|
155
155
|
else {
|
|
156
156
|
logger.info(`check.succeeded ${JSON.stringify({ rules: checkRules })}`);
|
|
157
|
+
logAdvisoryQualityDiagnostics(checkRules, checkResult.stdout, checkResult.stderr);
|
|
157
158
|
}
|
|
158
159
|
}
|
|
159
160
|
}
|
|
@@ -280,11 +281,84 @@ async function runKibiSync(worktree) {
|
|
|
280
281
|
async function runKibiCheck(worktree, rules) {
|
|
281
282
|
return new Promise((resolve) => {
|
|
282
283
|
const rulesArg = rules.join(",");
|
|
283
|
-
exec(`kibi check --rules ${rulesArg}`, { cwd: worktree }, (error) => {
|
|
284
|
-
|
|
284
|
+
exec(`kibi check --rules ${rulesArg} --format json`, { cwd: worktree }, (error, stdout, stderr) => {
|
|
285
|
+
const truncatedOut = truncateSyncOutput(stdout || undefined);
|
|
286
|
+
const truncatedErr = truncateSyncOutput(stderr || undefined);
|
|
287
|
+
resolve({
|
|
288
|
+
exitCode: error ? (error.code ?? 1) : 0,
|
|
289
|
+
...(truncatedOut !== undefined ? { stdout: truncatedOut } : {}),
|
|
290
|
+
...(truncatedErr !== undefined ? { stderr: truncatedErr } : {}),
|
|
291
|
+
});
|
|
285
292
|
});
|
|
286
293
|
});
|
|
287
294
|
}
|
|
295
|
+
function logAdvisoryQualityDiagnostics(rules, stdout, stderr) {
|
|
296
|
+
const diagnostics = parseQualityDiagnostics(stdout, stderr).filter((diagnostic) => !diagnostic.blocking);
|
|
297
|
+
if (diagnostics.length === 0)
|
|
298
|
+
return;
|
|
299
|
+
const severityCounts = new Map();
|
|
300
|
+
for (const diagnostic of diagnostics) {
|
|
301
|
+
severityCounts.set(diagnostic.severity, (severityCounts.get(diagnostic.severity) ?? 0) + 1);
|
|
302
|
+
}
|
|
303
|
+
const first = diagnostics[0];
|
|
304
|
+
if (first === undefined)
|
|
305
|
+
return;
|
|
306
|
+
logger.warn(`check.advisory_quality ${JSON.stringify({
|
|
307
|
+
rules,
|
|
308
|
+
count: diagnostics.length,
|
|
309
|
+
...Object.fromEntries(severityCounts),
|
|
310
|
+
firstId: first.id,
|
|
311
|
+
firstMessage: first.message,
|
|
312
|
+
})}`);
|
|
313
|
+
}
|
|
314
|
+
function parseQualityDiagnostics(stdout, stderr) {
|
|
315
|
+
for (const output of [stdout, stderr]) {
|
|
316
|
+
if (output === undefined || output.trim().length === 0)
|
|
317
|
+
continue;
|
|
318
|
+
try {
|
|
319
|
+
const parsed = JSON.parse(output);
|
|
320
|
+
const candidates = qualityDiagnosticCandidates(parsed);
|
|
321
|
+
const diagnostics = candidates.flatMap((candidate) => parseQualityDiagnostic(candidate) ?? []);
|
|
322
|
+
if (diagnostics.length > 0)
|
|
323
|
+
return diagnostics;
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
if (error instanceof SyntaxError)
|
|
327
|
+
continue;
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
function qualityDiagnosticCandidates(value) {
|
|
334
|
+
if (!isRecord(value))
|
|
335
|
+
return [];
|
|
336
|
+
const structuredContent = value.structuredContent;
|
|
337
|
+
if (isRecord(structuredContent)) {
|
|
338
|
+
const diagnostics = structuredContent.qualityDiagnostics;
|
|
339
|
+
return Array.isArray(diagnostics) ? diagnostics : [];
|
|
340
|
+
}
|
|
341
|
+
const diagnostics = value.qualityDiagnostics;
|
|
342
|
+
return Array.isArray(diagnostics) ? diagnostics : [];
|
|
343
|
+
}
|
|
344
|
+
function parseQualityDiagnostic(value) {
|
|
345
|
+
if (!isRecord(value))
|
|
346
|
+
return null;
|
|
347
|
+
const id = value.id;
|
|
348
|
+
const severity = value.severity;
|
|
349
|
+
const blocking = value.blocking;
|
|
350
|
+
const message = value.message;
|
|
351
|
+
if (typeof id !== "string" ||
|
|
352
|
+
typeof severity !== "string" ||
|
|
353
|
+
typeof blocking !== "boolean" ||
|
|
354
|
+
typeof message !== "string") {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
return { id, severity, blocking, message };
|
|
358
|
+
}
|
|
359
|
+
function isRecord(value) {
|
|
360
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
361
|
+
}
|
|
288
362
|
// implements REQ-opencode-kibi-plugin-v1
|
|
289
363
|
export function createSyncScheduler(opts) {
|
|
290
364
|
return new WorktreeSyncScheduler(opts);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"opencode":"0.
|
|
1
|
+
{"opencode":"0.18.0","mcp":"0.19.0","cli":"0.14.0","core":"0.7.0"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Kibi OpenCode plugin - thin adapter to integrate Kibi with OpenCode sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,9 +37,7 @@
|
|
|
37
37
|
"default": "./dist/tui.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
-
"files": [
|
|
41
|
-
"dist"
|
|
42
|
-
],
|
|
40
|
+
"files": ["dist"],
|
|
43
41
|
"engines": {
|
|
44
42
|
"node": ">=18"
|
|
45
43
|
},
|
|
@@ -62,7 +60,7 @@
|
|
|
62
60
|
"@opencode-ai/plugin": "^1.4.7",
|
|
63
61
|
"@opentui/core": "^0.1.99",
|
|
64
62
|
"@opentui/solid": "^0.1.99",
|
|
65
|
-
"kibi-cli": "^0.
|
|
63
|
+
"kibi-cli": "^0.14.0"
|
|
66
64
|
},
|
|
67
65
|
"devDependencies": {
|
|
68
66
|
"@types/node": "latest",
|