@harness-engineering/cli 2.4.5 → 2.5.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/agents-md-KAD3L2NO.js +13 -0
- package/dist/analyzer-VY6DJVOU-NI56JRFK.js +26 -0
- package/dist/{architecture-NX7FWQD3.js → architecture-OC7TFCFF.js} +7 -4
- package/dist/{assess-project-PLY6ZWMW.js → assess-project-ZGMRPTS3.js} +1 -1
- package/dist/bin/harness-mcp.js +20 -17
- package/dist/bin/harness.js +38 -35
- package/dist/{check-phase-gate-WFIEJKMN.js → check-phase-gate-PD5MVEZT.js} +8 -5
- package/dist/{chunk-O63AAPIZ.js → chunk-2LPUIBVH.js} +75 -1
- package/dist/{chunk-7TRTU7JL.js → chunk-2RNGLYOG.js} +2 -2
- package/dist/{chunk-73ACYXIS.js → chunk-4CWFVEVV.js} +1 -1
- package/dist/{chunk-4H2KIQ3I.js → chunk-57TS3UGN.js} +2 -2
- package/dist/{chunk-A5GXGRW6.js → chunk-65MJPWFN.js} +6 -6
- package/dist/{chunk-Z6PF2YXB.js → chunk-6SRKI77C.js} +3 -3
- package/dist/{chunk-LJTQVJEP.js → chunk-7DOGGXQU.js} +2 -2
- package/dist/{chunk-ZEKY6PQQ.js → chunk-A3EBWI7U.js} +2 -2
- package/dist/{chunk-TC4GQBIK.js → chunk-DOUS4ULX.js} +6 -6
- package/dist/{chunk-HCTXLKAH.js → chunk-ELMV6QU2.js} +2 -2
- package/dist/{chunk-KEF624FQ.js → chunk-HIZXUWWC.js} +8 -6
- package/dist/{chunk-YL2N4OZQ.js → chunk-IGBQIXPS.js} +2 -2
- package/dist/chunk-IWRYCUDO.js +3337 -0
- package/dist/{chunk-NSLFLGUB.js → chunk-IXKGMPMT.js} +2 -2
- package/dist/{chunk-M5TJOV3A.js → chunk-JE5GQVCQ.js} +2 -2
- package/dist/{chunk-65M5R3X6.js → chunk-KQ6TDRPS.js} +3927 -7322
- package/dist/{chunk-DUK6C7I7.js → chunk-LT244Y6E.js} +490 -300
- package/dist/{chunk-OPDIMKUZ.js → chunk-N6VWCISQ.js} +2 -2
- package/dist/{chunk-3XFFGAX4.js → chunk-NOOPJD5R.js} +3 -3
- package/dist/chunk-QCLOIUMX.js +395 -0
- package/dist/{chunk-FPUNXFJG.js → chunk-TCSNO7YW.js} +2 -2
- package/dist/{chunk-4PO2QAXO.js → chunk-UCL3UZIC.js} +779 -274
- package/dist/{chunk-DUCVDDZO.js → chunk-UF7BKRH4.js} +4 -4
- package/dist/chunk-XARGEOJV.js +21 -0
- package/dist/{chunk-72TZ2GZR.js → chunk-XIW24BJY.js} +7 -7
- package/dist/{chunk-PGY57ABC.js → chunk-XSI3RDH6.js} +7 -7
- package/dist/ci-workflow-WAG4TOV6.js +13 -0
- package/dist/{dist-O733ZU6I.js → dist-HVJUGPSY.js} +23 -1
- package/dist/{dist-SVZHSOLP.js → dist-TKHVYUWZ.js} +79 -48
- package/dist/{docs-7LXCR62T.js → docs-4WPBYSCF.js} +8 -5
- package/dist/engine-5BXDQT5T.js +13 -0
- package/dist/{entropy-LMOXWMR4.js → entropy-7FIUFACI.js} +6 -3
- package/dist/{feedback-CLBC3IMP.js → feedback-ER2HRTFD.js} +1 -1
- package/dist/{generate-agent-definitions-5FDIGEVZ.js → generate-agent-definitions-5PV4O3BD.js} +8 -5
- package/dist/index.js +49 -46
- package/dist/loader-6RTEYN5Q.js +15 -0
- package/dist/{mcp-4W45E6OH.js → mcp-QWSDEYMN.js} +20 -17
- package/dist/{performance-WZQIUHLR.js → performance-AZJAT35R.js} +8 -5
- package/dist/{review-pipeline-YPRUHKM7.js → review-pipeline-4VF73V4V.js} +6 -3
- package/dist/runtime-SAZGLILX.js +14 -0
- package/dist/{security-AZNLX3N6.js → security-ZWRABJNE.js} +1 -1
- package/dist/timeline-manager-FPYKJRHR-VVX7CSGW.js +24 -0
- package/dist/{tool-tiers-QVWKAZX4.js → tool-tiers-LTQNNAFU.js} +7 -2
- package/dist/{validate-S7G6H7F4.js → validate-AFJVI4OX.js} +7 -4
- package/dist/validate-cross-check-7PO7CBVT.js +13 -0
- package/package.json +6 -6
- package/dist/agents-md-KJVGWF2S.js +0 -10
- package/dist/ci-workflow-XDIKOFSN.js +0 -10
- package/dist/engine-MMQF2ZW5.js +0 -10
- package/dist/loader-WTWR2CWT.js +0 -12
- package/dist/runtime-WC2IWI2X.js +0 -11
- package/dist/validate-cross-check-Z73SCHXZ.js +0 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveProjectConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7DOGGXQU.js";
|
|
4
4
|
import {
|
|
5
5
|
sanitizePath
|
|
6
6
|
} from "./chunk-W6Y7ZW3Y.js";
|
|
@@ -49,7 +49,7 @@ async function handleValidateProject(input) {
|
|
|
49
49
|
checks.config = "pass";
|
|
50
50
|
const config = configResult.value;
|
|
51
51
|
try {
|
|
52
|
-
const core = await import("./dist-
|
|
52
|
+
const core = await import("./dist-TKHVYUWZ.js");
|
|
53
53
|
if (typeof core.validateFileStructure === "function" && Array.isArray(config.conventions)) {
|
|
54
54
|
const conventions = config.conventions;
|
|
55
55
|
const structureResult = await core.validateFileStructure(projectPath, conventions);
|
|
@@ -68,7 +68,7 @@ async function handleValidateProject(input) {
|
|
|
68
68
|
} catch {
|
|
69
69
|
}
|
|
70
70
|
try {
|
|
71
|
-
const core = await import("./dist-
|
|
71
|
+
const core = await import("./dist-TKHVYUWZ.js");
|
|
72
72
|
if (typeof core.validateAgentsMap === "function") {
|
|
73
73
|
const agentsMapPath = path.join(projectPath, "AGENTS.md");
|
|
74
74
|
const agentsResult = await core.validateAgentsMap(agentsMapPath);
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__esm
|
|
3
|
+
} from "./chunk-KFQGP6VL.js";
|
|
4
|
+
|
|
5
|
+
// ../core/dist/chunk-IIEDD47I.mjs
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
var ArchMetricCategorySchema, ViolationSchema, MetricResultSchema, CategoryBaselineSchema, ArchBaselineSchema, CategoryRegressionSchema, ArchDiffResultSchema, ThresholdConfigSchema, ArchConfigSchema, ConstraintRuleSchema, ViolationSnapshotSchema, ViolationHistorySchema, EmergenceConfidenceSchema, EmergentConstraintSuggestionSchema, EmergenceResultSchema;
|
|
8
|
+
var init_chunk_IIEDD47I = __esm({
|
|
9
|
+
"../core/dist/chunk-IIEDD47I.mjs"() {
|
|
10
|
+
"use strict";
|
|
11
|
+
ArchMetricCategorySchema = z.enum([
|
|
12
|
+
"circular-deps",
|
|
13
|
+
"layer-violations",
|
|
14
|
+
"complexity",
|
|
15
|
+
"coupling",
|
|
16
|
+
"forbidden-imports",
|
|
17
|
+
"module-size",
|
|
18
|
+
"dependency-depth"
|
|
19
|
+
]);
|
|
20
|
+
ViolationSchema = z.object({
|
|
21
|
+
id: z.string(),
|
|
22
|
+
// stable hash: sha256(relativePath + ':' + category + ':' + normalizedDetail)
|
|
23
|
+
file: z.string(),
|
|
24
|
+
// relative to project root
|
|
25
|
+
category: ArchMetricCategorySchema.optional(),
|
|
26
|
+
// context for baseline reporting
|
|
27
|
+
detail: z.string(),
|
|
28
|
+
// human-readable description
|
|
29
|
+
severity: z.enum(["error", "warning"])
|
|
30
|
+
});
|
|
31
|
+
MetricResultSchema = z.object({
|
|
32
|
+
category: ArchMetricCategorySchema,
|
|
33
|
+
scope: z.string(),
|
|
34
|
+
// e.g., 'project', 'src/services', 'src/api/routes.ts'
|
|
35
|
+
value: z.number(),
|
|
36
|
+
// numeric metric (violation count, complexity score, etc.)
|
|
37
|
+
violations: z.array(ViolationSchema),
|
|
38
|
+
metadata: z.record(z.unknown()).optional()
|
|
39
|
+
});
|
|
40
|
+
CategoryBaselineSchema = z.object({
|
|
41
|
+
value: z.number(),
|
|
42
|
+
// aggregate metric value at baseline time
|
|
43
|
+
violationIds: z.array(z.string())
|
|
44
|
+
// stable IDs of known violations (the allowlist)
|
|
45
|
+
});
|
|
46
|
+
ArchBaselineSchema = z.object({
|
|
47
|
+
version: z.literal(1),
|
|
48
|
+
updatedAt: z.string().datetime(),
|
|
49
|
+
// ISO 8601
|
|
50
|
+
updatedFrom: z.string(),
|
|
51
|
+
// commit hash
|
|
52
|
+
metrics: z.record(ArchMetricCategorySchema, CategoryBaselineSchema)
|
|
53
|
+
});
|
|
54
|
+
CategoryRegressionSchema = z.object({
|
|
55
|
+
category: ArchMetricCategorySchema,
|
|
56
|
+
baselineValue: z.number(),
|
|
57
|
+
currentValue: z.number(),
|
|
58
|
+
delta: z.number()
|
|
59
|
+
});
|
|
60
|
+
ArchDiffResultSchema = z.object({
|
|
61
|
+
passed: z.boolean(),
|
|
62
|
+
newViolations: z.array(ViolationSchema),
|
|
63
|
+
// in current but not in baseline -> FAIL
|
|
64
|
+
resolvedViolations: z.array(z.string()),
|
|
65
|
+
// in baseline but not in current -> celebrate
|
|
66
|
+
preExisting: z.array(z.string()),
|
|
67
|
+
// in both -> allowed, tracked
|
|
68
|
+
regressions: z.array(CategoryRegressionSchema)
|
|
69
|
+
// aggregate value exceeded baseline
|
|
70
|
+
});
|
|
71
|
+
ThresholdConfigSchema = z.record(
|
|
72
|
+
ArchMetricCategorySchema,
|
|
73
|
+
z.union([z.number(), z.record(z.string(), z.number())])
|
|
74
|
+
);
|
|
75
|
+
ArchConfigSchema = z.object({
|
|
76
|
+
enabled: z.boolean().default(true),
|
|
77
|
+
baselinePath: z.string().default(".harness/arch/baselines.json"),
|
|
78
|
+
thresholds: ThresholdConfigSchema.default({}),
|
|
79
|
+
modules: z.record(z.string(), ThresholdConfigSchema).default({})
|
|
80
|
+
});
|
|
81
|
+
ConstraintRuleSchema = z.object({
|
|
82
|
+
id: z.string(),
|
|
83
|
+
// stable hash: sha256(category + ':' + scope + ':' + description)
|
|
84
|
+
category: ArchMetricCategorySchema,
|
|
85
|
+
description: z.string(),
|
|
86
|
+
// e.g., "Layer 'services' must not import from 'ui'"
|
|
87
|
+
scope: z.string(),
|
|
88
|
+
// e.g., 'src/services/', 'project'
|
|
89
|
+
targets: z.array(z.string()).optional()
|
|
90
|
+
// forward-compat for governs edges
|
|
91
|
+
});
|
|
92
|
+
ViolationSnapshotSchema = z.object({
|
|
93
|
+
timestamp: z.string().datetime(),
|
|
94
|
+
violations: z.array(ViolationSchema)
|
|
95
|
+
});
|
|
96
|
+
ViolationHistorySchema = z.object({
|
|
97
|
+
version: z.literal(1),
|
|
98
|
+
snapshots: z.array(ViolationSnapshotSchema)
|
|
99
|
+
});
|
|
100
|
+
EmergenceConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
101
|
+
EmergentConstraintSuggestionSchema = z.object({
|
|
102
|
+
suggestedRule: ConstraintRuleSchema,
|
|
103
|
+
confidence: EmergenceConfidenceSchema,
|
|
104
|
+
occurrences: z.number(),
|
|
105
|
+
uniqueFiles: z.number(),
|
|
106
|
+
pattern: z.string(),
|
|
107
|
+
sampleViolations: z.array(ViolationSchema),
|
|
108
|
+
rationale: z.string()
|
|
109
|
+
});
|
|
110
|
+
EmergenceResultSchema = z.object({
|
|
111
|
+
suggestions: z.array(EmergentConstraintSuggestionSchema),
|
|
112
|
+
totalViolationsAnalyzed: z.number(),
|
|
113
|
+
windowWeeks: z.number(),
|
|
114
|
+
minOccurrences: z.number()
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// ../core/dist/chunk-XKLVJIPL.mjs
|
|
120
|
+
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync } from "fs";
|
|
121
|
+
import { randomBytes } from "crypto";
|
|
122
|
+
import { join, dirname } from "path";
|
|
123
|
+
import { z as z2 } from "zod";
|
|
124
|
+
var CategorySnapshotSchema, TimelineSnapshotSchema, TimelineFileSchema, TrendLineSchema, TrendResultSchema, DEFAULT_STABILITY_THRESHOLDS, ALL_CATEGORIES, TimelineManager;
|
|
125
|
+
var init_chunk_XKLVJIPL = __esm({
|
|
126
|
+
"../core/dist/chunk-XKLVJIPL.mjs"() {
|
|
127
|
+
"use strict";
|
|
128
|
+
init_chunk_IIEDD47I();
|
|
129
|
+
CategorySnapshotSchema = z2.object({
|
|
130
|
+
/** Aggregate metric value (e.g., violation count, avg complexity) */
|
|
131
|
+
value: z2.number(),
|
|
132
|
+
/** Count of violations in this category */
|
|
133
|
+
violationCount: z2.number()
|
|
134
|
+
});
|
|
135
|
+
TimelineSnapshotSchema = z2.object({
|
|
136
|
+
/** ISO 8601 timestamp of capture */
|
|
137
|
+
capturedAt: z2.string().datetime(),
|
|
138
|
+
/** Git commit hash at capture time */
|
|
139
|
+
commitHash: z2.string(),
|
|
140
|
+
/** Composite stability score (0-100, higher is healthier) */
|
|
141
|
+
stabilityScore: z2.number().min(0).max(100),
|
|
142
|
+
/** Per-category metric aggregates */
|
|
143
|
+
metrics: z2.record(ArchMetricCategorySchema, CategorySnapshotSchema)
|
|
144
|
+
});
|
|
145
|
+
TimelineFileSchema = z2.object({
|
|
146
|
+
version: z2.literal(1),
|
|
147
|
+
snapshots: z2.array(TimelineSnapshotSchema)
|
|
148
|
+
});
|
|
149
|
+
TrendLineSchema = z2.object({
|
|
150
|
+
/** Current value */
|
|
151
|
+
current: z2.number(),
|
|
152
|
+
/** Previous value (from comparison snapshot) */
|
|
153
|
+
previous: z2.number(),
|
|
154
|
+
/** Absolute delta (current - previous) */
|
|
155
|
+
delta: z2.number(),
|
|
156
|
+
/** Direction indicator */
|
|
157
|
+
direction: z2.enum(["improving", "stable", "declining"])
|
|
158
|
+
});
|
|
159
|
+
TrendResultSchema = z2.object({
|
|
160
|
+
/** Overall stability trend */
|
|
161
|
+
stability: TrendLineSchema,
|
|
162
|
+
/** Per-category trends */
|
|
163
|
+
categories: z2.record(ArchMetricCategorySchema, TrendLineSchema),
|
|
164
|
+
/** Number of snapshots analyzed */
|
|
165
|
+
snapshotCount: z2.number(),
|
|
166
|
+
/** Time range covered */
|
|
167
|
+
from: z2.string(),
|
|
168
|
+
to: z2.string()
|
|
169
|
+
});
|
|
170
|
+
DEFAULT_STABILITY_THRESHOLDS = {
|
|
171
|
+
"circular-deps": 5,
|
|
172
|
+
"layer-violations": 10,
|
|
173
|
+
complexity: 100,
|
|
174
|
+
coupling: 2,
|
|
175
|
+
"forbidden-imports": 5,
|
|
176
|
+
"module-size": 10,
|
|
177
|
+
"dependency-depth": 10
|
|
178
|
+
};
|
|
179
|
+
ALL_CATEGORIES = ArchMetricCategorySchema.options;
|
|
180
|
+
TimelineManager = class {
|
|
181
|
+
timelinePath;
|
|
182
|
+
constructor(rootDir) {
|
|
183
|
+
this.timelinePath = join(rootDir, ".harness", "arch", "timeline.json");
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Load timeline from disk.
|
|
187
|
+
* Returns empty TimelineFile if file does not exist or is invalid.
|
|
188
|
+
*/
|
|
189
|
+
load() {
|
|
190
|
+
if (!existsSync(this.timelinePath)) {
|
|
191
|
+
return { version: 1, snapshots: [] };
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const raw = readFileSync(this.timelinePath, "utf-8");
|
|
195
|
+
const data = JSON.parse(raw);
|
|
196
|
+
const parsed = TimelineFileSchema.safeParse(data);
|
|
197
|
+
if (!parsed.success) {
|
|
198
|
+
console.error(
|
|
199
|
+
`Timeline validation failed for ${this.timelinePath}:`,
|
|
200
|
+
parsed.error.format()
|
|
201
|
+
);
|
|
202
|
+
return { version: 1, snapshots: [] };
|
|
203
|
+
}
|
|
204
|
+
return parsed.data;
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(`Error loading timeline from ${this.timelinePath}:`, error);
|
|
207
|
+
return { version: 1, snapshots: [] };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Save timeline to disk using atomic write (temp file + rename).
|
|
212
|
+
* Creates parent directories if they do not exist.
|
|
213
|
+
*/
|
|
214
|
+
save(timeline) {
|
|
215
|
+
const dir = dirname(this.timelinePath);
|
|
216
|
+
if (!existsSync(dir)) {
|
|
217
|
+
mkdirSync(dir, { recursive: true });
|
|
218
|
+
}
|
|
219
|
+
const tmp = this.timelinePath + "." + randomBytes(4).toString("hex") + ".tmp";
|
|
220
|
+
writeFileSync(tmp, JSON.stringify(timeline, null, 2));
|
|
221
|
+
renameSync(tmp, this.timelinePath);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Capture a new snapshot from current metric results.
|
|
225
|
+
* Aggregates MetricResult[] by category, computes stability score,
|
|
226
|
+
* appends to timeline (or replaces if same commitHash), and saves.
|
|
227
|
+
*/
|
|
228
|
+
capture(results, commitHash) {
|
|
229
|
+
const metrics = this.aggregateByCategory(results);
|
|
230
|
+
const stabilityScore = this.computeStabilityScore(metrics);
|
|
231
|
+
const snapshot = {
|
|
232
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
233
|
+
commitHash,
|
|
234
|
+
stabilityScore,
|
|
235
|
+
metrics
|
|
236
|
+
};
|
|
237
|
+
const timeline = this.load();
|
|
238
|
+
const lastIndex = timeline.snapshots.length - 1;
|
|
239
|
+
if (lastIndex >= 0 && timeline.snapshots[lastIndex].commitHash === commitHash) {
|
|
240
|
+
timeline.snapshots[lastIndex] = snapshot;
|
|
241
|
+
} else {
|
|
242
|
+
timeline.snapshots.push(snapshot);
|
|
243
|
+
}
|
|
244
|
+
this.save(timeline);
|
|
245
|
+
return snapshot;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Compute trends between snapshots over a window.
|
|
249
|
+
* @param options.last - Number of recent snapshots to analyze (default: 10)
|
|
250
|
+
* @param options.since - ISO date string to filter snapshots from
|
|
251
|
+
*/
|
|
252
|
+
trends(options) {
|
|
253
|
+
const timeline = this.load();
|
|
254
|
+
let snapshots = timeline.snapshots;
|
|
255
|
+
if (options?.since) {
|
|
256
|
+
const sinceDate = new Date(options.since);
|
|
257
|
+
snapshots = snapshots.filter((s) => new Date(s.capturedAt) >= sinceDate);
|
|
258
|
+
}
|
|
259
|
+
if (options?.last && snapshots.length > options.last) {
|
|
260
|
+
snapshots = snapshots.slice(-options.last);
|
|
261
|
+
}
|
|
262
|
+
if (snapshots.length === 0) {
|
|
263
|
+
return this.emptyTrendResult();
|
|
264
|
+
}
|
|
265
|
+
if (snapshots.length === 1) {
|
|
266
|
+
const only = snapshots[0];
|
|
267
|
+
const m = only.metrics;
|
|
268
|
+
return {
|
|
269
|
+
stability: this.buildTrendLine(only.stabilityScore, only.stabilityScore, true),
|
|
270
|
+
categories: this.buildCategoryTrends(m, m),
|
|
271
|
+
snapshotCount: 1,
|
|
272
|
+
from: only.capturedAt,
|
|
273
|
+
to: only.capturedAt
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const first = snapshots[0];
|
|
277
|
+
const last = snapshots[snapshots.length - 1];
|
|
278
|
+
return {
|
|
279
|
+
stability: this.buildTrendLine(last.stabilityScore, first.stabilityScore, true),
|
|
280
|
+
categories: this.buildCategoryTrends(
|
|
281
|
+
last.metrics,
|
|
282
|
+
first.metrics
|
|
283
|
+
),
|
|
284
|
+
snapshotCount: snapshots.length,
|
|
285
|
+
from: first.capturedAt,
|
|
286
|
+
to: last.capturedAt
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Compute composite stability score from category metrics.
|
|
291
|
+
* Equal weight across all categories. Score is 0-100 (higher = healthier).
|
|
292
|
+
* health = max(0, 1 - (value / threshold)) per category.
|
|
293
|
+
*/
|
|
294
|
+
computeStabilityScore(metrics, thresholds = DEFAULT_STABILITY_THRESHOLDS) {
|
|
295
|
+
const healthScores = [];
|
|
296
|
+
for (const category of ALL_CATEGORIES) {
|
|
297
|
+
const snapshot = metrics[category];
|
|
298
|
+
if (!snapshot) {
|
|
299
|
+
healthScores.push(1);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
const threshold = thresholds[category] ?? 10;
|
|
303
|
+
const health = Math.max(0, 1 - snapshot.value / threshold);
|
|
304
|
+
healthScores.push(health);
|
|
305
|
+
}
|
|
306
|
+
const mean = healthScores.reduce((sum, h) => sum + h, 0) / healthScores.length;
|
|
307
|
+
return Math.round(mean * 100);
|
|
308
|
+
}
|
|
309
|
+
// --- Private helpers ---
|
|
310
|
+
aggregateByCategory(results) {
|
|
311
|
+
const metrics = {};
|
|
312
|
+
for (const result of results) {
|
|
313
|
+
const existing = metrics[result.category];
|
|
314
|
+
if (existing) {
|
|
315
|
+
existing.value += result.value;
|
|
316
|
+
existing.violationCount += result.violations.length;
|
|
317
|
+
} else {
|
|
318
|
+
metrics[result.category] = {
|
|
319
|
+
value: result.value,
|
|
320
|
+
violationCount: result.violations.length
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const category of ALL_CATEGORIES) {
|
|
325
|
+
if (!metrics[category]) {
|
|
326
|
+
metrics[category] = { value: 0, violationCount: 0 };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return metrics;
|
|
330
|
+
}
|
|
331
|
+
buildTrendLine(current, previous, isStabilityScore) {
|
|
332
|
+
const delta = current - previous;
|
|
333
|
+
let direction;
|
|
334
|
+
if (Math.abs(delta) < 2) {
|
|
335
|
+
direction = "stable";
|
|
336
|
+
} else if (isStabilityScore) {
|
|
337
|
+
direction = delta > 0 ? "improving" : "declining";
|
|
338
|
+
} else {
|
|
339
|
+
direction = delta < 0 ? "improving" : "declining";
|
|
340
|
+
}
|
|
341
|
+
return { current, previous, delta, direction };
|
|
342
|
+
}
|
|
343
|
+
buildCategoryTrends(currentMetrics, previousMetrics) {
|
|
344
|
+
const trends = {};
|
|
345
|
+
for (const category of ALL_CATEGORIES) {
|
|
346
|
+
const current = currentMetrics[category]?.value ?? 0;
|
|
347
|
+
const previous = previousMetrics[category]?.value ?? 0;
|
|
348
|
+
trends[category] = this.buildTrendLine(current, previous, false);
|
|
349
|
+
}
|
|
350
|
+
return trends;
|
|
351
|
+
}
|
|
352
|
+
emptyTrendResult() {
|
|
353
|
+
const zeroLine = { current: 0, previous: 0, delta: 0, direction: "stable" };
|
|
354
|
+
const categories = {};
|
|
355
|
+
for (const category of ALL_CATEGORIES) {
|
|
356
|
+
categories[category] = { ...zeroLine };
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
stability: { ...zeroLine },
|
|
360
|
+
categories,
|
|
361
|
+
snapshotCount: 0,
|
|
362
|
+
from: "",
|
|
363
|
+
to: ""
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
export {
|
|
371
|
+
ArchMetricCategorySchema,
|
|
372
|
+
ViolationSchema,
|
|
373
|
+
MetricResultSchema,
|
|
374
|
+
CategoryBaselineSchema,
|
|
375
|
+
ArchBaselineSchema,
|
|
376
|
+
CategoryRegressionSchema,
|
|
377
|
+
ArchDiffResultSchema,
|
|
378
|
+
ThresholdConfigSchema,
|
|
379
|
+
ArchConfigSchema,
|
|
380
|
+
ConstraintRuleSchema,
|
|
381
|
+
ViolationSnapshotSchema,
|
|
382
|
+
ViolationHistorySchema,
|
|
383
|
+
EmergenceConfidenceSchema,
|
|
384
|
+
EmergentConstraintSuggestionSchema,
|
|
385
|
+
EmergenceResultSchema,
|
|
386
|
+
init_chunk_IIEDD47I,
|
|
387
|
+
CategorySnapshotSchema,
|
|
388
|
+
TimelineSnapshotSchema,
|
|
389
|
+
TimelineFileSchema,
|
|
390
|
+
TrendLineSchema,
|
|
391
|
+
TrendResultSchema,
|
|
392
|
+
DEFAULT_STABILITY_THRESHOLDS,
|
|
393
|
+
TimelineManager,
|
|
394
|
+
init_chunk_XKLVJIPL
|
|
395
|
+
};
|
|
@@ -27,7 +27,7 @@ var runSecurityScanDefinition = {
|
|
|
27
27
|
};
|
|
28
28
|
async function handleRunSecurityScan(input) {
|
|
29
29
|
try {
|
|
30
|
-
const core = await import("./dist-
|
|
30
|
+
const core = await import("./dist-TKHVYUWZ.js");
|
|
31
31
|
const projectRoot = sanitizePath(input.path);
|
|
32
32
|
let configData = {};
|
|
33
33
|
try {
|
|
@@ -114,7 +114,7 @@ var getSecurityTrendsDefinition = {
|
|
|
114
114
|
};
|
|
115
115
|
async function handleGetSecurityTrends(input) {
|
|
116
116
|
try {
|
|
117
|
-
const core = await import("./dist-
|
|
117
|
+
const core = await import("./dist-TKHVYUWZ.js");
|
|
118
118
|
const projectRoot = sanitizePath(input.path);
|
|
119
119
|
const manager = new core.SecurityTimelineManager(projectRoot);
|
|
120
120
|
const trendOptions = {};
|