kibi-cli 2.17.0 → 2.19.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/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/cli-register-proof.js +9 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -6
- package/dist/commands/sync/manifest.d.ts +10 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +27 -0
- package/dist/commands/sync/untracked-targets.d.ts +17 -0
- package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
- package/dist/commands/sync/untracked-targets.js +81 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +76 -6
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +56 -33
- package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applicability.js +5 -2
- package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-catalog-4.js +4 -0
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +3 -0
- package/dist/operations/modeling/predicate-score-3.js +1 -1
- package/dist/operations/modeling/predicate-types.d.ts +5 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.d.ts +9 -0
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +30 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +10 -1
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- package/dist/prolog/store-lock.d.ts +41 -0
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/prolog/store-lock.js +91 -1
- package/dist/prolog.d.ts +7 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +21 -3
- package/dist/proof/inspect.d.ts +8 -1
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +19 -2
- package/dist/public/operations/catalog.d.ts +50 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +4 -4
- package/dist/public/operations/discovery-executors.d.ts +50 -1
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +418 -8
- package/dist/public/operations/migration-plan.d.ts +2 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +31 -0
- package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
- package/dist/public/operations/source-relationship-parity.js +55 -1
- package/dist/public/operations/specs/discovery.d.ts +50 -1
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +56 -1
- package/dist/public/skills/kibi-usage/SKILL.md +20 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
- package/dist/runtime/cli-runtime.d.ts.map +1 -1
- package/dist/runtime/cli-runtime.js +7 -2
- package/dist/search-answer.d.ts +12 -3
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +51 -4
- package/dist/utils/branch-store.d.ts +8 -0
- package/dist/utils/branch-store.d.ts.map +1 -1
- package/dist/utils/branch-store.js +27 -1
- package/dist/utils/content-freshness.d.ts +13 -0
- package/dist/utils/content-freshness.d.ts.map +1 -0
- package/dist/utils/content-freshness.js +171 -0
- package/dist/utils/symbol-manifest-layout.d.ts +9 -0
- package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
- package/dist/utils/symbol-manifest-layout.js +18 -0
- package/package.json +2 -2
- package/src/public/operations/contracts.ts +7 -4
- package/src/public/operations/discovery-executors.ts +556 -6
- package/src/public/operations/migration-plan.ts +44 -0
- package/src/public/operations/source-relationship-parity.ts +60 -3
- package/src/public/operations/specs/discovery.ts +67 -1
- package/src/public/skills/kibi-usage/SKILL.md +20 -3
- package/src/public/skills/kibi-usage/resources/proof.md +23 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
*/
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { load as parseYAML } from "js-yaml";
|
|
9
|
+
import { hashManifestWithCoordinates } from "../commands/sync/cache.js";
|
|
10
|
+
import { KB_PATHS, isSymbolsManifestPath } from "./kb-paths.js";
|
|
11
|
+
/** Stale-reason codes that compare file times only. */
|
|
12
|
+
export const MTIME_SOURCE_REASON_CODES = new Set([
|
|
13
|
+
"indexed_source_newer",
|
|
14
|
+
"knowledge_source_newer",
|
|
15
|
+
"documentation_source_newer",
|
|
16
|
+
]);
|
|
17
|
+
function sha256(bytes) {
|
|
18
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
19
|
+
}
|
|
20
|
+
function readJson(file) {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function record(value) {
|
|
29
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
30
|
+
? value
|
|
31
|
+
: null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Content evidence recorded by the last compilation: the store's sync-cache
|
|
35
|
+
* source hashes, and the per-symbol `sourceHash` of the generated coordinate
|
|
36
|
+
* artifact. A file whose bytes match that evidence has not changed since it
|
|
37
|
+
* was compiled, however new its mtime (a fresh clone, a checkout, a touch).
|
|
38
|
+
* Without either kind of evidence a reason is kept.
|
|
39
|
+
*/
|
|
40
|
+
class CompiledContentEvidence {
|
|
41
|
+
workspaceRoot;
|
|
42
|
+
hashes;
|
|
43
|
+
coordinateHashes = null;
|
|
44
|
+
manifestState = new Map();
|
|
45
|
+
constructor(workspaceRoot, storePath) {
|
|
46
|
+
this.workspaceRoot = workspaceRoot;
|
|
47
|
+
const cache = record(readJson(path.join(storePath, "sync-cache.json")));
|
|
48
|
+
const hashes = record(cache?.hashes) ?? {};
|
|
49
|
+
this.hashes = Object.fromEntries(Object.entries(hashes).filter((entry) => typeof entry[1] === "string"));
|
|
50
|
+
}
|
|
51
|
+
fileHash(relative) {
|
|
52
|
+
try {
|
|
53
|
+
return sha256(readFileSync(path.resolve(this.workspaceRoot, relative)));
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
manifest(key) {
|
|
60
|
+
const cached = this.manifestState.get(key);
|
|
61
|
+
if (cached)
|
|
62
|
+
return cached;
|
|
63
|
+
const absolute = path.resolve(this.workspaceRoot, key);
|
|
64
|
+
let state = { matches: false, sources: new Set() };
|
|
65
|
+
try {
|
|
66
|
+
const matches = hashManifestWithCoordinates(this.workspaceRoot, absolute, path.join(path.dirname(absolute), "symbol-coordinates.yaml")) === this.hashes[key];
|
|
67
|
+
const parsed = record(parseYAML(readFileSync(absolute, "utf8")));
|
|
68
|
+
const sources = new Set();
|
|
69
|
+
for (const symbol of Array.isArray(parsed?.symbols)
|
|
70
|
+
? parsed.symbols
|
|
71
|
+
: []) {
|
|
72
|
+
const row = record(symbol);
|
|
73
|
+
const source = row?.sourceFile ?? row?.source;
|
|
74
|
+
if (typeof source === "string" && source.length > 0)
|
|
75
|
+
sources.add(source.replaceAll("\\", "/"));
|
|
76
|
+
}
|
|
77
|
+
state = { matches, sources };
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// An unreadable manifest proves nothing; keep the reason.
|
|
81
|
+
}
|
|
82
|
+
this.manifestState.set(key, state);
|
|
83
|
+
return state;
|
|
84
|
+
}
|
|
85
|
+
coordinates() {
|
|
86
|
+
if (this.coordinateHashes)
|
|
87
|
+
return this.coordinateHashes;
|
|
88
|
+
const byFile = new Map();
|
|
89
|
+
// The coordinate artifact is content evidence in its own right: kb_upsert
|
|
90
|
+
// and sync record each symbol's source hash there. Consult the canonical
|
|
91
|
+
// artifact even when its manifest was never hashed (an untracked,
|
|
92
|
+
// upsert-only `.kb/symbols.yaml` that sync could not see).
|
|
93
|
+
const artifacts = new Set([
|
|
94
|
+
path.resolve(this.workspaceRoot, KB_PATHS.symbolCoordinates),
|
|
95
|
+
]);
|
|
96
|
+
for (const key of Object.keys(this.hashes)) {
|
|
97
|
+
if (!isSymbolsManifestPath(key) || !key.endsWith("symbols.yaml"))
|
|
98
|
+
continue;
|
|
99
|
+
artifacts.add(path.resolve(this.workspaceRoot, path.dirname(key), "symbol-coordinates.yaml"));
|
|
100
|
+
}
|
|
101
|
+
for (const artifact of artifacts) {
|
|
102
|
+
if (!existsSync(artifact))
|
|
103
|
+
continue;
|
|
104
|
+
try {
|
|
105
|
+
const parsed = record(parseYAML(readFileSync(artifact, "utf8")));
|
|
106
|
+
for (const value of Object.values(record(parsed?.coordinates) ?? {})) {
|
|
107
|
+
const row = record(value);
|
|
108
|
+
if (typeof row?.sourceFile !== "string" ||
|
|
109
|
+
typeof row.sourceHash !== "string")
|
|
110
|
+
continue;
|
|
111
|
+
const hashes = byFile.get(row.sourceFile) ?? new Set();
|
|
112
|
+
hashes.add(row.sourceHash);
|
|
113
|
+
byFile.set(row.sourceFile, hashes);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// A malformed artifact proves nothing.
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.coordinateHashes = byFile;
|
|
121
|
+
return byFile;
|
|
122
|
+
}
|
|
123
|
+
/** True only when recorded evidence proves the file's bytes are unchanged. */
|
|
124
|
+
unchanged(relative) {
|
|
125
|
+
const key = relative.replaceAll("\\", "/");
|
|
126
|
+
if (isSymbolsManifestPath(key) && this.hashes[key] !== undefined)
|
|
127
|
+
return this.manifest(key).matches;
|
|
128
|
+
const current = this.fileHash(key);
|
|
129
|
+
if (current === null)
|
|
130
|
+
return false;
|
|
131
|
+
const cached = this.hashes[key];
|
|
132
|
+
if (cached !== undefined)
|
|
133
|
+
return cached === current;
|
|
134
|
+
const coordinate = this.coordinates().get(key);
|
|
135
|
+
if (coordinate !== undefined && coordinate.size > 0)
|
|
136
|
+
return [...coordinate].every((hash) => hash === current);
|
|
137
|
+
// A code file that is only referenced from a symbol manifest is covered
|
|
138
|
+
// by that manifest's composite hash, which includes every referenced
|
|
139
|
+
// source's bytes.
|
|
140
|
+
return Object.keys(this.hashes).some((manifestKey) => {
|
|
141
|
+
if (!isSymbolsManifestPath(manifestKey))
|
|
142
|
+
return false;
|
|
143
|
+
const manifest = this.manifest(manifestKey);
|
|
144
|
+
return manifest.sources.has(key) && manifest.matches;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Drop mtime-only stale reasons whose file content provably matches what the
|
|
150
|
+
* store compiled. Reasons without such evidence are kept.
|
|
151
|
+
*/
|
|
152
|
+
// implements REQ-cli-status-pre-first-sync
|
|
153
|
+
export function confirmSourceNewerReasons(workspaceRoot, storePath, reasons) {
|
|
154
|
+
const kept = [];
|
|
155
|
+
const dropped = [];
|
|
156
|
+
let evidence = null;
|
|
157
|
+
for (const reason of reasons) {
|
|
158
|
+
if (typeof reason.code !== "string" ||
|
|
159
|
+
!MTIME_SOURCE_REASON_CODES.has(reason.code) ||
|
|
160
|
+
typeof reason.path !== "string") {
|
|
161
|
+
kept.push(reason);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
evidence ??= new CompiledContentEvidence(workspaceRoot, storePath);
|
|
165
|
+
if (evidence.unchanged(reason.path))
|
|
166
|
+
dropped.push(reason);
|
|
167
|
+
else
|
|
168
|
+
kept.push(reason);
|
|
169
|
+
}
|
|
170
|
+
return { kept, dropped };
|
|
171
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Document } from "yaml";
|
|
2
|
+
/**
|
|
3
|
+
* Render the `symbols` sequence of a parsed manifest, and every collection
|
|
4
|
+
* inside it, as block YAML. `kibi init` writes `symbols: []`, a flow
|
|
5
|
+
* sequence, and kibi-cli before 2.18.0 appended to it, so older manifests
|
|
6
|
+
* keep every symbol on that one line. Returns true when the layout changed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function blockifySymbolManifest(doc: Document): boolean;
|
|
9
|
+
//# sourceMappingURL=symbol-manifest-layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbol-manifest-layout.d.ts","sourceRoot":"","sources":["../../src/utils/symbol-manifest-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAA8B,MAAM,MAAM,CAAC;AAEjE;;;;;GAKG;AAEH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAO7D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { isCollection, isSeq, visit } from "yaml";
|
|
2
|
+
/**
|
|
3
|
+
* Render the `symbols` sequence of a parsed manifest, and every collection
|
|
4
|
+
* inside it, as block YAML. `kibi init` writes `symbols: []`, a flow
|
|
5
|
+
* sequence, and kibi-cli before 2.18.0 appended to it, so older manifests
|
|
6
|
+
* keep every symbol on that one line. Returns true when the layout changed.
|
|
7
|
+
*/
|
|
8
|
+
// implements REQ-kibi-hooks-flow-manifest, REQ-core-persistence
|
|
9
|
+
export function blockifySymbolManifest(doc) {
|
|
10
|
+
const symbols = doc.get("symbols", true);
|
|
11
|
+
if (!isSeq(symbols) || symbols.flow !== true)
|
|
12
|
+
return false;
|
|
13
|
+
visit(symbols, (_key, node) => {
|
|
14
|
+
if (isCollection(node))
|
|
15
|
+
node.flow = false;
|
|
16
|
+
});
|
|
17
|
+
return true;
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"fast-glob": "^3.2.12",
|
|
236
236
|
"gray-matter": "^4.0.3",
|
|
237
237
|
"js-yaml": "^4.1.0",
|
|
238
|
-
"kibi-core": "^0.
|
|
238
|
+
"kibi-core": "^0.20.0",
|
|
239
239
|
"kibi-plugin-builtin": "^0.5.0",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
@@ -441,10 +441,10 @@ const BASE_DATA_SCHEMAS: Readonly<
|
|
|
441
441
|
sourceType: stringValue,
|
|
442
442
|
}),
|
|
443
443
|
kb_skills_read: objectData({ content: stringValue }, ["content"]),
|
|
444
|
-
kb_query: objectData(
|
|
445
|
-
|
|
446
|
-
"count",
|
|
447
|
-
|
|
444
|
+
kb_query: objectData(
|
|
445
|
+
{ entities: recordArray, count: integerValue, factQuery: recordValue },
|
|
446
|
+
["entities", "count"],
|
|
447
|
+
),
|
|
448
448
|
kb_search: objectData(
|
|
449
449
|
{
|
|
450
450
|
results: valueArray,
|
|
@@ -452,6 +452,8 @@ const BASE_DATA_SCHEMAS: Readonly<
|
|
|
452
452
|
truncated: booleanValue,
|
|
453
453
|
queryAnalysis: recordValue,
|
|
454
454
|
answer: recordValue,
|
|
455
|
+
branch: { type: ["string", "null"] },
|
|
456
|
+
branchStore: recordValue,
|
|
455
457
|
},
|
|
456
458
|
["results", "count"],
|
|
457
459
|
),
|
|
@@ -476,6 +478,7 @@ const BASE_DATA_SCHEMAS: Readonly<
|
|
|
476
478
|
staleReasons: recordArray,
|
|
477
479
|
staleReasonCount: integerValue,
|
|
478
480
|
staleReasonsTruncated: booleanValue,
|
|
481
|
+
notices: recordArray,
|
|
479
482
|
branchAttachment: recordValue,
|
|
480
483
|
proofSnapshotChanges: recordArray,
|
|
481
484
|
proofSnapshotChangeCount: integerValue,
|