edsger 0.76.0 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/api-docs/index.d.ts +18 -0
- package/dist/commands/api-docs/index.js +41 -0
- package/dist/commands/quality-benchmark/index.d.ts +5 -0
- package/dist/commands/quality-benchmark/index.js +28 -0
- package/dist/index.js +29 -0
- package/dist/phases/api-docs/index.d.ts +47 -0
- package/dist/phases/api-docs/index.js +254 -0
- package/dist/phases/api-docs/mcp-server.d.ts +25 -0
- package/dist/phases/api-docs/mcp-server.js +82 -0
- package/dist/phases/api-docs/prompts.d.ts +16 -0
- package/dist/phases/api-docs/prompts.js +65 -0
- package/dist/phases/api-docs/types.d.ts +22 -0
- package/dist/phases/api-docs/types.js +10 -0
- package/dist/phases/find-architecture/index.js +13 -6
- package/dist/phases/find-architecture/prompts.d.ts +2 -1
- package/dist/phases/find-architecture/prompts.js +3 -2
- package/dist/phases/find-bugs/index.js +3 -1
- package/dist/phases/find-shared/baseline.d.ts +45 -0
- package/dist/phases/find-shared/baseline.js +56 -0
- package/dist/phases/find-shared/custom-rules.d.ts +39 -0
- package/dist/phases/find-shared/custom-rules.js +75 -0
- package/dist/phases/find-shared/detect-context.d.ts +40 -0
- package/dist/phases/find-shared/detect-context.js +247 -0
- package/dist/phases/find-shared/mcp.d.ts +6 -0
- package/dist/phases/find-shared/mcp.js +2 -0
- package/dist/phases/find-shared/rule-config.d.ts +37 -0
- package/dist/phases/find-shared/rule-config.js +67 -0
- package/dist/phases/find-shared/rule-packs.d.ts +65 -0
- package/dist/phases/find-shared/rule-packs.js +124 -0
- package/dist/phases/find-shared/scoped-read.d.ts +12 -0
- package/dist/phases/find-shared/scoped-read.js +33 -0
- package/dist/phases/find-smells/index.js +12 -5
- package/dist/phases/find-smells/prompts.d.ts +2 -1
- package/dist/phases/find-smells/prompts.js +4 -3
- package/dist/phases/quality-benchmark/gate.d.ts +50 -0
- package/dist/phases/quality-benchmark/gate.js +91 -0
- package/dist/phases/quality-benchmark/index.js +15 -1
- package/dist/phases/quality-benchmark/parsers.d.ts +23 -0
- package/dist/phases/quality-benchmark/parsers.js +210 -0
- package/dist/phases/quality-benchmark/rubric.md +37 -0
- package/dist/phases/quality-benchmark/tool-catalog.js +58 -1
- package/dist/phases/quality-benchmark/types.d.ts +8 -1
- package/package.json +1 -1
|
@@ -326,6 +326,55 @@ const dotnetListOutdatedParser = (stdout) => {
|
|
|
326
326
|
oneliner: `${count} outdated NuGet packages`,
|
|
327
327
|
};
|
|
328
328
|
};
|
|
329
|
+
const dartAnalyzeParser = (stdout) => {
|
|
330
|
+
// `dart analyze --format=machine` emits one finding per line, pipe-separated:
|
|
331
|
+
// SEVERITY|TYPE|ERROR_CODE|FILE|LINE|COLUMN|LENGTH|MESSAGE
|
|
332
|
+
// SEVERITY is INFO | WARNING | ERROR. Message pipes are backslash-escaped, so
|
|
333
|
+
// reading the first field (the severity) is unambiguous.
|
|
334
|
+
let errors = 0;
|
|
335
|
+
let warnings = 0;
|
|
336
|
+
let info = 0;
|
|
337
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
338
|
+
const pipe = line.indexOf('|');
|
|
339
|
+
if (pipe === -1) {
|
|
340
|
+
continue; // not a machine-format finding line (banner / blank)
|
|
341
|
+
}
|
|
342
|
+
const sev = line.slice(0, pipe).trim().toUpperCase();
|
|
343
|
+
if (sev === 'ERROR') {
|
|
344
|
+
errors++;
|
|
345
|
+
}
|
|
346
|
+
else if (sev === 'WARNING') {
|
|
347
|
+
warnings++;
|
|
348
|
+
}
|
|
349
|
+
else if (sev === 'INFO') {
|
|
350
|
+
info++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
tool_id: 'dart-analyze',
|
|
355
|
+
summary: { tier: 'counts', counts: { errors, warnings, info } },
|
|
356
|
+
oneliner: `${errors} errors, ${warnings} warnings`,
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
const dartPubOutdatedParser = (stdout) => {
|
|
360
|
+
// `dart pub outdated --json` →
|
|
361
|
+
// { packages: [{ package, current: {version} | null, latest: {version} | null }] }
|
|
362
|
+
const data = safeJson(stdout);
|
|
363
|
+
const pkgs = data?.packages ?? [];
|
|
364
|
+
const count = pkgs.filter((p) => {
|
|
365
|
+
const cur = p.current?.version;
|
|
366
|
+
const latest = p.latest?.version;
|
|
367
|
+
return Boolean(cur) && Boolean(latest) && cur !== latest;
|
|
368
|
+
}).length;
|
|
369
|
+
return {
|
|
370
|
+
tool_id: 'dart-pub-outdated',
|
|
371
|
+
summary: {
|
|
372
|
+
tier: 'counts',
|
|
373
|
+
counts: { errors: 0, warnings: count, info: 0 },
|
|
374
|
+
},
|
|
375
|
+
oneliner: `${count} outdated packages`,
|
|
376
|
+
};
|
|
377
|
+
};
|
|
329
378
|
// ---------------------------------------------------------------------------
|
|
330
379
|
// Tier 2 — counts + top-N findings (severity + file:line preserved)
|
|
331
380
|
// ---------------------------------------------------------------------------
|
|
@@ -891,6 +940,164 @@ const gocycloParser = (stdout, _stderr, ctx) => {
|
|
|
891
940
|
};
|
|
892
941
|
};
|
|
893
942
|
// ---------------------------------------------------------------------------
|
|
943
|
+
// Dependency graph (madge --json) — a bounded module graph for visualization
|
|
944
|
+
// ---------------------------------------------------------------------------
|
|
945
|
+
/** Max nodes kept in the persisted graph (cycle nodes prioritised). */
|
|
946
|
+
const MAX_GRAPH_NODES = 80;
|
|
947
|
+
/** Last two path segments — enough to disambiguate without the full path. */
|
|
948
|
+
function shortLabel(path) {
|
|
949
|
+
const parts = path.split('/');
|
|
950
|
+
return parts.slice(-2).join('/');
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Nodes that participate in an import cycle, via iterative Tarjan SCC (any SCC
|
|
954
|
+
* of size > 1, plus self-loops). Iterative to stay safe on large repos.
|
|
955
|
+
*/
|
|
956
|
+
function findCycleNodes(adj) {
|
|
957
|
+
const index = new Map();
|
|
958
|
+
const low = new Map();
|
|
959
|
+
const onStack = new Set();
|
|
960
|
+
const stack = [];
|
|
961
|
+
const cycleNodes = new Set();
|
|
962
|
+
let idx = 0;
|
|
963
|
+
for (const start of adj.keys()) {
|
|
964
|
+
if (index.has(start)) {
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const work = [{ node: start, i: 0 }];
|
|
968
|
+
while (work.length > 0) {
|
|
969
|
+
const frame = work[work.length - 1];
|
|
970
|
+
const node = frame.node;
|
|
971
|
+
if (frame.i === 0) {
|
|
972
|
+
index.set(node, idx);
|
|
973
|
+
low.set(node, idx);
|
|
974
|
+
idx++;
|
|
975
|
+
stack.push(node);
|
|
976
|
+
onStack.add(node);
|
|
977
|
+
}
|
|
978
|
+
const succs = adj.get(node) ?? [];
|
|
979
|
+
if (frame.i < succs.length) {
|
|
980
|
+
const w = succs[frame.i];
|
|
981
|
+
frame.i++;
|
|
982
|
+
if (!index.has(w)) {
|
|
983
|
+
work.push({ node: w, i: 0 });
|
|
984
|
+
}
|
|
985
|
+
else if (onStack.has(w)) {
|
|
986
|
+
low.set(node, Math.min(low.get(node) ?? 0, index.get(w) ?? 0));
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
if (low.get(node) === index.get(node)) {
|
|
991
|
+
const scc = [];
|
|
992
|
+
let w = '';
|
|
993
|
+
do {
|
|
994
|
+
w = stack.pop();
|
|
995
|
+
onStack.delete(w);
|
|
996
|
+
scc.push(w);
|
|
997
|
+
} while (w !== node);
|
|
998
|
+
if (scc.length > 1) {
|
|
999
|
+
for (const n of scc) {
|
|
1000
|
+
cycleNodes.add(n);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
else if ((adj.get(node) ?? []).includes(node)) {
|
|
1004
|
+
cycleNodes.add(node);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
work.pop();
|
|
1008
|
+
const parent = work[work.length - 1]?.node;
|
|
1009
|
+
if (parent !== undefined) {
|
|
1010
|
+
low.set(parent, Math.min(low.get(parent) ?? 0, low.get(node) ?? 0));
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return cycleNodes;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Build a bounded dependency graph from madge's adjacency map. Cycle nodes are
|
|
1019
|
+
* always kept; the remaining slots go to the highest-degree modules. Edges are
|
|
1020
|
+
* restricted to kept nodes.
|
|
1021
|
+
*/
|
|
1022
|
+
export function buildDependencyGraph(adjObj) {
|
|
1023
|
+
// Normalise: every referenced module is a node (targets may have no key).
|
|
1024
|
+
const adj = new Map();
|
|
1025
|
+
for (const [k, deps] of Object.entries(adjObj)) {
|
|
1026
|
+
const list = Array.isArray(deps) ? deps : [];
|
|
1027
|
+
if (!adj.has(k)) {
|
|
1028
|
+
adj.set(k, []);
|
|
1029
|
+
}
|
|
1030
|
+
adj.get(k).push(...list);
|
|
1031
|
+
for (const d of list) {
|
|
1032
|
+
if (!adj.has(d)) {
|
|
1033
|
+
adj.set(d, []);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
const fanOut = new Map();
|
|
1038
|
+
const fanIn = new Map();
|
|
1039
|
+
for (const [k, deps] of adj) {
|
|
1040
|
+
fanOut.set(k, deps.length);
|
|
1041
|
+
for (const d of deps) {
|
|
1042
|
+
fanIn.set(d, (fanIn.get(d) ?? 0) + 1);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
const cycleNodes = findCycleNodes(adj);
|
|
1046
|
+
const allIds = [...adj.keys()];
|
|
1047
|
+
const degree = (id) => (fanIn.get(id) ?? 0) + (fanOut.get(id) ?? 0);
|
|
1048
|
+
// Prioritise cycle nodes, then highest-degree modules, capped at the limit
|
|
1049
|
+
// so even a large strongly-connected cluster can't blow past the bound.
|
|
1050
|
+
const ranked = [...allIds].sort((a, b) => {
|
|
1051
|
+
const ca = cycleNodes.has(a) ? 1 : 0;
|
|
1052
|
+
const cb = cycleNodes.has(b) ? 1 : 0;
|
|
1053
|
+
return ca !== cb ? cb - ca : degree(b) - degree(a);
|
|
1054
|
+
});
|
|
1055
|
+
const kept = new Set(ranked.slice(0, MAX_GRAPH_NODES));
|
|
1056
|
+
const nodes = [...kept].map((id) => ({
|
|
1057
|
+
id,
|
|
1058
|
+
label: shortLabel(id),
|
|
1059
|
+
fan_in: fanIn.get(id) ?? 0,
|
|
1060
|
+
fan_out: fanOut.get(id) ?? 0,
|
|
1061
|
+
in_cycle: cycleNodes.has(id),
|
|
1062
|
+
}));
|
|
1063
|
+
const edges = [];
|
|
1064
|
+
const seen = new Set();
|
|
1065
|
+
for (const [from, deps] of adj) {
|
|
1066
|
+
if (!kept.has(from)) {
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
for (const to of deps) {
|
|
1070
|
+
if (!kept.has(to) || from === to) {
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
const key = `${from}\n${to}`;
|
|
1074
|
+
if (seen.has(key)) {
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
seen.add(key);
|
|
1078
|
+
edges.push({ from, to });
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return {
|
|
1082
|
+
nodes,
|
|
1083
|
+
edges,
|
|
1084
|
+
total_modules: allIds.length,
|
|
1085
|
+
truncated: allIds.length > kept.size,
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
const madgeGraphParser = (stdout) => {
|
|
1089
|
+
const adjObj = safeJson(stdout);
|
|
1090
|
+
const graph = adjObj
|
|
1091
|
+
? buildDependencyGraph(adjObj)
|
|
1092
|
+
: { nodes: [], edges: [], total_modules: 0, truncated: false };
|
|
1093
|
+
const cycleCount = graph.nodes.filter((n) => n.in_cycle).length;
|
|
1094
|
+
return {
|
|
1095
|
+
tool_id: 'madge-graph',
|
|
1096
|
+
summary: { tier: 'metrics', metrics: { graph } },
|
|
1097
|
+
oneliner: `${graph.total_modules} modules, ${cycleCount} in cycles`,
|
|
1098
|
+
};
|
|
1099
|
+
};
|
|
1100
|
+
// ---------------------------------------------------------------------------
|
|
894
1101
|
// Tier 3 — structured metrics
|
|
895
1102
|
// ---------------------------------------------------------------------------
|
|
896
1103
|
const sccParser = (stdout) => {
|
|
@@ -1051,6 +1258,8 @@ export const PARSERS = {
|
|
|
1051
1258
|
'go-mod-outdated': goModOutdatedParser,
|
|
1052
1259
|
'cargo-outdated': cargoOutdatedParser,
|
|
1053
1260
|
'dotnet-list-outdated': dotnetListOutdatedParser,
|
|
1261
|
+
'dart-analyze': dartAnalyzeParser,
|
|
1262
|
+
'dart-pub-outdated': dartPubOutdatedParser,
|
|
1054
1263
|
// Tier 2
|
|
1055
1264
|
semgrep: semgrepParser,
|
|
1056
1265
|
bandit: banditParser,
|
|
@@ -1072,6 +1281,7 @@ export const PARSERS = {
|
|
|
1072
1281
|
gocyclo: gocycloParser,
|
|
1073
1282
|
// Mixed (T3 metrics + T2 sub-findings)
|
|
1074
1283
|
jscpd: jscpdParser,
|
|
1284
|
+
'madge-graph': madgeGraphParser,
|
|
1075
1285
|
// Tier 3
|
|
1076
1286
|
scc: sccParser,
|
|
1077
1287
|
lizard: lizardParser,
|
|
@@ -352,6 +352,7 @@ For each dimension, the rubric specifies:
|
|
|
352
352
|
| JS/TS | 60 | 100 |
|
|
353
353
|
| Rust | 80 | 120 |
|
|
354
354
|
| Ruby | 30 | 60 |
|
|
355
|
+
| Dart | 60 | 100 |
|
|
355
356
|
|
|
356
357
|
These are measurement calibration, not different standards.
|
|
357
358
|
|
|
@@ -575,6 +576,15 @@ madge:
|
|
|
575
576
|
parser: madge
|
|
576
577
|
subscores: [architecture.circular_deps]
|
|
577
578
|
|
|
579
|
+
madge-graph:
|
|
580
|
+
applies_to: [js, ts] # full module graph for visualization (bounded)
|
|
581
|
+
probe: command -v madge
|
|
582
|
+
install: null # use npx
|
|
583
|
+
command: npx --yes madge --json --extensions js,jsx,ts,tsx %REPO_ROOT%
|
|
584
|
+
timeout_minutes: 5
|
|
585
|
+
parser: madge-graph
|
|
586
|
+
subscores: [architecture.circular_deps]
|
|
587
|
+
|
|
578
588
|
depcheck:
|
|
579
589
|
applies_to: [js, ts]
|
|
580
590
|
probe: command -v depcheck
|
|
@@ -847,6 +857,33 @@ dotnet-list-outdated:
|
|
|
847
857
|
subscores: [dependency_health.freshness]
|
|
848
858
|
```
|
|
849
859
|
|
|
860
|
+
### Dart / Flutter
|
|
861
|
+
|
|
862
|
+
Both tools ship with the Dart (and Flutter) SDK — no extra install. We never
|
|
863
|
+
auto-install an SDK, so on a machine without `dart` they degrade to unmeasured.
|
|
864
|
+
`dart analyze` is AST-based (same analyzer as the IDE plugins); duplication is
|
|
865
|
+
covered by the polyglot `jscpd` entry (Dart is in its `applies_to`).
|
|
866
|
+
|
|
867
|
+
```
|
|
868
|
+
dart-analyze:
|
|
869
|
+
applies_to: [dart]
|
|
870
|
+
probe: command -v dart
|
|
871
|
+
install: null # bundled with the Dart / Flutter SDK
|
|
872
|
+
command: dart analyze %REPO_ROOT% --format=machine
|
|
873
|
+
timeout_minutes: 5
|
|
874
|
+
parser: dart-analyze
|
|
875
|
+
subscores: [code_quality.lint]
|
|
876
|
+
|
|
877
|
+
dart-pub-outdated:
|
|
878
|
+
applies_to: [dart] # only when pubspec.yaml present
|
|
879
|
+
probe: command -v dart
|
|
880
|
+
install: null # bundled with the Dart / Flutter SDK
|
|
881
|
+
command: dart pub outdated --json
|
|
882
|
+
timeout_minutes: 3
|
|
883
|
+
parser: dart-pub-outdated
|
|
884
|
+
subscores: [dependency_health.freshness]
|
|
885
|
+
```
|
|
886
|
+
|
|
850
887
|
### Multi-language
|
|
851
888
|
|
|
852
889
|
```
|
|
@@ -58,7 +58,7 @@ const jscpd = {
|
|
|
58
58
|
id: 'jscpd',
|
|
59
59
|
label: 'jscpd (duplication)',
|
|
60
60
|
category: 'duplication',
|
|
61
|
-
applies_to: ['js', 'ts', 'py', 'java', 'go', 'cs', 'ruby'],
|
|
61
|
+
applies_to: ['js', 'ts', 'py', 'java', 'go', 'cs', 'ruby', 'dart'],
|
|
62
62
|
probe: 'command -v jscpd',
|
|
63
63
|
install: null, // always use npx
|
|
64
64
|
install_prereq: 'npx',
|
|
@@ -82,6 +82,20 @@ const madge = {
|
|
|
82
82
|
subscores: ['architecture.circular_deps'],
|
|
83
83
|
tolerate_nonzero_exit: true,
|
|
84
84
|
};
|
|
85
|
+
const madgeGraph = {
|
|
86
|
+
id: 'madge-graph',
|
|
87
|
+
label: 'madge (dependency graph)',
|
|
88
|
+
category: 'cycles',
|
|
89
|
+
applies_to: ['js', 'ts'],
|
|
90
|
+
probe: 'command -v madge',
|
|
91
|
+
install: null,
|
|
92
|
+
install_prereq: 'npx',
|
|
93
|
+
command: 'npx --yes madge --json --extensions js,jsx,ts,tsx %REPO_ROOT%',
|
|
94
|
+
timeout_minutes: 5,
|
|
95
|
+
parser: 'madge-graph',
|
|
96
|
+
subscores: ['architecture.circular_deps'],
|
|
97
|
+
tolerate_nonzero_exit: true,
|
|
98
|
+
};
|
|
85
99
|
const depcheck = {
|
|
86
100
|
id: 'depcheck',
|
|
87
101
|
label: 'depcheck (unused deps)',
|
|
@@ -470,6 +484,45 @@ const dotnetListOutdated = {
|
|
|
470
484
|
tolerate_nonzero_exit: true,
|
|
471
485
|
};
|
|
472
486
|
// ---------------------------------------------------------------------------
|
|
487
|
+
// Dart / Flutter
|
|
488
|
+
//
|
|
489
|
+
// Both tools ship with the Dart (and Flutter) SDK itself — no extra install,
|
|
490
|
+
// no pipx/go/cargo. We never auto-install an SDK (`install: null`), so on a
|
|
491
|
+
// machine without Dart they degrade to unmeasured rather than triggering a
|
|
492
|
+
// multi-hundred-MB toolchain download. `dart analyze` is AST-based (the same
|
|
493
|
+
// analyzer the IDE plugins use); duplication is covered by the polyglot jscpd
|
|
494
|
+
// entry above (Dart is in its `applies_to`).
|
|
495
|
+
// ---------------------------------------------------------------------------
|
|
496
|
+
const dartAnalyze = {
|
|
497
|
+
id: 'dart-analyze',
|
|
498
|
+
label: 'dart analyze',
|
|
499
|
+
category: 'lint',
|
|
500
|
+
applies_to: ['dart'],
|
|
501
|
+
probe: 'command -v dart',
|
|
502
|
+
install: null, // bundled with the Dart / Flutter SDK
|
|
503
|
+
install_prereq: null,
|
|
504
|
+
command: 'dart analyze %REPO_ROOT% --format=machine',
|
|
505
|
+
timeout_minutes: 5,
|
|
506
|
+
parser: 'dart-analyze',
|
|
507
|
+
subscores: ['code_quality.lint'],
|
|
508
|
+
tolerate_nonzero_exit: true, // exits 1/2/3 when analysis issues are found
|
|
509
|
+
};
|
|
510
|
+
const dartPubOutdated = {
|
|
511
|
+
id: 'dart-pub-outdated',
|
|
512
|
+
label: 'dart pub outdated',
|
|
513
|
+
category: 'dep-outdated',
|
|
514
|
+
applies_to: ['dart'],
|
|
515
|
+
probe: 'command -v dart',
|
|
516
|
+
install: null, // bundled with the Dart / Flutter SDK
|
|
517
|
+
install_prereq: null,
|
|
518
|
+
command: 'dart pub outdated --json',
|
|
519
|
+
timeout_minutes: 3,
|
|
520
|
+
parser: 'dart-pub-outdated',
|
|
521
|
+
subscores: ['dependency_health.freshness'],
|
|
522
|
+
tolerate_nonzero_exit: true,
|
|
523
|
+
requires: { file_present: ['pubspec.yaml'] },
|
|
524
|
+
};
|
|
525
|
+
// ---------------------------------------------------------------------------
|
|
473
526
|
// Multi-language / polyglot
|
|
474
527
|
// ---------------------------------------------------------------------------
|
|
475
528
|
const semgrep = {
|
|
@@ -549,6 +602,7 @@ export const TOOL_CATALOG = [
|
|
|
549
602
|
tscTypecheck,
|
|
550
603
|
jscpd,
|
|
551
604
|
madge,
|
|
605
|
+
madgeGraph,
|
|
552
606
|
depcheck,
|
|
553
607
|
npmAudit,
|
|
554
608
|
pnpmAudit,
|
|
@@ -580,6 +634,9 @@ export const TOOL_CATALOG = [
|
|
|
580
634
|
// C# / .NET
|
|
581
635
|
dotnetListVulnerable,
|
|
582
636
|
dotnetListOutdated,
|
|
637
|
+
// Dart / Flutter
|
|
638
|
+
dartAnalyze,
|
|
639
|
+
dartPubOutdated,
|
|
583
640
|
// Polyglot
|
|
584
641
|
semgrep,
|
|
585
642
|
gitleaks,
|
|
@@ -29,7 +29,7 @@ export interface DetectedContext {
|
|
|
29
29
|
total_loc_approx: number;
|
|
30
30
|
}
|
|
31
31
|
/** Language tag used to gate tool applicability. `'all'` = polyglot tools. */
|
|
32
|
-
export type LanguageTag = 'js' | 'ts' | 'py' | 'go' | 'rust' | 'java' | 'kotlin' | 'ruby' | 'c' | 'cpp' | 'cs' | 'swift' | 'all';
|
|
32
|
+
export type LanguageTag = 'js' | 'ts' | 'py' | 'go' | 'rust' | 'java' | 'kotlin' | 'ruby' | 'c' | 'cpp' | 'cs' | 'swift' | 'dart' | 'all';
|
|
33
33
|
export type ToolCategory = 'lint' | 'sast' | 'duplication' | 'complexity' | 'dead-code' | 'cycles' | 'coverage' | 'dep-vuln' | 'dep-outdated' | 'dep-unused' | 'dep-license' | 'secrets' | 'loc-stats' | 'typecheck';
|
|
34
34
|
export type InstallerPrereq = 'pipx' | 'go' | 'cargo' | 'npx' | 'gem' | null;
|
|
35
35
|
export interface ToolCatalogEntry {
|
|
@@ -102,6 +102,13 @@ export interface ToolRunOutput {
|
|
|
102
102
|
stderr_tail?: string;
|
|
103
103
|
/** Path on disk where the full tool output is saved for audit. */
|
|
104
104
|
raw_output_path?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Structured metrics from a tier-3 (`metrics`) parser, persisted so trend
|
|
107
|
+
* charts can read numeric values (e.g. jscpd `duplication_pct`, lizard
|
|
108
|
+
* `avg_cyclomatic_complexity`) without re-parsing the oneliner. Absent for
|
|
109
|
+
* count/finding tools.
|
|
110
|
+
*/
|
|
111
|
+
metrics?: Record<string, unknown>;
|
|
105
112
|
}
|
|
106
113
|
/**
|
|
107
114
|
* Tier 1 — counts only. Used for style/quality linters where individual
|