@documentation-robotics/cli 0.2.0 → 0.3.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/README.md +36 -2
- package/dist/analysis/relationship-classifier.js +2 -9
- package/dist/analysis/relationship-classifier.js.map +2 -2
- package/dist/analyzers/call-graph-utils.js +2 -2
- package/dist/analyzers/call-graph-utils.js.map +2 -2
- package/dist/analyzers/cards/build.js +171 -0
- package/dist/analyzers/cards/build.js.map +7 -0
- package/dist/analyzers/cards/classify.js +42 -0
- package/dist/analyzers/cards/classify.js.map +7 -0
- package/dist/analyzers/cards/edge-index.js +44 -0
- package/dist/analyzers/cards/edge-index.js.map +7 -0
- package/dist/analyzers/cards/facts.js +46 -0
- package/dist/analyzers/cards/facts.js.map +7 -0
- package/dist/analyzers/cards/graph.js +42 -0
- package/dist/analyzers/cards/graph.js.map +7 -0
- package/dist/analyzers/cards/index.js +8 -0
- package/dist/analyzers/cards/index.js.map +7 -0
- package/dist/analyzers/cards/tier0.js +18 -0
- package/dist/analyzers/cards/tier0.js.map +7 -0
- package/dist/analyzers/cards/types.js +1 -0
- package/dist/analyzers/cards/types.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +195 -171
- package/dist/analyzers/cbm-analyzer.js.map +2 -2
- package/dist/analyzers/cbm-responses.js +336 -0
- package/dist/analyzers/cbm-responses.js.map +7 -0
- package/dist/analyzers/edge-relationships.js +45 -0
- package/dist/analyzers/edge-relationships.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +9 -0
- package/dist/analyzers/mapping-loader.js.map +2 -2
- package/dist/analyzers/tier0-rules.js +153 -0
- package/dist/analyzers/tier0-rules.js.map +7 -0
- package/dist/analyzers/types.js.map +1 -1
- package/dist/audit/relationships/ai/prompts.js +10 -10
- package/dist/audit/relationships/ai/prompts.js.map +2 -2
- package/dist/audit/relationships/analysis/layer-templates.js +52 -52
- package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
- package/dist/cli.js +12 -2
- package/dist/cli.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +31 -1
- package/dist/coding-agents/default-permissions.js.map +2 -2
- package/dist/coding-agents/drbot-mcp.js +167 -0
- package/dist/coding-agents/drbot-mcp.js.map +7 -0
- package/dist/commands/add.js +3 -2
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +117 -14
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/changeset-verify.js +55 -0
- package/dist/commands/changeset-verify.js.map +7 -0
- package/dist/commands/changeset.js +43 -22
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +10 -4
- package/dist/commands/delete.js.map +2 -2
- package/dist/commands/farm.js +176 -60
- package/dist/commands/farm.js.map +2 -2
- package/dist/commands/mcp.js +19 -4
- package/dist/commands/mcp.js.map +2 -2
- package/dist/commands/relationship.js +207 -26
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/update.js +42 -23
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +99 -11
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/version.js +1 -1
- package/dist/commands/visualize.js +4 -3
- package/dist/commands/visualize.js.map +2 -2
- package/dist/core/catalog-migration.js +33 -0
- package/dist/core/catalog-migration.js.map +7 -0
- package/dist/core/changeset-analysis.js +421 -0
- package/dist/core/changeset-analysis.js.map +7 -0
- package/dist/core/changeset-base-index.js +153 -0
- package/dist/core/changeset-base-index.js.map +7 -0
- package/dist/core/changeset-brief-delta.js +102 -0
- package/dist/core/changeset-brief-delta.js.map +7 -0
- package/dist/core/changeset-graph.js +281 -0
- package/dist/core/changeset-graph.js.map +7 -0
- package/dist/core/changeset-only-guard.js +32 -0
- package/dist/core/changeset-only-guard.js.map +7 -0
- package/dist/core/changeset-summary.js +68 -0
- package/dist/core/changeset-summary.js.map +7 -0
- package/dist/core/changeset-validator.js +66 -20
- package/dist/core/changeset-validator.js.map +2 -2
- package/dist/core/changeset-verify.js +518 -0
- package/dist/core/changeset-verify.js.map +7 -0
- package/dist/core/changeset.js +56 -9
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/element-type-migration.js +145 -0
- package/dist/core/element-type-migration.js.map +7 -0
- package/dist/core/farm-manifest.js +12 -6
- package/dist/core/farm-manifest.js.map +2 -2
- package/dist/core/farm-sync-engine.js +1 -1
- package/dist/core/farm-sync-engine.js.map +2 -2
- package/dist/core/farm-sync-state.js +10 -2
- package/dist/core/farm-sync-state.js.map +2 -2
- package/dist/core/migration-registry.js +75 -46
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model-upgrade.js +102 -0
- package/dist/core/model-upgrade.js.map +7 -0
- package/dist/core/model.js +82 -30
- package/dist/core/model.js.map +2 -2
- package/dist/core/mutation-handler.js +71 -6
- package/dist/core/mutation-handler.js.map +2 -2
- package/dist/core/relationship-migration-table.js +6639 -0
- package/dist/core/relationship-migration-table.js.map +7 -0
- package/dist/core/relationship-migration.js +272 -0
- package/dist/core/relationship-migration.js.map +7 -0
- package/dist/core/relationship-prune.js +49 -0
- package/dist/core/relationship-prune.js.map +7 -0
- package/dist/core/relationship-staging.js +68 -0
- package/dist/core/relationship-staging.js.map +7 -0
- package/dist/core/relationships.js +26 -4
- package/dist/core/relationships.js.map +2 -2
- package/dist/core/report-data-model.js +7 -13
- package/dist/core/report-data-model.js.map +2 -2
- package/dist/core/staged-changeset-storage.js +64 -17
- package/dist/core/staged-changeset-storage.js.map +2 -2
- package/dist/core/staged-source-reference.js +28 -0
- package/dist/core/staged-source-reference.js.map +7 -0
- package/dist/core/staging-area.js +83 -18
- package/dist/core/staging-area.js.map +2 -2
- package/dist/core/virtual-projection.js +13 -5
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/export/archimate-exporter.js +1 -0
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/generated/compiled-validators.js +2 -2
- package/dist/generated/compiled-validators.js.map +2 -2
- package/dist/generated/layer-registry.js +16 -16
- package/dist/generated/layer-registry.js.map +2 -2
- package/dist/generated/node-types.js +186 -168
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +2236 -7790
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
- package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
- package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
- package/dist/integrations/claude_code/commands/dr-map.md +13 -0
- package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
- package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +26 -2
- package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
- package/dist/integrations/claude_code/docs/README.md +48 -1
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
- package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
- package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
- package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
- package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
- package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
- package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
- package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
- package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
- package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
- package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
- package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
- package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
- package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
- package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
- package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
- package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
- package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
- package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
- package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
- package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
- package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
- package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
- package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
- package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
- package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/hash-utils.js.map +2 -2
- package/dist/mcp/api-key-manager.js +14 -1
- package/dist/mcp/api-key-manager.js.map +2 -2
- package/dist/mcp/changeset-only.js +34 -0
- package/dist/mcp/changeset-only.js.map +7 -0
- package/dist/mcp/tool-registry.js +19 -3
- package/dist/mcp/tool-registry.js.map +2 -2
- package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
- package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
- package/dist/mcp/tools/changeset-editing.js +184 -0
- package/dist/mcp/tools/changeset-editing.js.map +7 -0
- package/dist/mcp/tools/changeset-lifecycle.js +83 -0
- package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
- package/dist/mcp/tools/changeset-shared.js +31 -0
- package/dist/mcp/tools/changeset-shared.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +17 -1
- package/dist/mcp/tools/changeset-show.js.map +2 -2
- package/dist/mcp/tools/changeset-verify.js +43 -0
- package/dist/mcp/tools/changeset-verify.js.map +7 -0
- package/dist/mcp/tools/index.js +19 -1
- package/dist/mcp/tools/index.js.map +2 -2
- package/dist/mcp/tools/model-add.js +6 -5
- package/dist/mcp/tools/model-add.js.map +2 -2
- package/dist/mcp/tools/model-delete.js +2 -2
- package/dist/mcp/tools/model-delete.js.map +2 -2
- package/dist/mcp/tools/model-search.js +14 -0
- package/dist/mcp/tools/model-search.js.map +2 -2
- package/dist/mcp/tools/model-show.js +29 -2
- package/dist/mcp/tools/model-show.js.map +2 -2
- package/dist/mcp/tools/model-trace.js +93 -30
- package/dist/mcp/tools/model-trace.js.map +2 -2
- package/dist/mcp/tools/model-update.js +35 -19
- package/dist/mcp/tools/model-update.js.map +2 -2
- package/dist/mcp/tools/relationship-tools.js +98 -0
- package/dist/mcp/tools/relationship-tools.js.map +7 -0
- package/dist/mcp/tools/shared.js +25 -2
- package/dist/mcp/tools/shared.js.map +2 -2
- package/dist/mcp/tools/spec-type.js +90 -0
- package/dist/mcp/tools/spec-type.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1029 -1658
- package/dist/schemas/bundled/apm.json +499 -734
- package/dist/schemas/bundled/application.json +735 -371
- package/dist/schemas/bundled/base.json +3 -220
- package/dist/schemas/bundled/business.json +452 -366
- package/dist/schemas/bundled/data-model.json +395 -411
- package/dist/schemas/bundled/data-store.json +334 -558
- package/dist/schemas/bundled/manifest.json +18 -18
- package/dist/schemas/bundled/motivation.json +349 -368
- package/dist/schemas/bundled/navigation.json +415 -731
- package/dist/schemas/bundled/product.json +325 -159
- package/dist/schemas/bundled/security.json +864 -1232
- package/dist/schemas/bundled/technology.json +574 -703
- package/dist/schemas/bundled/testing.json +718 -1643
- package/dist/schemas/bundled/ux.json +739 -1352
- package/dist/server/schemas.js +8 -0
- package/dist/server/schemas.js.map +2 -2
- package/dist/server/server.js +218 -34
- package/dist/server/server.js.map +2 -2
- package/dist/server-entry.js +2 -1
- package/dist/server-entry.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/utils/ansi-suppressor.js +6 -8
- package/dist/utils/ansi-suppressor.js.map +2 -2
- package/dist/utils/attribute-patch.js +41 -0
- package/dist/utils/attribute-patch.js.map +7 -0
- package/dist/utils/error-hints.js +29 -0
- package/dist/utils/error-hints.js.map +7 -0
- package/dist/utils/errors.js +9 -3
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/project-paths.js +26 -1
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/source-reference.js +14 -12
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +2 -2
- package/dist/utils/spec-version.js.map +1 -1
- package/dist/validators/composed-reference-validator.js +5 -27
- package/dist/validators/composed-reference-validator.js.map +2 -2
- package/dist/validators/reference-validator.js +0 -7
- package/dist/validators/reference-validator.js.map +2 -2
- package/dist/validators/relationship-schema-validator.js +3 -3
- package/dist/validators/relationship-schema-validator.js.map +2 -2
- package/dist/validators/schema-validator.js +27 -9
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/types.js.map +1 -1
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
- package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +2 -1
- package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
- package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { CLIError, ErrorCategory } from "../utils/errors.js";
|
|
3
|
+
function relativeToRoot(codebaseRoot, filePath) {
|
|
4
|
+
return !filePath || !codebaseRoot || !path.isAbsolute(filePath) ? filePath : path.relative(codebaseRoot, filePath);
|
|
5
|
+
}
|
|
6
|
+
class CbmToolError extends CLIError {
|
|
7
|
+
tool;
|
|
8
|
+
detail;
|
|
9
|
+
hint;
|
|
10
|
+
constructor(tool, detail, hint) {
|
|
11
|
+
super(`codebase-memory-mcp ${tool} failed: ${detail}`, ErrorCategory.SYSTEM, hint ? [hint] : void 0), this.tool = tool, this.detail = detail, this.hint = hint, this.name = "CbmToolError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const isObj = (v) => typeof v == "object" && v !== null && !Array.isArray(v), SEARCH_PAGE = 500, QUERY_PAGE = 5e3, TRACE_PAGE = 5e3, PROJECTS_PAGE = 500, MAX_OUTPUT_TOKENS = 1e6, MAX_PAGES = 1e3;
|
|
15
|
+
function assertToolResult(tool, raw) {
|
|
16
|
+
if (typeof raw == "string")
|
|
17
|
+
throw new CbmToolError(tool, raw.trim() || "empty response");
|
|
18
|
+
if (!isObj(raw))
|
|
19
|
+
return;
|
|
20
|
+
const hint = typeof raw.hint == "string" ? raw.hint : void 0;
|
|
21
|
+
if (typeof raw.error == "string")
|
|
22
|
+
throw new CbmToolError(tool, raw.error, hint);
|
|
23
|
+
if (raw.status === "error")
|
|
24
|
+
throw new CbmToolError(tool, typeof raw.message == "string" ? raw.message : "status: error", hint);
|
|
25
|
+
}
|
|
26
|
+
function groupedQualifiedName(prefix, name) {
|
|
27
|
+
return typeof prefix == "string" && prefix !== "" ? `${prefix}.${name}` : name;
|
|
28
|
+
}
|
|
29
|
+
const ROUTE_QN_RE = /^__route__([A-Z]+)__(.*)$/;
|
|
30
|
+
function parseLines(lines) {
|
|
31
|
+
if (typeof lines != "string")
|
|
32
|
+
return {};
|
|
33
|
+
const m = lines.match(/^(\d+)-(\d+)$/);
|
|
34
|
+
return m ? { start_line: Number(m[1]), end_line: Number(m[2]) } : {};
|
|
35
|
+
}
|
|
36
|
+
function pageInfo(raw) {
|
|
37
|
+
return {
|
|
38
|
+
hasMore: raw.has_more === !0,
|
|
39
|
+
nextOffset: typeof raw.next_offset == "number" ? raw.next_offset : void 0,
|
|
40
|
+
nextCursor: typeof raw.next_cursor == "string" ? raw.next_cursor : typeof raw.next == "string" ? raw.next : void 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function normalizeSearchGraph(raw) {
|
|
44
|
+
if (!isObj(raw))
|
|
45
|
+
throw new CLIError("Invalid response from search_graph", ErrorCategory.SYSTEM, [
|
|
46
|
+
"Ensure codebase-memory-mcp is properly installed and functional"
|
|
47
|
+
]);
|
|
48
|
+
const flat = raw.results ?? raw.nodes;
|
|
49
|
+
if (Array.isArray(flat))
|
|
50
|
+
return { nodes: flat, hasMore: !1 };
|
|
51
|
+
if (!Array.isArray(raw.groups) || !Array.isArray(raw.cols))
|
|
52
|
+
throw new CLIError("Invalid response from search_graph: no results", ErrorCategory.SYSTEM, [
|
|
53
|
+
`Expected 'groups' (CBM 0.11+) or 'results'/'nodes' (CBM 0.10), got keys: ${Object.keys(raw).join(", ") || "(none)"}`,
|
|
54
|
+
"Ensure codebase-memory-mcp is properly installed and returning valid results"
|
|
55
|
+
]);
|
|
56
|
+
const cols = raw.cols, nodes = [];
|
|
57
|
+
for (const group of raw.groups) {
|
|
58
|
+
const file = typeof group.file == "string" && group.file !== "" ? group.file : void 0;
|
|
59
|
+
for (const row of group.rows ?? []) {
|
|
60
|
+
const cell = {};
|
|
61
|
+
cols.forEach((c, i) => cell[c] = row[i]);
|
|
62
|
+
const rowName = String(cell.name ?? ""), qualifiedName = groupedQualifiedName(group.qn_prefix, rowName), label = String(cell.label ?? ""), properties = {};
|
|
63
|
+
for (const [k, v] of Object.entries(cell))
|
|
64
|
+
k === "name" || k === "label" || k === "lines" || k === "in" || k === "out" || v != null && (properties[k] = v);
|
|
65
|
+
Object.assign(properties, parseLines(cell.lines)), typeof cell.in == "number" && (properties.fan_in = cell.in), typeof cell.out == "number" && (properties.fan_out = cell.out);
|
|
66
|
+
let name = rowName;
|
|
67
|
+
const route = label === "Route" ? qualifiedName.match(ROUTE_QN_RE) : null;
|
|
68
|
+
route && (name = route[2]), properties.name = name, nodes.push({ name, qualified_name: qualifiedName, label, file_path: file, properties });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { nodes, ...pageInfo(raw) };
|
|
72
|
+
}
|
|
73
|
+
function normalizeQueryGraph(raw) {
|
|
74
|
+
if (!isObj(raw))
|
|
75
|
+
throw new CLIError("Invalid response from query_graph", ErrorCategory.SYSTEM, [
|
|
76
|
+
"Ensure codebase-memory-mcp is properly installed and functional"
|
|
77
|
+
]);
|
|
78
|
+
if (Array.isArray(raw.columns) && Array.isArray(raw.rows))
|
|
79
|
+
return { columns: raw.columns, rows: raw.rows, ...pageInfo(raw) };
|
|
80
|
+
if (Array.isArray(raw.records)) {
|
|
81
|
+
const records = raw.records, columns = Array.from(new Set(records.flatMap((r) => Object.keys(r))));
|
|
82
|
+
return { columns, rows: records.map((r) => columns.map((c) => r[c])), hasMore: !1 };
|
|
83
|
+
}
|
|
84
|
+
throw new CLIError("Invalid response from query_graph: no columns/rows", ErrorCategory.SYSTEM, [
|
|
85
|
+
`Got keys: ${Object.keys(raw).join(", ") || "(none)"}`
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
function queryRecords(result) {
|
|
89
|
+
return result.rows.map((row) => {
|
|
90
|
+
const rec = {};
|
|
91
|
+
return result.columns.forEach((c, i) => rec[c] = row[i]), rec;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function cellNumber(v) {
|
|
95
|
+
const n = typeof v == "number" ? v : typeof v == "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
96
|
+
return Number.isFinite(n) ? n : void 0;
|
|
97
|
+
}
|
|
98
|
+
function cellString(v) {
|
|
99
|
+
return typeof v == "string" && v !== "" ? v : void 0;
|
|
100
|
+
}
|
|
101
|
+
function cellLabel(v) {
|
|
102
|
+
if (Array.isArray(v))
|
|
103
|
+
return typeof v[0] == "string" ? v[0] : void 0;
|
|
104
|
+
if (!(typeof v != "string" || v === "")) {
|
|
105
|
+
if (v.startsWith("["))
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(v);
|
|
108
|
+
return Array.isArray(parsed) && typeof parsed[0] == "string" ? parsed[0] : void 0;
|
|
109
|
+
} catch {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
return v;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function nodeColumns(v) {
|
|
116
|
+
return `${v}.name AS name, ${v}.qualified_name AS qualified_name, ${v}.file_path AS file_path, ${v}.start_line AS start_line, ${v}.end_line AS end_line, labels(${v}) AS label`;
|
|
117
|
+
}
|
|
118
|
+
function nodesFromQuery(result) {
|
|
119
|
+
return queryRecords(result).flatMap((r) => {
|
|
120
|
+
if (result.columns.length === 1 && isObj(r[result.columns[0]]))
|
|
121
|
+
return [r[result.columns[0]]];
|
|
122
|
+
const name = cellString(r.name);
|
|
123
|
+
if (!name)
|
|
124
|
+
return [];
|
|
125
|
+
const properties = { name }, start = cellNumber(r.start_line), end = cellNumber(r.end_line);
|
|
126
|
+
return start !== void 0 && (properties.start_line = start), end !== void 0 && (properties.end_line = end), [
|
|
127
|
+
{
|
|
128
|
+
name,
|
|
129
|
+
qualified_name: cellString(r.qualified_name),
|
|
130
|
+
label: cellLabel(r.label) ?? "",
|
|
131
|
+
file_path: cellString(r.file_path),
|
|
132
|
+
properties
|
|
133
|
+
}
|
|
134
|
+
];
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function normalizeTracePath(raw, direction) {
|
|
138
|
+
if (!isObj(raw))
|
|
139
|
+
throw new CLIError("Invalid response from trace_path", ErrorCategory.SYSTEM, [
|
|
140
|
+
"Ensure codebase-memory-mcp is properly installed and functional"
|
|
141
|
+
]);
|
|
142
|
+
if (Array.isArray(raw.nodes))
|
|
143
|
+
return {
|
|
144
|
+
nodes: raw.nodes,
|
|
145
|
+
edges: Array.isArray(raw.edges) ? raw.edges : [],
|
|
146
|
+
shape: "flat",
|
|
147
|
+
hasMore: !1
|
|
148
|
+
};
|
|
149
|
+
const section = raw[direction === "inbound" ? "callers" : "callees"];
|
|
150
|
+
if (!isObj(section) || !Array.isArray(section.groups))
|
|
151
|
+
return null;
|
|
152
|
+
const cols = Array.isArray(section.cols) ? section.cols : ["name", "hop"], nameIdx = cols.indexOf("name"), hopIdx = cols.indexOf("hop"), nodes = [];
|
|
153
|
+
for (const group of section.groups)
|
|
154
|
+
for (const row of group.rows ?? []) {
|
|
155
|
+
const name = String(row[nameIdx >= 0 ? nameIdx : 0] ?? ""), hop = hopIdx >= 0 ? cellNumber(row[hopIdx]) : void 0, qualifiedName = groupedQualifiedName(group.qn_prefix, name);
|
|
156
|
+
nodes.push({
|
|
157
|
+
id: qualifiedName,
|
|
158
|
+
qualified_name: qualifiedName,
|
|
159
|
+
name,
|
|
160
|
+
source_symbol: name,
|
|
161
|
+
depth: hop ?? 1
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
const sectionPage = pageInfo(section), topPage = pageInfo(raw);
|
|
165
|
+
return {
|
|
166
|
+
nodes,
|
|
167
|
+
edges: [],
|
|
168
|
+
shape: "grouped",
|
|
169
|
+
hasMore: topPage.hasMore || sectionPage.hasMore,
|
|
170
|
+
nextCursor: topPage.nextCursor ?? sectionPage.nextCursor
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function normalizeListProjects(raw) {
|
|
174
|
+
if (!isObj(raw))
|
|
175
|
+
throw new CLIError("Invalid response from list_projects", ErrorCategory.SYSTEM, [
|
|
176
|
+
"Ensure codebase-memory-mcp is properly installed and functional"
|
|
177
|
+
]);
|
|
178
|
+
return { projects: Array.isArray(raw.projects) ? raw.projects : [], ...pageInfo(raw) };
|
|
179
|
+
}
|
|
180
|
+
function attachRouteHandlers(nodes, handlers) {
|
|
181
|
+
const byRoute = /* @__PURE__ */ new Map();
|
|
182
|
+
for (const h of handlers) {
|
|
183
|
+
const list = byRoute.get(h.route_qualified_name) ?? [];
|
|
184
|
+
list.some((x) => x.qualified_name === h.qualified_name) || list.push(h), byRoute.set(h.route_qualified_name, list);
|
|
185
|
+
}
|
|
186
|
+
return nodes.flatMap((node) => {
|
|
187
|
+
const list = byRoute.get(String(node.qualified_name ?? "")) ?? [];
|
|
188
|
+
if (list.length === 0)
|
|
189
|
+
return [node];
|
|
190
|
+
const shared = list.length > 1;
|
|
191
|
+
return list.map((h) => {
|
|
192
|
+
const properties = { ...node.properties ?? {} };
|
|
193
|
+
properties.handler_name ??= h.qualified_name, properties.symbol ??= h.name, h.start_line !== void 0 && (properties.start_line ??= h.start_line), h.end_line !== void 0 && (properties.end_line ??= h.end_line);
|
|
194
|
+
const file = shared ? h.file_path || node.file_path : node.file_path || h.file_path;
|
|
195
|
+
return { ...node, file_path: file, properties };
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const ROUTE_HANDLERS_QUERY = "MATCH (f)-[:HANDLES]->(r:Route) RETURN r.qualified_name AS route_qualified_name, f.qualified_name AS qualified_name, f.name AS name, f.file_path AS file_path, f.start_line AS start_line, f.end_line AS end_line";
|
|
200
|
+
function routeHandlersFromQuery(result) {
|
|
201
|
+
return queryRecords(result).flatMap((r) => {
|
|
202
|
+
const route = cellString(r.route_qualified_name), qn = cellString(r.qualified_name);
|
|
203
|
+
return !route || !qn ? [] : [
|
|
204
|
+
{
|
|
205
|
+
route_qualified_name: route,
|
|
206
|
+
qualified_name: qn,
|
|
207
|
+
name: cellString(r.name) ?? qn.slice(qn.lastIndexOf(".") + 1),
|
|
208
|
+
file_path: cellString(r.file_path),
|
|
209
|
+
start_line: cellNumber(r.start_line),
|
|
210
|
+
end_line: cellNumber(r.end_line)
|
|
211
|
+
}
|
|
212
|
+
];
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
class CbmTools {
|
|
216
|
+
client;
|
|
217
|
+
constructor(client) {
|
|
218
|
+
this.client = client;
|
|
219
|
+
}
|
|
220
|
+
/** One JSON tool call, with error payloads raised as {@link CbmToolError}. */
|
|
221
|
+
async call(tool, args = {}) {
|
|
222
|
+
const raw = await this.client.invokeTool(tool, { ...args, format: "json" });
|
|
223
|
+
return assertToolResult(tool, raw), raw;
|
|
224
|
+
}
|
|
225
|
+
async listProjects() {
|
|
226
|
+
const projects = [];
|
|
227
|
+
let offset = 0;
|
|
228
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
229
|
+
const res = normalizeListProjects(await this.call("list_projects", { limit: PROJECTS_PAGE, offset }));
|
|
230
|
+
projects.push(...res.projects);
|
|
231
|
+
const next = res.nextOffset ?? offset + res.projects.length;
|
|
232
|
+
if (!res.hasMore || next <= offset)
|
|
233
|
+
break;
|
|
234
|
+
offset = next;
|
|
235
|
+
}
|
|
236
|
+
return projects;
|
|
237
|
+
}
|
|
238
|
+
/** search_graph across every page. */
|
|
239
|
+
async searchGraph(args) {
|
|
240
|
+
const nodes = [];
|
|
241
|
+
let offset = 0;
|
|
242
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
243
|
+
const res = normalizeSearchGraph(await this.call("search_graph", {
|
|
244
|
+
...args,
|
|
245
|
+
limit: SEARCH_PAGE,
|
|
246
|
+
offset,
|
|
247
|
+
max_output_tokens: MAX_OUTPUT_TOKENS
|
|
248
|
+
}));
|
|
249
|
+
if (nodes.push(...res.nodes), !res.hasMore || res.nextOffset === void 0 || res.nextOffset <= offset)
|
|
250
|
+
break;
|
|
251
|
+
offset = res.nextOffset;
|
|
252
|
+
}
|
|
253
|
+
return nodes;
|
|
254
|
+
}
|
|
255
|
+
/** query_graph across every page (cursor continuation, offset as fallback). */
|
|
256
|
+
async queryGraph(query, project) {
|
|
257
|
+
const base = { query, project, max_rows: QUERY_PAGE, max_output_tokens: MAX_OUTPUT_TOKENS };
|
|
258
|
+
let res = normalizeQueryGraph(await this.call("query_graph", base));
|
|
259
|
+
const columns = res.columns, rows = [...res.rows];
|
|
260
|
+
let offset = 0;
|
|
261
|
+
for (let page = 1; page < MAX_PAGES && res.hasMore; page++) {
|
|
262
|
+
if (res.nextCursor)
|
|
263
|
+
res = normalizeQueryGraph(await this.call("query_graph", { ...base, cursor: res.nextCursor }));
|
|
264
|
+
else if (res.nextOffset !== void 0 && res.nextOffset > offset)
|
|
265
|
+
offset = res.nextOffset, res = normalizeQueryGraph(await this.call("query_graph", { ...base, offset }));
|
|
266
|
+
else
|
|
267
|
+
break;
|
|
268
|
+
rows.push(...res.rows);
|
|
269
|
+
}
|
|
270
|
+
return { columns, rows };
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* trace_path across every page. Depth is raised to CBM's minimum of 1. Returns null when
|
|
274
|
+
* the backend's response holds no call graph (see {@link normalizeTracePath}).
|
|
275
|
+
*/
|
|
276
|
+
async tracePath(args) {
|
|
277
|
+
const base = {
|
|
278
|
+
...args,
|
|
279
|
+
depth: Math.max(1, args.depth),
|
|
280
|
+
limit: TRACE_PAGE,
|
|
281
|
+
max_output_tokens: MAX_OUTPUT_TOKENS
|
|
282
|
+
};
|
|
283
|
+
let res = normalizeTracePath(await this.call("trace_path", base), args.direction);
|
|
284
|
+
if (!res)
|
|
285
|
+
return null;
|
|
286
|
+
const { shape } = res, nodes = [...res.nodes], edges = [...res.edges];
|
|
287
|
+
for (let page = 1; page < MAX_PAGES && res.hasMore && res.nextCursor && (res = normalizeTracePath(await this.call("trace_path", { ...base, cursor: res.nextCursor }), args.direction), !!res); page++)
|
|
288
|
+
nodes.push(...res.nodes), edges.push(...res.edges);
|
|
289
|
+
return { nodes, edges, shape };
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* File path of each qualified name, for trace_path results (which carry none since 0.11).
|
|
293
|
+
* Names containing quotes or backslashes are skipped rather than escaped.
|
|
294
|
+
*/
|
|
295
|
+
async filePathsOf(qualifiedNames, project) {
|
|
296
|
+
const files = /* @__PURE__ */ new Map(), safe = Array.from(new Set(qualifiedNames)).filter((q) => q && !/["'\\]/.test(q));
|
|
297
|
+
for (let i = 0; i < safe.length; i += 200) {
|
|
298
|
+
const list = safe.slice(i, i + 200).map((q) => `"${q}"`).join(", "), result = await this.queryGraph(`MATCH (n) WHERE n.qualified_name IN [${list}] RETURN n.qualified_name AS qualified_name, n.file_path AS file_path`, project);
|
|
299
|
+
for (const r of queryRecords(result)) {
|
|
300
|
+
const qn = cellString(r.qualified_name), file = cellString(r.file_path);
|
|
301
|
+
qn && file && !files.has(qn) && files.set(qn, file);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return files;
|
|
305
|
+
}
|
|
306
|
+
/** index_repository; returns the node/edge counts when CBM reports them. */
|
|
307
|
+
async indexRepository(repoPath) {
|
|
308
|
+
const raw = await this.call("index_repository", { repo_path: repoPath });
|
|
309
|
+
if (!isObj(raw))
|
|
310
|
+
throw new CLIError("Invalid response from index_repository", ErrorCategory.SYSTEM, [
|
|
311
|
+
"Ensure codebase-memory-mcp is properly installed and functional"
|
|
312
|
+
]);
|
|
313
|
+
return raw;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
export {
|
|
317
|
+
CbmToolError,
|
|
318
|
+
CbmTools,
|
|
319
|
+
ROUTE_HANDLERS_QUERY,
|
|
320
|
+
assertToolResult,
|
|
321
|
+
attachRouteHandlers,
|
|
322
|
+
cellLabel,
|
|
323
|
+
cellNumber,
|
|
324
|
+
cellString,
|
|
325
|
+
groupedQualifiedName,
|
|
326
|
+
nodeColumns,
|
|
327
|
+
nodesFromQuery,
|
|
328
|
+
normalizeListProjects,
|
|
329
|
+
normalizeQueryGraph,
|
|
330
|
+
normalizeSearchGraph,
|
|
331
|
+
normalizeTracePath,
|
|
332
|
+
queryRecords,
|
|
333
|
+
relativeToRoot,
|
|
334
|
+
routeHandlersFromQuery
|
|
335
|
+
};
|
|
336
|
+
//# sourceMappingURL=cbm-responses.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/analyzers/cbm-responses.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * codebase-memory-mcp (CBM) response contract\n *\n * Every CBM tool call made by the analyzer goes through {@link CbmTools}. It asks for JSON\n * (`format: \"json\"`; CBM 0.11+ answers in text otherwise), follows the result pages, turns\n * error payloads into errors, and normalises each tool's JSON into the shapes the analyzer\n * works with. Two response generations are accepted:\n *\n * - CBM 0.11 / 0.12 (grouped, paged):\n * - search_graph: `{ cols, groups: [{ qn_prefix, file, rows }], has_more, next_offset }`\n * - query_graph: `{ columns, rows, has_more, next_cursor, next_offset }`\n * - trace_path: `{ callers|callees: { cols: [name, hop], groups: [{ qn_prefix, rows }] }, next_cursor }`\n * - list_projects: `{ projects: [{ name, root_path }], has_more }`\n * - CBM 0.10 and earlier (flat): search_graph `{ results | nodes: [...] }`, trace_path\n * `{ nodes, edges }`, query_graph `{ columns, rows }` (or `{ records }`), list_projects\n * `{ projects }`. These pass through unchanged and are never paged.\n *\n * A grouped row's qualified name is `qn_prefix ? qn_prefix + \".\" + name : name` (the\n * response's own `qn_rule`); the row's `name` is that qualified-name suffix, which is the\n * symbol name for code nodes but only a fragment for Route nodes, whose path lives in the\n * qualified name (`__route__GET__/vets.html`).\n */\n\nimport * as path from \"path\";\nimport { CLIError, ErrorCategory } from \"../utils/errors.js\";\n\n/**\n * A CBM file path relative to the codebase root. CBM stores repository-relative paths\n * (0.10 and 0.12 alike); absolute ones are relativised, relative ones kept as they are.\n * `path.relative` alone would resolve a relative path against the process cwd.\n */\nexport function relativeToRoot(codebaseRoot: string | undefined, filePath: string): string {\n if (!filePath || !codebaseRoot || !path.isAbsolute(filePath)) return filePath;\n return path.relative(codebaseRoot, filePath);\n}\n\n/** Graph node in the shape the analyzer consumes (same as CBM 0.10's flat search results). */\nexport interface CbmGraphNode {\n id?: string;\n name?: string;\n qualified_name?: string;\n label: string;\n file_path?: string;\n properties?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface CbmProject {\n name: string;\n root_path: string;\n [key: string]: unknown;\n}\n\nexport interface QueryGraphResult {\n columns: string[];\n rows: unknown[][];\n}\n\n/** Call-graph node from trace_path; `depth` is the hop count from the traced symbol. */\nexport interface TraceNode {\n id?: string;\n qualified_name?: string;\n name?: string;\n file_path?: string;\n source_symbol?: string;\n depth?: number;\n [key: string]: unknown;\n}\n\nexport interface TraceEdge {\n from_node: string;\n to_node: string;\n type?: string;\n [key: string]: unknown;\n}\n\nexport interface TraceResult {\n nodes: TraceNode[];\n edges: TraceEdge[];\n /** \"grouped\" for CBM 0.11+ (no per-edge types, no file paths), \"flat\" for 0.10. */\n shape: \"grouped\" | \"flat\";\n}\n\n/** Handler of a Route, from a `(f)-[:HANDLES]->(r:Route)` query. */\nexport interface RouteHandler {\n route_qualified_name: string;\n qualified_name: string;\n name: string;\n file_path?: string;\n start_line?: number;\n end_line?: number;\n}\n\n/** Minimal client surface; StdioClient satisfies it. */\nexport interface CbmToolInvoker {\n invokeTool(name: string, args?: unknown, options?: { timeout?: number }): Promise<unknown>;\n}\n\n/** CBM answered a tool call with an error (bad Cypher, unknown symbol, failed index, ...). */\nexport class CbmToolError extends CLIError {\n constructor(\n public readonly tool: string,\n public readonly detail: string,\n public readonly hint?: string\n ) {\n super(\n `codebase-memory-mcp ${tool} failed: ${detail}`,\n ErrorCategory.SYSTEM,\n hint ? [hint] : undefined\n );\n this.name = \"CbmToolError\";\n }\n}\n\ntype Obj = Record<string, unknown>;\n\nconst isObj = (v: unknown): v is Obj => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Page sizes: the largest each tool accepts, so most results arrive in one page. */\nconst SEARCH_PAGE = 500;\nconst QUERY_PAGE = 5000;\nconst TRACE_PAGE = 5000;\nconst PROJECTS_PAGE = 500;\n/** Lifts CBM's default 3,200-token output budget, which otherwise truncates a page early. */\nconst MAX_OUTPUT_TOKENS = 1_000_000;\n/** Guard against a server that keeps reporting has_more without advancing. */\nconst MAX_PAGES = 1000;\n\n/**\n * Throw if a tool result is an error payload. Since we always ask for JSON, a bare string is\n * an error message (CBM 0.12 sends Cypher errors as text); objects carry `error` or\n * `status: \"error\"` with an optional `hint`.\n */\nexport function assertToolResult(tool: string, raw: unknown): void {\n if (typeof raw === \"string\") {\n throw new CbmToolError(tool, raw.trim() || \"empty response\");\n }\n if (!isObj(raw)) return;\n const hint = typeof raw.hint === \"string\" ? raw.hint : undefined;\n if (typeof raw.error === \"string\") {\n throw new CbmToolError(tool, raw.error, hint);\n }\n if (raw.status === \"error\") {\n throw new CbmToolError(tool, typeof raw.message === \"string\" ? raw.message : \"status: error\", hint);\n }\n}\n\n/** Qualified name from a grouped row (the response's qn_rule). */\nexport function groupedQualifiedName(prefix: unknown, name: string): string {\n return typeof prefix === \"string\" && prefix !== \"\" ? `${prefix}.${name}` : name;\n}\n\nconst ROUTE_QN_RE = /^__route__([A-Z]+)__(.*)$/;\n\n/** Parse \"12-30\" into start/end lines. */\nfunction parseLines(lines: unknown): { start_line?: number; end_line?: number } {\n if (typeof lines !== \"string\") return {};\n const m = lines.match(/^(\\d+)-(\\d+)$/);\n return m ? { start_line: Number(m[1]), end_line: Number(m[2]) } : {};\n}\n\n/** Paging cursor fields shared by the grouped tools. */\ninterface PageInfo {\n hasMore: boolean;\n nextOffset?: number;\n nextCursor?: string;\n}\n\nfunction pageInfo(raw: Obj): PageInfo {\n return {\n hasMore: raw.has_more === true,\n nextOffset: typeof raw.next_offset === \"number\" ? raw.next_offset : undefined,\n nextCursor:\n typeof raw.next_cursor === \"string\"\n ? raw.next_cursor\n : typeof raw.next === \"string\"\n ? raw.next\n : undefined,\n };\n}\n\n/**\n * Normalise one search_graph page into analyzer nodes.\n *\n * Grouped rows become `{ name, qualified_name, label, file_path, properties }` where\n * properties carry `name`, `start_line`/`end_line` (from `lines`), `fan_in`/`fan_out`\n * (from `in`/`out`) and any extra `fields` columns. Route nodes get the path from their\n * qualified name as `name` and the method as `properties.method` when absent, matching\n * how CBM stores them (name `/vets`, qualified name `__route__GET__/vets`).\n */\nexport function normalizeSearchGraph(raw: unknown): { nodes: CbmGraphNode[] } & PageInfo {\n if (!isObj(raw)) {\n throw new CLIError(\"Invalid response from search_graph\", ErrorCategory.SYSTEM, [\n \"Ensure codebase-memory-mcp is properly installed and functional\",\n ]);\n }\n\n // CBM 0.10 and earlier: flat node arrays.\n const flat = raw.results ?? raw.nodes;\n if (Array.isArray(flat)) {\n return { nodes: flat as CbmGraphNode[], hasMore: false };\n }\n\n if (!Array.isArray(raw.groups) || !Array.isArray(raw.cols)) {\n throw new CLIError(\"Invalid response from search_graph: no results\", ErrorCategory.SYSTEM, [\n `Expected 'groups' (CBM 0.11+) or 'results'/'nodes' (CBM 0.10), got keys: ${Object.keys(raw).join(\", \") || \"(none)\"}`,\n \"Ensure codebase-memory-mcp is properly installed and returning valid results\",\n ]);\n }\n\n const cols = raw.cols as string[];\n const nodes: CbmGraphNode[] = [];\n for (const group of raw.groups as Obj[]) {\n const file = typeof group.file === \"string\" && group.file !== \"\" ? group.file : undefined;\n for (const row of (group.rows ?? []) as unknown[][]) {\n const cell: Obj = {};\n cols.forEach((c, i) => (cell[c] = row[i]));\n const rowName = String(cell.name ?? \"\");\n const qualifiedName = groupedQualifiedName(group.qn_prefix, rowName);\n const label = String(cell.label ?? \"\");\n\n const properties: Obj = {};\n for (const [k, v] of Object.entries(cell)) {\n if (k === \"name\" || k === \"label\" || k === \"lines\" || k === \"in\" || k === \"out\") continue;\n if (v !== null && v !== undefined) properties[k] = v;\n }\n Object.assign(properties, parseLines(cell.lines));\n if (typeof cell.in === \"number\") properties.fan_in = cell.in;\n if (typeof cell.out === \"number\") properties.fan_out = cell.out;\n\n let name = rowName;\n const route = label === \"Route\" ? qualifiedName.match(ROUTE_QN_RE) : null;\n // The method stays in the qualified name, where the analyzer reads it\n if (route) name = route[2];\n properties.name = name;\n\n nodes.push({ name, qualified_name: qualifiedName, label, file_path: file, properties });\n }\n }\n return { nodes, ...pageInfo(raw) };\n}\n\n/** Normalise one query_graph page. Accepts `{ columns, rows }` and the legacy `{ records }`. */\nexport function normalizeQueryGraph(raw: unknown): QueryGraphResult & PageInfo {\n if (!isObj(raw)) {\n throw new CLIError(\"Invalid response from query_graph\", ErrorCategory.SYSTEM, [\n \"Ensure codebase-memory-mcp is properly installed and functional\",\n ]);\n }\n if (Array.isArray(raw.columns) && Array.isArray(raw.rows)) {\n return { columns: raw.columns as string[], rows: raw.rows as unknown[][], ...pageInfo(raw) };\n }\n if (Array.isArray(raw.records)) {\n const records = raw.records as Obj[];\n const columns = Array.from(new Set(records.flatMap((r) => Object.keys(r))));\n return { columns, rows: records.map((r) => columns.map((c) => r[c])), hasMore: false };\n }\n throw new CLIError(\"Invalid response from query_graph: no columns/rows\", ErrorCategory.SYSTEM, [\n `Got keys: ${Object.keys(raw).join(\", \") || \"(none)\"}`,\n ]);\n}\n\n/** query_graph rows as objects keyed by column name (use `AS` aliases for clean keys). */\nexport function queryRecords(result: QueryGraphResult): Array<Record<string, unknown>> {\n return result.rows.map((row) => {\n const rec: Record<string, unknown> = {};\n result.columns.forEach((c, i) => (rec[c] = row[i]));\n return rec;\n });\n}\n\n/** Number from a query_graph cell (CBM 0.12 returns integers as strings). */\nexport function cellNumber(v: unknown): number | undefined {\n const n = typeof v === \"number\" ? v : typeof v === \"string\" && v.trim() !== \"\" ? Number(v) : NaN;\n return Number.isFinite(n) ? n : undefined;\n}\n\n/** String from a query_graph cell; empty strings and nulls become undefined. */\nexport function cellString(v: unknown): string | undefined {\n return typeof v === \"string\" && v !== \"\" ? v : undefined;\n}\n\n/** First label from a `labels(n)` cell (CBM 0.12 returns it as a JSON string `[\"Route\"]`). */\nexport function cellLabel(v: unknown): string | undefined {\n if (Array.isArray(v)) return typeof v[0] === \"string\" ? v[0] : undefined;\n if (typeof v !== \"string\" || v === \"\") return undefined;\n if (v.startsWith(\"[\")) {\n try {\n const parsed = JSON.parse(v) as unknown;\n return Array.isArray(parsed) && typeof parsed[0] === \"string\" ? parsed[0] : undefined;\n } catch {\n return undefined;\n }\n }\n return v;\n}\n\n/**\n * RETURN clause yielding the columns {@link nodesFromQuery} reads, for node variable `v`.\n * CBM 0.12 returns only the name for a bare `RETURN n`, so node queries spell the columns out.\n */\nexport function nodeColumns(v: string): string {\n return (\n `${v}.name AS name, ${v}.qualified_name AS qualified_name, ${v}.file_path AS file_path, ` +\n `${v}.start_line AS start_line, ${v}.end_line AS end_line, labels(${v}) AS label`\n );\n}\n\n/**\n * Graph nodes from a query_graph result whose columns come from {@link nodeColumns}.\n * A legacy `{ records: [{ n: {...node} }] }` result (one node object per record) is\n * passed through.\n */\nexport function nodesFromQuery(result: QueryGraphResult): CbmGraphNode[] {\n return queryRecords(result).flatMap((r) => {\n if (result.columns.length === 1 && isObj(r[result.columns[0]])) {\n return [r[result.columns[0]] as CbmGraphNode];\n }\n const name = cellString(r.name);\n if (!name) return [];\n const properties: Obj = { name };\n const start = cellNumber(r.start_line);\n const end = cellNumber(r.end_line);\n if (start !== undefined) properties.start_line = start;\n if (end !== undefined) properties.end_line = end;\n return [\n {\n name,\n qualified_name: cellString(r.qualified_name),\n label: cellLabel(r.label) ?? \"\",\n file_path: cellString(r.file_path),\n properties,\n },\n ];\n });\n}\n\n/**\n * Normalise one trace_path page into call-graph nodes.\n *\n * Returns null when the response holds no call graph in either shape, which is how a\n * backend without trace_path support answers; callers fall back to a Cypher CALLS query.\n */\nexport function normalizeTracePath(\n raw: unknown,\n direction: \"inbound\" | \"outbound\"\n): (TraceResult & PageInfo) | null {\n if (!isObj(raw)) {\n throw new CLIError(\"Invalid response from trace_path\", ErrorCategory.SYSTEM, [\n \"Ensure codebase-memory-mcp is properly installed and functional\",\n ]);\n }\n if (Array.isArray(raw.nodes)) {\n return {\n nodes: raw.nodes as TraceNode[],\n edges: Array.isArray(raw.edges) ? (raw.edges as TraceEdge[]) : [],\n shape: \"flat\",\n hasMore: false,\n };\n }\n\n const section = raw[direction === \"inbound\" ? \"callers\" : \"callees\"];\n if (!isObj(section) || !Array.isArray(section.groups)) return null;\n const cols = Array.isArray(section.cols) ? (section.cols as string[]) : [\"name\", \"hop\"];\n const nameIdx = cols.indexOf(\"name\");\n const hopIdx = cols.indexOf(\"hop\");\n const nodes: TraceNode[] = [];\n for (const group of section.groups as Obj[]) {\n for (const row of (group.rows ?? []) as unknown[][]) {\n const name = String(row[nameIdx >= 0 ? nameIdx : 0] ?? \"\");\n const hop = hopIdx >= 0 ? cellNumber(row[hopIdx]) : undefined;\n const qualifiedName = groupedQualifiedName(group.qn_prefix, name);\n nodes.push({\n id: qualifiedName,\n qualified_name: qualifiedName,\n name,\n source_symbol: name,\n depth: hop ?? 1,\n });\n }\n }\n const sectionPage = pageInfo(section);\n const topPage = pageInfo(raw);\n return {\n nodes,\n edges: [],\n shape: \"grouped\",\n hasMore: topPage.hasMore || sectionPage.hasMore,\n nextCursor: topPage.nextCursor ?? sectionPage.nextCursor,\n };\n}\n\n/** Normalise one list_projects page. */\nexport function normalizeListProjects(raw: unknown): { projects: CbmProject[] } & PageInfo {\n if (!isObj(raw)) {\n throw new CLIError(\"Invalid response from list_projects\", ErrorCategory.SYSTEM, [\n \"Ensure codebase-memory-mcp is properly installed and functional\",\n ]);\n }\n // No projects array means nothing is indexed yet.\n const projects = Array.isArray(raw.projects) ? (raw.projects as CbmProject[]) : [];\n return { projects, ...pageInfo(raw) };\n}\n\n/**\n * Attach HANDLES-edge handlers to Route nodes.\n *\n * CBM Route nodes carry only `{ method, source }`; the handler, its symbol, its lines and\n * often the file come from the function that HANDLES the route. CBM keys a Route by method\n * and path, so routers mounted under different prefixes can share one Route node with\n * several handlers (fastapi's `GET /{}` is handled by both `users.read_user_by_id` and\n * `items.read_item`): such a route becomes one node per handler, each with the handler's\n * file. A route with a single handler keeps its own values where it has them.\n */\nexport function attachRouteHandlers(nodes: CbmGraphNode[], handlers: RouteHandler[]): CbmGraphNode[] {\n const byRoute = new Map<string, RouteHandler[]>();\n for (const h of handlers) {\n const list = byRoute.get(h.route_qualified_name) ?? [];\n if (!list.some((x) => x.qualified_name === h.qualified_name)) list.push(h);\n byRoute.set(h.route_qualified_name, list);\n }\n return nodes.flatMap((node) => {\n const list = byRoute.get(String(node.qualified_name ?? \"\")) ?? [];\n if (list.length === 0) return [node];\n const shared = list.length > 1;\n return list.map((h) => {\n const properties: Obj = { ...(node.properties ?? {}) };\n properties.handler_name ??= h.qualified_name;\n properties.symbol ??= h.name;\n if (h.start_line !== undefined) properties.start_line ??= h.start_line;\n if (h.end_line !== undefined) properties.end_line ??= h.end_line;\n const file = shared ? h.file_path || node.file_path : node.file_path || h.file_path;\n return { ...node, file_path: file, properties };\n });\n });\n}\n\n/** Cypher returning every Route handler as {@link RouteHandler} columns. */\nexport const ROUTE_HANDLERS_QUERY =\n \"MATCH (f)-[:HANDLES]->(r:Route) RETURN r.qualified_name AS route_qualified_name, \" +\n \"f.qualified_name AS qualified_name, f.name AS name, f.file_path AS file_path, \" +\n \"f.start_line AS start_line, f.end_line AS end_line\";\n\nexport function routeHandlersFromQuery(result: QueryGraphResult): RouteHandler[] {\n return queryRecords(result).flatMap((r) => {\n const route = cellString(r.route_qualified_name);\n const qn = cellString(r.qualified_name);\n if (!route || !qn) return [];\n return [\n {\n route_qualified_name: route,\n qualified_name: qn,\n name: cellString(r.name) ?? qn.slice(qn.lastIndexOf(\".\") + 1),\n file_path: cellString(r.file_path),\n start_line: cellNumber(r.start_line),\n end_line: cellNumber(r.end_line),\n },\n ];\n });\n}\n\n/**\n * Paged, normalised access to the CBM tools.\n *\n * All calls add `format: \"json\"`; CBM 0.10 ignores the argument.\n */\nexport class CbmTools {\n constructor(private readonly client: CbmToolInvoker) {}\n\n /** One JSON tool call, with error payloads raised as {@link CbmToolError}. */\n async call(tool: string, args: Record<string, unknown> = {}): Promise<unknown> {\n const raw = await this.client.invokeTool(tool, { ...args, format: \"json\" });\n assertToolResult(tool, raw);\n return raw;\n }\n\n async listProjects(): Promise<CbmProject[]> {\n const projects: CbmProject[] = [];\n let offset = 0;\n for (let page = 0; page < MAX_PAGES; page++) {\n const res = normalizeListProjects(\n await this.call(\"list_projects\", { limit: PROJECTS_PAGE, offset })\n );\n projects.push(...res.projects);\n const next = res.nextOffset ?? offset + res.projects.length;\n if (!res.hasMore || next <= offset) break;\n offset = next;\n }\n return projects;\n }\n\n /** search_graph across every page. */\n async searchGraph(args: Record<string, unknown>): Promise<CbmGraphNode[]> {\n const nodes: CbmGraphNode[] = [];\n let offset = 0;\n for (let page = 0; page < MAX_PAGES; page++) {\n const res = normalizeSearchGraph(\n await this.call(\"search_graph\", {\n ...args,\n limit: SEARCH_PAGE,\n offset,\n max_output_tokens: MAX_OUTPUT_TOKENS,\n })\n );\n nodes.push(...res.nodes);\n if (!res.hasMore || res.nextOffset === undefined || res.nextOffset <= offset) break;\n offset = res.nextOffset;\n }\n return nodes;\n }\n\n /** query_graph across every page (cursor continuation, offset as fallback). */\n async queryGraph(query: string, project: string): Promise<QueryGraphResult> {\n const base = { query, project, max_rows: QUERY_PAGE, max_output_tokens: MAX_OUTPUT_TOKENS };\n let res = normalizeQueryGraph(await this.call(\"query_graph\", base));\n const columns = res.columns;\n const rows = [...res.rows];\n let offset = 0;\n for (let page = 1; page < MAX_PAGES && res.hasMore; page++) {\n if (res.nextCursor) {\n res = normalizeQueryGraph(await this.call(\"query_graph\", { ...base, cursor: res.nextCursor }));\n } else if (res.nextOffset !== undefined && res.nextOffset > offset) {\n offset = res.nextOffset;\n res = normalizeQueryGraph(await this.call(\"query_graph\", { ...base, offset }));\n } else {\n break;\n }\n rows.push(...res.rows);\n }\n return { columns, rows };\n }\n\n /**\n * trace_path across every page. Depth is raised to CBM's minimum of 1. Returns null when\n * the backend's response holds no call graph (see {@link normalizeTracePath}).\n */\n async tracePath(args: {\n function_name: string;\n direction: \"inbound\" | \"outbound\";\n depth: number;\n project: string;\n }): Promise<TraceResult | null> {\n const base = {\n ...args,\n depth: Math.max(1, args.depth),\n limit: TRACE_PAGE,\n max_output_tokens: MAX_OUTPUT_TOKENS,\n };\n let res = normalizeTracePath(await this.call(\"trace_path\", base), args.direction);\n if (!res) return null;\n const { shape } = res;\n const nodes = [...res.nodes];\n const edges = [...res.edges];\n for (let page = 1; page < MAX_PAGES && res.hasMore && res.nextCursor; page++) {\n res = normalizeTracePath(\n await this.call(\"trace_path\", { ...base, cursor: res.nextCursor }),\n args.direction\n );\n if (!res) break;\n nodes.push(...res.nodes);\n edges.push(...res.edges);\n }\n return { nodes, edges, shape };\n }\n\n /**\n * File path of each qualified name, for trace_path results (which carry none since 0.11).\n * Names containing quotes or backslashes are skipped rather than escaped.\n */\n async filePathsOf(qualifiedNames: string[], project: string): Promise<Map<string, string>> {\n const files = new Map<string, string>();\n const safe = Array.from(new Set(qualifiedNames)).filter((q) => q && !/[\"'\\\\]/.test(q));\n for (let i = 0; i < safe.length; i += 200) {\n const list = safe.slice(i, i + 200).map((q) => `\"${q}\"`).join(\", \");\n const result = await this.queryGraph(\n `MATCH (n) WHERE n.qualified_name IN [${list}] RETURN n.qualified_name AS qualified_name, n.file_path AS file_path`,\n project\n );\n for (const r of queryRecords(result)) {\n const qn = cellString(r.qualified_name);\n const file = cellString(r.file_path);\n if (qn && file && !files.has(qn)) files.set(qn, file);\n }\n }\n return files;\n }\n\n /** index_repository; returns the node/edge counts when CBM reports them. */\n async indexRepository(repoPath: string): Promise<{ nodes?: number; edges?: number; [k: string]: unknown }> {\n const raw = await this.call(\"index_repository\", { repo_path: repoPath });\n if (!isObj(raw)) {\n throw new CLIError(\"Invalid response from index_repository\", ErrorCategory.SYSTEM, [\n \"Ensure codebase-memory-mcp is properly installed and functional\",\n ]);\n }\n return raw;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAuBA,YAAY,UAAU;AACtB,SAAS,UAAU,qBAAqB;AAOlC,SAAU,eAAe,cAAkC,UAAgB;AAC/E,SAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,KAAK,WAAW,QAAQ,IAAU,WAC9D,KAAK,SAAS,cAAc,QAAQ;AAC7C;AAiEM,MAAO,qBAAqB,SAAQ;EAEtB;EACA;EACA;EAHlB,YACkB,MACA,QACA,MAAa;AAE7B,UACE,uBAAuB,IAAI,YAAY,MAAM,IAC7C,cAAc,QACd,OAAO,CAAC,IAAI,IAAI,MAAS,GAPX,KAAA,OAAA,MACA,KAAA,SAAA,QACA,KAAA,OAAA,MAOhB,KAAK,OAAO;EACd;;AAKF,MAAM,QAAQ,CAAC,MAAyB,OAAO,KAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC,GAGzF,cAAc,KACd,aAAa,KACb,aAAa,KACb,gBAAgB,KAEhB,oBAAoB,KAEpB,YAAY;AAOZ,SAAU,iBAAiB,MAAc,KAAY;AACzD,MAAI,OAAO,OAAQ;AACjB,UAAM,IAAI,aAAa,MAAM,IAAI,KAAI,KAAM,gBAAgB;AAE7D,MAAI,CAAC,MAAM,GAAG;AAAG;AACjB,QAAM,OAAO,OAAO,IAAI,QAAS,WAAW,IAAI,OAAO;AACvD,MAAI,OAAO,IAAI,SAAU;AACvB,UAAM,IAAI,aAAa,MAAM,IAAI,OAAO,IAAI;AAE9C,MAAI,IAAI,WAAW;AACjB,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,WAAY,WAAW,IAAI,UAAU,iBAAiB,IAAI;AAEtG;AAGM,SAAU,qBAAqB,QAAiB,MAAY;AAChE,SAAO,OAAO,UAAW,YAAY,WAAW,KAAK,GAAG,MAAM,IAAI,IAAI,KAAK;AAC7E;AAEA,MAAM,cAAc;AAGpB,SAAS,WAAW,OAAc;AAChC,MAAI,OAAO,SAAU;AAAU,WAAO,CAAA;AACtC,QAAM,IAAI,MAAM,MAAM,eAAe;AACrC,SAAO,IAAI,EAAE,YAAY,OAAO,EAAE,CAAC,CAAC,GAAG,UAAU,OAAO,EAAE,CAAC,CAAC,EAAC,IAAK,CAAA;AACpE;AASA,SAAS,SAAS,KAAQ;AACxB,SAAO;IACL,SAAS,IAAI,aAAa;IAC1B,YAAY,OAAO,IAAI,eAAgB,WAAW,IAAI,cAAc;IACpE,YACE,OAAO,IAAI,eAAgB,WACvB,IAAI,cACJ,OAAO,IAAI,QAAS,WAClB,IAAI,OACJ;;AAEZ;AAWM,SAAU,qBAAqB,KAAY;AAC/C,MAAI,CAAC,MAAM,GAAG;AACZ,UAAM,IAAI,SAAS,sCAAsC,cAAc,QAAQ;MAC7E;KACD;AAIH,QAAM,OAAO,IAAI,WAAW,IAAI;AAChC,MAAI,MAAM,QAAQ,IAAI;AACpB,WAAO,EAAE,OAAO,MAAwB,SAAS,GAAK;AAGxD,MAAI,CAAC,MAAM,QAAQ,IAAI,MAAM,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI;AACvD,UAAM,IAAI,SAAS,kDAAkD,cAAc,QAAQ;MACzF,4EAA4E,OAAO,KAAK,GAAG,EAAE,KAAK,IAAI,KAAK,QAAQ;MACnH;KACD;AAGH,QAAM,OAAO,IAAI,MACX,QAAwB,CAAA;AAC9B,aAAW,SAAS,IAAI,QAAiB;AACvC,UAAM,OAAO,OAAO,MAAM,QAAS,YAAY,MAAM,SAAS,KAAK,MAAM,OAAO;AAChF,eAAW,OAAQ,MAAM,QAAQ,CAAA,GAAoB;AACnD,YAAM,OAAY,CAAA;AAClB,WAAK,QAAQ,CAAC,GAAG,MAAO,KAAK,CAAC,IAAI,IAAI,CAAC,CAAE;AACzC,YAAM,UAAU,OAAO,KAAK,QAAQ,EAAE,GAChC,gBAAgB,qBAAqB,MAAM,WAAW,OAAO,GAC7D,QAAQ,OAAO,KAAK,SAAS,EAAE,GAE/B,aAAkB,CAAA;AACxB,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI;AACtC,QAAI,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,MAAM,QAAQ,MAAM,SACtE,KAAM,SAAyB,WAAW,CAAC,IAAI;AAErD,aAAO,OAAO,YAAY,WAAW,KAAK,KAAK,CAAC,GAC5C,OAAO,KAAK,MAAO,aAAU,WAAW,SAAS,KAAK,KACtD,OAAO,KAAK,OAAQ,aAAU,WAAW,UAAU,KAAK;AAE5D,UAAI,OAAO;AACX,YAAM,QAAQ,UAAU,UAAU,cAAc,MAAM,WAAW,IAAI;AAErE,MAAI,UAAO,OAAO,MAAM,CAAC,IACzB,WAAW,OAAO,MAElB,MAAM,KAAK,EAAE,MAAM,gBAAgB,eAAe,OAAO,WAAW,MAAM,WAAU,CAAE;IACxF;EACF;AACA,SAAO,EAAE,OAAO,GAAG,SAAS,GAAG,EAAC;AAClC;AAGM,SAAU,oBAAoB,KAAY;AAC9C,MAAI,CAAC,MAAM,GAAG;AACZ,UAAM,IAAI,SAAS,qCAAqC,cAAc,QAAQ;MAC5E;KACD;AAEH,MAAI,MAAM,QAAQ,IAAI,OAAO,KAAK,MAAM,QAAQ,IAAI,IAAI;AACtD,WAAO,EAAE,SAAS,IAAI,SAAqB,MAAM,IAAI,MAAqB,GAAG,SAAS,GAAG,EAAC;AAE5F,MAAI,MAAM,QAAQ,IAAI,OAAO,GAAG;AAC9B,UAAM,UAAU,IAAI,SACd,UAAU,MAAM,KAAK,IAAI,IAAI,QAAQ,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1E,WAAO,EAAE,SAAS,MAAM,QAAQ,IAAI,CAAC,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,GAAK;EACtF;AACA,QAAM,IAAI,SAAS,sDAAsD,cAAc,QAAQ;IAC7F,aAAa,OAAO,KAAK,GAAG,EAAE,KAAK,IAAI,KAAK,QAAQ;GACrD;AACH;AAGM,SAAU,aAAa,QAAwB;AACnD,SAAO,OAAO,KAAK,IAAI,CAAC,QAAO;AAC7B,UAAM,MAA+B,CAAA;AACrC,kBAAO,QAAQ,QAAQ,CAAC,GAAG,MAAO,IAAI,CAAC,IAAI,IAAI,CAAC,CAAE,GAC3C;EACT,CAAC;AACH;AAGM,SAAU,WAAW,GAAU;AACnC,QAAM,IAAI,OAAO,KAAM,WAAW,IAAI,OAAO,KAAM,YAAY,EAAE,KAAI,MAAO,KAAK,OAAO,CAAC,IAAI;AAC7F,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAGM,SAAU,WAAW,GAAU;AACnC,SAAO,OAAO,KAAM,YAAY,MAAM,KAAK,IAAI;AACjD;AAGM,SAAU,UAAU,GAAU;AAClC,MAAI,MAAM,QAAQ,CAAC;AAAG,WAAO,OAAO,EAAE,CAAC,KAAM,WAAW,EAAE,CAAC,IAAI;AAC/D,MAAI,SAAO,KAAM,YAAY,MAAM,KACnC;QAAI,EAAE,WAAW,GAAG;AAClB,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,CAAC;AAC3B,eAAO,MAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,CAAC,KAAM,WAAW,OAAO,CAAC,IAAI;MAC9E,QAAQ;AACN;MACF;AAEF,WAAO;;AACT;AAMM,SAAU,YAAY,GAAS;AACnC,SACE,GAAG,CAAC,kBAAkB,CAAC,sCAAsC,CAAC,4BAC3D,CAAC,8BAA8B,CAAC,iCAAiC,CAAC;AAEzE;AAOM,SAAU,eAAe,QAAwB;AACrD,SAAO,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAK;AACxC,QAAI,OAAO,QAAQ,WAAW,KAAK,MAAM,EAAE,OAAO,QAAQ,CAAC,CAAC,CAAC;AAC3D,aAAO,CAAC,EAAE,OAAO,QAAQ,CAAC,CAAC,CAAiB;AAE9C,UAAM,OAAO,WAAW,EAAE,IAAI;AAC9B,QAAI,CAAC;AAAM,aAAO,CAAA;AAClB,UAAM,aAAkB,EAAE,KAAI,GACxB,QAAQ,WAAW,EAAE,UAAU,GAC/B,MAAM,WAAW,EAAE,QAAQ;AACjC,WAAI,UAAU,WAAW,WAAW,aAAa,QAC7C,QAAQ,WAAW,WAAW,WAAW,MACtC;MACL;QACE;QACA,gBAAgB,WAAW,EAAE,cAAc;QAC3C,OAAO,UAAU,EAAE,KAAK,KAAK;QAC7B,WAAW,WAAW,EAAE,SAAS;QACjC;;;EAGN,CAAC;AACH;AAQM,SAAU,mBACd,KACA,WAAiC;AAEjC,MAAI,CAAC,MAAM,GAAG;AACZ,UAAM,IAAI,SAAS,oCAAoC,cAAc,QAAQ;MAC3E;KACD;AAEH,MAAI,MAAM,QAAQ,IAAI,KAAK;AACzB,WAAO;MACL,OAAO,IAAI;MACX,OAAO,MAAM,QAAQ,IAAI,KAAK,IAAK,IAAI,QAAwB,CAAA;MAC/D,OAAO;MACP,SAAS;;AAIb,QAAM,UAAU,IAAI,cAAc,YAAY,YAAY,SAAS;AACnE,MAAI,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,QAAQ,QAAQ,MAAM;AAAG,WAAO;AAC9D,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,IAAK,QAAQ,OAAoB,CAAC,QAAQ,KAAK,GAChF,UAAU,KAAK,QAAQ,MAAM,GAC7B,SAAS,KAAK,QAAQ,KAAK,GAC3B,QAAqB,CAAA;AAC3B,aAAW,SAAS,QAAQ;AAC1B,eAAW,OAAQ,MAAM,QAAQ,CAAA,GAAoB;AACnD,YAAM,OAAO,OAAO,IAAI,WAAW,IAAI,UAAU,CAAC,KAAK,EAAE,GACnD,MAAM,UAAU,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,QAC9C,gBAAgB,qBAAqB,MAAM,WAAW,IAAI;AAChE,YAAM,KAAK;QACT,IAAI;QACJ,gBAAgB;QAChB;QACA,eAAe;QACf,OAAO,OAAO;OACf;IACH;AAEF,QAAM,cAAc,SAAS,OAAO,GAC9B,UAAU,SAAS,GAAG;AAC5B,SAAO;IACL;IACA,OAAO,CAAA;IACP,OAAO;IACP,SAAS,QAAQ,WAAW,YAAY;IACxC,YAAY,QAAQ,cAAc,YAAY;;AAElD;AAGM,SAAU,sBAAsB,KAAY;AAChD,MAAI,CAAC,MAAM,GAAG;AACZ,UAAM,IAAI,SAAS,uCAAuC,cAAc,QAAQ;MAC9E;KACD;AAIH,SAAO,EAAE,UADQ,MAAM,QAAQ,IAAI,QAAQ,IAAK,IAAI,WAA4B,CAAA,GAC7D,GAAG,SAAS,GAAG,EAAC;AACrC;AAYM,SAAU,oBAAoB,OAAuB,UAAwB;AACjF,QAAM,UAAU,oBAAI,IAAG;AACvB,aAAW,KAAK,UAAU;AACxB,UAAM,OAAO,QAAQ,IAAI,EAAE,oBAAoB,KAAK,CAAA;AACpD,IAAK,KAAK,KAAK,CAAC,MAAM,EAAE,mBAAmB,EAAE,cAAc,KAAG,KAAK,KAAK,CAAC,GACzE,QAAQ,IAAI,EAAE,sBAAsB,IAAI;EAC1C;AACA,SAAO,MAAM,QAAQ,CAAC,SAAQ;AAC5B,UAAM,OAAO,QAAQ,IAAI,OAAO,KAAK,kBAAkB,EAAE,CAAC,KAAK,CAAA;AAC/D,QAAI,KAAK,WAAW;AAAG,aAAO,CAAC,IAAI;AACnC,UAAM,SAAS,KAAK,SAAS;AAC7B,WAAO,KAAK,IAAI,CAAC,MAAK;AACpB,YAAM,aAAkB,EAAE,GAAI,KAAK,cAAc,CAAA,EAAG;AACpD,iBAAW,iBAAiB,EAAE,gBAC9B,WAAW,WAAW,EAAE,MACpB,EAAE,eAAe,WAAW,WAAW,eAAe,EAAE,aACxD,EAAE,aAAa,WAAW,WAAW,aAAa,EAAE;AACxD,YAAM,OAAO,SAAS,EAAE,aAAa,KAAK,YAAY,KAAK,aAAa,EAAE;AAC1E,aAAO,EAAE,GAAG,MAAM,WAAW,MAAM,WAAU;IAC/C,CAAC;EACH,CAAC;AACH;AAGO,MAAM,uBACX;AAII,SAAU,uBAAuB,QAAwB;AAC7D,SAAO,aAAa,MAAM,EAAE,QAAQ,CAAC,MAAK;AACxC,UAAM,QAAQ,WAAW,EAAE,oBAAoB,GACzC,KAAK,WAAW,EAAE,cAAc;AACtC,WAAI,CAAC,SAAS,CAAC,KAAW,CAAA,IACnB;MACL;QACE,sBAAsB;QACtB,gBAAgB;QAChB,MAAM,WAAW,EAAE,IAAI,KAAK,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;QAC5D,WAAW,WAAW,EAAE,SAAS;QACjC,YAAY,WAAW,EAAE,UAAU;QACnC,UAAU,WAAW,EAAE,QAAQ;;;EAGrC,CAAC;AACH;AAOM,MAAO,SAAQ;EACU;EAA7B,YAA6B,QAAsB;AAAtB,SAAA,SAAA;EAAyB;;EAGtD,MAAM,KAAK,MAAc,OAAgC,CAAA,GAAE;AACzD,UAAM,MAAM,MAAM,KAAK,OAAO,WAAW,MAAM,EAAE,GAAG,MAAM,QAAQ,OAAM,CAAE;AAC1E,4BAAiB,MAAM,GAAG,GACnB;EACT;EAEA,MAAM,eAAY;AAChB,UAAM,WAAyB,CAAA;AAC/B,QAAI,SAAS;AACb,aAAS,OAAO,GAAG,OAAO,WAAW,QAAQ;AAC3C,YAAM,MAAM,sBACV,MAAM,KAAK,KAAK,iBAAiB,EAAE,OAAO,eAAe,OAAM,CAAE,CAAC;AAEpE,eAAS,KAAK,GAAG,IAAI,QAAQ;AAC7B,YAAM,OAAO,IAAI,cAAc,SAAS,IAAI,SAAS;AACrD,UAAI,CAAC,IAAI,WAAW,QAAQ;AAAQ;AACpC,eAAS;IACX;AACA,WAAO;EACT;;EAGA,MAAM,YAAY,MAA6B;AAC7C,UAAM,QAAwB,CAAA;AAC9B,QAAI,SAAS;AACb,aAAS,OAAO,GAAG,OAAO,WAAW,QAAQ;AAC3C,YAAM,MAAM,qBACV,MAAM,KAAK,KAAK,gBAAgB;QAC9B,GAAG;QACH,OAAO;QACP;QACA,mBAAmB;OACpB,CAAC;AAGJ,UADA,MAAM,KAAK,GAAG,IAAI,KAAK,GACnB,CAAC,IAAI,WAAW,IAAI,eAAe,UAAa,IAAI,cAAc;AAAQ;AAC9E,eAAS,IAAI;IACf;AACA,WAAO;EACT;;EAGA,MAAM,WAAW,OAAe,SAAe;AAC7C,UAAM,OAAO,EAAE,OAAO,SAAS,UAAU,YAAY,mBAAmB,kBAAiB;AACzF,QAAI,MAAM,oBAAoB,MAAM,KAAK,KAAK,eAAe,IAAI,CAAC;AAClE,UAAM,UAAU,IAAI,SACd,OAAO,CAAC,GAAG,IAAI,IAAI;AACzB,QAAI,SAAS;AACb,aAAS,OAAO,GAAG,OAAO,aAAa,IAAI,SAAS,QAAQ;AAC1D,UAAI,IAAI;AACN,cAAM,oBAAoB,MAAM,KAAK,KAAK,eAAe,EAAE,GAAG,MAAM,QAAQ,IAAI,WAAU,CAAE,CAAC;eACpF,IAAI,eAAe,UAAa,IAAI,aAAa;AAC1D,iBAAS,IAAI,YACb,MAAM,oBAAoB,MAAM,KAAK,KAAK,eAAe,EAAE,GAAG,MAAM,OAAM,CAAE,CAAC;;AAE7E;AAEF,WAAK,KAAK,GAAG,IAAI,IAAI;IACvB;AACA,WAAO,EAAE,SAAS,KAAI;EACxB;;;;;EAMA,MAAM,UAAU,MAKf;AACC,UAAM,OAAO;MACX,GAAG;MACH,OAAO,KAAK,IAAI,GAAG,KAAK,KAAK;MAC7B,OAAO;MACP,mBAAmB;;AAErB,QAAI,MAAM,mBAAmB,MAAM,KAAK,KAAK,cAAc,IAAI,GAAG,KAAK,SAAS;AAChF,QAAI,CAAC;AAAK,aAAO;AACjB,UAAM,EAAE,MAAK,IAAK,KACZ,QAAQ,CAAC,GAAG,IAAI,KAAK,GACrB,QAAQ,CAAC,GAAG,IAAI,KAAK;AAC3B,aAAS,OAAO,GAAG,OAAO,aAAa,IAAI,WAAW,IAAI,eACxD,MAAM,mBACJ,MAAM,KAAK,KAAK,cAAc,EAAE,GAAG,MAAM,QAAQ,IAAI,WAAU,CAAE,GACjE,KAAK,SAAS,GAEZ,EAAC,MAL+D;AAMpE,YAAM,KAAK,GAAG,IAAI,KAAK,GACvB,MAAM,KAAK,GAAG,IAAI,KAAK;AAEzB,WAAO,EAAE,OAAO,OAAO,MAAK;EAC9B;;;;;EAMA,MAAM,YAAY,gBAA0B,SAAe;AACzD,UAAM,QAAQ,oBAAI,IAAG,GACf,OAAO,MAAM,KAAK,IAAI,IAAI,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,SAAS,KAAK,CAAC,CAAC;AACrF,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,KAAK;AACzC,YAAM,OAAO,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,GAC5D,SAAS,MAAM,KAAK,WACxB,wCAAwC,IAAI,yEAC5C,OAAO;AAET,iBAAW,KAAK,aAAa,MAAM,GAAG;AACpC,cAAM,KAAK,WAAW,EAAE,cAAc,GAChC,OAAO,WAAW,EAAE,SAAS;AACnC,QAAI,MAAM,QAAQ,CAAC,MAAM,IAAI,EAAE,KAAG,MAAM,IAAI,IAAI,IAAI;MACtD;IACF;AACA,WAAO;EACT;;EAGA,MAAM,gBAAgB,UAAgB;AACpC,UAAM,MAAM,MAAM,KAAK,KAAK,oBAAoB,EAAE,WAAW,SAAQ,CAAE;AACvE,QAAI,CAAC,MAAM,GAAG;AACZ,YAAM,IAAI,SAAS,0CAA0C,cAAc,QAAQ;QACjF;OACD;AAEH,WAAO;EACT;;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function edgePreferences(m) {
|
|
2
|
+
if (m.usage !== "verification_and_mapping")
|
|
3
|
+
return [];
|
|
4
|
+
const listed = m.conditions?.predicates;
|
|
5
|
+
return Array.isArray(listed) && listed.length ? listed.map((p, i) => {
|
|
6
|
+
if (typeof p == "string")
|
|
7
|
+
return { predicate: p, invert: !1 };
|
|
8
|
+
if (p && typeof p == "object" && typeof p.predicate == "string")
|
|
9
|
+
return { predicate: p.predicate, invert: p.invert === !0 };
|
|
10
|
+
throw new Error(`Edge mapping ${m.cbm_edge}: conditions.predicates[${i}] must be a predicate name or {predicate, invert}`);
|
|
11
|
+
}) : m.dr_relationship ? [{ predicate: m.dr_relationship, invert: m.directionality_transform === "invert" }] : [];
|
|
12
|
+
}
|
|
13
|
+
function typeAllowed(allowed, type) {
|
|
14
|
+
return !Array.isArray(allowed) || !allowed.length ? !0 : allowed.some((a) => typeof a == "string" && (a === type || a.endsWith(".*") && type.startsWith(a.slice(0, -1))));
|
|
15
|
+
}
|
|
16
|
+
function edgeAllowed(m, fromType, toType) {
|
|
17
|
+
const c = m.conditions ?? {};
|
|
18
|
+
return typeAllowed(c.from_types, fromType) && typeAllowed(c.to_types, toType);
|
|
19
|
+
}
|
|
20
|
+
function relationshipFor(m, fromType, toType, hasTriple) {
|
|
21
|
+
if (!edgeAllowed(m, fromType, toType))
|
|
22
|
+
return null;
|
|
23
|
+
for (const pref of edgePreferences(m)) {
|
|
24
|
+
const [source, destination] = pref.invert ? [toType, fromType] : [fromType, toType], id = `${source}.${pref.predicate}.${destination}`;
|
|
25
|
+
if (hasTriple(id))
|
|
26
|
+
return { id, source, predicate: pref.predicate, destination, inverted: pref.invert };
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
function dedupeRelationships(items) {
|
|
31
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32
|
+
return items.filter((x) => {
|
|
33
|
+
if (x.sourceId === x.destinationId)
|
|
34
|
+
return !1;
|
|
35
|
+
const key = `${x.sourceId}|${x.relationship.id}|${x.destinationId}`;
|
|
36
|
+
return seen.has(key) ? !1 : (seen.add(key), !0);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
dedupeRelationships,
|
|
41
|
+
edgeAllowed,
|
|
42
|
+
edgePreferences,
|
|
43
|
+
relationshipFor
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=edge-relationships.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/analyzers/edge-relationships.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Turn a CBM edge between two classified symbols into a DR relationship.\n *\n * An edge mapping (spec/analyzers/<name>/edge-mapping.json) that creates relationships\n * (`usage: \"verification_and_mapping\"`) lists, in `conditions.predicates`, the predicates that can\n * express it in order of preference, each optionally inverted (the DR relationship runs against\n * the CBM edge). The relationship is the first catalog triple\n * `{source type}.{predicate}.{destination type}` that exists for the two element types the ends\n * were classified as; the relationship catalog stays the authority on which triples are valid.\n * Without `conditions.predicates` the mapping's own `dr_relationship` and\n * `directionality_transform` are the only preference.\n *\n * `conditions.from_types` / `conditions.to_types`, when present, restrict the element types the\n * CBM edge's source and target may have (\"layer.type\", or \"layer.*\" for any type of a layer); an\n * edge between other types creates no relationship, whatever the catalog holds.\n *\n * Example: CALLS prefers depends-on, then uses, then accesses. A call from an\n * application.applicationfunction to another becomes\n * application.applicationfunction.depends-on.application.applicationfunction; a call from an\n * application.applicationservice to an application.dataobject has no depends-on or uses triple and\n * becomes application.applicationservice.accesses.application.dataobject.\n *\n * Callers pass only edges between two different accepted elements, and should collapse duplicates\n * with dedupeRelationships: CALLS, CALL_REFERENCE and USAGE can all link the same pair.\n */\nimport type { AnalyzerEdgeMapping } from \"./types.js\";\n\nexport interface EdgePreference {\n predicate: string;\n /** true when the DR relationship runs from the CBM edge's target to its source */\n invert: boolean;\n}\n\nexport interface EdgeRelationship {\n id: string;\n source: string;\n predicate: string;\n destination: string;\n inverted: boolean;\n}\n\n/** The predicates an edge mapping may use, in order of preference. */\nexport function edgePreferences(m: AnalyzerEdgeMapping): EdgePreference[] {\n if (m.usage !== \"verification_and_mapping\") return [];\n const listed = (m.conditions as { predicates?: unknown } | undefined)?.predicates;\n if (Array.isArray(listed) && listed.length) {\n return listed.map((p, i) => {\n if (typeof p === \"string\") return { predicate: p, invert: false };\n if (p && typeof p === \"object\" && typeof (p as { predicate?: unknown }).predicate === \"string\") {\n return { predicate: (p as { predicate: string }).predicate, invert: (p as { invert?: unknown }).invert === true };\n }\n throw new Error(`Edge mapping ${m.cbm_edge}: conditions.predicates[${i}] must be a predicate name or {predicate, invert}`);\n });\n }\n return m.dr_relationship ? [{ predicate: m.dr_relationship, invert: m.directionality_transform === \"invert\" }] : [];\n}\n\nfunction typeAllowed(allowed: unknown, type: string): boolean {\n if (!Array.isArray(allowed) || !allowed.length) return true;\n return allowed.some((a) => typeof a === \"string\" && (a === type || (a.endsWith(\".*\") && type.startsWith(a.slice(0, -1)))));\n}\n\n/** Whether an edge mapping allows an edge between these two element types (CBM direction). */\nexport function edgeAllowed(m: AnalyzerEdgeMapping, fromType: string, toType: string): boolean {\n const c = (m.conditions ?? {}) as { from_types?: unknown; to_types?: unknown };\n return typeAllowed(c.from_types, fromType) && typeAllowed(c.to_types, toType);\n}\n\n/**\n * The relationship a CBM edge `from \u2192 to` becomes, given the DR element types its ends were\n * classified as, or null when no preferred predicate has a catalog triple for that pair.\n */\nexport function relationshipFor(\n m: AnalyzerEdgeMapping,\n fromType: string,\n toType: string,\n hasTriple: (id: string) => boolean\n): EdgeRelationship | null {\n if (!edgeAllowed(m, fromType, toType)) return null;\n for (const pref of edgePreferences(m)) {\n const [source, destination] = pref.invert ? [toType, fromType] : [fromType, toType];\n const id = `${source}.${pref.predicate}.${destination}`;\n if (hasTriple(id)) return { id, source, predicate: pref.predicate, destination, inverted: pref.invert };\n }\n return null;\n}\n\n/**\n * One relationship per (source element, relationship id, destination element), keeping the first.\n * `items` carry the element ids of the two ends alongside the relationship.\n */\nexport function dedupeRelationships<T extends { sourceId: string; destinationId: string; relationship: EdgeRelationship }>(items: T[]): T[] {\n const seen = new Set<string>();\n return items.filter((x) => {\n if (x.sourceId === x.destinationId) return false;\n const key = `${x.sourceId}|${x.relationship.id}|${x.destinationId}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n"],
|
|
5
|
+
"mappings": "AA0CM,SAAU,gBAAgB,GAAsB;AACpD,MAAI,EAAE,UAAU;AAA4B,WAAO,CAAA;AACnD,QAAM,SAAU,EAAE,YAAqD;AACvE,SAAI,MAAM,QAAQ,MAAM,KAAK,OAAO,SAC3B,OAAO,IAAI,CAAC,GAAG,MAAK;AACzB,QAAI,OAAO,KAAM;AAAU,aAAO,EAAE,WAAW,GAAG,QAAQ,GAAK;AAC/D,QAAI,KAAK,OAAO,KAAM,YAAY,OAAQ,EAA8B,aAAc;AACpF,aAAO,EAAE,WAAY,EAA4B,WAAW,QAAS,EAA2B,WAAW,GAAI;AAEjH,UAAM,IAAI,MAAM,gBAAgB,EAAE,QAAQ,2BAA2B,CAAC,mDAAmD;EAC3H,CAAC,IAEI,EAAE,kBAAkB,CAAC,EAAE,WAAW,EAAE,iBAAiB,QAAQ,EAAE,6BAA6B,SAAQ,CAAE,IAAI,CAAA;AACnH;AAEA,SAAS,YAAY,SAAkB,MAAY;AACjD,SAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,SAAe,KAChD,QAAQ,KAAK,CAAC,MAAM,OAAO,KAAM,aAAa,MAAM,QAAS,EAAE,SAAS,IAAI,KAAK,KAAK,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG;AAC3H;AAGM,SAAU,YAAY,GAAwB,UAAkB,QAAc;AAClF,QAAM,IAAK,EAAE,cAAc,CAAA;AAC3B,SAAO,YAAY,EAAE,YAAY,QAAQ,KAAK,YAAY,EAAE,UAAU,MAAM;AAC9E;AAMM,SAAU,gBACd,GACA,UACA,QACA,WAAkC;AAElC,MAAI,CAAC,YAAY,GAAG,UAAU,MAAM;AAAG,WAAO;AAC9C,aAAW,QAAQ,gBAAgB,CAAC,GAAG;AACrC,UAAM,CAAC,QAAQ,WAAW,IAAI,KAAK,SAAS,CAAC,QAAQ,QAAQ,IAAI,CAAC,UAAU,MAAM,GAC5E,KAAK,GAAG,MAAM,IAAI,KAAK,SAAS,IAAI,WAAW;AACrD,QAAI,UAAU,EAAE;AAAG,aAAO,EAAE,IAAI,QAAQ,WAAW,KAAK,WAAW,aAAa,UAAU,KAAK,OAAM;EACvG;AACA,SAAO;AACT;AAMM,SAAU,oBAA2G,OAAU;AACnI,QAAM,OAAO,oBAAI,IAAG;AACpB,SAAO,MAAM,OAAO,CAAC,MAAK;AACxB,QAAI,EAAE,aAAa,EAAE;AAAe,aAAO;AAC3C,UAAM,MAAM,GAAG,EAAE,QAAQ,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa;AACjE,WAAI,KAAK,IAAI,GAAG,IAAU,MAC1B,KAAK,IAAI,GAAG,GACL;EACT,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -82,6 +82,15 @@ class MappingLoader {
|
|
|
82
82
|
if (Array.isArray(heuristics))
|
|
83
83
|
return heuristics.find((h) => h.name === name);
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Get every heuristic, in file order (Tier-0 rules among them; see tier0-rules.ts)
|
|
87
|
+
*
|
|
88
|
+
* @returns Array of heuristics
|
|
89
|
+
*/
|
|
90
|
+
getHeuristics() {
|
|
91
|
+
const heuristics = this.artifact.heuristics.heuristics;
|
|
92
|
+
return Array.isArray(heuristics) ? heuristics : [];
|
|
93
|
+
}
|
|
85
94
|
/**
|
|
86
95
|
* Get all filtering rules
|
|
87
96
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/analyzers/mapping-loader.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * MappingLoader - reads and validates packed analyzer artifact\n *\n * Loads the compiled CBM mapping from cli/src/schemas/bundled/analyzers/<name>.json\n * and provides typed accessors for node mappings, edge mappings, heuristics, and rules.\n *\n * The loader validates the top-level structure and throws CLIError if the artifact\n * is malformed or missing required keys, with a suggestion to run npm run build:spec.\n */\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { CLIError, ErrorCategory } from \"../utils/errors.js\";\nimport type {\n AnalyzerEdgeMapping,\n AnalyzerHeuristic,\n AnalyzerNodeMapping,\n ConfidenceRubric,\n FilteringRule,\n} from \"./types.js\";\n\n/**\n * Validate analyzer name to prevent path traversal attacks\n * Rejects names containing `/`, `\\`, or `..`\n *\n * @param analyzerName Analyzer name to validate\n * @throws CLIError if name contains path traversal characters\n */\nfunction validateAnalyzerName(analyzerName: string): void {\n if (!analyzerName) {\n throw new CLIError(\n \"Analyzer name cannot be empty\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n\n if (analyzerName.includes(\"/\") || analyzerName.includes(\"\\\\\")) {\n throw new CLIError(\n \"Analyzer name cannot contain path separators (/ or \\\\)\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n\n if (analyzerName.includes(\"..\")) {\n throw new CLIError(\n \"Analyzer name cannot contain parent directory references (..)\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n}\n\ninterface AnalyzerManifestMetadata {\n name: string;\n display_name: string;\n description: string;\n homepage: string;\n binary_names?: string[];\n mcp_server_name?: string;\n [key: string]: unknown;\n}\n\ninterface PackedArtifact {\n name: string;\n version: string;\n analyzer?: AnalyzerManifestMetadata;\n nodes_by_label: Record<string, AnalyzerNodeMapping>;\n edges_by_type: Record<string, AnalyzerEdgeMapping>;\n heuristics: {\n confidence_rubric?: ConfidenceRubric;\n heuristics?: AnalyzerHeuristic[];\n filtering_rules?: FilteringRule[];\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\nexport class MappingLoader {\n private artifact: PackedArtifact;\n private analyzerName: string;\n\n private constructor(artifact: PackedArtifact, analyzerName: string) {\n this.artifact = artifact;\n this.analyzerName = analyzerName;\n }\n\n /**\n * Load a mapping artifact from the bundled schemas directory\n *\n * @param analyzerName Name of the analyzer (e.g., \"cbm\")\n * @returns Loaded and validated MappingLoader instance\n * @throws CLIError if artifact is missing, malformed, or analyzerName contains path traversal characters\n */\n static async load(analyzerName: string): Promise<MappingLoader> {\n validateAnalyzerName(analyzerName);\n\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const artifactPath = path.join(\n currentDir,\n \"../schemas/bundled/analyzers\",\n `${analyzerName}.json`\n );\n\n let artifact: unknown;\n try {\n const content = await fs.readFile(artifactPath, \"utf-8\");\n artifact = JSON.parse(content);\n } catch (error) {\n const message =\n error instanceof Error && \"code\" in error && error.code === \"ENOENT\"\n ? `Analyzer mapping not found: ${analyzerName}`\n : `Failed to read analyzer mapping: ${error instanceof Error ? error.message : String(error)}`;\n\n throw new CLIError(\n message,\n ErrorCategory.NOT_FOUND,\n [\n \"Run `npm run build:spec` to compile the analyzer mapping artifacts\",\n `Check that spec/analyzers/${analyzerName}/ sources exist`,\n ]\n );\n }\n\n // Validate structure\n if (!artifact || typeof artifact !== \"object\") {\n throw new CLIError(\n `Invalid analyzer mapping structure: expected object, got ${typeof artifact}`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n const obj = artifact as Record<string, unknown>;\n const requiredKeys = [\"nodes_by_label\", \"edges_by_type\", \"heuristics\"];\n const missingKeys = requiredKeys.filter((key) => !(key in obj));\n\n if (missingKeys.length > 0) {\n throw new CLIError(\n `Invalid analyzer mapping: missing required keys: ${missingKeys.join(\", \")}`,\n ErrorCategory.VALIDATION,\n [\n \"Run `npm run build:spec` to recompile the analyzer mapping artifacts\",\n `Ensure spec/analyzers/${analyzerName}/ contains valid layer/node/edge definitions`,\n ]\n );\n }\n\n // Validate types of required keys\n if (typeof obj.nodes_by_label !== \"object\" || obj.nodes_by_label === null) {\n throw new CLIError(\n `Invalid analyzer mapping: nodes_by_label must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n if (typeof obj.edges_by_type !== \"object\" || obj.edges_by_type === null) {\n throw new CLIError(\n `Invalid analyzer mapping: edges_by_type must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n if (typeof obj.heuristics !== \"object\" || obj.heuristics === null) {\n throw new CLIError(\n `Invalid analyzer mapping: heuristics must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n return new MappingLoader(artifact as PackedArtifact, analyzerName);\n }\n\n /**\n * Get the mapping for a node label\n *\n * @param label CBM node label (e.g., \"Route\", \"Function\")\n * @returns The mapping definition or undefined if not found\n */\n getNodeMapping(label: string): AnalyzerNodeMapping | undefined {\n return this.artifact.nodes_by_label[label];\n }\n\n /**\n * Get the mapping for an edge type\n *\n * @param type CBM edge type (e.g., \"HTTP_CALLS\", \"HANDLES\")\n * @returns The mapping definition or undefined if not found\n */\n getEdgeMapping(type: string): AnalyzerEdgeMapping | undefined {\n return this.artifact.edges_by_type[type];\n }\n\n /**\n * Get a heuristic by name\n *\n * @param name Heuristic name (e.g., \"min_fan_in\")\n * @returns The heuristic definition or undefined if not found\n */\n getHeuristic(name: string): AnalyzerHeuristic | undefined {\n const heuristics = this.artifact.heuristics.heuristics;\n if (!Array.isArray(heuristics)) {\n return undefined;\n }\n return heuristics.find((h) => h.name === name);\n }\n\n /**\n * Get all filtering rules\n *\n * @returns Array of filtering rules\n */\n getFilteringRules(): FilteringRule[] {\n const rules = this.artifact.heuristics.filtering_rules;\n return Array.isArray(rules) ? rules : [];\n }\n\n /**\n * Get the confidence rubric\n *\n * @returns Confidence levels and their score ranges\n */\n getConfidenceRubric(): ConfidenceRubric | undefined {\n return this.artifact.heuristics.confidence_rubric;\n }\n\n /**\n * Get all node labels in the mapping\n *\n * @returns Array of CBM label names\n */\n getNodeLabels(): string[] {\n return Object.keys(this.artifact.nodes_by_label);\n }\n\n /**\n * Get all edge types in the mapping\n *\n * @returns Array of CBM edge type names\n */\n getEdgeTypes(): string[] {\n return Object.keys(this.artifact.edges_by_type);\n }\n\n /**\n * Get analyzer name\n *\n * @returns The name of the loaded analyzer\n */\n getAnalyzerName(): string {\n return this.analyzerName;\n }\n\n /**\n * Get analyzer version\n *\n * @returns The version of the artifact\n */\n getVersion(): string {\n return this.artifact.version;\n }\n\n /**\n * Get analyzer metadata (display name, description, homepage, etc.)\n *\n * @returns Analyzer metadata object or undefined if not available\n */\n getAnalyzerMetadata(): AnalyzerManifestMetadata | undefined {\n return this.artifact.analyzer;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * MappingLoader - reads and validates packed analyzer artifact\n *\n * Loads the compiled CBM mapping from cli/src/schemas/bundled/analyzers/<name>.json\n * and provides typed accessors for node mappings, edge mappings, heuristics, and rules.\n *\n * The loader validates the top-level structure and throws CLIError if the artifact\n * is malformed or missing required keys, with a suggestion to run npm run build:spec.\n */\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport { fileURLToPath } from \"url\";\nimport { CLIError, ErrorCategory } from \"../utils/errors.js\";\nimport type {\n AnalyzerEdgeMapping,\n AnalyzerHeuristic,\n AnalyzerNodeMapping,\n ConfidenceRubric,\n FilteringRule,\n} from \"./types.js\";\n\n/**\n * Validate analyzer name to prevent path traversal attacks\n * Rejects names containing `/`, `\\`, or `..`\n *\n * @param analyzerName Analyzer name to validate\n * @throws CLIError if name contains path traversal characters\n */\nfunction validateAnalyzerName(analyzerName: string): void {\n if (!analyzerName) {\n throw new CLIError(\n \"Analyzer name cannot be empty\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n\n if (analyzerName.includes(\"/\") || analyzerName.includes(\"\\\\\")) {\n throw new CLIError(\n \"Analyzer name cannot contain path separators (/ or \\\\)\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n\n if (analyzerName.includes(\"..\")) {\n throw new CLIError(\n \"Analyzer name cannot contain parent directory references (..)\",\n ErrorCategory.VALIDATION,\n []\n );\n }\n}\n\ninterface AnalyzerManifestMetadata {\n name: string;\n display_name: string;\n description: string;\n homepage: string;\n binary_names?: string[];\n mcp_server_name?: string;\n [key: string]: unknown;\n}\n\ninterface PackedArtifact {\n name: string;\n version: string;\n analyzer?: AnalyzerManifestMetadata;\n nodes_by_label: Record<string, AnalyzerNodeMapping>;\n edges_by_type: Record<string, AnalyzerEdgeMapping>;\n heuristics: {\n confidence_rubric?: ConfidenceRubric;\n heuristics?: AnalyzerHeuristic[];\n filtering_rules?: FilteringRule[];\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\nexport class MappingLoader {\n private artifact: PackedArtifact;\n private analyzerName: string;\n\n private constructor(artifact: PackedArtifact, analyzerName: string) {\n this.artifact = artifact;\n this.analyzerName = analyzerName;\n }\n\n /**\n * Load a mapping artifact from the bundled schemas directory\n *\n * @param analyzerName Name of the analyzer (e.g., \"cbm\")\n * @returns Loaded and validated MappingLoader instance\n * @throws CLIError if artifact is missing, malformed, or analyzerName contains path traversal characters\n */\n static async load(analyzerName: string): Promise<MappingLoader> {\n validateAnalyzerName(analyzerName);\n\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const artifactPath = path.join(\n currentDir,\n \"../schemas/bundled/analyzers\",\n `${analyzerName}.json`\n );\n\n let artifact: unknown;\n try {\n const content = await fs.readFile(artifactPath, \"utf-8\");\n artifact = JSON.parse(content);\n } catch (error) {\n const message =\n error instanceof Error && \"code\" in error && error.code === \"ENOENT\"\n ? `Analyzer mapping not found: ${analyzerName}`\n : `Failed to read analyzer mapping: ${error instanceof Error ? error.message : String(error)}`;\n\n throw new CLIError(\n message,\n ErrorCategory.NOT_FOUND,\n [\n \"Run `npm run build:spec` to compile the analyzer mapping artifacts\",\n `Check that spec/analyzers/${analyzerName}/ sources exist`,\n ]\n );\n }\n\n // Validate structure\n if (!artifact || typeof artifact !== \"object\") {\n throw new CLIError(\n `Invalid analyzer mapping structure: expected object, got ${typeof artifact}`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n const obj = artifact as Record<string, unknown>;\n const requiredKeys = [\"nodes_by_label\", \"edges_by_type\", \"heuristics\"];\n const missingKeys = requiredKeys.filter((key) => !(key in obj));\n\n if (missingKeys.length > 0) {\n throw new CLIError(\n `Invalid analyzer mapping: missing required keys: ${missingKeys.join(\", \")}`,\n ErrorCategory.VALIDATION,\n [\n \"Run `npm run build:spec` to recompile the analyzer mapping artifacts\",\n `Ensure spec/analyzers/${analyzerName}/ contains valid layer/node/edge definitions`,\n ]\n );\n }\n\n // Validate types of required keys\n if (typeof obj.nodes_by_label !== \"object\" || obj.nodes_by_label === null) {\n throw new CLIError(\n `Invalid analyzer mapping: nodes_by_label must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n if (typeof obj.edges_by_type !== \"object\" || obj.edges_by_type === null) {\n throw new CLIError(\n `Invalid analyzer mapping: edges_by_type must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n if (typeof obj.heuristics !== \"object\" || obj.heuristics === null) {\n throw new CLIError(\n `Invalid analyzer mapping: heuristics must be an object`,\n ErrorCategory.VALIDATION,\n [\"Run `npm run build:spec` to recompile the analyzer mapping artifacts\"]\n );\n }\n\n return new MappingLoader(artifact as PackedArtifact, analyzerName);\n }\n\n /**\n * Get the mapping for a node label\n *\n * @param label CBM node label (e.g., \"Route\", \"Function\")\n * @returns The mapping definition or undefined if not found\n */\n getNodeMapping(label: string): AnalyzerNodeMapping | undefined {\n return this.artifact.nodes_by_label[label];\n }\n\n /**\n * Get the mapping for an edge type\n *\n * @param type CBM edge type (e.g., \"HTTP_CALLS\", \"HANDLES\")\n * @returns The mapping definition or undefined if not found\n */\n getEdgeMapping(type: string): AnalyzerEdgeMapping | undefined {\n return this.artifact.edges_by_type[type];\n }\n\n /**\n * Get a heuristic by name\n *\n * @param name Heuristic name (e.g., \"min_fan_in\")\n * @returns The heuristic definition or undefined if not found\n */\n getHeuristic(name: string): AnalyzerHeuristic | undefined {\n const heuristics = this.artifact.heuristics.heuristics;\n if (!Array.isArray(heuristics)) {\n return undefined;\n }\n return heuristics.find((h) => h.name === name);\n }\n\n /**\n * Get every heuristic, in file order (Tier-0 rules among them; see tier0-rules.ts)\n *\n * @returns Array of heuristics\n */\n getHeuristics(): AnalyzerHeuristic[] {\n const heuristics = this.artifact.heuristics.heuristics;\n return Array.isArray(heuristics) ? heuristics : [];\n }\n\n /**\n * Get all filtering rules\n *\n * @returns Array of filtering rules\n */\n getFilteringRules(): FilteringRule[] {\n const rules = this.artifact.heuristics.filtering_rules;\n return Array.isArray(rules) ? rules : [];\n }\n\n /**\n * Get the confidence rubric\n *\n * @returns Confidence levels and their score ranges\n */\n getConfidenceRubric(): ConfidenceRubric | undefined {\n return this.artifact.heuristics.confidence_rubric;\n }\n\n /**\n * Get all node labels in the mapping\n *\n * @returns Array of CBM label names\n */\n getNodeLabels(): string[] {\n return Object.keys(this.artifact.nodes_by_label);\n }\n\n /**\n * Get all edge types in the mapping\n *\n * @returns Array of CBM edge type names\n */\n getEdgeTypes(): string[] {\n return Object.keys(this.artifact.edges_by_type);\n }\n\n /**\n * Get analyzer name\n *\n * @returns The name of the loaded analyzer\n */\n getAnalyzerName(): string {\n return this.analyzerName;\n }\n\n /**\n * Get analyzer version\n *\n * @returns The version of the artifact\n */\n getVersion(): string {\n return this.artifact.version;\n }\n\n /**\n * Get analyzer metadata (display name, description, homepage, etc.)\n *\n * @returns Analyzer metadata object or undefined if not available\n */\n getAnalyzerMetadata(): AnalyzerManifestMetadata | undefined {\n return this.artifact.analyzer;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|