kibi-cli 2.21.2 → 2.22.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 (96) hide show
  1. package/dist/cli-register-foundation.d.ts.map +1 -1
  2. package/dist/cli-register-foundation.js +1 -0
  3. package/dist/commands/check.d.ts +0 -1
  4. package/dist/commands/check.d.ts.map +1 -1
  5. package/dist/commands/check.js +47 -111
  6. package/dist/commands/doctor.d.ts.map +1 -1
  7. package/dist/commands/doctor.js +96 -6
  8. package/dist/commands/init-helpers.d.ts +14 -1
  9. package/dist/commands/init-helpers.d.ts.map +1 -1
  10. package/dist/commands/init-helpers.js +49 -10
  11. package/dist/commands/init.d.ts +1 -0
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +12 -1
  14. package/dist/commands/legacy-storage-migration.d.ts +46 -1
  15. package/dist/commands/legacy-storage-migration.d.ts.map +1 -1
  16. package/dist/commands/legacy-storage-migration.js +219 -9
  17. package/dist/commands/migrate.d.ts.map +1 -1
  18. package/dist/commands/migrate.js +48 -182
  19. package/dist/commands/sync/manifest.d.ts.map +1 -1
  20. package/dist/commands/sync/manifest.js +9 -0
  21. package/dist/extractors/symbols-coordinator.js +1 -0
  22. package/dist/intent-search.d.ts.map +1 -1
  23. package/dist/intent-search.js +28 -6
  24. package/dist/operations/migration/inventory-rederive.d.ts +40 -1
  25. package/dist/operations/migration/inventory-rederive.d.ts.map +1 -1
  26. package/dist/operations/migration/inventory-rederive.js +99 -8
  27. package/dist/operations/migration/requirement-status.d.ts +25 -0
  28. package/dist/operations/migration/requirement-status.d.ts.map +1 -0
  29. package/dist/operations/migration/requirement-status.js +105 -0
  30. package/dist/operations/migration/schema6.d.ts +25 -0
  31. package/dist/operations/migration/schema6.d.ts.map +1 -1
  32. package/dist/operations/migration/schema6.js +265 -7
  33. package/dist/operations/migration/symbol-legacy-links.d.ts +51 -0
  34. package/dist/operations/migration/symbol-legacy-links.d.ts.map +1 -0
  35. package/dist/operations/migration/symbol-legacy-links.js +319 -0
  36. package/dist/operations/mutation/context-warning.d.ts +12 -2
  37. package/dist/operations/mutation/context-warning.d.ts.map +1 -1
  38. package/dist/operations/mutation/context-warning.js +6 -6
  39. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  40. package/dist/operations/mutation/source-authoring.js +130 -35
  41. package/dist/operations/mutation/types.d.ts +2 -1
  42. package/dist/operations/mutation/types.d.ts.map +1 -1
  43. package/dist/operations/mutation/upsert.d.ts +17 -2
  44. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  45. package/dist/operations/mutation/upsert.js +28 -11
  46. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  47. package/dist/operations/mutation/validate-upsert.js +40 -8
  48. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  49. package/dist/operations/planning/apply-plan.js +18 -3
  50. package/dist/public/impact/diagnostics.d.ts.map +1 -1
  51. package/dist/public/impact/diagnostics.js +24 -14
  52. package/dist/public/operations/catalog.d.ts +3 -3
  53. package/dist/public/operations/check-executor.d.ts.map +1 -1
  54. package/dist/public/operations/check-executor.js +3 -0
  55. package/dist/public/operations/contracts.d.ts.map +1 -1
  56. package/dist/public/operations/contracts.js +6 -1
  57. package/dist/public/operations/discovery-executors.d.ts +2 -0
  58. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  59. package/dist/public/operations/discovery-executors.js +9 -2
  60. package/dist/public/operations/migration-plan.d.ts +2 -0
  61. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  62. package/dist/public/operations/migration-plan.js +101 -16
  63. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  64. package/dist/public/operations/result-envelope.js +27 -1
  65. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  66. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  67. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  68. package/dist/public/operations/specs/check.d.ts +1 -1
  69. package/dist/public/operations/specs/planning.d.ts +2 -2
  70. package/dist/public/operations/specs/planning.js +2 -2
  71. package/dist/public/operations/symbol-granularity-check.d.ts +18 -0
  72. package/dist/public/operations/symbol-granularity-check.d.ts.map +1 -0
  73. package/dist/public/operations/symbol-granularity-check.js +175 -0
  74. package/dist/public/symbol-granularity.d.ts +17 -0
  75. package/dist/public/symbol-granularity.d.ts.map +1 -1
  76. package/dist/public/symbol-granularity.js +21 -0
  77. package/dist/traceability/evidence-model.d.ts +7 -3
  78. package/dist/traceability/evidence-model.d.ts.map +1 -1
  79. package/dist/traceability/staged-symbols-manifest.js +6 -5
  80. package/dist/traceability/validate.d.ts +14 -1
  81. package/dist/traceability/validate.d.ts.map +1 -1
  82. package/dist/traceability/validate.js +41 -0
  83. package/dist/utils/rule-registry.generated.d.ts +5 -0
  84. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  85. package/dist/utils/rule-registry.generated.js +6 -0
  86. package/package.json +3 -3
  87. package/src/public/impact/diagnostics.ts +29 -17
  88. package/src/public/operations/check-executor.ts +7 -0
  89. package/src/public/operations/contracts.ts +6 -1
  90. package/src/public/operations/discovery-executors.ts +12 -2
  91. package/src/public/operations/migration-plan.ts +100 -19
  92. package/src/public/operations/result-envelope.ts +34 -1
  93. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  94. package/src/public/operations/specs/planning.ts +2 -2
  95. package/src/public/operations/symbol-granularity-check.ts +233 -0
  96. package/src/public/symbol-granularity.ts +37 -0
