kibi-cli 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +11 -0
  3. package/dist/commands/check.d.ts.map +1 -1
  4. package/dist/commands/check.js +204 -3
  5. package/dist/commands/init-helpers.d.ts.map +1 -1
  6. package/dist/commands/init-helpers.js +11 -14
  7. package/dist/commands/sync/manifest.d.ts +8 -2
  8. package/dist/commands/sync/manifest.d.ts.map +1 -1
  9. package/dist/commands/sync/manifest.js +56 -11
  10. package/dist/commands/sync.d.ts +1 -0
  11. package/dist/commands/sync.d.ts.map +1 -1
  12. package/dist/commands/sync.js +9 -7
  13. package/dist/commands/usage-metrics.d.ts +8 -0
  14. package/dist/commands/usage-metrics.d.ts.map +1 -0
  15. package/dist/commands/usage-metrics.js +323 -0
  16. package/dist/extractors/manifest.d.ts +30 -0
  17. package/dist/extractors/manifest.d.ts.map +1 -1
  18. package/dist/extractors/manifest.js +60 -7
  19. package/dist/extractors/symbol-coordinates.d.ts +15 -0
  20. package/dist/extractors/symbol-coordinates.d.ts.map +1 -0
  21. package/dist/extractors/symbol-coordinates.js +83 -0
  22. package/dist/public/extractors/manifest.d.ts +1 -1
  23. package/dist/public/extractors/manifest.d.ts.map +1 -1
  24. package/dist/public/extractors/manifest.js +1 -1
  25. package/dist/traceability/evidence-model.d.ts +142 -0
  26. package/dist/traceability/evidence-model.d.ts.map +1 -0
  27. package/dist/traceability/evidence-model.js +70 -0
  28. package/dist/traceability/git-staged.d.ts +1 -0
  29. package/dist/traceability/git-staged.d.ts.map +1 -1
  30. package/dist/traceability/git-staged.js +28 -3
  31. package/dist/traceability/staged-diagnostics.d.ts +25 -0
  32. package/dist/traceability/staged-diagnostics.d.ts.map +1 -0
  33. package/dist/traceability/staged-diagnostics.js +67 -0
  34. package/dist/traceability/staged-impact-contract.d.ts +57 -0
  35. package/dist/traceability/staged-impact-contract.d.ts.map +1 -0
  36. package/dist/traceability/staged-impact-contract.js +202 -0
  37. package/dist/traceability/staged-symbols-manifest.d.ts +23 -0
  38. package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -0
  39. package/dist/traceability/staged-symbols-manifest.js +269 -0
  40. package/dist/traceability/symbol-extract.d.ts.map +1 -1
  41. package/dist/traceability/symbol-extract.js +33 -9
  42. package/dist/utils/manifest-paths.d.ts +8 -0
  43. package/dist/utils/manifest-paths.d.ts.map +1 -0
  44. package/dist/utils/manifest-paths.js +62 -0
  45. package/package.json +1 -1
  46. package/src/public/extractors/manifest.ts +2 -0
