Plugin development
Extend Kibi with capability plugins: the SDK, manifests, and plugin lifecycle.
Kibi capability plugins extend seven host-owned seams without taking over validation, Prolog, mutation, or proof:
kibi.semantic-classifier.v1— lane / ambiguity classification over host propositionskibi.ontology-pack.v1— predicate schemas and match candidateskibi.symbol-extractor.v1— synchronous legacy symbol analysiskibi.symbol-extractor.v2— asynchronous analysis with explicit completenesskibi.vocabulary-alignment.v1— modeling-time vocabulary convergence:rankSubjects(reuse an existing subject or declarenew_subject) andcompareClaims(possible-duplicate candidates for review)kibi.check-policy.v1— data-onlykb_checkpolicy: ownership and pattern-marker rules evaluated by Kibi (see Check policy)kibi.reference-extractor.v1— what a source file references (imports, calls, environment variables, first arguments, thrown errors, property values), read without running it, forsourceproofs over languages Kibi does not read itself (see Reference extractor)
Every capability is optional for third-party plugins: a plugin declares only the capabilities it provides, and plugins written before a capability existed keep validating and loading unchanged. The builtin plugin provides all six (its check policy is empty).
This is distinct from host plugins such as kibi-cursor, kibi-opencode,
kibi-codex, kibi-zcode, and kibi-claude, which adapt an IDE or agent host to Kibi's
operation surface. Capability plugins never register MCP tools or Git hooks.
Automatic builtin
kibi-plugin-builtin ships with the standard CLI distribution and is always
registered. It is not listed in package.json kibi.plugins. With no
kibi.plugins config, only the builtin pack runs.
Named export
Activated packages must export a single named kibiPlugin binding that
passes validateKibiPlugin / defineKibiPlugin from kibi-plugin-sdk.
A default export alone is not accepted by the host loader.
import { KIBI_PLUGIN_API_VERSION, defineKibiPlugin } from "kibi-plugin-sdk";
export const kibiPlugin = defineKibiPlugin({
apiVersion: KIBI_PLUGIN_API_VERSION,
id: "example-ontology",
version: "0.1.0",
permissions: { network: false, metered: false, secrets: [] },
capabilities: {
ontologyPack: {
id: "example-ontology.pack",
schemas: () => [],
match: () => [],
},
},
});Third-party plugins need only kibi-plugin-sdk. They must not depend on
kibi-cli or kibi-mcp internals.
Activation
Declare the package as a project dependency and activate capabilities under
package.json:
{
"kibi": {
"plugins": [
{
"package": "kibi-plugin-jev",
"capabilities": {
"kibi.semantic-classifier.v1": { "mode": "augment" }
}
}
]
}
}Rules:
- Bare package names only (no paths, URLs, or aliases)
- Package must appear in
dependencies,devDependencies, oroptionalDependencies - Resolution follows the project's package manager (npm, pnpm symlink, Yarn PnP)
NODE_PATH, global installs, and ambient ancestor packages are rejected
Configuration surface
package.json#kibi.plugins is the canonical activation and mode surface. Each entry names a bare package and the capabilities it may provide, with augment, replace, or shadow. That manifest is small, declarative, and validated before any plugin module is imported. Provider secrets stay in the environment, outside repository configuration.
The manifest does not include kibi.config.ts, generic plugin options, plugin factories, or executable config.
kibi doctor prints the parsed plugin rows (package, capability, mode, declared dependency) without importing the plugin package. First-party Jev secret and model diagnostics are known statically. Generic plugins do not get secret introspection. A configured package that is not listed in dependencies, devDependencies, or optionalDependencies fails that check. Add the package to one of those fields, or remove the kibi.plugins entry. Editing package.json remains the way to enable or disable a plugin.
Modes
Per capability, at most one replace is allowed. augment providers run in
package activation order with capability-specific builtin positioning.
shadow providers never affect canonical results; they may contribute
comparison metadata only.
| Capability | Builtin position | Notes |
|---|---|---|
| Semantic classifier | First for augment; fallback for replace failure |
External classifiers run only from the semantic advisor (kb_model mode analyze, CLI semantic-advisor) and kb_compile_intent. Valid empty decisions[] under replace is abstention (conservative none), not builtin fill. |
| Ontology pack | Catalog starts with builtin for augment |
replace excludes the builtin provider catalog. Valid empty match() is abstention (no builtin consult). Allowed wherever Kibi already matches ontology |
| Symbol extractor v1 | Builtin first for supported files under augment |
Legacy replacement and fallback behavior |
| Symbol extractor v2 | Builtin first for supported files under augment |
First claiming provider owns the result; partial, failed, or unsupported output never becomes a successful fallback |
| Search hints | None; the builtin gives no hints | replace or shadow only; augment is rejected. Runs only from kb_search, and only when the intent answer abstained or its top score is below 0.44. A provider may name only the entities it was given, at most 20, scored in [0, 1] in score then id order; a failing, late (3 s first call, 0.5 s after) or invalid answer gives no hints and a diagnostic. Hints never change results, the answer layer or abstention |
| Vocabulary alignment | Builtin always runs first; fallback for replace failure |
External providers run only from requirement modeling (kb_model mode requirement, CLI model-requirement). augment refines only clauses the builtin left as new_subject and pairs it did not judge duplicates (it can add candidates, never remove them). A provider may only choose among the builtin-ranked candidates or new_subject; any other answer is rejected and falls back to builtin with fallbackUsed. Results are advice in the modeling plan, never check outcomes |
Sync maintenance paths (sync, check, kb_upsert, status, proof, and
related) keep deterministic builtin analysis for every capability and must
never invoke external semantic classifiers, ontology packs, symbol extractors,
or vocabulary-alignment providers. kb_check in particular never imports
plugin code: domain-redundancy, subject-key-identity, and the other Prolog
checks remain the pass/fail authority, and an activated check policy is read
as JSON data and evaluated by Kibi's own policy-ownership and
policy-markers rules. Async advisor / compile-intent / staged-symbol paths compose the
registry (replace / augment / shadow); replace mode strips or overrides any
sync-path builtin suggestions before results are returned.
Maintenance source analysis uses the builtin extractor and explicitly activated, host-approved v2 source analyzers. Before importing an external source entrypoint, the host verifies its release-qualified package, runtime dependency and asset hashes. An unknown version, modified asset, missing dependency or escaping symlink fails analysis. Other plugin capabilities are not imported by this path. Semantic classification and ontology activation do not authorize their execution during maintenance.
For staged checks, activation is read from captured Git trees. A commit changing source retains baseline source analyzers, so disabling a plugin in the same commit cannot bypass analysis. A configuration-only uninstall is permitted. Installed package files still have to match the host approval.
Source analysis v2
An extractor exports symbolExtractorV2 with supports({ path, language }) and
an asynchronous analyze({ path, content, timeoutMs }). The input is supplied
snapshot text; providers must not substitute working-tree contents. The host owns
the analysis deadline: timeoutMs is the budget it grants, set slightly below its
own deadline, so a provider should return a failed result with its own timeout
diagnostic before the budget elapses. The host abandons calls that outlive its
deadline. Results carry
contractVersion: "kibi.symbol-extractor.v2" and one status:
| Status | Meaning |
|---|---|
ok |
Complete supported analysis; zero declarations is valid. |
partial |
Some declarations were recovered; diagnostics and uncovered ranges explain uncertainty. |
unsupported |
The provider cannot analyze this input; no symbol evidence is returned. |
failed |
Operational or validation failure; no successful symbol evidence is returned. |
Only after-side gaps the author can act on block a change. The committed (before)
side is informational. On the staged side, failed and non-local diagnostics
(syntax, parse, timeout, integrity) always block, because a removed brace can
surface far from the edited lines. A local limitation, such as a Rust macro or a
Python decorator, matters only when its uncovered range overlaps a changed line.
Positions are one-based lines and zero-based UTF-16 columns, with exclusive ends. The host validates bounds against original content, including CRLF and non-BMP characters. Qualified names and signatures are declaration locators; authored KB IDs remain the authority for identity, ownership and proof. An ambiguous locator cannot transfer an existing ID.
kibi-plugin-treesitter is optional. Every language's grammar is a data-only
kibi-lang-* pack (kibi-lang-python, kibi-lang-go, kibi-lang-rust,
kibi-lang-java, kibi-lang-csharp, kibi-lang-php, kibi-lang-c,
kibi-lang-cpp, kibi-lang-bash, kibi-lang-ruby, kibi-lang-hcl,
kibi-lang-prolog) installed next to it. The plugin pins each
pack's version and grammar SHA-256 in its catalog and checks the bytes before
loading them; a file whose pack is missing, another version or modified is
refused with the install line, never skipped. Declare the exact qualified
release and the packs you need as project dependencies and add:
{
"kibi": {
"plugins": [{
"package": "kibi-plugin-treesitter",
"capabilities": { "kibi.symbol-extractor.v2": { "mode": "augment" } }
}]
}
}The package contains the built-in grammar WASM, the queries for every language,
notices and integrity metadata. Analysis
does not download parsers, start language servers or execute consumer build tools.
It keeps a small pool of persistent workers that load each grammar once; a
worker that times out or fails is discarded and replaced. Its deadline and
resource limits contain parser failures; they are not a
sandbox for arbitrary plugin JavaScript. After a reviewed package rebuild,
node scripts/qualify-source-analyzers.mjs --write records its exact runtime
closure; the command without --write verifies it. CI and packing must pass
verification without rewriting approval. Update the package integrity manifest
before recording approval, and requalify the relocated tarball. See
qualification and
delivery scope.
Trust boundary
Explicit activation grants code-execution trust to the npm package. Module
evaluation can run arbitrary top-level code. Kibi cannot enforce a third-party
network: false declaration after import.
permissions.network, permissions.metered, and permissions.secrets are
disclosure-only metadata for operators and UIs, not an in-process sandbox.
Plugins cannot bypass completeness checks, Prolog, mutation gates, or proof.
Host validators stamp provider provenance (pluginId, mode, external,
network/metered, fallback).
Privacy and cost
Semantic classifiers may receive claim keys, proposition text, and minimal role
or signal context. Prefer local/offline providers when possible. Metered
providers must declare metered: true and list required secret names.
Testing
- Unit-test classifiers/packs/extractors against
kibi-plugin-sdkvalidators - Keep import side effects free of network I/O until an allowed capability call
- For optional providers such as Jev, cover missing key, timeout, and malformed responses with injectable clients and offline fixtures
Vocabulary alignment contract
rankSubjects({ clauses }) receives, per clause, a claimKey, the clause
text, the proposedSubjectKey Kibi would otherwise declare, and builtin
candidates (subjectKey, score in [0, 1], optional title and
requirementTitles). It returns one decision per clause: choice is one of the
candidate subject keys or new_subject, plus a confidence in [0, 1].
compareClaims({ pairs }) receives pairs sharing a subject key (or predicate
name) whose signatures differ, and returns { pairKey, sameObligation, confidence } per pair. sameObligation: true only nominates the pair for
review; exact duplicates are detected by the deterministic domain-redundancy
check.
Results are validated by validateRankSubjectsResult and
validateCompareClaimsResult: foreign keys, duplicate keys, invented subjects,
and out-of-range confidences are rejected before they reach the plan.
Check policy
kibi.check-policy.v1 lets a plugin turn on stricter kb_check rules for the
projects that activate it, without running plugin code during checks. The
plugin's package.json names a JSON document:
{ "name": "my-policy", "kibi": { "checkPolicy": "check-policy.json" } }During kb_check, Kibi resolves each package activated for
kibi.check-policy.v1 (only augment is accepted), reads that file from
inside the package root, and validates it with validateCheckPolicyDocument.
It never imports the module. A policy that is missing, escapes the package
root or fails validation blocks the check rather than silently switching off.
The plugin should also export the same document as capabilities.checkPolicy
so validateKibiPlugin and tooling can see it.
A document has a contractVersion (kibi.check-policy.v1), a kebab-case
id, a title, and two optional rule lists:
ownership(rulepolicy-ownership): symbols whosesourceFilematchesinclude(gitignore-style globs, minusexclude) and whose title matchessymbolTitlePatternmust implement a current requirement linkedrequires_predicateto an asserted fact of one ofrequirePredicates. Symbols taggedexemptTagand executable test symbols are skipped.markers(rulepolicy-markers): for every current requirement grounded inpatternPredicate, the pattern is the fact argument atpatternArgument. Each assertedmarkerPredicatefact whosemarkerPatternArgumentnames that pattern contributes the literal atmarkerArgument. Every source file implementing the requirement, plus its sibling files with the same stem and asiblingExtensionsextension, must contain each marker (_and-spellings both match).
Rule ids must be unique across both lists, predicate names are snake_case and
argument indexes are zero-based. kibi-plugin-ui is the first-party policy.
Optional UI design plugin
kibi-plugin-ui (experimental) ships a check policy that keeps React and
Angular components on their agreed design patterns. /kibi-bootstrap offers it
when kb_plan_bootstrap finds production *.tsx, *.jsx or *.component.ts
files (pluginOffers), unless the project already activated it or listed it
under package.json kibi.declinedPlugins. It declares no permissions and has
no runtime code path. The UI vocabulary it relies on (ui_pattern,
same_pattern, pattern_marker, ui_container) and component-scope proof for
pattern requirements are part of Kibi core. See
packages/plugin-ui/README.md and
ui-requirements.md.
Reference extractor
kibi.reference-extractor.v1 serves the references source-proof reader
for languages Kibi does not parse itself. A provider exposes supports({path})
and extract({path, content, extract, callee?, key?, timeoutMs?}), and
returns {status: "ok", language, members, unresolved} or a refusal
(unsupported or failed, with a code and message). It lists only what it
reads exactly and reports every construct it cannot read statically as an
unresolved row with its 1-based line and text; Kibi then refuses the read and
names those places, so a proof never rests on a guess. Results are checked
with validateReferenceExtractionResult from kibi-plugin-sdk. The capability
activates only in augment mode.
A proof is evidence, so the host uses a provider only when its installed files
match a release-qualified closure Kibi ships (the same list impact review
uses); an activated but unqualified provider is reported and never imported.
kibi-plugin-treesitter is the qualified provider today and reads Go, Java,
C# and Rust; see proving-requirements.md for what
each extract lists and refuses.
{
"devDependencies": {
"kibi-plugin-treesitter": "3.0.0",
"kibi-lang-go": "1.0.0",
"kibi-lang-rust": "1.0.0"
},
"kibi": {
"plugins": [
{
"package": "kibi-plugin-treesitter",
"capabilities": {
"kibi.symbol-extractor.v2": { "mode": "augment" },
"kibi.reference-extractor.v1": { "mode": "augment" }
}
}
]
}
}A Go, Java, C# or Rust project without a package.json creates one that only
holds this list (npm init -y, then npm install -D kibi-plugin-treesitter kibi-lang-go). kibi init, kibi doctor and kb_plan_bootstrap suggest the
plugin and the packs when such sources are tracked and it is not active for
proofs, and kibi doctor names a pack that is missing or not the version the
installed plugin pins.
Only files people write count toward a pack. A file matched by a
package.json kibi.generatedPaths glob, marked linguist-generated or
linguist-vendored in .gitattributes, or one that opens with a generator
marker (@generated, Code generated ... DO NOT EDIT, auto-generated),
counts only when the KB references it, for example as a symbol's sourceFile
or in a proof contract's files. A pack the project does not want can be
declined on its own:
{
"kibi": {
"declinedLanguagePacks": ["kibi-lang-cpp", "kibi-lang-bash"]
}
}kibi.generatedPaths takes gitignore-style globs for codegen output that has
no header or attribute ("generatedPaths": ["src/api/**", "!src/api/index.ts"]);
a ! pattern, like linguist-generated=false, marks a path as hand-written.
The same rules exempt generated files from the commit gate's requirement-link
check (see kibi check --staged in the CLI reference).
Kibi then stops suggesting a declined pack, and kibi sync and the staged check treat files
in its language as files no plugin reads: symbols there are placed by text
match, and a staged change to one gets a whole-file review instead of blocking
on the missing pack. A generated file whose pack is missing gets the same
review. A pack declined in the same commit as a source change in its language
takes effect from the next commit, like other analyzer settings.
Optional search hints plugin
kibi-plugin-search-hints implements kibi.search-hints.v1 with the bundled,
MIT-licensed potion-base-8M static embedding (int8, about 7.7 MB) and a pure
JavaScript tokenizer checked against the upstream one. It needs no network,
native code or model download. Activate it in replace mode:
{
"kibi": {
"plugins": [
{
"package": "kibi-plugin-search-hints",
"capabilities": { "kibi.search-hints.v1": { "mode": "replace" } }
}
]
}
}When a kb_search answer is weak, the host sends the question and the current
entities (title, statement and body, up to 2000 characters each) and shows the
closest ones as queryAnalysis.hints. Entity vectors are cached under Kibi's
runtime directory and rebuilt only for entities whose text changed; on a KB of
about 3,000 entities the first weak search builds them in under a second.
/kibi-bootstrap offers the plugin once the KB holds 50 or more requirement
documents. See packages/plugin-search-hints/README.md.
Optional Jev provider
kibi-plugin-jev implements kibi.semantic-classifier.v1 and
kibi.vocabulary-alignment.v1 via TypeSafe Jev (@typesafe-ai/sdk). Each is
activated separately. It is not a default CLI or MCP dependency.
- Install and activate explicitly (see install.md)
- Set
TYPESAFE_API_KEYvia Kibi env bootstrap (same for every MCP host):
mkdir -p ~/.config/kibi
printf '%s\n' 'TYPESAFE_API_KEY=...' >> ~/.config/kibi/env Optional project override: <workspace>/.env.kibi. Process env wins;
KIBI_ENV_FILE replaces the project path; legacy .env fills gaps only.
Never store the key in package.json. Restart long-running MCP after changes.
- Optional
KIBI_JEV_MODEL(defaultjev-latest; empty or whitespace is unset) - Optional
KIBI_JEV_TIMEOUT_MS, a positive integer of at most 120000. Malformed values fail with a provider diagnostic that includes the effective model and not the API key - Explicit
JevSemanticClassifierOptions.modelandtimeoutMsoverride those environment defaults. They are a programmatic constructor API, not fields inpackage.json - Importing the package, or leaving it installed but inactive, performs no TypeSafe client or network call
- On failure Kibi falls back to the builtin classifier with an advisory warning
- Requirement modeling (
kb_modelmoderequirement) does not invoke the classifier. External semantic classifiers run only from the semantic advisor (kb_modelmodeanalyze) andkb_compile_intent - Vocabulary alignment (
rankSubjectsas achoiceover the builtin top 5 plusnew_subject;compareClaimsas anoulper pair) runs only from requirement modeling (kb_modelmoderequirement), and only whenkibi.vocabulary-alignment.v1is activated - Live tests require both
KIBI_JEV_LIVE_TEST=1andTYPESAFE_API_KEY kibi doctorreports first-party Jev secret source labels and model/timeout without importing the plugin or leaking values
See also packages/plugin-jev/README.md.