kibi-cursor 2.10.0 → 2.11.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/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +91 -0
- package/README.md +21 -3
- package/agent-plugin/plugin.json +1 -1
- package/agent-plugin/skills/kibi-commit-review/SKILL.md +6 -2
- package/agent-plugin/skills/kibi-decisions/SKILL.md +7 -2
- package/agent-plugin/skills/kibi-decisions/resources/signoff-views.md +3 -1
- package/agent-plugin/skills/kibi-usage/SKILL.md +14 -3
- package/agent-plugin/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/agent-plugin/skills/kibi-usage/resources/proof.md +44 -3
- package/bin/hook-runner.mjs +3233 -0
- package/bin/launch-kibi-mcp.mjs +2 -1
- package/dist/.cursor-plugin/plugin.json +1 -1
- package/dist/bin/hook-runner.mjs +3233 -0
- package/dist/bin/launch-kibi-mcp.mjs +2 -1
- package/dist/hook-runner.js +245 -200
- package/dist/hooks/hooks.json +6 -6
- package/dist/skills/.canon-hash.json +6 -5
- package/dist/skills/kibi-commit-review/SKILL.md +6 -2
- package/dist/skills/kibi-decisions/SKILL.md +7 -2
- package/dist/skills/kibi-decisions/resources/signoff-views.md +3 -1
- package/dist/skills/kibi-usage/SKILL.md +14 -3
- package/dist/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/dist/skills/kibi-usage/resources/proof.md +44 -3
- package/hooks/hooks.json +6 -6
- package/package.json +5 -3
- package/skills/.canon-hash.json +6 -5
- package/skills/kibi-commit-review/SKILL.md +6 -2
- package/skills/kibi-decisions/SKILL.md +7 -2
- package/skills/kibi-decisions/resources/signoff-views.md +3 -1
- package/skills/kibi-usage/SKILL.md +14 -3
- package/skills/kibi-usage/resources/prolog-proofs.md +207 -0
- package/skills/kibi-usage/resources/proof.md +44 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cursor",
|
|
3
3
|
"displayName": "Kibi",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.11.0",
|
|
5
5
|
"description": "Optional Cursor adapter for Kibi. Requires project-local kibi-cli, kibi-mcp, and kibi-core (SWI-Prolog is bundled on Linux and macOS) before the bundled MCP server can start.",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
7
7
|
"author": {
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
# kibi-cursor
|
|
2
2
|
|
|
3
|
+
## 2.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 4b4e7cb: A SWI-Prolog project can now prove its requirements against its own predicates. Exhaustive domains, `value_source` numbers, rule tables, state machines and patterns read `.pl` and `.plt` files with the SWI-Prolog Kibi already runs on, the same way they read TypeScript, JavaScript and Python exports, so a Prolog library no longer has to fall back to file patterns or a harness written in another language to reach `proved`. The kibi-usage skill (2.38.0) ships a `kibi_certificate` module (`resources/prolog-proofs.md`) that Prolog proof files use to write their certificates.
|
|
8
|
+
|
|
9
|
+
In a proof contract, `export` names a predicate indicator (`status/1`, `phrase//1`, `operators:operator/2`) and the new `arg` key on `domain_source` and `value_source` names the 1-based argument that holds the members of a predicate with more than one argument. A module file loads as its module and a plain file into `user`. Kibi refuses a read it cannot decide exactly: solutions that leave the argument unbound, more than 100,000 solutions, several arguments without `arg`, `arg` together with `field`, and `arg` on a TypeScript or Python export. A Prolog proof file imports its domain when it names the predicate and loads the file (`use_module`, `library(X)` for a pack's `prolog/` directory, `consult`, `ensure_loaded` or a `:- [...]` list), or when it is the `.plt` beside it. A rule predicate is called once per row and decided by success; a transition table is a predicate of arity 2 or 3 whose solutions are the edges, or a `next/3` predicate driven over its `domains`; a pattern is a single atom or string. One certificate reads one language, and a contract mixing languages is refused.
|
|
10
|
+
|
|
11
|
+
Code proof evaluation now goes through one evaluator per language, and a file in a language without an evaluator is refused with the language named. A code proof receipt is now also bound to the files of the symbols its contract reads (`domain_source`, `value_source`, `initial_source`, `pattern_source`, `domains`, `predicate_source`), so it goes stale when the file a domain is read from changes even when that symbol is not `covered_by` the test.
|
|
12
|
+
|
|
13
|
+
## Migration
|
|
14
|
+
|
|
15
|
+
No files change shape. Code proof receipts whose contracts read a symbol outside the test's covered symbols get a new binding hash and need `kibi prove` once.
|
|
16
|
+
|
|
17
|
+
- 79c86a7: Source proofs now read SWI-Prolog. The `references` reader accepts `.pl` and `.plt` files, so a Prolog pack can prove which libraries its modules load, which predicates they call (for example that nothing calls `use_foreign_library/1`), which environment variables they read and which errors they throw. A new `prolog` reader lists one argument of every fact with a given functor, such as the `requires/1` facts of `pack.pl`. Both read the files term by term with SWI-Prolog and never run the project's code. The kibi-usage skill (2.39.0) describes both readers.
|
|
18
|
+
|
|
19
|
+
`imports` lists each loaded file as written (`library(lists)`, `helpers`) from load directives and from load goals in clause bodies. `calls` lists `Name/Arity` (or `Module:Name/Arity`) of every goal in clause bodies and directives. It walks control constructs, grammar rules, lambdas and meta-predicate arguments, using the file's own `meta_predicate` declarations and SWI-Prolog's. `env` reads `getenv/2`, `string_args` reads the first argument of calls to `callee`, and `thrown` reads the error of `throw(error(type_error(...), _))` and the `library(error)` helpers. A rethrow of a caught ball does not count as a new error. Operators come from the file's own `op/3` directives, the module export lists it can read, and the SWI-Prolog libraries it loads.
|
|
20
|
+
|
|
21
|
+
Some reads cannot be decided statically, and Kibi refuses them with their file and line: a variable called as a goal or closure, a goal qualified by a variable module, a clause asserted from a variable, and conditional compilation. `property` is refused for Prolog files. The `prolog` reader takes `file`, `functor` (`Name/Arity`) and `arg`. It refuses a fact with several arguments when no `arg` is given, and a fact whose argument holds a variable. A list argument contributes its items. The read keeps its members in file order for `precedes` claims.
|
|
22
|
+
|
|
23
|
+
## Migration
|
|
24
|
+
|
|
25
|
+
No files change shape. The source reader vocabulary now includes `prolog`.
|
|
26
|
+
|
|
27
|
+
- f53950a: Source proofs now read Go, Java, C# and Rust. The `references` reader hands these files to `kibi-plugin-treesitter`, which lists what they import, call, read from the environment, pass as a first argument, throw and set as a property value, from their syntax trees and without compiling or running them. The kibi-usage skill (2.40.0) describes it.
|
|
28
|
+
|
|
29
|
+
A new plugin capability, `kibi.reference-extractor.v1` (kibi-plugin-sdk), lets a plugin answer the `references` reader for a language Kibi does not parse itself. A provider returns the members it reads exactly and an `unresolved` row (line and text) for each construct it cannot read statically, and Kibi refuses the read with those places. `validateReferenceExtractionResult` checks results. The capability activates only in `augment` mode.
|
|
30
|
+
|
|
31
|
+
`kibi-plugin-treesitter` provides it for Go, Java, C# and Rust, in the same bounded workers as symbol extraction. Members are written as the code names them: Go import paths, Java imports (`java.util.*`), C# `using` targets and Rust `use` paths expanded from their groups for `imports`; `os.Getenv`, `System.out.println` (and the method reference `System.out::println`), `Console.WriteLine` and `std::env::var` for `calls`, with `*.name` for a method on an expression Kibi cannot name; `os.Getenv`, `os.LookupEnv`, `System.getenv`, `Environment.GetEnvironmentVariable`, `env::var`, `env::var_os`, `env!` and `option_env!` for `env`; Go panics and Java and C# `throw new X()` for `thrown`; Go keyed literals and C# object, indexer and collection initializers for `property`. Names a file gives itself are followed: Go import aliases, Java single static imports, C# `using static` and `using X = ...` aliases and Rust `use` bindings (`use std::env::var as getvar`) read as the canonical name, and fully qualified forms (`java.lang.System.getenv`, `System.Environment.GetEnvironmentVariable`, `std::env::var`) match too: calls on `java.lang` and C# `System` types are listed under both spellings. C# `imports` also holds the project's `global using` lines, `<Using Include>` items and the usings `ImplicitUsings` adds, and `global::` prefixes are dropped. A function or method used as a value (`logf := logrus.Error`, a C# method group, a Rust path passed on) is listed in `calls`. A Go identifier that shadows a package the file does not import, and a Rust path whose root the file does not bind (a local `mod env`), are not taken for that package. A name computed at run time, the whole environment (including `os.ExpandEnv` and `Environment.ExpandEnvironmentVariables`), an environment getter handed on as a value (`g := os.Getenv`, `System::getenv`), a dot, static-wildcard or glob import that could bring a getter or call into scope, a call with no static name (`fs[0]()`, `<T as Tr>::m()`), reflection (also by fully qualified name, `Activator.CreateInstance` and Go `plugin`) and C# `dynamic` anywhere (for `calls` and `env` alike), a C# `#if` block, a Rust `#[cfg(...)]` item other than `#[cfg(test)]` (whose items are skipped), for Rust `calls` and `thrown` any macro invocation, attribute macro or derive outside the standard library, for Rust `env` a macro whose arguments mention a getter, and for `string_args` a macro whose arguments mention the callee fail the read with their place.
|
|
32
|
+
|
|
33
|
+
Proofs use a reference extractor only when its installed files match the release-qualified closure Kibi ships, the same check impact review applies, so an unqualified or modified provider is never imported. Without an active provider the read is refused with what to install. `kibi init`, `kibi doctor` and `kb_plan_bootstrap` suggest `kibi-plugin-treesitter` for symbols when sources in any of its languages are tracked, and for proofs when Go, Java, C# or Rust sources are tracked and it is not active for them, with the exact `kibi.plugins` entry to add; a project without a `package.json` creates one that only holds Kibi's plugin list and gitignores `node_modules/`. `kb_plan_bootstrap`'s source glob now also covers `.pyi`, `.cxx`, `.hh`, `.hxx`, `.cs`, `.sh`, `.bash`, `.tf` and `.hcl`.
|
|
34
|
+
|
|
35
|
+
`kibi-plugin-treesitter` 3.0 ships no grammars. Each grammar moved, unchanged, into its own data-only package, so a project downloads only the languages it uses and every language is installed the same way: `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` (Terraform and HCL) and `kibi-lang-prolog` (SWI-Prolog), all at 1.0.0. JavaScript and TypeScript need no pack, since Kibi parses them itself. A pack holds the grammar WebAssembly and its license, with no code or install scripts. The plugin pins each pack's exact version and each grammar file's SHA-256 in `catalog.json` and lists the packs as optional peer dependencies in their major (`^1.0.0`); a worker checks the bytes before loading them. A file whose pack is missing (`TREESITTER_LANGUAGE_PACK_MISSING`) or another version (`TREESITTER_LANGUAGE_PACK_VERSION`) is now `unsupported` with the install line, and a modified grammar file is `failed` (`TREESITTER_LANGUAGE_PACK_MISMATCH`), for symbols and for references; neither is skipped. Packs resolve where Node finds them from the plugin (npm, pnpm and Bun; not Yarn Plug'n'Play). `kibi doctor` and `kibi init` name the packs the tracked sources need, and `kb_plan_bootstrap`'s plugin offer lists them in `alsoInstall`. `kibi doctor` also names an installed pack whose grammar does not match the pinned hash. While a pack is missing or another version, `kibi sync` warns once per file with the install line, and the staged check blocks a change to that file instead of asking for a whole-file review, so a missing install never disarms the gate.
|
|
36
|
+
|
|
37
|
+
Symbol signatures that told overloads apart wrongly are fixed: C++ `f(int&&)` no longer reads as `f(int&)`, a C/C++ variadic `...` and parenthesized declarators (`int (*p)`) are kept, C# `ref`, `out` and `in` parameters and `params` methods get their own signature, Java comments inside a parameter list no longer drop it, and a C++ function declarator inside a parameter or template parameter list is not reported as a symbol. Symbols whose locator changes this way need their `symbols.yaml` entry updated once.
|
|
38
|
+
|
|
39
|
+
A file the grammar reads only partly (a C `#include`, a Rust macro, a nested HCL block) no longer stops `kibi sync --refresh-symbol-coordinates` for the whole repository: symbols declared outside its uncovered ranges keep their coordinates, the others get none, and one warning per file names the lines and diagnostics. The staged check validates the same symbols and asks for a review only where a change touches an uncovered range. A file the analyzer cannot read as a whole (a syntax error, which the grammars also report for some valid code such as an `#ifdef` inside an `if`, a timeout or a changed grammar) gives only its own symbols no coordinates and is listed as a failed refresh; the rest of the repository still refreshes. Local gaps past the 64-diagnostic cap are summarized as one range instead of a whole-file limit, so large Terraform files keep their symbols. Kibi's own `.kb/` files are never analyzed as project source, so they no longer ask for kibi-lang-prolog, and a `.pl` file alone (often Perl) no longer counts as Prolog for the pack suggestion.
|
|
40
|
+
|
|
41
|
+
The `references` readers refuse more constructs they cannot read exactly, so a claim such as "never calls X" or "reads no environment variable" can no longer pass on code that does. Go resolves a local `os` (parameter, block, `range`, type switch) and a non-standard package imported as `os` by scope, refuses cgo, `//go:linkname` and `.Environ()` for `env`, and lists a method on a receiver that visibly comes from package p also as `p.M`. Java follows receivers by declared type, cast and `new`, reads imports with comments, does not take `import other.System` or a `System` class declared in the file for `java.lang.System`, and refuses `getenv`, `exit`, `halt`, `exec`, `load` and `loadLibrary` on a receiver of unknown type. C# files a bare call under a type only when the file declares it or one known `using static` type has it, and refuses other bare calls and getters it cannot place; `@` verbatim identifiers are read without the `@`. Rust resolves paths through `use`, `mod` and `extern crate` in scope (`use Command as C`, `self::`, `extern crate std as s`) and refuses `env`, `imports` and `string_args` on macros outside the standard library and common formatting macros, `include!`, a nested `env!` and attributes configured with `env`. Kibi's Python reader follows import aliases for `env` and `calls`, reads `os.environ.setdefault`, `pop` and `in`, lists `from urllib import request` as `urllib.request` too, and refuses the whole environment handed on or copied, `getattr` on an imported module, `exec`, `eval`, `__import__`, `importlib.import_module`, `sys.modules` and star imports. Kibi's Prolog reader no longer fails on `meta_predicate` declarations, reads `apply/2` and literal `assertz` clauses, refuses `term_expansion` and `goal_expansion` in the files and in project files they load, refuses an `include` of a file outside `files`, and fails instead of silently dropping a loaded file it cannot read.
|
|
42
|
+
|
|
43
|
+
A `references` claim is decided only where it holds. Reference members are matched by name segment (`.`, `/`, `::`), so `excludes` fails on a member under an excluded item (`java.sql` excludes `java.sql.Connection`, `github.com/labstack/echo/v4` excludes `.../echo/v4/middleware`), on the prefix of an item ending in `*` (`java.sql.*`, `os.*`), and, with the member, the item and the files named, on a member that may be the item: a wildcard import, a shorter name (`File.delete`) or a call on a receiver Kibi cannot name (`*.delete` may be `java.io.File.delete`). `includes` and `subset_of` count only members that certainly are an item, so `*.m` never satisfies `X.m` and `os` does not allow `os/exec`. A source proof is rejected when a symbol the test covers or that implements the claim's requirement lies in a file the read does not cover, and the refusal for files the reader cannot read no longer advises dropping them from `files`: it says the claim cannot be decided for them. An `eq`, `subset_of` or `excludes` claim read with `string_args` on an environment getter (`os.Getenv`, `env::var_os`, `System.getenv`, `getenv/2`, ...) is refused with a pointer to `extract: env`.
|
|
44
|
+
|
|
45
|
+
Failures that looked like success are now reported. `kibi doctor` fails when a plugin `package.json#kibi.plugins` declares is not installed, and when the installed `kibi-plugin-treesitter` is not the release-qualified build this Kibi pins (such as an older build published under the same version), naming the fix. `kibi sync --refresh-symbol-coordinates` refuses, keeping the stored coordinates, instead of dropping them and reporting success. A symbol Kibi cannot place in its existing source file is reported by `kibi check` (advisory `symbol-coordinates-missing`, a warning) and by `kb_upsert`. `kibi prove` counts a test whose certificate or claim binding was rejected as `refused`, not `passed` (a `refused` field in its summary), and says when a derived binding cannot prove the claim because the receipt is not end to end; `kibi proof explain` and signoff list each claim's bindings and rejections whatever the requirement's proof status, and say why a derived claim is not yet proved.
|
|
46
|
+
|
|
47
|
+
C and C++ headers are read as the compiler reads them: an include guard (`#ifndef G` / `#define G` ... `#endif`), the `#ifdef __cplusplus` / `extern "C" {` linkage guard and an `#if 0` block are blanked before parsing, keeping every line and column, so guarded headers keep their symbol coordinates and a header edit no longer blocks the commit. Terraform nested blocks (`validation`, `lifecycle`, `dynamic`, `ingress`) belong to the block that holds them and are no longer analysis gaps, and `moved`, `import`, `removed` and `check` blocks are neither symbols nor gaps. Past the 64-diagnostic cap, local gaps are merged into the fewest ranges that fit, closing first the gaps between them that hold no symbol start, so declarations between gap groups keep their coordinates instead of one range covering the rest of the file.
|
|
48
|
+
|
|
49
|
+
## Migration
|
|
50
|
+
- Install the grammar packs for the languages you analyze next to the plugin, for example `npm install --save-dev kibi-plugin-treesitter@3 kibi-lang-python kibi-lang-go`. Python and SWI-Prolog need their packs too (`kibi-lang-python`, `kibi-lang-prolog`); until they are installed, symbol extraction for those files is unsupported, while Kibi's own Python and Prolog source-proof readers keep working. `kibi doctor` prints the exact line for the tracked sources. Until the packs are installed, those files are reported as unsupported instead of analyzed.
|
|
51
|
+
- Re-check `excludes` reference claims written against a parent name or with a trailing `*`: they now fail on members under it, and claims over files with `*.m` calls or short names that may be an excluded item are refused. A proof whose test covers, or whose requirement is implemented by, a symbol outside `files` is rejected until those files are read. Rewrite environment claims read with `string_args` on a getter to `extract: env`. Scripts that read `kibi prove`'s `passed` count now see refused tests under `refused`.
|
|
52
|
+
- To prove references in Go, Java, C# or Rust, also add `"kibi.reference-extractor.v1": { "mode": "augment" }` to the `kibi-plugin-treesitter` entry of `package.json#kibi.plugins`.
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- Each hook decision is now recorded in usage telemetry, as on the other hosts (`KIBI_TELEMETRY=0` turns it off). The README npm copy route replaces an earlier copy instead of nesting into it and explains that the copy needs no `node_modules`, and the missing-`kibi-mcp` error says how to use a global `kibi-mcp` through the manual MCP entry.
|
|
57
|
+
|
|
58
|
+
- The kibi-usage skill (2.40.1) describes the `must keep true` line of edit snippets as the requirement's modeled claims or its own statement.
|
|
59
|
+
|
|
60
|
+
- Edit snippets say what the lead requirement must keep true as the claims its facts model, in words with their operator and members (for example "REQ-GREET must keep true: The greet function must not call console.log."), or as the requirement's own statement, instead of listing internal fact IDs and subject keys. A requirement title that an older Kibi cut at a dot inside a name is shown whole.
|
|
61
|
+
|
|
62
|
+
- 048d1c9: Close false proofs found by testing the language packs before release:
|
|
63
|
+
- A references claim whose subject key or claim text names a project file (also with a repository prefix, another letter case, no extension, or spaces) is rejected unless that file is among the files read, so a claim about one file cannot be decided on another.
|
|
64
|
+
- `excludes` now fails on a longer name ending in the excluded item at a segment boundary (`checkNotNull` excludes `Preconditions.checkNotNull`).
|
|
65
|
+
- Rust raw identifiers (`std::r#env::var`) are read as their plain names.
|
|
66
|
+
- C# method groups handed on as values (`Environment.Exit` in a switch arm, `??`, `yield return`, a lambda body, `!`) count as calls, as does a method group on any receiver (`Process.GetCurrentProcess().Kill`); an `Environment` that is not provably `System.Environment` (pattern or `out` variable, type parameter, primary-constructor parameter, alias, a project type) refuses or is read as what it binds.
|
|
67
|
+
- Go: env reads through a helper or variable in another file of the package, or in another package of the project, refuse; a package imported under a well-known name (`logrus`, `exec`) is listed by its import path.
|
|
68
|
+
- Java: a `System` class of the same package in another source root refuses `System` reads.
|
|
69
|
+
- Python: references through a module's private name (`from tempfile import _os`), reflection on a module (`getattr`, `vars`, a module assigned to a name) and ctypes or cffi refuse; `posix.X` and `nt.X` are read as `os.X`.
|
|
70
|
+
- SWI-Prolog: goals in `'~@'` message lines and in grammar bodies passed to `phrase`/`call_dcg` are read; an aliased include resolves only through the `file_search_path` declarations read, never by path suffix, and include refusals name the file to add to `files:` only when it is the exact resolution.
|
|
71
|
+
- The Cursor hook runner is a self-contained bundle, so a copied or GitHub-installed plugin no longer fails with `ERR_MODULE_NOT_FOUND`.
|
|
72
|
+
- Plugins accept kibi-plugin-sdk 0.6 as a peer.
|
|
73
|
+
|
|
74
|
+
- f9ec786: Usage telemetry events now carry a `channel`: `release` for an installed Kibi package, `source` when Kibi runs from a checkout of its own repository, or the value of `KIBI_TELEMETRY_CHANNEL` when a harness sets one (for example `agent-test`). Development and test runs can then be filtered out of real usage. Nothing else about what is sent changes.
|
|
75
|
+
- Updated dependencies [f9ec786]
|
|
76
|
+
- kibi-agent-core@0.11.1
|
|
77
|
+
|
|
78
|
+
## 2.10.1
|
|
79
|
+
|
|
80
|
+
### Patch Changes
|
|
81
|
+
|
|
82
|
+
- ce41f02: Agents now keep a signoff in the pull request it approves. The guidance used to time the signoff as an end-of-work step and said only that an agent must never set `approved_by` itself, so agents often opened the pull request first and then carried the person's answer in a separate follow-up pull request.
|
|
83
|
+
- kibi-usage 2.37.0, kibi-decisions 1.5.0 and kibi-commit-review 1.4.0 (and the signoff-views resource) say to ask for the signoff before opening or merging the pull request, and to record the person's answer (their name in `approved_by` or a `*_waiver_approved_by` field, their words in `origin.ref` or `approval_ref`) on the same branch and pull request. Writing down an answer the person gave is not self-approval. A pull request that only carries a signoff, firmness or waiver approval is ruled out.
|
|
84
|
+
- The stop-hook signoff reminder (Claude Code, Codex, Cursor, ZCode) adds the same rule. So do the `agent-requirement-unapproved` check action, the `dimension_unrepresented` repair hint and the waiver lines of `kibi proof explain`.
|
|
85
|
+
|
|
86
|
+
- 43d4366: Usage telemetry data is cleaner and more complete. Telemetry is now off when the home directory lies inside the temp directory, as eval, packaging and end-to-end harnesses set it, so each harness run no longer counts as a new install; Kibi's own eval and packed-test sandboxes also set `KIBI_TELEMETRY=0`. Events now name the host agent from `CLAUDECODE`, `CURSOR_AGENT`, `CODEX_SANDBOX`, `GEMINI_CLI`, `OPENCODE`, `AI_AGENT` or the MCP client name, carry a hashed host session id in every host, and say whether they come from a hosted cloud workspace. Failed CLI commands and MCP tool results now carry a fixed error category: the result's error code (for example `upsert_dry_run_invalid`), the failing `kibi doctor` checks, or the `kibi migrate` approval problem. `kibi migrate --apply-safe` now prints the current plan hash when the approved hash is missing or out of date.
|
|
87
|
+
|
|
88
|
+
Hooks in Claude Code, Codex, Cursor, ZCode and OpenCode now all send the anonymous `kibi_hook` event (before, only Claude Code did, and only in diagnostic mode for the others), and each hook remembers, as hashes in `telemetry-sessions.json`, which host session last ran in the workspace. CLI and MCP events take the session id from `KIBI_SESSION_ID`, `CLAUDE_CODE_SESSION_ID` or `CODEX_THREAD_ID` when the host exposes one, and otherwise from that workspace marker (at most 30 minutes old), so events from Cursor and OpenCode sessions, which expose no session variable, can be grouped too. Every host's hook also sends the queue when it is due, and right away at the end of a turn.
|
|
89
|
+
|
|
90
|
+
- Updated dependencies [ce41f02]
|
|
91
|
+
- Updated dependencies [43d4366]
|
|
92
|
+
- kibi-agent-core@0.11.0
|
|
93
|
+
|
|
3
94
|
## 2.10.0
|
|
4
95
|
|
|
5
96
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -46,12 +46,20 @@ On WSL workspaces, Cursor reads `~/.cursor/plugins/local` in your Linux home, no
|
|
|
46
46
|
npm install --save-dev kibi-cursor
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Copy the installed package into Cursor's local plugin directory (do not
|
|
49
|
+
Copy the installed package into Cursor's local plugin directory (do not
|
|
50
|
+
symlink). Remove an earlier copy first, or `cp` nests the new one inside it:
|
|
50
51
|
|
|
51
52
|
```bash
|
|
52
|
-
|
|
53
|
+
mkdir -p ~/.cursor/plugins/local
|
|
54
|
+
rm -rf ~/.cursor/plugins/local/kibi-cursor
|
|
55
|
+
cp -R "$(npm root)/kibi-cursor" ~/.cursor/plugins/local/kibi-cursor
|
|
53
56
|
```
|
|
54
57
|
|
|
58
|
+
The copy needs no `node_modules`: the hooks run `bin/hook-runner.mjs`, a
|
|
59
|
+
self-contained bundle, and the MCP launcher `bin/launch-kibi-mcp.mjs` uses
|
|
60
|
+
only Node built-ins and starts the opened project's own `kibi-mcp`. Copy again
|
|
61
|
+
after upgrading `kibi-cursor`.
|
|
62
|
+
|
|
55
63
|
## MCP configuration
|
|
56
64
|
|
|
57
65
|
The plugin bundles `mcp.json` with a thin launcher that locates and starts the
|
|
@@ -81,6 +89,16 @@ Toggle the bundled MCP server from **Settings → Features → Model Context Pro
|
|
|
81
89
|
Cursor expands `${CURSOR_PLUGIN_ROOT}` to the installed plugin directory and
|
|
82
90
|
`${workspaceFolder}` to the opened workspace before starting the command.
|
|
83
91
|
|
|
92
|
+
Unlike the Claude Code, Codex and ZCode launchers, which fall back to a
|
|
93
|
+
global `kibi-mcp` (with a notice on stderr), the Cursor plugin launcher only
|
|
94
|
+
runs the opened project's own `kibi-mcp`: Cursor starts plugin MCP servers
|
|
95
|
+
from a host-chosen directory, and a project-local package keeps the server
|
|
96
|
+
version tied to the project's `kibi-cli` and `.kb/` schema. Without one it
|
|
97
|
+
refuses with an error that says how to install it. To use a global
|
|
98
|
+
`kibi-mcp` anyway, turn off the plugin's MCP server and add the manual entry
|
|
99
|
+
below (`npx --no-install` finds a project-local install first, then a global
|
|
100
|
+
one).
|
|
101
|
+
|
|
84
102
|
Manual MCP fallback (no plugin install required):
|
|
85
103
|
|
|
86
104
|
```json
|
|
@@ -119,7 +137,7 @@ agent-plugin/
|
|
|
119
137
|
|
|
120
138
|
### Advisory editor hooks
|
|
121
139
|
|
|
122
|
-
Hooks are warning-only and never replace MCP/CLI behavior:
|
|
140
|
+
Hooks are warning-only and never replace MCP/CLI behavior. `hooks/hooks.json` runs `bin/hook-runner.mjs`, a committed self-contained bundle (with `kibi-agent-core` inlined), so a copied plugin directory or a GitHub marketplace install runs the hooks without `node_modules` or a build step:
|
|
123
141
|
|
|
124
142
|
- **sessionStart**: bootstrap reminder when `.kb/manifest.json` is missing.
|
|
125
143
|
- **preToolUse**: warns on explicit direct `.kb/**` edits without blocking. Before the first edit of a file whose symbols implement a requirement, it adds the shared Kibi snippet (from `kibi-agent-core/snippets`, the same builder the Claude Code plugin uses): the requirements and their implementing symbols, what the lead requirement must keep true and the decision behind it, the covering tests, and the symbol the edit lands in. A superseded or retired lead requirement gets no "must keep true" or "Decision" line. Once per path per session. When the edited code implements a firm requirement (a rule the person set), the message first tells the agent to ask the person before an edit that breaks it, once per requirement per conversation (Cursor does not enforce a hook's "ask"; `KIBI_CONFIRM_FIRM_EDITS=0` turns this off).
|
package/agent-plugin/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "kibi",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.11.0",
|
|
5
5
|
"description": "Repo-local, per-branch, queryable knowledge base. Portable Agent Plugin packaging Kibi's Agent Skills and MCP server.",
|
|
6
6
|
"author": { "name": "Kibi" },
|
|
7
7
|
"homepage": "https://looted.github.io/kibi/",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-commit-review
|
|
3
3
|
name: kibi-commit-review
|
|
4
4
|
description: "Use when kibi check --staged or the Kibi pre-commit hook blocks a commit (kibi_impact_evidence_missing, symbols_manifest_stale, symbol_granularity_violation, symbol-coverage, kibi check-generated drift, Staged impact review failed), or when preparing a commit or pull-request impact review. Covers reading the per-file Detail lines, authoring .kb/symbols.yaml symbol entries through kb_upsert, staging the tracked Kibi evidence with git add (symbol coordinates are derived and never staged), kb_prepare_impact_review for a staged or diff scope, the kibi signoff requirements summary to show before a commit, and how merge commits and migration renames are judged."
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.4.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -91,7 +91,11 @@ apps); never attach the `.html` as a file, which chat apps show as raw source;
|
|
|
91
91
|
past 15 requirements the Markdown stops and only the page lists them all.
|
|
92
92
|
Host details: `kibi-decisions`, `resources/signoff-views.md`. `--no-proof` skips the proof lookup, which reads coverage and is slow
|
|
93
93
|
on a large knowledge base. The summary is not an approval: never set
|
|
94
|
-
`approved_by` or `origin.ref` from it.
|
|
94
|
+
`approved_by` or `origin.ref` from it. Show it before you open or merge the
|
|
95
|
+
pull request. When the person answers, record their answer in the same
|
|
96
|
+
change (their name in `approved_by`, their words in `origin.ref` or
|
|
97
|
+
`approval_ref`) and push it to the same pull request; never move a signoff,
|
|
98
|
+
firmness or waiver approval into a separate pull request.
|
|
95
99
|
|
|
96
100
|
## Impact reviews
|
|
97
101
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-decisions
|
|
3
3
|
name: kibi-decisions
|
|
4
4
|
description: "Use when the person states a rule or decision (must, never, always, from now on, we decided), when a Kibi hook asks to confirm or warns about an edit to code that implements a firm requirement, when the Stop hook says source files changed but nothing was recorded, when kb_check quotes requirement_statement_unverified, or when a request conflicts with a recorded requirement or ADR. Covers reading firmness and origin, recording a person's rule as firm with origin.ref, superseding with a Why this changed section, the kb_compile_intent unresolved/reviewCandidates/conflictReviews loop, asking before overriding a firm rule, and putting decisions to the person as one numbered kibi signoff --questions view."
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.5.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -115,7 +115,12 @@ again until none is open, then record each rule with their words in
|
|
|
115
115
|
`origin.ref`. Fields and outputs: `resources/signoff-views.md`.
|
|
116
116
|
|
|
117
117
|
Never invent words for `origin.ref`, never set `approved_by` yourself, and
|
|
118
|
-
mark your own picks `default` and guesses `exploratory`.
|
|
118
|
+
mark your own picks `default` and guesses `exploratory`. Once the person has
|
|
119
|
+
answered, recording their answer (their name in `approved_by` or a
|
|
120
|
+
`*_waiver_approved_by` field, their words in `origin.ref` or `approval_ref`)
|
|
121
|
+
is yours to do: put it on the branch of the change it approves and push it to
|
|
122
|
+
that pull request, never to a separate one. Ask before you open or merge the
|
|
123
|
+
pull request, so the answer lands in it. Payload shapes,
|
|
119
124
|
refusal messages and the `kb_compile_intent` review loop are in
|
|
120
125
|
`resources/decision-records.md`.
|
|
121
126
|
|
|
@@ -51,7 +51,9 @@ writes and opens the page; `--format json` prints the view.
|
|
|
51
51
|
The person answers by number ("1 yes, 2 exploratory, 3 reword: ..."). Copy
|
|
52
52
|
each answer into `answer`, render again, and record the rules only once every
|
|
53
53
|
question is answered. An answer is the person's words: quote it in
|
|
54
|
-
`origin.ref`; a view itself is never an approval.
|
|
54
|
+
`origin.ref`; a view itself is never an approval. Record the answers on the
|
|
55
|
+
branch of the change they approve and push them to its pull request, never to
|
|
56
|
+
a separate one.
|
|
55
57
|
|
|
56
58
|
## Summary contents
|
|
57
59
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-usage
|
|
3
3
|
name: Kibi Usage
|
|
4
4
|
description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
|
|
5
|
-
version: 2.
|
|
5
|
+
version: 2.40.1
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -20,6 +20,7 @@ resources:
|
|
|
20
20
|
- resources/fact-lanes.md
|
|
21
21
|
- resources/workflows.md
|
|
22
22
|
- resources/proof.md
|
|
23
|
+
- resources/prolog-proofs.md
|
|
23
24
|
- resources/ui-requirements.md
|
|
24
25
|
- resources/logic-ir.md
|
|
25
26
|
- resources/kb-improvement.md
|
|
@@ -94,8 +95,10 @@ it is a pointer to the knowledge, never proof. Each line answers one question:
|
|
|
94
95
|
requirement first. Read the lead requirement with `kb_query({id})` before
|
|
95
96
|
changing behavior; add `includeBody: true` to see its authored prose, and
|
|
96
97
|
fix a stale sentence there with `kb_upsert` `document.body`.
|
|
97
|
-
- `REQ-… must keep true:
|
|
98
|
-
|
|
98
|
+
- `REQ-… must keep true: <claim>` (edit only): what the lead requirement
|
|
99
|
+
requires, as the claims its facts model (with the operator and members,
|
|
100
|
+
such as "The greet function must not call console.log") or, without
|
|
101
|
+
them, its own statement. An edit that breaks one of them is a requirement
|
|
99
102
|
change: ask before changing the rule (see Decision firmness), and never
|
|
100
103
|
code around the fact.
|
|
101
104
|
- `Decision: ADR-…` (edit only): the decision behind the lead requirement.
|
|
@@ -372,6 +375,14 @@ human sets `approved_by`; `kb_check` flags approvals an agent recorded without
|
|
|
372
375
|
corroboration. Never fill in an approval yourself. Record why a requirement
|
|
373
376
|
exists in `rationale`, or link the ADR that explains it.
|
|
374
377
|
|
|
378
|
+
A signoff belongs to the change it approves. Ask for it before you open or
|
|
379
|
+
merge the pull request, and when the person answers, record their answer
|
|
380
|
+
(`approved_by` or a `*_waiver_approved_by` field set to them, their words in
|
|
381
|
+
`origin.ref` or `approval_ref`) on the same branch and push it to the same pull
|
|
382
|
+
request. Writing down an answer the person gave is not approving it yourself.
|
|
383
|
+
Never open a separate pull request only to carry a signoff, firmness or waiver
|
|
384
|
+
approval.
|
|
385
|
+
|
|
375
386
|
## Decision firmness
|
|
376
387
|
|
|
377
388
|
Requirements and ADRs carry `firmness`, which says what you may do with them.
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# SWI-Prolog Code Proofs
|
|
2
|
+
|
|
3
|
+
Kibi reads code models from SWI-Prolog files (`.pl`, `.plt`) with the
|
|
4
|
+
SWI-Prolog it runs on (KIBI_SWIPL, the bundled runtime, then `swipl` on
|
|
5
|
+
PATH). It loads the file a symbol names (a module, or a plain file into
|
|
6
|
+
`user`) and reads a predicate named by its indicator, the symbol's title
|
|
7
|
+
(`operator/2`, `lists:append/3`, `greeting//1`) or `export`.
|
|
8
|
+
|
|
9
|
+
- A domain (`exhaustive`, `domains`, `value_source` with `measure: count`)
|
|
10
|
+
is the distinct solutions of one argument. A one-argument predicate needs
|
|
11
|
+
nothing more; for more arguments set `arg` (1-based) to the one that names
|
|
12
|
+
each member: `domain_source: {symbol_id: SYM-operator, arg: 1}` lists every
|
|
13
|
+
`Name` of `operator(Name, Description)`. A solution that leaves that
|
|
14
|
+
argument unbound refuses the read.
|
|
15
|
+
- A value (`value_source`) is the single number a constant holds:
|
|
16
|
+
`hard_limit_seconds(10).` Zero or several solutions refuse the read.
|
|
17
|
+
- A `rule` predicate is called once per row of its argument domains; success
|
|
18
|
+
is true, failure false, an exception an error row (which rejects).
|
|
19
|
+
- A `state_machine` transition table is a `Name/2` (from, to) or `Name/3`
|
|
20
|
+
(from, event, to) fact table: every solution is an edge. With `domains:
|
|
21
|
+
{state, event}` a `Name/3` next-state predicate is called on every state
|
|
22
|
+
and event. `initial_source` names an `initial/1`-style fact.
|
|
23
|
+
- A `regex` reads an atom or string constant as a `type: string` pattern;
|
|
24
|
+
Kibi decides string facts on it. Patterns for `library(pcre)` are not read.
|
|
25
|
+
|
|
26
|
+
Loading a module runs its directives, the same trust a JavaScript import
|
|
27
|
+
gives. The proof file must name the predicate and load its file
|
|
28
|
+
(`use_module`, `ensure_loaded`, `consult`, a `:- [..]` load list, or
|
|
29
|
+
`library(X)` for a pack's `prolog/X.pl`), or be the plunit `.plt` file
|
|
30
|
+
beside it; otherwise Kibi treats the domain as hand-listed. `arg` on a
|
|
31
|
+
TypeScript, JavaScript or Python export is refused, and so is `field` on a
|
|
32
|
+
Prolog predicate.
|
|
33
|
+
|
|
34
|
+
## Writing the certificate
|
|
35
|
+
|
|
36
|
+
A Prolog test writes the `kibi.code-certificate.v1` JSON into the directory
|
|
37
|
+
`kibi prove` sets in `KIBI_PROOF_CERTIFICATES`. Copy this module into the
|
|
38
|
+
project's test directory (for example `test/kibi_certificate.pl`) and call it
|
|
39
|
+
from the proof. Read members from the code with `findall/3` over the domain
|
|
40
|
+
predicate; never list them by hand.
|
|
41
|
+
|
|
42
|
+
```prolog
|
|
43
|
+
:- module(kibi_certificate,
|
|
44
|
+
[ kibi_exhaustive_certificate/5, % +TestId, +SymbolId, +Members, :Check, -Certificate
|
|
45
|
+
kibi_rule_certificate/4, % +TestId, +Domains, +Predicates, -Certificate
|
|
46
|
+
kibi_machine_certificate/5, % +TestId, +SymbolId, +Edges, +Initial, -Certificate
|
|
47
|
+
kibi_pattern_certificate/4, % +TestId, +SymbolId, +Text, -Certificate
|
|
48
|
+
kibi_write_certificate/1 % +Certificate
|
|
49
|
+
]).
|
|
50
|
+
:- use_module(library(http/json)).
|
|
51
|
+
:- use_module(library(apply)).
|
|
52
|
+
:- use_module(library(lists)).
|
|
53
|
+
|
|
54
|
+
:- meta_predicate
|
|
55
|
+
kibi_exhaustive_certificate(+, +, +, 1, -).
|
|
56
|
+
|
|
57
|
+
% Members as Kibi reads them: atoms and strings as text, numbers as written,
|
|
58
|
+
% other terms quoted.
|
|
59
|
+
kibi_text(Value, Text) :- string(Value), !, Text = Value.
|
|
60
|
+
kibi_text(Value, Text) :- atom(Value), !, atom_string(Value, Text).
|
|
61
|
+
kibi_text(Value, Text) :- number(Value), !, number_string(Value, Text).
|
|
62
|
+
kibi_text(Value, Text) :- format(string(Text), "~q", [Value]).
|
|
63
|
+
|
|
64
|
+
%! kibi_exhaustive_certificate(+TestId, +SymbolId, +Members, :Check, -Certificate)
|
|
65
|
+
%
|
|
66
|
+
% Run Check on every member of the domain and record each outcome. Read
|
|
67
|
+
% Members from the code (findall over the domain predicate), never list
|
|
68
|
+
% them by hand: Kibi re-reads the predicate and rejects a different set.
|
|
69
|
+
kibi_exhaustive_certificate(TestId, SymbolId, Members, Check, Certificate) :-
|
|
70
|
+
maplist(kibi_text, Members, Texts),
|
|
71
|
+
maplist(exhaustive_result(Check), Members, Texts, Results),
|
|
72
|
+
Certificate = _{version: "kibi.code-certificate.v1", kind: "exhaustive",
|
|
73
|
+
test_id: TestId,
|
|
74
|
+
domain: _{symbol_id: SymbolId, members: Texts},
|
|
75
|
+
results: Results}.
|
|
76
|
+
|
|
77
|
+
exhaustive_result(Check, Member, Text, Result) :-
|
|
78
|
+
catch(( call(Check, Member)
|
|
79
|
+
-> Result = _{member: Text, outcome: "passed"}
|
|
80
|
+
; Result = _{member: Text, outcome: "failed", message: "the check failed"}
|
|
81
|
+
),
|
|
82
|
+
Error,
|
|
83
|
+
( message_to_text(Error, Message),
|
|
84
|
+
Result = _{member: Text, outcome: "failed", message: Message} )).
|
|
85
|
+
|
|
86
|
+
%! kibi_rule_certificate(+TestId, +Domains, +Predicates, -Certificate)
|
|
87
|
+
%
|
|
88
|
+
% Domains is a list of domain(Type, SymbolId, Members); Predicates a list
|
|
89
|
+
% of predicate(Name, SymbolId, ArgTypes, Goal), where Goal is called with
|
|
90
|
+
% one argument per type appended and decides the row by success or
|
|
91
|
+
% failure.
|
|
92
|
+
kibi_rule_certificate(TestId, Domains, Predicates, Certificate) :-
|
|
93
|
+
findall(Type-_{symbol_id: SymbolId, members: Texts},
|
|
94
|
+
( member(domain(Type, SymbolId, Members), Domains), maplist(kibi_text, Members, Texts) ),
|
|
95
|
+
DomainPairs),
|
|
96
|
+
dict_pairs(DomainDict, _, DomainPairs),
|
|
97
|
+
findall(Name-_{symbol_id: SymbolId, arg_types: ArgTypes, rows: Rows},
|
|
98
|
+
( member(predicate(Name, SymbolId, ArgTypes, Goal), Predicates),
|
|
99
|
+
maplist(domain_members(Domains), ArgTypes, Lists),
|
|
100
|
+
findall(Row, (maplist(member, Tuple, Lists), rule_row(Goal, Tuple, Row)), Rows)
|
|
101
|
+
),
|
|
102
|
+
PredicatePairs),
|
|
103
|
+
dict_pairs(PredicateDict, _, PredicatePairs),
|
|
104
|
+
Certificate = _{version: "kibi.code-certificate.v1", kind: "rule",
|
|
105
|
+
test_id: TestId, domains: DomainDict, predicates: PredicateDict}.
|
|
106
|
+
|
|
107
|
+
domain_members(Domains, Type, Members) :-
|
|
108
|
+
memberchk(domain(Type, _, Members), Domains).
|
|
109
|
+
|
|
110
|
+
rule_row(Goal, Tuple, Row) :-
|
|
111
|
+
maplist(kibi_text, Tuple, Args),
|
|
112
|
+
Call =.. [call, Goal|Tuple],
|
|
113
|
+
catch(( once(Call) -> Row = _{args: Args, value: true} ; Row = _{args: Args, value: false} ),
|
|
114
|
+
Error,
|
|
115
|
+
( message_to_text(Error, Message), Row = _{args: Args, error: Message} )).
|
|
116
|
+
|
|
117
|
+
%! kibi_machine_certificate(+TestId, +SymbolId, +Edges, +Initial, -Certificate)
|
|
118
|
+
%
|
|
119
|
+
% Edges is the list of From-To pairs of the transition table (findall over
|
|
120
|
+
% it); Initial is the list of initial states, or [] when the contract names
|
|
121
|
+
% no initial_source.
|
|
122
|
+
kibi_machine_certificate(TestId, SymbolId, Edges, Initial, Certificate) :-
|
|
123
|
+
findall([F, T], (member(From-To, Edges), kibi_text(From, F), kibi_text(To, T)), Pairs0),
|
|
124
|
+
sort(Pairs0, Pairs),
|
|
125
|
+
findall(S, (member([A, B], Pairs), member(S, [A, B])), Ends),
|
|
126
|
+
sort(Ends, States),
|
|
127
|
+
Machine0 = _{symbol_id: SymbolId, states: States, transitions: Pairs},
|
|
128
|
+
( Initial == []
|
|
129
|
+
-> Machine = Machine0
|
|
130
|
+
; maplist(kibi_text, Initial, InitialTexts0),
|
|
131
|
+
sort(InitialTexts0, InitialTexts),
|
|
132
|
+
Machine = Machine0.put(initial, InitialTexts)
|
|
133
|
+
),
|
|
134
|
+
Certificate = _{version: "kibi.code-certificate.v1", kind: "state_machine",
|
|
135
|
+
test_id: TestId, machine: Machine}.
|
|
136
|
+
|
|
137
|
+
%! kibi_pattern_certificate(+TestId, +SymbolId, +Text, -Certificate)
|
|
138
|
+
%
|
|
139
|
+
% The string or atom constant a string claim is about.
|
|
140
|
+
kibi_pattern_certificate(TestId, SymbolId, Value, Certificate) :-
|
|
141
|
+
kibi_text(Value, Text),
|
|
142
|
+
Certificate = _{version: "kibi.code-certificate.v1", kind: "regex", test_id: TestId,
|
|
143
|
+
pattern: _{symbol_id: SymbolId, type: "string", value: Text}}.
|
|
144
|
+
|
|
145
|
+
%! kibi_write_certificate(+Certificate)
|
|
146
|
+
%
|
|
147
|
+
% Leave the certificate where kibi prove reads it (the directory in
|
|
148
|
+
% KIBI_PROOF_CERTIFICATES). Does nothing outside a kibi prove run.
|
|
149
|
+
kibi_write_certificate(Certificate) :-
|
|
150
|
+
( getenv('KIBI_PROOF_CERTIFICATES', Directory), Directory \== ''
|
|
151
|
+
-> make_directory_path(Directory),
|
|
152
|
+
format(atom(Name), "~w.json", [Certificate.test_id]),
|
|
153
|
+
directory_file_path(Directory, Name, File),
|
|
154
|
+
setup_call_cleanup(open(File, write, Out, [encoding(utf8)]),
|
|
155
|
+
json_write_dict(Out, Certificate, [width(0)]),
|
|
156
|
+
close(Out))
|
|
157
|
+
; true
|
|
158
|
+
).
|
|
159
|
+
|
|
160
|
+
message_to_text(Error, Text) :-
|
|
161
|
+
catch(message_to_codes(Error, Text), _, fail), !.
|
|
162
|
+
message_to_text(Error, Text) :- format(string(Text), "~q", [Error]).
|
|
163
|
+
|
|
164
|
+
message_to_codes(Error, Text) :-
|
|
165
|
+
'$messages':translate_message(Error, Lines, []),
|
|
166
|
+
with_output_to(string(Raw), print_message_lines(current_output, '', Lines)),
|
|
167
|
+
split_string(Raw, "", "\n ", [Text]).
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
A proof that the operators are exactly the claimed set:
|
|
171
|
+
|
|
172
|
+
```prolog
|
|
173
|
+
:- use_module('../prolog/operators').
|
|
174
|
+
:- use_module(kibi_certificate).
|
|
175
|
+
:- initialization(main, main).
|
|
176
|
+
|
|
177
|
+
main :-
|
|
178
|
+
findall(Name, operator(Name, _), Names0),
|
|
179
|
+
list_to_set(Names0, Names),
|
|
180
|
+
kibi_exhaustive_certificate('TEST-operator-set', 'SYM-operator', Names,
|
|
181
|
+
[Name]>>operator(Name, _), Certificate),
|
|
182
|
+
kibi_write_certificate(Certificate).
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Its contract: `kind: exhaustive`, `domain_source: {symbol_id: SYM-operator,
|
|
186
|
+
arg: 1}`, `proves: [CLAIM-...]`. A rule proof passes
|
|
187
|
+
`[domain(status, 'SYM-status', Statuses)]` and
|
|
188
|
+
`[predicate(detected, 'SYM-detected', [status], detected)]`; its contract
|
|
189
|
+
names `predicate_source: {detected: {symbol_id: SYM-detected}}` and
|
|
190
|
+
`domains: {status: {symbol_id: SYM-status}}`.
|
|
191
|
+
|
|
192
|
+
## Source proofs over Prolog code
|
|
193
|
+
|
|
194
|
+
A `source` proof needs no certificate: Kibi reads the Prolog files itself,
|
|
195
|
+
term by term, and never runs them. Use the `references` reader on `.pl` and
|
|
196
|
+
`.plt` files for what modules load (`extract: imports`, members as written:
|
|
197
|
+
`library(lists)`, `helpers`), call (`calls`, `Name/Arity` or
|
|
198
|
+
`Module:Name/Arity`), read from the environment (`env`, `getenv/2`), pass as
|
|
199
|
+
a first argument (`string_args` with `callee`) and throw (`thrown`: the error
|
|
200
|
+
of `error(type_error(...), _)` is `type_error`). Use the `prolog` reader for
|
|
201
|
+
facts: `{reader: prolog, file: pack.pl, functor: requires/1}` lists a pack's
|
|
202
|
+
requirements, and `arg` picks the argument of a fact with several. A goal or
|
|
203
|
+
closure held in a variable (`call(Goal)`, `maplist(G, L)`), a goal with a
|
|
204
|
+
variable module, a clause asserted from a variable and `:- if(...)` fail the
|
|
205
|
+
read with their place: name the predicate instead. The claim cannot be
|
|
206
|
+
decided for a file the read refuses; never drop a file the claim is about
|
|
207
|
+
from `files`.
|
|
@@ -262,11 +262,15 @@ left:
|
|
|
262
262
|
each list item, such as the `run` of each workflow step), `glob` (`pattern`, `capture`),
|
|
263
263
|
`openapi` (`file`, `select: operations|unsecured_operations|...`),
|
|
264
264
|
`references` (`files`, `extract: env|imports|calls|string_args|thrown|
|
|
265
|
-
property
|
|
265
|
+
property`; TypeScript, JavaScript, Python and SWI-Prolog files, and Go,
|
|
266
|
+
Java, C# and Rust files through `kibi-plugin-treesitter`), `schema` (`format: drizzle|prisma|sql`, `select:
|
|
266
267
|
tables|columns|required_columns|nullable_columns|foreign_keys`), `routes`
|
|
267
268
|
(Hono or Express app export, `path_prefix`, `names_from`, `select:
|
|
268
269
|
routes|middleware`) or `validator` (zod, valibot or pydantic, `select:
|
|
269
|
-
fields|required_fields|optional_fields`)
|
|
270
|
+
fields|required_fields|optional_fields`) or `prolog` (`file`, `functor:
|
|
271
|
+
requires/1`, `arg`: one argument of each fact, such as a pack's
|
|
272
|
+
requirements in `pack.pl`). Prolog files are read term by term and never
|
|
273
|
+
run; see `resources/prolog-proofs.md`. A member Kibi cannot read
|
|
270
274
|
statically (a computed env name, a dynamic import, an env object handed
|
|
271
275
|
to a variable) fails the read with its location, and so does an empty
|
|
272
276
|
read (a glob matching no file, migrations with no `CREATE TABLE`), so a
|
|
@@ -288,6 +292,40 @@ left:
|
|
|
288
292
|
The builders need kibi-cli 2.33.0 or newer in the project. The env
|
|
289
293
|
extractor reads `process.env`, `import.meta.env`, `Bun.env`,
|
|
290
294
|
`Deno.env.get`, `os.environ` and `os.getenv`, through casts.
|
|
295
|
+
- `references` over Go, Java, C# or Rust needs `kibi-plugin-treesitter` and
|
|
296
|
+
the language's grammar pack (`kibi-lang-go`, `kibi-lang-java`,
|
|
297
|
+
`kibi-lang-csharp`, `kibi-lang-rust`) in `devDependencies`, with the
|
|
298
|
+
plugin activated for proofs:
|
|
299
|
+
`"kibi.reference-extractor.v1": { "mode": "augment" }` in its
|
|
300
|
+
`kibi.plugins` entry (next to `"kibi.symbol-extractor.v2"`). A project
|
|
301
|
+
with no `package.json` creates one that only holds this list. Without it
|
|
302
|
+
the read is refused with what to install, so ask the operator before
|
|
303
|
+
installing; `kibi doctor` names what the project needs. Members are
|
|
304
|
+
written as the code names them (`os.Getenv`, `System.out.println`,
|
|
305
|
+
`Console.WriteLine`, `std::env::var`, Go import paths, `java.util.*`,
|
|
306
|
+
`std::io::Write`); a method on an expression Kibi cannot name is
|
|
307
|
+
`*.name`. Reflection, a name computed at run time, the whole
|
|
308
|
+
environment, C# `dynamic` and `#if`, Rust `#[cfg(...)]` items other than
|
|
309
|
+
`#[cfg(test)]`, and any macro for Rust `calls` and `thrown` fail the
|
|
310
|
+
read with their place: the claim cannot be decided for that file, so
|
|
311
|
+
model a claim the code can show (Rust `calls` over a file with
|
|
312
|
+
`println!` is always refused). Never drop a file the claim is about
|
|
313
|
+
from `files`: the read decides a claim only for the files it covers,
|
|
314
|
+
and a proof is rejected when a symbol the test covers or that
|
|
315
|
+
implements the claim's requirement lies outside them.
|
|
316
|
+
- Reference members match by name segments (`.`, `/`, `::`). `excludes`
|
|
317
|
+
fails on anything that is or may be an excluded item: `java.sql`
|
|
318
|
+
excludes `java.sql.Connection`, `github.com/labstack/echo/v4` excludes
|
|
319
|
+
`.../echo/v4/middleware`, a trailing `*` is a prefix (`java.sql.*`), a
|
|
320
|
+
wildcard import or a shorter name (`File.delete`) may be the item, and
|
|
321
|
+
`*.delete` (a call on a receiver Kibi cannot name) may be
|
|
322
|
+
`java.io.File.delete`, so the claim is refused naming it. `includes` and
|
|
323
|
+
`subset_of` count only members that certainly are an item (the item, a
|
|
324
|
+
member under `X.*`, or `a.b.m` for `*.m`); `eq` compares spellings.
|
|
325
|
+
- For a claim about what the code reads from the environment use
|
|
326
|
+
`extract: env`: `string_args` on a getter (`os.Getenv`, `env::var`,
|
|
327
|
+
`System.getenv`, `getenv/2`, ...) sees only that getter, so Kibi refuses
|
|
328
|
+
it for `eq`, `subset_of` and `excludes`.
|
|
291
329
|
|
|
292
330
|
`arithmetic` and `smt` from `constraint_source` alone prove that the
|
|
293
331
|
knowledge base's facts entail the claim, not that code follows the facts:
|
|
@@ -318,7 +356,10 @@ read for a test that wants to assert on it), fail the test when it records a fai
|
|
|
318
356
|
(`failedMembers`, `failedCalls`), and call `writeCodeCertificate`; it writes to the
|
|
319
357
|
directory `kibi prove` sets in `KIBI_PROOF_CERTIFICATES` and does nothing
|
|
320
358
|
elsewhere. A Python test writes the same `kibi.code-certificate.v1` JSON
|
|
321
|
-
to that directory itself.
|
|
359
|
+
to that directory itself. Kibi also reads SWI-Prolog domains, constants,
|
|
360
|
+
rule predicates, transition tables and string constants (`arg` picks a
|
|
361
|
+
predicate's argument); `resources/prolog-proofs.md` has the rules and a
|
|
362
|
+
certificate module to copy. Wire the KB: the proof file symbol is
|
|
322
363
|
`executable_for` the test and named in `required_proofs`; the domain symbol
|
|
323
364
|
(and every symbol a rule, machine or pattern contract names) `implements`
|
|
324
365
|
the requirement and is `covered_by` the test; the test
|