@@ -0,0 +1,23 @@
1
+ import type { StagedFile } from "./git-staged.js";
2
+ export interface StagedSymbolsManifestAssessment {
3
+ state: "fresh" | "stale" | "missing" | "not_required";
4
+ sourcePaths: string[];
5
+ path: string;
6
+ }
7
+ export interface StagedAuthoredSymbolsManifestEvidence {
8
+ path: string;
9
+ entries: Array<{
10
+ sourcePath: string;
11
+ entityIds: string[];
12
+ }>;
13
+ }
14
+ export declare function assessStagedSymbolsManifest(options: {
15
+ symbolsManifestPath: string;
16
+ sourceFiles: StagedFile[];
17
+ stagedFiles: StagedFile[];
18
+ }): StagedSymbolsManifestAssessment;
19
+ export declare function collectStagedAuthoredSymbolsManifestEvidence(options: {
20
+ sourceFiles: StagedFile[];
21
+ stagedFiles: StagedFile[];
22
+ }): StagedAuthoredSymbolsManifestEvidence;
23
+ //# sourceMappingURL=staged-symbols-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA8BlD,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;CACd;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;CAC7D;AAmQD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,GAAG,+BAA+B,CA0ElC;AAoBD,wBAAgB,4CAA4C,CAAC,OAAO,EAAE;IACpE,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,GAAG,qCAAqC,CA6CxC"}
@@ -0,0 +1,269 @@
1
+ import { execSync } from "node:child_process";
2
+ import * as path from "node:path";
3
+ import { extractManifestSymbolRecordsString, } from "../extractors/manifest.js";
4
+ import { mergeCoordinatesWithManifest, readCoordinateArtifact, } from "../extractors/symbol-coordinates.js";
5
+ import { analyzeSourceText } from "../extractors/symbols-coordinator.js";
6
+ import { resolveSymbolsManifestPaths } from "../utils/manifest-paths.js";
7
+ function toRepoRelativePath(absoluteOrRelativePath) {
8
+ if (!path.isAbsolute(absoluteOrRelativePath)) {
9
+ return absoluteOrRelativePath.replace(/\\/g, "/");
10
+ }
11
+ const relativePath = path.relative(process.cwd(), absoluteOrRelativePath);
12
+ return relativePath.replace(/\\/g, "/");
13
+ }
14
+ function resolveRelativeManifestPaths(symbolsManifestPath) {
15
+ if (symbolsManifestPath) {
16
+ const normalizedSymbolsPath = toRepoRelativePath(symbolsManifestPath);
17
+ return {
18
+ symbolsPath: normalizedSymbolsPath,
19
+ coordinatesPath: path
20
+ .join(path.dirname(normalizedSymbolsPath), "symbol-coordinates.yaml")
21
+ .replace(/\\/g, "/"),
22
+ };
23
+ }
24
+ const { coordinatesPath, symbolsPath } = resolveSymbolsManifestPaths(process.cwd());
25
+ return {
26
+ symbolsPath: toRepoRelativePath(symbolsPath),
27
+ coordinatesPath: toRepoRelativePath(coordinatesPath),
28
+ };
29
+ }
30
+ function readHeadFileContent(filePath) {
31
+ try {
32
+ return execSync(`git show HEAD:${filePath}`, {
33
+ encoding: "utf8",
34
+ stdio: ["pipe", "pipe", "pipe"],
35
+ });
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function parseManifestRecords(content, filePath) {
42
+ if (content === null || content === undefined) {
43
+ return [];
44
+ }
45
+ try {
46
+ return extractManifestSymbolRecordsString(content, filePath);
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ function parseCoordinateArtifact(content) {
53
+ if (content === null || content === undefined) {
54
+ return { coordinates: {} };
55
+ }
56
+ try {
57
+ return readCoordinateArtifact(content);
58
+ }
59
+ catch {
60
+ return null;
61
+ }
62
+ }
63
+ function normalizeNumber(value) {
64
+ return typeof value === "number" ? value : null;
65
+ }
66
+ function normalizeLinks(links) {
67
+ if (!Array.isArray(links)) {
68
+ return [];
69
+ }
70
+ const normalized = [];
71
+ for (const link of links) {
72
+ if (typeof link === "string") {
73
+ normalized.push(link);
74
+ continue;
75
+ }
76
+ if (link &&
77
+ typeof link === "object" &&
78
+ typeof link.type === "string" &&
79
+ typeof link.target === "string") {
80
+ normalized.push({ type: link.type, target: link.target });
81
+ }
82
+ }
83
+ return normalized.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
84
+ }
85
+ function normalizeRelationships(relationships) {
86
+ if (!Array.isArray(relationships)) {
87
+ return [];
88
+ }
89
+ return relationships
90
+ .flatMap((relationship) => {
91
+ if (relationship &&
92
+ typeof relationship.type === "string" &&
93
+ typeof relationship.target === "string") {
94
+ return [{ type: relationship.type, target: relationship.target }];
95
+ }
96
+ return [];
97
+ })
98
+ .sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
99
+ }
100
+ function normalizeManifestSymbolsForSourceFile(records, sourceFile) {
101
+ return records
102
+ .filter((record) => {
103
+ const recordSource = typeof record.sourceFile === "string"
104
+ ? record.sourceFile
105
+ : typeof record.source === "string"
106
+ ? record.source
107
+ : null;
108
+ return recordSource === sourceFile && typeof record.title === "string";
109
+ })
110
+ .map((record) => ({
111
+ title: record.title,
112
+ sourceFile,
113
+ sourceLine: normalizeNumber(record.sourceLine),
114
+ sourceColumn: normalizeNumber(record.sourceColumn),
115
+ sourceEndLine: normalizeNumber(record.sourceEndLine),
116
+ sourceEndColumn: normalizeNumber(record.sourceEndColumn),
117
+ }))
118
+ .sort(compareNormalizedSymbols);
119
+ }
120
+ function normalizeAuthoredManifestSymbolsForSourceFile(records, sourceFile) {
121
+ return records
122
+ .filter((record) => {
123
+ const recordSource = typeof record.sourceFile === "string"
124
+ ? record.sourceFile
125
+ : typeof record.source === "string"
126
+ ? record.source
127
+ : null;
128
+ return recordSource === sourceFile && typeof record.title === "string";
129
+ })
130
+ .map((record) => ({
131
+ id: typeof record.id === "string" ? record.id : null,
132
+ title: record.title,
133
+ sourceFile,
134
+ status: typeof record.status === "string" ? record.status : null,
135
+ links: normalizeLinks(record.links),
136
+ relationships: normalizeRelationships(record.relationships),
137
+ tags: Array.isArray(record.tags)
138
+ ? record.tags.filter((tag) => typeof tag === "string").sort()
139
+ : [],
140
+ owner: typeof record.owner === "string" ? record.owner : null,
141
+ priority: typeof record.priority === "string" ? record.priority : null,
142
+ severity: typeof record.severity === "string" ? record.severity : null,
143
+ textRef: typeof record.text_ref === "string" ? record.text_ref : null,
144
+ }))
145
+ .sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
146
+ }
147
+ function normalizeExpectedSymbolsForStagedFile(stagedFile) {
148
+ const analysis = analyzeSourceText(stagedFile.path, stagedFile.content ?? "");
149
+ return analysis.symbols
150
+ .map((symbol) => ({
151
+ title: symbol.name,
152
+ sourceFile: stagedFile.path,
153
+ sourceLine: symbol.startLine,
154
+ sourceColumn: symbol.startColumn,
155
+ sourceEndLine: symbol.endLine,
156
+ sourceEndColumn: symbol.endColumn,
157
+ }))
158
+ .sort(compareNormalizedSymbols);
159
+ }
160
+ function compareNormalizedSymbols(left, right) {
161
+ return JSON.stringify(left).localeCompare(JSON.stringify(right));
162
+ }
163
+ function signaturesEqual(left, right) {
164
+ return JSON.stringify(left) === JSON.stringify(right);
165
+ }
166
+ function uniqueSorted(paths) {
167
+ return Array.from(new Set(paths)).sort();
168
+ }
169
+ function mergeManifestRecordsWithCoordinates(manifestRecords, coordinateArtifact) {
170
+ return mergeCoordinatesWithManifest(manifestRecords ?? [], coordinateArtifact);
171
+ }
172
+ function getEffectiveManifestRecords(options) {
173
+ const { headCoordinateArtifact, headManifestRecords, paths, stagedFiles } = options;
174
+ const stagedManifestFile = stagedFiles.find((file) => file.path === paths.symbolsPath);
175
+ const stagedCoordinatesFile = stagedFiles.find((file) => file.path === paths.coordinatesPath);
176
+ const stagedManifestRecords = stagedManifestFile
177
+ ? parseManifestRecords(stagedManifestFile.content, paths.symbolsPath)
178
+ : headManifestRecords;
179
+ const stagedCoordinateArtifact = stagedCoordinatesFile
180
+ ? parseCoordinateArtifact(stagedCoordinatesFile.content)
181
+ : headCoordinateArtifact;
182
+ return {
183
+ stagedManifestFile,
184
+ stagedCoordinatesFile,
185
+ stagedManifestRecords,
186
+ stagedCoordinateArtifact,
187
+ };
188
+ }
189
+ export function assessStagedSymbolsManifest(options) {
190
+ const { sourceFiles, stagedFiles, symbolsManifestPath } = options;
191
+ const paths = resolveRelativeManifestPaths(symbolsManifestPath);
192
+ const headManifestRecords = parseManifestRecords(readHeadFileContent(paths.symbolsPath), paths.symbolsPath);
193
+ const headCoordinateArtifact = parseCoordinateArtifact(readHeadFileContent(paths.coordinatesPath));
194
+ const { stagedCoordinatesFile, stagedCoordinateArtifact, stagedManifestRecords, } = getEffectiveManifestRecords({
195
+ stagedFiles,
196
+ paths,
197
+ headManifestRecords,
198
+ headCoordinateArtifact,
199
+ });
200
+ const baselineMergedRecords = mergeManifestRecordsWithCoordinates(headManifestRecords, headCoordinateArtifact);
201
+ const stagedMergedRecords = mergeManifestRecordsWithCoordinates(stagedManifestRecords, stagedCoordinateArtifact);
202
+ const requiredRefreshPaths = [];
203
+ const freshPaths = new Set();
204
+ for (const sourceFile of sourceFiles) {
205
+ const expectedSymbols = normalizeExpectedSymbolsForStagedFile(sourceFile);
206
+ const baselineSymbols = normalizeManifestSymbolsForSourceFile(baselineMergedRecords, sourceFile.path);
207
+ if (signaturesEqual(expectedSymbols, baselineSymbols)) {
208
+ continue;
209
+ }
210
+ requiredRefreshPaths.push(sourceFile.path);
211
+ if (!stagedCoordinatesFile || stagedCoordinateArtifact === null) {
212
+ continue;
213
+ }
214
+ const stagedSymbols = normalizeManifestSymbolsForSourceFile(stagedMergedRecords, sourceFile.path);
215
+ if (signaturesEqual(expectedSymbols, stagedSymbols)) {
216
+ freshPaths.add(sourceFile.path);
217
+ }
218
+ }
219
+ const sourcePaths = uniqueSorted(requiredRefreshPaths);
220
+ if (sourcePaths.length === 0) {
221
+ return { state: "not_required", sourcePaths: [], path: paths.coordinatesPath };
222
+ }
223
+ if (sourcePaths.every((sourcePath) => freshPaths.has(sourcePath))) {
224
+ return { state: "fresh", sourcePaths, path: paths.coordinatesPath };
225
+ }
226
+ if (!stagedCoordinatesFile) {
227
+ return { state: "missing", sourcePaths, path: paths.coordinatesPath };
228
+ }
229
+ return { state: "stale", sourcePaths, path: paths.coordinatesPath };
230
+ }
231
+ function getEntityIdsForSourceFile(records, sourceFile) {
232
+ return records
233
+ .filter((record) => {
234
+ const recordSource = typeof record.sourceFile === "string"
235
+ ? record.sourceFile
236
+ : typeof record.source === "string"
237
+ ? record.source
238
+ : null;
239
+ return recordSource === sourceFile && typeof record.id === "string";
240
+ })
241
+ .map((record) => record.id)
242
+ .sort();
243
+ }
244
+ export function collectStagedAuthoredSymbolsManifestEvidence(options) {
245
+ const { sourceFiles, stagedFiles } = options;
246
+ const paths = resolveRelativeManifestPaths();
247
+ const stagedManifestFile = stagedFiles.find((file) => file.path === paths.symbolsPath);
248
+ if (!stagedManifestFile) {
249
+ return { path: paths.symbolsPath, entries: [] };
250
+ }
251
+ const headManifestRecords = parseManifestRecords(readHeadFileContent(paths.symbolsPath), paths.symbolsPath) ?? [];
252
+ const stagedManifestRecords = parseManifestRecords(stagedManifestFile.content, paths.symbolsPath);
253
+ if (stagedManifestRecords === null) {
254
+ return { path: paths.symbolsPath, entries: [] };
255
+ }
256
+ const entries = [];
257
+ for (const sourceFile of sourceFiles) {
258
+ const headSymbols = normalizeAuthoredManifestSymbolsForSourceFile(headManifestRecords, sourceFile.path);
259
+ const stagedSymbols = normalizeAuthoredManifestSymbolsForSourceFile(stagedManifestRecords, sourceFile.path);
260
+ if (signaturesEqual(headSymbols, stagedSymbols)) {
261
+ continue;
262
+ }
263
+ entries.push({
264
+ sourcePath: sourceFile.path,
265
+ entityIds: getEntityIdsForSourceFile(stagedManifestRecords, sourceFile.path),
266
+ });
267
+ }
268
+ return { path: paths.symbolsPath, entries };
269
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAU7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;AA2LD,wBAAgB,4BAA4B,CAE1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,GAC9B,eAAe,EAAE,CA+JnB"}
1
+ {"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAU7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;AA+ND,wBAAgB,4BAA4B,CAE1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,GAC9B,eAAe,EAAE,CA+JnB"}
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { Project, ScriptKind } from "ts-morph";
3
- import { extractFromManifest } from "../extractors/manifest.js";
3
+ import { readManifestWithCoordinateOverlay } from "../extractors/manifest.js";
4
4
  const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
5
5
  "implements",
6
6
  "covered_by",
@@ -55,15 +55,12 @@ function resolveSymbolTraceability(filePath, name, manifestLookup) {
55
55
  }
56
56
  for (const manifestPath of candidateManifestPaths) {
57
57
  try {
58
- const ents = extractFromManifest(manifestPath);
59
- for (const e of ents) {
60
- if (e.entity.title === name) {
58
+ const records = readManifestWithCoordinateOverlay(manifestPath);
59
+ for (const record of records) {
60
+ if (record.title === name && typeof record.id === "string") {
61
61
  return {
62
- id: e.entity.id,
63
- relationships: filterTraceabilityRelationships(e.relationships.map((relationship) => ({
64
- type: relationship.type,
65
- to: relationship.to,
66
- }))),
62
+ id: record.id,
63
+ relationships: filterTraceabilityRelationships(extractRelationshipsFromManifestRecord(record)),
67
64
  };
68
65
  }
69
66
  }
@@ -74,6 +71,33 @@ function resolveSymbolTraceability(filePath, name, manifestLookup) {
74
71
  }
75
72
  return { id: createHashFallbackId(filePath, name) };
76
73
  }
74
+ function extractRelationshipsFromManifestRecord(record) {
75
+ const relationships = [];
76
+ if (Array.isArray(record.links)) {
77
+ for (const link of record.links) {
78
+ if (typeof link === "string") {
79
+ relationships.push({ type: "implements", to: link });
80
+ continue;
81
+ }
82
+ if (link &&
83
+ typeof link === "object" &&
84
+ typeof link.type === "string" &&
85
+ typeof link.target === "string") {
86
+ relationships.push({ type: link.type, to: link.target });
87
+ }
88
+ }
89
+ }
90
+ if (Array.isArray(record.relationships)) {
91
+ for (const relationship of record.relationships) {
92
+ if (relationship &&
93
+ typeof relationship.type === "string" &&
94
+ typeof relationship.target === "string") {
95
+ relationships.push({ type: relationship.type, to: relationship.target });
96
+ }
97
+ }
98
+ }
99
+ return relationships;
100
+ }
77
101
  function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifestLookup) {
78
102
  const { id, relationships } = resolveSymbolTraceability(stagedFile.path, name, manifestLookup);
79
103
  const manifestReqLinks = getRequirementLinks(relationships);
@@ -0,0 +1,8 @@
1
+ export declare const DEFAULT_SYMBOLS_PATH = "documentation/symbols.yaml";
2
+ export declare const DEFAULT_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
3
+ export declare function resolveSymbolsManifestPaths(workspaceRoot: string, configPath?: string): {
4
+ symbolsPath: string;
5
+ coordinatesPath: string;
6
+ };
7
+ export declare function resolveSymbolsManifestPath(workspaceRoot: string, configPath?: string): string;
8
+ //# sourceMappingURL=manifest-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-paths.d.ts","sourceRoot":"","sources":["../../src/utils/manifest-paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,wBAAwB,0CAA0C,CAAC;AAuEhF,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CASlD;AAGD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAER"}
@@ -0,0 +1,62 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ export const DEFAULT_SYMBOLS_PATH = "documentation/symbols.yaml";
4
+ export const DEFAULT_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
5
+ function isNonEmptyString(value) {
6
+ return typeof value === "string" && value.trim().length > 0;
7
+ }
8
+ function resolveConfigPath(workspaceRoot, configPath) {
9
+ const configuredPath = configPath ?? path.join(workspaceRoot, ".kb", "config.json");
10
+ return path.isAbsolute(configuredPath)
11
+ ? configuredPath
12
+ : path.resolve(workspaceRoot, configuredPath);
13
+ }
14
+ function readManifestResolverConfig(workspaceRoot, configPath) {
15
+ const resolvedConfigPath = resolveConfigPath(workspaceRoot, configPath);
16
+ if (!existsSync(resolvedConfigPath)) {
17
+ return null;
18
+ }
19
+ try {
20
+ return JSON.parse(readFileSync(resolvedConfigPath, "utf8"));
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ function resolveConfiguredSymbolsPath(workspaceRoot, configPath) {
27
+ const config = readManifestResolverConfig(workspaceRoot, configPath);
28
+ if (!config) {
29
+ return null;
30
+ }
31
+ const configuredPathCandidate = config.paths?.symbols ?? config.symbolsManifest;
32
+ if (!isNonEmptyString(configuredPathCandidate)) {
33
+ return null;
34
+ }
35
+ return path.isAbsolute(configuredPathCandidate)
36
+ ? configuredPathCandidate
37
+ : path.resolve(workspaceRoot, configuredPathCandidate);
38
+ }
39
+ function resolveDefaultSymbolsPath(workspaceRoot) {
40
+ const candidates = [
41
+ path.join(workspaceRoot, DEFAULT_SYMBOLS_PATH),
42
+ path.join(workspaceRoot, "symbols.yaml"),
43
+ path.join(workspaceRoot, "symbols.yml"),
44
+ ];
45
+ return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0] ?? path.join(workspaceRoot, DEFAULT_SYMBOLS_PATH);
46
+ }
47
+ function deriveCoordinatesPath(symbolsPath) {
48
+ return path.join(path.dirname(symbolsPath), path.basename(DEFAULT_COORDINATES_PATH));
49
+ }
50
+ // implements REQ-cli-sync
51
+ export function resolveSymbolsManifestPaths(workspaceRoot, configPath) {
52
+ const symbolsPath = resolveConfiguredSymbolsPath(workspaceRoot, configPath) ??
53
+ resolveDefaultSymbolsPath(workspaceRoot);
54
+ return {
55
+ symbolsPath,
56
+ coordinatesPath: deriveCoordinatesPath(symbolsPath),
57
+ };
58
+ }
59
+ // implements REQ-cli-sync
60
+ export function resolveSymbolsManifestPath(workspaceRoot, configPath) {
61
+ return resolveSymbolsManifestPaths(workspaceRoot, configPath).symbolsPath;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -19,6 +19,8 @@
19
19
  export {
20
20
  extractFromManifest,
21
21
  extractFromManifestString,
22
+ readManifestWithCoordinateOverlay,
22
23
  type ExtractionResult,
23
24
  type ManifestError,
25
+ type ManifestSymbolRecord,
24
26
  } from "../../extractors/manifest.js";