kibi-cli 2.18.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 (46) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/commands/doctor.js +11 -5
  4. package/dist/commands/sync/manifest.d.ts +10 -0
  5. package/dist/commands/sync/manifest.d.ts.map +1 -1
  6. package/dist/commands/sync/manifest.js +27 -0
  7. package/dist/commands/sync/untracked-targets.d.ts +17 -0
  8. package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
  9. package/dist/commands/sync/untracked-targets.js +43 -1
  10. package/dist/commands/sync.d.ts.map +1 -1
  11. package/dist/commands/sync.js +51 -2
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +16 -23
  14. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  15. package/dist/operations/mutation/source-authoring.js +5 -12
  16. package/dist/prolog/store-lock.d.ts +19 -0
  17. package/dist/prolog/store-lock.d.ts.map +1 -1
  18. package/dist/prolog/store-lock.js +46 -0
  19. package/dist/proof/inspect.d.ts +1 -1
  20. package/dist/proof/inspect.d.ts.map +1 -1
  21. package/dist/proof/inspect.js +9 -2
  22. package/dist/public/operations/catalog.d.ts +50 -1
  23. package/dist/public/operations/catalog.d.ts.map +1 -1
  24. package/dist/public/operations/contracts.js +1 -4
  25. package/dist/public/operations/discovery-executors.d.ts +36 -0
  26. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  27. package/dist/public/operations/discovery-executors.js +361 -1
  28. package/dist/public/operations/specs/discovery.d.ts +50 -1
  29. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  30. package/dist/public/operations/specs/discovery.js +56 -1
  31. package/dist/public/skills/kibi-usage/SKILL.md +9 -1
  32. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  33. package/dist/runtime/cli-runtime.js +7 -2
  34. package/dist/search-answer.d.ts +12 -3
  35. package/dist/search-answer.d.ts.map +1 -1
  36. package/dist/search-answer.js +51 -4
  37. package/dist/utils/content-freshness.d.ts.map +1 -1
  38. package/dist/utils/content-freshness.js +12 -2
  39. package/dist/utils/symbol-manifest-layout.d.ts +9 -0
  40. package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
  41. package/dist/utils/symbol-manifest-layout.js +18 -0
  42. package/package.json +2 -2
  43. package/src/public/operations/contracts.ts +4 -4
  44. package/src/public/operations/discovery-executors.ts +472 -0
  45. package/src/public/operations/specs/discovery.ts +67 -1
  46. package/src/public/skills/kibi-usage/SKILL.md +9 -1
@@ -9,10 +9,20 @@ const ENTITY_TYPES = [
9
9
  "symbol",
10
10
  "fact",
11
11
  ];
