kibi-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/aggregated-checks.d.ts.map +1 -1
- package/dist/commands/aggregated-checks.js +3 -2
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +97 -51
- package/dist/commands/discovery-shared.d.ts +12 -5
- package/dist/commands/discovery-shared.d.ts.map +1 -1
- package/dist/commands/discovery-shared.js +21 -13
- package/dist/commands/doctor.js +9 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +2 -3
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +15 -5
- package/dist/commands/search.js +1 -1
- package/dist/commands/sync/cache.d.ts +14 -4
- package/dist/commands/sync/cache.d.ts.map +1 -1
- package/dist/commands/sync/cache.js +36 -14
- package/dist/commands/sync/extraction.d.ts.map +1 -1
- package/dist/commands/sync/extraction.js +4 -9
- package/dist/commands/sync/manifest.d.ts +14 -3
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +29 -10
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +9 -5
- package/dist/commands/sync/staging.d.ts +19 -3
- package/dist/commands/sync/staging.d.ts.map +1 -1
- package/dist/commands/sync/staging.js +50 -27
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +16 -20
- package/dist/diagnostics.d.ts +1 -10
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +6 -12
- package/dist/env.d.ts +7 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +41 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +17 -15
- package/dist/extractors/markdown.d.ts +2 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +124 -30
- package/dist/extractors/relationships.d.ts.map +1 -1
- package/dist/extractors/relationships.js +10 -4
- package/dist/extractors/symbols-coordinator.d.ts +5 -2
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +5 -2
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +56 -10
- package/dist/prolog/codec.d.ts +0 -43
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +68 -74
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +45 -29
- package/dist/public/schemas/entity.d.ts +3 -0
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +1 -0
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +1 -0
- package/dist/query/service.d.ts +9 -0
- package/dist/query/service.d.ts.map +1 -1
- package/dist/query/service.js +27 -10
- package/dist/schemas/entity.schema.json +23 -0
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +19 -3
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +22 -6
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +10 -4
- package/dist/traceability/temp-kb.d.ts +12 -0
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +42 -17
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +11 -2
- package/dist/utils/branch-resolver.d.ts +4 -0
- package/dist/utils/branch-resolver.d.ts.map +1 -1
- package/dist/utils/branch-resolver.js +29 -12
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +8 -2
- package/dist/utils/rule-registry.js +2 -2
- package/package.json +3 -9
- package/src/public/schemas/entity.ts +1 -0
- package/src/public/schemas/relationship.ts +1 -0
- package/src/schemas/entity.schema.json +23 -0
- package/src/schemas/relationship.schema.json +1 -0
|
@@ -74,23 +74,25 @@ function extractFromParsedManifest(manifest, filePath) {
|
|
|
74
74
|
throw new ManifestError("Missing required field: title", filePath);
|
|
75
75
|
}
|
|
76
76
|
const id = symbol.id || generateId(filePath, symbol.title);
|
|
77
|
+
const entity = {
|
|
78
|
+
id,
|
|
79
|
+
type: "symbol",
|
|
80
|
+
title: symbol.title,
|
|
81
|
+
status: symbol.status || "active",
|
|
82
|
+
created_at: symbol.created_at || new Date().toISOString(),
|
|
83
|
+
updated_at: symbol.updated_at || new Date().toISOString(),
|
|
84
|
+
source: filePath,
|
|
85
|
+
...(symbol.tags !== undefined ? { tags: symbol.tags } : {}),
|
|
86
|
+
...(symbol.owner !== undefined ? { owner: symbol.owner } : {}),
|
|
87
|
+
...(symbol.priority !== undefined ? { priority: symbol.priority } : {}),
|
|
88
|
+
...(symbol.severity !== undefined ? { severity: symbol.severity } : {}),
|
|
89
|
+
...(symbol.text_ref !== undefined ? { text_ref: symbol.text_ref } : {}),
|
|
90
|
+
};
|
|
91
|
+
const sourceFile = symbol.sourceFile ?? symbol.source;
|
|
77
92
|
return {
|
|
78
|
-
entity
|
|
79
|
-
id,
|
|
80
|
-
type: "symbol",
|
|
81
|
-
title: symbol.title,
|
|
82
|
-
status: symbol.status || "active",
|
|
83
|
-
created_at: symbol.created_at || new Date().toISOString(),
|
|
84
|
-
updated_at: symbol.updated_at || new Date().toISOString(),
|
|
85
|
-
source: filePath,
|
|
86
|
-
tags: symbol.tags,
|
|
87
|
-
owner: symbol.owner,
|
|
88
|
-
priority: symbol.priority,
|
|
89
|
-
severity: symbol.severity,
|
|
90
|
-
text_ref: symbol.text_ref,
|
|
91
|
-
},
|
|
93
|
+
entity,
|
|
92
94
|
relationships: extractRelationships(id, symbol),
|
|
93
|
-
sourceFile
|
|
95
|
+
...(sourceFile !== undefined ? { sourceFile } : {}),
|
|
94
96
|
};
|
|
95
97
|
});
|
|
96
98
|
}
|
|
@@ -11,6 +11,8 @@ export interface ExtractedEntity {
|
|
|
11
11
|
priority?: string;
|
|
12
12
|
severity?: string;
|
|
13
13
|
text_ref?: string;
|
|
14
|
+
verification_scope?: "unit" | "integration" | "end_to_end";
|
|
15
|
+
verification_perspective?: "internal" | "consumer";
|
|
14
16
|
fact_kind?: "subject" | "property_value" | "observation" | "meta";
|
|
15
17
|
subject_key?: string;
|
|
16
18
|
property_key?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAwDA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,YAAY,CAAC;IAC3D,wBAAwB,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IAEnD,SAAS,CAAC,EAAE,SAAS,GAAG,gBAAgB,GAAG,aAAa,GAAG,MAAM,CAAC;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IACtD,UAAU,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AA2BD,qBAAa,gBAAiB,SAAQ,KAAK;IAOhC,QAAQ,EAAE,MAAM;IANlB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IAYM,QAAQ;CAUlB;AA8DD,wBAAgB,sBAAsB,CAEpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CAkDV;AAmRD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAElB;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAatE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE;AASD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQpE"}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
19
|
import { readFileSync } from "node:fs";
|
|
20
20
|
import Ajv from "ajv";
|
|
21
|
-
import
|
|
21
|
+
import { load as yamlLoad } from "js-yaml";
|
|
22
22
|
import entitySchema from "../schemas/entity.schema.json" with { type: "json" };
|
|
23
23
|
// Typed fact field constants for extraction
|
|
24
24
|
const FACT_STRING_FIELDS = [
|
|
@@ -42,6 +42,10 @@ const FACT_ONLY_FIELDS = [
|
|
|
42
42
|
...FACT_NUMBER_FIELDS,
|
|
43
43
|
...FACT_BOOLEAN_FIELDS,
|
|
44
44
|
];
|
|
45
|
+
const TEST_ENUM_FIELDS = [
|
|
46
|
+
"verification_scope",
|
|
47
|
+
"verification_perspective",
|
|
48
|
+
];
|
|
45
49
|
const ajv = new Ajv({ strict: false, allErrors: true });
|
|
46
50
|
const validateExtractedEntity = ajv.compile(entitySchema);
|
|
47
51
|
const DEFAULT_STATUS_BY_TYPE = {
|
|
@@ -60,12 +64,15 @@ export class FrontmatterError extends Error {
|
|
|
60
64
|
hint;
|
|
61
65
|
originalError;
|
|
62
66
|
constructor(message, filePath, options) {
|
|
67
|
+
// implements REQ-003
|
|
63
68
|
super(message);
|
|
64
69
|
this.filePath = filePath;
|
|
65
70
|
this.name = "FrontmatterError";
|
|
66
71
|
this.classification = options?.classification || "Generic Error";
|
|
67
72
|
this.hint = options?.hint || "Check the file for syntax errors.";
|
|
68
|
-
|
|
73
|
+
if (options?.originalError !== undefined) {
|
|
74
|
+
this.originalError = options.originalError;
|
|
75
|
+
}
|
|
69
76
|
}
|
|
70
77
|
toString() {
|
|
71
78
|
let msg = `${this.filePath}: [${this.classification}] ${this.message}`;
|
|
@@ -78,41 +85,91 @@ export class FrontmatterError extends Error {
|
|
|
78
85
|
return msg;
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
|
-
|
|
88
|
+
function isObjectRecord(value) {
|
|
89
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
90
|
+
}
|
|
91
|
+
function isVerificationScope(value) {
|
|
92
|
+
return value === "unit" || value === "integration" || value === "end_to_end";
|
|
93
|
+
}
|
|
94
|
+
function isVerificationPerspective(value) {
|
|
95
|
+
return value === "internal" || value === "consumer";
|
|
96
|
+
}
|
|
97
|
+
function parseFrontmatter(content) {
|
|
98
|
+
const trimmedStart = content.trimStart();
|
|
99
|
+
if (!trimmedStart.startsWith("---")) {
|
|
100
|
+
return { data: {}, content };
|
|
101
|
+
}
|
|
102
|
+
const parts = trimmedStart.split("---");
|
|
103
|
+
if (parts.length < 3) {
|
|
104
|
+
return { data: {}, content };
|
|
105
|
+
}
|
|
106
|
+
const frontmatter = parts[1];
|
|
107
|
+
if (frontmatter === undefined) {
|
|
108
|
+
return { data: {}, content };
|
|
109
|
+
}
|
|
110
|
+
const parsed = yamlLoad(frontmatter);
|
|
111
|
+
return {
|
|
112
|
+
data: isObjectRecord(parsed) ? parsed : {},
|
|
113
|
+
content: parts.slice(2).join("---"),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function hasLikelyUnquotedColonInTitle(content) {
|
|
117
|
+
if (!content.trim().startsWith("---")) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const parts = content.split("---");
|
|
121
|
+
if (parts.length < 2) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const frontmatter = parts[1];
|
|
125
|
+
return frontmatter
|
|
126
|
+
? /^\s*title:\s*(?!["'])(?![>|])[^#\n]*:\s+\S.*$/m.test(frontmatter)
|
|
127
|
+
: false;
|
|
128
|
+
}
|
|
129
|
+
export function detectEmbeddedEntities(
|
|
130
|
+
// implements REQ-007, REQ-004
|
|
131
|
+
data, entityType) {
|
|
82
132
|
if (entityType !== "req") {
|
|
83
133
|
return [];
|
|
84
134
|
}
|
|
85
135
|
const detected = [];
|
|
86
|
-
const
|
|
136
|
+
const hasEmbeddedValue = (value) => value !== null &&
|
|
137
|
+
value !== undefined &&
|
|
138
|
+
(Array.isArray(value) ||
|
|
139
|
+
typeof value === "object" ||
|
|
140
|
+
typeof value === "string");
|
|
141
|
+
const scenarioFields = [
|
|
142
|
+
"scenario",
|
|
143
|
+
"scenarios",
|
|
144
|
+
"given",
|
|
145
|
+
"when",
|
|
146
|
+
"then",
|
|
147
|
+
"steps",
|
|
148
|
+
];
|
|
87
149
|
for (const field of scenarioFields) {
|
|
88
|
-
if (field
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
value !== undefined &&
|
|
92
|
-
(Array.isArray(value) ||
|
|
93
|
-
typeof value === "object" ||
|
|
94
|
-
typeof value === "string")) {
|
|
95
|
-
if (!detected.includes("scenario")) {
|
|
96
|
-
detected.push("scenario");
|
|
97
|
-
}
|
|
98
|
-
break;
|
|
150
|
+
if (hasEmbeddedValue(data[field])) {
|
|
151
|
+
if (!detected.includes("scenario")) {
|
|
152
|
+
detected.push("scenario");
|
|
99
153
|
}
|
|
154
|
+
break;
|
|
100
155
|
}
|
|
101
156
|
}
|
|
102
|
-
const testFields = [
|
|
157
|
+
const testFields = [
|
|
158
|
+
"test",
|
|
159
|
+
"tests",
|
|
160
|
+
"testCase",
|
|
161
|
+
"testCases",
|
|
162
|
+
"assertion",
|
|
163
|
+
"assertions",
|
|
164
|
+
"testStep",
|
|
165
|
+
"testSteps",
|
|
166
|
+
];
|
|
103
167
|
for (const field of testFields) {
|
|
104
|
-
if (field
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
value !== undefined &&
|
|
108
|
-
(Array.isArray(value) ||
|
|
109
|
-
typeof value === "object" ||
|
|
110
|
-
typeof value === "string")) {
|
|
111
|
-
if (!detected.includes("test")) {
|
|
112
|
-
detected.push("test");
|
|
113
|
-
}
|
|
114
|
-
break;
|
|
168
|
+
if (hasEmbeddedValue(data[field])) {
|
|
169
|
+
if (!detected.includes("test")) {
|
|
170
|
+
detected.push("test");
|
|
115
171
|
}
|
|
172
|
+
break;
|
|
116
173
|
}
|
|
117
174
|
}
|
|
118
175
|
return detected;
|
|
@@ -120,7 +177,7 @@ export function detectEmbeddedEntities(data, entityType) {
|
|
|
120
177
|
// implements REQ-007, REQ-004
|
|
121
178
|
function extractFromMarkdownContent(content, filePath) {
|
|
122
179
|
try {
|
|
123
|
-
const { data } =
|
|
180
|
+
const { data } = parseFrontmatter(content);
|
|
124
181
|
if (content.trim().startsWith("---")) {
|
|
125
182
|
const parts = content.split("---");
|
|
126
183
|
if (parts.length < 3) {
|
|
@@ -205,7 +262,43 @@ function extractFromMarkdownContent(content, filePath) {
|
|
|
205
262
|
});
|
|
206
263
|
}
|
|
207
264
|
}
|
|
265
|
+
if (type !== "test") {
|
|
266
|
+
const invalidTestField = TEST_ENUM_FIELDS.find((field) => data[field] !== undefined);
|
|
267
|
+
if (invalidTestField) {
|
|
268
|
+
throw new FrontmatterError(`Test-only fields are only allowed on type: test (found ${invalidTestField})`, filePath, {
|
|
269
|
+
classification: "Test Field on Non-Test Entity",
|
|
270
|
+
hint: "Remove test-only fields or change the entity type to test.",
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (type === "test") {
|
|
275
|
+
if (data.verification_scope !== undefined) {
|
|
276
|
+
if (!isVerificationScope(data.verification_scope)) {
|
|
277
|
+
throw new FrontmatterError("Invalid verification_scope; expected one of: unit, integration, end_to_end", filePath, {
|
|
278
|
+
classification: "Invalid Test Verification Scope",
|
|
279
|
+
hint: "Use one of: unit, integration, end_to_end.",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
entity.verification_scope = data.verification_scope;
|
|
283
|
+
}
|
|
284
|
+
if (data.verification_perspective !== undefined) {
|
|
285
|
+
if (!isVerificationPerspective(data.verification_perspective)) {
|
|
286
|
+
throw new FrontmatterError("Invalid verification_perspective; expected one of: internal, consumer", filePath, {
|
|
287
|
+
classification: "Invalid Test Verification Perspective",
|
|
288
|
+
hint: "Use one of: internal, consumer.",
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
entity.verification_perspective = data.verification_perspective;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
208
294
|
// Add typed fact fields only for fact entities
|
|
295
|
+
//
|
|
296
|
+
// INTENTIONAL DYNAMIC CAST: The casts below assign parsed markdown frontmatter
|
|
297
|
+
// values to ExtractedEntity fields. TypeScript cannot verify that a runtime string
|
|
298
|
+
// from a const array (e.g., FACT_STRING_FIELDS) is a valid key of ExtractedEntity,
|
|
299
|
+
// even though all fields are declared as optional properties on the interface.
|
|
300
|
+
// Fields are validated against the JSON Schema (via validateExtractedEntity) below,
|
|
301
|
+
// so any invalid field will be caught at runtime before the entity is returned.
|
|
209
302
|
if (type === "fact") {
|
|
210
303
|
// String fields
|
|
211
304
|
for (const field of FACT_STRING_FIELDS) {
|
|
@@ -255,8 +348,9 @@ function extractFromMarkdownContent(content, filePath) {
|
|
|
255
348
|
const message = error.message;
|
|
256
349
|
let classification = "Frontmatter YAML syntax error";
|
|
257
350
|
let hint = "Check the YAML syntax in your frontmatter.";
|
|
258
|
-
if (message.includes("incomplete explicit mapping pair") &&
|
|
259
|
-
message.includes(":"))
|
|
351
|
+
if ((message.includes("incomplete explicit mapping pair") &&
|
|
352
|
+
message.includes(":")) ||
|
|
353
|
+
hasLikelyUnquotedColonInTitle(content)) {
|
|
260
354
|
classification = "Unquoted colon likely in title";
|
|
261
355
|
hint =
|
|
262
356
|
'Wrap values containing colons in quotes (e.g., title: "Foo: Bar").';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/extractors/relationships.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAsBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,GACvB,qBAAqB,EAAE,CAyCzB;
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/extractors/relationships.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAsBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,GACvB,qBAAqB,EAAE,CAyCzB;AAmDD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,EAAE,GAC/B,qBAAqB,EAAE,CAEzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,qBAAqB,EAAE,EACtC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IACP,YAAY,EAAE,qBAAqB,CAAC;IACpC,KAAK,EAAE,cAAc,GAAG,YAAY,CAAC;CACtC,CAAC,CAgBD"}
|
|
@@ -99,10 +99,16 @@ function convertRecordToRelationship(record) {
|
|
|
99
99
|
record.source ||
|
|
100
100
|
record.confidence !== undefined) {
|
|
101
101
|
relationship.metadata = {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
...(record.created_at !== undefined
|
|
103
|
+
? { created_at: record.created_at }
|
|
104
|
+
: {}),
|
|
105
|
+
...(record.created_by !== undefined
|
|
106
|
+
? { created_by: record.created_by }
|
|
107
|
+
: {}),
|
|
108
|
+
...(record.source !== undefined ? { source: record.source } : {}),
|
|
109
|
+
...(record.confidence !== undefined
|
|
110
|
+
? { confidence: record.confidence }
|
|
111
|
+
: {}),
|
|
106
112
|
};
|
|
107
113
|
}
|
|
108
114
|
return relationship;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { type ManifestSymbolEntry } from "./symbols-ts.js";
|
|
1
|
+
import { type ManifestSymbolEntry, enrichSymbolCoordinatesWithTsMorph } from "./symbols-ts.js";
|
|
2
2
|
export type { ManifestSymbolEntry };
|
|
3
|
-
|
|
3
|
+
interface EnrichSymbolCoordinatesDeps {
|
|
4
|
+
enrichTsCoordinates: typeof enrichSymbolCoordinatesWithTsMorph;
|
|
5
|
+
}
|
|
6
|
+
export declare function enrichSymbolCoordinates(entries: ManifestSymbolEntry[], workspaceRoot: string, deps?: Partial<EnrichSymbolCoordinatesDeps>): Promise<ManifestSymbolEntry[]>;
|
|
4
7
|
//# sourceMappingURL=symbols-coordinator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,mBAAmB,EACxB,kCAAkC,EACnC,MAAM,iBAAiB,CAAC;AAazB,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,OAAO,kCAAkC,CAAC;CAChE;AAED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,mBAAmB,EAAE,EAC9B,aAAa,EAAE,MAAM,EACrB,IAAI,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,GAC1C,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAqChC"}
|
|
@@ -28,13 +28,16 @@ const TS_JS_EXTENSIONS = new Set([
|
|
|
28
28
|
".mjs",
|
|
29
29
|
".cjs",
|
|
30
30
|
]);
|
|
31
|
-
export async function enrichSymbolCoordinates(entries, workspaceRoot) {
|
|
31
|
+
export async function enrichSymbolCoordinates(entries, workspaceRoot, deps) {
|
|
32
32
|
// implements REQ-vscode-traceability
|
|
33
|
+
const enrichTsCoordinates = deps?.enrichTsCoordinates ?? enrichSymbolCoordinatesWithTsMorph;
|
|
33
34
|
const output = entries.map((entry) => ({ ...entry }));
|
|
34
35
|
const tsIndices = [];
|
|
35
36
|
const tsEntries = [];
|
|
36
37
|
for (let index = 0; index < output.length; index++) {
|
|
37
38
|
const entry = output[index];
|
|
39
|
+
if (!entry)
|
|
40
|
+
continue;
|
|
38
41
|
const resolved = resolveSourcePath(entry.sourceFile, workspaceRoot);
|
|
39
42
|
if (!resolved)
|
|
40
43
|
continue;
|
|
@@ -47,7 +50,7 @@ export async function enrichSymbolCoordinates(entries, workspaceRoot) {
|
|
|
47
50
|
output[index] = enrichWithRegexHeuristic(entry, resolved.absolutePath);
|
|
48
51
|
}
|
|
49
52
|
if (tsEntries.length > 0) {
|
|
50
|
-
const enrichedTs = await
|
|
53
|
+
const enrichedTs = await enrichTsCoordinates(tsEntries, workspaceRoot);
|
|
51
54
|
for (let i = 0; i < tsIndices.length; i++) {
|
|
52
55
|
const target = tsIndices[i];
|
|
53
56
|
const enriched = enrichedTs[i];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbols-ts.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-ts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"symbols-ts.d.ts","sourceRoot":"","sources":["../../src/extractors/symbols-ts.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAaD,wBAAsB,kCAAkC,CACtD,OAAO,EAAE,mBAAmB,EAAE,EAC9B,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAuEhC"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
|
-
import
|
|
18
|
+
import { access, readFile } from "node:fs/promises";
|
|
19
19
|
import * as path from "node:path";
|
|
20
20
|
import { Project, } from "ts-morph";
|
|
21
21
|
const SUPPORTED_SOURCE_EXTENSIONS = new Set([
|
|
@@ -37,14 +37,14 @@ export async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot)
|
|
|
37
37
|
const enriched = [];
|
|
38
38
|
for (const entry of entries) {
|
|
39
39
|
try {
|
|
40
|
-
const
|
|
41
|
-
if (!
|
|
40
|
+
const absolutePath = await resolveSourcePath(entry.sourceFile, workspaceRoot);
|
|
41
|
+
if (!absolutePath) {
|
|
42
42
|
enriched.push(entry);
|
|
43
43
|
continue;
|
|
44
44
|
}
|
|
45
|
-
const sourceFile = getOrAddSourceFile(project, sourceFileCache,
|
|
45
|
+
const sourceFile = getOrAddSourceFile(project, sourceFileCache, absolutePath);
|
|
46
46
|
if (!sourceFile) {
|
|
47
|
-
enriched.push(entry);
|
|
47
|
+
enriched.push(await enrichWithTextFallback(entry, absolutePath));
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
50
|
const match = findNamedDeclaration(sourceFile, entry.title);
|
|
@@ -71,12 +71,17 @@ export async function enrichSymbolCoordinatesWithTsMorph(entries, workspaceRoot)
|
|
|
71
71
|
catch (error) {
|
|
72
72
|
const message = error instanceof Error ? error.message : String(error);
|
|
73
73
|
console.warn(`[kibi] Failed to enrich symbol coordinates for ${entry.id}: ${message}`);
|
|
74
|
-
|
|
74
|
+
const absolutePath = await resolveSourcePath(entry.sourceFile, workspaceRoot);
|
|
75
|
+
if (!absolutePath) {
|
|
76
|
+
enriched.push(entry);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
enriched.push(await enrichWithTextFallback(entry, absolutePath));
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
return enriched;
|
|
78
83
|
}
|
|
79
|
-
function resolveSourcePath(sourceFile, workspaceRoot) {
|
|
84
|
+
async function resolveSourcePath(sourceFile, workspaceRoot) {
|
|
80
85
|
if (!sourceFile)
|
|
81
86
|
return null;
|
|
82
87
|
const absolute = path.isAbsolute(sourceFile)
|
|
@@ -85,8 +90,12 @@ function resolveSourcePath(sourceFile, workspaceRoot) {
|
|
|
85
90
|
const ext = path.extname(absolute).toLowerCase();
|
|
86
91
|
if (!SUPPORTED_SOURCE_EXTENSIONS.has(ext))
|
|
87
92
|
return null;
|
|
88
|
-
|
|
93
|
+
try {
|
|
94
|
+
await access(absolute);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
89
97
|
return null;
|
|
98
|
+
}
|
|
90
99
|
return absolute;
|
|
91
100
|
}
|
|
92
101
|
function getOrAddSourceFile(project, cache, absolutePath) {
|
|
@@ -102,6 +111,37 @@ function getOrAddSourceFile(project, cache, absolutePath) {
|
|
|
102
111
|
return null;
|
|
103
112
|
}
|
|
104
113
|
}
|
|
114
|
+
function enrichWithTextFallback(entry, absolutePath) {
|
|
115
|
+
return enrichWithTextFallbackInternal(entry, absolutePath);
|
|
116
|
+
}
|
|
117
|
+
async function enrichWithTextFallbackInternal(entry, absolutePath) {
|
|
118
|
+
try {
|
|
119
|
+
const content = await readFile(absolutePath, "utf8");
|
|
120
|
+
const lines = content.split(/\r?\n/);
|
|
121
|
+
const escapedTitle = entry.title.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
122
|
+
const pattern = new RegExp(`\\b${escapedTitle}\\b`);
|
|
123
|
+
for (let index = 0; index < lines.length; index++) {
|
|
124
|
+
const line = lines[index];
|
|
125
|
+
if (!line)
|
|
126
|
+
continue;
|
|
127
|
+
const match = pattern.exec(line);
|
|
128
|
+
if (!match || match.index < 0)
|
|
129
|
+
continue;
|
|
130
|
+
return {
|
|
131
|
+
...entry,
|
|
132
|
+
sourceLine: index + 1,
|
|
133
|
+
sourceColumn: match.index,
|
|
134
|
+
sourceEndLine: index + 1,
|
|
135
|
+
sourceEndColumn: match.index + entry.title.length,
|
|
136
|
+
coordinatesGeneratedAt: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return entry;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return entry;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
105
145
|
function findNamedDeclaration(sourceFile, title) {
|
|
106
146
|
const candidates = [];
|
|
107
147
|
for (const decl of sourceFile.getFunctions()) {
|
|
@@ -179,7 +219,10 @@ function findNamedDeclaration(sourceFile, title) {
|
|
|
179
219
|
}
|
|
180
220
|
// Fail closed: only return if exactly one unique match
|
|
181
221
|
if (internalCandidates.length === 1) {
|
|
182
|
-
|
|
222
|
+
const candidate = internalCandidates[0];
|
|
223
|
+
if (candidate) {
|
|
224
|
+
return candidate;
|
|
225
|
+
}
|
|
183
226
|
}
|
|
184
227
|
// Third pass: unique class methods
|
|
185
228
|
const methodCandidates = [];
|
|
@@ -195,7 +238,10 @@ function findNamedDeclaration(sourceFile, title) {
|
|
|
195
238
|
}
|
|
196
239
|
// Fail closed: only return if exactly one unique match
|
|
197
240
|
if (methodCandidates.length === 1) {
|
|
198
|
-
|
|
241
|
+
const candidate = methodCandidates[0];
|
|
242
|
+
if (candidate) {
|
|
243
|
+
return candidate;
|
|
244
|
+
}
|
|
199
245
|
}
|
|
200
246
|
return null;
|
|
201
247
|
}
|
package/dist/prolog/codec.d.ts
CHANGED
|
@@ -23,53 +23,15 @@ export declare function toPrologString(value: string): string;
|
|
|
23
23
|
* Alias for escapeAtom for semantic clarity.
|
|
24
24
|
*/
|
|
25
25
|
export declare function escapeAtomContent(value: string): string;
|
|
26
|
-
/**
|
|
27
|
-
* Parse a Prolog list of lists into a JavaScript array.
|
|
28
|
-
* Input: "[[a,b,c],[d,e,f]]"
|
|
29
|
-
* Output: [["a", "b", "c"], ["d", "e", "f"]]
|
|
30
|
-
*/
|
|
31
26
|
export declare function parseListOfLists(listStr: string): string[][];
|
|
32
|
-
/**
|
|
33
|
-
* Parse a single entity from Prolog binding format.
|
|
34
|
-
* Input: "[abc123, req, [id=abc123, title=\"Test\", ...]]"
|
|
35
|
-
*/
|
|
36
27
|
export declare function parseEntityFromBinding(bindingStr: string): Record<string, unknown>;
|
|
37
|
-
/**
|
|
38
|
-
* Parse entity from array returned by parseListOfLists.
|
|
39
|
-
* Input: ["abc123", "req", "[id=abc123, title=\"Test\", ...]"]
|
|
40
|
-
*/
|
|
41
28
|
export declare function parseEntityFromList(data: string[]): Record<string, unknown>;
|
|
42
|
-
/**
|
|
43
|
-
* Parse Prolog property list into JavaScript object.
|
|
44
|
-
* Input: "[id=abc123, title=\"Test\"]"
|
|
45
|
-
*/
|
|
46
29
|
export declare function parsePropertyList(propsStr: string): Record<string, unknown>;
|
|
47
|
-
/**
|
|
48
|
-
* Parse a single Prolog value, handling typed literals and URIs.
|
|
49
|
-
*/
|
|
50
30
|
export declare function parsePrologValue(valueInput: string): unknown;
|
|
51
|
-
/**
|
|
52
|
-
* General-purpose split at top level (not inside brackets or quotes).
|
|
53
|
-
* More robust version used by property parsing.
|
|
54
|
-
*/
|
|
55
31
|
export declare function splitTopLevelGeneral(str: string, delimiter: string): string[];
|
|
56
|
-
/**
|
|
57
|
-
* Split a string by delimiter at the top level (not inside brackets or quotes).
|
|
58
|
-
* @see splitTopLevelGeneral
|
|
59
|
-
*/
|
|
60
32
|
export declare function splitTopLevel(str: string, delimiter: string): string[];
|
|
61
|
-
/**
|
|
62
|
-
* Parse an atom list from Prolog response.
|
|
63
|
-
* Input: "[a, b, c]" or atom string
|
|
64
|
-
*/
|
|
65
33
|
export declare function parseAtomList(raw: string): string[];
|
|
66
|
-
/**
|
|
67
|
-
* Parse a list of pairs from Prolog response.
|
|
68
|
-
*/
|
|
69
34
|
export declare function parsePairList(raw: string): Array<[string, string]>;
|
|
70
|
-
/**
|
|
71
|
-
* Parse a list of triples from Prolog response.
|
|
72
|
-
*/
|
|
73
35
|
export declare function parseTriples(raw: string): Array<[string, string, string]>;
|
|
74
36
|
/**
|
|
75
37
|
* Parsed violation from Prolog check output.
|
|
@@ -81,10 +43,5 @@ export interface ParsedViolation {
|
|
|
81
43
|
suggestion: string;
|
|
82
44
|
source?: string;
|
|
83
45
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Parse a Prolog list of violation/5 terms into JavaScript objects.
|
|
86
|
-
* Handles descriptions and suggestions that contain commas or nested parens.
|
|
87
|
-
* Input: "[violation(rule,'EntityId',\"Desc\",\"Sugg\",'src')]"
|
|
88
|
-
*/
|
|
89
46
|
export declare function parseViolationRows(raw: string): ParsedViolation[];
|
|
90
47
|
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/prolog/codec.ts"],"names":[],"mappings":"AAUA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/prolog/codec.ts"],"names":[],"mappings":"AAUA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGhD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASpD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CA8C5D;AAED,wBAAgB,sBAAsB,CAEpC,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBzB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqB3E;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA8B3E;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CA8F5D;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAoD7E;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAGtE;AAqBD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAenD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAmBlE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBzE;AAuDD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CA6DjE"}
|