kibi-opencode 0.19.4 → 1.0.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 +8 -6
- package/dist/e2e-coverage-signals.js +3 -3
- package/dist/enforcement-policy.js +7 -17
- package/dist/file-entity-links.js +1 -3
- package/dist/file-filter.d.ts +9 -9
- package/dist/file-filter.js +16 -68
- package/dist/kibi-bootstrap-alias.d.ts +2 -0
- package/dist/kibi-bootstrap-alias.js +14 -0
- package/dist/kibi-bootstrap-capability.d.ts +32 -0
- package/dist/{init-kibi-capability.js → kibi-bootstrap-capability.js} +12 -11
- package/dist/meaningful-change-classifier.js +27 -8
- package/dist/path-kind.js +21 -39
- package/dist/plugin-startup.js +3 -3
- package/dist/plugin.d.ts +1 -1
- package/dist/plugin.js +4 -4
- package/dist/prompt.d.ts +4 -4
- package/dist/prompt.js +24 -60
- package/dist/repo-posture.js +31 -84
- package/dist/risk-classifier.d.ts +3 -3
- package/dist/risk-classifier.js +9 -6
- package/dist/scheduler.js +2 -2
- package/dist/version-metadata.json +1 -1
- package/dist/work-context-resolver.js +2 -2
- package/dist/workspace-health.js +8 -56
- package/package.json +6 -4
- package/dist/init-kibi-alias.d.ts +0 -14
- package/dist/init-kibi-alias.js +0 -43
- package/dist/init-kibi-capability.d.ts +0 -32
package/dist/prompt.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { isPluginEnabled } from "./config.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getKibiBootstrapCommandCapability, } from "./kibi-bootstrap-capability.js";
|
|
4
4
|
import { getSourceLinkedRequirementIds } from "./source-linked-guidance.js";
|
|
5
5
|
const SENTINEL = "<!-- kibi-opencode -->";
|
|
6
6
|
// ── Token budget enforcement ───────────────────────────────────────────
|
|
@@ -56,17 +56,17 @@ function deriveFileBucket(pathKind) {
|
|
|
56
56
|
function getFocusEdit(context) {
|
|
57
57
|
return (context.focusEdit ?? context.recentEdits[context.recentEdits.length - 1]);
|
|
58
58
|
}
|
|
59
|
-
function
|
|
59
|
+
function buildKibiBootstrapReference(capability = getKibiBootstrapCommandCapability()) {
|
|
60
60
|
if (capability.supported) {
|
|
61
|
-
return "Bootstrap existing
|
|
61
|
+
return "Bootstrap existing repositories with `/kibi-bootstrap`; the canonical `kibi-bootstrap` skill owns planner, approval, apply, and repair semantics.";
|
|
62
62
|
}
|
|
63
|
-
return "
|
|
63
|
+
return "This host cannot inject native `/kibi-bootstrap`; use `/kibi:kibi-bootstrap:mcp` to route to the canonical `kibi-bootstrap` skill.";
|
|
64
64
|
}
|
|
65
|
-
function buildBootstrapRequiredBody(capability =
|
|
66
|
-
const
|
|
67
|
-
? "
|
|
68
|
-
: "-
|
|
69
|
-
return `Kibi
|
|
65
|
+
function buildBootstrapRequiredBody(capability = getKibiBootstrapCommandCapability()) {
|
|
66
|
+
const command = capability.supported
|
|
67
|
+
? "Use `/kibi-bootstrap`."
|
|
68
|
+
: "Use `/kibi:kibi-bootstrap:mcp`; this host does not support native command injection.";
|
|
69
|
+
return `Kibi infrastructure is not initialized. Run \`kibi init\`, then inspect \`kb_status.bootstrap.nextAction\` and route the explicit bootstrap request to the canonical \`kibi-bootstrap\` skill. ${command}`;
|
|
70
70
|
}
|
|
71
71
|
function buildHardGateBlock(block) {
|
|
72
72
|
const pathLines = block.shownPaths.map((path) => `- \`${path}\``);
|
|
@@ -80,18 +80,7 @@ function buildHardGateBlock(block) {
|
|
|
80
80
|
reasonLine,
|
|
81
81
|
"Affected files:",
|
|
82
82
|
...pathLines,
|
|
83
|
-
"
|
|
84
|
-
"- MCP tools and the trusted project-local CLI are peer surfaces over the same 18 operations; choose by what is visible and approved here.",
|
|
85
|
-
"- If Kibi MCP tools are visible and approved, use MCP.",
|
|
86
|
-
"- Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with `--input <file|->`.",
|
|
87
|
-
"- If neither interface is available, Kibi operation is blocked; tell the operator to enable MCP or the trusted project-local CLI.",
|
|
88
|
-
"- Do not infer MCP availability from config file existence. Do not read or edit `.kb/` files directly.",
|
|
89
|
-
"- Query before mutate. Run `kb_upsert` sequentially. Run `kb_check` before completion.",
|
|
90
|
-
"- Run `kb_search` for impacted requirements, tests, ADRs, and facts.",
|
|
91
|
-
"- Run `kb_query` with `sourceFile` for each affected file.",
|
|
92
|
-
"- Run `kb_status` if branch or snapshot freshness matters.",
|
|
93
|
-
"- Run `kb_upsert` for required traceability, relationship, or fact updates.",
|
|
94
|
-
"- Run `kb_check` before continuing.",
|
|
83
|
+
"Inspect typed Kibi status `nextActions`, then route to the canonical kibi-usage, kibi-freshness, or kibi-traceability skill. Use the host-visible approved peer surface and never read or edit `.kb` directly.",
|
|
95
84
|
]
|
|
96
85
|
.filter((line) => line !== null)
|
|
97
86
|
.join("\n");
|
|
@@ -123,17 +112,13 @@ Production code: use \`implements\` (symbol→req) for requirement ownership. Te
|
|
|
123
112
|
- \`covered_by\` is coverage evidence only
|
|
124
113
|
- Prefer scenario-first: req→scenario→test when scenarios exist
|
|
125
114
|
- Route durable knowledge comments to KB entities, not inline comments
|
|
126
|
-
-
|
|
115
|
+
- .kb/symbols.yaml refresh is required when extraction output changes; do not revert as scope creep`,
|
|
127
116
|
manual_kb_edit: `⚠️ **WARNING: Direct .kb/ edits bypass validation**
|
|
128
117
|
|
|
129
|
-
|
|
130
|
-
- Query before mutate and run kb_upsert sequentially
|
|
131
|
-
- Use kb_upsert to create/update entities
|
|
132
|
-
- Use kb_delete to remove entities
|
|
133
|
-
- Use kb_check to validate consistency`,
|
|
118
|
+
Do not read or edit \`.kb/\` directly. Follow typed Kibi status \`nextActions\` and route durable changes through the canonical kibi-usage, kibi-freshness, or kibi-traceability skill.`,
|
|
134
119
|
};
|
|
135
120
|
// ── Posture overrides ──────────────────────────────────────────────────
|
|
136
|
-
export function postureGuidance(posture, capability =
|
|
121
|
+
export function postureGuidance(posture, capability = getKibiBootstrapCommandCapability()) {
|
|
137
122
|
// implements REQ-opencode-prompt-injection
|
|
138
123
|
switch (posture) {
|
|
139
124
|
case "vendored_only":
|
|
@@ -146,7 +131,7 @@ ${buildBootstrapRequiredBody(capability)}`;
|
|
|
146
131
|
case "root_partial":
|
|
147
132
|
return `⚠️ **Partial KB setup detected**
|
|
148
133
|
|
|
149
|
-
Root .kb/
|
|
134
|
+
Root .kb/manifest.json exists but some canonical KB targets under .kb/ are missing. Guidance remains advisory until the user/operator restores the canonical layout.`;
|
|
150
135
|
default:
|
|
151
136
|
return null;
|
|
152
137
|
}
|
|
@@ -155,7 +140,7 @@ Root .kb/config.json exists but some configured KB targets are missing. Guidance
|
|
|
155
140
|
/**
|
|
156
141
|
* Build prompt guidance block based on posture, risk class, and cache state.
|
|
157
142
|
*/
|
|
158
|
-
function buildContextualGuidance(context, capability =
|
|
143
|
+
function buildContextualGuidance(context, capability = getKibiBootstrapCommandCapability()) {
|
|
159
144
|
if (context.hardGateBlock) {
|
|
160
145
|
return `${SENTINEL}\n\n${buildHardGateBlock(context.hardGateBlock)}`;
|
|
161
146
|
}
|
|
@@ -437,7 +422,7 @@ function buildCommentSuggestionGuidance(suggestion) {
|
|
|
437
422
|
Your recent code edit contains a comment that looks like a **strict domain fact** (invariants, properties, limits, defaults, or cardinality constraints).
|
|
438
423
|
|
|
439
424
|
**Action**: Route to a FACT entity in the strict fact lane:
|
|
440
|
-
- Create
|
|
425
|
+
- Create \`.kb/facts/FACT-xxx.md\` with the invariant (use \`constrains\` + \`requires_property\` for contradiction-safe reasoning)
|
|
441
426
|
- Bug/workaround notes: use \`fact_kind: observation\` or \`meta\` instead — these are non-blocking and excluded from contradiction inference
|
|
442
427
|
- Link it to relevant requirements
|
|
443
428
|
|
|
@@ -448,7 +433,7 @@ This keeps domain truths centralized, searchable, and contradiction-safe.`;
|
|
|
448
433
|
Your recent code edit contains a comment that looks like a **technical decision** (tradeoffs, rationale, or architecture choices).
|
|
449
434
|
|
|
450
435
|
**Action**: Instead of inline comments, route this to an ADR entity:
|
|
451
|
-
- Create
|
|
436
|
+
- Create \`.kb/adr/ADR-xxx.md\` documenting the decision
|
|
452
437
|
- Include context, options considered, and the chosen approach
|
|
453
438
|
- Link to constrained code symbols
|
|
454
439
|
|
|
@@ -459,7 +444,7 @@ This preserves decision context for future maintainers.`;
|
|
|
459
444
|
Your recent code edit contains a comment that looks like **behavior intent** (system capabilities or user-facing requirements).
|
|
460
445
|
|
|
461
446
|
**Action**: Instead of inline comments, route this to a REQ entity:
|
|
462
|
-
- Create
|
|
447
|
+
- Create \`.kb/requirements/REQ-xxx.md\` with the behavior description
|
|
463
448
|
- Add SCEN and TEST entities for specification and verification
|
|
464
449
|
- Link code: production uses \`implements\` (symbol→req) for ownership; test code uses \`executable_for\`; \`covered_by\` is coverage evidence only
|
|
465
450
|
|
|
@@ -478,41 +463,20 @@ Before implementing or explaining code:
|
|
|
478
463
|
/**
|
|
479
464
|
* Build the static guidance block (original behavior).
|
|
480
465
|
*/
|
|
481
|
-
function buildBaseGuidance(capability =
|
|
466
|
+
function buildBaseGuidance(capability = getKibiBootstrapCommandCapability()) {
|
|
482
467
|
return `${SENTINEL}
|
|
483
|
-
|
|
468
|
+
Use typed Kibi status and its \`nextActions\` as the workflow contract. Route general work to \`kibi-usage\`, freshness decisions to \`kibi-freshness\`, source proof to \`kibi-traceability\`, and an explicit bootstrap request to \`kibi-bootstrap\`. Use the host-visible approved Kibi peer surface; if it is unavailable, report the blocked capability. Never read or edit \`.kb/\` directly.
|
|
484
469
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
Keep changed symbols traceable: production code uses \`implements\` (symbol→req) for ownership; test code uses \`executable_for\`; \`covered_by\` is coverage evidence only. Inline \`// implements REQ-xxx\` comments remain backward-compatible.
|
|
488
|
-
|
|
489
|
-
Kibi capability selection (MCP tools and the trusted project-local CLI are peer surfaces over the same 18 operations):
|
|
490
|
-
1. If Kibi MCP tools are visible and approved, use MCP.
|
|
491
|
-
2. Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with \`--input <file|->\`.
|
|
492
|
-
3. If neither interface is available, Kibi operation is blocked; tell the operator to enable MCP or the trusted project-local CLI.
|
|
493
|
-
Do not infer MCP availability from config file existence. Do not read or edit \`.kb/\` files directly. Query before mutate. Run \`kb_upsert\` sequentially. Run \`kb_check\` before completion.
|
|
470
|
+
Keep changed symbols traceable: production code uses \`implements\` (symbol→req) for ownership; test code uses \`executable_for\`; \`covered_by\` is coverage evidence only.
|
|
494
471
|
|
|
495
472
|
Dogfood note for this repo: OpenCode here uses local built \`kibi-mcp\` and \`kibi-opencode\` artifacts. If you change package versions or local package wiring, run \`bun run build\` before relying on OpenCode in this workspace.
|
|
496
473
|
|
|
497
|
-
|
|
498
|
-
1. **Discover**: Run kb_search for REQ, ADR, TEST, FACT. Decompose broad queries (e.g., "Apple Sign-In").
|
|
499
|
-
2. **Confirm**: Run kb_query with sourceFile, id, type, or tags once you know the exact follow-up target.
|
|
500
|
-
3. **Inspect freshness**: Run kb_status when branch or stale-state confidence matters.
|
|
501
|
-
4. **Document intent**: If you are about to explain code, STOP. Route that explanation to kb_upsert instead of inline comments.
|
|
502
|
-
5. **Link during work**: When creating KB entities, include relationship rows: specified_by (req→scenario), implements (symbol→req for ownership), covered_by (symbol→test for coverage), executable_for (test code→test).
|
|
503
|
-
6. **Validate**: Run kb_check after KB mutations to catch violations early.
|
|
504
|
-
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.
|
|
505
|
-
|
|
506
|
-
MCP and CLI expose the same 18 Kibi operations. Use the exact MCP tool or dedicated CLI route; do not invent a generic runner.
|
|
507
|
-
|
|
508
|
-
For comprehensive Kibi usage guidance (relationships, fact lanes, workflows), use \`kb_skills_load\` with skill id \`kibi-usage\`.
|
|
509
|
-
|
|
510
|
-
${buildInitKibiBootstrapReference(capability)}`;
|
|
474
|
+
${buildKibiBootstrapReference(capability)}`;
|
|
511
475
|
}
|
|
512
476
|
/**
|
|
513
477
|
* Build prompt with contextual guidance based on posture, risk class, and cache state.
|
|
514
478
|
*/
|
|
515
|
-
export function buildPrompt(context, capability =
|
|
479
|
+
export function buildPrompt(context, capability = getKibiBootstrapCommandCapability()) {
|
|
516
480
|
if (!context) {
|
|
517
481
|
return buildBaseGuidance(capability).trim();
|
|
518
482
|
}
|
|
@@ -521,7 +485,7 @@ export function buildPrompt(context, capability = getInitKibiCommandCapability()
|
|
|
521
485
|
/**
|
|
522
486
|
* Inject prompt guidance if not already present.
|
|
523
487
|
*/
|
|
524
|
-
export function injectPrompt(current, config, context, capability =
|
|
488
|
+
export function injectPrompt(current, config, context, capability = getKibiBootstrapCommandCapability()) {
|
|
525
489
|
if (!config.prompt.enabled || !isPluginEnabled(config)) {
|
|
526
490
|
return current;
|
|
527
491
|
}
|
package/dist/repo-posture.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// implements REQ-opencode-smart-enforcement-v1, REQ-opencode-kibi-plugin-v1
|
|
2
2
|
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
-
//
|
|
5
|
-
const
|
|
6
|
-
requirements: "
|
|
7
|
-
scenarios: "
|
|
8
|
-
tests: "
|
|
9
|
-
adr: "
|
|
10
|
-
flags: "
|
|
11
|
-
events: "
|
|
12
|
-
facts: "
|
|
13
|
-
symbols: "
|
|
4
|
+
// Canonical Kibi knowledge targets — must stay in sync with kb-paths.ts.
|
|
5
|
+
const CANONICAL_KB_TARGETS = {
|
|
6
|
+
requirements: ".kb/requirements",
|
|
7
|
+
scenarios: ".kb/scenarios",
|
|
8
|
+
tests: ".kb/tests",
|
|
9
|
+
adr: ".kb/adr",
|
|
10
|
+
flags: ".kb/flags",
|
|
11
|
+
events: ".kb/events",
|
|
12
|
+
facts: ".kb/facts",
|
|
13
|
+
symbols: ".kb/symbols.yaml",
|
|
14
14
|
};
|
|
15
15
|
// ── helpers ────────────────────────────────────────────────────────────
|
|
16
16
|
function findVendoredTrees(cwd) {
|
|
@@ -19,7 +19,7 @@ function findVendoredTrees(cwd) {
|
|
|
19
19
|
["kibi", "opencode.json"],
|
|
20
20
|
["kibi", "package.json"],
|
|
21
21
|
["kibi", "packages", "mcp"],
|
|
22
|
-
["kibi", "
|
|
22
|
+
["kibi", ".kb"],
|
|
23
23
|
];
|
|
24
24
|
for (const marker of vendoredMarkers) {
|
|
25
25
|
const markerPath = join(cwd, ...marker);
|
|
@@ -37,63 +37,22 @@ function findVendoredTrees(cwd) {
|
|
|
37
37
|
}
|
|
38
38
|
return [...new Set(results)];
|
|
39
39
|
}
|
|
40
|
-
/** Check if
|
|
41
|
-
function
|
|
42
|
-
return existsSync(join(cwd, ".kb", "
|
|
40
|
+
/** Check if `.kb/manifest.json` exists at root. */
|
|
41
|
+
function rootKbManifestExists(cwd) {
|
|
42
|
+
return existsSync(join(cwd, ".kb", "manifest.json"));
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
function readRootConfig(cwd) {
|
|
46
|
-
try {
|
|
47
|
-
return (JSON.parse(readFileSync(join(cwd, ".kb", "config.json"), "utf8")) || null);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
/** Check if all configured KB targets resolve (directories / files exist). */
|
|
44
|
+
/** Check if all canonical KB targets resolve (directories / files exist). */
|
|
54
45
|
function rootTargetsAllResolve(cwd) {
|
|
55
|
-
const
|
|
56
|
-
let config = {};
|
|
57
|
-
try {
|
|
58
|
-
config = JSON.parse(readFileSync(configPath, "utf8")) || {};
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
const paths = config.paths;
|
|
64
|
-
const defaultKeys = [
|
|
65
|
-
"requirements",
|
|
66
|
-
"scenarios",
|
|
67
|
-
"tests",
|
|
68
|
-
"adr",
|
|
69
|
-
"flags",
|
|
70
|
-
"events",
|
|
71
|
-
"facts",
|
|
72
|
-
"symbols",
|
|
73
|
-
];
|
|
74
|
-
for (const key of defaultKeys) {
|
|
75
|
-
const raw = paths?.[key] ?? DEFAULT_SYNC_PATHS[key];
|
|
46
|
+
for (const raw of Object.values(CANONICAL_KB_TARGETS)) {
|
|
76
47
|
if (!raw)
|
|
77
48
|
return false;
|
|
78
|
-
|
|
79
|
-
const normalized = raw.replace(/\/+$/, "");
|
|
80
|
-
const isFile = normalized.endsWith(".yaml") || normalized.endsWith(".yml");
|
|
49
|
+
const isFile = raw.endsWith(".yaml") || raw.endsWith(".yml");
|
|
81
50
|
if (isFile) {
|
|
82
|
-
if (!existsSync(resolve(cwd,
|
|
51
|
+
if (!existsSync(resolve(cwd, raw)))
|
|
83
52
|
return false;
|
|
84
53
|
}
|
|
85
|
-
else {
|
|
86
|
-
|
|
87
|
-
const segments = normalized.split("/");
|
|
88
|
-
const rootSegments = [];
|
|
89
|
-
for (const seg of segments) {
|
|
90
|
-
if (seg.includes("*") || seg.includes("?") || seg.includes("["))
|
|
91
|
-
break;
|
|
92
|
-
rootSegments.push(seg);
|
|
93
|
-
}
|
|
94
|
-
const dirPath = rootSegments.join("/") || ".";
|
|
95
|
-
if (!existsSync(resolve(cwd, dirPath)))
|
|
96
|
-
return false;
|
|
54
|
+
else if (!existsSync(resolve(cwd, raw))) {
|
|
55
|
+
return false;
|
|
97
56
|
}
|
|
98
57
|
}
|
|
99
58
|
return true;
|
|
@@ -134,54 +93,42 @@ function hasRootKibiIntent(cwd) {
|
|
|
134
93
|
}
|
|
135
94
|
// ── main classifier ────────────────────────────────────────────────────
|
|
136
95
|
export function detectPosture(cwd) {
|
|
137
|
-
const
|
|
96
|
+
const hasRootManifest = rootKbManifestExists(cwd);
|
|
138
97
|
const vendoredTrees = findVendoredTrees(cwd);
|
|
139
|
-
// 1. hybrid_root_plus_vendored — root
|
|
140
|
-
if (
|
|
98
|
+
// 1. hybrid_root_plus_vendored — root manifest AND vendored subtrees
|
|
99
|
+
if (hasRootManifest && vendoredTrees.length > 0) {
|
|
141
100
|
return {
|
|
142
101
|
state: "hybrid_root_plus_vendored",
|
|
143
102
|
needsBootstrap: false,
|
|
144
|
-
reason: `Root .kb/
|
|
103
|
+
reason: `Root .kb/manifest.json exists alongside vendored tree(s): ${vendoredTrees.join(", ")}`,
|
|
145
104
|
maintenanceDegraded: false,
|
|
146
105
|
};
|
|
147
106
|
}
|
|
148
|
-
// 2-3. root
|
|
149
|
-
if (
|
|
150
|
-
const config = readRootConfig(cwd);
|
|
151
|
-
const maint = config?.maintenance;
|
|
152
|
-
const maintenanceDisabled = maint !== undefined && maint.enabled === false;
|
|
153
|
-
// maintenance_degraded overlay: maintenance explicitly disabled → root_active
|
|
154
|
-
if (maintenanceDisabled) {
|
|
155
|
-
return {
|
|
156
|
-
state: "root_active",
|
|
157
|
-
needsBootstrap: false,
|
|
158
|
-
reason: "Root .kb/config.json exists; maintenance mode explicitly disabled",
|
|
159
|
-
maintenanceDegraded: true,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
107
|
+
// 2-3. root manifest exists (check target resolution)
|
|
108
|
+
if (hasRootManifest) {
|
|
162
109
|
const allResolve = rootTargetsAllResolve(cwd);
|
|
163
110
|
if (allResolve) {
|
|
164
111
|
return {
|
|
165
112
|
state: "root_active",
|
|
166
113
|
needsBootstrap: false,
|
|
167
|
-
reason: "Root .kb/
|
|
114
|
+
reason: "Root .kb/manifest.json exists and all canonical KB targets resolve",
|
|
168
115
|
maintenanceDegraded: false,
|
|
169
116
|
};
|
|
170
117
|
}
|
|
171
|
-
// 3. root_partial —
|
|
118
|
+
// 3. root_partial — manifest exists but targets are missing
|
|
172
119
|
return {
|
|
173
120
|
state: "root_partial",
|
|
174
121
|
needsBootstrap: true,
|
|
175
|
-
reason: "Root .kb/
|
|
122
|
+
reason: "Root .kb/manifest.json exists but some canonical KB targets are missing",
|
|
176
123
|
maintenanceDegraded: false,
|
|
177
124
|
};
|
|
178
125
|
}
|
|
179
|
-
// 4. vendored_only — no root
|
|
126
|
+
// 4. vendored_only — no root manifest, but vendored markers found
|
|
180
127
|
if (vendoredTrees.length > 0) {
|
|
181
128
|
return {
|
|
182
129
|
state: "vendored_only",
|
|
183
130
|
needsBootstrap: false,
|
|
184
|
-
reason: `No root .kb/
|
|
131
|
+
reason: `No root .kb/manifest.json, but vendored Kibi tree(s) detected: ${vendoredTrees.join(", ")}`,
|
|
185
132
|
maintenanceDegraded: false,
|
|
186
133
|
};
|
|
187
134
|
}
|
|
@@ -27,10 +27,10 @@ export interface RiskClassification {
|
|
|
27
27
|
* Classify a file edit into a deterministic risk class.
|
|
28
28
|
*
|
|
29
29
|
* Classification order (first match wins):
|
|
30
|
-
* 1. manual_kb_edit —
|
|
30
|
+
* 1. manual_kb_edit — opaque/derived `.kb/` files (pathKind "kb")
|
|
31
31
|
* 2. safe_test_only — pathKind is "test"
|
|
32
|
-
* 3. req_policy_candidate — pathKind is "requirement"
|
|
33
|
-
* 4. kb_doc_structural — pathKind is "scenario", "adr", or "
|
|
32
|
+
* 3. req_policy_candidate — pathKind is "requirement" (including `.kb/requirements`)
|
|
33
|
+
* 4. kb_doc_structural — pathKind is "scenario", "adr", "fact", "flag", "event", or "symbol"
|
|
34
34
|
* 5. safe_docs_only — pathKind is "unknown" (markdown/config/etc.)
|
|
35
35
|
* 6. traceability_candidate — code with exports AND (hasDurableComment OR missing traceability)
|
|
36
36
|
* 7. behavior_candidate — code with exports, already has traceability, no durable comment
|
package/dist/risk-classifier.js
CHANGED
|
@@ -11,10 +11,10 @@ const TRACEABILITY_PATTERN = /implements\s+REQ-[A-Za-z0-9_-]+/i;
|
|
|
11
11
|
* Classify a file edit into a deterministic risk class.
|
|
12
12
|
*
|
|
13
13
|
* Classification order (first match wins):
|
|
14
|
-
* 1. manual_kb_edit —
|
|
14
|
+
* 1. manual_kb_edit — opaque/derived `.kb/` files (pathKind "kb")
|
|
15
15
|
* 2. safe_test_only — pathKind is "test"
|
|
16
|
-
* 3. req_policy_candidate — pathKind is "requirement"
|
|
17
|
-
* 4. kb_doc_structural — pathKind is "scenario", "adr", or "
|
|
16
|
+
* 3. req_policy_candidate — pathKind is "requirement" (including `.kb/requirements`)
|
|
17
|
+
* 4. kb_doc_structural — pathKind is "scenario", "adr", "fact", "flag", "event", or "symbol"
|
|
18
18
|
* 5. safe_docs_only — pathKind is "unknown" (markdown/config/etc.)
|
|
19
19
|
* 6. traceability_candidate — code with exports AND (hasDurableComment OR missing traceability)
|
|
20
20
|
* 7. behavior_candidate — code with exports, already has traceability, no durable comment
|
|
@@ -22,11 +22,14 @@ const TRACEABILITY_PATTERN = /implements\s+REQ-[A-Za-z0-9_-]+/i;
|
|
|
22
22
|
*/
|
|
23
23
|
export function classifyRisk(params) {
|
|
24
24
|
const { pathKind, isUnderKb, hasMustPriority, hasDurableComment, fileContent, } = params;
|
|
25
|
-
// 1. manual_kb_edit —
|
|
26
|
-
|
|
25
|
+
// 1. manual_kb_edit — derived/runtime `.kb/` files, not canonical lanes.
|
|
26
|
+
// Entity lanes under `.kb/` keep their typed pathKind (requirement, symbol, …).
|
|
27
|
+
if (isUnderKb && pathKind === "kb") {
|
|
27
28
|
return {
|
|
28
29
|
riskClass: "manual_kb_edit",
|
|
29
|
-
reasons: [
|
|
30
|
+
reasons: [
|
|
31
|
+
"File is opaque or derived `.kb/` runtime state — manual edits bypass KB validation",
|
|
32
|
+
],
|
|
30
33
|
};
|
|
31
34
|
}
|
|
32
35
|
// 2. safe_test_only — test files are low-risk
|
package/dist/scheduler.js
CHANGED
|
@@ -256,7 +256,7 @@ async function runKibiSync(worktree) {
|
|
|
256
256
|
? `${error.message}${signal}`
|
|
257
257
|
: signal || undefined;
|
|
258
258
|
resolve({
|
|
259
|
-
exitCode: error.code
|
|
259
|
+
exitCode: typeof error.code === "number" ? error.code : 1,
|
|
260
260
|
syncCommand: "kibi sync",
|
|
261
261
|
...(truncatedOut !== undefined ? { syncStdout: truncatedOut } : {}),
|
|
262
262
|
...(truncatedErr !== undefined ? { syncStderr: truncatedErr } : {}),
|
|
@@ -285,7 +285,7 @@ async function runKibiCheck(worktree, rules) {
|
|
|
285
285
|
const truncatedOut = truncateSyncOutput(stdout || undefined);
|
|
286
286
|
const truncatedErr = truncateSyncOutput(stderr || undefined);
|
|
287
287
|
resolve({
|
|
288
|
-
exitCode: error
|
|
288
|
+
exitCode: error && typeof error.code === "number" ? error.code : 0,
|
|
289
289
|
...(truncatedOut !== undefined ? { stdout: truncatedOut } : {}),
|
|
290
290
|
...(truncatedErr !== undefined ? { stderr: truncatedErr } : {}),
|
|
291
291
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"opencode":"0.
|
|
1
|
+
{"opencode":"1.0.0","mcp":"1.0.0","cli":"1.0.1","core":"0.11.0"}
|
|
@@ -82,7 +82,7 @@ function findGitMetadata(candidatePath, searchBoundary) {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
function hasRootKbConfig(root) {
|
|
85
|
-
return existsSync(join(root, ".kb", "
|
|
85
|
+
return existsSync(join(root, ".kb", "manifest.json"));
|
|
86
86
|
}
|
|
87
87
|
function uniqueResolved(paths) {
|
|
88
88
|
const seen = new Set();
|
|
@@ -157,7 +157,7 @@ function resolveBranch(git) {
|
|
|
157
157
|
return "HEAD";
|
|
158
158
|
}
|
|
159
159
|
const branch = ref.slice("refs/heads/".length);
|
|
160
|
-
return branch
|
|
160
|
+
return branch;
|
|
161
161
|
}
|
|
162
162
|
function normalizeRepoRelativePath(fromRoot, targetPath) {
|
|
163
163
|
const rawRelativePath = relative(fromRoot, targetPath);
|
package/dist/workspace-health.js
CHANGED
|
@@ -3,18 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { getKbExistenceTargets } from "./file-filter.js";
|
|
5
5
|
import { detectPosture } from "./repo-posture.js";
|
|
6
|
-
const
|
|
7
|
-
// Fallback defaults used when .kb/config.json does not exist
|
|
8
|
-
const KIBI_DOC_DIRS = [
|
|
9
|
-
"documentation/requirements",
|
|
10
|
-
"documentation/scenarios",
|
|
11
|
-
"documentation/tests",
|
|
12
|
-
"documentation/adr",
|
|
13
|
-
"documentation/flags",
|
|
14
|
-
"documentation/events",
|
|
15
|
-
"documentation/facts",
|
|
16
|
-
"documentation/symbols.yaml",
|
|
17
|
-
];
|
|
6
|
+
const KB_MANIFEST_FILE = ".kb/manifest.json";
|
|
18
7
|
// implements REQ-opencode-kibi-plugin-v1
|
|
19
8
|
/**
|
|
20
9
|
* Analyze workspace health for Kibi bootstrap and initialization.
|
|
@@ -22,56 +11,19 @@ const KIBI_DOC_DIRS = [
|
|
|
22
11
|
* bootstrap-needs to the posture result.
|
|
23
12
|
*/
|
|
24
13
|
export function checkWorkspaceHealth(cwd) {
|
|
25
|
-
// Use posture detection for root-scoped classification
|
|
26
14
|
const posture = detectPosture(cwd);
|
|
27
|
-
const
|
|
28
|
-
const missingConfig = !fs.existsSync(
|
|
15
|
+
const manifestPath = path.join(cwd, KB_MANIFEST_FILE);
|
|
16
|
+
const missingConfig = !fs.existsSync(manifestPath);
|
|
29
17
|
const missingDocDirs = [];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
missingDocDirs.push(docDir);
|
|
36
|
-
}
|
|
18
|
+
const targets = getKbExistenceTargets(cwd);
|
|
19
|
+
for (const target of targets) {
|
|
20
|
+
const fullPath = path.resolve(cwd, target.relativePath);
|
|
21
|
+
if (!fs.existsSync(fullPath)) {
|
|
22
|
+
missingDocDirs.push(target.relativePath);
|
|
37
23
|
}
|
|
38
24
|
}
|
|
39
|
-
else {
|
|
40
|
-
// Config exists: check if user specified custom paths
|
|
41
|
-
let hasUserPaths = false;
|
|
42
|
-
try {
|
|
43
|
-
const raw = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
44
|
-
hasUserPaths = Boolean(raw?.paths);
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
hasUserPaths = false;
|
|
48
|
-
}
|
|
49
|
-
if (hasUserPaths) {
|
|
50
|
-
// User has custom paths: resolve targets dynamically
|
|
51
|
-
const targets = getKbExistenceTargets(cwd);
|
|
52
|
-
for (const target of targets) {
|
|
53
|
-
const fullPath = path.resolve(cwd, target.relativePath);
|
|
54
|
-
if (!fs.existsSync(fullPath)) {
|
|
55
|
-
missingDocDirs.push(target.relativePath);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
// Config exists but no custom paths: use hardcoded defaults
|
|
61
|
-
for (const docDir of KIBI_DOC_DIRS) {
|
|
62
|
-
const fullPath = path.resolve(cwd, docDir);
|
|
63
|
-
if (!fs.existsSync(fullPath)) {
|
|
64
|
-
missingDocDirs.push(docDir);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Check for any evidence of Kibi usage
|
|
70
25
|
const kbDir = path.join(cwd, ".kb");
|
|
71
26
|
const hasKbEvidence = fs.existsSync(kbDir) && fs.readdirSync(kbDir).length > 0;
|
|
72
|
-
// Restore lenient threshold for repos that have a config but are missing a few dirs.
|
|
73
|
-
// Uninitialized repos always need bootstrap; partial repos fall back to the legacy
|
|
74
|
-
// >2 missing dirs threshold so small gaps (e.g. unused flags/events) do not nag.
|
|
75
27
|
const needsBootstrap = posture.state === "root_uninitialized" ||
|
|
76
28
|
(posture.state === "root_partial" && missingDocDirs.length > 2);
|
|
77
29
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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,11 @@
|
|
|
37
37
|
"default": "./dist/tui.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
|
-
"files": [
|
|
40
|
+
"files": [
|
|
41
|
+
"dist"
|
|
42
|
+
],
|
|
41
43
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
44
|
+
"node": ">=22"
|
|
43
45
|
},
|
|
44
46
|
"license": "AGPL-3.0-or-later",
|
|
45
47
|
"repository": {
|
|
@@ -60,7 +62,7 @@
|
|
|
60
62
|
"@opencode-ai/plugin": "^1.4.7",
|
|
61
63
|
"@opentui/core": "^0.1.99",
|
|
62
64
|
"@opentui/solid": "^0.1.99",
|
|
63
|
-
"kibi-
|
|
65
|
+
"kibi-runtime": "^1.0.0"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
68
|
"@types/node": "latest",
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the canonical native alias for the Kibi MCP bootstrap workflow.
|
|
3
|
-
* This is a thin wrapper over the MCP-defined workflow, preserving all
|
|
4
|
-
* semantic markers while removing namespacing and keeping text concise.
|
|
5
|
-
*
|
|
6
|
-
* Markers (MUST PRESERVE):
|
|
7
|
-
* - "at most 4 bounded questions"
|
|
8
|
-
* - "kb_autopilot_generate"
|
|
9
|
-
* - "preview" or "approval"
|
|
10
|
-
* - "kb_upsert"
|
|
11
|
-
* - "kb_check"
|
|
12
|
-
* - "sequential" or similar ordering language
|
|
13
|
-
*/
|
|
14
|
-
export declare function buildInitKibiAlias(): string;
|
package/dist/init-kibi-alias.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds the canonical native alias for the Kibi MCP bootstrap workflow.
|
|
3
|
-
* This is a thin wrapper over the MCP-defined workflow, preserving all
|
|
4
|
-
* semantic markers while removing namespacing and keeping text concise.
|
|
5
|
-
*
|
|
6
|
-
* Markers (MUST PRESERVE):
|
|
7
|
-
* - "at most 4 bounded questions"
|
|
8
|
-
* - "kb_autopilot_generate"
|
|
9
|
-
* - "preview" or "approval"
|
|
10
|
-
* - "kb_upsert"
|
|
11
|
-
* - "kb_check"
|
|
12
|
-
* - "sequential" or similar ordering language
|
|
13
|
-
*/
|
|
14
|
-
export function buildInitKibiAlias() {
|
|
15
|
-
const lines = [
|
|
16
|
-
"# /init-kibi: Interactive Activation",
|
|
17
|
-
"",
|
|
18
|
-
"Use this workflow to onboard a new or empty repository into Kibi through interactive discovery.",
|
|
19
|
-
"",
|
|
20
|
-
"## 1. Gather Declared Context",
|
|
21
|
-
"Ask at most 4 bounded questions to gather intent: Project Summary, Source of Truth, Priority Root, and Verification Anchors.",
|
|
22
|
-
"",
|
|
23
|
-
"## 2. Synthesize Candidates",
|
|
24
|
-
"Call `kb_autopilot_generate` with the gathered context to synthesize candidate entities. This tool is **read-only**.",
|
|
25
|
-
"In OpenCode, Kibi MCP tools are host-prefixed: canonical `kb_autopilot_generate`, `kb_upsert`, and `kb_check` may appear as `kibi_kb_autopilot_generate`, `kibi_kb_upsert`, and `kibi_kb_check`.",
|
|
26
|
-
"OpenCode prefixes Kibi MCP tools with `kibi_`; use the visible prefixed name when the host requires exact tool identifiers.",
|
|
27
|
-
"",
|
|
28
|
-
"## 3. Preview and Approval",
|
|
29
|
-
"Present the `promptBlock`, a summary of synthesized `candidates`, and the exact `applyPlan` payloads to the user. **Wait for explicit approval** before proceeding to writes.",
|
|
30
|
-
"",
|
|
31
|
-
"## 4. Apply Approved Candidates",
|
|
32
|
-
"Apply approved candidates sequentially using `kb_upsert` (following ascending phase order). Confirm success of each write before moving to the next. Run `kb_check` after the batch to verify KB integrity.",
|
|
33
|
-
"",
|
|
34
|
-
"## Rules",
|
|
35
|
-
"- Never apply changes without a user-facing preview and approval.",
|
|
36
|
-
"- MCP tools and the trusted project-local CLI are peer surfaces; choose by what is visible and approved here.",
|
|
37
|
-
"- If Kibi MCP tools are visible and approved, use MCP.",
|
|
38
|
-
"- Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with `--input <file|->`. If neither interface is available, stop and tell the operator.",
|
|
39
|
-
"- Do not infer MCP availability from config file existence. Do not read or edit `.kb/` files directly.",
|
|
40
|
-
"- Query before mutate. Run `kb_upsert` sequentially. Run `kb_check` before completion.",
|
|
41
|
-
];
|
|
42
|
-
return lines.join("\n");
|
|
43
|
-
}
|