kibi-cli 0.12.7 → 0.14.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 (114) hide show
  1. package/dist/cli.js +1 -0
  2. package/dist/commands/check.d.ts +1 -0
  3. package/dist/commands/check.d.ts.map +1 -1
  4. package/dist/commands/check.js +130 -82
  5. package/dist/commands/discovery-shared.d.ts +1 -0
  6. package/dist/commands/discovery-shared.d.ts.map +1 -1
  7. package/dist/commands/discovery-shared.js +2 -191
  8. package/dist/commands/discovery-table.d.ts +3 -0
  9. package/dist/commands/discovery-table.d.ts.map +1 -0
  10. package/dist/commands/discovery-table.js +204 -0
  11. package/dist/commands/sync/discovery.d.ts.map +1 -1
  12. package/dist/commands/sync/discovery.js +8 -1
  13. package/dist/commands/sync/extraction.d.ts +6 -1
  14. package/dist/commands/sync/extraction.d.ts.map +1 -1
  15. package/dist/commands/sync/extraction.js +7 -3
  16. package/dist/commands/sync/persistence.d.ts.map +1 -1
  17. package/dist/commands/sync/persistence.js +39 -5
  18. package/dist/extractors/manifest.d.ts.map +1 -1
  19. package/dist/extractors/manifest.js +18 -0
  20. package/dist/extractors/markdown.d.ts +6 -0
  21. package/dist/extractors/markdown.d.ts.map +1 -1
  22. package/dist/extractors/symbols-coordinator.d.ts +1 -1
  23. package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
  24. package/dist/extractors/symbols-ts.js +62 -0
  25. package/dist/prolog.d.ts.map +1 -1
  26. package/dist/prolog.js +11 -7
  27. package/dist/public/impact/analyzer.d.ts +3 -0
  28. package/dist/public/impact/analyzer.d.ts.map +1 -0
  29. package/dist/public/impact/analyzer.js +54 -0
  30. package/dist/public/impact/coverage-depth-quality.d.ts +4 -0
  31. package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -0
  32. package/dist/public/impact/coverage-depth-quality.js +179 -0
  33. package/dist/public/impact/diagnostics.d.ts +7 -0
  34. package/dist/public/impact/diagnostics.d.ts.map +1 -0
  35. package/dist/public/impact/diagnostics.js +122 -0
  36. package/dist/public/impact/diff-meaning.d.ts +2 -0
  37. package/dist/public/impact/diff-meaning.d.ts.map +1 -0
  38. package/dist/public/impact/diff-meaning.js +101 -0
  39. package/dist/public/impact/full-kb-quality.d.ts +10 -0
  40. package/dist/public/impact/full-kb-quality.d.ts.map +1 -0
  41. package/dist/public/impact/full-kb-quality.js +158 -0
  42. package/dist/public/impact/manifest.d.ts +5 -0
  43. package/dist/public/impact/manifest.d.ts.map +1 -0
  44. package/dist/public/impact/manifest.js +43 -0
  45. package/dist/public/impact/requirement-quality.d.ts +4 -0
  46. package/dist/public/impact/requirement-quality.d.ts.map +1 -0
  47. package/dist/public/impact/requirement-quality.js +216 -0
  48. package/dist/public/impact/source-changes.d.ts +5 -0
  49. package/dist/public/impact/source-changes.d.ts.map +1 -0
  50. package/dist/public/impact/source-changes.js +129 -0
  51. package/dist/public/impact/summaries.d.ts +6 -0
  52. package/dist/public/impact/summaries.d.ts.map +1 -0
  53. package/dist/public/impact/summaries.js +68 -0
  54. package/dist/public/impact/symbol-quality-model.d.ts +21 -0
  55. package/dist/public/impact/symbol-quality-model.d.ts.map +1 -0
  56. package/dist/public/impact/symbol-quality-model.js +116 -0
  57. package/dist/public/impact/symbol-quality.d.ts +4 -0
  58. package/dist/public/impact/symbol-quality.d.ts.map +1 -0
  59. package/dist/public/impact/symbol-quality.js +159 -0
  60. package/dist/public/impact/types.d.ts +79 -0
  61. package/dist/public/impact/types.d.ts.map +1 -0
  62. package/dist/public/impact/types.js +1 -0
  63. package/dist/public/impact-diagnostics.d.ts +7 -0
  64. package/dist/public/impact-diagnostics.d.ts.map +1 -0
  65. package/dist/public/impact-diagnostics.js +5 -0
  66. package/dist/public/schemas/entity.d.ts.map +1 -1
  67. package/dist/public/schemas/entity.js +21 -0
  68. package/dist/public/skills/kibi-usage/SKILL.md +20 -5
  69. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  70. package/dist/public/skills/kibi-usage/resources/relationship-directions.md +30 -0
  71. package/dist/public/skills/kibi-usage/resources/workflows.md +14 -0
  72. package/dist/public/symbol-granularity.d.ts +1 -1
  73. package/dist/public/symbol-granularity.d.ts.map +1 -1
  74. package/dist/public/symbol-granularity.js +2 -0
  75. package/dist/traceability/evidence-model.d.ts +1 -1
  76. package/dist/traceability/evidence-model.d.ts.map +1 -1
  77. package/dist/traceability/evidence-model.js +1 -1
  78. package/dist/traceability/staged-diagnostics.d.ts +6 -6
  79. package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
  80. package/dist/traceability/staged-diagnostics.js +12 -6
  81. package/dist/traceability/staged-impact-contract.js +1 -1
  82. package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
  83. package/dist/traceability/staged-symbols-manifest.js +23 -1
  84. package/dist/traceability/symbol-extract.d.ts.map +1 -1
  85. package/dist/traceability/symbol-extract.js +41 -0
  86. package/dist/traceability/temp-kb.d.ts.map +1 -1
  87. package/dist/types/entities.d.ts +5 -1
  88. package/dist/types/entities.d.ts.map +1 -1
  89. package/dist/utils/prolog-query-plan-safety.d.ts +8 -0
  90. package/dist/utils/prolog-query-plan-safety.d.ts.map +1 -0
  91. package/dist/utils/prolog-query-plan-safety.js +47 -0
  92. package/dist/utils/rule-registry.d.ts.map +1 -1
  93. package/dist/utils/rule-registry.js +12 -0
  94. package/package.json +7 -9
  95. package/schema/entities.pl +4 -0
  96. package/src/public/impact/analyzer.ts +90 -0
  97. package/src/public/impact/coverage-depth-quality.ts +268 -0
  98. package/src/public/impact/diagnostics.ts +192 -0
  99. package/src/public/impact/diff-meaning.ts +121 -0
  100. package/src/public/impact/full-kb-quality.ts +203 -0
  101. package/src/public/impact/manifest.ts +57 -0
  102. package/src/public/impact/requirement-quality.ts +282 -0
  103. package/src/public/impact/source-changes.ts +180 -0
  104. package/src/public/impact/summaries.ts +100 -0
  105. package/src/public/impact/symbol-quality-model.ts +171 -0
  106. package/src/public/impact/symbol-quality.ts +207 -0
  107. package/src/public/impact/types.ts +99 -0
  108. package/src/public/impact-diagnostics.ts +22 -0
  109. package/src/public/schemas/entity.ts +23 -0
  110. package/src/public/skills/kibi-usage/SKILL.md +20 -5
  111. package/src/public/skills/kibi-usage/resources/fact-lanes.md +5 -5
  112. package/src/public/skills/kibi-usage/resources/relationship-directions.md +30 -0
  113. package/src/public/skills/kibi-usage/resources/workflows.md +14 -0
  114. package/src/public/symbol-granularity.ts +4 -0