@@ -1 +1 @@
1
- {"version":3,"file":"cli-register-foundation.d.ts","sourceRoot":"","sources":["../src/cli-register-foundation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAezC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqJjE"}
1
+ {"version":3,"file":"cli-register-foundation.d.ts","sourceRoot":"","sources":["../src/cli-register-foundation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAezC,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAyJjE"}
@@ -12,6 +12,7 @@ export function registerFoundationCommands(program) {
12
12
  .option("--no-hooks", "Do not install git hooks (hooks installed by default)")
13
13
  .option("--github", "Scaffold the GitHub Pages badge + requirement-health report workflow")
14
14
  .option("--badge-only", "With --github, publish only the badge (opt out of the full report)")
15
+ .option("--print-hook <hook>", "Print a snippet that delegates an existing hook (pre-commit, post-checkout, post-merge, post-rewrite) to kibi, and write nothing")
15
16
  .action(withExitCode(async (options) => (await import("./commands/init.js")).initCommand(options)));
16
17
  program
17
18
  .command("migrate")
@@ -25,5 +25,4 @@ export declare function checkDomainContradictions(prolog: PrologProcess): Promis
25
25
  export declare function checkStrictFactShape(prolog: PrologProcess): Promise<Violation[]>;
26
26
  export declare function checkStrictReqFactPairing(prolog: PrologProcess): Promise<Violation[]>;
27
27
  export declare function checkStrictReadiness(prolog: PrologProcess): Promise<Violation[]>;
28
- export declare function checkSymbolCoverage(prolog: PrologProcess): Promise<Violation[]>;
29
28
  //# sourceMappingURL=check.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA8E7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AA8gBD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC5B,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAC3B,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAMpB;AAGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA8nB/B;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAoDtB;AAID,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAkEtB;AAID,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAmGtB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,SAAS,EAAE,CAAC,CAyCtB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAgDtB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CA0BtB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CA+BtB"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAgF7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B;AA8gBD,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EACtC,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC5B,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAC3B,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAMpB;AAGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAqlB/B;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAoDtB;AAID,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgBnB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAkEtB;AAID,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAmGtB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,SAAS,EAAE,CAAC,CAyCtB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAgDtB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CA0BtB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqBtB"}
@@ -41,7 +41,7 @@ import { classifyKibiImpactEvidence, isBehaviorSourceEdit, parseKibiImpactOverri
41
41
  import { assessStagedSymbolsManifest, collectStagedAuthoredSymbolsManifestEvidence, } from "../traceability/staged-symbols-manifest.js";
42
42
  import { createManifestLookupSentinelKey, extractSymbolsFromStagedFileAsync, } from "../traceability/symbol-extract.js";
43
43
  import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, projectStagedEntities, } from "../traceability/temp-kb.js";
44
- import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
44
+ import { formatCoverageViolations, formatViolations as formatStagedViolations, validateStagedSymbolCoverage, validateStagedSymbols, } from "../traceability/validate.js";
45
45
  import { resolveReadBranchAttachment } from "../utils/branch-resolver.js";
46
46
  import { CANONICAL_ENTITY_PATHS, isEntityLanePath } from "../utils/kb-paths.js";
47
47
  import { safeCleanupProlog } from "../utils/prolog-cleanup.js";
@@ -708,122 +708,82 @@ export async function checkCommand(options) {
708
708
  : {}),
709
709
  symbolsByFile,
710
710
  }), ...createSemanticReviewDiagnostics({ symbolsByFile }), ...createStagedEntityIdStyleDiagnostics(markdownFiles));