12
+ const FACT_KINDS = [
13
+ "subject",
14
+ "property_value",
15
+ "predicate",
16
+ "predicate_schema",
17
+ "observation",
18
+ "meta",
19
+ "rule",
20
+ "rule_schema",
21
+ ];
12
22
  export const querySpec = {
13
23
  name: "kb_query",
14
24
  cliName: "query",
15
- description: "Read entities from the KB with filters. Use for discovery and lookup before edits. Do not use for writes. No mutation side effects. Tags filter by metadata tags only, not entity IDs.",
25
+ description: "Read entities from the KB with exact filters, or ask typed fact questions. Use for lookup by id, type, tags or sourceFile, and for precise questions about modeled facts: predicate facts by name with named arguments (predicate + where), property facts of a subject (subjectKey + propertyKey), or the catalog of facts of one kind (factKind; 'predicate_schema' lists the predicates you can ask about with their argument names and constants). Typed answers return matching fact rows plus factQuery: the schema used, distinct counts, values that do exist when nothing matched, warnings about the question and the next call to make. Read-only. Tags filter by metadata tags only, not entity IDs.",
16
26
  businessInputSchema: {
17
27
  type: "object",
18
28
  properties: {
@@ -34,6 +44,51 @@ export const querySpec = {
34
44
  type: "string",
35
45
  description: "Optional source-file substring filter. Example: 'src/auth/login.ts'. Uses KB source linkage, not file-system scanning.",
36
46
  },
47
+ factKind: {
48
+ type: "string",
49
+ enum: FACT_KINDS,
50
+ description: "Typed fact filter: list facts of one kind with their typed fields. 'predicate_schema' returns the catalog of askable predicates (argument names, constants, fact counts). Example: 'predicate_schema'.",
51
+ },
52
+ predicate: {
53
+ type: "string",
54
+ description: "Typed fact filter: predicate name to ask about, e.g. 'has_role' or 'permission_rule'. Combine with where for named arguments. Returns every stored fact of that predicate with its arguments, polarity and allow/deny effect.",
55
+ },
56
+ namespace: {
57
+ type: "string",
58
+ description: "Optional predicate namespace when the same name exists in several. Default: any namespace.",
59
+ },
60
+ where: {
61
+ type: "object",
62
+ additionalProperties: { type: "string" },
63
+ description: "Named argument values for predicate, order-free, using the argument names from the predicate's schema. Example: {actor: 'admin', decision: 'allow'}. An unknown name returns a warning with the declared names; no match returns the values that do exist.",
64
+ },
65
+ args: {
66
+ type: "array",
67
+ items: { type: ["string", "null"] },
68
+ description: "Positional argument values for predicate when it has no schema; null matches any value. Example: ['admin', null, null, 'allow']. Prefer where.",
69
+ },
70
+ polarity: {
71
+ type: "string",
72
+ enum: ["assert", "deny"],
73
+ description: "Optional polarity filter for predicate facts. Default: both.",
74
+ },
75
+ scope: {
76
+ type: "string",
77
+ enum: ["all", "current_requirements"],
78
+ description: "Optional: 'current_requirements' keeps only facts a current (not superseded or retired) requirement requires.",
79
+ },
80
+ distinct: {
81
+ type: "string",
82
+ description: "Optional argument name to count distinct values of across the matching predicate facts, e.g. distinct: 'role' with predicate 'has_role' and where {user: 'alice'} answers how many roles alice has.",
83
+ },
84
+ subjectKey: {
85
+ type: "string",
86
+ description: "Typed fact filter: dotted subject key whose property facts to list, e.g. 'user.role_assignment'. Combine with propertyKey for one property.",
87
+ },
88
+ propertyKey: {
89
+ type: "string",
90
+ description: "Optional property key under subjectKey, e.g. 'max_roles'. Returns operator, value and unit of each fact.",
91
+ },
37
92
  limit: {
38
93
  type: "number",
39
94
  default: 100,
@@ -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.14.0
5
+ version: 2.15.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -409,6 +409,14 @@ Prolog `symbol-coverage` also requires the test to `validates` the requirement
409
409
  enough. Do not rely on legacy `// implements REQ-<area>-<behavior>` comments as the
410
410
  traceability record.
411
411
 
412
+ Set the symbol's `title` to the behavioral member the extractor can locate in
413
+ `sourceFile`: `ClassName.methodName`, an exported function, or a class, not a
414
+ file or module name. A traceability link (`implements`, `covered_by`, …) from
415
+ a symbol whose `title` names no such member is refused by the granularity gate
416
+ when the file already has granular behavioral symbols, unless the symbol sets
417
+ `granularity_reason` to `config-artifact`, `module-level-behavior`,
418
+ `extractor-miss`, `legacy-link` or `test-suite`.
419
+
412
420
  ## Complete Logical Coverage
413
421
 
414
422
  Decompose all atomic normative clauses into `claim_key`/`claim_text` entries in
@@ -1 +1 @@
1
- {"version":3,"file":"cli-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/cli-runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAKV,gBAAgB,EAEhB,cAAc,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAC;AAOrC,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,qBAAqB,GAChC,MAAM,CAOR;AA2GD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,cAAmB,GAC3B,gBAAgB,CA4IlB"}
1
+ {"version":3,"file":"cli-runtime.d.ts","sourceRoot":"","sources":["../../src/runtime/cli-runtime.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAKV,gBAAgB,EAEhB,cAAc,EACf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAEL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAC;AAOrC,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,qBAAqB,GAChC,MAAM,CAOR;AA2GD,wBAAgB,gBAAgB,CAC9B,OAAO,GAAE,cAAmB,GAC3B,gBAAgB,CAgJlB"}
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { retryAttachAfterBreakingStaleLock } from "../prolog/store-lock.js";
2
+ import { noteDeadHolderBeforeAttach, retryAttachAfterBreakingStaleLock, } from "../prolog/store-lock.js";
3
3
  import { EngineClient } from "../engine.js";
4
4
  import { CapabilityRegistryCache, ensureCapabilityRegistry, } from "../plugins/registry.js";
5
5
  import { nodeFilesystem, nodeGit, nodeNetwork, } from "../public/operations/node-ports.js";
@@ -186,8 +186,13 @@ export function createCliRuntime(options = {}) {
186
186
  await prolog.start?.();
187
187
  const kbPath = attachment.storePath;
188
188
  if (!usesEngine) {
189
+ // implements REQ-core-journaled-engine-persistence
190
+ // A killed holder never makes kb_attach fail, so note it first.
191
+ const recordTakeover = noteDeadHolderBeforeAttach(kbPath);
189
192
  let attached = await prolog.query(`kb_attach('${quoteProlog(kbPath)}')`);
190
- if (!attached.success) {
193
+ if (attached.success)
194
+ recordTakeover();
195
+ else {
191
196
  attached = await retryAttachAfterBreakingStaleLock(prolog, kbPath, attached);
192
197
  }
193
198
  if (!attached.success) {
@@ -66,12 +66,21 @@ export type SearchAnswerVerdict = Readonly<{
66
66
  export type SearchAnswerException = SearchAnswerEntity & Readonly<{
67
67
  approvedBy?: string;
68
68
  }>;
69
+ /**
70
+ * A linked fact says what it states in its typed form, so an agent can read
71
+ * `has_role(alice, admin)` or `user.role_assignment max_roles lte 2` without
72
+ * opening the entity, and `ask` is the exact kb_query that answers precise
73
+ * questions about the same predicate or subject.
74
+ */
75
+ export type SearchAnswerFact = SearchAnswerEntity & Readonly<{
76
+ factKind?: string;
77
+ claim?: string;
78
+ ask?: string;
79
+ }>;
69
80
  export type SearchAnswerRequirement = SearchAnswerEntity & Readonly<{
70
81
  score: number;
71
82
  via: string;
72
- facts: readonly (SearchAnswerEntity & {
73
- factKind?: string;
74
- })[];
83
+ facts: readonly SearchAnswerFact[];
75
84
  scenarios: readonly SearchAnswerEntity[];
76
85
  tests: readonly SearchAnswerTest[];
77
86
  adrs: readonly SearchAnswerEntity[];
@@ -1 +1 @@
1
- {"version":3,"file":"search-answer.d.ts","sourceRoot":"","sources":["../src/search-answer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-answer.d.ts","sourceRoot":"","sources":["../src/search-answer.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,19 @@ const ROW_KEYS = [
43
43
  "approved_by",
44
44
  "source",
45
45
  "sourceFile",
46
+ "predicate_namespace",
47
+ "predicate_name",
48
+ "predicate_args",
49
+ "polarity",
50
+ "subject_key",
51
+ "property_key",
52
+ "operator",
53
+ "value_type",
54
+ "value_string",
55
+ "value_int",
56
+ "value_number",
57
+ "value_bool",
58
+ "unit",
46
59
  ];
47
60
  // implements REQ-kibi-search-answer-layer-v2
48
61
  export const SEARCH_ANSWER_LIMITS = {
@@ -76,6 +89,40 @@ function brief(entity) {
76
89
  ...(status ? { status } : {}),
77
90
  };
78
91
  }
92
+ function queryCall(args) {
93
+ return `kb_query(${JSON.stringify(args)})`;
94
+ }
95
+ /** Typed statement and follow-up query of a linked fact, by fact kind. */
96
+ // implements REQ-kibi-query-fact-filters
97
+ function factDetail(fact) {
98
+ const factKind = text(fact.fact_kind);
99
+ const base = factKind ? { factKind } : {};
100
+ const predicate = text(fact.predicate_name);
101
+ if (factKind === "predicate" && predicate) {
102
+ const args = Array.isArray(fact.predicate_args)
103
+ ? fact.predicate_args.map(text).join(", ")
104
+ : text(fact.predicate_args);
105
+ const polarity = text(fact.polarity) === "deny" ? " [deny]" : "";
106
+ return {
107
+ ...base,
108
+ claim: `${predicate}(${args})${polarity}`,
109
+ ask: queryCall({ predicate }),
110
+ };
111
+ }
112
+ const subjectKey = text(fact.subject_key);
113
+ const propertyKey = text(fact.property_key);
114
+ if (factKind === "property_value" && subjectKey && propertyKey) {
115
+ const unit = text(fact.unit);
116
+ const valueType = text(fact.value_type) || "string";
117
+ const value = text(fact[`value_${valueType}`]);
118
+ const claim = `${subjectKey} ${propertyKey} ${text(fact.operator) || "eq"} ${value}${unit ? ` ${unit}` : ""}`;
119
+ return { ...base, claim, ask: queryCall({ subjectKey, propertyKey }) };
120
+ }
121
+ if (factKind === "subject" && subjectKey) {
122
+ return { ...base, claim: subjectKey, ask: queryCall({ subjectKey }) };
123
+ }
124
+ return base;
125
+ }
79
126
  function edgesGoal(ids) {
80
127
  const idList = ids.map((id) => `'${escapeAtom(id)}'`).join(",");
81
128
  return `findall([Rel,From,To], (member(Rel, [${ANSWER_RELATIONSHIPS.join(",")}]), kb_relationship(Rel, From, To), (member(From, [${idList}]); member(To, [${idList}]))), Edges)`;
@@ -666,10 +713,10 @@ export async function buildSearchAnswer(prolog, matches, options = {}) {
666
713
  ...brief(req.entity),
667
714
  score: req.score,
668
715
  via: req.via,
669
- facts: rowsOf(plan.facts).map((fact) => {
670
- const factKind = text(fact.fact_kind);
671
- return { ...brief(fact), ...(factKind ? { factKind } : {}) };
672
- }),
716
+ facts: rowsOf(plan.facts).map((fact) => ({
717
+ ...brief(fact),
718
+ ...factDetail(fact),
719
+ })),
673
720
  scenarios: rowsOf(plan.scenarios).map(brief),
674
721
  tests: rowsOf(testIds[index] ?? []).map((row) => ({
675
722
  ...brief(row),
@@ -1 +1 @@
1
- {"version":3,"file":"content-freshness.d.ts","sourceRoot":"","sources":["../../src/utils/content-freshness.ts"],"names":[],"mappings":"AAYA,uDAAuD;AACvD,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAIxD,CAAC;AAEH,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AA6IhD;;;GAGG;AAEH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBvC"}
1
+ {"version":3,"file":"content-freshness.d.ts","sourceRoot":"","sources":["../../src/utils/content-freshness.ts"],"names":[],"mappings":"AAYA,uDAAuD;AACvD,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CAIxD,CAAC;AAEH,KAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAyJhD;;;GAGG;AAEH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBvC"}
@@ -7,7 +7,7 @@ import { existsSync, readFileSync } from "node:fs";
7
7
  import * as path from "node:path";
8
8
  import { load as parseYAML } from "js-yaml";
9
9
  import { hashManifestWithCoordinates } from "../commands/sync/cache.js";
10
- import { isSymbolsManifestPath } from "./kb-paths.js";
10
+ import { KB_PATHS, isSymbolsManifestPath } from "./kb-paths.js";
11
11
  /** Stale-reason codes that compare file times only. */
12
12
  export const MTIME_SOURCE_REASON_CODES = new Set([
13
13
  "indexed_source_newer",
@@ -35,6 +35,7 @@ function record(value) {
35
35
  * source hashes, and the per-symbol `sourceHash` of the generated coordinate
36
36
  * artifact. A file whose bytes match that evidence has not changed since it
37
37
  * was compiled, however new its mtime (a fresh clone, a checkout, a touch).
38
+ * Without either kind of evidence a reason is kept.
38
39
  */
39
40
  class CompiledContentEvidence {
40
41
  workspaceRoot;
@@ -85,10 +86,19 @@ class CompiledContentEvidence {
85
86
  if (this.coordinateHashes)
86
87
  return this.coordinateHashes;
87
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
+ ]);
88
96
  for (const key of Object.keys(this.hashes)) {
89
97
  if (!isSymbolsManifestPath(key) || !key.endsWith("symbols.yaml"))
90
98
  continue;
91
- const artifact = path.resolve(this.workspaceRoot, path.dirname(key), "symbol-coordinates.yaml");
99
+ artifacts.add(path.resolve(this.workspaceRoot, path.dirname(key), "symbol-coordinates.yaml"));
100
+ }
101
+ for (const artifact of artifacts) {
92
102
  if (!existsSync(artifact))
93
103
  continue;
94
104
  try {
@@ -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.18.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.19.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({ 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,