@@ -0,0 +1,43 @@
1
+ import { existsSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { extractFromManifest } from "../../extractors/manifest.js";
4
+ import { loadConfig } from "../../utils/config.js";
5
+ import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
6
+ import { normalizeSourceFile } from "./source-changes.js";
7
+ function toTraceabilityRelationships(relationships) {
8
+ return relationships
9
+ .filter((relationship) => isTraceabilityRelationshipType(relationship.type))
10
+ .map((relationship) => ({ type: relationship.type, to: relationship.to }));
11
+ }
12
+ export function readImpactManifestResults(workspaceRoot) {
13
+ const config = loadConfig(workspaceRoot);
14
+ const symbolsPath = config.paths.symbols;
15
+ if (!symbolsPath)
16
+ return [];
17
+ const absolutePath = path.isAbsolute(symbolsPath)
18
+ ? symbolsPath
19
+ : path.resolve(workspaceRoot, symbolsPath);
20
+ if (!existsSync(absolutePath))
21
+ return [];
22
+ return extractFromManifest(absolutePath).map((result) => ({
23
+ entity: {
24
+ ...result.entity,
25
+ source: normalizeSourceFile(workspaceRoot, result.entity.source),
26
+ },
27
+ relationships: result.relationships,
28
+ ...(result.sourceFile !== undefined
29
+ ? { sourceFile: normalizeSourceFile(workspaceRoot, result.sourceFile) }
30
+ : {}),
31
+ }));
32
+ }
33
+ export function createImpactManifestLookup(manifestResults) {
34
+ const manifestLookup = new Map();
35
+ for (const result of manifestResults) {
36
+ const sourceFile = result.sourceFile ?? result.entity.source;
37
+ manifestLookup.set(`${sourceFile}:${result.entity.title}`, {
38
+ id: result.entity.id,
39
+ relationships: toTraceabilityRelationships(result.relationships),
40
+ });
41
+ }
42
+ return manifestLookup;
43
+ }
@@ -0,0 +1,4 @@
1
+ import type { KibiImpactDiagnostic } from "../../traceability/staged-diagnostics.js";
2
+ import type { RequirementQualityDiagnosticsOptions } from "./types.js";
3
+ export declare function createRequirementQualityDiagnostics(options: RequirementQualityDiagnosticsOptions): KibiImpactDiagnostic[];
4
+ //# sourceMappingURL=requirement-quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-quality.d.ts","sourceRoot":"","sources":["../../../src/public/impact/requirement-quality.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AACrF,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,YAAY,CAAC;AA+QvE,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,oCAAoC,GAC5C,oBAAoB,EAAE,CAMxB"}
@@ -0,0 +1,216 @@
1
+ const CURRENT_REQUIREMENT_STATUSES = new Set([
2
+ "open",
3
+ "in_progress",
4
+ "active",
5
+ "accepted",
6
+ ]);
7
+ const TEST_LIKE_REQUIREMENT_STATUSES = new Set([
8
+ "passing",
9
+ "failing",
10
+ "skipped",
11
+ "pending",
12
+ ]);
13
+ const STRICT_RELATIONSHIP_TYPES = new Set([
14
+ "constrains",
15
+ "requires_property",
16
+ "requires_predicate",
17
+ ]);
18
+ const BROAD_REQUIREMENT_THRESHOLDS = {
19
+ implementingSymbols: 8,
20
+ scenarios: 6,
21
+ tests: 8,
22
+ dependentRequirements: 5,
23
+ };
24
+ const NORMATIVE_INDICATOR_PATTERNS = [
25
+ ["must", /\bmust\b/i],
26
+ ["only", /\bonly\b/i],
27
+ ["never", /\bnever\b/i],
28
+ ["limit", /\blimits?\b/i],
29
+ ["minimum", /\bminimum\b/i],
30
+ ["maximum", /\bmaximum\b/i],
31
+ ["at least", /\bat\s+least\b/i],
32
+ ["at most", /\bat\s+most\b/i],
33
+ ];
34
+ function isRequirement(result) {
35
+ return result.entity.type === "req";
36
+ }
37
+ function isCurrentRequirement(result) {
38
+ return (isRequirement(result) &&
39
+ CURRENT_REQUIREMENT_STATUSES.has(result.entity.status));
40
+ }
41
+ function sortedUnique(values) {
42
+ return [...new Set(values)].sort();
43
+ }
44
+ function entityTypeById(results) {
45
+ const types = new Map();
46
+ for (const result of results) {
47
+ types.set(result.entity.id, result.entity.type);
48
+ }
49
+ return types;
50
+ }
51
+ function targetHasType(targetId, type, typesByEntityId) {
52
+ const targetType = typesByEntityId.get(targetId);
53
+ return targetType === undefined || targetType === type;
54
+ }
55
+ function collectFanout(requirementId, results, typesByEntityId) {
56
+ const implementingSymbolIds = [];
57
+ const scenarioIds = [];
58
+ const testIds = [];
59
+ const dependentRequirementIds = [];
60
+ for (const result of results) {
61
+ for (const relationship of result.relationships) {
62
+ if (relationship.type === "implements" &&
63
+ relationship.to === requirementId) {
64
+ implementingSymbolIds.push(result.entity.id);
65
+ }
66
+ if (result.entity.id === requirementId &&
67
+ relationship.type === "specified_by" &&
68
+ targetHasType(relationship.to, "scenario", typesByEntityId)) {
69
+ scenarioIds.push(relationship.to);
70
+ }
71
+ if (result.entity.id === requirementId &&
72
+ (relationship.type === "verified_by" ||
73
+ relationship.type === "covered_by") &&
74
+ targetHasType(relationship.to, "test", typesByEntityId)) {
75
+ testIds.push(relationship.to);
76
+ }
77
+ if (relationship.type === "depends_on") {
78
+ if (result.entity.id === requirementId &&
79
+ targetHasType(relationship.to, "req", typesByEntityId)) {
80
+ dependentRequirementIds.push(relationship.to);
81
+ }
82
+ if (relationship.to === requirementId && result.entity.type === "req") {
83
+ dependentRequirementIds.push(result.entity.id);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ return {
89
+ implementingSymbolIds: sortedUnique(implementingSymbolIds),
90
+ scenarioIds: sortedUnique(scenarioIds),
91
+ testIds: sortedUnique(testIds),
92
+ dependentRequirementIds: sortedUnique(dependentRequirementIds),
93
+ };
94
+ }
95
+ function exceedsBroadThreshold(fanout) {
96
+ return (fanout.implementingSymbolIds.length >
97
+ BROAD_REQUIREMENT_THRESHOLDS.implementingSymbols ||
98
+ fanout.scenarioIds.length > BROAD_REQUIREMENT_THRESHOLDS.scenarios ||
99
+ fanout.testIds.length > BROAD_REQUIREMENT_THRESHOLDS.tests ||
100
+ fanout.dependentRequirementIds.length >
101
+ BROAD_REQUIREMENT_THRESHOLDS.dependentRequirements);
102
+ }
103
+ function isUmbrellaRequirement(result) {
104
+ const tags = result.entity.tags ?? [];
105
+ return tags.some((tag) => tag === "umbrella" || tag === "epic");
106
+ }
107
+ function createBroadRequirementDiagnostics(results) {
108
+ const typesByEntityId = entityTypeById(results);
109
+ return results.flatMap((result) => {
110
+ if (!isCurrentRequirement(result))
111
+ return [];
112
+ const fanout = collectFanout(result.entity.id, results, typesByEntityId);
113
+ if (!exceedsBroadThreshold(fanout))
114
+ return [];
115
+ if (isUmbrellaRequirement(result))
116
+ return [];
117
+ return [
118
+ {
119
+ id: "broad_requirement_review",
120
+ severity: "review",
121
+ blocking: false,
122
+ category: "requirement",
123
+ entityId: result.entity.id,
124
+ source: result.entity.source,
125
+ files: [result.entity.source],
126
+ docs: ["docs/entity-schema.md"],
127
+ message: `Requirement ${result.entity.id} has broad fanout that exceeds requirement review thresholds.`,
128
+ suggestion: "Split this into smaller behavioral requirements, or keep an explicit umbrella/epic tag when broad ownership is intentional.",
129
+ evidence: {
130
+ implementingSymbolIds: fanout.implementingSymbolIds,
131
+ implementingSymbolCount: fanout.implementingSymbolIds.length,
132
+ implementingSymbolThreshold: BROAD_REQUIREMENT_THRESHOLDS.implementingSymbols,
133
+ scenarioIds: fanout.scenarioIds,
134
+ scenarioCount: fanout.scenarioIds.length,
135
+ scenarioThreshold: BROAD_REQUIREMENT_THRESHOLDS.scenarios,
136
+ testIds: fanout.testIds,
137
+ testCount: fanout.testIds.length,
138
+ testThreshold: BROAD_REQUIREMENT_THRESHOLDS.tests,
139
+ dependentRequirementIds: fanout.dependentRequirementIds,
140
+ dependentRequirementCount: fanout.dependentRequirementIds.length,
141
+ dependentRequirementThreshold: BROAD_REQUIREMENT_THRESHOLDS.dependentRequirements,
142
+ },
143
+ },
144
+ ];
145
+ });
146
+ }
147
+ function createRequirementStatusDiagnostics(results) {
148
+ return results.flatMap((result) => {
149
+ if (!isRequirement(result))
150
+ return [];
151
+ if (!TEST_LIKE_REQUIREMENT_STATUSES.has(result.entity.status))
152
+ return [];
153
+ return [
154
+ {
155
+ id: "requirement_status_review",
156
+ severity: "review",
157
+ blocking: false,
158
+ category: "status",
159
+ entityId: result.entity.id,
160
+ source: result.entity.source,
161
+ files: [result.entity.source],
162
+ docs: ["docs/entity-schema.md"],
163
+ message: `Requirement ${result.entity.id} uses test-like status ${result.entity.status}.`,
164
+ suggestion: "Use requirement lifecycle statuses such as open, in_progress, active, accepted, closed, deprecated, or superseded; keep passing/failing statuses on test entities.",
165
+ evidence: { status: result.entity.status },
166
+ },
167
+ ];
168
+ });
169
+ }
170
+ function strictRelationshipTypes(result) {
171
+ return sortedUnique(result.relationships
172
+ .filter((relationship) => STRICT_RELATIONSHIP_TYPES.has(relationship.type))
173
+ .map((relationship) => relationship.type));
174
+ }
175
+ function matchedNormativeIndicators(text) {
176
+ return NORMATIVE_INDICATOR_PATTERNS.filter((entry) => entry[1].test(text)).map((entry) => entry[0]);
177
+ }
178
+ function createStrictFactModelingDiagnostics(options) {
179
+ return options.manifestResults.flatMap((result) => {
180
+ if (!isCurrentRequirement(result))
181
+ return [];
182
+ if (options.hardViolationEntityIds?.has(result.entity.id))
183
+ return [];
184
+ const relationshipTypes = strictRelationshipTypes(result);
185
+ if (relationshipTypes.length > 0)
186
+ return [];
187
+ const indicators = matchedNormativeIndicators(result.entity.title);
188
+ if (indicators.length === 0)
189
+ return [];
190
+ return [
191
+ {
192
+ id: "strict_fact_modeling_review",
193
+ severity: "review",
194
+ blocking: false,
195
+ category: "fact",
196
+ entityId: result.entity.id,
197
+ source: result.entity.source,
198
+ files: [result.entity.source],
199
+ docs: ["docs/modeling-cheatsheet.md", "docs/error-reference.md"],
200
+ message: `Requirement ${result.entity.id} contains normative prose but has no strict fact or predicate relationship.`,
201
+ suggestion: "Use kb_model_requirement for strict property facts, or kb_suggest_predicates when the claim fits a predicate schema.",
202
+ evidence: {
203
+ matchedIndicators: indicators,
204
+ strictRelationshipTypes: relationshipTypes,
205
+ },
206
+ },
207
+ ];
208
+ });
209
+ }
210
+ export function createRequirementQualityDiagnostics(options) {
211
+ return [
212
+ ...createBroadRequirementDiagnostics(options.manifestResults),
213
+ ...createRequirementStatusDiagnostics(options.manifestResults),
214
+ ...createStrictFactModelingDiagnostics(options),
215
+ ];
216
+ }
@@ -0,0 +1,5 @@
1
+ import type { ChangedFileImpactOptions, SourceChange } from "./types.js";
2
+ export declare function normalizeSourceFile(workspaceRoot: string, sourceFile: string): string;
3
+ export declare function uniqueSorted(values: Iterable<string>): string[];
4
+ export declare function collectSourceChanges(options: ChangedFileImpactOptions): SourceChange[];
5
+ //# sourceMappingURL=source-changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source-changes.d.ts","sourceRoot":"","sources":["../../../src/public/impact/source-changes.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAiBzE,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAE/D;AA6HD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,wBAAwB,GAChC,YAAY,EAAE,CAchB"}
@@ -0,0 +1,129 @@
1
+ import { execSync } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import * as path from "node:path";
4
+ import { getStagedFiles, parseHunksFromDiff, } from "../../traceability/git-staged.js";
5
+ import { hasMeaningfulSourceDiff } from "./diff-meaning.js";
6
+ const SOURCE_EXTENSIONS = new Set([
7
+ ".ts",
8
+ ".tsx",
9
+ ".js",
10
+ ".jsx",
11
+ ".mts",
12
+ ".cts",
13
+ ".mjs",
14
+ ".cjs",
15
+ ]);
16
+ function isSupportedSourcePath(filePath) {
17
+ return SOURCE_EXTENSIONS.has(path.extname(filePath));
18
+ }
19
+ export function normalizeSourceFile(workspaceRoot, sourceFile) {
20
+ const relativePath = path.isAbsolute(sourceFile)
21
+ ? path.relative(workspaceRoot, sourceFile)
22
+ : sourceFile;
23
+ return relativePath.split(path.sep).join("/");
24
+ }
25
+ export function uniqueSorted(values) {
26
+ return [...new Set(values)].sort();
27
+ }
28
+ function escapeGitPath(filePath) {
29
+ return filePath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
30
+ }
31
+ function runGit(workspaceRoot, command) {
32
+ return execSync(command, { cwd: workspaceRoot, encoding: "utf8" });
33
+ }
34
+ function readWorkingTreeSource(workspaceRoot, sourceFile) {
35
+ const absolutePath = path.resolve(workspaceRoot, sourceFile);
36
+ if (!existsSync(absolutePath))
37
+ return null;
38
+ return readFileSync(absolutePath, "utf8");
39
+ }
40
+ function fullFileHunk(content) {
41
+ return [{ start: 1, end: Math.max(1, content.split(/\r?\n/).length) }];
42
+ }
43
+ function getWorkingTreeDiffSourceFiles(workspaceRoot) {
44
+ try {
45
+ return uniqueSorted(runGit(workspaceRoot, "git diff --name-only --diff-filter=ACMR")
46
+ .split(/\r?\n/)
47
+ .map((filePath) => normalizeSourceFile(workspaceRoot, filePath.trim()))
48
+ .filter((filePath) => filePath.length > 0)
49
+ .filter(isSupportedSourcePath));
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ }
55
+ function getWorkingTreeHunks(workspaceRoot, sourceFile) {
56
+ try {
57
+ const diffText = runGit(workspaceRoot, `git diff -U0 -- "${escapeGitPath(sourceFile)}"`);
58
+ return parseHunksFromDiff(diffText);
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ }
64
+ function getStagedSourceChanges(workspaceRoot, sourceFileFilter) {
65
+ const stagedFiles = getStagedFiles((command, options) => execSync(command, { ...options, cwd: workspaceRoot }));
66
+ return stagedFiles
67
+ .filter((file) => isSupportedSourcePath(file.path))
68
+ .filter((file) => sourceFileFilter.size === 0 || sourceFileFilter.has(file.path))
69
+ .flatMap((file) => {
70
+ if (file.content === undefined || file.hunkRanges.length === 0)
71
+ return [];
72
+ if (file.diffText !== undefined &&
73
+ !hasMeaningfulSourceDiff(file.diffText)) {
74
+ return [];
75
+ }
76
+ return [
77
+ {
78
+ file: file.path,
79
+ status: file.status,
80
+ hunkRanges: file.hunkRanges,
81
+ content: file.content,
82
+ },
83
+ ];
84
+ });
85
+ }
86
+ function getWorkingTreeSourceChanges(workspaceRoot, sourceFileFilter) {
87
+ const sourceFiles = sourceFileFilter.size > 0
88
+ ? [...sourceFileFilter]
89
+ : getWorkingTreeDiffSourceFiles(workspaceRoot);
90
+ return sourceFiles.flatMap((sourceFile) => {
91
+ const content = readWorkingTreeSource(workspaceRoot, sourceFile);
92
+ if (content === null)
93
+ return [];
94
+ const diffText = runGit(workspaceRoot, `git diff -U0 -- "${escapeGitPath(sourceFile)}"`);
95
+ if (!hasMeaningfulSourceDiff(diffText))
96
+ return [];
97
+ const hunkRanges = getWorkingTreeHunks(workspaceRoot, sourceFile);
98
+ if (hunkRanges.length === 0)
99
+ return [];
100
+ return [{ file: sourceFile, status: "M", hunkRanges, content }];
101
+ });
102
+ }
103
+ function getExplicitSourceChanges(workspaceRoot, sourceFileFilter) {
104
+ return [...sourceFileFilter].flatMap((sourceFile) => {
105
+ const content = readWorkingTreeSource(workspaceRoot, sourceFile);
106
+ if (content === null)
107
+ return [];
108
+ return [
109
+ {
110
+ file: sourceFile,
111
+ status: "M",
112
+ hunkRanges: fullFileHunk(content),
113
+ content,
114
+ },
115
+ ];
116
+ });
117
+ }
118
+ export function collectSourceChanges(options) {
119
+ const workspaceRoot = path.resolve(options.workspaceRoot);
120
+ const sourceFileFilter = new Set((options.sourceFiles ?? [])
121
+ .map((sourceFile) => normalizeSourceFile(workspaceRoot, sourceFile))
122
+ .filter(isSupportedSourcePath));
123
+ if (options.staged)
124
+ return getStagedSourceChanges(workspaceRoot, sourceFileFilter);
125
+ if (options.includeWorkingTreeDiff) {
126
+ return getWorkingTreeSourceChanges(workspaceRoot, sourceFileFilter);
127
+ }
128
+ return getExplicitSourceChanges(workspaceRoot, sourceFileFilter);
129
+ }
@@ -0,0 +1,6 @@
1
+ import type { ExtractionResult } from "../../extractors/markdown.js";
2
+ import type { ImpactExtractedSymbol, ImpactLinkedEntity, SymbolsByFile } from "./types.js";
3
+ export declare function formatExtractedSymbols(symbolsByFile: SymbolsByFile): ImpactExtractedSymbol[];
4
+ export declare function collectLinkedEntities(symbolsByFile: SymbolsByFile, manifestResults: readonly ExtractionResult[], activeSourceFiles: ReadonlySet<string>): ImpactLinkedEntity[];
5
+ export declare function buildNextActions(sourceFiles: readonly string[]): string[];
6
+ //# sourceMappingURL=summaries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaries.d.ts","sourceRoot":"","sources":["../../../src/public/impact/summaries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAYpB,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,aAAa,GAC3B,qBAAqB,EAAE,CAazB;AAED,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,SAAS,gBAAgB,EAAE,EAC5C,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,GACrC,kBAAkB,EAAE,CA4CtB;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAYzE"}
@@ -0,0 +1,68 @@
1
+ import { isTraceabilityRelationshipType } from "../symbol-granularity.js";
2
+ import { uniqueSorted } from "./source-changes.js";
3
+ function linkedEntityIdsFor(symbol) {
4
+ return uniqueSorted((symbol.relationships ?? [])
5
+ .filter((relationship) => isTraceabilityRelationshipType(relationship.type))
6
+ .map((relationship) => relationship.to));
7
+ }
8
+ export function formatExtractedSymbols(symbolsByFile) {
9
+ return [...symbolsByFile.values()]
10
+ .flatMap((symbols) => [...symbols])
11
+ .map((symbol) => ({
12
+ id: symbol.id,
13
+ name: symbol.name,
14
+ kind: symbol.kind,
15
+ role: symbol.role,
16
+ location: symbol.location,
17
+ hunkRanges: symbol.hunkRanges,
18
+ linkedEntityIds: linkedEntityIdsFor(symbol),
19
+ }))
20
+ .sort((left, right) => left.name.localeCompare(right.name));
21
+ }
22
+ export function collectLinkedEntities(symbolsByFile, manifestResults, activeSourceFiles) {
23
+ const linkedEntities = new Map();
24
+ const add = (entity) => {
25
+ linkedEntities.set(`${entity.sourceSymbolId}:${entity.relationshipType}:${entity.id}`, entity);
26
+ };
27
+ for (const symbols of symbolsByFile.values()) {
28
+ for (const symbol of symbols) {
29
+ for (const relationship of symbol.relationships ?? []) {
30
+ if (!isTraceabilityRelationshipType(relationship.type))
31
+ continue;
32
+ add({
33
+ id: relationship.to,
34
+ relationshipType: relationship.type,
35
+ sourceSymbolId: symbol.id,
36
+ sourceSymbolName: symbol.name,
37
+ });
38
+ }
39
+ }
40
+ }
41
+ for (const result of manifestResults) {
42
+ if (result.sourceFile === undefined ||
43
+ !activeSourceFiles.has(result.sourceFile)) {
44
+ continue;
45
+ }
46
+ for (const relationship of result.relationships) {
47
+ if (!isTraceabilityRelationshipType(relationship.type))
48
+ continue;
49
+ add({
50
+ id: relationship.to,
51
+ relationshipType: relationship.type,
52
+ sourceSymbolId: result.entity.id,
53
+ sourceSymbolName: result.entity.title,
54
+ });
55
+ }
56
+ }
57
+ return [...linkedEntities.values()].sort((left, right) => left.id.localeCompare(right.id));
58
+ }
59
+ export function buildNextActions(sourceFiles) {
60
+ if (sourceFiles.length === 0)
61
+ return [];
62
+ return [
63
+ `kb_search({ query: "${sourceFiles.join(" ")}" }) to discover requirements/tests that may describe the changed behavior.`,
64
+ ...sourceFiles.map((sourceFile) => `kb_query({ sourceFile: "${sourceFile}" }) to inspect exact source-linked Kibi entities before deciding whether requirements/tests need updates.`),
65
+ "Update narrower symbols, requirements, tests, or strict facts when diagnostics show broad or stale modeling.",
66
+ "rerun kb_check with the same sourceFiles/impact options after Kibi updates.",
67
+ ];
68
+ }
@@ -0,0 +1,21 @@
1
+ import type { ExtractionResult } from "../../extractors/markdown.js";
2
+ import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
3
+ import { type SymbolKind, type SymbolRole } from "../symbol-granularity.js";
4
+ import type { SymbolQualityDiagnosticsOptions } from "./types.js";
5
+ export type SymbolMetadata = {
6
+ readonly result: ExtractionResult;
7
+ readonly id: string;
8
+ readonly title: string;
9
+ readonly sourceFile: string;
10
+ readonly source: string;
11
+ readonly kind: SymbolKind | "component" | "service" | "module" | "config";
12
+ readonly role: SymbolRole;
13
+ readonly requirementTargetIds: readonly string[];
14
+ readonly coordinateKey: string | null;
15
+ };
16
+ export declare const AGGREGATE_KINDS: ReadonlySet<string>;
17
+ export declare function collectSymbols(options: SymbolQualityDiagnosticsOptions): readonly SymbolMetadata[];
18
+ export declare function isModuleOrConfig(symbol: SymbolMetadata): boolean;
19
+ export declare function narrowerManifestSymbols(symbol: SymbolMetadata, symbols: readonly SymbolMetadata[]): readonly SymbolMetadata[];
20
+ export declare function narrowerExtractedSymbols(symbol: SymbolMetadata, symbolsByFile: ReadonlyMap<string, readonly ExtractedSymbol[]>): readonly ExtractedSymbol[];
21
+ //# sourceMappingURL=symbol-quality-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbol-quality-model.d.ts","sourceRoot":"","sources":["../../../src/public/impact/symbol-quality-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAC5E,OAAO,EACL,KAAK,UAAU,EACf,KAAK,UAAU,EAEhB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAC1E,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAK9C,CAAC;AAyFH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,+BAA+B,GACvC,SAAS,cAAc,EAAE,CAyB3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhE;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,cAAc,EAAE,GACjC,SAAS,cAAc,EAAE,CAO3B;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,EACtB,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC,GAC7D,SAAS,eAAe,EAAE,CAO5B"}
@@ -0,0 +1,116 @@
1
+ import { getSymbolRole, } from "../symbol-granularity.js";
2
+ export const AGGREGATE_KINDS = new Set([
3
+ "class",
4
+ "component",
5
+ "service",
6
+ "module",
7
+ ]);
8
+ function isActiveSymbol(result) {
9
+ return result.entity.type === "symbol" && result.entity.status === "active";
10
+ }
11
+ function getRequirementTargets(result) {
12
+ return [
13
+ ...new Set(result.relationships
14
+ .filter((relationship) => relationship.type === "implements" &&
15
+ relationship.to.startsWith("REQ-"))
16
+ .map((relationship) => relationship.to)),
17
+ ].sort();
18
+ }
19
+ function inferKind(result) {
20
+ const explicitKind = result.entity.symbol_kind;
21
+ if (explicitKind === "component" ||
22
+ explicitKind === "service" ||
23
+ explicitKind === "module" ||
24
+ explicitKind === "config") {
25
+ return explicitKind;
26
+ }
27
+ if (explicitKind === "function" ||
28
+ explicitKind === "class" ||
29
+ explicitKind === "method" ||
30
+ explicitKind === "property" ||
31
+ explicitKind === "accessor" ||
32
+ explicitKind === "interface" ||
33
+ explicitKind === "type" ||
34
+ explicitKind === "variable" ||
35
+ explicitKind === "enum" ||
36
+ explicitKind === "unknown") {
37
+ return explicitKind;
38
+ }
39
+ if (result.entity.title.endsWith("Component"))
40
+ return "component";
41
+ if (result.entity.title.endsWith("Service"))
42
+ return "service";
43
+ if (result.entity.title.endsWith("Module"))
44
+ return "module";
45
+ if (result.entity.title.endsWith("Config"))
46
+ return "config";
47
+ if (result.entity.title.includes("."))
48
+ return "method";
49
+ return "unknown";
50
+ }
51
+ function inferRole(result, kind) {
52
+ const explicitRole = result.entity.symbol_role;
53
+ if (explicitRole === "behavioral" ||
54
+ explicitRole === "structural" ||
55
+ explicitRole === "type-shape" ||
56
+ explicitRole === "config" ||
57
+ explicitRole === "module" ||
58
+ explicitRole === "unknown") {
59
+ return explicitRole;
60
+ }
61
+ if (kind === "component" || kind === "service")
62
+ return "behavioral";
63
+ if (kind === "module")
64
+ return "module";
65
+ if (kind === "config")
66
+ return "config";
67
+ return getSymbolRole({ name: result.entity.title, kind });
68
+ }
69
+ function getCoordinateKey(result) {
70
+ const { sourceLine, sourceColumn, sourceEndLine, sourceEndColumn } = result.entity;
71
+ if (sourceLine === undefined ||
72
+ sourceColumn === undefined ||
73
+ sourceEndLine === undefined ||
74
+ sourceEndColumn === undefined) {
75
+ return null;
76
+ }
77
+ return `${sourceLine}:${sourceColumn}:${sourceEndLine}:${sourceEndColumn}`;
78
+ }
79
+ export function collectSymbols(options) {
80
+ return options.manifestResults
81
+ .filter((result) => options.activeEntityIds === undefined
82
+ ? isActiveSymbol(result)
83
+ : isActiveSymbol(result) &&
84
+ options.activeEntityIds.has(result.entity.id))
85
+ .flatMap((result) => {
86
+ if (result.sourceFile === undefined)
87
+ return [];
88
+ const kind = inferKind(result);
89
+ return [
90
+ {
91
+ result,
92
+ id: result.entity.id,
93
+ title: result.entity.title,
94
+ sourceFile: result.sourceFile,
95
+ source: result.entity.source,
96
+ kind,
97
+ role: inferRole(result, kind),
98
+ requirementTargetIds: getRequirementTargets(result),
99
+ coordinateKey: getCoordinateKey(result),
100
+ },
101
+ ];
102
+ });
103
+ }
104
+ export function isModuleOrConfig(symbol) {
105
+ return symbol.role === "module" || symbol.role === "config";
106
+ }
107
+ export function narrowerManifestSymbols(symbol, symbols) {
108
+ return symbols.filter((candidate) => candidate.id !== symbol.id &&
109
+ candidate.sourceFile === symbol.sourceFile &&
110
+ candidate.title.startsWith(`${symbol.title}.`));
111
+ }
112
+ export function narrowerExtractedSymbols(symbol, symbolsByFile) {
113
+ return (symbolsByFile.get(symbol.sourceFile) ?? []).filter((candidate) => candidate.name !== symbol.title &&
114
+ candidate.name.startsWith(`${symbol.title}.`) &&
115
+ candidate.role === "behavioral");
116
+ }
@@ -0,0 +1,4 @@
1
+ import type { KibiImpactDiagnostic } from "../../traceability/staged-diagnostics.js";
2
+ import type { SymbolQualityDiagnosticsOptions } from "./types.js";
3
+ export declare function createSymbolQualityDiagnostics(options: SymbolQualityDiagnosticsOptions): KibiImpactDiagnostic[];
4
+ //# sourceMappingURL=symbol-quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbol-quality.d.ts","sourceRoot":"","sources":["../../../src/public/impact/symbol-quality.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAUrF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AA0LlE,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,oBAAoB,EAAE,CAOxB"}