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.
Files changed (80) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/cli-register-proof.js +9 -1
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +18 -6
  6. package/dist/commands/sync/manifest.d.ts +10 -0
  7. package/dist/commands/sync/manifest.d.ts.map +1 -1
  8. package/dist/commands/sync/manifest.js +27 -0
  9. package/dist/commands/sync/untracked-targets.d.ts +17 -0
  10. package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
  11. package/dist/commands/sync/untracked-targets.js +81 -9
  12. package/dist/commands/sync.d.ts.map +1 -1
  13. package/dist/commands/sync.js +76 -6
  14. package/dist/engine.d.ts +4 -0
  15. package/dist/engine.d.ts.map +1 -1
  16. package/dist/engine.js +56 -33
  17. package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
  18. package/dist/operations/modeling/predicate-applicability.js +5 -2
  19. package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
  20. package/dist/operations/modeling/predicate-catalog-4.js +4 -0
  21. package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
  22. package/dist/operations/modeling/predicate-loader.js +3 -0
  23. package/dist/operations/modeling/predicate-score-3.js +1 -1
  24. package/dist/operations/modeling/predicate-types.d.ts +5 -0
  25. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  26. package/dist/operations/mutation/source-authoring.d.ts +9 -0
  27. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  28. package/dist/operations/mutation/source-authoring.js +30 -1
  29. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  30. package/dist/operations/planning/apply-plan.js +10 -1
  31. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  32. package/dist/operations/semantic-advisor/clauses.js +7 -1
  33. package/dist/prolog/store-lock.d.ts +41 -0
  34. package/dist/prolog/store-lock.d.ts.map +1 -1
  35. package/dist/prolog/store-lock.js +91 -1
  36. package/dist/prolog.d.ts +7 -1
  37. package/dist/prolog.d.ts.map +1 -1
  38. package/dist/prolog.js +21 -3
  39. package/dist/proof/inspect.d.ts +8 -1
  40. package/dist/proof/inspect.d.ts.map +1 -1
  41. package/dist/proof/inspect.js +19 -2
  42. package/dist/public/operations/catalog.d.ts +50 -1
  43. package/dist/public/operations/catalog.d.ts.map +1 -1
  44. package/dist/public/operations/contracts.d.ts.map +1 -1
  45. package/dist/public/operations/contracts.js +4 -4
  46. package/dist/public/operations/discovery-executors.d.ts +50 -1
  47. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  48. package/dist/public/operations/discovery-executors.js +418 -8
  49. package/dist/public/operations/migration-plan.d.ts +2 -0
  50. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  51. package/dist/public/operations/migration-plan.js +31 -0
  52. package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
  53. package/dist/public/operations/source-relationship-parity.js +55 -1
  54. package/dist/public/operations/specs/discovery.d.ts +50 -1
  55. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  56. package/dist/public/operations/specs/discovery.js +56 -1
  57. package/dist/public/skills/kibi-usage/SKILL.md +20 -3
  58. package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
  59. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  60. package/dist/runtime/cli-runtime.js +7 -2
  61. package/dist/search-answer.d.ts +12 -3
  62. package/dist/search-answer.d.ts.map +1 -1
  63. package/dist/search-answer.js +51 -4
  64. package/dist/utils/branch-store.d.ts +8 -0
  65. package/dist/utils/branch-store.d.ts.map +1 -1
  66. package/dist/utils/branch-store.js +27 -1
  67. package/dist/utils/content-freshness.d.ts +13 -0
  68. package/dist/utils/content-freshness.d.ts.map +1 -0
  69. package/dist/utils/content-freshness.js +171 -0
  70. package/dist/utils/symbol-manifest-layout.d.ts +9 -0
  71. package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
  72. package/dist/utils/symbol-manifest-layout.js +18 -0
  73. package/package.json +2 -2
  74. package/src/public/operations/contracts.ts +7 -4
  75. package/src/public/operations/discovery-executors.ts +556 -6
  76. package/src/public/operations/migration-plan.ts +44 -0
  77. package/src/public/operations/source-relationship-parity.ts +60 -3
  78. package/src/public/operations/specs/discovery.ts +67 -1
  79. package/src/public/skills/kibi-usage/SKILL.md +20 -3
  80. 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.17.0",
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.18.2",
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({ entities: recordArray, count: integerValue }, [
445
- "entities",
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,