kibi-cli 2.22.0 → 2.23.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-register-foundation.d.ts.map +1 -1
- package/dist/cli-register-foundation.js +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +51 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +40 -0
- package/dist/commands/init-helpers.d.ts +2 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +3 -0
- package/dist/commands/migrate.d.ts +47 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +82 -15
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +15 -1
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +37 -35
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +32 -4
- package/dist/operations/bootstrap/activation.d.ts +7 -1
- package/dist/operations/bootstrap/activation.d.ts.map +1 -1
- package/dist/operations/bootstrap/activation.js +27 -2
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +56 -16
- package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance-actions.js +14 -2
- package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
- package/dist/operations/bootstrap/guidance.js +6 -2
- package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
- package/dist/operations/bootstrap/presentation.js +75 -9
- package/dist/operations/bootstrap/types.d.ts +6 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/migration/kb-sources.d.ts +8 -5
- package/dist/operations/migration/kb-sources.d.ts.map +1 -1
- package/dist/operations/migration/kb-sources.js +50 -14
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +11 -2
- package/dist/operations/migration/source-paths.d.ts +18 -1
- package/dist/operations/migration/source-paths.d.ts.map +1 -1
- package/dist/operations/migration/source-paths.js +111 -16
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +22 -7
- package/dist/operations/mutation/requirement-semantics.d.ts +16 -0
- package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -1
- package/dist/operations/mutation/requirement-semantics.js +50 -0
- package/dist/operations/mutation/strict-fact.js +1 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +2 -1
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +43 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +38 -9
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +49 -17
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/ingestion-boundary.js +25 -1
- package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts +19 -0
- package/dist/operations/semantic-advisor/stored-ledger-drift.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/stored-ledger-drift.js +78 -0
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +7 -1
- package/dist/public/impact/diagnostics.d.ts.map +1 -1
- package/dist/public/impact/diagnostics.js +16 -3
- package/dist/public/impact/types.d.ts +2 -0
- package/dist/public/impact/types.d.ts.map +1 -1
- package/dist/public/operations/catalog.d.ts +7 -2
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +10 -3
- package/dist/public/operations/check-helpers.d.ts +10 -0
- package/dist/public/operations/check-helpers.d.ts.map +1 -1
- package/dist/public/operations/check-helpers.js +54 -0
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +1 -0
- package/dist/public/operations/decision-firmness.d.ts +3 -1
- package/dist/public/operations/decision-firmness.d.ts.map +1 -1
- package/dist/public/operations/decision-firmness.js +13 -3
- package/dist/public/operations/discovery-executors.d.ts +18 -0
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +77 -3
- package/dist/public/operations/migration-plan.d.ts +13 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +29 -1
- package/dist/public/operations/schema6-check-actions.d.ts +4 -0
- package/dist/public/operations/schema6-check-actions.d.ts.map +1 -1
- package/dist/public/operations/schema6-check-actions.js +39 -2
- package/dist/public/operations/source-path-dangling.d.ts +1 -1
- package/dist/public/operations/source-path-dangling.d.ts.map +1 -1
- package/dist/public/operations/source-path-dangling.js +11 -2
- package/dist/public/operations/specs/composite.d.ts +1 -1
- package/dist/public/operations/specs/discovery.d.ts +5 -0
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +5 -0
- package/dist/public/operations/specs/planning.d.ts +1 -1
- package/dist/public/operations/specs/planning.js +1 -1
- package/dist/public/operations/specs/reporting.d.ts.map +1 -1
- package/dist/public/operations/specs/reporting.js +6 -4
- package/dist/public/operations/specs/semantic.d.ts +1 -1
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +30 -3
- package/dist/public/skills/kibi-bootstrap/SKILL.md +3 -2
- package/dist/public/skills/kibi-usage/SKILL.md +13 -5
- package/dist/public/skills/kibi-usage/resources/workflows.md +3 -1
- package/dist/search-answer.d.ts +14 -0
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +42 -4
- package/dist/traceability/git-change-snapshot.d.ts +46 -0
- package/dist/traceability/git-change-snapshot.d.ts.map +1 -1
- package/dist/traceability/git-change-snapshot.js +187 -3
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/traceability/staged-diagnostics.js +5 -6
- package/dist/traceability/staged-inventory-drift.d.ts +24 -0
- package/dist/traceability/staged-inventory-drift.d.ts.map +1 -0
- package/dist/traceability/staged-inventory-drift.js +84 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +1 -2
- package/dist/utils/content-freshness.d.ts +9 -0
- package/dist/utils/content-freshness.d.ts.map +1 -1
- package/dist/utils/content-freshness.js +33 -0
- package/dist/utils/merge-driver-setup.d.ts +10 -0
- package/dist/utils/merge-driver-setup.d.ts.map +1 -0
- package/dist/utils/merge-driver-setup.js +69 -0
- package/dist/utils/rule-registry.generated.d.ts +1 -1
- package/dist/utils/rule-registry.generated.js +1 -1
- package/package.json +2 -2
- package/src/public/impact/analyzer.ts +13 -1
- package/src/public/impact/diagnostics.ts +20 -3
- package/src/public/impact/types.ts +2 -0
- package/src/public/operations/check-executor.ts +16 -4
- package/src/public/operations/check-helpers.ts +65 -0
- package/src/public/operations/contracts.ts +1 -0
- package/src/public/operations/decision-firmness.ts +18 -2
- package/src/public/operations/discovery-executors.ts +113 -3
- package/src/public/operations/migration-plan.ts +47 -1
- package/src/public/operations/schema6-check-actions.ts +43 -2
- package/src/public/operations/source-path-dangling.ts +12 -2
- package/src/public/operations/specs/discovery.ts +6 -0
- package/src/public/operations/specs/planning.ts +1 -1
- package/src/public/operations/specs/reporting.ts +6 -4
- package/src/public/operations/specs/semantic.ts +41 -4
- package/src/public/skills/kibi-bootstrap/SKILL.md +3 -2
- package/src/public/skills/kibi-usage/SKILL.md +13 -5
- package/src/public/skills/kibi-usage/resources/workflows.md +3 -1
|
@@ -14,7 +14,53 @@ export interface GitChangeSnapshot {
|
|
|
14
14
|
/** Reject a changed HEAD or index tree after staged analysis. */
|
|
15
15
|
assertUnchanged(): void;
|
|
16
16
|
inventory: StagedPath[];
|
|
17
|
+
/**
|
|
18
|
+
* Staged merge commits only: paths whose staged content is taken unchanged
|
|
19
|
+
* from a merged parent. They are part of `baseTree`, so the inventory
|
|
20
|
+
* holds only what the merge itself wrote (its conflict resolutions and
|
|
21
|
+
* edits that differ from every parent).
|
|
22
|
+
*/
|
|
23
|
+
mergeInherited?: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Staged snapshots only: knowledge files the commit moves into the
|
|
26
|
+
* canonical `.kb/` layout (for example after `kibi migrate`). `baseTree`
|
|
27
|
+
* holds each source's committed content at its target path, so the
|
|
28
|
+
* inventory shows only what the commit changed inside those files.
|
|
29
|
+
*/
|
|
30
|
+
renameResolved?: ReadonlyArray<{
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}>;
|
|
17
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* For a staged merge commit, the tree the merge is judged against: HEAD
|
|
37
|
+
* with every path the merge takes unchanged from a merged parent replaced
|
|
38
|
+
* by that staged content. Diffing it against the staged tree leaves only
|
|
39
|
+
* what the merge itself wrote, so debt inherited from the merged branch is
|
|
40
|
+
* not charged to the merge. Null when no merge is in progress or nothing
|
|
41
|
+
* is inherited.
|
|
42
|
+
*/
|
|
43
|
+
export declare function mergeResolutionBase(root: string, baseTree: string, headTree: string, parents: readonly string[]): {
|
|
44
|
+
baseTree: string;
|
|
45
|
+
inherited: string[];
|
|
46
|
+
} | null;
|
|
47
|
+
/**
|
|
48
|
+
* For a staged snapshot, the tree a commit that relocates knowledge files
|
|
49
|
+
* into `.kb/` is judged against: `baseTree` with each relocated file's
|
|
50
|
+
* committed content placed at its new path and the old path removed.
|
|
51
|
+
* Diffing it against the staged tree leaves only the edits the commit made
|
|
52
|
+
* inside those files, so findings that already existed at the old path are
|
|
53
|
+
* not charged to the move. Every other change, including an edit to a file
|
|
54
|
+
* that was already canonical, is diffed as before. Null when nothing is
|
|
55
|
+
* relocated.
|
|
56
|
+
*/
|
|
57
|
+
export declare function renameResolutionBase(root: string, baseTree: string, headTree: string): {
|
|
58
|
+
baseTree: string;
|
|
59
|
+
renames: Array<{
|
|
60
|
+
from: string;
|
|
61
|
+
to: string;
|
|
62
|
+
}>;
|
|
63
|
+
} | null;
|
|
18
64
|
/** Capture one immutable tree from the index and inventory only Git objects. */
|
|
19
65
|
export declare function captureStagedSnapshot(workspaceRoot: string): GitChangeSnapshot;
|
|
20
66
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-change-snapshot.d.ts","sourceRoot":"","sources":["../../src/traceability/git-change-snapshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git-change-snapshot.d.ts","sourceRoot":"","sources":["../../src/traceability/git-change-snapshot.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAIV,UAAU,EAEX,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAGpE,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;IACzC,uEAAuE;IACvE,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChE,iEAAiE;IACjE,eAAe,IAAI,IAAI,CAAC;IACxB,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9D;AAueD;;;;;;;GAOG;AAEH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA6ClD;AAsED;;;;;;;;;GASG;AAEH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAsB3E;AAED,gFAAgF;AAEhF,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,GACpB,iBAAiB,CAuCnB;AAED;;;;;GAKG;AAEH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,iBAAiB,CAyBnB"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { ENTITY_LANES, isEntityLanePath, isKbKnowledgePath, isSymbolsManifestPath, } from "../utils/kb-paths.js";
|
|
3
6
|
const GIT_EXEC_MAX_BUFFER = 64 * 1024 * 1024;
|
|
4
7
|
function runGit(root, args) {
|
|
5
8
|
try {
|
|
@@ -368,12 +371,188 @@ function snapshotFromTrees(root, baseTree, headTree, headCommit, readGit, assert
|
|
|
368
371
|
inventory: buildInventory(root, baseTree, headTree),
|
|
369
372
|
};
|
|
370
373
|
}
|
|
374
|
+
/** The commits named by MERGE_HEAD while a merge commit is being made. */
|
|
375
|
+
function pendingMergeParents(root) {
|
|
376
|
+
let mergeHeadPath;
|
|
377
|
+
try {
|
|
378
|
+
mergeHeadPath = gitText(root, ["rev-parse", "--git-path", "MERGE_HEAD"]);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return [];
|
|
382
|
+
}
|
|
383
|
+
let text;
|
|
384
|
+
try {
|
|
385
|
+
text = readFileSync(path.resolve(root, mergeHeadPath), "utf8");
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
return text
|
|
391
|
+
.split(/\r?\n/)
|
|
392
|
+
.map((line) => line.trim())
|
|
393
|
+
.filter((line) => /^[a-f0-9]{40,64}$/.test(line));
|
|
394
|
+
}
|
|
395
|
+
function changedPaths(root, from, to) {
|
|
396
|
+
return runGit(root, ["diff", "--name-only", "-z", "--no-renames", from, to])
|
|
397
|
+
.toString("utf8")
|
|
398
|
+
.split("\0")
|
|
399
|
+
.filter(Boolean);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* For a staged merge commit, the tree the merge is judged against: HEAD
|
|
403
|
+
* with every path the merge takes unchanged from a merged parent replaced
|
|
404
|
+
* by that staged content. Diffing it against the staged tree leaves only
|
|
405
|
+
* what the merge itself wrote, so debt inherited from the merged branch is
|
|
406
|
+
* not charged to the merge. Null when no merge is in progress or nothing
|
|
407
|
+
* is inherited.
|
|
408
|
+
*/
|
|
409
|
+
// implements REQ-014
|
|
410
|
+
export function mergeResolutionBase(root, baseTree, headTree, parents) {
|
|
411
|
+
if (parents.length === 0)
|
|
412
|
+
return null;
|
|
413
|
+
const changed = changedPaths(root, baseTree, headTree);
|
|
414
|
+
if (changed.length === 0)
|
|
415
|
+
return null;
|
|
416
|
+
const inherited = new Set();
|
|
417
|
+
for (const parent of parents) {
|
|
418
|
+
const parentTree = treeForCommit(root, parent);
|
|
419
|
+
const differs = new Set(changedPaths(root, parentTree, headTree));
|
|
420
|
+
for (const file of changed) {
|
|
421
|
+
if (!differs.has(file))
|
|
422
|
+
inherited.add(file);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (inherited.size === 0)
|
|
426
|
+
return null;
|
|
427
|
+
const paths = [...inherited].sort();
|
|
428
|
+
const listed = runGit(root, [
|
|
429
|
+
"ls-tree",
|
|
430
|
+
"-r",
|
|
431
|
+
"-z",
|
|
432
|
+
"--full-tree",
|
|
433
|
+
headTree,
|
|
434
|
+
"--",
|
|
435
|
+
...paths.map(literalPathspec),
|
|
436
|
+
])
|
|
437
|
+
.toString("utf8")
|
|
438
|
+
.split("\0")
|
|
439
|
+
.filter(Boolean);
|
|
440
|
+
const present = new Set();
|
|
441
|
+
const entries = [];
|
|
442
|
+
for (const row of listed) {
|
|
443
|
+
const tab = row.indexOf("\t");
|
|
444
|
+
if (tab < 0)
|
|
445
|
+
continue;
|
|
446
|
+
const [mode, , oid] = row.slice(0, tab).split(" ");
|
|
447
|
+
const file = row.slice(tab + 1);
|
|
448
|
+
present.add(file);
|
|
449
|
+
entries.push(`${mode} ${oid}\t${file}`);
|
|
450
|
+
}
|
|
451
|
+
const zero = "0".repeat(baseTree.length);
|
|
452
|
+
for (const file of paths) {
|
|
453
|
+
// Deleted on the merged side and absent from the staged tree.
|
|
454
|
+
if (!present.has(file))
|
|
455
|
+
entries.push(`0 ${zero}\t${file}`);
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
baseTree: writeTreeWithEntries(root, baseTree, entries, "kibi-merge-base-"),
|
|
459
|
+
inherited: paths,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
/** Write `baseTree` with `--index-info` entries applied in a scratch index. */
|
|
463
|
+
function writeTreeWithEntries(root, baseTree, entries, prefix) {
|
|
464
|
+
const scratch = mkdtempSync(path.join(tmpdir(), prefix));
|
|
465
|
+
const env = { ...process.env, GIT_INDEX_FILE: path.join(scratch, "index") };
|
|
466
|
+
try {
|
|
467
|
+
const git = (args, input) => execFileSync("git", [...args], {
|
|
468
|
+
cwd: root,
|
|
469
|
+
env,
|
|
470
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER,
|
|
471
|
+
...(input !== undefined ? { input } : {}),
|
|
472
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
473
|
+
});
|
|
474
|
+
git(["read-tree", baseTree]);
|
|
475
|
+
git(["update-index", "-z", "--index-info"], `${entries.join("\0")}\0`);
|
|
476
|
+
return git(["write-tree"]).toString("utf8").trim();
|
|
477
|
+
}
|
|
478
|
+
finally {
|
|
479
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
function renamedPaths(root, from, to) {
|
|
483
|
+
return parseNameStatus(runGit(root, [
|
|
484
|
+
"diff",
|
|
485
|
+
"--name-status",
|
|
486
|
+
"-z",
|
|
487
|
+
"-M",
|
|
488
|
+
"--diff-filter=R",
|
|
489
|
+
from,
|
|
490
|
+
to,
|
|
491
|
+
])).flatMap((row) => row.status === "R" && row.oldPath !== undefined
|
|
492
|
+
? [{ from: row.oldPath, to: row.path }]
|
|
493
|
+
: []);
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* True when `from -> to` moves one knowledge file from outside the canonical
|
|
497
|
+
* layout into the same kind of canonical `.kb/` location: an entity lane
|
|
498
|
+
* file into the same lane, or a symbols manifest / coordinates file onto the
|
|
499
|
+
* canonical file of the same name (`.yml` and `.yaml` alike).
|
|
500
|
+
*/
|
|
501
|
+
function isKnowledgeRelocation(from, to) {
|
|
502
|
+
if (isKbKnowledgePath(from) || !isKbKnowledgePath(to))
|
|
503
|
+
return false;
|
|
504
|
+
if (isSymbolsManifestPath(to)) {
|
|
505
|
+
const stem = (file) => path.posix.basename(file).replace(/\.ya?ml$/, "");
|
|
506
|
+
return stem(from) === stem(to);
|
|
507
|
+
}
|
|
508
|
+
const lane = to.split("/")[1] ?? "";
|
|
509
|
+
return (ENTITY_LANES.includes(lane) &&
|
|
510
|
+
`/${from}`.includes(`/${lane}/`));
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* For a staged snapshot, the tree a commit that relocates knowledge files
|
|
514
|
+
* into `.kb/` is judged against: `baseTree` with each relocated file's
|
|
515
|
+
* committed content placed at its new path and the old path removed.
|
|
516
|
+
* Diffing it against the staged tree leaves only the edits the commit made
|
|
517
|
+
* inside those files, so findings that already existed at the old path are
|
|
518
|
+
* not charged to the move. Every other change, including an edit to a file
|
|
519
|
+
* that was already canonical, is diffed as before. Null when nothing is
|
|
520
|
+
* relocated.
|
|
521
|
+
*/
|
|
522
|
+
// implements REQ-014
|
|
523
|
+
export function renameResolutionBase(root, baseTree, headTree) {
|
|
524
|
+
const zero = "0".repeat(baseTree.length);
|
|
525
|
+
const renames = [];
|
|
526
|
+
const entries = [];
|
|
527
|
+
for (const rename of renamedPaths(root, baseTree, headTree)) {
|
|
528
|
+
if (!isKnowledgeRelocation(rename.from, rename.to))
|
|
529
|
+
continue;
|
|
530
|
+
const source = readTreeEntry(root, baseTree, rename.from);
|
|
531
|
+
if (source?.type !== "blob")
|
|
532
|
+
continue;
|
|
533
|
+
renames.push(rename);
|
|
534
|
+
entries.push(`0 ${zero}\t${rename.from}`);
|
|
535
|
+
entries.push(`${source.mode} ${source.oid}\t${rename.to}`);
|
|
536
|
+
}
|
|
537
|
+
if (renames.length === 0)
|
|
538
|
+
return null;
|
|
539
|
+
return {
|
|
540
|
+
baseTree: writeTreeWithEntries(root, baseTree, entries, "kibi-rename-base-"),
|
|
541
|
+
renames: renames.sort((left, right) => left.to.localeCompare(right.to)),
|
|
542
|
+
};
|
|
543
|
+
}
|
|
371
544
|
/** Capture one immutable tree from the index and inventory only Git objects. */
|
|
372
545
|
// implements REQ-014
|
|
373
546
|
export function captureStagedSnapshot(workspaceRoot) {
|
|
374
547
|
const headCommit = resolveHeadCommit(workspaceRoot);
|
|
375
|
-
const baseTree = treeForCommit(workspaceRoot, headCommit);
|
|
376
548
|
const headTree = gitText(workspaceRoot, ["write-tree"]);
|
|
549
|
+
const commitTree = treeForCommit(workspaceRoot, headCommit);
|
|
550
|
+
const merge = headCommit
|
|
551
|
+
? mergeResolutionBase(workspaceRoot, commitTree, headTree, pendingMergeParents(workspaceRoot))
|
|
552
|
+
: null;
|
|
553
|
+
const mergeTree = merge?.baseTree ?? commitTree;
|
|
554
|
+
const relocation = renameResolutionBase(workspaceRoot, mergeTree, headTree);
|
|
555
|
+
const baseTree = relocation?.baseTree ?? mergeTree;
|
|
377
556
|
const readGit = (args) => translateReadGit(workspaceRoot, args, baseTree, headTree, headCommit);
|
|
378
557
|
const assertUnchanged = () => {
|
|
379
558
|
const currentHead = resolveHeadCommit(workspaceRoot);
|
|
@@ -382,7 +561,12 @@ export function captureStagedSnapshot(workspaceRoot) {
|
|
|
382
561
|
throw new Error("Git index or HEAD changed during staged analysis; retry the operation");
|
|
383
562
|
}
|
|
384
563
|
};
|
|
385
|
-
|
|
564
|
+
const snapshot = snapshotFromTrees(workspaceRoot, baseTree, headTree, headCommit, readGit, assertUnchanged);
|
|
565
|
+
return {
|
|
566
|
+
...snapshot,
|
|
567
|
+
...(merge ? { mergeInherited: merge.inherited } : {}),
|
|
568
|
+
...(relocation ? { renameResolved: relocation.renames } : {}),
|
|
569
|
+
};
|
|
386
570
|
}
|
|
387
571
|
/**
|
|
388
572
|
* Capture changes between explicit revisions. The caller chooses `base`; for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-diagnostics.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,MAAM,sBAAsB,GAC9B,8BAA8B,GAC9B,wBAAwB,GACxB,8BAA8B,GAC9B,+BAA+B,GAC/B,iCAAiC,GACjC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,2BAA2B,GAC3B,6BAA6B,GAC7B,yBAAyB,GACzB,wCAAwC,GACxC,wBAAwB,CAAC;AAE7B,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,uDAAuD;IACvD,EAAE,EAAE,sBAAsB,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACxC,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,gDAAgD;IAChD,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"staged-diagnostics.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,MAAM,sBAAsB,GAC9B,8BAA8B,GAC9B,wBAAwB,GACxB,8BAA8B,GAC9B,+BAA+B,GAC/B,iCAAiC,GACjC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,2BAA2B,GAC3B,6BAA6B,GAC7B,yBAAyB,GACzB,wCAAwC,GACxC,wBAAwB,CAAC;AAE7B,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,uDAAuD;IACvD,EAAE,EAAE,sBAAsB,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACxC,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,gDAAgD;IAChD,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAsID,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,kBAAkB,GAC3B,oBAAoB,CAoBtB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,kBAAkB,EAC5B,mBAAmB,GAAE,MAAmC,GACvD,oBAAoB,EAAE,CAiDxB"}
|
|
@@ -8,13 +8,12 @@ function deriveCoordinatesPath(symbolsManifestPath) {
|
|
|
8
8
|
function formatFileList(paths) {
|
|
9
9
|
return paths.join(", ");
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Every name, in order: the gate output is the list a person works through,
|
|
13
|
+
* so it is never cut short.
|
|
14
|
+
*/
|
|
13
15
|
function formatNameList(names) {
|
|
14
|
-
|
|
15
|
-
const overflow = names.length - shown.length;
|
|
16
|
-
const rendered = shown.join(", ");
|
|
17
|
-
return overflow > 0 ? `${rendered} … and ${overflow} more` : rendered;
|
|
16
|
+
return names.join(", ");
|
|
18
17
|
}
|
|
19
18
|
function symbolsManifestFileDetails(evidence, paths) {
|
|
20
19
|
const pathSet = new Set(paths);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExtractionResult } from "../extractors/markdown.js";
|
|
2
|
+
/** A staged requirement whose semantic inventory no longer matches its prose. */
|
|
3
|
+
export declare const STAGED_INVENTORY_DRIFT_RULE = "semantic-inventory-drift";
|
|
4
|
+
export type StagedInventoryDriftViolation = Readonly<{
|
|
5
|
+
rule: typeof STAGED_INVENTORY_DRIFT_RULE;
|
|
6
|
+
entityId: string;
|
|
7
|
+
source: string;
|
|
8
|
+
message: string;
|
|
9
|
+
errors: readonly string[];
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* The staged requirements that declare a semantic inventory contract (now
|
|
13
|
+
* or in the base tree) whose inventory no longer matches their prose. The
|
|
14
|
+
* next `kibi sync` refuses them under the proposition-complete boundary;
|
|
15
|
+
* the gate applies the same test, so drift is caught at the commit that
|
|
16
|
+
* introduces it instead of after a later merge. Requirements without a
|
|
17
|
+
* contract are left to sync, which decides from its own baseline.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stagedInventoryDriftViolations(input: {
|
|
20
|
+
stagedPaths: ReadonlySet<string>;
|
|
21
|
+
snapshotEntities: readonly ExtractionResult[];
|
|
22
|
+
readBase: (path: string) => string | null;
|
|
23
|
+
}): StagedInventoryDriftViolation[];
|
|
24
|
+
//# sourceMappingURL=staged-inventory-drift.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-inventory-drift.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-inventory-drift.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAKlE,iFAAiF;AAEjF,eAAO,MAAM,2BAA2B,6BAA6B,CAAC;AAGtE,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,IAAI,EAAE,OAAO,2BAA2B,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B,CAAC,CAAC;AAgCH;;;;;;;GAOG;AAEH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,gBAAgB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC3C,GAAG,6BAA6B,EAAE,CA8BlC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { extractFromMarkdownString } from "../extractors/markdown.js";
|
|
19
|
+
import { analyzeSemanticAdvisorInput } from "../operations/semantic-advisor/analyze-prose.js";
|
|
20
|
+
import { validateSemanticInventoryBoundary } from "../operations/semantic-advisor/ingestion-boundary.js";
|
|
21
|
+
/** A staged requirement whose semantic inventory no longer matches its prose. */
|
|
22
|
+
// implements REQ-kibi-proposition-complete-ingestion
|
|
23
|
+
export const STAGED_INVENTORY_DRIFT_RULE = "semantic-inventory-drift";
|
|
24
|
+
function advertisesContract(entity) {
|
|
25
|
+
return (entity.semantic_inventory_version !== undefined ||
|
|
26
|
+
entity.semantic_source_hash !== undefined ||
|
|
27
|
+
entity.semantic_inventory !== undefined);
|
|
28
|
+
}
|
|
29
|
+
function payloadOf(result) {
|
|
30
|
+
return {
|
|
31
|
+
type: result.entity.type,
|
|
32
|
+
id: result.entity.id,
|
|
33
|
+
properties: result.entity,
|
|
34
|
+
relationships: result.relationships,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function baseResult(path, readBase) {
|
|
38
|
+
const content = readBase(path);
|
|
39
|
+
if (content === null)
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return extractFromMarkdownString(content, path);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The staged requirements that declare a semantic inventory contract (now
|
|
50
|
+
* or in the base tree) whose inventory no longer matches their prose. The
|
|
51
|
+
* next `kibi sync` refuses them under the proposition-complete boundary;
|
|
52
|
+
* the gate applies the same test, so drift is caught at the commit that
|
|
53
|
+
* introduces it instead of after a later merge. Requirements without a
|
|
54
|
+
* contract are left to sync, which decides from its own baseline.
|
|
55
|
+
*/
|
|
56
|
+
// implements REQ-kibi-proposition-complete-ingestion
|
|
57
|
+
export function stagedInventoryDriftViolations(input) {
|
|
58
|
+
const violations = [];
|
|
59
|
+
for (const result of input.snapshotEntities) {
|
|
60
|
+
if (result.entity.type !== "req")
|
|
61
|
+
continue;
|
|
62
|
+
const source = result.entity.source;
|
|
63
|
+
if (!input.stagedPaths.has(source))
|
|
64
|
+
continue;
|
|
65
|
+
const payload = payloadOf(result);
|
|
66
|
+
const semantic = analyzeSemanticAdvisorInput({ payload });
|
|
67
|
+
const boundary = validateSemanticInventoryBoundary(payload, result.relationships, semantic.receipt);
|
|
68
|
+
if (boundary.errors.length === 0)
|
|
69
|
+
continue;
|
|
70
|
+
const base = baseResult(source, input.readBase);
|
|
71
|
+
const enforce = advertisesContract(result.entity) ||
|
|
72
|
+
(base !== null && advertisesContract(base.entity));
|
|
73
|
+
if (!enforce)
|
|
74
|
+
continue;
|
|
75
|
+
violations.push({
|
|
76
|
+
rule: STAGED_INVENTORY_DRIFT_RULE,
|
|
77
|
+
entityId: result.entity.id,
|
|
78
|
+
source,
|
|
79
|
+
errors: boundary.errors,
|
|
80
|
+
message: `${result.entity.id} (${source}): the semantic inventory does not match the requirement's prose, so the next kibi sync rejects it: ${boundary.errors.join("; ")}. Run 'kibi migrate' to re-derive it, or kb_model mode analyze with the complete prose and keep its inventory contract.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return violations.sort((left, right) => left.entityId.localeCompare(right.entityId));
|
|
84
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,qCAAqC,CAAC;AAI7C,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0ClD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,mCAAmC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6ED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,wBAAwB,GACpC,wBAAwB,GAAG,IAAI,CAOjC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,GAAG,IAAI,CAMhB;
|
|
1
|
+
{"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAKzB,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,qCAAqC,CAAC;AAI7C,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA0ClD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,mCAAmC,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5D,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6ED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,wBAAwB,GACpC,wBAAwB,GAAG,IAAI,CAOjC;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,GAAG,IAAI,CAMhB;AAoTD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,oBAAoB,CAAC;CAC7E,GAAG,+BAA+B,CAoLlC;AAED,wBAAgB,4CAA4C,CAAC,OAAO,EAAE;IACpE,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACpD,GAAG,qCAAqC,CAqExC"}
|
|
@@ -281,8 +281,7 @@ function diffSymbolEvidence(sourcePath, expectedSymbols, stagedSymbols) {
|
|
|
281
281
|
if (missing.length === 0 && extra.length === 0) {
|
|
282
282
|
return null;
|
|
283
283
|
}
|
|
284
|
-
// Keep the complete lists:
|
|
285
|
-
// accurate "… and N more"), while JSON evidence carries every name.
|
|
284
|
+
// Keep the complete lists: the gate output names every symbol.
|
|
286
285
|
return {
|
|
287
286
|
path: sourcePath,
|
|
288
287
|
expectedCount: expectedSymbols.length,
|
|
@@ -9,5 +9,14 @@ export declare function confirmSourceNewerReasons(workspaceRoot: string, storePa
|
|
|
9
9
|
kept: Reason[];
|
|
10
10
|
dropped: Reason[];
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* The `meta` freshness block reporting operations return (kb_graph,
|
|
14
|
+
* kb_find_gaps, kb_coverage) comes straight from Prolog, which compares file
|
|
15
|
+
* times only. Apply the same content confirmation kb_status does, so a
|
|
16
|
+
* checkout or `touch` that changed no byte is not reported as stale.
|
|
17
|
+
*/
|
|
18
|
+
export declare function confirmMetaFreshness<T extends {
|
|
19
|
+
readonly meta?: Readonly<Record<string, unknown>>;
|
|
20
|
+
}>(payload: T, workspaceRoot: string): T;
|
|
12
21
|
export {};
|
|
13
22
|
//# sourceMappingURL=content-freshness.d.ts.map
|
|
@@ -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;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"}
|
|
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;AAED;;;;;GAKG;AAEH,wBAAgB,oBAAoB,CAClC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,EAC/D,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,GAAG,CAAC,CAoCtC"}
|
|
@@ -169,3 +169,36 @@ export function confirmSourceNewerReasons(workspaceRoot, storePath, reasons) {
|
|
|
169
169
|
}
|
|
170
170
|
return { kept, dropped };
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* The `meta` freshness block reporting operations return (kb_graph,
|
|
174
|
+
* kb_find_gaps, kb_coverage) comes straight from Prolog, which compares file
|
|
175
|
+
* times only. Apply the same content confirmation kb_status does, so a
|
|
176
|
+
* checkout or `touch` that changed no byte is not reported as stale.
|
|
177
|
+
*/
|
|
178
|
+
// implements REQ-cli-status-pre-first-sync
|
|
179
|
+
export function confirmMetaFreshness(payload, workspaceRoot) {
|
|
180
|
+
const meta = payload.meta;
|
|
181
|
+
if (meta === undefined ||
|
|
182
|
+
typeof meta.kbPath !== "string" ||
|
|
183
|
+
!Array.isArray(meta.staleReasons))
|
|
184
|
+
return payload;
|
|
185
|
+
const reasons = meta.staleReasons.filter((reason) => record(reason) !== null);
|
|
186
|
+
const { kept, dropped } = confirmSourceNewerReasons(workspaceRoot, meta.kbPath, reasons);
|
|
187
|
+
if (dropped.length === 0)
|
|
188
|
+
return payload;
|
|
189
|
+
const allConfirmed = kept.length === 0 &&
|
|
190
|
+
meta.staleReasonsTruncated !== true &&
|
|
191
|
+
meta.syncState === "stale";
|
|
192
|
+
return {
|
|
193
|
+
...payload,
|
|
194
|
+
meta: {
|
|
195
|
+
...meta,
|
|
196
|
+
staleReasons: kept,
|
|
197
|
+
// A truncated list counts more reasons than it carries.
|
|
198
|
+
staleReasonCount: Math.max(kept.length, (typeof meta.staleReasonCount === "number"
|
|
199
|
+
? meta.staleReasonCount
|
|
200
|
+
: reasons.length) - dropped.length),
|
|
201
|
+
...(allConfirmed ? { syncState: "fresh", dirty: false } : {}),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** How to route the authored manifests to `kibi merge-driver`. */
|
|
2
|
+
export declare const MERGE_DRIVER_SETUP = "Add to .gitattributes: \".kb/symbols.yaml merge=kibi\" and \".kb/relationships/*.yaml merge=kibi\"; then run: git config merge.kibi.driver \"npm exec --no -- kibi merge-driver %O %A %B\" (see docs/cli-reference.md#kibi-merge-driver-base-current-other).";
|
|
3
|
+
/**
|
|
4
|
+
* What keeps Git from resolving `.kb/symbols.yaml` with `kibi merge-driver`:
|
|
5
|
+
* the attribute that routes the file to it, and the driver command. Empty
|
|
6
|
+
* when both are set, and null outside a Git work tree or while the
|
|
7
|
+
* manifest holds no symbol records (nothing to conflict on yet).
|
|
8
|
+
*/
|
|
9
|
+
export declare function manifestMergeDriverGaps(cwd: string): string[] | null;
|
|
10
|
+
//# sourceMappingURL=merge-driver-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-driver-setup.d.ts","sourceRoot":"","sources":["../../src/utils/merge-driver-setup.ts"],"names":[],"mappings":"AAsBA,kEAAkE;AAElE,eAAO,MAAM,kBAAkB,iQAC2N,CAAC;AAwB3P;;;;;GAKG;AAEH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAiBpE"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { readFileSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
/** How to route the authored manifests to `kibi merge-driver`. */
|
|
22
|
+
// implements REQ-cli-doctor
|
|
23
|
+
export const MERGE_DRIVER_SETUP = 'Add to .gitattributes: ".kb/symbols.yaml merge=kibi" and ".kb/relationships/*.yaml merge=kibi"; then run: git config merge.kibi.driver "npm exec --no -- kibi merge-driver %O %A %B" (see docs/cli-reference.md#kibi-merge-driver-base-current-other).';
|
|
24
|
+
function gitOutput(cwd, args) {
|
|
25
|
+
try {
|
|
26
|
+
return execFileSync("git", [...args], {
|
|
27
|
+
cwd,
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
30
|
+
}).trim();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function hasSymbolRecords(cwd) {
|
|
37
|
+
try {
|
|
38
|
+
return /^\s*-\s+id\s*:/m.test(readFileSync(path.join(cwd, ".kb", "symbols.yaml"), "utf8"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* What keeps Git from resolving `.kb/symbols.yaml` with `kibi merge-driver`:
|
|
46
|
+
* the attribute that routes the file to it, and the driver command. Empty
|
|
47
|
+
* when both are set, and null outside a Git work tree or while the
|
|
48
|
+
* manifest holds no symbol records (nothing to conflict on yet).
|
|
49
|
+
*/
|
|
50
|
+
// implements REQ-cli-doctor
|
|
51
|
+
export function manifestMergeDriverGaps(cwd) {
|
|
52
|
+
if (gitOutput(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (!hasSymbolRecords(cwd))
|
|
56
|
+
return null;
|
|
57
|
+
const attribute = gitOutput(cwd, [
|
|
58
|
+
"check-attr",
|
|
59
|
+
"merge",
|
|
60
|
+
"--",
|
|
61
|
+
".kb/symbols.yaml",
|
|
62
|
+
]);
|
|
63
|
+
const routed = attribute?.endsWith(": merge: kibi") === true;
|
|
64
|
+
const driver = gitOutput(cwd, ["config", "--get", "merge.kibi.driver"]);
|
|
65
|
+
return [
|
|
66
|
+
...(routed ? [] : [".kb/symbols.yaml is not routed to merge=kibi"]),
|
|
67
|
+
...(driver ? [] : ["merge.kibi.driver is not set"]),
|
|
68
|
+
];
|
|
69
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export declare const GENERATED_RULES: readonly [{
|
|
2
2
|
readonly name: "must-priority-coverage";
|
|
3
|
-
readonly description: "Every must-priority requirement must have a scenario and a test";
|
|
3
|
+
readonly description: "Every current must-priority requirement (not superseded, retired, or closed without the implemented tag) must have a scenario and a test";
|
|
4
4
|
readonly enforcementClass: "canonical";
|
|
5
5
|
readonly category: "coverage";
|
|
6
6
|
}, {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export const GENERATED_RULES = [
|
|
11
11
|
{
|
|
12
12
|
name: "must-priority-coverage",
|
|
13
|
-
description: "Every must-priority requirement must have a scenario and a test",
|
|
13
|
+
description: "Every current must-priority requirement (not superseded, retired, or closed without the implemented tag) must have a scenario and a test",
|
|
14
14
|
enforcementClass: "canonical",
|
|
15
15
|
category: "coverage",
|
|
16
16
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.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.22.
|
|
238
|
+
"kibi-core": "^0.22.1",
|
|
239
239
|
"kibi-plugin-builtin": "^0.5.2",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
@@ -11,6 +11,7 @@ import { captureStagedSnapshot } from "../../traceability/git-change-snapshot.js
|
|
|
11
11
|
import { readSnapshotKnowledge } from "../../traceability/snapshot-knowledge.js";
|
|
12
12
|
import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
|
|
13
13
|
import { extractSymbolsFromStagedFileAsync } from "../../traceability/symbol-extract.js";
|
|
14
|
+
import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
|
|
14
15
|
import {
|
|
15
16
|
createSemanticReviewDiagnostics,
|
|
16
17
|
createSymbolGranularityDiagnostics,
|
|
@@ -153,7 +154,18 @@ export async function analyzeChangedFileImpact(
|
|
|
153
154
|
manifestResults: activeManifestResults,
|
|
154
155
|
symbolsByFile,
|
|
155
156
|
}),
|
|
156
|
-
...createSemanticReviewDiagnostics({
|
|
157
|
+
...createSemanticReviewDiagnostics({
|
|
158
|
+
symbolsByFile,
|
|
159
|
+
linkedSourceFiles: new Set(
|
|
160
|
+
activeManifestResults
|
|
161
|
+
.filter((result) =>
|
|
162
|
+
result.relationships.some((relationship) =>
|
|
163
|
+
isTraceabilityRelationshipType(relationship.type),
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
.map((result) => result.sourceFile as string),
|
|
167
|
+
),
|
|
168
|
+
}),
|
|
157
169
|
];
|
|
158
170
|
const maxDiagnostics = options.maxDiagnostics;
|
|
159
171
|
const cappedDiagnostics =
|