@harness-engineering/graph 0.3.4 → 0.4.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/index.d.mts +79 -14
- package/dist/index.d.ts +79 -14
- package/dist/index.js +487 -93
- package/dist/index.mjs +485 -93
- package/package.json +8 -10
package/dist/index.mjs
CHANGED
|
@@ -35,7 +35,9 @@ var NODE_TYPES = [
|
|
|
35
35
|
// Design
|
|
36
36
|
"design_token",
|
|
37
37
|
"aesthetic_intent",
|
|
38
|
-
"design_constraint"
|
|
38
|
+
"design_constraint",
|
|
39
|
+
// Traceability
|
|
40
|
+
"requirement"
|
|
39
41
|
];
|
|
40
42
|
var EDGE_TYPES = [
|
|
41
43
|
// Code relationships
|
|
@@ -64,7 +66,11 @@ var EDGE_TYPES = [
|
|
|
64
66
|
"uses_token",
|
|
65
67
|
"declares_intent",
|
|
66
68
|
"violates_design",
|
|
67
|
-
"platform_binding"
|
|
69
|
+
"platform_binding",
|
|
70
|
+
// Traceability relationships
|
|
71
|
+
"requires",
|
|
72
|
+
"verified_by",
|
|
73
|
+
"tested_by"
|
|
68
74
|
];
|
|
69
75
|
var OBSERVABILITY_TYPES = /* @__PURE__ */ new Set(["span", "metric", "log"]);
|
|
70
76
|
var CURRENT_SCHEMA_VERSION = 1;
|
|
@@ -94,9 +100,6 @@ var GraphEdgeSchema = z.object({
|
|
|
94
100
|
metadata: z.record(z.unknown()).optional()
|
|
95
101
|
});
|
|
96
102
|
|
|
97
|
-
// src/store/GraphStore.ts
|
|
98
|
-
import loki from "lokijs";
|
|
99
|
-
|
|
100
103
|
// src/store/Serializer.ts
|
|
101
104
|
import { readFile, writeFile, mkdir, access } from "fs/promises";
|
|
102
105
|
import { join } from "path";
|
|
@@ -141,28 +144,38 @@ function safeMerge(target, source) {
|
|
|
141
144
|
}
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
});
|
|
154
|
-
this.edges = this.db.addCollection("edges", {
|
|
155
|
-
indices: ["from", "to", "type"]
|
|
156
|
-
});
|
|
147
|
+
function edgeKey(from, to, type) {
|
|
148
|
+
return `${from}\0${to}\0${type}`;
|
|
149
|
+
}
|
|
150
|
+
function addToIndex(index, key, edge) {
|
|
151
|
+
const list = index.get(key);
|
|
152
|
+
if (list) {
|
|
153
|
+
list.push(edge);
|
|
154
|
+
} else {
|
|
155
|
+
index.set(key, [edge]);
|
|
157
156
|
}
|
|
157
|
+
}
|
|
158
|
+
function removeFromIndex(index, key, edge) {
|
|
159
|
+
const list = index.get(key);
|
|
160
|
+
if (!list) return;
|
|
161
|
+
const idx = list.indexOf(edge);
|
|
162
|
+
if (idx !== -1) list.splice(idx, 1);
|
|
163
|
+
if (list.length === 0) index.delete(key);
|
|
164
|
+
}
|
|
165
|
+
var GraphStore = class {
|
|
166
|
+
nodeMap = /* @__PURE__ */ new Map();
|
|
167
|
+
edgeMap = /* @__PURE__ */ new Map();
|
|
168
|
+
// keyed by from\0to\0type
|
|
169
|
+
edgesByFrom = /* @__PURE__ */ new Map();
|
|
170
|
+
edgesByTo = /* @__PURE__ */ new Map();
|
|
171
|
+
edgesByType = /* @__PURE__ */ new Map();
|
|
158
172
|
// --- Node operations ---
|
|
159
173
|
addNode(node) {
|
|
160
|
-
const existing = this.
|
|
174
|
+
const existing = this.nodeMap.get(node.id);
|
|
161
175
|
if (existing) {
|
|
162
176
|
safeMerge(existing, node);
|
|
163
|
-
this.nodes.update(existing);
|
|
164
177
|
} else {
|
|
165
|
-
this.
|
|
178
|
+
this.nodeMap.set(node.id, { ...node });
|
|
166
179
|
}
|
|
167
180
|
}
|
|
168
181
|
batchAddNodes(nodes) {
|
|
@@ -171,44 +184,44 @@ var GraphStore = class {
|
|
|
171
184
|
}
|
|
172
185
|
}
|
|
173
186
|
getNode(id) {
|
|
174
|
-
const
|
|
175
|
-
if (!
|
|
176
|
-
return
|
|
187
|
+
const node = this.nodeMap.get(id);
|
|
188
|
+
if (!node) return null;
|
|
189
|
+
return { ...node };
|
|
177
190
|
}
|
|
178
191
|
findNodes(query) {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
192
|
+
const results = [];
|
|
193
|
+
for (const node of this.nodeMap.values()) {
|
|
194
|
+
if (query.type !== void 0 && node.type !== query.type) continue;
|
|
195
|
+
if (query.name !== void 0 && node.name !== query.name) continue;
|
|
196
|
+
if (query.path !== void 0 && node.path !== query.path) continue;
|
|
197
|
+
results.push({ ...node });
|
|
198
|
+
}
|
|
199
|
+
return results;
|
|
184
200
|
}
|
|
185
201
|
removeNode(id) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const edgesToRemove = this.edges.find({
|
|
191
|
-
$or: [{ from: id }, { to: id }]
|
|
192
|
-
});
|
|
202
|
+
this.nodeMap.delete(id);
|
|
203
|
+
const fromEdges = this.edgesByFrom.get(id) ?? [];
|
|
204
|
+
const toEdges = this.edgesByTo.get(id) ?? [];
|
|
205
|
+
const edgesToRemove = /* @__PURE__ */ new Set([...fromEdges, ...toEdges]);
|
|
193
206
|
for (const edge of edgesToRemove) {
|
|
194
|
-
this.
|
|
207
|
+
this.removeEdgeInternal(edge);
|
|
195
208
|
}
|
|
196
209
|
}
|
|
197
210
|
// --- Edge operations ---
|
|
198
211
|
addEdge(edge) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
to: edge.to,
|
|
202
|
-
type: edge.type
|
|
203
|
-
});
|
|
212
|
+
const key = edgeKey(edge.from, edge.to, edge.type);
|
|
213
|
+
const existing = this.edgeMap.get(key);
|
|
204
214
|
if (existing) {
|
|
205
215
|
if (edge.metadata) {
|
|
206
216
|
safeMerge(existing, edge);
|
|
207
|
-
this.edges.update(existing);
|
|
208
217
|
}
|
|
209
218
|
return;
|
|
210
219
|
}
|
|
211
|
-
|
|
220
|
+
const copy = { ...edge };
|
|
221
|
+
this.edgeMap.set(key, copy);
|
|
222
|
+
addToIndex(this.edgesByFrom, edge.from, copy);
|
|
223
|
+
addToIndex(this.edgesByTo, edge.to, copy);
|
|
224
|
+
addToIndex(this.edgesByType, edge.type, copy);
|
|
212
225
|
}
|
|
213
226
|
batchAddEdges(edges) {
|
|
214
227
|
for (const edge of edges) {
|
|
@@ -216,22 +229,38 @@ var GraphStore = class {
|
|
|
216
229
|
}
|
|
217
230
|
}
|
|
218
231
|
getEdges(query) {
|
|
219
|
-
|
|
220
|
-
if (query.from !== void 0
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
232
|
+
let candidates;
|
|
233
|
+
if (query.from !== void 0 && query.to !== void 0 && query.type !== void 0) {
|
|
234
|
+
const edge = this.edgeMap.get(edgeKey(query.from, query.to, query.type));
|
|
235
|
+
return edge ? [{ ...edge }] : [];
|
|
236
|
+
} else if (query.from !== void 0) {
|
|
237
|
+
candidates = this.edgesByFrom.get(query.from) ?? [];
|
|
238
|
+
} else if (query.to !== void 0) {
|
|
239
|
+
candidates = this.edgesByTo.get(query.to) ?? [];
|
|
240
|
+
} else if (query.type !== void 0) {
|
|
241
|
+
candidates = this.edgesByType.get(query.type) ?? [];
|
|
242
|
+
} else {
|
|
243
|
+
candidates = this.edgeMap.values();
|
|
244
|
+
}
|
|
245
|
+
const results = [];
|
|
246
|
+
for (const edge of candidates) {
|
|
247
|
+
if (query.from !== void 0 && edge.from !== query.from) continue;
|
|
248
|
+
if (query.to !== void 0 && edge.to !== query.to) continue;
|
|
249
|
+
if (query.type !== void 0 && edge.type !== query.type) continue;
|
|
250
|
+
results.push({ ...edge });
|
|
251
|
+
}
|
|
252
|
+
return results;
|
|
224
253
|
}
|
|
225
254
|
getNeighbors(nodeId, direction = "both") {
|
|
226
255
|
const neighborIds = /* @__PURE__ */ new Set();
|
|
227
256
|
if (direction === "outbound" || direction === "both") {
|
|
228
|
-
const outEdges = this.
|
|
257
|
+
const outEdges = this.edgesByFrom.get(nodeId) ?? [];
|
|
229
258
|
for (const edge of outEdges) {
|
|
230
259
|
neighborIds.add(edge.to);
|
|
231
260
|
}
|
|
232
261
|
}
|
|
233
262
|
if (direction === "inbound" || direction === "both") {
|
|
234
|
-
const inEdges = this.
|
|
263
|
+
const inEdges = this.edgesByTo.get(nodeId) ?? [];
|
|
235
264
|
for (const edge of inEdges) {
|
|
236
265
|
neighborIds.add(edge.from);
|
|
237
266
|
}
|
|
@@ -245,20 +274,23 @@ var GraphStore = class {
|
|
|
245
274
|
}
|
|
246
275
|
// --- Counts ---
|
|
247
276
|
get nodeCount() {
|
|
248
|
-
return this.
|
|
277
|
+
return this.nodeMap.size;
|
|
249
278
|
}
|
|
250
279
|
get edgeCount() {
|
|
251
|
-
return this.
|
|
280
|
+
return this.edgeMap.size;
|
|
252
281
|
}
|
|
253
282
|
// --- Clear ---
|
|
254
283
|
clear() {
|
|
255
|
-
this.
|
|
256
|
-
this.
|
|
284
|
+
this.nodeMap.clear();
|
|
285
|
+
this.edgeMap.clear();
|
|
286
|
+
this.edgesByFrom.clear();
|
|
287
|
+
this.edgesByTo.clear();
|
|
288
|
+
this.edgesByType.clear();
|
|
257
289
|
}
|
|
258
290
|
// --- Persistence ---
|
|
259
291
|
async save(dirPath) {
|
|
260
|
-
const allNodes = this.
|
|
261
|
-
const allEdges = this.
|
|
292
|
+
const allNodes = Array.from(this.nodeMap.values()).map((n) => ({ ...n }));
|
|
293
|
+
const allEdges = Array.from(this.edgeMap.values()).map((e) => ({ ...e }));
|
|
262
294
|
await saveGraph(dirPath, allNodes, allEdges);
|
|
263
295
|
}
|
|
264
296
|
async load(dirPath) {
|
|
@@ -266,17 +298,25 @@ var GraphStore = class {
|
|
|
266
298
|
if (!data) return false;
|
|
267
299
|
this.clear();
|
|
268
300
|
for (const node of data.nodes) {
|
|
269
|
-
this.
|
|
301
|
+
this.nodeMap.set(node.id, { ...node });
|
|
270
302
|
}
|
|
271
303
|
for (const edge of data.edges) {
|
|
272
|
-
|
|
304
|
+
const copy = { ...edge };
|
|
305
|
+
const key = edgeKey(edge.from, edge.to, edge.type);
|
|
306
|
+
this.edgeMap.set(key, copy);
|
|
307
|
+
addToIndex(this.edgesByFrom, edge.from, copy);
|
|
308
|
+
addToIndex(this.edgesByTo, edge.to, copy);
|
|
309
|
+
addToIndex(this.edgesByType, edge.type, copy);
|
|
273
310
|
}
|
|
274
311
|
return true;
|
|
275
312
|
}
|
|
276
313
|
// --- Internal ---
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
314
|
+
removeEdgeInternal(edge) {
|
|
315
|
+
const key = edgeKey(edge.from, edge.to, edge.type);
|
|
316
|
+
this.edgeMap.delete(key);
|
|
317
|
+
removeFromIndex(this.edgesByFrom, edge.from, edge);
|
|
318
|
+
removeFromIndex(this.edgesByTo, edge.to, edge);
|
|
319
|
+
removeFromIndex(this.edgesByType, edge.type, edge);
|
|
280
320
|
}
|
|
281
321
|
};
|
|
282
322
|
|
|
@@ -357,11 +397,11 @@ var VectorStore = class _VectorStore {
|
|
|
357
397
|
};
|
|
358
398
|
|
|
359
399
|
// src/query/ContextQL.ts
|
|
360
|
-
function
|
|
400
|
+
function edgeKey2(e) {
|
|
361
401
|
return `${e.from}|${e.to}|${e.type}`;
|
|
362
402
|
}
|
|
363
403
|
function addEdge(state, edge) {
|
|
364
|
-
const key =
|
|
404
|
+
const key = edgeKey2(edge);
|
|
365
405
|
if (!state.edgeSet.has(key)) {
|
|
366
406
|
state.edgeSet.add(key);
|
|
367
407
|
state.resultEdges.push(edge);
|
|
@@ -541,6 +581,7 @@ var CodeIngestor = class {
|
|
|
541
581
|
constructor(store) {
|
|
542
582
|
this.store = store;
|
|
543
583
|
}
|
|
584
|
+
store;
|
|
544
585
|
async ingest(rootDir) {
|
|
545
586
|
const start = Date.now();
|
|
546
587
|
const errors = [];
|
|
@@ -563,6 +604,7 @@ var CodeIngestor = class {
|
|
|
563
604
|
this.store.addEdge(edge);
|
|
564
605
|
edgesAdded++;
|
|
565
606
|
}
|
|
607
|
+
edgesAdded += this.extractReqAnnotations(fileContents, rootDir);
|
|
566
608
|
return {
|
|
567
609
|
nodesAdded,
|
|
568
610
|
nodesUpdated: 0,
|
|
@@ -921,6 +963,48 @@ var CodeIngestor = class {
|
|
|
921
963
|
if (/\.jsx?$/.test(filePath)) return "javascript";
|
|
922
964
|
return "unknown";
|
|
923
965
|
}
|
|
966
|
+
/**
|
|
967
|
+
* Scan file contents for @req annotations and create verified_by edges
|
|
968
|
+
* linking requirement nodes to the annotated files.
|
|
969
|
+
* Format: // @req <feature-name>#<index>
|
|
970
|
+
*/
|
|
971
|
+
extractReqAnnotations(fileContents, rootDir) {
|
|
972
|
+
const REQ_TAG = /\/\/\s*@req\s+([\w-]+)#(\d+)/g;
|
|
973
|
+
const reqNodes = this.store.findNodes({ type: "requirement" });
|
|
974
|
+
let edgesAdded = 0;
|
|
975
|
+
for (const [filePath, content] of fileContents) {
|
|
976
|
+
let match;
|
|
977
|
+
REQ_TAG.lastIndex = 0;
|
|
978
|
+
while ((match = REQ_TAG.exec(content)) !== null) {
|
|
979
|
+
const featureName = match[1];
|
|
980
|
+
const reqIndex = parseInt(match[2], 10);
|
|
981
|
+
const reqNode = reqNodes.find(
|
|
982
|
+
(n) => n.metadata.featureName === featureName && n.metadata.index === reqIndex
|
|
983
|
+
);
|
|
984
|
+
if (!reqNode) {
|
|
985
|
+
console.warn(
|
|
986
|
+
`@req annotation references non-existent requirement: ${featureName}#${reqIndex} in ${filePath}`
|
|
987
|
+
);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const relPath = path.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
991
|
+
const fileNodeId = `file:${relPath}`;
|
|
992
|
+
this.store.addEdge({
|
|
993
|
+
from: reqNode.id,
|
|
994
|
+
to: fileNodeId,
|
|
995
|
+
type: "verified_by",
|
|
996
|
+
confidence: 1,
|
|
997
|
+
metadata: {
|
|
998
|
+
method: "annotation",
|
|
999
|
+
tag: `@req ${featureName}#${reqIndex}`,
|
|
1000
|
+
confidence: 1
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
edgesAdded++;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return edgesAdded;
|
|
1007
|
+
}
|
|
924
1008
|
};
|
|
925
1009
|
|
|
926
1010
|
// src/ingest/GitIngestor.ts
|
|
@@ -932,6 +1016,8 @@ var GitIngestor = class {
|
|
|
932
1016
|
this.store = store;
|
|
933
1017
|
this.gitRunner = gitRunner;
|
|
934
1018
|
}
|
|
1019
|
+
store;
|
|
1020
|
+
gitRunner;
|
|
935
1021
|
async ingest(rootDir) {
|
|
936
1022
|
const start = Date.now();
|
|
937
1023
|
const errors = [];
|
|
@@ -1111,6 +1197,7 @@ var TopologicalLinker = class {
|
|
|
1111
1197
|
constructor(store) {
|
|
1112
1198
|
this.store = store;
|
|
1113
1199
|
}
|
|
1200
|
+
store;
|
|
1114
1201
|
link() {
|
|
1115
1202
|
let edgesAdded = 0;
|
|
1116
1203
|
const files = this.store.findNodes({ type: "file" });
|
|
@@ -1208,6 +1295,7 @@ var KnowledgeIngestor = class {
|
|
|
1208
1295
|
constructor(store) {
|
|
1209
1296
|
this.store = store;
|
|
1210
1297
|
}
|
|
1298
|
+
store;
|
|
1211
1299
|
async ingestADRs(adrDir) {
|
|
1212
1300
|
const start = Date.now();
|
|
1213
1301
|
const errors = [];
|
|
@@ -1393,8 +1481,218 @@ var KnowledgeIngestor = class {
|
|
|
1393
1481
|
}
|
|
1394
1482
|
};
|
|
1395
1483
|
|
|
1396
|
-
// src/ingest/
|
|
1484
|
+
// src/ingest/RequirementIngestor.ts
|
|
1485
|
+
import * as fs3 from "fs/promises";
|
|
1486
|
+
import * as path4 from "path";
|
|
1487
|
+
var REQUIREMENT_SECTIONS = [
|
|
1488
|
+
"Observable Truths",
|
|
1489
|
+
"Success Criteria",
|
|
1490
|
+
"Acceptance Criteria"
|
|
1491
|
+
];
|
|
1492
|
+
var SECTION_HEADING_RE = /^#{2,3}\s+(.+)$/;
|
|
1493
|
+
var NUMBERED_ITEM_RE = /^\s*(\d+)\.\s+(.+)$/;
|
|
1494
|
+
function detectEarsPattern(text) {
|
|
1495
|
+
const lower = text.toLowerCase();
|
|
1496
|
+
if (/^if\b.+\bthen\b.+\bshall not\b/.test(lower)) return "unwanted";
|
|
1497
|
+
if (/^when\b/.test(lower)) return "event-driven";
|
|
1498
|
+
if (/^while\b/.test(lower)) return "state-driven";
|
|
1499
|
+
if (/^where\b/.test(lower)) return "optional";
|
|
1500
|
+
if (/^the\s+\w+\s+shall\b/.test(lower)) return "ubiquitous";
|
|
1501
|
+
return void 0;
|
|
1502
|
+
}
|
|
1397
1503
|
var CODE_NODE_TYPES2 = ["file", "function", "class", "method", "interface", "variable"];
|
|
1504
|
+
var RequirementIngestor = class {
|
|
1505
|
+
constructor(store) {
|
|
1506
|
+
this.store = store;
|
|
1507
|
+
}
|
|
1508
|
+
store;
|
|
1509
|
+
/**
|
|
1510
|
+
* Scan a specs directory for `<feature>/proposal.md` files,
|
|
1511
|
+
* extract numbered requirements from recognized sections,
|
|
1512
|
+
* and create requirement nodes with convention-based edges.
|
|
1513
|
+
*/
|
|
1514
|
+
async ingestSpecs(specsDir) {
|
|
1515
|
+
const start = Date.now();
|
|
1516
|
+
const errors = [];
|
|
1517
|
+
let nodesAdded = 0;
|
|
1518
|
+
let edgesAdded = 0;
|
|
1519
|
+
let featureDirs;
|
|
1520
|
+
try {
|
|
1521
|
+
const entries = await fs3.readdir(specsDir, { withFileTypes: true });
|
|
1522
|
+
featureDirs = entries.filter((e) => e.isDirectory()).map((e) => path4.join(specsDir, e.name));
|
|
1523
|
+
} catch {
|
|
1524
|
+
return emptyResult(Date.now() - start);
|
|
1525
|
+
}
|
|
1526
|
+
for (const featureDir of featureDirs) {
|
|
1527
|
+
const featureName = path4.basename(featureDir);
|
|
1528
|
+
const specPath = path4.join(featureDir, "proposal.md");
|
|
1529
|
+
let content;
|
|
1530
|
+
try {
|
|
1531
|
+
content = await fs3.readFile(specPath, "utf-8");
|
|
1532
|
+
} catch {
|
|
1533
|
+
continue;
|
|
1534
|
+
}
|
|
1535
|
+
try {
|
|
1536
|
+
const specHash = hash(specPath);
|
|
1537
|
+
const specNodeId = `file:${specPath}`;
|
|
1538
|
+
this.store.addNode({
|
|
1539
|
+
id: specNodeId,
|
|
1540
|
+
type: "document",
|
|
1541
|
+
name: path4.basename(specPath),
|
|
1542
|
+
path: specPath,
|
|
1543
|
+
metadata: { featureName }
|
|
1544
|
+
});
|
|
1545
|
+
const requirements = this.extractRequirements(content, specPath, specHash, featureName);
|
|
1546
|
+
for (const req of requirements) {
|
|
1547
|
+
this.store.addNode(req.node);
|
|
1548
|
+
nodesAdded++;
|
|
1549
|
+
this.store.addEdge({
|
|
1550
|
+
from: req.node.id,
|
|
1551
|
+
to: specNodeId,
|
|
1552
|
+
type: "specifies"
|
|
1553
|
+
});
|
|
1554
|
+
edgesAdded++;
|
|
1555
|
+
edgesAdded += this.linkByPathPattern(req.node.id, featureName);
|
|
1556
|
+
edgesAdded += this.linkByKeywordOverlap(req.node.id, req.node.name);
|
|
1557
|
+
}
|
|
1558
|
+
} catch (err) {
|
|
1559
|
+
errors.push(`${specPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return {
|
|
1563
|
+
nodesAdded,
|
|
1564
|
+
nodesUpdated: 0,
|
|
1565
|
+
edgesAdded,
|
|
1566
|
+
edgesUpdated: 0,
|
|
1567
|
+
errors,
|
|
1568
|
+
durationMs: Date.now() - start
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Parse markdown content and extract numbered items from recognized sections.
|
|
1573
|
+
*/
|
|
1574
|
+
extractRequirements(content, specPath, specHash, featureName) {
|
|
1575
|
+
const lines = content.split("\n");
|
|
1576
|
+
const results = [];
|
|
1577
|
+
let currentSection;
|
|
1578
|
+
let inRequirementSection = false;
|
|
1579
|
+
let globalIndex = 0;
|
|
1580
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1581
|
+
const line = lines[i];
|
|
1582
|
+
const headingMatch = line.match(SECTION_HEADING_RE);
|
|
1583
|
+
if (headingMatch) {
|
|
1584
|
+
const heading = headingMatch[1].trim();
|
|
1585
|
+
const isReqSection = REQUIREMENT_SECTIONS.some(
|
|
1586
|
+
(s) => heading.toLowerCase() === s.toLowerCase()
|
|
1587
|
+
);
|
|
1588
|
+
if (isReqSection) {
|
|
1589
|
+
currentSection = heading;
|
|
1590
|
+
inRequirementSection = true;
|
|
1591
|
+
} else {
|
|
1592
|
+
inRequirementSection = false;
|
|
1593
|
+
}
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
if (!inRequirementSection) continue;
|
|
1597
|
+
const itemMatch = line.match(NUMBERED_ITEM_RE);
|
|
1598
|
+
if (!itemMatch) continue;
|
|
1599
|
+
const index = parseInt(itemMatch[1], 10);
|
|
1600
|
+
const text = itemMatch[2].trim();
|
|
1601
|
+
const rawText = line.trim();
|
|
1602
|
+
const lineNumber = i + 1;
|
|
1603
|
+
globalIndex++;
|
|
1604
|
+
const nodeId = `req:${specHash}:${globalIndex}`;
|
|
1605
|
+
const earsPattern = detectEarsPattern(text);
|
|
1606
|
+
results.push({
|
|
1607
|
+
node: {
|
|
1608
|
+
id: nodeId,
|
|
1609
|
+
type: "requirement",
|
|
1610
|
+
name: text,
|
|
1611
|
+
path: specPath,
|
|
1612
|
+
location: {
|
|
1613
|
+
fileId: `file:${specPath}`,
|
|
1614
|
+
startLine: lineNumber,
|
|
1615
|
+
endLine: lineNumber
|
|
1616
|
+
},
|
|
1617
|
+
metadata: {
|
|
1618
|
+
specPath,
|
|
1619
|
+
index,
|
|
1620
|
+
section: currentSection,
|
|
1621
|
+
rawText,
|
|
1622
|
+
earsPattern,
|
|
1623
|
+
featureName
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
return results;
|
|
1629
|
+
}
|
|
1630
|
+
/**
|
|
1631
|
+
* Convention-based linking: match requirement to code/test files
|
|
1632
|
+
* by feature name in their path.
|
|
1633
|
+
*/
|
|
1634
|
+
linkByPathPattern(reqId, featureName) {
|
|
1635
|
+
let count = 0;
|
|
1636
|
+
const fileNodes = this.store.findNodes({ type: "file" });
|
|
1637
|
+
for (const node of fileNodes) {
|
|
1638
|
+
if (!node.path) continue;
|
|
1639
|
+
const normalizedPath = node.path.replace(/\\/g, "/");
|
|
1640
|
+
const isCodeMatch = normalizedPath.includes("packages/") && path4.basename(normalizedPath).includes(featureName);
|
|
1641
|
+
const isTestMatch = normalizedPath.includes("/tests/") && // platform-safe
|
|
1642
|
+
path4.basename(normalizedPath).includes(featureName);
|
|
1643
|
+
if (isCodeMatch && !isTestMatch) {
|
|
1644
|
+
this.store.addEdge({
|
|
1645
|
+
from: reqId,
|
|
1646
|
+
to: node.id,
|
|
1647
|
+
type: "requires",
|
|
1648
|
+
confidence: 0.5,
|
|
1649
|
+
metadata: { method: "convention", matchReason: "path-pattern" }
|
|
1650
|
+
});
|
|
1651
|
+
count++;
|
|
1652
|
+
} else if (isTestMatch) {
|
|
1653
|
+
this.store.addEdge({
|
|
1654
|
+
from: reqId,
|
|
1655
|
+
to: node.id,
|
|
1656
|
+
type: "verified_by",
|
|
1657
|
+
confidence: 0.5,
|
|
1658
|
+
metadata: { method: "convention", matchReason: "path-pattern" }
|
|
1659
|
+
});
|
|
1660
|
+
count++;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
return count;
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Convention-based linking: match requirement text to code nodes
|
|
1667
|
+
* by keyword overlap (function/class names appearing in requirement text).
|
|
1668
|
+
*/
|
|
1669
|
+
linkByKeywordOverlap(reqId, reqText) {
|
|
1670
|
+
let count = 0;
|
|
1671
|
+
for (const nodeType of CODE_NODE_TYPES2) {
|
|
1672
|
+
const codeNodes = this.store.findNodes({ type: nodeType });
|
|
1673
|
+
for (const node of codeNodes) {
|
|
1674
|
+
if (node.name.length < 3) continue;
|
|
1675
|
+
const escaped = node.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1676
|
+
const namePattern = new RegExp(`\\b${escaped}\\b`, "i");
|
|
1677
|
+
if (namePattern.test(reqText)) {
|
|
1678
|
+
const edgeType = node.path && node.path.replace(/\\/g, "/").includes("/tests/") ? "verified_by" : "requires";
|
|
1679
|
+
this.store.addEdge({
|
|
1680
|
+
from: reqId,
|
|
1681
|
+
to: node.id,
|
|
1682
|
+
type: edgeType,
|
|
1683
|
+
confidence: 0.6,
|
|
1684
|
+
metadata: { method: "convention", matchReason: "keyword-overlap" }
|
|
1685
|
+
});
|
|
1686
|
+
count++;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return count;
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
|
|
1694
|
+
// src/ingest/connectors/ConnectorUtils.ts
|
|
1695
|
+
var CODE_NODE_TYPES3 = ["file", "function", "class", "method", "interface", "variable"];
|
|
1398
1696
|
var SANITIZE_RULES = [
|
|
1399
1697
|
// Strip XML/HTML-like instruction tags that could be interpreted as system prompts
|
|
1400
1698
|
{
|
|
@@ -1429,7 +1727,7 @@ function sanitizeExternalText(text, maxLength = 2e3) {
|
|
|
1429
1727
|
}
|
|
1430
1728
|
function linkToCode(store, content, sourceNodeId, edgeType, options) {
|
|
1431
1729
|
let edgesCreated = 0;
|
|
1432
|
-
for (const type of
|
|
1730
|
+
for (const type of CODE_NODE_TYPES3) {
|
|
1433
1731
|
const nodes = store.findNodes({ type });
|
|
1434
1732
|
for (const node of nodes) {
|
|
1435
1733
|
if (node.name.length < 3) continue;
|
|
@@ -1449,13 +1747,14 @@ function linkToCode(store, content, sourceNodeId, edgeType, options) {
|
|
|
1449
1747
|
}
|
|
1450
1748
|
|
|
1451
1749
|
// src/ingest/connectors/SyncManager.ts
|
|
1452
|
-
import * as
|
|
1453
|
-
import * as
|
|
1750
|
+
import * as fs4 from "fs/promises";
|
|
1751
|
+
import * as path5 from "path";
|
|
1454
1752
|
var SyncManager = class {
|
|
1455
1753
|
constructor(store, graphDir) {
|
|
1456
1754
|
this.store = store;
|
|
1457
|
-
this.metadataPath =
|
|
1755
|
+
this.metadataPath = path5.join(graphDir, "sync-metadata.json");
|
|
1458
1756
|
}
|
|
1757
|
+
store;
|
|
1459
1758
|
registrations = /* @__PURE__ */ new Map();
|
|
1460
1759
|
metadataPath;
|
|
1461
1760
|
registerConnector(connector, config) {
|
|
@@ -1508,15 +1807,15 @@ var SyncManager = class {
|
|
|
1508
1807
|
}
|
|
1509
1808
|
async loadMetadata() {
|
|
1510
1809
|
try {
|
|
1511
|
-
const raw = await
|
|
1810
|
+
const raw = await fs4.readFile(this.metadataPath, "utf-8");
|
|
1512
1811
|
return JSON.parse(raw);
|
|
1513
1812
|
} catch {
|
|
1514
1813
|
return { connectors: {} };
|
|
1515
1814
|
}
|
|
1516
1815
|
}
|
|
1517
1816
|
async saveMetadata(metadata) {
|
|
1518
|
-
await
|
|
1519
|
-
await
|
|
1817
|
+
await fs4.mkdir(path5.dirname(this.metadataPath), { recursive: true });
|
|
1818
|
+
await fs4.writeFile(this.metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
1520
1819
|
}
|
|
1521
1820
|
};
|
|
1522
1821
|
|
|
@@ -2045,11 +2344,12 @@ var FusionLayer = class {
|
|
|
2045
2344
|
};
|
|
2046
2345
|
|
|
2047
2346
|
// src/entropy/GraphEntropyAdapter.ts
|
|
2048
|
-
var
|
|
2347
|
+
var CODE_NODE_TYPES4 = ["file", "function", "class", "method", "interface", "variable"];
|
|
2049
2348
|
var GraphEntropyAdapter = class {
|
|
2050
2349
|
constructor(store) {
|
|
2051
2350
|
this.store = store;
|
|
2052
2351
|
}
|
|
2352
|
+
store;
|
|
2053
2353
|
/**
|
|
2054
2354
|
* Find all `documents` edges and classify them as stale or missing-target.
|
|
2055
2355
|
*
|
|
@@ -2111,7 +2411,7 @@ var GraphEntropyAdapter = class {
|
|
|
2111
2411
|
}
|
|
2112
2412
|
findEntryPoints() {
|
|
2113
2413
|
const entryPoints = [];
|
|
2114
|
-
for (const nodeType of
|
|
2414
|
+
for (const nodeType of CODE_NODE_TYPES4) {
|
|
2115
2415
|
const nodes = this.store.findNodes({ type: nodeType });
|
|
2116
2416
|
for (const node of nodes) {
|
|
2117
2417
|
const isIndexFile = nodeType === "file" && node.name === "index.ts";
|
|
@@ -2147,7 +2447,7 @@ var GraphEntropyAdapter = class {
|
|
|
2147
2447
|
}
|
|
2148
2448
|
collectUnreachableNodes(visited) {
|
|
2149
2449
|
const unreachableNodes = [];
|
|
2150
|
-
for (const nodeType of
|
|
2450
|
+
for (const nodeType of CODE_NODE_TYPES4) {
|
|
2151
2451
|
const nodes = this.store.findNodes({ type: nodeType });
|
|
2152
2452
|
for (const node of nodes) {
|
|
2153
2453
|
if (!visited.has(node.id)) {
|
|
@@ -2190,6 +2490,7 @@ var GraphComplexityAdapter = class {
|
|
|
2190
2490
|
constructor(store) {
|
|
2191
2491
|
this.store = store;
|
|
2192
2492
|
}
|
|
2493
|
+
store;
|
|
2193
2494
|
/**
|
|
2194
2495
|
* Compute complexity hotspots by combining cyclomatic complexity with change frequency.
|
|
2195
2496
|
*
|
|
@@ -2277,6 +2578,7 @@ var GraphCouplingAdapter = class {
|
|
|
2277
2578
|
constructor(store) {
|
|
2278
2579
|
this.store = store;
|
|
2279
2580
|
}
|
|
2581
|
+
store;
|
|
2280
2582
|
/**
|
|
2281
2583
|
* Compute coupling data for all file nodes in the graph.
|
|
2282
2584
|
*
|
|
@@ -2346,6 +2648,7 @@ var GraphAnomalyAdapter = class {
|
|
|
2346
2648
|
constructor(store) {
|
|
2347
2649
|
this.store = store;
|
|
2348
2650
|
}
|
|
2651
|
+
store;
|
|
2349
2652
|
detect(options) {
|
|
2350
2653
|
const threshold = options?.threshold != null && options.threshold > 0 ? options.threshold : DEFAULT_THRESHOLD;
|
|
2351
2654
|
const requestedMetrics = options?.metrics ?? [...DEFAULT_METRICS];
|
|
@@ -2964,9 +3267,9 @@ var EntityExtractor = class {
|
|
|
2964
3267
|
}
|
|
2965
3268
|
const pathConsumed = /* @__PURE__ */ new Set();
|
|
2966
3269
|
for (const match of trimmed.matchAll(FILE_PATH_RE)) {
|
|
2967
|
-
const
|
|
2968
|
-
add(
|
|
2969
|
-
pathConsumed.add(
|
|
3270
|
+
const path7 = match[0];
|
|
3271
|
+
add(path7);
|
|
3272
|
+
pathConsumed.add(path7);
|
|
2970
3273
|
}
|
|
2971
3274
|
const allConsumed = buildConsumedSet(quotedConsumed, casingConsumed, pathConsumed);
|
|
2972
3275
|
const words = trimmed.split(/\s+/);
|
|
@@ -3037,8 +3340,8 @@ var EntityResolver = class {
|
|
|
3037
3340
|
if (isPathLike && node.path.includes(raw)) {
|
|
3038
3341
|
return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
|
|
3039
3342
|
}
|
|
3040
|
-
const
|
|
3041
|
-
if (
|
|
3343
|
+
const basename5 = node.path.split("/").pop() ?? "";
|
|
3344
|
+
if (basename5.includes(raw)) {
|
|
3042
3345
|
return { raw, nodeId: node.id, node, confidence: 0.6, method: "path" };
|
|
3043
3346
|
}
|
|
3044
3347
|
if (raw.length >= 4 && node.path.includes(raw)) {
|
|
@@ -3113,13 +3416,13 @@ var ResponseFormatter = class {
|
|
|
3113
3416
|
const context = Array.isArray(d?.context) ? d.context : [];
|
|
3114
3417
|
const firstEntity = entities[0];
|
|
3115
3418
|
const nodeType = firstEntity?.node.type ?? "node";
|
|
3116
|
-
const
|
|
3419
|
+
const path7 = firstEntity?.node.path ?? "unknown";
|
|
3117
3420
|
let neighborCount = 0;
|
|
3118
3421
|
const firstContext = context[0];
|
|
3119
3422
|
if (firstContext && Array.isArray(firstContext.nodes)) {
|
|
3120
3423
|
neighborCount = firstContext.nodes.length;
|
|
3121
3424
|
}
|
|
3122
|
-
return `**${entityName}** is a ${nodeType} at \`${
|
|
3425
|
+
return `**${entityName}** is a ${nodeType} at \`${path7}\`. Connected to ${neighborCount} nodes.`;
|
|
3123
3426
|
}
|
|
3124
3427
|
formatAnomaly(data) {
|
|
3125
3428
|
const d = data;
|
|
@@ -3260,7 +3563,7 @@ var PHASE_NODE_TYPES = {
|
|
|
3260
3563
|
debug: ["failure", "learning", "function", "method"],
|
|
3261
3564
|
plan: ["adr", "document", "module", "layer"]
|
|
3262
3565
|
};
|
|
3263
|
-
var
|
|
3566
|
+
var CODE_NODE_TYPES5 = /* @__PURE__ */ new Set([
|
|
3264
3567
|
"file",
|
|
3265
3568
|
"function",
|
|
3266
3569
|
"class",
|
|
@@ -3475,7 +3778,7 @@ var Assembler = class {
|
|
|
3475
3778
|
*/
|
|
3476
3779
|
checkCoverage() {
|
|
3477
3780
|
const codeNodes = [];
|
|
3478
|
-
for (const type of
|
|
3781
|
+
for (const type of CODE_NODE_TYPES5) {
|
|
3479
3782
|
codeNodes.push(...this.store.findNodes({ type }));
|
|
3480
3783
|
}
|
|
3481
3784
|
const documented = [];
|
|
@@ -3499,6 +3802,89 @@ var Assembler = class {
|
|
|
3499
3802
|
}
|
|
3500
3803
|
};
|
|
3501
3804
|
|
|
3805
|
+
// src/query/Traceability.ts
|
|
3806
|
+
function queryTraceability(store, options) {
|
|
3807
|
+
const allRequirements = store.findNodes({ type: "requirement" });
|
|
3808
|
+
const filtered = allRequirements.filter((node) => {
|
|
3809
|
+
if (options?.specPath && node.metadata?.specPath !== options.specPath) return false;
|
|
3810
|
+
if (options?.featureName && node.metadata?.featureName !== options.featureName) return false;
|
|
3811
|
+
return true;
|
|
3812
|
+
});
|
|
3813
|
+
if (filtered.length === 0) return [];
|
|
3814
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3815
|
+
for (const req of filtered) {
|
|
3816
|
+
const meta = req.metadata;
|
|
3817
|
+
const specPath = meta?.specPath ?? "";
|
|
3818
|
+
const featureName = meta?.featureName ?? "";
|
|
3819
|
+
const key = `${specPath}\0${featureName}`;
|
|
3820
|
+
const list = groups.get(key);
|
|
3821
|
+
if (list) {
|
|
3822
|
+
list.push(req);
|
|
3823
|
+
} else {
|
|
3824
|
+
groups.set(key, [req]);
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
const results = [];
|
|
3828
|
+
for (const [, reqs] of groups) {
|
|
3829
|
+
const firstReq = reqs[0];
|
|
3830
|
+
const firstMeta = firstReq.metadata;
|
|
3831
|
+
const specPath = firstMeta?.specPath ?? "";
|
|
3832
|
+
const featureName = firstMeta?.featureName ?? "";
|
|
3833
|
+
const requirements = [];
|
|
3834
|
+
for (const req of reqs) {
|
|
3835
|
+
const requiresEdges = store.getEdges({ from: req.id, type: "requires" });
|
|
3836
|
+
const codeFiles = requiresEdges.map((edge) => {
|
|
3837
|
+
const targetNode = store.getNode(edge.to);
|
|
3838
|
+
return {
|
|
3839
|
+
path: targetNode?.path ?? edge.to,
|
|
3840
|
+
confidence: edge.confidence ?? edge.metadata?.confidence ?? 0,
|
|
3841
|
+
method: edge.metadata?.method ?? "convention"
|
|
3842
|
+
};
|
|
3843
|
+
});
|
|
3844
|
+
const verifiedByEdges = store.getEdges({ from: req.id, type: "verified_by" });
|
|
3845
|
+
const testFiles = verifiedByEdges.map((edge) => {
|
|
3846
|
+
const targetNode = store.getNode(edge.to);
|
|
3847
|
+
return {
|
|
3848
|
+
path: targetNode?.path ?? edge.to,
|
|
3849
|
+
confidence: edge.confidence ?? edge.metadata?.confidence ?? 0,
|
|
3850
|
+
method: edge.metadata?.method ?? "convention"
|
|
3851
|
+
};
|
|
3852
|
+
});
|
|
3853
|
+
const hasCode = codeFiles.length > 0;
|
|
3854
|
+
const hasTests = testFiles.length > 0;
|
|
3855
|
+
const status = hasCode && hasTests ? "full" : hasCode ? "code-only" : hasTests ? "test-only" : "none";
|
|
3856
|
+
const allConfidences = [
|
|
3857
|
+
...codeFiles.map((f) => f.confidence),
|
|
3858
|
+
...testFiles.map((f) => f.confidence)
|
|
3859
|
+
];
|
|
3860
|
+
const maxConfidence = allConfidences.length > 0 ? Math.max(...allConfidences) : 0;
|
|
3861
|
+
requirements.push({
|
|
3862
|
+
requirementId: req.id,
|
|
3863
|
+
requirementName: req.name,
|
|
3864
|
+
index: req.metadata?.index ?? 0,
|
|
3865
|
+
codeFiles,
|
|
3866
|
+
testFiles,
|
|
3867
|
+
status,
|
|
3868
|
+
maxConfidence
|
|
3869
|
+
});
|
|
3870
|
+
}
|
|
3871
|
+
requirements.sort((a, b) => a.index - b.index);
|
|
3872
|
+
const total = requirements.length;
|
|
3873
|
+
const withCode = requirements.filter((r) => r.codeFiles.length > 0).length;
|
|
3874
|
+
const withTests = requirements.filter((r) => r.testFiles.length > 0).length;
|
|
3875
|
+
const fullyTraced = requirements.filter((r) => r.status === "full").length;
|
|
3876
|
+
const untraceable = requirements.filter((r) => r.status === "none").length;
|
|
3877
|
+
const coveragePercent = total > 0 ? Math.round(fullyTraced / total * 100) : 0;
|
|
3878
|
+
results.push({
|
|
3879
|
+
specPath,
|
|
3880
|
+
featureName,
|
|
3881
|
+
requirements,
|
|
3882
|
+
summary: { total, withCode, withTests, fullyTraced, untraceable, coveragePercent }
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3885
|
+
return results;
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3502
3888
|
// src/constraints/GraphConstraintAdapter.ts
|
|
3503
3889
|
import { minimatch } from "minimatch";
|
|
3504
3890
|
import { relative as relative2 } from "path";
|
|
@@ -3506,6 +3892,7 @@ var GraphConstraintAdapter = class {
|
|
|
3506
3892
|
constructor(store) {
|
|
3507
3893
|
this.store = store;
|
|
3508
3894
|
}
|
|
3895
|
+
store;
|
|
3509
3896
|
computeDependencyGraph() {
|
|
3510
3897
|
const fileNodes = this.store.findNodes({ type: "file" });
|
|
3511
3898
|
const nodes = fileNodes.map((n) => n.path ?? n.id);
|
|
@@ -3557,14 +3944,14 @@ var GraphConstraintAdapter = class {
|
|
|
3557
3944
|
};
|
|
3558
3945
|
|
|
3559
3946
|
// src/ingest/DesignIngestor.ts
|
|
3560
|
-
import * as
|
|
3561
|
-
import * as
|
|
3947
|
+
import * as fs5 from "fs/promises";
|
|
3948
|
+
import * as path6 from "path";
|
|
3562
3949
|
function isDTCGToken(obj) {
|
|
3563
3950
|
return typeof obj === "object" && obj !== null && "$value" in obj && "$type" in obj;
|
|
3564
3951
|
}
|
|
3565
3952
|
async function readFileOrNull(filePath) {
|
|
3566
3953
|
try {
|
|
3567
|
-
return await
|
|
3954
|
+
return await fs5.readFile(filePath, "utf-8");
|
|
3568
3955
|
} catch {
|
|
3569
3956
|
return null;
|
|
3570
3957
|
}
|
|
@@ -3650,6 +4037,7 @@ var DesignIngestor = class {
|
|
|
3650
4037
|
constructor(store) {
|
|
3651
4038
|
this.store = store;
|
|
3652
4039
|
}
|
|
4040
|
+
store;
|
|
3653
4041
|
async ingestTokens(tokensPath) {
|
|
3654
4042
|
const start = Date.now();
|
|
3655
4043
|
const content = await readFileOrNull(tokensPath);
|
|
@@ -3709,8 +4097,8 @@ var DesignIngestor = class {
|
|
|
3709
4097
|
async ingestAll(designDir) {
|
|
3710
4098
|
const start = Date.now();
|
|
3711
4099
|
const [tokensResult, intentResult] = await Promise.all([
|
|
3712
|
-
this.ingestTokens(
|
|
3713
|
-
this.ingestDesignIntent(
|
|
4100
|
+
this.ingestTokens(path6.join(designDir, "tokens.json")),
|
|
4101
|
+
this.ingestDesignIntent(path6.join(designDir, "DESIGN.md"))
|
|
3714
4102
|
]);
|
|
3715
4103
|
const merged = mergeResults(tokensResult, intentResult);
|
|
3716
4104
|
return { ...merged, durationMs: Date.now() - start };
|
|
@@ -3722,6 +4110,7 @@ var DesignConstraintAdapter = class {
|
|
|
3722
4110
|
constructor(store) {
|
|
3723
4111
|
this.store = store;
|
|
3724
4112
|
}
|
|
4113
|
+
store;
|
|
3725
4114
|
checkForHardcodedColors(source, file, strictness) {
|
|
3726
4115
|
const severity = this.mapSeverity(strictness);
|
|
3727
4116
|
const tokenNodes = this.store.findNodes({ type: "design_token" });
|
|
@@ -3805,6 +4194,7 @@ var GraphFeedbackAdapter = class {
|
|
|
3805
4194
|
constructor(store) {
|
|
3806
4195
|
this.store = store;
|
|
3807
4196
|
}
|
|
4197
|
+
store;
|
|
3808
4198
|
computeImpactData(changedFiles) {
|
|
3809
4199
|
const affectedTests = [];
|
|
3810
4200
|
const affectedDocs = [];
|
|
@@ -3957,10 +4347,10 @@ var TaskIndependenceAnalyzer = class {
|
|
|
3957
4347
|
includeTypes: ["file"]
|
|
3958
4348
|
});
|
|
3959
4349
|
for (const n of queryResult.nodes) {
|
|
3960
|
-
const
|
|
3961
|
-
if (!fileSet.has(
|
|
3962
|
-
if (!result.has(
|
|
3963
|
-
result.set(
|
|
4350
|
+
const path7 = n.path ?? n.id.replace(/^file:/, "");
|
|
4351
|
+
if (!fileSet.has(path7)) {
|
|
4352
|
+
if (!result.has(path7)) {
|
|
4353
|
+
result.set(path7, file);
|
|
3964
4354
|
}
|
|
3965
4355
|
}
|
|
3966
4356
|
}
|
|
@@ -4344,6 +4734,7 @@ export {
|
|
|
4344
4734
|
KnowledgeIngestor,
|
|
4345
4735
|
NODE_TYPES,
|
|
4346
4736
|
OBSERVABILITY_TYPES,
|
|
4737
|
+
RequirementIngestor,
|
|
4347
4738
|
ResponseFormatter,
|
|
4348
4739
|
SlackConnector,
|
|
4349
4740
|
SyncManager,
|
|
@@ -4356,5 +4747,6 @@ export {
|
|
|
4356
4747
|
linkToCode,
|
|
4357
4748
|
loadGraph,
|
|
4358
4749
|
project,
|
|
4750
|
+
queryTraceability,
|
|
4359
4751
|
saveGraph
|
|
4360
4752
|
};
|