kibi-opencode 0.19.3 → 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/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 { getInitKibiCommandCapability, } from "./init-kibi-capability.js";
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 buildInitKibiBootstrapReference(capability = getInitKibiCommandCapability()) {
59
+ function buildKibiBootstrapReference(capability = getKibiBootstrapCommandCapability()) {
60
60
  if (capability.supported) {
61
- return "Bootstrap existing repos: when the Kibi OpenCode plugin is active and native injection is supported, `/init-kibi` is the canonical short alias; `/kibi:init-kibi:mcp` remains the namespaced MCP fallback for the retroactive initialization (`kb_autopilot_generate`) workflow.";
61
+ return "Bootstrap existing repositories with `/kibi-bootstrap`; the canonical `kibi-bootstrap` skill owns planner, approval, apply, and repair semantics.";
62
62
  }
63
- return "Bootstrap existing repos: this host does not support native `/init-kibi` injection, so Kibi must fail closed and does not register a fake native alias; use `/kibi:init-kibi:mcp` for the retroactive initialization (`kb_autopilot_generate`) workflow.";
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 = getInitKibiCommandCapability()) {
66
- const commandBullet = capability.supported
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."
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.";
69
- return `Kibi capability selection (MCP tools and the trusted project-local CLI are peer surfaces over the same 18 operations):\n1. If Kibi MCP tools are visible and approved, use MCP.\n2. Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with \`--input <file|->\`.\n3. If neither interface is available, Kibi operation is blocked; tell the operator.\nDo 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.\n\nThis 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 operator to handle setup or repair if bootstrap is insufficient.\n- Load \`kibi-usage\` with \`kb_skills_load\` for comprehensive guidance.`;
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
- "Capability-based recovery steps:",
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
- - documentation/symbols.yaml refresh is required when extraction output changes; do not revert as scope creep`,
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
- The Kibi knowledge base is managed through visible MCP tools or the trusted project-local CLI — peer surfaces over the same 18 operations. Do not read or edit \`.kb/\` files directly.
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 = getInitKibiCommandCapability()) {
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/config.json exists but some configured KB targets are missing. Guidance remains advisory until the user/operator restores the configured KB targets.`;
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 = getInitKibiCommandCapability()) {
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 \`documentation/facts/FACT-xxx.md\` with the invariant (use \`constrains\` + \`requires_property\` for contradiction-safe reasoning)
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 \`documentation/adr/ADR-xxx.md\` documenting the decision
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 \`documentation/requirements/REQ-xxx.md\` with the behavior description
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 = getInitKibiCommandCapability()) {
466
+ function buildBaseGuidance(capability = getKibiBootstrapCommandCapability()) {
482
467
  return `${SENTINEL}
483
- This project uses Kibi through capability-selected MCP or CLI JSON routes. Prefer storing durable knowledge in Kibi over code comments.
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
- Before changing behavior: use kb_search for discovery, then kb_query by sourceFile, id, type, or tags for exact follow-up; do not rely on undocumented tools.
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
- **Kibi-first workflow:**
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 = getInitKibiCommandCapability()) {
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 = getInitKibiCommandCapability()) {
488
+ export function injectPrompt(current, config, context, capability = getKibiBootstrapCommandCapability()) {
525
489
  if (!config.prompt.enabled || !isPluginEnabled(config)) {
526
490
  return current;
527
491
  }
@@ -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
- // Default sync paths — must stay in sync with file-filter.ts DEFAULT_SYNC_PATHS
5
- const DEFAULT_SYNC_PATHS = {
6
- requirements: "documentation/requirements/**/*.md",
7
- scenarios: "documentation/scenarios/**/*.md",
8
- tests: "documentation/tests/**/*.md",
9
- adr: "documentation/adr/**/*.md",
10
- flags: "documentation/flags/**/*.md",
11
- events: "documentation/events/**/*.md",
12
- facts: "documentation/facts/**/*.md",
13
- symbols: "documentation/symbols.yaml",
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", "documentation"],
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 .kb/config.json exists at root. */
41
- function rootKbConfigExists(cwd) {
42
- return existsSync(join(cwd, ".kb", "config.json"));
40
+ /** Check if `.kb/manifest.json` exists at root. */
41
+ function rootKbManifestExists(cwd) {
42
+ return existsSync(join(cwd, ".kb", "manifest.json"));
43
43
  }
44
- /** Read and parse .kb/config.json. Returns null on failure. */
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 configPath = join(cwd, ".kb", "config.json");
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
- // Normalize: strip trailing slashes and glob patterns to get the root dir/file path
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, normalized)))
51
+ if (!existsSync(resolve(cwd, raw)))
83
52
  return false;
84
53
  }
85
- else {
86
- // Strip first glob segment
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 hasRootConfig = rootKbConfigExists(cwd);
96
+ const hasRootManifest = rootKbManifestExists(cwd);
138
97
  const vendoredTrees = findVendoredTrees(cwd);
139
- // 1. hybrid_root_plus_vendored — root config AND vendored subtrees
140
- if (hasRootConfig && vendoredTrees.length > 0) {
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/config.json exists alongside vendored tree(s): ${vendoredTrees.join(", ")}`,
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 config exists (check maintenance mode and target resolution)
149
- if (hasRootConfig) {
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/config.json exists and all configured KB targets resolve",
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 — root config exists but targets are missing
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/config.json exists but some configured KB targets are missing",
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 config, but vendored markers found
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/config.json, but vendored Kibi tree(s) detected: ${vendoredTrees.join(", ")}`,
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 — file is under .kb/
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 "fact"
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
@@ -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 — file is under .kb/
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 "fact"
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 — direct KB manipulation is always risky
26
- if (isUnderKb) {
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: ["File is under .kb/ — manual edits bypass KB validation"],
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 ?? 1,
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 ? (error.code ?? 1) : 0,
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.19.3","mcp":"0.21.1","cli":"0.18.1","core":"0.9.1"}
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", "config.json"));
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 === "master" ? "main" : branch;
160
+ return branch;
161
161
  }
162
162
  function normalizeRepoRelativePath(fromRoot, targetPath) {
163
163
  const rawRelativePath = relative(fromRoot, targetPath);
@@ -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 KB_CONFIG_FILE = ".kb/config.json";
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 configPath = path.join(cwd, KB_CONFIG_FILE);
28
- const missingConfig = !fs.existsSync(configPath);
15
+ const manifestPath = path.join(cwd, KB_MANIFEST_FILE);
16
+ const missingConfig = !fs.existsSync(manifestPath);
29
17
  const missingDocDirs = [];
30
- if (missingConfig) {
31
- // No config file: fall back to hardcoded defaults
32
- for (const docDir of KIBI_DOC_DIRS) {
33
- const fullPath = path.resolve(cwd, docDir);
34
- if (!fs.existsSync(fullPath)) {
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.19.3",
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": ["dist"],
40
+ "files": [
41
+ "dist"
42
+ ],
41
43
  "engines": {
42
- "node": ">=18"
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-cli": "^0.18.0"
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;
@@ -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
- }