711
- if (allSymbols.length === 0 && stagedEntityResults.length === 0) {
712
- if (stagedKibiDiagnostics.length > 0) {
713
- const blocking = hasBlockingImpactDiagnostics(stagedKibiDiagnostics);
714
- printStagedResult({
715
- format: options.format,
716
- coverage: stagedCoverage,
717
- qualityDiagnostics: stagedKibiDiagnostics,
718
- messages: [
719
- formatStagedKibiDiagnostics(stagedKibiDiagnostics),
720
- // Advisory-only findings still pass; say so explicitly.
721
- ...(!blocking
722
- ? ["✓ No violations found in staged files."]
723
- : []),
724
- ],
725
- });
726
- if (options.dryRun) {
727
- return { exitCode: 0 };
728
- }
729
- return { exitCode: blocking ? 1 : 0 };
730
- }
731
- printSnapshotResult({
732
- format: options.format,
733
- coverage: stagedCoverage,
734
- messages: [noValidatedSymbolsMessage(sourceAnalysis)],
711
+ // Run the Prolog rules over the staged knowledge: the minLinks
712
+ // ownership gate for changed source symbols, and full-check
713
+ // symbol-coverage for every changed symbol (changed source symbols
714
+ // plus symbols whose staged manifest entry changed).
715
+ let violationsRaw = [];
716
+ let coverageViolations = [];
717
+ if (allSymbols.length > 0 || activeStagedSymbolEntityIds.size > 0) {
718
+ tempCtx = await createTempKb();
719
+ await projectStagedEntities(tempCtx.prolog, snapshotEntities);
720
+ const overlayFacts = createOverlayFacts(allSymbols);
721
+ const fs = await import("node:fs/promises");
722
+ await fs.writeFile(tempCtx.overlayPath, overlayFacts, "utf8");
723
+ await fs.cp(tempCtx.overlayPath, path.join(tempCtx.kbPath, "changed_symbols.pl"));
724
+ await consultOverlay(tempCtx);
725
+ violationsRaw = await validateStagedSymbols({
726
+ minLinks,
727
+ prolog: tempCtx.prolog,
735
728
  });
736
- return { exitCode: 0 };
737
- }
738
- if (allSymbols.length === 0) {
739
- if (stagedKibiDiagnostics.length > 0) {
740
- const blocking = hasBlockingImpactDiagnostics(stagedKibiDiagnostics);
741
- printStagedResult({
742
- format: options.format,
743
- coverage: stagedCoverage,
744
- qualityDiagnostics: stagedKibiDiagnostics,
745
- messages: [
746
- formatStagedKibiDiagnostics(stagedKibiDiagnostics),
747
- // Advisory-only findings still pass; say so explicitly.
748
- ...(!blocking
749
- ? ["✓ No violations found in staged files."]
750
- : []),
751
- ],
752
- });
753
- if (options.dryRun) {
754
- return { exitCode: 0 };
755
- }
756
- return { exitCode: blocking ? 1 : 0 };
757
- }
758
- printSnapshotResult({
759
- format: options.format,
760
- coverage: stagedCoverage,
761
- messages: ["✓ No violations found in staged files."],
729
+ coverageViolations = await validateStagedSymbolCoverage({
730
+ prolog: tempCtx.prolog,
731
+ extraSymbolIds: [...activeStagedSymbolEntityIds],
762
732
  });
763
- return { exitCode: 0 };
733
+ await cleanupTempKb(tempCtx.tempDir);
764
734
  }
765
- // Create temp KB
766
- tempCtx = await createTempKb();
767
- await projectStagedEntities(tempCtx.prolog, snapshotEntities);
768
- const overlayFacts = createOverlayFacts(allSymbols);
769
- const fs = await import("node:fs/promises");
770
- await fs.writeFile(tempCtx.overlayPath, overlayFacts, "utf8");
771
- await fs.cp(tempCtx.overlayPath, path.join(tempCtx.kbPath, "changed_symbols.pl"));
772
- await consultOverlay(tempCtx);
773
- const violationsRaw = await validateStagedSymbols({
774
- minLinks,
775
- prolog: tempCtx.prolog,
776
- });
777
- const violationsFormatted = formatStagedViolations(violationsRaw);
778
- if (violationsRaw && violationsRaw.length > 0) {
779
- printSnapshotResult({
735
+ // Without changed source symbols the result was historically printed
736
+ // without re-asserting the snapshot; keep that behavior.
737
+ const print = allSymbols.length === 0 ? printStagedResult : printSnapshotResult;
738
+ const passMessage = allSymbols.length === 0
739
+ ? "✓ No violations found in staged files."
740
+ : "✓ No violations found in staged symbols.";
741
+ if (violationsRaw.length > 0 || coverageViolations.length > 0) {
742
+ print({
780
743
  format: options.format,
781
744
  coverage: stagedCoverage,
782
- violations: violationsRaw,
745
+ violations: [...violationsRaw, ...coverageViolations],
783
746
  qualityDiagnostics: stagedKibiDiagnostics,
784
747
  messages: [
785
748
  ...(stagedKibiDiagnostics.length > 0
786
749
  ? [formatStagedKibiDiagnostics(stagedKibiDiagnostics)]
787
750
  : []),
788
- violationsFormatted,
751
+ ...(coverageViolations.length > 0
752
+ ? [formatCoverageViolations(coverageViolations)]
753
+ : []),
754
+ ...(violationsRaw.length > 0
755
+ ? [formatStagedViolations(violationsRaw)]
756
+ : []),
789
757
  ],
790
758
  });
791
- await cleanupTempKb(tempCtx.tempDir);
792
- if (options.dryRun) {
793
- return { exitCode: 0 };
794
- }
795
- return { exitCode: 1 };
759
+ return { exitCode: options.dryRun ? 0 : 1 };
796
760
  }
797
761
  if (stagedKibiDiagnostics.length > 0) {
798
- await cleanupTempKb(tempCtx.tempDir);
799
762
  const blocking = hasBlockingImpactDiagnostics(stagedKibiDiagnostics);
800
- printSnapshotResult({
763
+ print({
801
764
  format: options.format,
802
765
  coverage: stagedCoverage,
803
766
  qualityDiagnostics: stagedKibiDiagnostics,
804
767
  messages: [
805
768
  formatStagedKibiDiagnostics(stagedKibiDiagnostics),
806
- ...(!blocking
807
- ? ["✓ No violations found in staged symbols."]
808
- : []),
769
+ // Advisory-only findings still pass; say so explicitly.
770
+ ...(!blocking ? [passMessage] : []),
809
771
  ],
810
772
  });
811
773
  if (options.dryRun) {
812
774
  return { exitCode: 0 };
813
775
  }
814
- if (!blocking) {
815
- return { exitCode: 0 };
816
- }
817
- return {
818
- exitCode: 1,
819
- };
776
+ return { exitCode: blocking ? 1 : 0 };
820
777
  }
821
778
  printSnapshotResult({
822
779
  format: options.format,
823
780
  coverage: stagedCoverage,
824
- messages: ["✓ No violations found in staged symbols."],
781
+ messages: [
782
+ allSymbols.length === 0 && stagedEntityResults.length === 0
783
+ ? noValidatedSymbolsMessage(sourceAnalysis)
784
+ : passMessage,
785
+ ],
825
786
  });
826
- await cleanupTempKb(tempCtx.tempDir);
827
787
  return { exitCode: 0 };
828
788
  }
829
789
  catch (err) {
@@ -1293,27 +1253,3 @@ export async function checkStrictReadiness(prolog) {
1293
1253
  }
1294
1254
  return violations;
1295
1255
  }
1296
- export async function checkSymbolCoverage(prolog) {
1297
- const violations = [];
1298
- const uncoveredResult = await prolog.query("setof(Symbol, symbol_no_req_coverage(Symbol, _), Symbols)");
1299
- if (uncoveredResult.success && uncoveredResult.bindings.Symbols) {
1300
- const symbolsStr = uncoveredResult.bindings.Symbols;
1301
- const match = symbolsStr.match(/\[(.*)\]/);
1302
- const content = getMatchGroup(match)?.trim();
1303
- if (content) {
1304
- const symbolMatches = content.matchAll(/'([^']+)'/g);
1305
- for (const symbolMatch of symbolMatches) {
1306
- const symbolId = getMatchGroup(symbolMatch);
1307
- if (!symbolId)
1308
- continue;
1309
- violations.push({
1310
- rule: "symbol-coverage",
1311
- entityId: symbolId,
1312
- description: "Code symbol is not traceable to any functional requirement.",
1313
- suggestion: "Update symbols.yaml to link this symbol to a related requirement.",
1314
- });
1315
- }
1316
- }
1317
- }
1318
- return violations;
1319
- }
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA4FA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAGD,wBAAsB,aAAa,CACjC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmH/B;AAED,wBAAsB,4BAA4B,CAChD,IAAI,GAAE,MAAM,OAAO,CAAC;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,CACT,GACxC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;;;;;;;;;;;;;;;MAkE3C;AAkGD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAepB"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAgGA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAGD,wBAAsB,aAAa,CACjC,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAmH/B;AAED,wBAAsB,4BAA4B,CAChD,IAAI,GAAE,MAAM,OAAO,CAAC;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,CACT,GACxC,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;;;;;;;;;;;;;;;;MAkE3C;AAkGD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAepB"}
@@ -29,7 +29,7 @@ import { resolveBranchAttachment, resolveReadBranchAttachment, } from "../utils/
29
29
  import { branchStoreCompilation, inspectBranchStore, storeLockTakeoverReason, uncompiledBranchStoreReason, } from "../utils/branch-store.js";
30
30
  import { resolveGitRepository, } from "../utils/git-repository-context.js";
31
31
  import { readKbManifestStatus } from "../utils/kb-manifest.js";
32
- import { hasManagedHooks, outdatedManagedHooks } from "./init-helpers.js";
32
+ import { KIBI_HOOK_BEGIN, hasManagedHooks, outdatedManagedHooks, } from "./init-helpers.js";
33
33
  import { planLegacyStorageMigration } from "./legacy-storage-migration.js";
34
34
  /**
35
35
  * First-party plugin diagnostics known without importing the package.
@@ -634,15 +634,18 @@ function checkLegacyStorage() {
634
634
  if (plan.blockers.length > 0) {
635
635
  return {
636
636
  passed: false,
637
- message: `${plan.moves.length} legacy knowledge file(s) outside .kb/ cannot be moved automatically`,
638
- remediation: `${plan.blockers.join(" ")} Compare each legacy copy with its .kb/ copy and keep the current one: replace the .kb/ copy when the legacy one is newer, or remove (git rm) the legacy copy when it is a stale duplicate. Then run: kibi migrate --yes`,
637
+ message: `${plan.blockers.length} legacy knowledge file(s) outside .kb/ cannot be moved automatically`,
638
+ remediation: `${plan.blockers.join(" ")} Then run: kibi migrate --yes`,
639
639
  };
640
640
  }
641
- if (plan.legacyConfig === "present" || plan.moves.length > 0) {
641
+ if (plan.legacyConfig === "present" ||
642
+ plan.moves.length > 0 ||
643
+ plan.removals.length > 0) {
644
+ const outside = plan.moves.length + plan.removals.length;
642
645
  return {
643
646
  passed: false,
644
- message: plan.moves.length > 0
645
- ? `${plan.moves.length} legacy knowledge file(s) still outside .kb/`
647
+ message: outside > 0
648
+ ? `${outside} legacy knowledge file(s) still outside .kb/${plan.removals.length > 0 ? ` (${plan.removals.length} identical to the .kb/ copy, which kibi migrate removes)` : ""}`
646
649
  : "Legacy .kb/config.json still present",
647
650
  remediation: "Run: kibi migrate --yes",
648
651
  };
@@ -860,6 +863,66 @@ function checkGitHooks() {
860
863
  remediation: "Run: kibi init",
861
864
  };
862
865
  }
866
+ /**
867
+ * Individual shell commands of a hook script, ignoring comments. Lines are
868
+ * split on command separators so `a && b` counts as two commands.
869
+ */
870
+ // implements REQ-git-hook-effective-install
871
+ function hookCommands(content) {
872
+ return content
873
+ .split("\n")
874
+ .filter((line) => !line.trimStart().startsWith("#"))
875
+ .flatMap((line) => line.split(/&&|\|\||;|\|/))
876
+ .map((command) => command.trim())
877
+ .filter((command) => command.length > 0);
878
+ }
879
+ // Matches `kibi <sub>`, `npx kibi <sub>`, `node_modules/.bin/kibi <sub>` and
880
+ // the template's `"$KIBI_BIN" <sub>`.
881
+ // implements REQ-git-hook-effective-install
882
+ function invokesKibi(command, subcommand) {
883
+ return new RegExp(`(?:\\bkibi|\\bKIBI_BIN)["'}]*\\s+${subcommand}(?=\\s|$)`).test(command);
884
+ }
885
+ /** The pre-commit gate the managed template runs, as a custom hook must. */
886
+ const PRE_COMMIT_DELEGATIONS = [
887
+ {
888
+ label: "kibi check-generated --staged --changed-only",
889
+ matches: (command) => invokesKibi(command, "check-generated") &&
890
+ /\s--staged(?=\s|$)/.test(command) &&
891
+ /\s--changed-only(?=\s|$)/.test(command),
892
+ },
893
+ {
894
+ label: "kibi check --staged",
895
+ matches: (command) => invokesKibi(command, "check") && /\s--staged(?=\s|$)/.test(command),
896
+ },
897
+ ];
898
+ // implements REQ-git-hook-effective-install
899
+ function checkCustomPreCommitHook(content, executable) {
900
+ const commands = hookCommands(content);
901
+ const missing = PRE_COMMIT_DELEGATIONS.filter((delegation) => !commands.some(delegation.matches)).map((delegation) => `'${delegation.label}'`);
902
+ if (missing.length > 0) {
903
+ // An unmarked file that still carries the old template header is a Kibi
904
+ // hook from before managed sections; removing it lets init reinstall it.
905
+ const oldTemplate = content.includes("# pre-commit hook for kibi")
906
+ ? " If this file is an old Kibi template, delete it and run 'kibi init' instead."
907
+ : "";
908
+ return {
909
+ passed: false,
910
+ message: `Custom pre-commit hook (no kibi-managed section) does not run ${missing.join(" or ")}; commits skip Kibi enforcement${hooksPathSuffix()}`,
911
+ remediation: `Delegate from your hook to both 'kibi check-generated --staged --changed-only' and 'kibi check --staged'. Print a copy-paste snippet that resolves a project-local kibi with: kibi init --print-hook pre-commit (kibi init never edits a custom hook).${oldTemplate}`,
912
+ };
913
+ }
914
+ if (!executable) {
915
+ return {
916
+ passed: false,
917
+ message: `Installed but not executable${hooksPathSuffix()}`,
918
+ remediation: remediationHookPaths("pre-commit"),
919
+ };
920
+ }
921
+ return {
922
+ passed: true,
923
+ message: `Custom hook delegates to 'kibi check-generated --staged --changed-only' and 'kibi check --staged'${hooksPathSuffix()}`,
924
+ };
925
+ }
863
926
  function checkPreCommitHook() {
864
927
  const postCheckoutPath = path.join(effectiveHooksDir(), "post-checkout");
865
928
  const postMergePath = path.join(effectiveHooksDir(), "post-merge");
@@ -885,6 +948,12 @@ function checkPreCommitHook() {
885
948
  const preCommitExecutable = (preCommitStats.mode & 0o111) !== 0;
886
949
  // Read hook content to determine whether it's using the new staged check
887
950
  const content = readFileSync(preCommitPath, "utf-8");
951
+ // A hook without a kibi-managed section is the user's own (hand-written,
952
+ // husky, lefthook, ...). `kibi init` never edits it, so the only useful
953
+ // check is whether it delegates to the gate the managed template runs.
954
+ if (!content.includes(KIBI_HOOK_BEGIN)) {
955
+ return checkCustomPreCommitHook(content, preCommitExecutable);
956
+ }
888
957
  // Current templates resolve the kibi binary into KIBI_BIN before invoking
889
958
  // it (git hooks do not get node_modules/.bin on PATH); legacy templates
890
959
  // invoked bare `kibi`.
@@ -963,6 +1032,27 @@ function checkPostRewriteHook() {
963
1032
  // Read hook content to verify it invokes kibi. Current templates invoke
964
1033
  // the resolved KIBI_BIN; legacy templates invoked bare `kibi sync`.
965
1034
  const content = readFileSync(postRewritePath, "utf-8");
1035
+ // A custom hook (no kibi-managed section) is never edited by init.
1036
+ // implements REQ-git-hook-effective-install
1037
+ if (!content.includes(KIBI_HOOK_BEGIN)) {
1038
+ if (!hookCommands(content).some((cmd) => invokesKibi(cmd, "sync"))) {
1039
+ return {
1040
+ passed: false,
1041
+ message: `Custom post-rewrite hook (no kibi-managed section) does not run 'kibi sync'${hooksPathSuffix()}`,
1042
+ remediation: "Delegate from your hook to 'kibi sync' after rebases. Print a copy-paste snippet that resolves a project-local kibi with: kibi init --print-hook post-rewrite (kibi init never edits a custom hook).",
1043
+ };
1044
+ }
1045
+ return postRewriteExecutable
1046
+ ? {
1047
+ passed: true,
1048
+ message: `Custom hook delegates to 'kibi sync'${hooksPathSuffix()}`,
1049
+ }
1050
+ : {
1051
+ passed: false,
1052
+ message: `Installed but not executable${hooksPathSuffix()}`,
1053
+ remediation: remediationHookPaths("post-rewrite"),
1054
+ };
1055
+ }
966
1056
  const usesKibi = content.includes('"$KIBI_BIN" sync') || content.includes("kibi sync");
967
1057
  const resolvesKibi = content.includes("KIBI_BIN=");
968
1058
  if (!usesKibi) {
@@ -4,10 +4,23 @@ export declare function createManifestFile(kbDir: string): void;
4
4
  export declare function updateGitIgnore(cwd: string): void;
5
5
  export declare function ensureSymbolsManifestFile(cwd: string): void;
6
6
  export declare function copySchemaFiles(kbDir: string, schemaSourceDir: string): Promise<void>;
7
+ export declare const KIBI_HOOK_BEGIN = "# BEGIN kibi-managed";
7
8
  export type InstallHookResult = "installed" | "updated" | "skipped-foreign" | "skipped-symlink" | "skipped-unsupported";
9
+ /** Hook names `kibi init` manages, in install order. */
10
+ export declare const MANAGED_HOOK_NAMES: readonly ["post-checkout", "post-merge", "post-rewrite", "pre-commit"];
11
+ export type ManagedHookName = (typeof MANAGED_HOOK_NAMES)[number];
12
+ export declare function isManagedHookName(value: string): value is ManagedHookName;
13
+ /**
14
+ * A copy-paste snippet for wiring Kibi into a hook that Kibi does not own
15
+ * (hand-written, husky, lefthook, ...). It reuses the managed template's
16
+ * KIBI_BIN resolution, so a project-local kibi CLI is found even though git
17
+ * does not put node_modules/.bin on PATH, and every command propagates its
18
+ * failure so the delegation is never weaker than the template it replaces.
19
+ */
20
+ export declare function hookDelegationSnippet(hook: ManagedHookName): string;
8
21
  export declare function installHook(hookPath: string, content: string): InstallHookResult;
9
22
  export interface GitHookInstallResult {
10
- hook: "pre-commit" | "post-checkout" | "post-merge" | "post-rewrite";
23
+ hook: ManagedHookName;
11
24
  result: InstallHookResult;
12
25
  }
13
26
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AA6IA,wBAAsB,gBAAgB,CACpC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,IAAI,CA0BN;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA6BjD;AAkDD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU3D;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAuBf;AASD,MAAM,MAAM,iBAAiB,GACzB,WAAW,GACX,SAAS,GACT,iBAAiB,GACjB,iBAAiB,GACjB,qBAAqB,CAAC;AAoB1B,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,iBAAiB,CAwEnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,YAAY,GAAG,eAAe,GAAG,YAAY,GAAG,cAAc,CAAC;IACrE,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAiBD;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAU/D;AAED,wEAAwE;AAExE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQzD;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAChD,oBAAoB,EAAE,CA8CxB"}
1
+ {"version":3,"file":"init-helpers.d.ts","sourceRoot":"","sources":["../../src/commands/init-helpers.ts"],"names":[],"mappings":"AA6IA,wBAAsB,gBAAgB,CACpC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,IAAI,CA0BN;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAGtD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA6BjD;AAkDD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAU3D;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED,eAAO,MAAM,eAAe,yBAAyB,CAAC;AAOtD,MAAM,MAAM,iBAAiB,GACzB,WAAW,GACX,SAAS,GACT,iBAAiB,GACjB,iBAAiB,GACjB,qBAAqB,CAAC;AAE1B,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,wEAKrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAGlE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,eAAe,CAEzE;AAED;;;;;;GAMG;AAEH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAqBnE;AAkBD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,iBAAiB,CAwEnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAiBD;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAU/D;AAED,wEAAwE;AAExE,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAQzD;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GAChD,oBAAoB,EAAE,CA8CxB"}
@@ -258,26 +258,65 @@ export async function copySchemaFiles(kbDir, schemaSourceDir) {
258
258
  }
259
259
  console.log(`✓ Ensured ${schemaFiles.length} schema files`);
260
260
  }
261
- const KIBI_HOOK_BEGIN = "# BEGIN kibi-managed";
261
+ export const KIBI_HOOK_BEGIN = "# BEGIN kibi-managed";
262
262
  const KIBI_HOOK_END = "# END kibi-managed";
263
263
  function escapeRegex(s) {
264
264
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
265
265
  }
266
+ /** Hook names `kibi init` manages, in install order. */
267
+ export const MANAGED_HOOK_NAMES = [
268
+ "post-checkout",
269
+ "post-merge",
270
+ "post-rewrite",
271
+ "pre-commit",
272
+ ];
273
+ // implements REQ-git-hook-effective-install
274
+ export function isManagedHookName(value) {
275
+ return MANAGED_HOOK_NAMES.includes(value);
276
+ }
277
+ /**
278
+ * A copy-paste snippet for wiring Kibi into a hook that Kibi does not own
279
+ * (hand-written, husky, lefthook, ...). It reuses the managed template's
280
+ * KIBI_BIN resolution, so a project-local kibi CLI is found even though git
281
+ * does not put node_modules/.bin on PATH, and every command propagates its
282
+ * failure so the delegation is never weaker than the template it replaces.
283
+ */
284
+ // implements REQ-git-hook-effective-install
285
+ export function hookDelegationSnippet(hook) {
286
+ const header = [
287
+ `# Kibi ${hook} delegation (printed by 'kibi init --print-hook ${hook}').`,
288
+ `# Paste into your existing ${hook} hook; it runs the same Kibi commands`,
289
+ "# as the Kibi-managed template.",
290
+ ].join("\n");
291
+ const guarded = (condition, commands) => {
292
+ const body = `${KIBI_BIN_RESOLVER}\n${commands}`.replace(/^(?=.)/gm, " ");
293
+ return `${header}\nif ${condition}; then\n${body}\nfi\n`;
294
+ };
295
+ switch (hook) {
296
+ case "pre-commit":
297
+ return `${header}\n${KIBI_BIN_RESOLVER}\n"$KIBI_BIN" check-generated --staged --changed-only || exit $?\n"$KIBI_BIN" check --staged || exit $?\n`;
298
+ case "post-checkout":
299
+ // Only branch checkouts refresh the KB; file checkouts are skipped.
300
+ return guarded('[ "$3" = "1" ]', '"$KIBI_BIN" sync');
301
+ case "post-merge":
302
+ return `${header}\n${KIBI_BIN_RESOLVER}\n"$KIBI_BIN" sync\n`;
303
+ case "post-rewrite":
304
+ return guarded('[ "$1" = "rebase" ]', '"$KIBI_BIN" sync');
305
+ }
306
+ }
266
307
  function hookIntegrationRecipe(hook) {
267
- // Recipes must reproduce the exact checks the Kibi template runs for that
268
- // hook, with failure propagation, or the delegation is weaker than the
269
- // template it replaces.
308
+ // Recipes must name the exact checks the Kibi template runs for that hook;
309
+ // the printed snippet adds CLI resolution and failure propagation.
310
+ const printHook = `print a copy-paste snippet that resolves a project-local kibi with 'kibi init --print-hook ${hook}'`;
270
311
  switch (hook) {
271
312
  case "pre-commit":
272
- return "delegate from that hook with 'kibi check-generated --staged --changed-only && kibi check --staged' (both commands must succeed; '&&' propagates the first failure)";
313
+ return `delegate from that hook to both 'kibi check-generated --staged --changed-only' and 'kibi check --staged' (both must succeed); ${printHook}`;
273
314
  case "post-checkout":
274
- return "delegate from that hook to 'kibi sync' on branch checkouts (the template skips file checkouts)";
315
+ return `delegate from that hook to 'kibi sync' on branch checkouts (the template skips file checkouts); ${printHook}`;
275
316
  case "post-merge":
276
- return "delegate from that hook to 'kibi sync'";
317
+ return `delegate from that hook to 'kibi sync'; ${printHook}`;
277
318
  case "post-rewrite":
278
- return "delegate from that hook to 'kibi sync' after rebase/amend rewrites";
279
- default:
280
- return "delegate to the matching Kibi template command";
319
+ return `delegate from that hook to 'kibi sync' after rebase rewrites; ${printHook}`;
281
320
  }
282
321
  }
283
322
  export function installHook(hookPath, content) {
@@ -2,6 +2,7 @@ interface InitOptions {
2
2
  hooks?: boolean;
3
3
  github?: boolean;
4
4
  badgeOnly?: boolean;
5
+ printHook?: string;
5
6
  }
6
7
  export declare function initCommand(options: InitOptions): Promise<{
7
8
  exitCode: number;
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoGA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAoDD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAkJ/B"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAuGA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAoDD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgK/B"}
@@ -24,7 +24,7 @@ import { resolveBranchAttachment } from "../utils/branch-resolver.js";
24
24
  import { readLegacyKbConfig } from "../utils/config.js";
25
25
  import { resolveGitRepository, } from "../utils/git-repository-context.js";
26
26
  import { scaffoldGitHubIntegration } from "./github-init.js";
27
- import { copySchemaFiles, createKbDirectoryStructure, createManifestFile, ensureSymbolsManifestFile, installGitHooks, updateGitIgnore, } from "./init-helpers.js";
27
+ import { MANAGED_HOOK_NAMES, copySchemaFiles, createKbDirectoryStructure, createManifestFile, ensureSymbolsManifestFile, hookDelegationSnippet, installGitHooks, isManagedHookName, updateGitIgnore, } from "./init-helpers.js";
28
28
  const __filename = fileURLToPath(import.meta.url);
29
29
  const __dirname = path.dirname(__filename);
30
30
  /**
@@ -121,6 +121,17 @@ async function initNextAction(projectRoot) {
121
121
  // implements REQ-cli-init
122
122
  // implements REQ-kibi-github-report-integration
123
123
  export async function initCommand(options) {
124
+ if (options.printHook !== undefined) {
125
+ // Print-only mode: emit a delegation snippet for a hook Kibi does not
126
+ // own and touch nothing, not even the repository context.
127
+ // implements REQ-git-hook-effective-install
128
+ if (!isManagedHookName(options.printHook)) {
129
+ console.error(`Error: unknown hook '${options.printHook}' for --print-hook. Expected one of: ${MANAGED_HOOK_NAMES.join(", ")}.`);
130
+ return { exitCode: 1 };
131
+ }
132
+ process.stdout.write(hookDelegationSnippet(options.printHook));
133
+ return { exitCode: 0 };
134
+ }
124
135
  if (options.badgeOnly === true && options.github !== true) {
125
136
  console.error("Error: --badge-only requires --github. The recommended integration is `kibi init --github` (badge + full report).");
126
137
  return { exitCode: 1 };
@@ -9,7 +9,10 @@ import { type EntityLane } from "../utils/kb-paths.js";
9
9
  * - legacy `.kb/config.json` is read (never trusted blindly);
10
10
  * - configured or default legacy paths are discovered on disk;
11
11
  * - files move into `.kb/<lane>/` without content changes;
12
- * - destination conflicts abort before any file moves;
12
+ * - a legacy copy identical to its destination is removed instead of moved;
13
+ * - other destination conflicts abort before any file moves, each with the
14
+ * resolution Kibi can tell (a stale subset of the symbols manifest, a
15
+ * generated coordinates file, or a real conflict);
13
16
  * - partial migrations resume safely (already-moved files are no-ops);
14
17
  * - on success the legacy config is retired.
15
18
  */
@@ -22,9 +25,34 @@ export interface StorageMigrationPlan {
22
25
  /** Lifecycle state preserved into the canonical manifest. */
23
26
  schemaVersion: number | null;
24
27
  semanticAdvisorBackfill: SemanticAdvisorBackfill | null;
28
+ /**
29
+ * Legacy copies byte-identical to their `.kb/` destination: the migration
30
+ * removes them instead of moving them.
31
+ */
32
+ removals: StorageMigrationRemoval[];
33
+ /** Existing destinations that block a move, with how to resolve each. */
34
+ conflicts: StorageMigrationConflict[];
25
35
  /** Why the migration cannot proceed automatically, if blocked. */
26
36
  blockers: string[];
27
37
  }
38
+ export interface StorageMigrationRemoval {
39
+ /** The legacy copy to remove. */
40
+ path: string;
41
+ /** The `.kb/` file it duplicates. */
42
+ duplicateOf: string;
43
+ }
44
+ export interface StorageMigrationConflict {
45
+ from: string;
46
+ to: string;
47
+ /**
48
+ * `stale_subset`: every symbol of the legacy manifest is already in the
49
+ * `.kb/` one. `generated`: a symbol-coordinates file, which sync
50
+ * regenerates. `ambiguous`: Kibi cannot tell which legacy file is meant.
51
+ * `conflict`: different content Kibi cannot classify.
52
+ */
53
+ kind: "stale_subset" | "generated" | "ambiguous" | "conflict";
54
+ resolution: string;
55
+ }
28
56
  export interface StorageMigrationMove {
29
57
  from: string;
30
58
  to: string;
@@ -34,6 +62,13 @@ export interface StorageMigrationMove {
34
62
  export declare function planLegacyStorageMigration(cwd: string): StorageMigrationPlan;
35
63
  export interface StorageMigrationResult {
36
64
  movedFiles: string[];
65
+ /** Legacy copies removed because they were identical to their destination. */
66
+ removedFiles: string[];
67
+ /**
68
+ * Moved files Git does not track yet. Each got a pending-source receipt, so
69
+ * `kibi sync` compiles them before they are staged; empty outside Git.
70
+ */
71
+ untrackedMovedFiles: string[];
37
72
  retiredLegacyConfig: boolean;
38
73
  manifestPath: string;
39
74
  schemaVersion: number;
@@ -47,6 +82,16 @@ export interface StorageMigrationResult {
47
82
  export declare function applyLegacyStorageMigration(cwd: string, plan: StorageMigrationPlan): StorageMigrationResult;
48
83
  /** Rewrite recovery pending-source receipts after a storage cutover. */
49
84
  export declare function rewritePendingSourceReceiptPaths(cwd: string, moves: readonly StorageMigrationMove[]): number;
85
+ /**
86
+ * Bind every moved file Git does not track to its bytes with a
87
+ * pending-source receipt, the record kb_upsert writes for the files it
88
+ * creates. `kibi sync` compiles tracked sources plus receipted ones, so the
89
+ * moved knowledge compiles at once; Kibi never stages anything itself, and
90
+ * each receipt retires when Git starts tracking its file. Later migration
91
+ * rewrites rebind the receipt they find. Covers files a previous,
92
+ * interrupted run moved too. Returns the untracked destinations.
93
+ */
94
+ export declare function writeMovedSourceReceipts(cwd: string, moves: readonly StorageMigrationMove[]): string[];
50
95
  /** True when the workspace still needs the legacy storage migration. */
51
96
  export declare function needsLegacyStorageMigration(cwd: string): boolean;
52
97
  /** Entity lanes that must exist for the canonical layout to be complete. */
@@ -1 +1 @@
1
- {"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"AAoCA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AA2BD,6DAA6D;AAC7D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAwH5E;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,oBAAoB,GACzB,sBAAsB,CAwDxB;AA+BD,wEAAwE;AACxE,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,CAwCR;AA8BD,wEAAwE;AACxE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOhE;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAI/D"}
1
+ {"version":3,"file":"legacy-storage-migration.d.ts","sourceRoot":"","sources":["../../src/commands/legacy-storage-migration.ts"],"names":[],"mappings":"AAwCA,OAAO,EACL,KAAK,uBAAuB,EAI7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAGL,KAAK,UAAU,EAGhB,MAAM,sBAAsB,CAAC;AAI9B;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6DAA6D;IAC7D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACxD;;;OAGG;IACH,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,yEAAyE;IACzE,SAAS,EAAE,wBAAwB,EAAE,CAAC;IACtC,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB;IACtC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX;;;;;OAKG;IACH,IAAI,EAAE,cAAc,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC;IAC9D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;CAC9B;AA2BD,6DAA6D;AAC7D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAkJ5E;AAoID,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,uBAAuB,EAAE,uBAAuB,CAAC;CAClD;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,oBAAoB,GACzB,sBAAsB,CAyExB;AAkCD,wEAAwE;AACxE,wBAAgB,gCAAgC,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,CAwCR;AAqBD;;;;;;;;GAQG;AAEH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,SAAS,oBAAoB,EAAE,GACrC,MAAM,EAAE,CAqBV;AA8BD,wEAAwE;AACxE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAShE;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAI/D"}