@harness-engineering/core 0.13.0 → 0.13.1
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/architecture/matchers.d.mts +1 -1
- package/dist/architecture/matchers.d.ts +1 -1
- package/dist/architecture/matchers.js +27 -35
- package/dist/architecture/matchers.mjs +1 -1
- package/dist/{chunk-ZHGBWFYD.mjs → chunk-D6VFA6AS.mjs} +22 -29
- package/dist/index.d.mts +85 -83
- package/dist/index.d.ts +85 -83
- package/dist/index.js +300 -273
- package/dist/index.mjs +267 -235
- package/dist/{matchers-Dj1t5vpg.d.mts → matchers-D20x48U9.d.mts} +46 -46
- package/dist/{matchers-Dj1t5vpg.d.ts → matchers-D20x48U9.d.ts} +46 -46
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -36,11 +36,12 @@ import {
|
|
|
36
36
|
fileExists,
|
|
37
37
|
findFiles,
|
|
38
38
|
readFileContent,
|
|
39
|
+
relativePosix,
|
|
39
40
|
resolveFileToLayer,
|
|
40
41
|
runAll,
|
|
41
42
|
validateDependencies,
|
|
42
43
|
violationId
|
|
43
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-D6VFA6AS.mjs";
|
|
44
45
|
|
|
45
46
|
// src/index.ts
|
|
46
47
|
export * from "@harness-engineering/types";
|
|
@@ -365,7 +366,7 @@ async function validateAgentsMap(path20 = "./AGENTS.md") {
|
|
|
365
366
|
|
|
366
367
|
// src/context/doc-coverage.ts
|
|
367
368
|
import { minimatch } from "minimatch";
|
|
368
|
-
import { basename
|
|
369
|
+
import { basename } from "path";
|
|
369
370
|
function determineImportance(filePath) {
|
|
370
371
|
const name = basename(filePath).toLowerCase();
|
|
371
372
|
if (name === "index.ts" || name === "index.js" || name === "main.ts") {
|
|
@@ -405,7 +406,7 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
405
406
|
try {
|
|
406
407
|
const sourceFiles = await findFiles("**/*.{ts,js,tsx,jsx}", sourceDir);
|
|
407
408
|
const filteredSourceFiles = sourceFiles.filter((file) => {
|
|
408
|
-
const relativePath =
|
|
409
|
+
const relativePath = relativePosix(sourceDir, file);
|
|
409
410
|
return !excludePatterns.some((pattern) => {
|
|
410
411
|
return minimatch(relativePath, pattern, { dot: true }) || minimatch(file, pattern, { dot: true });
|
|
411
412
|
});
|
|
@@ -428,7 +429,7 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
428
429
|
const undocumented = [];
|
|
429
430
|
const gaps = [];
|
|
430
431
|
for (const sourceFile of filteredSourceFiles) {
|
|
431
|
-
const relativePath =
|
|
432
|
+
const relativePath = relativePosix(sourceDir, sourceFile);
|
|
432
433
|
const fileName = basename(sourceFile);
|
|
433
434
|
const isDocumented = documentedPaths.has(relativePath) || documentedPaths.has(fileName) || documentedPaths.has(`src/${relativePath}`);
|
|
434
435
|
if (isDocumented) {
|
|
@@ -464,7 +465,7 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
464
465
|
}
|
|
465
466
|
|
|
466
467
|
// src/context/knowledge-map.ts
|
|
467
|
-
import { join as join2, basename as basename2
|
|
468
|
+
import { join as join2, basename as basename2 } from "path";
|
|
468
469
|
function suggestFix(path20, existingFiles) {
|
|
469
470
|
const targetName = basename2(path20).toLowerCase();
|
|
470
471
|
const similar = existingFiles.find((file) => {
|
|
@@ -488,7 +489,7 @@ async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
|
488
489
|
totalLinks: agentsTotalLinks
|
|
489
490
|
} = agentsResult.value;
|
|
490
491
|
const existingFiles = await findFiles("**/*", rootDir);
|
|
491
|
-
const relativeExistingFiles = existingFiles.map((f) =>
|
|
492
|
+
const relativeExistingFiles = existingFiles.map((f) => relativePosix(rootDir, f));
|
|
492
493
|
const brokenLinks = agentsBrokenLinks.map((link) => {
|
|
493
494
|
const section = sections.find(
|
|
494
495
|
(s) => s.links.some((l) => l.path === link.path && l.line === link.line)
|
|
@@ -513,7 +514,7 @@ async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
|
513
514
|
}
|
|
514
515
|
|
|
515
516
|
// src/context/generate.ts
|
|
516
|
-
import {
|
|
517
|
+
import { basename as basename3, dirname as dirname2 } from "path";
|
|
517
518
|
var DEFAULT_SECTIONS = [
|
|
518
519
|
{
|
|
519
520
|
name: "Documentation",
|
|
@@ -529,7 +530,7 @@ var DEFAULT_SECTIONS = [
|
|
|
529
530
|
function groupByDirectory(files, rootDir) {
|
|
530
531
|
const groups = /* @__PURE__ */ new Map();
|
|
531
532
|
for (const file of files) {
|
|
532
|
-
const relativePath =
|
|
533
|
+
const relativePath = relativePosix(rootDir, file);
|
|
533
534
|
const dir = dirname2(relativePath);
|
|
534
535
|
if (!groups.has(dir)) {
|
|
535
536
|
groups.set(dir, []);
|
|
@@ -585,7 +586,7 @@ async function generateAgentsMap(config, graphSections) {
|
|
|
585
586
|
allFiles.push(...files);
|
|
586
587
|
}
|
|
587
588
|
const filteredFiles = allFiles.filter((file) => {
|
|
588
|
-
const relativePath =
|
|
589
|
+
const relativePath = relativePosix(rootDir, file);
|
|
589
590
|
return !matchesExcludePattern(relativePath, excludePaths);
|
|
590
591
|
});
|
|
591
592
|
lines.push("## Repository Structure");
|
|
@@ -613,11 +614,11 @@ async function generateAgentsMap(config, graphSections) {
|
|
|
613
614
|
}
|
|
614
615
|
const sectionFiles = await findFiles(section.pattern, rootDir);
|
|
615
616
|
const filteredSectionFiles = sectionFiles.filter((file) => {
|
|
616
|
-
const relativePath =
|
|
617
|
+
const relativePath = relativePosix(rootDir, file);
|
|
617
618
|
return !matchesExcludePattern(relativePath, excludePaths);
|
|
618
619
|
});
|
|
619
620
|
for (const file of filteredSectionFiles.slice(0, 20)) {
|
|
620
|
-
lines.push(formatFileLink(
|
|
621
|
+
lines.push(formatFileLink(relativePosix(rootDir, file)));
|
|
621
622
|
}
|
|
622
623
|
if (filteredSectionFiles.length > 20) {
|
|
623
624
|
lines.push(`- _... and ${filteredSectionFiles.length - 20} more files_`);
|
|
@@ -1572,7 +1573,7 @@ var TypeScriptParser = class {
|
|
|
1572
1573
|
};
|
|
1573
1574
|
|
|
1574
1575
|
// src/entropy/snapshot.ts
|
|
1575
|
-
import { join as join3, resolve
|
|
1576
|
+
import { join as join3, resolve } from "path";
|
|
1576
1577
|
import { minimatch as minimatch2 } from "minimatch";
|
|
1577
1578
|
async function resolveEntryPoints(rootDir, explicitEntries) {
|
|
1578
1579
|
if (explicitEntries && explicitEntries.length > 0) {
|
|
@@ -1836,7 +1837,7 @@ async function buildSnapshot(config) {
|
|
|
1836
1837
|
sourceFilePaths.push(...files2);
|
|
1837
1838
|
}
|
|
1838
1839
|
sourceFilePaths = sourceFilePaths.filter((f) => {
|
|
1839
|
-
const rel =
|
|
1840
|
+
const rel = relativePosix(rootDir, f);
|
|
1840
1841
|
return !excludePatterns.some((p) => minimatch2(rel, p));
|
|
1841
1842
|
});
|
|
1842
1843
|
const files = [];
|
|
@@ -2368,9 +2369,8 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
|
|
|
2368
2369
|
|
|
2369
2370
|
// src/entropy/detectors/patterns.ts
|
|
2370
2371
|
import { minimatch as minimatch3 } from "minimatch";
|
|
2371
|
-
import { relative as relative5 } from "path";
|
|
2372
2372
|
function fileMatchesPattern(filePath, pattern, rootDir) {
|
|
2373
|
-
const relativePath =
|
|
2373
|
+
const relativePath = relativePosix(rootDir, filePath);
|
|
2374
2374
|
return minimatch3(relativePath, pattern);
|
|
2375
2375
|
}
|
|
2376
2376
|
function checkConfigPattern(pattern, file, rootDir) {
|
|
@@ -6405,238 +6405,270 @@ var ALL_CHECKS = [
|
|
|
6405
6405
|
"phase-gate",
|
|
6406
6406
|
"arch"
|
|
6407
6407
|
];
|
|
6408
|
-
async function
|
|
6409
|
-
const start = Date.now();
|
|
6408
|
+
async function runValidateCheck(projectRoot, config) {
|
|
6410
6409
|
const issues = [];
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
|
|
6410
|
+
const agentsPath = path12.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
6411
|
+
const result = await validateAgentsMap(agentsPath);
|
|
6412
|
+
if (!result.ok) {
|
|
6413
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
6414
|
+
} else if (!result.value.valid) {
|
|
6415
|
+
if (result.value.errors) {
|
|
6416
|
+
for (const err of result.value.errors) {
|
|
6417
|
+
issues.push({ severity: "error", message: err.message });
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
for (const section of result.value.missingSections) {
|
|
6421
|
+
issues.push({ severity: "warning", message: `Missing section: ${section}` });
|
|
6422
|
+
}
|
|
6423
|
+
for (const link of result.value.brokenLinks) {
|
|
6424
|
+
issues.push({
|
|
6425
|
+
severity: "warning",
|
|
6426
|
+
message: `Broken link: ${link.text} \u2192 ${link.path}`,
|
|
6427
|
+
file: link.path
|
|
6428
|
+
});
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
return issues;
|
|
6432
|
+
}
|
|
6433
|
+
async function runDepsCheck(projectRoot, config) {
|
|
6434
|
+
const issues = [];
|
|
6435
|
+
const rawLayers = config.layers;
|
|
6436
|
+
if (rawLayers && rawLayers.length > 0) {
|
|
6437
|
+
const parser = new TypeScriptParser();
|
|
6438
|
+
const layers = rawLayers.map(
|
|
6439
|
+
(l) => defineLayer(
|
|
6440
|
+
l.name,
|
|
6441
|
+
Array.isArray(l.patterns) ? l.patterns : [l.pattern],
|
|
6442
|
+
l.allowedDependencies
|
|
6443
|
+
)
|
|
6444
|
+
);
|
|
6445
|
+
const result = await validateDependencies({
|
|
6446
|
+
layers,
|
|
6447
|
+
rootDir: projectRoot,
|
|
6448
|
+
parser
|
|
6449
|
+
});
|
|
6450
|
+
if (!result.ok) {
|
|
6451
|
+
issues.push({ severity: "error", message: result.error.message });
|
|
6452
|
+
} else if (result.value.violations.length > 0) {
|
|
6453
|
+
for (const v of result.value.violations) {
|
|
6454
|
+
issues.push({
|
|
6455
|
+
severity: "error",
|
|
6456
|
+
message: `${v.reason}: ${v.file} imports ${v.imports} (${v.fromLayer} \u2192 ${v.toLayer})`,
|
|
6457
|
+
file: v.file,
|
|
6458
|
+
line: v.line
|
|
6459
|
+
});
|
|
6436
6460
|
}
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
return issues;
|
|
6464
|
+
}
|
|
6465
|
+
async function runDocsCheck(projectRoot, config) {
|
|
6466
|
+
const issues = [];
|
|
6467
|
+
const docsDir = path12.join(projectRoot, config.docsDir ?? "docs");
|
|
6468
|
+
const entropyConfig = config.entropy || {};
|
|
6469
|
+
const result = await checkDocCoverage("project", {
|
|
6470
|
+
docsDir,
|
|
6471
|
+
sourceDir: projectRoot,
|
|
6472
|
+
excludePatterns: entropyConfig.excludePatterns || [
|
|
6473
|
+
"**/node_modules/**",
|
|
6474
|
+
"**/dist/**",
|
|
6475
|
+
"**/*.test.ts",
|
|
6476
|
+
"**/fixtures/**"
|
|
6477
|
+
]
|
|
6478
|
+
});
|
|
6479
|
+
if (!result.ok) {
|
|
6480
|
+
issues.push({ severity: "warning", message: result.error.message });
|
|
6481
|
+
} else if (result.value.gaps.length > 0) {
|
|
6482
|
+
for (const gap of result.value.gaps) {
|
|
6483
|
+
issues.push({
|
|
6484
|
+
severity: "warning",
|
|
6485
|
+
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6486
|
+
file: gap.file
|
|
6487
|
+
});
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
return issues;
|
|
6491
|
+
}
|
|
6492
|
+
async function runEntropyCheck(projectRoot, _config) {
|
|
6493
|
+
const issues = [];
|
|
6494
|
+
const analyzer = new EntropyAnalyzer({
|
|
6495
|
+
rootDir: projectRoot,
|
|
6496
|
+
analyze: { drift: true, deadCode: true, patterns: false }
|
|
6497
|
+
});
|
|
6498
|
+
const result = await analyzer.analyze();
|
|
6499
|
+
if (!result.ok) {
|
|
6500
|
+
issues.push({ severity: "warning", message: result.error.message });
|
|
6501
|
+
} else {
|
|
6502
|
+
const report = result.value;
|
|
6503
|
+
if (report.drift) {
|
|
6504
|
+
for (const drift of report.drift.drifts) {
|
|
6505
|
+
issues.push({
|
|
6506
|
+
severity: "warning",
|
|
6507
|
+
message: `Doc drift (${drift.type}): ${drift.details}`,
|
|
6508
|
+
file: drift.docFile,
|
|
6509
|
+
line: drift.line
|
|
6510
|
+
});
|
|
6467
6511
|
}
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
"**/dist/**",
|
|
6477
|
-
"**/*.test.ts",
|
|
6478
|
-
"**/fixtures/**"
|
|
6479
|
-
]
|
|
6512
|
+
}
|
|
6513
|
+
if (report.deadCode) {
|
|
6514
|
+
for (const dead of report.deadCode.deadExports) {
|
|
6515
|
+
issues.push({
|
|
6516
|
+
severity: "warning",
|
|
6517
|
+
message: `Dead export: ${dead.name}`,
|
|
6518
|
+
file: dead.file,
|
|
6519
|
+
line: dead.line
|
|
6480
6520
|
});
|
|
6481
|
-
if (!result.ok) {
|
|
6482
|
-
issues.push({ severity: "warning", message: result.error.message });
|
|
6483
|
-
} else if (result.value.gaps.length > 0) {
|
|
6484
|
-
for (const gap of result.value.gaps) {
|
|
6485
|
-
issues.push({
|
|
6486
|
-
severity: "warning",
|
|
6487
|
-
message: `Undocumented: ${gap.file} (suggested: ${gap.suggestedSection})`,
|
|
6488
|
-
file: gap.file
|
|
6489
|
-
});
|
|
6490
|
-
}
|
|
6491
|
-
}
|
|
6492
|
-
break;
|
|
6493
6521
|
}
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6524
|
+
return issues;
|
|
6525
|
+
}
|
|
6526
|
+
async function runSecurityCheck(projectRoot, config) {
|
|
6527
|
+
const issues = [];
|
|
6528
|
+
const securityConfig = parseSecurityConfig(config.security);
|
|
6529
|
+
if (!securityConfig.enabled) return issues;
|
|
6530
|
+
const scanner = new SecurityScanner(securityConfig);
|
|
6531
|
+
scanner.configureForProject(projectRoot);
|
|
6532
|
+
const { glob: globFn } = await import("glob");
|
|
6533
|
+
const sourceFiles = await globFn("**/*.{ts,tsx,js,jsx,go,py}", {
|
|
6534
|
+
cwd: projectRoot,
|
|
6535
|
+
ignore: securityConfig.exclude ?? [
|
|
6536
|
+
"**/node_modules/**",
|
|
6537
|
+
"**/dist/**",
|
|
6538
|
+
"**/*.test.ts",
|
|
6539
|
+
"**/fixtures/**"
|
|
6540
|
+
],
|
|
6541
|
+
absolute: true
|
|
6542
|
+
});
|
|
6543
|
+
const scanResult = await scanner.scanFiles(sourceFiles);
|
|
6544
|
+
for (const finding of scanResult.findings) {
|
|
6545
|
+
issues.push({
|
|
6546
|
+
severity: finding.severity === "info" ? "warning" : finding.severity,
|
|
6547
|
+
message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
|
|
6548
|
+
file: finding.file,
|
|
6549
|
+
line: finding.line
|
|
6550
|
+
});
|
|
6551
|
+
}
|
|
6552
|
+
return issues;
|
|
6553
|
+
}
|
|
6554
|
+
async function runPerfCheck(projectRoot, config) {
|
|
6555
|
+
const issues = [];
|
|
6556
|
+
const perfConfig = config.performance || {};
|
|
6557
|
+
const perfAnalyzer = new EntropyAnalyzer({
|
|
6558
|
+
rootDir: projectRoot,
|
|
6559
|
+
analyze: {
|
|
6560
|
+
complexity: perfConfig.complexity || true,
|
|
6561
|
+
coupling: perfConfig.coupling || true,
|
|
6562
|
+
sizeBudget: perfConfig.sizeBudget || false
|
|
6563
|
+
}
|
|
6564
|
+
});
|
|
6565
|
+
const perfResult = await perfAnalyzer.analyze();
|
|
6566
|
+
if (!perfResult.ok) {
|
|
6567
|
+
issues.push({ severity: "warning", message: perfResult.error.message });
|
|
6568
|
+
} else {
|
|
6569
|
+
const perfReport = perfResult.value;
|
|
6570
|
+
if (perfReport.complexity) {
|
|
6571
|
+
for (const v of perfReport.complexity.violations) {
|
|
6572
|
+
issues.push({
|
|
6573
|
+
severity: v.severity === "info" ? "warning" : v.severity,
|
|
6574
|
+
message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
|
|
6575
|
+
file: v.file,
|
|
6576
|
+
line: v.line
|
|
6498
6577
|
});
|
|
6499
|
-
const result = await analyzer.analyze();
|
|
6500
|
-
if (!result.ok) {
|
|
6501
|
-
issues.push({ severity: "warning", message: result.error.message });
|
|
6502
|
-
} else {
|
|
6503
|
-
const report = result.value;
|
|
6504
|
-
if (report.drift) {
|
|
6505
|
-
for (const drift of report.drift.drifts) {
|
|
6506
|
-
issues.push({
|
|
6507
|
-
severity: "warning",
|
|
6508
|
-
message: `Doc drift (${drift.type}): ${drift.details}`,
|
|
6509
|
-
file: drift.docFile,
|
|
6510
|
-
line: drift.line
|
|
6511
|
-
});
|
|
6512
|
-
}
|
|
6513
|
-
}
|
|
6514
|
-
if (report.deadCode) {
|
|
6515
|
-
for (const dead of report.deadCode.deadExports) {
|
|
6516
|
-
issues.push({
|
|
6517
|
-
severity: "warning",
|
|
6518
|
-
message: `Dead export: ${dead.name}`,
|
|
6519
|
-
file: dead.file,
|
|
6520
|
-
line: dead.line
|
|
6521
|
-
});
|
|
6522
|
-
}
|
|
6523
|
-
}
|
|
6524
|
-
}
|
|
6525
|
-
break;
|
|
6526
6578
|
}
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
cwd: projectRoot,
|
|
6535
|
-
ignore: securityConfig.exclude ?? [
|
|
6536
|
-
"**/node_modules/**",
|
|
6537
|
-
"**/dist/**",
|
|
6538
|
-
"**/*.test.ts",
|
|
6539
|
-
"**/fixtures/**"
|
|
6540
|
-
],
|
|
6541
|
-
absolute: true
|
|
6579
|
+
}
|
|
6580
|
+
if (perfReport.coupling) {
|
|
6581
|
+
for (const v of perfReport.coupling.violations) {
|
|
6582
|
+
issues.push({
|
|
6583
|
+
severity: v.severity === "info" ? "warning" : v.severity,
|
|
6584
|
+
message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
|
|
6585
|
+
file: v.file
|
|
6542
6586
|
});
|
|
6543
|
-
const scanResult = await scanner.scanFiles(sourceFiles);
|
|
6544
|
-
for (const finding of scanResult.findings) {
|
|
6545
|
-
issues.push({
|
|
6546
|
-
severity: finding.severity === "info" ? "warning" : finding.severity,
|
|
6547
|
-
message: `[${finding.ruleId}] ${finding.message}: ${finding.match}`,
|
|
6548
|
-
file: finding.file,
|
|
6549
|
-
line: finding.line
|
|
6550
|
-
});
|
|
6551
|
-
}
|
|
6552
|
-
break;
|
|
6553
6587
|
}
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6588
|
+
}
|
|
6589
|
+
}
|
|
6590
|
+
return issues;
|
|
6591
|
+
}
|
|
6592
|
+
async function runPhaseGateCheck(_projectRoot, config) {
|
|
6593
|
+
const issues = [];
|
|
6594
|
+
const phaseGates = config.phaseGates;
|
|
6595
|
+
if (!phaseGates?.enabled) {
|
|
6596
|
+
return issues;
|
|
6597
|
+
}
|
|
6598
|
+
issues.push({
|
|
6599
|
+
severity: "warning",
|
|
6600
|
+
message: "Phase gate is enabled but requires CLI context. Run `harness check-phase-gate` separately for full validation."
|
|
6601
|
+
});
|
|
6602
|
+
return issues;
|
|
6603
|
+
}
|
|
6604
|
+
async function runArchCheck(projectRoot, config) {
|
|
6605
|
+
const issues = [];
|
|
6606
|
+
const rawArchConfig = config.architecture;
|
|
6607
|
+
const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
|
|
6608
|
+
if (!archConfig.enabled) return issues;
|
|
6609
|
+
const results = await runAll(archConfig, projectRoot);
|
|
6610
|
+
const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
|
|
6611
|
+
const baseline = baselineManager.load();
|
|
6612
|
+
if (baseline) {
|
|
6613
|
+
const diffResult = diff(results, baseline);
|
|
6614
|
+
if (!diffResult.passed) {
|
|
6615
|
+
for (const v of diffResult.newViolations) {
|
|
6616
|
+
issues.push({
|
|
6617
|
+
severity: v.severity,
|
|
6618
|
+
message: `[${v.category || "arch"}] NEW: ${v.detail}`,
|
|
6619
|
+
file: v.file
|
|
6563
6620
|
});
|
|
6564
|
-
const perfResult = await perfAnalyzer.analyze();
|
|
6565
|
-
if (!perfResult.ok) {
|
|
6566
|
-
issues.push({ severity: "warning", message: perfResult.error.message });
|
|
6567
|
-
} else {
|
|
6568
|
-
const perfReport = perfResult.value;
|
|
6569
|
-
if (perfReport.complexity) {
|
|
6570
|
-
for (const v of perfReport.complexity.violations) {
|
|
6571
|
-
issues.push({
|
|
6572
|
-
severity: v.severity === "info" ? "warning" : v.severity,
|
|
6573
|
-
message: `[Tier ${v.tier}] ${v.metric}: ${v.function} in ${v.file} (${v.value} > ${v.threshold})`,
|
|
6574
|
-
file: v.file,
|
|
6575
|
-
line: v.line
|
|
6576
|
-
});
|
|
6577
|
-
}
|
|
6578
|
-
}
|
|
6579
|
-
if (perfReport.coupling) {
|
|
6580
|
-
for (const v of perfReport.coupling.violations) {
|
|
6581
|
-
issues.push({
|
|
6582
|
-
severity: v.severity === "info" ? "warning" : v.severity,
|
|
6583
|
-
message: `[Tier ${v.tier}] ${v.metric}: ${v.file} (${v.value} > ${v.threshold})`,
|
|
6584
|
-
file: v.file
|
|
6585
|
-
});
|
|
6586
|
-
}
|
|
6587
|
-
}
|
|
6588
|
-
}
|
|
6589
|
-
break;
|
|
6590
6621
|
}
|
|
6591
|
-
|
|
6592
|
-
const phaseGates = config.phaseGates;
|
|
6593
|
-
if (!phaseGates?.enabled) {
|
|
6594
|
-
break;
|
|
6595
|
-
}
|
|
6622
|
+
for (const r of diffResult.regressions) {
|
|
6596
6623
|
issues.push({
|
|
6597
|
-
severity: "
|
|
6598
|
-
message:
|
|
6624
|
+
severity: "error",
|
|
6625
|
+
message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
|
|
6599
6626
|
});
|
|
6600
|
-
break;
|
|
6601
6627
|
}
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
if (!diffResult.passed) {
|
|
6612
|
-
for (const v of diffResult.newViolations) {
|
|
6613
|
-
issues.push({
|
|
6614
|
-
severity: v.severity,
|
|
6615
|
-
message: `[${v.category || "arch"}] NEW: ${v.detail}`,
|
|
6616
|
-
file: v.file
|
|
6617
|
-
});
|
|
6618
|
-
}
|
|
6619
|
-
for (const r of diffResult.regressions) {
|
|
6620
|
-
issues.push({
|
|
6621
|
-
severity: "error",
|
|
6622
|
-
message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
|
|
6623
|
-
});
|
|
6624
|
-
}
|
|
6625
|
-
}
|
|
6626
|
-
} else {
|
|
6627
|
-
for (const result of results) {
|
|
6628
|
-
for (const v of result.violations) {
|
|
6629
|
-
issues.push({
|
|
6630
|
-
severity: v.severity,
|
|
6631
|
-
message: `[${result.category}] ${v.detail}`,
|
|
6632
|
-
file: v.file
|
|
6633
|
-
});
|
|
6634
|
-
}
|
|
6635
|
-
}
|
|
6636
|
-
}
|
|
6637
|
-
break;
|
|
6628
|
+
}
|
|
6629
|
+
} else {
|
|
6630
|
+
for (const result of results) {
|
|
6631
|
+
for (const v of result.violations) {
|
|
6632
|
+
issues.push({
|
|
6633
|
+
severity: v.severity,
|
|
6634
|
+
message: `[${result.category}] ${v.detail}`,
|
|
6635
|
+
file: v.file
|
|
6636
|
+
});
|
|
6638
6637
|
}
|
|
6639
6638
|
}
|
|
6639
|
+
}
|
|
6640
|
+
return issues;
|
|
6641
|
+
}
|
|
6642
|
+
async function runSingleCheck(name, projectRoot, config) {
|
|
6643
|
+
const start = Date.now();
|
|
6644
|
+
const issues = [];
|
|
6645
|
+
try {
|
|
6646
|
+
switch (name) {
|
|
6647
|
+
case "validate":
|
|
6648
|
+
issues.push(...await runValidateCheck(projectRoot, config));
|
|
6649
|
+
break;
|
|
6650
|
+
case "deps":
|
|
6651
|
+
issues.push(...await runDepsCheck(projectRoot, config));
|
|
6652
|
+
break;
|
|
6653
|
+
case "docs":
|
|
6654
|
+
issues.push(...await runDocsCheck(projectRoot, config));
|
|
6655
|
+
break;
|
|
6656
|
+
case "entropy":
|
|
6657
|
+
issues.push(...await runEntropyCheck(projectRoot, config));
|
|
6658
|
+
break;
|
|
6659
|
+
case "security":
|
|
6660
|
+
issues.push(...await runSecurityCheck(projectRoot, config));
|
|
6661
|
+
break;
|
|
6662
|
+
case "perf":
|
|
6663
|
+
issues.push(...await runPerfCheck(projectRoot, config));
|
|
6664
|
+
break;
|
|
6665
|
+
case "phase-gate":
|
|
6666
|
+
issues.push(...await runPhaseGateCheck(projectRoot, config));
|
|
6667
|
+
break;
|
|
6668
|
+
case "arch":
|
|
6669
|
+
issues.push(...await runArchCheck(projectRoot, config));
|
|
6670
|
+
break;
|
|
6671
|
+
}
|
|
6640
6672
|
} catch (error) {
|
|
6641
6673
|
issues.push({
|
|
6642
6674
|
severity: "error",
|
|
@@ -7011,7 +7043,7 @@ async function readContextFile(projectRoot, filePath, reason) {
|
|
|
7011
7043
|
if (!result.ok) return null;
|
|
7012
7044
|
const content = result.value;
|
|
7013
7045
|
const lines = content.split("\n").length;
|
|
7014
|
-
const relPath = path14.isAbsolute(filePath) ?
|
|
7046
|
+
const relPath = path14.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
|
|
7015
7047
|
return { path: relPath, content, reason, lines };
|
|
7016
7048
|
}
|
|
7017
7049
|
function extractImportSources(content) {
|
|
@@ -7029,7 +7061,7 @@ async function resolveImportPath(projectRoot, fromFile, importSource) {
|
|
|
7029
7061
|
const fromDir = path14.dirname(path14.join(projectRoot, fromFile));
|
|
7030
7062
|
const basePath = path14.resolve(fromDir, importSource);
|
|
7031
7063
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
7032
|
-
const relBase =
|
|
7064
|
+
const relBase = relativePosix(projectRoot, basePath);
|
|
7033
7065
|
const candidates = [
|
|
7034
7066
|
relBase + ".ts",
|
|
7035
7067
|
relBase + ".tsx",
|
|
@@ -7048,7 +7080,7 @@ async function findTestFiles(projectRoot, sourceFile) {
|
|
|
7048
7080
|
const baseName = path14.basename(sourceFile, path14.extname(sourceFile));
|
|
7049
7081
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
7050
7082
|
const results = await findFiles(pattern, projectRoot);
|
|
7051
|
-
return results.map((f) =>
|
|
7083
|
+
return results.map((f) => relativePosix(projectRoot, f));
|
|
7052
7084
|
}
|
|
7053
7085
|
async function gatherImportContext(projectRoot, changedFiles, budget) {
|
|
7054
7086
|
const contextFiles = [];
|
|
@@ -8910,7 +8942,7 @@ Run "harness update" to upgrade.`;
|
|
|
8910
8942
|
}
|
|
8911
8943
|
|
|
8912
8944
|
// src/index.ts
|
|
8913
|
-
var VERSION = "0.
|
|
8945
|
+
var VERSION = "0.13.0";
|
|
8914
8946
|
export {
|
|
8915
8947
|
AGENT_DESCRIPTORS,
|
|
8916
8948
|
ARCHITECTURE_DESCRIPTOR,
|