@harness-engineering/core 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -34,33 +34,58 @@ __export(index_exports, {
34
34
  AGENT_DESCRIPTORS: () => AGENT_DESCRIPTORS,
35
35
  ARCHITECTURE_DESCRIPTOR: () => ARCHITECTURE_DESCRIPTOR,
36
36
  AgentActionEmitter: () => AgentActionEmitter,
37
+ ArchBaselineManager: () => ArchBaselineManager,
38
+ ArchBaselineSchema: () => ArchBaselineSchema,
39
+ ArchConfigSchema: () => ArchConfigSchema,
40
+ ArchDiffResultSchema: () => ArchDiffResultSchema,
41
+ ArchMetricCategorySchema: () => ArchMetricCategorySchema,
37
42
  BUG_DETECTION_DESCRIPTOR: () => BUG_DETECTION_DESCRIPTOR,
38
43
  BaselineManager: () => BaselineManager,
39
44
  BenchmarkRunner: () => BenchmarkRunner,
45
+ BlueprintGenerator: () => BlueprintGenerator,
46
+ BundleConstraintsSchema: () => BundleConstraintsSchema,
47
+ BundleSchema: () => BundleSchema,
40
48
  COMPLIANCE_DESCRIPTOR: () => COMPLIANCE_DESCRIPTOR,
49
+ CategoryBaselineSchema: () => CategoryBaselineSchema,
50
+ CategoryRegressionSchema: () => CategoryRegressionSchema,
41
51
  ChecklistBuilder: () => ChecklistBuilder,
52
+ CircularDepsCollector: () => CircularDepsCollector,
53
+ ComplexityCollector: () => ComplexityCollector,
42
54
  ConfirmationSchema: () => ConfirmationSchema,
43
55
  ConsoleSink: () => ConsoleSink,
56
+ ConstraintRuleSchema: () => ConstraintRuleSchema,
57
+ ContentPipeline: () => ContentPipeline,
58
+ ContributionsSchema: () => ContributionsSchema,
59
+ CouplingCollector: () => CouplingCollector,
44
60
  CriticalPathResolver: () => CriticalPathResolver,
45
61
  DEFAULT_PROVIDER_TIERS: () => DEFAULT_PROVIDER_TIERS,
46
62
  DEFAULT_SECURITY_CONFIG: () => DEFAULT_SECURITY_CONFIG,
47
63
  DEFAULT_STATE: () => DEFAULT_STATE,
48
64
  DEFAULT_STREAM_INDEX: () => DEFAULT_STREAM_INDEX,
65
+ DepDepthCollector: () => DepDepthCollector,
49
66
  EmitInteractionInputSchema: () => EmitInteractionInputSchema,
50
67
  EntropyAnalyzer: () => EntropyAnalyzer,
51
68
  EntropyConfigSchema: () => EntropyConfigSchema,
52
69
  ExclusionSet: () => ExclusionSet,
53
70
  FailureEntrySchema: () => FailureEntrySchema,
54
71
  FileSink: () => FileSink,
72
+ ForbiddenImportCollector: () => ForbiddenImportCollector,
55
73
  GateConfigSchema: () => GateConfigSchema,
56
74
  GateResultSchema: () => GateResultSchema,
57
75
  HandoffSchema: () => HandoffSchema,
58
76
  HarnessStateSchema: () => HarnessStateSchema,
59
77
  InteractionTypeSchema: () => InteractionTypeSchema,
78
+ LayerViolationCollector: () => LayerViolationCollector,
79
+ LockfilePackageSchema: () => LockfilePackageSchema,
80
+ LockfileSchema: () => LockfileSchema,
81
+ ManifestSchema: () => ManifestSchema,
82
+ MetricResultSchema: () => MetricResultSchema,
83
+ ModuleSizeCollector: () => ModuleSizeCollector,
60
84
  NoOpExecutor: () => NoOpExecutor,
61
85
  NoOpSink: () => NoOpSink,
62
86
  NoOpTelemetryAdapter: () => NoOpTelemetryAdapter,
63
87
  PatternConfigSchema: () => PatternConfigSchema,
88
+ ProjectScanner: () => ProjectScanner,
64
89
  QuestionSchema: () => QuestionSchema,
65
90
  REQUIRED_SECTIONS: () => REQUIRED_SECTIONS,
66
91
  RegressionDetector: () => RegressionDetector,
@@ -68,16 +93,26 @@ __export(index_exports, {
68
93
  SECURITY_DESCRIPTOR: () => SECURITY_DESCRIPTOR,
69
94
  SecurityConfigSchema: () => SecurityConfigSchema,
70
95
  SecurityScanner: () => SecurityScanner,
96
+ SharableBoundaryConfigSchema: () => SharableBoundaryConfigSchema,
97
+ SharableForbiddenImportSchema: () => SharableForbiddenImportSchema,
98
+ SharableLayerSchema: () => SharableLayerSchema,
99
+ SharableSecurityRulesSchema: () => SharableSecurityRulesSchema,
71
100
  StreamIndexSchema: () => StreamIndexSchema,
72
101
  StreamInfoSchema: () => StreamInfoSchema,
102
+ ThresholdConfigSchema: () => ThresholdConfigSchema,
73
103
  TransitionSchema: () => TransitionSchema,
74
104
  TypeScriptParser: () => TypeScriptParser,
75
105
  VERSION: () => VERSION,
106
+ ViolationSchema: () => ViolationSchema,
107
+ addProvenance: () => addProvenance,
76
108
  analyzeDiff: () => analyzeDiff,
77
109
  appendFailure: () => appendFailure,
78
110
  appendLearning: () => appendLearning,
79
111
  applyFixes: () => applyFixes,
80
112
  applyHotspotDowngrade: () => applyHotspotDowngrade,
113
+ archMatchers: () => archMatchers,
114
+ archModule: () => archModule,
115
+ architecture: () => architecture,
81
116
  archiveFailures: () => archiveFailures,
82
117
  archiveStream: () => archiveStream,
83
118
  buildDependencyGraph: () => buildDependencyGraph,
@@ -87,6 +122,7 @@ __export(index_exports, {
87
122
  checkEligibility: () => checkEligibility,
88
123
  classifyFinding: () => classifyFinding,
89
124
  configureFeedback: () => configureFeedback,
125
+ constraintRuleId: () => constraintRuleId,
90
126
  contextBudget: () => contextBudget,
91
127
  contextFilter: () => contextFilter,
92
128
  createBoundaryValidator: () => createBoundaryValidator,
@@ -101,6 +137,8 @@ __export(index_exports, {
101
137
  cryptoRules: () => cryptoRules,
102
138
  deduplicateCleanupFindings: () => deduplicateCleanupFindings,
103
139
  deduplicateFindings: () => deduplicateFindings,
140
+ deepMergeConstraints: () => deepMergeConstraints,
141
+ defaultCollectors: () => defaultCollectors,
104
142
  defineLayer: () => defineLayer,
105
143
  deserializationRules: () => deserializationRules,
106
144
  detectChangeType: () => detectChangeType,
@@ -113,9 +151,12 @@ __export(index_exports, {
113
151
  detectPatternViolations: () => detectPatternViolations,
114
152
  detectSizeBudgetViolations: () => detectSizeBudgetViolations,
115
153
  detectStack: () => detectStack,
154
+ detectStaleConstraints: () => detectStaleConstraints,
116
155
  determineAssessment: () => determineAssessment,
156
+ diff: () => diff,
117
157
  executeWorkflow: () => executeWorkflow,
118
158
  expressRules: () => expressRules,
159
+ extractBundle: () => extractBundle,
119
160
  extractMarkdownLinks: () => extractMarkdownLinks,
120
161
  extractSections: () => extractSections,
121
162
  fanOutReview: () => fanOutReview,
@@ -146,6 +187,7 @@ __export(index_exports, {
146
187
  networkRules: () => networkRules,
147
188
  nodeRules: () => nodeRules,
148
189
  parseDiff: () => parseDiff,
190
+ parseManifest: () => parseManifest,
149
191
  parseRoadmap: () => parseRoadmap,
150
192
  parseSecurityConfig: () => parseSecurityConfig,
151
193
  parseSize: () => parseSize,
@@ -153,6 +195,8 @@ __export(index_exports, {
153
195
  previewFix: () => previewFix,
154
196
  reactRules: () => reactRules,
155
197
  readCheckState: () => readCheckState,
198
+ readLockfile: () => readLockfile,
199
+ removeProvenance: () => removeProvenance,
156
200
  requestMultiplePeerReviews: () => requestMultiplePeerReviews,
157
201
  requestPeerReview: () => requestPeerReview,
158
202
  resetFeedbackConfig: () => resetFeedbackConfig,
@@ -160,6 +204,8 @@ __export(index_exports, {
160
204
  resolveModelTier: () => resolveModelTier,
161
205
  resolveRuleSeverity: () => resolveRuleSeverity,
162
206
  resolveStreamPath: () => resolveStreamPath,
207
+ resolveThresholds: () => resolveThresholds,
208
+ runAll: () => runAll,
163
209
  runArchitectureAgent: () => runArchitectureAgent,
164
210
  runBugDetectionAgent: () => runBugDetectionAgent,
165
211
  runCIChecks: () => runCIChecks,
@@ -179,6 +225,7 @@ __export(index_exports, {
179
225
  setActiveStream: () => setActiveStream,
180
226
  shouldRunCheck: () => shouldRunCheck,
181
227
  spawnBackgroundCheck: () => spawnBackgroundCheck,
228
+ syncConstraintNodes: () => syncConstraintNodes,
182
229
  syncRoadmap: () => syncRoadmap,
183
230
  touchStream: () => touchStream,
184
231
  trackAction: () => trackAction,
@@ -191,6 +238,9 @@ __export(index_exports, {
191
238
  validateFindings: () => validateFindings,
192
239
  validateKnowledgeMap: () => validateKnowledgeMap,
193
240
  validatePatternConfig: () => validatePatternConfig,
241
+ violationId: () => violationId,
242
+ writeConfig: () => writeConfig,
243
+ writeLockfile: () => writeLockfile,
194
244
  xssRules: () => xssRules
195
245
  });
196
246
  module.exports = __toCommonJS(index_exports);
@@ -213,17 +263,17 @@ var import_util = require("util");
213
263
  var import_glob = require("glob");
214
264
  var accessAsync = (0, import_util.promisify)(import_fs.access);
215
265
  var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
216
- async function fileExists(path11) {
266
+ async function fileExists(path13) {
217
267
  try {
218
- await accessAsync(path11, import_fs.constants.F_OK);
268
+ await accessAsync(path13, import_fs.constants.F_OK);
219
269
  return true;
220
270
  } catch {
221
271
  return false;
222
272
  }
223
273
  }
224
- async function readFileContent(path11) {
274
+ async function readFileContent(path13) {
225
275
  try {
226
- const content = await readFileAsync(path11, "utf-8");
276
+ const content = await readFileAsync(path13, "utf-8");
227
277
  return (0, import_types.Ok)(content);
228
278
  } catch (error) {
229
279
  return (0, import_types.Err)(error);
@@ -271,15 +321,15 @@ function validateConfig(data, schema) {
271
321
  let message = "Configuration validation failed";
272
322
  const suggestions = [];
273
323
  if (firstError) {
274
- const path11 = firstError.path.join(".");
275
- const pathDisplay = path11 ? ` at "${path11}"` : "";
324
+ const path13 = firstError.path.join(".");
325
+ const pathDisplay = path13 ? ` at "${path13}"` : "";
276
326
  if (firstError.code === "invalid_type") {
277
327
  const received = firstError.received;
278
328
  const expected = firstError.expected;
279
329
  if (received === "undefined") {
280
330
  code = "MISSING_FIELD";
281
331
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
282
- suggestions.push(`Field "${path11}" is required and must be of type "${expected}"`);
332
+ suggestions.push(`Field "${path13}" is required and must be of type "${expected}"`);
283
333
  } else {
284
334
  code = "INVALID_TYPE";
285
335
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -492,30 +542,30 @@ function extractSections(content) {
492
542
  return result;
493
543
  });
494
544
  }
495
- function isExternalLink(path11) {
496
- return path11.startsWith("http://") || path11.startsWith("https://") || path11.startsWith("#") || path11.startsWith("mailto:");
545
+ function isExternalLink(path13) {
546
+ return path13.startsWith("http://") || path13.startsWith("https://") || path13.startsWith("#") || path13.startsWith("mailto:");
497
547
  }
498
548
  function resolveLinkPath(linkPath, baseDir) {
499
549
  return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
500
550
  }
501
- async function validateAgentsMap(path11 = "./AGENTS.md") {
551
+ async function validateAgentsMap(path13 = "./AGENTS.md") {
502
552
  console.warn(
503
553
  "[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
504
554
  );
505
- const contentResult = await readFileContent(path11);
555
+ const contentResult = await readFileContent(path13);
506
556
  if (!contentResult.ok) {
507
557
  return (0, import_types.Err)(
508
558
  createError(
509
559
  "PARSE_ERROR",
510
560
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
511
- { path: path11 },
561
+ { path: path13 },
512
562
  ["Ensure the file exists", "Check file permissions"]
513
563
  )
514
564
  );
515
565
  }
516
566
  const content = contentResult.value;
517
567
  const sections = extractSections(content);
518
- const baseDir = (0, import_path.dirname)(path11);
568
+ const baseDir = (0, import_path.dirname)(path13);
519
569
  const sectionTitles = sections.map((s) => s.title);
520
570
  const missingSections = REQUIRED_SECTIONS.filter(
521
571
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -555,6 +605,7 @@ async function validateAgentsMap(path11 = "./AGENTS.md") {
555
605
  }
556
606
 
557
607
  // src/context/doc-coverage.ts
608
+ var import_minimatch = require("minimatch");
558
609
  var import_path2 = require("path");
559
610
  function determineImportance(filePath) {
560
611
  const name = (0, import_path2.basename)(filePath).toLowerCase();
@@ -577,7 +628,7 @@ function suggestSection(filePath, domain) {
577
628
  return `${domain} Reference`;
578
629
  }
579
630
  async function checkDocCoverage(domain, options = {}) {
580
- const { docsDir = "./docs", sourceDir = "./src", excludePatterns = [], graphCoverage } = options;
631
+ const { docsDir = "./docs", sourceDir = ".", excludePatterns = [], graphCoverage } = options;
581
632
  if (graphCoverage) {
582
633
  const gaps = graphCoverage.undocumented.map((file) => ({
583
634
  file,
@@ -597,9 +648,7 @@ async function checkDocCoverage(domain, options = {}) {
597
648
  const filteredSourceFiles = sourceFiles.filter((file) => {
598
649
  const relativePath = (0, import_path2.relative)(sourceDir, file);
599
650
  return !excludePatterns.some((pattern) => {
600
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
601
- const regex = new RegExp("^" + escaped + "$");
602
- return regex.test(relativePath) || regex.test(file);
651
+ return (0, import_minimatch.minimatch)(relativePath, pattern, { dot: true }) || (0, import_minimatch.minimatch)(file, pattern, { dot: true });
603
652
  });
604
653
  });
605
654
  const docFiles = await findFiles("**/*.md", docsDir);
@@ -657,8 +706,8 @@ async function checkDocCoverage(domain, options = {}) {
657
706
 
658
707
  // src/context/knowledge-map.ts
659
708
  var import_path3 = require("path");
660
- function suggestFix(path11, existingFiles) {
661
- const targetName = (0, import_path3.basename)(path11).toLowerCase();
709
+ function suggestFix(path13, existingFiles) {
710
+ const targetName = (0, import_path3.basename)(path13).toLowerCase();
662
711
  const similar = existingFiles.find((file) => {
663
712
  const fileName = (0, import_path3.basename)(file).toLowerCase();
664
713
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -666,7 +715,7 @@ function suggestFix(path11, existingFiles) {
666
715
  if (similar) {
667
716
  return `Did you mean "${similar}"?`;
668
717
  }
669
- return `Create the file "${path11}" or remove the link`;
718
+ return `Create the file "${path13}" or remove the link`;
670
719
  }
671
720
  async function validateKnowledgeMap(rootDir = process.cwd()) {
672
721
  console.warn(
@@ -1011,7 +1060,7 @@ function getPhaseCategories(phase) {
1011
1060
  }
1012
1061
 
1013
1062
  // src/constraints/layers.ts
1014
- var import_minimatch = require("minimatch");
1063
+ var import_minimatch2 = require("minimatch");
1015
1064
  function defineLayer(name, patterns, allowedDependencies) {
1016
1065
  return {
1017
1066
  name,
@@ -1022,7 +1071,7 @@ function defineLayer(name, patterns, allowedDependencies) {
1022
1071
  function resolveFileToLayer(file, layers) {
1023
1072
  for (const layer of layers) {
1024
1073
  for (const pattern of layer.patterns) {
1025
- if ((0, import_minimatch.minimatch)(file, pattern)) {
1074
+ if ((0, import_minimatch2.minimatch)(file, pattern)) {
1026
1075
  return layer;
1027
1076
  }
1028
1077
  }
@@ -1269,8 +1318,8 @@ function createBoundaryValidator(schema, name) {
1269
1318
  return (0, import_types.Ok)(result.data);
1270
1319
  }
1271
1320
  const suggestions = result.error.issues.map((issue) => {
1272
- const path11 = issue.path.join(".");
1273
- return path11 ? `${path11}: ${issue.message}` : issue.message;
1321
+ const path13 = issue.path.join(".");
1322
+ return path13 ? `${path13}: ${issue.message}` : issue.message;
1274
1323
  });
1275
1324
  return (0, import_types.Err)(
1276
1325
  createError(
@@ -1314,6 +1363,422 @@ function validateBoundaries(boundaries, data) {
1314
1363
  });
1315
1364
  }
1316
1365
 
1366
+ // src/constraints/sharing/types.ts
1367
+ var import_zod = require("zod");
1368
+ var ManifestSchema = import_zod.z.object({
1369
+ name: import_zod.z.string(),
1370
+ version: import_zod.z.string(),
1371
+ description: import_zod.z.string().optional(),
1372
+ include: import_zod.z.array(import_zod.z.string()).min(1),
1373
+ minHarnessVersion: import_zod.z.string().optional(),
1374
+ keywords: import_zod.z.array(import_zod.z.string()).optional().default([]),
1375
+ layers: import_zod.z.record(import_zod.z.array(import_zod.z.string())).optional(),
1376
+ boundaries: import_zod.z.array(
1377
+ import_zod.z.object({
1378
+ name: import_zod.z.string(),
1379
+ layer: import_zod.z.string(),
1380
+ direction: import_zod.z.enum(["input", "output"]),
1381
+ schema: import_zod.z.string()
1382
+ })
1383
+ ).optional()
1384
+ });
1385
+ var BundleConstraintsSchema = import_zod.z.object({
1386
+ layers: import_zod.z.array(
1387
+ import_zod.z.object({
1388
+ name: import_zod.z.string(),
1389
+ pattern: import_zod.z.string(),
1390
+ allowedDependencies: import_zod.z.array(import_zod.z.string())
1391
+ })
1392
+ ).optional(),
1393
+ forbiddenImports: import_zod.z.array(
1394
+ import_zod.z.object({
1395
+ from: import_zod.z.string(),
1396
+ disallow: import_zod.z.array(import_zod.z.string()),
1397
+ message: import_zod.z.string().optional()
1398
+ })
1399
+ ).optional(),
1400
+ boundaries: import_zod.z.object({
1401
+ requireSchema: import_zod.z.array(import_zod.z.string()).optional()
1402
+ }).optional(),
1403
+ architecture: import_zod.z.object({
1404
+ thresholds: import_zod.z.record(import_zod.z.unknown()).optional(),
1405
+ modules: import_zod.z.record(import_zod.z.record(import_zod.z.unknown())).optional()
1406
+ }).optional(),
1407
+ security: import_zod.z.object({
1408
+ rules: import_zod.z.record(import_zod.z.string()).optional()
1409
+ }).optional()
1410
+ });
1411
+ var BundleSchema = import_zod.z.object({
1412
+ name: import_zod.z.string(),
1413
+ version: import_zod.z.string(),
1414
+ description: import_zod.z.string().optional(),
1415
+ minHarnessVersion: import_zod.z.string().optional(),
1416
+ manifest: ManifestSchema,
1417
+ constraints: BundleConstraintsSchema,
1418
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional()
1419
+ });
1420
+ var ContributionsSchema = import_zod.z.record(import_zod.z.unknown());
1421
+ var LockfilePackageSchema = import_zod.z.object({
1422
+ version: import_zod.z.string(),
1423
+ source: import_zod.z.string(),
1424
+ installedAt: import_zod.z.string(),
1425
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional().nullable(),
1426
+ resolved: import_zod.z.string().optional(),
1427
+ integrity: import_zod.z.string().optional(),
1428
+ provenance: import_zod.z.array(import_zod.z.string()).optional()
1429
+ });
1430
+ var LockfileSchema = import_zod.z.object({
1431
+ version: import_zod.z.literal(1).optional(),
1432
+ // Used by some tests
1433
+ lockfileVersion: import_zod.z.literal(1).optional(),
1434
+ // Standard field
1435
+ packages: import_zod.z.record(LockfilePackageSchema)
1436
+ }).refine((data) => data.version !== void 0 || data.lockfileVersion !== void 0, {
1437
+ message: "Either 'version' or 'lockfileVersion' must be present and equal to 1"
1438
+ });
1439
+ var SharableLayerSchema = import_zod.z.unknown();
1440
+ var SharableForbiddenImportSchema = import_zod.z.unknown();
1441
+ var SharableBoundaryConfigSchema = import_zod.z.unknown();
1442
+ var SharableSecurityRulesSchema = import_zod.z.unknown();
1443
+
1444
+ // src/constraints/sharing/write-config.ts
1445
+ var fs = __toESM(require("fs/promises"));
1446
+ async function writeConfig(filePath, content) {
1447
+ try {
1448
+ const json = JSON.stringify(content, null, 2) + "\n";
1449
+ await fs.writeFile(filePath, json, "utf-8");
1450
+ return (0, import_types.Ok)(void 0);
1451
+ } catch (error) {
1452
+ return (0, import_types.Err)(error instanceof Error ? error : new Error(String(error)));
1453
+ }
1454
+ }
1455
+
1456
+ // src/constraints/sharing/manifest.ts
1457
+ function parseManifest(parsed) {
1458
+ const result = ManifestSchema.safeParse(parsed);
1459
+ if (!result.success) {
1460
+ const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1461
+ return { ok: false, error: `Invalid manifest: ${issues}` };
1462
+ }
1463
+ return { ok: true, value: result.data };
1464
+ }
1465
+
1466
+ // src/constraints/sharing/bundle.ts
1467
+ function resolveDotPath(obj, dotPath) {
1468
+ const segments = dotPath.split(".");
1469
+ let current = obj;
1470
+ for (const segment of segments) {
1471
+ if (current === null || typeof current !== "object") {
1472
+ return void 0;
1473
+ }
1474
+ current = current[segment];
1475
+ }
1476
+ return current;
1477
+ }
1478
+ function setDotPath(obj, dotPath, value) {
1479
+ const segments = dotPath.split(".");
1480
+ const lastSegment = segments[segments.length - 1];
1481
+ const parentSegments = segments.slice(0, -1);
1482
+ let current = obj;
1483
+ for (const segment of parentSegments) {
1484
+ if (current[segment] === void 0 || current[segment] === null || typeof current[segment] !== "object") {
1485
+ current[segment] = {};
1486
+ }
1487
+ current = current[segment];
1488
+ }
1489
+ if (lastSegment !== void 0) {
1490
+ current[lastSegment] = value;
1491
+ }
1492
+ }
1493
+ function extractBundle(manifest, config) {
1494
+ const constraints = {};
1495
+ for (const includePath of manifest.include) {
1496
+ const value = resolveDotPath(config, includePath);
1497
+ if (value !== void 0) {
1498
+ setDotPath(constraints, includePath, value);
1499
+ }
1500
+ }
1501
+ const bundle = {
1502
+ name: manifest.name,
1503
+ version: manifest.version,
1504
+ ...manifest.minHarnessVersion !== void 0 && {
1505
+ minHarnessVersion: manifest.minHarnessVersion
1506
+ },
1507
+ ...manifest.description !== void 0 && {
1508
+ description: manifest.description
1509
+ },
1510
+ manifest,
1511
+ constraints
1512
+ };
1513
+ const parsed = BundleSchema.safeParse(bundle);
1514
+ if (!parsed.success) {
1515
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1516
+ return { ok: false, error: `Invalid bundle: ${issues}` };
1517
+ }
1518
+ return { ok: true, value: parsed.data };
1519
+ }
1520
+
1521
+ // src/constraints/sharing/merge.ts
1522
+ function deepEqual(a, b) {
1523
+ if (a === b) return true;
1524
+ if (typeof a !== typeof b) return false;
1525
+ if (typeof a !== "object" || a === null || b === null) return false;
1526
+ if (Array.isArray(a) && Array.isArray(b)) {
1527
+ if (a.length !== b.length) return false;
1528
+ return a.every((val, i) => deepEqual(val, b[i]));
1529
+ }
1530
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1531
+ const keysA = Object.keys(a);
1532
+ const keysB = Object.keys(b);
1533
+ if (keysA.length !== keysB.length) return false;
1534
+ return keysA.every(
1535
+ (key) => deepEqual(a[key], b[key])
1536
+ );
1537
+ }
1538
+ function stringArraysEqual(a, b) {
1539
+ if (a.length !== b.length) return false;
1540
+ const sortedA = [...a].sort();
1541
+ const sortedB = [...b].sort();
1542
+ return sortedA.every((val, i) => val === sortedB[i]);
1543
+ }
1544
+ function deepMergeConstraints(localConfig, bundleConstraints, _existingContributions) {
1545
+ const config = { ...localConfig };
1546
+ const contributions = {};
1547
+ const conflicts = [];
1548
+ if (bundleConstraints.layers && bundleConstraints.layers.length > 0) {
1549
+ const localLayers = Array.isArray(localConfig.layers) ? localConfig.layers : [];
1550
+ const mergedLayers = [...localLayers];
1551
+ const contributedLayerNames = [];
1552
+ for (const bundleLayer of bundleConstraints.layers) {
1553
+ const existing = localLayers.find((l) => l.name === bundleLayer.name);
1554
+ if (!existing) {
1555
+ mergedLayers.push(bundleLayer);
1556
+ contributedLayerNames.push(bundleLayer.name);
1557
+ } else {
1558
+ const same = existing.pattern === bundleLayer.pattern && stringArraysEqual(existing.allowedDependencies, bundleLayer.allowedDependencies);
1559
+ if (!same) {
1560
+ conflicts.push({
1561
+ section: "layers",
1562
+ key: bundleLayer.name,
1563
+ localValue: existing,
1564
+ packageValue: bundleLayer,
1565
+ description: `Layer '${bundleLayer.name}' already exists locally with different configuration`
1566
+ });
1567
+ }
1568
+ }
1569
+ }
1570
+ config.layers = mergedLayers;
1571
+ if (contributedLayerNames.length > 0) {
1572
+ contributions.layers = contributedLayerNames;
1573
+ }
1574
+ }
1575
+ if (bundleConstraints.forbiddenImports && bundleConstraints.forbiddenImports.length > 0) {
1576
+ const localFI = Array.isArray(localConfig.forbiddenImports) ? localConfig.forbiddenImports : [];
1577
+ const mergedFI = [...localFI];
1578
+ const contributedFromKeys = [];
1579
+ for (const bundleRule of bundleConstraints.forbiddenImports) {
1580
+ const existing = localFI.find((r) => r.from === bundleRule.from);
1581
+ if (!existing) {
1582
+ const entry = {
1583
+ from: bundleRule.from,
1584
+ disallow: bundleRule.disallow
1585
+ };
1586
+ if (bundleRule.message !== void 0) {
1587
+ entry.message = bundleRule.message;
1588
+ }
1589
+ mergedFI.push(entry);
1590
+ contributedFromKeys.push(bundleRule.from);
1591
+ } else {
1592
+ const same = stringArraysEqual(existing.disallow, bundleRule.disallow);
1593
+ if (!same) {
1594
+ conflicts.push({
1595
+ section: "forbiddenImports",
1596
+ key: bundleRule.from,
1597
+ localValue: existing,
1598
+ packageValue: bundleRule,
1599
+ description: `Forbidden import rule for '${bundleRule.from}' already exists locally with different disallow list`
1600
+ });
1601
+ }
1602
+ }
1603
+ }
1604
+ config.forbiddenImports = mergedFI;
1605
+ if (contributedFromKeys.length > 0) {
1606
+ contributions.forbiddenImports = contributedFromKeys;
1607
+ }
1608
+ }
1609
+ if (bundleConstraints.boundaries) {
1610
+ const localBoundaries = localConfig.boundaries ?? { requireSchema: [] };
1611
+ const localSchemas = new Set(localBoundaries.requireSchema ?? []);
1612
+ const bundleSchemas = bundleConstraints.boundaries.requireSchema ?? [];
1613
+ const newSchemas = [];
1614
+ for (const schema of bundleSchemas) {
1615
+ if (!localSchemas.has(schema)) {
1616
+ newSchemas.push(schema);
1617
+ localSchemas.add(schema);
1618
+ }
1619
+ }
1620
+ config.boundaries = {
1621
+ requireSchema: [...localBoundaries.requireSchema ?? [], ...newSchemas]
1622
+ };
1623
+ if (newSchemas.length > 0) {
1624
+ contributions.boundaries = newSchemas;
1625
+ }
1626
+ }
1627
+ if (bundleConstraints.architecture) {
1628
+ const localArch = localConfig.architecture ?? {
1629
+ enabled: true,
1630
+ baselinePath: ".harness/arch/baselines.json",
1631
+ thresholds: {},
1632
+ modules: {}
1633
+ };
1634
+ const mergedThresholds = { ...localArch.thresholds };
1635
+ const contributedThresholdKeys = [];
1636
+ const bundleThresholds = bundleConstraints.architecture.thresholds ?? {};
1637
+ for (const [category, value] of Object.entries(bundleThresholds)) {
1638
+ if (!(category in mergedThresholds)) {
1639
+ mergedThresholds[category] = value;
1640
+ contributedThresholdKeys.push(category);
1641
+ } else if (!deepEqual(mergedThresholds[category], value)) {
1642
+ conflicts.push({
1643
+ section: "architecture.thresholds",
1644
+ key: category,
1645
+ localValue: mergedThresholds[category],
1646
+ packageValue: value,
1647
+ description: `Architecture threshold '${category}' already exists locally with a different value`
1648
+ });
1649
+ }
1650
+ }
1651
+ const mergedModules = { ...localArch.modules };
1652
+ const contributedModuleKeys = [];
1653
+ const bundleModules = bundleConstraints.architecture.modules ?? {};
1654
+ for (const [modulePath, bundleCategoryMap] of Object.entries(bundleModules)) {
1655
+ if (!(modulePath in mergedModules)) {
1656
+ mergedModules[modulePath] = bundleCategoryMap;
1657
+ for (const cat of Object.keys(bundleCategoryMap)) {
1658
+ contributedModuleKeys.push(`${modulePath}:${cat}`);
1659
+ }
1660
+ } else {
1661
+ const localCategoryMap = mergedModules[modulePath];
1662
+ const mergedCategoryMap = { ...localCategoryMap };
1663
+ for (const [category, value] of Object.entries(bundleCategoryMap)) {
1664
+ if (!(category in mergedCategoryMap)) {
1665
+ mergedCategoryMap[category] = value;
1666
+ contributedModuleKeys.push(`${modulePath}:${category}`);
1667
+ } else if (!deepEqual(mergedCategoryMap[category], value)) {
1668
+ conflicts.push({
1669
+ section: "architecture.modules",
1670
+ key: `${modulePath}:${category}`,
1671
+ localValue: mergedCategoryMap[category],
1672
+ packageValue: value,
1673
+ description: `Architecture module override '${modulePath}' category '${category}' already exists locally with a different value`
1674
+ });
1675
+ }
1676
+ }
1677
+ mergedModules[modulePath] = mergedCategoryMap;
1678
+ }
1679
+ }
1680
+ config.architecture = {
1681
+ ...localArch,
1682
+ thresholds: mergedThresholds,
1683
+ modules: mergedModules
1684
+ };
1685
+ if (contributedThresholdKeys.length > 0) {
1686
+ contributions["architecture.thresholds"] = contributedThresholdKeys;
1687
+ }
1688
+ if (contributedModuleKeys.length > 0) {
1689
+ contributions["architecture.modules"] = contributedModuleKeys;
1690
+ }
1691
+ }
1692
+ if (bundleConstraints.security?.rules) {
1693
+ const localSecurity = localConfig.security ?? { rules: {} };
1694
+ const localRules = localSecurity.rules ?? {};
1695
+ const mergedRules = { ...localRules };
1696
+ const contributedRuleIds = [];
1697
+ for (const [ruleId, severity] of Object.entries(bundleConstraints.security.rules)) {
1698
+ if (!(ruleId in mergedRules)) {
1699
+ mergedRules[ruleId] = severity;
1700
+ contributedRuleIds.push(ruleId);
1701
+ } else if (mergedRules[ruleId] !== severity) {
1702
+ conflicts.push({
1703
+ section: "security.rules",
1704
+ key: ruleId,
1705
+ localValue: mergedRules[ruleId],
1706
+ packageValue: severity,
1707
+ description: `Security rule '${ruleId}' already exists locally with severity '${mergedRules[ruleId]}', bundle has '${severity}'`
1708
+ });
1709
+ }
1710
+ }
1711
+ config.security = { ...localSecurity, rules: mergedRules };
1712
+ if (contributedRuleIds.length > 0) {
1713
+ contributions["security.rules"] = contributedRuleIds;
1714
+ }
1715
+ }
1716
+ return { config, contributions, conflicts };
1717
+ }
1718
+
1719
+ // src/constraints/sharing/lockfile.ts
1720
+ var fs2 = __toESM(require("fs/promises"));
1721
+ async function readLockfile(lockfilePath) {
1722
+ let raw;
1723
+ try {
1724
+ raw = await fs2.readFile(lockfilePath, "utf-8");
1725
+ } catch (err) {
1726
+ if (isNodeError(err) && err.code === "ENOENT") {
1727
+ return { ok: true, value: null };
1728
+ }
1729
+ return {
1730
+ ok: false,
1731
+ error: `Failed to read lockfile: ${err instanceof Error ? err.message : String(err)}`
1732
+ };
1733
+ }
1734
+ let parsed;
1735
+ try {
1736
+ parsed = JSON.parse(raw);
1737
+ } catch {
1738
+ return {
1739
+ ok: false,
1740
+ error: `Failed to parse lockfile as JSON: file contains invalid JSON`
1741
+ };
1742
+ }
1743
+ const result = LockfileSchema.safeParse(parsed);
1744
+ if (!result.success) {
1745
+ return {
1746
+ ok: false,
1747
+ error: `Lockfile schema validation failed: ${result.error.issues.map((i) => i.message).join(", ")}`
1748
+ };
1749
+ }
1750
+ return { ok: true, value: result.data };
1751
+ }
1752
+ async function writeLockfile(lockfilePath, lockfile) {
1753
+ await writeConfig(lockfilePath, lockfile);
1754
+ }
1755
+ function addProvenance(lockfile, packageName, entry) {
1756
+ return {
1757
+ ...lockfile,
1758
+ packages: {
1759
+ ...lockfile.packages,
1760
+ [packageName]: entry
1761
+ }
1762
+ };
1763
+ }
1764
+ function removeProvenance(lockfile, packageName) {
1765
+ const existing = lockfile.packages[packageName];
1766
+ if (!existing) {
1767
+ return { lockfile, contributions: null };
1768
+ }
1769
+ const { [packageName]: _removed, ...remaining } = lockfile.packages;
1770
+ return {
1771
+ lockfile: {
1772
+ ...lockfile,
1773
+ packages: remaining
1774
+ },
1775
+ contributions: existing.contributions ?? null
1776
+ };
1777
+ }
1778
+ function isNodeError(err) {
1779
+ return err instanceof Error && "code" in err;
1780
+ }
1781
+
1317
1782
  // src/shared/parsers/typescript.ts
1318
1783
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
1319
1784
 
@@ -1339,11 +1804,11 @@ function walk(node, visitor) {
1339
1804
  var TypeScriptParser = class {
1340
1805
  name = "typescript";
1341
1806
  extensions = [".ts", ".tsx", ".mts", ".cts"];
1342
- async parseFile(path11) {
1343
- const contentResult = await readFileContent(path11);
1807
+ async parseFile(path13) {
1808
+ const contentResult = await readFileContent(path13);
1344
1809
  if (!contentResult.ok) {
1345
1810
  return (0, import_types.Err)(
1346
- createParseError("NOT_FOUND", `File not found: ${path11}`, { path: path11 }, [
1811
+ createParseError("NOT_FOUND", `File not found: ${path13}`, { path: path13 }, [
1347
1812
  "Check that the file exists",
1348
1813
  "Verify the path is correct"
1349
1814
  ])
@@ -1353,7 +1818,7 @@ var TypeScriptParser = class {
1353
1818
  const ast = (0, import_typescript_estree.parse)(contentResult.value, {
1354
1819
  loc: true,
1355
1820
  range: true,
1356
- jsx: path11.endsWith(".tsx"),
1821
+ jsx: path13.endsWith(".tsx"),
1357
1822
  errorOnUnknownASTType: false
1358
1823
  });
1359
1824
  return (0, import_types.Ok)({
@@ -1364,7 +1829,7 @@ var TypeScriptParser = class {
1364
1829
  } catch (e) {
1365
1830
  const error = e;
1366
1831
  return (0, import_types.Err)(
1367
- createParseError("SYNTAX_ERROR", `Failed to parse ${path11}: ${error.message}`, { path: path11 }, [
1832
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path13}: ${error.message}`, { path: path13 }, [
1368
1833
  "Check for syntax errors in the file",
1369
1834
  "Ensure valid TypeScript syntax"
1370
1835
  ])
@@ -1531,7 +1996,7 @@ var TypeScriptParser = class {
1531
1996
 
1532
1997
  // src/entropy/snapshot.ts
1533
1998
  var import_path6 = require("path");
1534
- var import_minimatch2 = require("minimatch");
1999
+ var import_minimatch3 = require("minimatch");
1535
2000
  async function resolveEntryPoints(rootDir, explicitEntries) {
1536
2001
  if (explicitEntries && explicitEntries.length > 0) {
1537
2002
  const resolved = explicitEntries.map((e) => (0, import_path6.resolve)(rootDir, e));
@@ -1648,22 +2113,22 @@ function extractInlineRefs(content) {
1648
2113
  }
1649
2114
  return refs;
1650
2115
  }
1651
- async function parseDocumentationFile(path11) {
1652
- const contentResult = await readFileContent(path11);
2116
+ async function parseDocumentationFile(path13) {
2117
+ const contentResult = await readFileContent(path13);
1653
2118
  if (!contentResult.ok) {
1654
2119
  return (0, import_types.Err)(
1655
2120
  createEntropyError(
1656
2121
  "PARSE_ERROR",
1657
- `Failed to read documentation file: ${path11}`,
1658
- { file: path11 },
2122
+ `Failed to read documentation file: ${path13}`,
2123
+ { file: path13 },
1659
2124
  ["Check that the file exists"]
1660
2125
  )
1661
2126
  );
1662
2127
  }
1663
2128
  const content = contentResult.value;
1664
- const type = path11.endsWith(".md") ? "markdown" : "text";
2129
+ const type = path13.endsWith(".md") ? "markdown" : "text";
1665
2130
  return (0, import_types.Ok)({
1666
- path: path11,
2131
+ path: path13,
1667
2132
  type,
1668
2133
  content,
1669
2134
  codeBlocks: extractCodeBlocks(content),
@@ -1795,7 +2260,7 @@ async function buildSnapshot(config) {
1795
2260
  }
1796
2261
  sourceFilePaths = sourceFilePaths.filter((f) => {
1797
2262
  const rel = (0, import_path6.relative)(rootDir, f);
1798
- return !excludePatterns.some((p) => (0, import_minimatch2.minimatch)(rel, p));
2263
+ return !excludePatterns.some((p) => (0, import_minimatch3.minimatch)(rel, p));
1799
2264
  });
1800
2265
  const files = [];
1801
2266
  for (const filePath of sourceFilePaths) {
@@ -2325,11 +2790,11 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
2325
2790
  }
2326
2791
 
2327
2792
  // src/entropy/detectors/patterns.ts
2328
- var import_minimatch3 = require("minimatch");
2793
+ var import_minimatch4 = require("minimatch");
2329
2794
  var import_path9 = require("path");
2330
2795
  function fileMatchesPattern(filePath, pattern, rootDir) {
2331
2796
  const relativePath = (0, import_path9.relative)(rootDir, filePath);
2332
- return (0, import_minimatch3.minimatch)(relativePath, pattern);
2797
+ return (0, import_minimatch4.minimatch)(relativePath, pattern);
2333
2798
  }
2334
2799
  function checkConfigPattern(pattern, file, rootDir) {
2335
2800
  const matches = [];
@@ -3331,14 +3796,14 @@ var EntropyAnalyzer = class {
3331
3796
  };
3332
3797
 
3333
3798
  // src/entropy/fixers/safe-fixes.ts
3334
- var fs = __toESM(require("fs"));
3799
+ var fs3 = __toESM(require("fs"));
3335
3800
  var import_util2 = require("util");
3336
3801
  var import_path10 = require("path");
3337
- var readFile4 = (0, import_util2.promisify)(fs.readFile);
3338
- var writeFile2 = (0, import_util2.promisify)(fs.writeFile);
3339
- var unlink2 = (0, import_util2.promisify)(fs.unlink);
3340
- var mkdir2 = (0, import_util2.promisify)(fs.mkdir);
3341
- var copyFile2 = (0, import_util2.promisify)(fs.copyFile);
3802
+ var readFile5 = (0, import_util2.promisify)(fs3.readFile);
3803
+ var writeFile3 = (0, import_util2.promisify)(fs3.writeFile);
3804
+ var unlink2 = (0, import_util2.promisify)(fs3.unlink);
3805
+ var mkdir2 = (0, import_util2.promisify)(fs3.mkdir);
3806
+ var copyFile2 = (0, import_util2.promisify)(fs3.copyFile);
3342
3807
  var DEFAULT_FIX_CONFIG = {
3343
3808
  dryRun: false,
3344
3809
  fixTypes: ["unused-imports", "dead-files"],
@@ -3465,25 +3930,25 @@ async function applySingleFix(fix, config) {
3465
3930
  break;
3466
3931
  case "delete-lines":
3467
3932
  if (fix.line !== void 0) {
3468
- const content = await readFile4(fix.file, "utf-8");
3933
+ const content = await readFile5(fix.file, "utf-8");
3469
3934
  const lines = content.split("\n");
3470
3935
  lines.splice(fix.line - 1, 1);
3471
- await writeFile2(fix.file, lines.join("\n"));
3936
+ await writeFile3(fix.file, lines.join("\n"));
3472
3937
  }
3473
3938
  break;
3474
3939
  case "replace":
3475
3940
  if (fix.oldContent && fix.newContent !== void 0) {
3476
- const content = await readFile4(fix.file, "utf-8");
3941
+ const content = await readFile5(fix.file, "utf-8");
3477
3942
  const newContent = content.replace(fix.oldContent, fix.newContent);
3478
- await writeFile2(fix.file, newContent);
3943
+ await writeFile3(fix.file, newContent);
3479
3944
  }
3480
3945
  break;
3481
3946
  case "insert":
3482
3947
  if (fix.line !== void 0 && fix.newContent) {
3483
- const content = await readFile4(fix.file, "utf-8");
3948
+ const content = await readFile5(fix.file, "utf-8");
3484
3949
  const lines = content.split("\n");
3485
3950
  lines.splice(fix.line - 1, 0, fix.newContent);
3486
- await writeFile2(fix.file, lines.join("\n"));
3951
+ await writeFile3(fix.file, lines.join("\n"));
3487
3952
  }
3488
3953
  break;
3489
3954
  }
@@ -3660,46 +4125,46 @@ function deduplicateCleanupFindings(findings) {
3660
4125
  }
3661
4126
 
3662
4127
  // src/entropy/config/schema.ts
3663
- var import_zod = require("zod");
3664
- var MustExportRuleSchema = import_zod.z.object({
3665
- type: import_zod.z.literal("must-export"),
3666
- names: import_zod.z.array(import_zod.z.string())
4128
+ var import_zod2 = require("zod");
4129
+ var MustExportRuleSchema = import_zod2.z.object({
4130
+ type: import_zod2.z.literal("must-export"),
4131
+ names: import_zod2.z.array(import_zod2.z.string())
3667
4132
  });
3668
- var MustExportDefaultRuleSchema = import_zod.z.object({
3669
- type: import_zod.z.literal("must-export-default"),
3670
- kind: import_zod.z.enum(["class", "function", "object"]).optional()
4133
+ var MustExportDefaultRuleSchema = import_zod2.z.object({
4134
+ type: import_zod2.z.literal("must-export-default"),
4135
+ kind: import_zod2.z.enum(["class", "function", "object"]).optional()
3671
4136
  });
3672
- var NoExportRuleSchema = import_zod.z.object({
3673
- type: import_zod.z.literal("no-export"),
3674
- names: import_zod.z.array(import_zod.z.string())
4137
+ var NoExportRuleSchema = import_zod2.z.object({
4138
+ type: import_zod2.z.literal("no-export"),
4139
+ names: import_zod2.z.array(import_zod2.z.string())
3675
4140
  });
3676
- var MustImportRuleSchema = import_zod.z.object({
3677
- type: import_zod.z.literal("must-import"),
3678
- from: import_zod.z.string(),
3679
- names: import_zod.z.array(import_zod.z.string()).optional()
4141
+ var MustImportRuleSchema = import_zod2.z.object({
4142
+ type: import_zod2.z.literal("must-import"),
4143
+ from: import_zod2.z.string(),
4144
+ names: import_zod2.z.array(import_zod2.z.string()).optional()
3680
4145
  });
3681
- var NoImportRuleSchema = import_zod.z.object({
3682
- type: import_zod.z.literal("no-import"),
3683
- from: import_zod.z.string()
4146
+ var NoImportRuleSchema = import_zod2.z.object({
4147
+ type: import_zod2.z.literal("no-import"),
4148
+ from: import_zod2.z.string()
3684
4149
  });
3685
- var NamingRuleSchema = import_zod.z.object({
3686
- type: import_zod.z.literal("naming"),
3687
- match: import_zod.z.string(),
3688
- convention: import_zod.z.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
4150
+ var NamingRuleSchema = import_zod2.z.object({
4151
+ type: import_zod2.z.literal("naming"),
4152
+ match: import_zod2.z.string(),
4153
+ convention: import_zod2.z.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
3689
4154
  });
3690
- var MaxExportsRuleSchema = import_zod.z.object({
3691
- type: import_zod.z.literal("max-exports"),
3692
- count: import_zod.z.number().positive()
4155
+ var MaxExportsRuleSchema = import_zod2.z.object({
4156
+ type: import_zod2.z.literal("max-exports"),
4157
+ count: import_zod2.z.number().positive()
3693
4158
  });
3694
- var MaxLinesRuleSchema = import_zod.z.object({
3695
- type: import_zod.z.literal("max-lines"),
3696
- count: import_zod.z.number().positive()
4159
+ var MaxLinesRuleSchema = import_zod2.z.object({
4160
+ type: import_zod2.z.literal("max-lines"),
4161
+ count: import_zod2.z.number().positive()
3697
4162
  });
3698
- var RequireJSDocRuleSchema = import_zod.z.object({
3699
- type: import_zod.z.literal("require-jsdoc"),
3700
- for: import_zod.z.array(import_zod.z.enum(["function", "class", "export"]))
4163
+ var RequireJSDocRuleSchema = import_zod2.z.object({
4164
+ type: import_zod2.z.literal("require-jsdoc"),
4165
+ for: import_zod2.z.array(import_zod2.z.enum(["function", "class", "export"]))
3701
4166
  });
3702
- var RuleSchema = import_zod.z.discriminatedUnion("type", [
4167
+ var RuleSchema = import_zod2.z.discriminatedUnion("type", [
3703
4168
  MustExportRuleSchema,
3704
4169
  MustExportDefaultRuleSchema,
3705
4170
  NoExportRuleSchema,
@@ -3710,47 +4175,47 @@ var RuleSchema = import_zod.z.discriminatedUnion("type", [
3710
4175
  MaxLinesRuleSchema,
3711
4176
  RequireJSDocRuleSchema
3712
4177
  ]);
3713
- var ConfigPatternSchema = import_zod.z.object({
3714
- name: import_zod.z.string().min(1),
3715
- description: import_zod.z.string(),
3716
- severity: import_zod.z.enum(["error", "warning"]),
3717
- files: import_zod.z.array(import_zod.z.string()),
4178
+ var ConfigPatternSchema = import_zod2.z.object({
4179
+ name: import_zod2.z.string().min(1),
4180
+ description: import_zod2.z.string(),
4181
+ severity: import_zod2.z.enum(["error", "warning"]),
4182
+ files: import_zod2.z.array(import_zod2.z.string()),
3718
4183
  rule: RuleSchema,
3719
- message: import_zod.z.string().optional()
4184
+ message: import_zod2.z.string().optional()
3720
4185
  });
3721
- var PatternConfigSchema = import_zod.z.object({
3722
- patterns: import_zod.z.array(ConfigPatternSchema),
3723
- customPatterns: import_zod.z.array(import_zod.z.any()).optional(),
4186
+ var PatternConfigSchema = import_zod2.z.object({
4187
+ patterns: import_zod2.z.array(ConfigPatternSchema),
4188
+ customPatterns: import_zod2.z.array(import_zod2.z.any()).optional(),
3724
4189
  // Code patterns are functions, can't validate
3725
- ignoreFiles: import_zod.z.array(import_zod.z.string()).optional()
4190
+ ignoreFiles: import_zod2.z.array(import_zod2.z.string()).optional()
3726
4191
  });
3727
- var DriftConfigSchema = import_zod.z.object({
3728
- docPaths: import_zod.z.array(import_zod.z.string()).optional(),
3729
- checkApiSignatures: import_zod.z.boolean().optional(),
3730
- checkExamples: import_zod.z.boolean().optional(),
3731
- checkStructure: import_zod.z.boolean().optional(),
3732
- ignorePatterns: import_zod.z.array(import_zod.z.string()).optional()
4192
+ var DriftConfigSchema = import_zod2.z.object({
4193
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional(),
4194
+ checkApiSignatures: import_zod2.z.boolean().optional(),
4195
+ checkExamples: import_zod2.z.boolean().optional(),
4196
+ checkStructure: import_zod2.z.boolean().optional(),
4197
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional()
3733
4198
  });
3734
- var DeadCodeConfigSchema = import_zod.z.object({
3735
- entryPoints: import_zod.z.array(import_zod.z.string()).optional(),
3736
- includeTypes: import_zod.z.boolean().optional(),
3737
- includeInternals: import_zod.z.boolean().optional(),
3738
- ignorePatterns: import_zod.z.array(import_zod.z.string()).optional(),
3739
- treatDynamicImportsAs: import_zod.z.enum(["used", "unknown"]).optional()
4199
+ var DeadCodeConfigSchema = import_zod2.z.object({
4200
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4201
+ includeTypes: import_zod2.z.boolean().optional(),
4202
+ includeInternals: import_zod2.z.boolean().optional(),
4203
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional(),
4204
+ treatDynamicImportsAs: import_zod2.z.enum(["used", "unknown"]).optional()
3740
4205
  });
3741
- var EntropyConfigSchema = import_zod.z.object({
3742
- rootDir: import_zod.z.string(),
3743
- parser: import_zod.z.any().optional(),
4206
+ var EntropyConfigSchema = import_zod2.z.object({
4207
+ rootDir: import_zod2.z.string(),
4208
+ parser: import_zod2.z.any().optional(),
3744
4209
  // LanguageParser instance, can't validate
3745
- entryPoints: import_zod.z.array(import_zod.z.string()).optional(),
3746
- analyze: import_zod.z.object({
3747
- drift: import_zod.z.union([import_zod.z.boolean(), DriftConfigSchema]).optional(),
3748
- deadCode: import_zod.z.union([import_zod.z.boolean(), DeadCodeConfigSchema]).optional(),
3749
- patterns: import_zod.z.union([import_zod.z.boolean(), PatternConfigSchema]).optional()
4210
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4211
+ analyze: import_zod2.z.object({
4212
+ drift: import_zod2.z.union([import_zod2.z.boolean(), DriftConfigSchema]).optional(),
4213
+ deadCode: import_zod2.z.union([import_zod2.z.boolean(), DeadCodeConfigSchema]).optional(),
4214
+ patterns: import_zod2.z.union([import_zod2.z.boolean(), PatternConfigSchema]).optional()
3750
4215
  }),
3751
- include: import_zod.z.array(import_zod.z.string()).optional(),
3752
- exclude: import_zod.z.array(import_zod.z.string()).optional(),
3753
- docPaths: import_zod.z.array(import_zod.z.string()).optional()
4216
+ include: import_zod2.z.array(import_zod2.z.string()).optional(),
4217
+ exclude: import_zod2.z.array(import_zod2.z.string()).optional(),
4218
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional()
3754
4219
  });
3755
4220
  function validatePatternConfig(config) {
3756
4221
  const result = PatternConfigSchema.safeParse(config);
@@ -4007,7 +4472,7 @@ var RegressionDetector = class {
4007
4472
  };
4008
4473
 
4009
4474
  // src/performance/critical-path.ts
4010
- var fs2 = __toESM(require("fs"));
4475
+ var fs4 = __toESM(require("fs"));
4011
4476
  var path = __toESM(require("path"));
4012
4477
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git"]);
4013
4478
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
@@ -4059,7 +4524,7 @@ var CriticalPathResolver = class {
4059
4524
  walkDir(dir, entries) {
4060
4525
  let items;
4061
4526
  try {
4062
- items = fs2.readdirSync(dir, { withFileTypes: true });
4527
+ items = fs4.readdirSync(dir, { withFileTypes: true });
4063
4528
  } catch {
4064
4529
  return;
4065
4530
  }
@@ -4075,7 +4540,7 @@ var CriticalPathResolver = class {
4075
4540
  scanFile(filePath, entries) {
4076
4541
  let content;
4077
4542
  try {
4078
- content = fs2.readFileSync(filePath, "utf-8");
4543
+ content = fs4.readFileSync(filePath, "utf-8");
4079
4544
  } catch {
4080
4545
  return;
4081
4546
  }
@@ -4254,17 +4719,17 @@ function resetFeedbackConfig() {
4254
4719
  }
4255
4720
 
4256
4721
  // src/feedback/review/diff-analyzer.ts
4257
- function parseDiff(diff) {
4722
+ function parseDiff(diff2) {
4258
4723
  try {
4259
- if (!diff.trim()) {
4260
- return (0, import_types.Ok)({ diff, files: [] });
4724
+ if (!diff2.trim()) {
4725
+ return (0, import_types.Ok)({ diff: diff2, files: [] });
4261
4726
  }
4262
4727
  const files = [];
4263
4728
  const newFileRegex = /new file mode/;
4264
4729
  const deletedFileRegex = /deleted file mode/;
4265
4730
  const additionRegex = /^\+(?!\+\+)/gm;
4266
4731
  const deletionRegex = /^-(?!--)/gm;
4267
- const diffParts = diff.split(/(?=diff --git)/);
4732
+ const diffParts = diff2.split(/(?=diff --git)/);
4268
4733
  for (const part of diffParts) {
4269
4734
  if (!part.trim()) continue;
4270
4735
  const headerMatch = /diff --git a\/(.+?) b\/(.+?)(?:\n|$)/.exec(part);
@@ -4287,7 +4752,7 @@ function parseDiff(diff) {
4287
4752
  deletions
4288
4753
  });
4289
4754
  }
4290
- return (0, import_types.Ok)({ diff, files });
4755
+ return (0, import_types.Ok)({ diff: diff2, files });
4291
4756
  } catch (error) {
4292
4757
  return (0, import_types.Err)({
4293
4758
  code: "DIFF_PARSE_ERROR",
@@ -4891,77 +5356,1265 @@ var NoOpSink = class {
4891
5356
  }
4892
5357
  };
4893
5358
 
5359
+ // src/architecture/types.ts
5360
+ var import_zod3 = require("zod");
5361
+ var ArchMetricCategorySchema = import_zod3.z.enum([
5362
+ "circular-deps",
5363
+ "layer-violations",
5364
+ "complexity",
5365
+ "coupling",
5366
+ "forbidden-imports",
5367
+ "module-size",
5368
+ "dependency-depth"
5369
+ ]);
5370
+ var ViolationSchema = import_zod3.z.object({
5371
+ id: import_zod3.z.string(),
5372
+ // stable hash: sha256(relativePath + ':' + category + ':' + normalizedDetail)
5373
+ file: import_zod3.z.string(),
5374
+ // relative to project root
5375
+ category: ArchMetricCategorySchema.optional(),
5376
+ // context for baseline reporting
5377
+ detail: import_zod3.z.string(),
5378
+ // human-readable description
5379
+ severity: import_zod3.z.enum(["error", "warning"])
5380
+ });
5381
+ var MetricResultSchema = import_zod3.z.object({
5382
+ category: ArchMetricCategorySchema,
5383
+ scope: import_zod3.z.string(),
5384
+ // e.g., 'project', 'src/services', 'src/api/routes.ts'
5385
+ value: import_zod3.z.number(),
5386
+ // numeric metric (violation count, complexity score, etc.)
5387
+ violations: import_zod3.z.array(ViolationSchema),
5388
+ metadata: import_zod3.z.record(import_zod3.z.unknown()).optional()
5389
+ });
5390
+ var CategoryBaselineSchema = import_zod3.z.object({
5391
+ value: import_zod3.z.number(),
5392
+ // aggregate metric value at baseline time
5393
+ violationIds: import_zod3.z.array(import_zod3.z.string())
5394
+ // stable IDs of known violations (the allowlist)
5395
+ });
5396
+ var ArchBaselineSchema = import_zod3.z.object({
5397
+ version: import_zod3.z.literal(1),
5398
+ updatedAt: import_zod3.z.string().datetime(),
5399
+ // ISO 8601
5400
+ updatedFrom: import_zod3.z.string(),
5401
+ // commit hash
5402
+ metrics: import_zod3.z.record(ArchMetricCategorySchema, CategoryBaselineSchema)
5403
+ });
5404
+ var CategoryRegressionSchema = import_zod3.z.object({
5405
+ category: ArchMetricCategorySchema,
5406
+ baselineValue: import_zod3.z.number(),
5407
+ currentValue: import_zod3.z.number(),
5408
+ delta: import_zod3.z.number()
5409
+ });
5410
+ var ArchDiffResultSchema = import_zod3.z.object({
5411
+ passed: import_zod3.z.boolean(),
5412
+ newViolations: import_zod3.z.array(ViolationSchema),
5413
+ // in current but not in baseline -> FAIL
5414
+ resolvedViolations: import_zod3.z.array(import_zod3.z.string()),
5415
+ // in baseline but not in current -> celebrate
5416
+ preExisting: import_zod3.z.array(import_zod3.z.string()),
5417
+ // in both -> allowed, tracked
5418
+ regressions: import_zod3.z.array(CategoryRegressionSchema)
5419
+ // aggregate value exceeded baseline
5420
+ });
5421
+ var ThresholdConfigSchema = import_zod3.z.record(
5422
+ ArchMetricCategorySchema,
5423
+ import_zod3.z.union([import_zod3.z.number(), import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number())])
5424
+ );
5425
+ var ArchConfigSchema = import_zod3.z.object({
5426
+ enabled: import_zod3.z.boolean().default(true),
5427
+ baselinePath: import_zod3.z.string().default(".harness/arch/baselines.json"),
5428
+ thresholds: ThresholdConfigSchema.default({}),
5429
+ modules: import_zod3.z.record(import_zod3.z.string(), ThresholdConfigSchema).default({})
5430
+ });
5431
+ var ConstraintRuleSchema = import_zod3.z.object({
5432
+ id: import_zod3.z.string(),
5433
+ // stable hash: sha256(category + ':' + scope + ':' + description)
5434
+ category: ArchMetricCategorySchema,
5435
+ description: import_zod3.z.string(),
5436
+ // e.g., "Layer 'services' must not import from 'ui'"
5437
+ scope: import_zod3.z.string(),
5438
+ // e.g., 'src/services/', 'project'
5439
+ targets: import_zod3.z.array(import_zod3.z.string()).optional()
5440
+ // forward-compat for governs edges
5441
+ });
5442
+
5443
+ // src/architecture/collectors/circular-deps.ts
5444
+ var import_node_path3 = require("path");
5445
+
5446
+ // src/architecture/collectors/hash.ts
5447
+ var import_node_crypto = require("crypto");
5448
+ function violationId(relativePath, category, normalizedDetail) {
5449
+ const path13 = relativePath.replace(/\\/g, "/");
5450
+ const input = `${path13}:${category}:${normalizedDetail}`;
5451
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5452
+ }
5453
+ function constraintRuleId(category, scope, description) {
5454
+ const input = `${category}:${scope}:${description}`;
5455
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5456
+ }
5457
+
5458
+ // src/architecture/collectors/circular-deps.ts
5459
+ var CircularDepsCollector = class {
5460
+ category = "circular-deps";
5461
+ getRules(_config, _rootDir) {
5462
+ const description = "No circular dependencies allowed";
5463
+ return [
5464
+ {
5465
+ id: constraintRuleId(this.category, "project", description),
5466
+ category: this.category,
5467
+ description,
5468
+ scope: "project"
5469
+ }
5470
+ ];
5471
+ }
5472
+ async collect(_config, rootDir) {
5473
+ const files = await findFiles("**/*.ts", rootDir);
5474
+ const stubParser = {
5475
+ name: "typescript",
5476
+ extensions: [".ts", ".tsx"],
5477
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "not needed" } }),
5478
+ extractImports: () => ({
5479
+ ok: false,
5480
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5481
+ }),
5482
+ extractExports: () => ({
5483
+ ok: false,
5484
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5485
+ }),
5486
+ health: async () => ({ ok: true, value: { available: true } })
5487
+ };
5488
+ const graphResult = await buildDependencyGraph(files, stubParser);
5489
+ if (!graphResult.ok) {
5490
+ return [
5491
+ {
5492
+ category: this.category,
5493
+ scope: "project",
5494
+ value: 0,
5495
+ violations: [],
5496
+ metadata: { error: "Failed to build dependency graph" }
5497
+ }
5498
+ ];
5499
+ }
5500
+ const result = detectCircularDeps(graphResult.value);
5501
+ if (!result.ok) {
5502
+ return [
5503
+ {
5504
+ category: this.category,
5505
+ scope: "project",
5506
+ value: 0,
5507
+ violations: [],
5508
+ metadata: { error: "Failed to detect circular deps" }
5509
+ }
5510
+ ];
5511
+ }
5512
+ const { cycles, largestCycle } = result.value;
5513
+ const violations = cycles.map((cycle) => {
5514
+ const cyclePath = cycle.cycle.map((f) => (0, import_node_path3.relative)(rootDir, f)).join(" -> ");
5515
+ const firstFile = (0, import_node_path3.relative)(rootDir, cycle.cycle[0]);
5516
+ return {
5517
+ id: violationId(firstFile, this.category, cyclePath),
5518
+ file: firstFile,
5519
+ detail: `Circular dependency: ${cyclePath}`,
5520
+ severity: cycle.severity
5521
+ };
5522
+ });
5523
+ return [
5524
+ {
5525
+ category: this.category,
5526
+ scope: "project",
5527
+ value: cycles.length,
5528
+ violations,
5529
+ metadata: { largestCycle, cycleCount: cycles.length }
5530
+ }
5531
+ ];
5532
+ }
5533
+ };
5534
+
5535
+ // src/architecture/collectors/layer-violations.ts
5536
+ var import_node_path4 = require("path");
5537
+ var LayerViolationCollector = class {
5538
+ category = "layer-violations";
5539
+ getRules(_config, _rootDir) {
5540
+ const description = "No layer boundary violations allowed";
5541
+ return [
5542
+ {
5543
+ id: constraintRuleId(this.category, "project", description),
5544
+ category: this.category,
5545
+ description,
5546
+ scope: "project"
5547
+ }
5548
+ ];
5549
+ }
5550
+ async collect(_config, rootDir) {
5551
+ const stubParser = {
5552
+ name: "typescript",
5553
+ extensions: [".ts", ".tsx"],
5554
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5555
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5556
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5557
+ health: async () => ({ ok: true, value: { available: true } })
5558
+ };
5559
+ const result = await validateDependencies({
5560
+ layers: [],
5561
+ rootDir,
5562
+ parser: stubParser,
5563
+ fallbackBehavior: "skip"
5564
+ });
5565
+ if (!result.ok) {
5566
+ return [
5567
+ {
5568
+ category: this.category,
5569
+ scope: "project",
5570
+ value: 0,
5571
+ violations: [],
5572
+ metadata: { error: "Failed to validate dependencies" }
5573
+ }
5574
+ ];
5575
+ }
5576
+ const layerViolations = result.value.violations.filter(
5577
+ (v) => v.reason === "WRONG_LAYER"
5578
+ );
5579
+ const violations = layerViolations.map((v) => {
5580
+ const relFile = (0, import_node_path4.relative)(rootDir, v.file);
5581
+ const relImport = (0, import_node_path4.relative)(rootDir, v.imports);
5582
+ const detail = `${v.fromLayer} -> ${v.toLayer}: ${relFile} imports ${relImport}`;
5583
+ return {
5584
+ id: violationId(relFile, this.category, detail),
5585
+ file: relFile,
5586
+ category: this.category,
5587
+ detail,
5588
+ severity: "error"
5589
+ };
5590
+ });
5591
+ return [
5592
+ {
5593
+ category: this.category,
5594
+ scope: "project",
5595
+ value: violations.length,
5596
+ violations
5597
+ }
5598
+ ];
5599
+ }
5600
+ };
5601
+
5602
+ // src/architecture/collectors/complexity.ts
5603
+ var import_node_path5 = require("path");
5604
+ var ComplexityCollector = class {
5605
+ category = "complexity";
5606
+ getRules(_config, _rootDir) {
5607
+ const description = "Cyclomatic complexity must stay within thresholds";
5608
+ return [
5609
+ {
5610
+ id: constraintRuleId(this.category, "project", description),
5611
+ category: this.category,
5612
+ description,
5613
+ scope: "project"
5614
+ }
5615
+ ];
5616
+ }
5617
+ async collect(_config, rootDir) {
5618
+ const files = await findFiles("**/*.ts", rootDir);
5619
+ const snapshot = {
5620
+ files: files.map((f) => ({
5621
+ path: f,
5622
+ ast: { type: "Program", body: null, language: "typescript" },
5623
+ imports: [],
5624
+ exports: [],
5625
+ internalSymbols: [],
5626
+ jsDocComments: []
5627
+ })),
5628
+ dependencyGraph: { nodes: [], edges: [] },
5629
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5630
+ docs: [],
5631
+ codeReferences: [],
5632
+ entryPoints: [],
5633
+ rootDir,
5634
+ config: { rootDir, analyze: {} },
5635
+ buildTime: 0
5636
+ };
5637
+ const complexityThreshold = _config.thresholds.complexity;
5638
+ const maxComplexity = typeof complexityThreshold === "number" ? complexityThreshold : complexityThreshold?.max ?? 15;
5639
+ const complexityConfig = {
5640
+ thresholds: {
5641
+ cyclomaticComplexity: {
5642
+ error: maxComplexity,
5643
+ warn: Math.floor(maxComplexity * 0.7)
5644
+ }
5645
+ }
5646
+ };
5647
+ const result = await detectComplexityViolations(snapshot, complexityConfig);
5648
+ if (!result.ok) {
5649
+ return [
5650
+ {
5651
+ category: this.category,
5652
+ scope: "project",
5653
+ value: 0,
5654
+ violations: [],
5655
+ metadata: { error: "Failed to detect complexity violations" }
5656
+ }
5657
+ ];
5658
+ }
5659
+ const { violations: complexityViolations, stats } = result.value;
5660
+ const filtered = complexityViolations.filter(
5661
+ (v) => v.severity === "error" || v.severity === "warning"
5662
+ );
5663
+ const violations = filtered.map((v) => {
5664
+ const relFile = (0, import_node_path5.relative)(rootDir, v.file);
5665
+ const idDetail = `${v.metric}:${v.function}`;
5666
+ return {
5667
+ id: violationId(relFile, this.category, idDetail),
5668
+ file: relFile,
5669
+ category: this.category,
5670
+ detail: `${v.metric}=${v.value} in ${v.function} (threshold: ${v.threshold})`,
5671
+ severity: v.severity
5672
+ };
5673
+ });
5674
+ return [
5675
+ {
5676
+ category: this.category,
5677
+ scope: "project",
5678
+ value: violations.length,
5679
+ violations,
5680
+ metadata: {
5681
+ filesAnalyzed: stats.filesAnalyzed,
5682
+ functionsAnalyzed: stats.functionsAnalyzed
5683
+ }
5684
+ }
5685
+ ];
5686
+ }
5687
+ };
5688
+
5689
+ // src/architecture/collectors/coupling.ts
5690
+ var import_node_path6 = require("path");
5691
+ var CouplingCollector = class {
5692
+ category = "coupling";
5693
+ getRules(_config, _rootDir) {
5694
+ const description = "Coupling metrics must stay within thresholds";
5695
+ return [
5696
+ {
5697
+ id: constraintRuleId(this.category, "project", description),
5698
+ category: this.category,
5699
+ description,
5700
+ scope: "project"
5701
+ }
5702
+ ];
5703
+ }
5704
+ async collect(_config, rootDir) {
5705
+ const files = await findFiles("**/*.ts", rootDir);
5706
+ const snapshot = {
5707
+ files: files.map((f) => ({
5708
+ path: f,
5709
+ ast: { type: "Program", body: null, language: "typescript" },
5710
+ imports: [],
5711
+ exports: [],
5712
+ internalSymbols: [],
5713
+ jsDocComments: []
5714
+ })),
5715
+ dependencyGraph: { nodes: [], edges: [] },
5716
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5717
+ docs: [],
5718
+ codeReferences: [],
5719
+ entryPoints: [],
5720
+ rootDir,
5721
+ config: { rootDir, analyze: {} },
5722
+ buildTime: 0
5723
+ };
5724
+ const result = await detectCouplingViolations(snapshot);
5725
+ if (!result.ok) {
5726
+ return [
5727
+ {
5728
+ category: this.category,
5729
+ scope: "project",
5730
+ value: 0,
5731
+ violations: [],
5732
+ metadata: { error: "Failed to detect coupling violations" }
5733
+ }
5734
+ ];
5735
+ }
5736
+ const { violations: couplingViolations, stats } = result.value;
5737
+ const filtered = couplingViolations.filter(
5738
+ (v) => v.severity === "error" || v.severity === "warning"
5739
+ );
5740
+ const violations = filtered.map((v) => {
5741
+ const relFile = (0, import_node_path6.relative)(rootDir, v.file);
5742
+ const idDetail = `${v.metric}`;
5743
+ return {
5744
+ id: violationId(relFile, this.category, idDetail),
5745
+ file: relFile,
5746
+ category: this.category,
5747
+ detail: `${v.metric}=${v.value} (threshold: ${v.threshold})`,
5748
+ severity: v.severity
5749
+ };
5750
+ });
5751
+ return [
5752
+ {
5753
+ category: this.category,
5754
+ scope: "project",
5755
+ value: violations.length,
5756
+ violations,
5757
+ metadata: { filesAnalyzed: stats.filesAnalyzed }
5758
+ }
5759
+ ];
5760
+ }
5761
+ };
5762
+
5763
+ // src/architecture/collectors/forbidden-imports.ts
5764
+ var import_node_path7 = require("path");
5765
+ var ForbiddenImportCollector = class {
5766
+ category = "forbidden-imports";
5767
+ getRules(_config, _rootDir) {
5768
+ const description = "No forbidden imports allowed";
5769
+ return [
5770
+ {
5771
+ id: constraintRuleId(this.category, "project", description),
5772
+ category: this.category,
5773
+ description,
5774
+ scope: "project"
5775
+ }
5776
+ ];
5777
+ }
5778
+ async collect(_config, rootDir) {
5779
+ const stubParser = {
5780
+ name: "typescript",
5781
+ extensions: [".ts", ".tsx"],
5782
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5783
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5784
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5785
+ health: async () => ({ ok: true, value: { available: true } })
5786
+ };
5787
+ const result = await validateDependencies({
5788
+ layers: [],
5789
+ rootDir,
5790
+ parser: stubParser,
5791
+ fallbackBehavior: "skip"
5792
+ });
5793
+ if (!result.ok) {
5794
+ return [
5795
+ {
5796
+ category: this.category,
5797
+ scope: "project",
5798
+ value: 0,
5799
+ violations: [],
5800
+ metadata: { error: "Failed to validate dependencies" }
5801
+ }
5802
+ ];
5803
+ }
5804
+ const forbidden = result.value.violations.filter(
5805
+ (v) => v.reason === "FORBIDDEN_IMPORT"
5806
+ );
5807
+ const violations = forbidden.map((v) => {
5808
+ const relFile = (0, import_node_path7.relative)(rootDir, v.file);
5809
+ const relImport = (0, import_node_path7.relative)(rootDir, v.imports);
5810
+ const detail = `forbidden import: ${relFile} -> ${relImport}`;
5811
+ return {
5812
+ id: violationId(relFile, this.category, detail),
5813
+ file: relFile,
5814
+ category: this.category,
5815
+ detail,
5816
+ severity: "error"
5817
+ };
5818
+ });
5819
+ return [
5820
+ {
5821
+ category: this.category,
5822
+ scope: "project",
5823
+ value: violations.length,
5824
+ violations
5825
+ }
5826
+ ];
5827
+ }
5828
+ };
5829
+
5830
+ // src/architecture/collectors/module-size.ts
5831
+ var import_promises2 = require("fs/promises");
5832
+ var import_node_path8 = require("path");
5833
+ async function discoverModules(rootDir) {
5834
+ const modules = [];
5835
+ async function scanDir(dir) {
5836
+ let entries;
5837
+ try {
5838
+ entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
5839
+ } catch {
5840
+ return;
5841
+ }
5842
+ const tsFiles = [];
5843
+ const subdirs = [];
5844
+ for (const entry of entries) {
5845
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") {
5846
+ continue;
5847
+ }
5848
+ const fullPath = (0, import_node_path8.join)(dir, entry.name);
5849
+ if (entry.isDirectory()) {
5850
+ subdirs.push(fullPath);
5851
+ } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".test.tsx") && !entry.name.endsWith(".spec.ts")) {
5852
+ tsFiles.push(fullPath);
5853
+ }
5854
+ }
5855
+ if (tsFiles.length > 0) {
5856
+ let totalLoc = 0;
5857
+ for (const f of tsFiles) {
5858
+ try {
5859
+ const content = await (0, import_promises2.readFile)(f, "utf-8");
5860
+ totalLoc += content.split("\n").filter((line) => line.trim().length > 0).length;
5861
+ } catch {
5862
+ }
5863
+ }
5864
+ modules.push({
5865
+ modulePath: (0, import_node_path8.relative)(rootDir, dir),
5866
+ fileCount: tsFiles.length,
5867
+ totalLoc,
5868
+ files: tsFiles.map((f) => (0, import_node_path8.relative)(rootDir, f))
5869
+ });
5870
+ }
5871
+ for (const sub of subdirs) {
5872
+ await scanDir(sub);
5873
+ }
5874
+ }
5875
+ await scanDir(rootDir);
5876
+ return modules;
5877
+ }
5878
+ var ModuleSizeCollector = class {
5879
+ category = "module-size";
5880
+ getRules(config, _rootDir) {
5881
+ const thresholds = config.thresholds["module-size"];
5882
+ let maxLoc = Infinity;
5883
+ let maxFiles = Infinity;
5884
+ if (typeof thresholds === "object" && thresholds !== null) {
5885
+ const t = thresholds;
5886
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5887
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5888
+ }
5889
+ const rules = [];
5890
+ if (maxLoc < Infinity) {
5891
+ const desc = `Module LOC must not exceed ${maxLoc}`;
5892
+ rules.push({
5893
+ id: constraintRuleId(this.category, "project", desc),
5894
+ category: this.category,
5895
+ description: desc,
5896
+ scope: "project"
5897
+ });
5898
+ }
5899
+ if (maxFiles < Infinity) {
5900
+ const desc = `Module file count must not exceed ${maxFiles}`;
5901
+ rules.push({
5902
+ id: constraintRuleId(this.category, "project", desc),
5903
+ category: this.category,
5904
+ description: desc,
5905
+ scope: "project"
5906
+ });
5907
+ }
5908
+ if (rules.length === 0) {
5909
+ const desc = "Module size must stay within thresholds";
5910
+ rules.push({
5911
+ id: constraintRuleId(this.category, "project", desc),
5912
+ category: this.category,
5913
+ description: desc,
5914
+ scope: "project"
5915
+ });
5916
+ }
5917
+ return rules;
5918
+ }
5919
+ async collect(config, rootDir) {
5920
+ const modules = await discoverModules(rootDir);
5921
+ const thresholds = config.thresholds["module-size"];
5922
+ let maxLoc = Infinity;
5923
+ let maxFiles = Infinity;
5924
+ if (typeof thresholds === "object" && thresholds !== null) {
5925
+ const t = thresholds;
5926
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5927
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5928
+ }
5929
+ return modules.map((mod) => {
5930
+ const violations = [];
5931
+ if (mod.totalLoc > maxLoc) {
5932
+ violations.push({
5933
+ id: violationId(mod.modulePath, this.category, "totalLoc-exceeded"),
5934
+ file: mod.modulePath,
5935
+ detail: `Module has ${mod.totalLoc} lines of code (threshold: ${maxLoc})`,
5936
+ severity: "warning"
5937
+ });
5938
+ }
5939
+ if (mod.fileCount > maxFiles) {
5940
+ violations.push({
5941
+ id: violationId(mod.modulePath, this.category, "fileCount-exceeded"),
5942
+ file: mod.modulePath,
5943
+ detail: `Module has ${mod.fileCount} files (threshold: ${maxFiles})`,
5944
+ severity: "warning"
5945
+ });
5946
+ }
5947
+ return {
5948
+ category: this.category,
5949
+ scope: mod.modulePath,
5950
+ value: mod.totalLoc,
5951
+ violations,
5952
+ metadata: { fileCount: mod.fileCount, totalLoc: mod.totalLoc }
5953
+ };
5954
+ });
5955
+ }
5956
+ };
5957
+
5958
+ // src/architecture/collectors/dep-depth.ts
5959
+ var import_promises3 = require("fs/promises");
5960
+ var import_node_path9 = require("path");
5961
+ function extractImportSources(content, filePath) {
5962
+ const importRegex = /(?:import|export)\s+.*?from\s+['"](\.[^'"]+)['"]/g;
5963
+ const dynamicRegex = /import\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g;
5964
+ const sources = [];
5965
+ const dir = (0, import_node_path9.dirname)(filePath);
5966
+ for (const regex of [importRegex, dynamicRegex]) {
5967
+ let match;
5968
+ while ((match = regex.exec(content)) !== null) {
5969
+ let resolved = (0, import_node_path9.resolve)(dir, match[1]);
5970
+ if (!resolved.endsWith(".ts") && !resolved.endsWith(".tsx")) {
5971
+ resolved += ".ts";
5972
+ }
5973
+ sources.push(resolved);
5974
+ }
5975
+ }
5976
+ return sources;
5977
+ }
5978
+ async function collectTsFiles(dir) {
5979
+ const results = [];
5980
+ async function scan(d) {
5981
+ let entries;
5982
+ try {
5983
+ entries = await (0, import_promises3.readdir)(d, { withFileTypes: true });
5984
+ } catch {
5985
+ return;
5986
+ }
5987
+ for (const entry of entries) {
5988
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist")
5989
+ continue;
5990
+ const fullPath = (0, import_node_path9.join)(d, entry.name);
5991
+ if (entry.isDirectory()) {
5992
+ await scan(fullPath);
5993
+ } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".test.tsx") && !entry.name.endsWith(".spec.ts")) {
5994
+ results.push(fullPath);
5995
+ }
5996
+ }
5997
+ }
5998
+ await scan(dir);
5999
+ return results;
6000
+ }
6001
+ function computeLongestChain(file, graph, visited, memo) {
6002
+ if (memo.has(file)) return memo.get(file);
6003
+ if (visited.has(file)) return 0;
6004
+ visited.add(file);
6005
+ const deps = graph.get(file) || [];
6006
+ let maxDepth = 0;
6007
+ for (const dep of deps) {
6008
+ const depth = 1 + computeLongestChain(dep, graph, visited, memo);
6009
+ if (depth > maxDepth) maxDepth = depth;
6010
+ }
6011
+ visited.delete(file);
6012
+ memo.set(file, maxDepth);
6013
+ return maxDepth;
6014
+ }
6015
+ var DepDepthCollector = class {
6016
+ category = "dependency-depth";
6017
+ getRules(config, _rootDir) {
6018
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : null;
6019
+ const desc = threshold !== null ? `Dependency chain depth must not exceed ${threshold}` : "Dependency chain depth must stay within thresholds";
6020
+ return [
6021
+ {
6022
+ id: constraintRuleId(this.category, "project", desc),
6023
+ category: this.category,
6024
+ description: desc,
6025
+ scope: "project"
6026
+ }
6027
+ ];
6028
+ }
6029
+ async collect(config, rootDir) {
6030
+ const allFiles = await collectTsFiles(rootDir);
6031
+ const graph = /* @__PURE__ */ new Map();
6032
+ const fileSet = new Set(allFiles);
6033
+ for (const file of allFiles) {
6034
+ try {
6035
+ const content = await (0, import_promises3.readFile)(file, "utf-8");
6036
+ const imports = extractImportSources(content, file).filter((imp) => fileSet.has(imp));
6037
+ graph.set(file, imports);
6038
+ } catch {
6039
+ graph.set(file, []);
6040
+ }
6041
+ }
6042
+ const moduleMap = /* @__PURE__ */ new Map();
6043
+ for (const file of allFiles) {
6044
+ const relDir = (0, import_node_path9.relative)(rootDir, (0, import_node_path9.dirname)(file));
6045
+ if (!moduleMap.has(relDir)) moduleMap.set(relDir, []);
6046
+ moduleMap.get(relDir).push(file);
6047
+ }
6048
+ const memo = /* @__PURE__ */ new Map();
6049
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : Infinity;
6050
+ const results = [];
6051
+ for (const [modulePath, files] of moduleMap) {
6052
+ let longestChain = 0;
6053
+ for (const file of files) {
6054
+ const depth = computeLongestChain(file, graph, /* @__PURE__ */ new Set(), memo);
6055
+ if (depth > longestChain) longestChain = depth;
6056
+ }
6057
+ const violations = [];
6058
+ if (longestChain > threshold) {
6059
+ violations.push({
6060
+ id: violationId(modulePath, this.category, "depth-exceeded"),
6061
+ file: modulePath,
6062
+ detail: `Import chain depth is ${longestChain} (threshold: ${threshold})`,
6063
+ severity: "warning"
6064
+ });
6065
+ }
6066
+ results.push({
6067
+ category: this.category,
6068
+ scope: modulePath,
6069
+ value: longestChain,
6070
+ violations,
6071
+ metadata: { longestChain }
6072
+ });
6073
+ }
6074
+ return results;
6075
+ }
6076
+ };
6077
+
6078
+ // src/architecture/collectors/index.ts
6079
+ var defaultCollectors = [
6080
+ new CircularDepsCollector(),
6081
+ new LayerViolationCollector(),
6082
+ new ComplexityCollector(),
6083
+ new CouplingCollector(),
6084
+ new ForbiddenImportCollector(),
6085
+ new ModuleSizeCollector(),
6086
+ new DepDepthCollector()
6087
+ ];
6088
+ async function runAll(config, rootDir, collectors = defaultCollectors) {
6089
+ const results = await Promise.allSettled(collectors.map((c) => c.collect(config, rootDir)));
6090
+ const allResults = [];
6091
+ for (let i = 0; i < results.length; i++) {
6092
+ const result = results[i];
6093
+ if (result.status === "fulfilled") {
6094
+ allResults.push(...result.value);
6095
+ } else {
6096
+ allResults.push({
6097
+ category: collectors[i].category,
6098
+ scope: "project",
6099
+ value: 0,
6100
+ violations: [],
6101
+ metadata: { error: String(result.reason) }
6102
+ });
6103
+ }
6104
+ }
6105
+ return allResults;
6106
+ }
6107
+
6108
+ // src/architecture/sync-constraints.ts
6109
+ function syncConstraintNodes(store, rules, violations) {
6110
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6111
+ const ruleIds = new Set(rules.map((r) => r.id));
6112
+ const violationsByCategory = /* @__PURE__ */ new Map();
6113
+ for (const result of violations) {
6114
+ const files = result.violations.map((v) => v.file);
6115
+ const existing = violationsByCategory.get(result.category) ?? [];
6116
+ violationsByCategory.set(result.category, [...existing, ...files]);
6117
+ }
6118
+ const existingNodesById = /* @__PURE__ */ new Map();
6119
+ for (const node of store.findNodes({ type: "constraint" })) {
6120
+ existingNodesById.set(node.id, node);
6121
+ }
6122
+ for (const rule of rules) {
6123
+ const existing = existingNodesById.get(rule.id);
6124
+ const createdAt = existing?.createdAt ?? now;
6125
+ const previousLastViolatedAt = existing?.lastViolatedAt ?? null;
6126
+ const hasViolation = hasMatchingViolation(rule, violationsByCategory);
6127
+ const lastViolatedAt = hasViolation ? now : previousLastViolatedAt;
6128
+ store.upsertNode({
6129
+ id: rule.id,
6130
+ type: "constraint",
6131
+ name: rule.description,
6132
+ category: rule.category,
6133
+ scope: rule.scope,
6134
+ createdAt,
6135
+ lastViolatedAt
6136
+ });
6137
+ }
6138
+ const existingConstraints = store.findNodes({ type: "constraint" });
6139
+ for (const node of existingConstraints) {
6140
+ if (!ruleIds.has(node.id)) {
6141
+ store.removeNode(node.id);
6142
+ }
6143
+ }
6144
+ }
6145
+ function hasMatchingViolation(rule, violationsByCategory) {
6146
+ const files = violationsByCategory.get(rule.category);
6147
+ if (!files || files.length === 0) return false;
6148
+ if (rule.scope === "project") return true;
6149
+ return files.some((file) => file.startsWith(rule.scope));
6150
+ }
6151
+
6152
+ // src/architecture/detect-stale.ts
6153
+ function detectStaleConstraints(store, windowDays = 30, category) {
6154
+ const now = Date.now();
6155
+ const windowMs = windowDays * 24 * 60 * 60 * 1e3;
6156
+ const cutoff = now - windowMs;
6157
+ let constraints = store.findNodes({ type: "constraint" });
6158
+ if (category) {
6159
+ constraints = constraints.filter((n) => n.category === category);
6160
+ }
6161
+ const totalConstraints = constraints.length;
6162
+ const staleConstraints = [];
6163
+ for (const node of constraints) {
6164
+ const lastViolatedAt = node.lastViolatedAt ?? null;
6165
+ const createdAt = node.createdAt;
6166
+ const comparisonTimestamp = lastViolatedAt ?? createdAt;
6167
+ if (!comparisonTimestamp) continue;
6168
+ const timestampMs = new Date(comparisonTimestamp).getTime();
6169
+ if (timestampMs < cutoff) {
6170
+ const daysSince = Math.floor((now - timestampMs) / (24 * 60 * 60 * 1e3));
6171
+ staleConstraints.push({
6172
+ id: node.id,
6173
+ category: node.category,
6174
+ description: node.name ?? "",
6175
+ scope: node.scope ?? "project",
6176
+ lastViolatedAt,
6177
+ daysSinceLastViolation: daysSince
6178
+ });
6179
+ }
6180
+ }
6181
+ staleConstraints.sort((a, b) => b.daysSinceLastViolation - a.daysSinceLastViolation);
6182
+ return { staleConstraints, totalConstraints, windowDays };
6183
+ }
6184
+
6185
+ // src/architecture/baseline-manager.ts
6186
+ var import_node_fs3 = require("fs");
6187
+ var import_node_crypto2 = require("crypto");
6188
+ var import_node_path10 = require("path");
6189
+ var ArchBaselineManager = class {
6190
+ baselinesPath;
6191
+ constructor(projectRoot, baselinePath) {
6192
+ this.baselinesPath = baselinePath ? (0, import_node_path10.join)(projectRoot, baselinePath) : (0, import_node_path10.join)(projectRoot, ".harness", "arch", "baselines.json");
6193
+ }
6194
+ /**
6195
+ * Snapshot the current metric results into an ArchBaseline.
6196
+ * Aggregates multiple MetricResults for the same category by summing values
6197
+ * and concatenating violation IDs.
6198
+ */
6199
+ capture(results, commitHash) {
6200
+ const metrics = {};
6201
+ for (const result of results) {
6202
+ const existing = metrics[result.category];
6203
+ if (existing) {
6204
+ existing.value += result.value;
6205
+ existing.violationIds.push(...result.violations.map((v) => v.id));
6206
+ } else {
6207
+ metrics[result.category] = {
6208
+ value: result.value,
6209
+ violationIds: result.violations.map((v) => v.id)
6210
+ };
6211
+ }
6212
+ }
6213
+ return {
6214
+ version: 1,
6215
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6216
+ updatedFrom: commitHash,
6217
+ metrics
6218
+ };
6219
+ }
6220
+ /**
6221
+ * Load the baselines file from disk.
6222
+ * Returns null if the file does not exist, contains invalid JSON,
6223
+ * or fails ArchBaselineSchema validation.
6224
+ */
6225
+ load() {
6226
+ if (!(0, import_node_fs3.existsSync)(this.baselinesPath)) {
6227
+ console.error(`Baseline file not found at: ${this.baselinesPath}`);
6228
+ return null;
6229
+ }
6230
+ try {
6231
+ const raw = (0, import_node_fs3.readFileSync)(this.baselinesPath, "utf-8");
6232
+ const data = JSON.parse(raw);
6233
+ const parsed = ArchBaselineSchema.safeParse(data);
6234
+ if (!parsed.success) {
6235
+ console.error(
6236
+ `Baseline validation failed for ${this.baselinesPath}:`,
6237
+ parsed.error.format()
6238
+ );
6239
+ return null;
6240
+ }
6241
+ return parsed.data;
6242
+ } catch (error) {
6243
+ console.error(`Error loading baseline from ${this.baselinesPath}:`, error);
6244
+ return null;
6245
+ }
6246
+ }
6247
+ /**
6248
+ * Save an ArchBaseline to disk.
6249
+ * Creates parent directories if they do not exist.
6250
+ * Uses atomic write (write to temp file, then rename) to prevent corruption.
6251
+ */
6252
+ save(baseline) {
6253
+ const dir = (0, import_node_path10.dirname)(this.baselinesPath);
6254
+ if (!(0, import_node_fs3.existsSync)(dir)) {
6255
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
6256
+ }
6257
+ const tmp = this.baselinesPath + "." + (0, import_node_crypto2.randomBytes)(4).toString("hex") + ".tmp";
6258
+ (0, import_node_fs3.writeFileSync)(tmp, JSON.stringify(baseline, null, 2));
6259
+ (0, import_node_fs3.renameSync)(tmp, this.baselinesPath);
6260
+ }
6261
+ };
6262
+
6263
+ // src/architecture/diff.ts
6264
+ function aggregateByCategory(results) {
6265
+ const map = /* @__PURE__ */ new Map();
6266
+ for (const result of results) {
6267
+ const existing = map.get(result.category);
6268
+ if (existing) {
6269
+ existing.value += result.value;
6270
+ existing.violations.push(...result.violations);
6271
+ } else {
6272
+ map.set(result.category, {
6273
+ value: result.value,
6274
+ violations: [...result.violations]
6275
+ });
6276
+ }
6277
+ }
6278
+ return map;
6279
+ }
6280
+ function diff(current, baseline) {
6281
+ const aggregated = aggregateByCategory(current);
6282
+ const newViolations = [];
6283
+ const resolvedViolations = [];
6284
+ const preExisting = [];
6285
+ const regressions = [];
6286
+ const visitedCategories = /* @__PURE__ */ new Set();
6287
+ for (const [category, agg] of aggregated) {
6288
+ visitedCategories.add(category);
6289
+ const baselineCategory = baseline.metrics[category];
6290
+ const baselineViolationIds = new Set(baselineCategory?.violationIds ?? []);
6291
+ const baselineValue = baselineCategory?.value ?? 0;
6292
+ for (const violation of agg.violations) {
6293
+ if (baselineViolationIds.has(violation.id)) {
6294
+ preExisting.push(violation.id);
6295
+ } else {
6296
+ newViolations.push(violation);
6297
+ }
6298
+ }
6299
+ const currentViolationIds = new Set(agg.violations.map((v) => v.id));
6300
+ if (baselineCategory) {
6301
+ for (const id of baselineCategory.violationIds) {
6302
+ if (!currentViolationIds.has(id)) {
6303
+ resolvedViolations.push(id);
6304
+ }
6305
+ }
6306
+ }
6307
+ if (baselineCategory && agg.value > baselineValue) {
6308
+ regressions.push({
6309
+ category,
6310
+ baselineValue,
6311
+ currentValue: agg.value,
6312
+ delta: agg.value - baselineValue
6313
+ });
6314
+ }
6315
+ }
6316
+ for (const [category, baselineCategory] of Object.entries(baseline.metrics)) {
6317
+ if (!visitedCategories.has(category) && baselineCategory) {
6318
+ for (const id of baselineCategory.violationIds) {
6319
+ resolvedViolations.push(id);
6320
+ }
6321
+ }
6322
+ }
6323
+ const passed = newViolations.length === 0 && regressions.length === 0;
6324
+ return {
6325
+ passed,
6326
+ newViolations,
6327
+ resolvedViolations,
6328
+ preExisting,
6329
+ regressions
6330
+ };
6331
+ }
6332
+
6333
+ // src/architecture/config.ts
6334
+ function resolveThresholds(scope, config) {
6335
+ const projectThresholds = {};
6336
+ for (const [key, val] of Object.entries(config.thresholds)) {
6337
+ projectThresholds[key] = typeof val === "object" && val !== null && !Array.isArray(val) ? { ...val } : val;
6338
+ }
6339
+ if (scope === "project") {
6340
+ return projectThresholds;
6341
+ }
6342
+ const moduleOverrides = config.modules[scope];
6343
+ if (!moduleOverrides) {
6344
+ return projectThresholds;
6345
+ }
6346
+ const merged = { ...projectThresholds };
6347
+ for (const [category, moduleValue] of Object.entries(moduleOverrides)) {
6348
+ const projectValue = projectThresholds[category];
6349
+ if (projectValue !== void 0 && typeof projectValue === "object" && !Array.isArray(projectValue) && typeof moduleValue === "object" && !Array.isArray(moduleValue)) {
6350
+ merged[category] = {
6351
+ ...projectValue,
6352
+ ...moduleValue
6353
+ };
6354
+ } else {
6355
+ merged[category] = moduleValue;
6356
+ }
6357
+ }
6358
+ return merged;
6359
+ }
6360
+
6361
+ // src/architecture/matchers.ts
6362
+ function architecture(options) {
6363
+ return {
6364
+ kind: "arch-handle",
6365
+ scope: "project",
6366
+ rootDir: options?.rootDir ?? process.cwd(),
6367
+ config: options?.config
6368
+ };
6369
+ }
6370
+ function archModule(modulePath, options) {
6371
+ return {
6372
+ kind: "arch-handle",
6373
+ scope: modulePath,
6374
+ rootDir: options?.rootDir ?? process.cwd(),
6375
+ config: options?.config
6376
+ };
6377
+ }
6378
+ function resolveConfig(handle) {
6379
+ return ArchConfigSchema.parse(handle.config ?? {});
6380
+ }
6381
+ async function collectCategory(handle, collector) {
6382
+ if ("_mockResults" in handle && handle._mockResults) {
6383
+ return handle._mockResults;
6384
+ }
6385
+ const config = resolveConfig(handle);
6386
+ return collector.collect(config, handle.rootDir);
6387
+ }
6388
+ function formatViolationList(violations, limit = 10) {
6389
+ const lines = violations.slice(0, limit).map((v) => ` - ${v.file}: ${v.detail}`);
6390
+ if (violations.length > limit) {
6391
+ lines.push(` ... and ${violations.length - limit} more`);
6392
+ }
6393
+ return lines.join("\n");
6394
+ }
6395
+ async function toHaveNoCircularDeps(received) {
6396
+ const results = await collectCategory(received, new CircularDepsCollector());
6397
+ const violations = results.flatMap((r) => r.violations);
6398
+ const pass = violations.length === 0;
6399
+ return {
6400
+ pass,
6401
+ message: () => pass ? "Expected circular dependencies but found none" : `Found ${violations.length} circular dependenc${violations.length === 1 ? "y" : "ies"}:
6402
+ ${formatViolationList(violations)}`
6403
+ };
6404
+ }
6405
+ async function toHaveNoLayerViolations(received) {
6406
+ const results = await collectCategory(received, new LayerViolationCollector());
6407
+ const violations = results.flatMap((r) => r.violations);
6408
+ const pass = violations.length === 0;
6409
+ return {
6410
+ pass,
6411
+ message: () => pass ? "Expected layer violations but found none" : `Found ${violations.length} layer violation${violations.length === 1 ? "" : "s"}:
6412
+ ${formatViolationList(violations)}`
6413
+ };
6414
+ }
6415
+ async function toMatchBaseline(received, options) {
6416
+ let diffResult;
6417
+ if ("_mockDiff" in received && received._mockDiff) {
6418
+ diffResult = received._mockDiff;
6419
+ } else {
6420
+ const config = resolveConfig(received);
6421
+ const results = await runAll(config, received.rootDir);
6422
+ const manager = new ArchBaselineManager(received.rootDir, config.baselinePath);
6423
+ const baseline = manager.load();
6424
+ if (!baseline) {
6425
+ return {
6426
+ pass: false,
6427
+ message: () => "No baseline found. Run `harness check-arch --update-baseline` to create one."
6428
+ };
6429
+ }
6430
+ diffResult = diff(results, baseline);
6431
+ }
6432
+ const tolerance = options?.tolerance ?? 0;
6433
+ const effectiveNewCount = Math.max(0, diffResult.newViolations.length - tolerance);
6434
+ const pass = effectiveNewCount === 0 && diffResult.regressions.length === 0;
6435
+ return {
6436
+ pass,
6437
+ message: () => {
6438
+ if (pass) {
6439
+ return "Expected baseline regression but architecture matches baseline";
6440
+ }
6441
+ const parts = [];
6442
+ if (diffResult.newViolations.length > 0) {
6443
+ parts.push(
6444
+ `${diffResult.newViolations.length} new violation${diffResult.newViolations.length === 1 ? "" : "s"}${tolerance > 0 ? ` (tolerance: ${tolerance})` : ""}:
6445
+ ${formatViolationList(diffResult.newViolations)}`
6446
+ );
6447
+ }
6448
+ if (diffResult.regressions.length > 0) {
6449
+ const regLines = diffResult.regressions.map(
6450
+ (r) => ` - ${r.category}: ${r.baselineValue} -> ${r.currentValue} (+${r.delta})`
6451
+ );
6452
+ parts.push(`Regressions:
6453
+ ${regLines.join("\n")}`);
6454
+ }
6455
+ return `Baseline check failed:
6456
+ ${parts.join("\n\n")}`;
6457
+ }
6458
+ };
6459
+ }
6460
+ function filterByScope(results, scope) {
6461
+ return results.filter(
6462
+ (r) => r.scope === scope || r.scope.startsWith(scope + "/") || r.scope === "project"
6463
+ );
6464
+ }
6465
+ async function toHaveMaxComplexity(received, maxComplexity) {
6466
+ const results = await collectCategory(received, new ComplexityCollector());
6467
+ const scoped = filterByScope(results, received.scope);
6468
+ const violations = scoped.flatMap((r) => r.violations);
6469
+ const totalValue = scoped.reduce((sum, r) => sum + r.value, 0);
6470
+ const pass = totalValue <= maxComplexity && violations.length === 0;
6471
+ return {
6472
+ pass,
6473
+ message: () => pass ? `Expected complexity to exceed ${maxComplexity} but it was within limits` : `Module '${received.scope}' has complexity violations (${violations.length} violation${violations.length === 1 ? "" : "s"}):
6474
+ ${formatViolationList(violations)}`
6475
+ };
6476
+ }
6477
+ async function toHaveMaxCoupling(received, limits) {
6478
+ const config = resolveConfig(received);
6479
+ if (limits.fanIn !== void 0 || limits.fanOut !== void 0) {
6480
+ config.thresholds.coupling = {
6481
+ ...typeof config.thresholds.coupling === "object" ? config.thresholds.coupling : {},
6482
+ ...limits.fanIn !== void 0 ? { maxFanIn: limits.fanIn } : {},
6483
+ ...limits.fanOut !== void 0 ? { maxFanOut: limits.fanOut } : {}
6484
+ };
6485
+ }
6486
+ const collector = new CouplingCollector();
6487
+ const results = "_mockResults" in received && received._mockResults ? received._mockResults : await collector.collect(config, received.rootDir);
6488
+ const scoped = filterByScope(results, received.scope);
6489
+ const violations = scoped.flatMap((r) => r.violations);
6490
+ const pass = violations.length === 0;
6491
+ return {
6492
+ pass,
6493
+ message: () => pass ? `Expected coupling violations in '${received.scope}' but found none` : `Module '${received.scope}' has ${violations.length} coupling violation${violations.length === 1 ? "" : "s"} (fanIn limit: ${limits.fanIn ?? "none"}, fanOut limit: ${limits.fanOut ?? "none"}):
6494
+ ${formatViolationList(violations)}`
6495
+ };
6496
+ }
6497
+ async function toHaveMaxFileCount(received, maxFiles) {
6498
+ const results = await collectCategory(received, new ModuleSizeCollector());
6499
+ const scoped = filterByScope(results, received.scope);
6500
+ const fileCount = scoped.reduce((max, r) => {
6501
+ const meta = r.metadata;
6502
+ const fc = typeof meta?.fileCount === "number" ? meta.fileCount : 0;
6503
+ return fc > max ? fc : max;
6504
+ }, 0);
6505
+ const pass = fileCount <= maxFiles;
6506
+ return {
6507
+ pass,
6508
+ message: () => pass ? `Expected file count in '${received.scope}' to exceed ${maxFiles} but it was ${fileCount}` : `Module '${received.scope}' has ${fileCount} files (limit: ${maxFiles})`
6509
+ };
6510
+ }
6511
+ async function toNotDependOn(received, forbiddenModule) {
6512
+ const results = await collectCategory(received, new ForbiddenImportCollector());
6513
+ const allViolations = results.flatMap((r) => r.violations);
6514
+ const scopePrefix = received.scope.replace(/\/+$/, "");
6515
+ const forbiddenPrefix = forbiddenModule.replace(/\/+$/, "");
6516
+ const relevantViolations = allViolations.filter(
6517
+ (v) => (v.file === scopePrefix || v.file.startsWith(scopePrefix + "/")) && (v.detail.includes(forbiddenPrefix + "/") || v.detail.endsWith(forbiddenPrefix))
6518
+ );
6519
+ const pass = relevantViolations.length === 0;
6520
+ return {
6521
+ pass,
6522
+ message: () => pass ? `Expected '${received.scope}' to depend on '${forbiddenModule}' but no such imports found` : `Module '${received.scope}' depends on '${forbiddenModule}' (${relevantViolations.length} import${relevantViolations.length === 1 ? "" : "s"}):
6523
+ ${formatViolationList(relevantViolations)}`
6524
+ };
6525
+ }
6526
+ async function toHaveMaxDepDepth(received, maxDepth) {
6527
+ const results = await collectCategory(received, new DepDepthCollector());
6528
+ const scoped = filterByScope(results, received.scope);
6529
+ const maxActual = scoped.reduce((max, r) => r.value > max ? r.value : max, 0);
6530
+ const pass = maxActual <= maxDepth;
6531
+ return {
6532
+ pass,
6533
+ message: () => pass ? `Expected dependency depth in '${received.scope}' to exceed ${maxDepth} but it was ${maxActual}` : `Module '${received.scope}' has dependency depth ${maxActual} (limit: ${maxDepth})`
6534
+ };
6535
+ }
6536
+ var archMatchers = {
6537
+ toHaveNoCircularDeps,
6538
+ toHaveNoLayerViolations,
6539
+ toMatchBaseline,
6540
+ toHaveMaxComplexity,
6541
+ toHaveMaxCoupling,
6542
+ toHaveMaxFileCount,
6543
+ toNotDependOn,
6544
+ toHaveMaxDepDepth
6545
+ };
6546
+
4894
6547
  // src/state/types.ts
4895
- var import_zod2 = require("zod");
4896
- var FailureEntrySchema = import_zod2.z.object({
4897
- date: import_zod2.z.string(),
4898
- skill: import_zod2.z.string(),
4899
- type: import_zod2.z.string(),
4900
- description: import_zod2.z.string()
6548
+ var import_zod4 = require("zod");
6549
+ var FailureEntrySchema = import_zod4.z.object({
6550
+ date: import_zod4.z.string(),
6551
+ skill: import_zod4.z.string(),
6552
+ type: import_zod4.z.string(),
6553
+ description: import_zod4.z.string()
4901
6554
  });
4902
- var HandoffSchema = import_zod2.z.object({
4903
- timestamp: import_zod2.z.string(),
4904
- fromSkill: import_zod2.z.string(),
4905
- phase: import_zod2.z.string(),
4906
- summary: import_zod2.z.string(),
4907
- completed: import_zod2.z.array(import_zod2.z.string()).default([]),
4908
- pending: import_zod2.z.array(import_zod2.z.string()).default([]),
4909
- concerns: import_zod2.z.array(import_zod2.z.string()).default([]),
4910
- decisions: import_zod2.z.array(
4911
- import_zod2.z.object({
4912
- what: import_zod2.z.string(),
4913
- why: import_zod2.z.string()
6555
+ var HandoffSchema = import_zod4.z.object({
6556
+ timestamp: import_zod4.z.string(),
6557
+ fromSkill: import_zod4.z.string(),
6558
+ phase: import_zod4.z.string(),
6559
+ summary: import_zod4.z.string(),
6560
+ completed: import_zod4.z.array(import_zod4.z.string()).default([]),
6561
+ pending: import_zod4.z.array(import_zod4.z.string()).default([]),
6562
+ concerns: import_zod4.z.array(import_zod4.z.string()).default([]),
6563
+ decisions: import_zod4.z.array(
6564
+ import_zod4.z.object({
6565
+ what: import_zod4.z.string(),
6566
+ why: import_zod4.z.string()
4914
6567
  })
4915
6568
  ).default([]),
4916
- blockers: import_zod2.z.array(import_zod2.z.string()).default([]),
4917
- contextKeywords: import_zod2.z.array(import_zod2.z.string()).default([])
6569
+ blockers: import_zod4.z.array(import_zod4.z.string()).default([]),
6570
+ contextKeywords: import_zod4.z.array(import_zod4.z.string()).default([])
4918
6571
  });
4919
- var GateCheckSchema = import_zod2.z.object({
4920
- name: import_zod2.z.string(),
4921
- passed: import_zod2.z.boolean(),
4922
- command: import_zod2.z.string(),
4923
- output: import_zod2.z.string().optional(),
4924
- duration: import_zod2.z.number().optional()
6572
+ var GateCheckSchema = import_zod4.z.object({
6573
+ name: import_zod4.z.string(),
6574
+ passed: import_zod4.z.boolean(),
6575
+ command: import_zod4.z.string(),
6576
+ output: import_zod4.z.string().optional(),
6577
+ duration: import_zod4.z.number().optional()
4925
6578
  });
4926
- var GateResultSchema = import_zod2.z.object({
4927
- passed: import_zod2.z.boolean(),
4928
- checks: import_zod2.z.array(GateCheckSchema)
6579
+ var GateResultSchema = import_zod4.z.object({
6580
+ passed: import_zod4.z.boolean(),
6581
+ checks: import_zod4.z.array(GateCheckSchema)
4929
6582
  });
4930
- var GateConfigSchema = import_zod2.z.object({
4931
- checks: import_zod2.z.array(
4932
- import_zod2.z.object({
4933
- name: import_zod2.z.string(),
4934
- command: import_zod2.z.string()
6583
+ var GateConfigSchema = import_zod4.z.object({
6584
+ checks: import_zod4.z.array(
6585
+ import_zod4.z.object({
6586
+ name: import_zod4.z.string(),
6587
+ command: import_zod4.z.string()
4935
6588
  })
4936
6589
  ).optional(),
4937
- trace: import_zod2.z.boolean().optional()
6590
+ trace: import_zod4.z.boolean().optional()
4938
6591
  });
4939
- var HarnessStateSchema = import_zod2.z.object({
4940
- schemaVersion: import_zod2.z.literal(1),
4941
- position: import_zod2.z.object({
4942
- phase: import_zod2.z.string().optional(),
4943
- task: import_zod2.z.string().optional()
6592
+ var HarnessStateSchema = import_zod4.z.object({
6593
+ schemaVersion: import_zod4.z.literal(1),
6594
+ position: import_zod4.z.object({
6595
+ phase: import_zod4.z.string().optional(),
6596
+ task: import_zod4.z.string().optional()
4944
6597
  }).default({}),
4945
- decisions: import_zod2.z.array(
4946
- import_zod2.z.object({
4947
- date: import_zod2.z.string(),
4948
- decision: import_zod2.z.string(),
4949
- context: import_zod2.z.string()
6598
+ decisions: import_zod4.z.array(
6599
+ import_zod4.z.object({
6600
+ date: import_zod4.z.string(),
6601
+ decision: import_zod4.z.string(),
6602
+ context: import_zod4.z.string()
4950
6603
  })
4951
6604
  ).default([]),
4952
- blockers: import_zod2.z.array(
4953
- import_zod2.z.object({
4954
- id: import_zod2.z.string(),
4955
- description: import_zod2.z.string(),
4956
- status: import_zod2.z.enum(["open", "resolved"])
6605
+ blockers: import_zod4.z.array(
6606
+ import_zod4.z.object({
6607
+ id: import_zod4.z.string(),
6608
+ description: import_zod4.z.string(),
6609
+ status: import_zod4.z.enum(["open", "resolved"])
4957
6610
  })
4958
6611
  ).default([]),
4959
- progress: import_zod2.z.record(import_zod2.z.enum(["pending", "in_progress", "complete"])).default({}),
4960
- lastSession: import_zod2.z.object({
4961
- date: import_zod2.z.string(),
4962
- summary: import_zod2.z.string(),
4963
- lastSkill: import_zod2.z.string().optional(),
4964
- pendingTasks: import_zod2.z.array(import_zod2.z.string()).optional()
6612
+ progress: import_zod4.z.record(import_zod4.z.enum(["pending", "in_progress", "complete"])).default({}),
6613
+ lastSession: import_zod4.z.object({
6614
+ date: import_zod4.z.string(),
6615
+ summary: import_zod4.z.string(),
6616
+ lastSkill: import_zod4.z.string().optional(),
6617
+ pendingTasks: import_zod4.z.array(import_zod4.z.string()).optional()
4965
6618
  }).optional()
4966
6619
  });
4967
6620
  var DEFAULT_STATE = {
@@ -4973,27 +6626,27 @@ var DEFAULT_STATE = {
4973
6626
  };
4974
6627
 
4975
6628
  // src/state/state-manager.ts
4976
- var fs4 = __toESM(require("fs"));
6629
+ var fs6 = __toESM(require("fs"));
4977
6630
  var path3 = __toESM(require("path"));
4978
6631
  var import_child_process2 = require("child_process");
4979
6632
 
4980
6633
  // src/state/stream-resolver.ts
4981
- var fs3 = __toESM(require("fs"));
6634
+ var fs5 = __toESM(require("fs"));
4982
6635
  var path2 = __toESM(require("path"));
4983
6636
  var import_child_process = require("child_process");
4984
6637
 
4985
6638
  // src/state/stream-types.ts
4986
- var import_zod3 = require("zod");
4987
- var StreamInfoSchema = import_zod3.z.object({
4988
- name: import_zod3.z.string(),
4989
- branch: import_zod3.z.string().optional(),
4990
- createdAt: import_zod3.z.string(),
4991
- lastActiveAt: import_zod3.z.string()
6639
+ var import_zod5 = require("zod");
6640
+ var StreamInfoSchema = import_zod5.z.object({
6641
+ name: import_zod5.z.string(),
6642
+ branch: import_zod5.z.string().optional(),
6643
+ createdAt: import_zod5.z.string(),
6644
+ lastActiveAt: import_zod5.z.string()
4992
6645
  });
4993
- var StreamIndexSchema = import_zod3.z.object({
4994
- schemaVersion: import_zod3.z.literal(1),
4995
- activeStream: import_zod3.z.string().nullable(),
4996
- streams: import_zod3.z.record(StreamInfoSchema)
6646
+ var StreamIndexSchema = import_zod5.z.object({
6647
+ schemaVersion: import_zod5.z.literal(1),
6648
+ activeStream: import_zod5.z.string().nullable(),
6649
+ streams: import_zod5.z.record(StreamInfoSchema)
4997
6650
  });
4998
6651
  var DEFAULT_STREAM_INDEX = {
4999
6652
  schemaVersion: 1,
@@ -5024,11 +6677,11 @@ function validateStreamName(name) {
5024
6677
  }
5025
6678
  async function loadStreamIndex(projectPath) {
5026
6679
  const idxPath = indexPath(projectPath);
5027
- if (!fs3.existsSync(idxPath)) {
6680
+ if (!fs5.existsSync(idxPath)) {
5028
6681
  return (0, import_types.Ok)({ ...DEFAULT_STREAM_INDEX, streams: {} });
5029
6682
  }
5030
6683
  try {
5031
- const raw = fs3.readFileSync(idxPath, "utf-8");
6684
+ const raw = fs5.readFileSync(idxPath, "utf-8");
5032
6685
  const parsed = JSON.parse(raw);
5033
6686
  const result = StreamIndexSchema.safeParse(parsed);
5034
6687
  if (!result.success) {
@@ -5046,8 +6699,8 @@ async function loadStreamIndex(projectPath) {
5046
6699
  async function saveStreamIndex(projectPath, index) {
5047
6700
  const dir = streamsDir(projectPath);
5048
6701
  try {
5049
- fs3.mkdirSync(dir, { recursive: true });
5050
- fs3.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
6702
+ fs5.mkdirSync(dir, { recursive: true });
6703
+ fs5.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
5051
6704
  return (0, import_types.Ok)(void 0);
5052
6705
  } catch (error) {
5053
6706
  return (0, import_types.Err)(
@@ -5129,7 +6782,7 @@ async function createStream(projectPath, name, branch) {
5129
6782
  }
5130
6783
  const streamPath = path2.join(streamsDir(projectPath), name);
5131
6784
  try {
5132
- fs3.mkdirSync(streamPath, { recursive: true });
6785
+ fs5.mkdirSync(streamPath, { recursive: true });
5133
6786
  } catch (error) {
5134
6787
  return (0, import_types.Err)(
5135
6788
  new Error(
@@ -5173,9 +6826,9 @@ async function archiveStream(projectPath, name) {
5173
6826
  const streamPath = path2.join(streamsDir(projectPath), name);
5174
6827
  const archiveDir = path2.join(projectPath, HARNESS_DIR, "archive", "streams");
5175
6828
  try {
5176
- fs3.mkdirSync(archiveDir, { recursive: true });
6829
+ fs5.mkdirSync(archiveDir, { recursive: true });
5177
6830
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5178
- fs3.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
6831
+ fs5.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
5179
6832
  } catch (error) {
5180
6833
  return (0, import_types.Err)(
5181
6834
  new Error(
@@ -5198,18 +6851,18 @@ function getStreamForBranch(index, branch) {
5198
6851
  var STATE_FILES = ["state.json", "handoff.json", "learnings.md", "failures.md"];
5199
6852
  async function migrateToStreams(projectPath) {
5200
6853
  const harnessDir = path2.join(projectPath, HARNESS_DIR);
5201
- if (fs3.existsSync(indexPath(projectPath))) {
6854
+ if (fs5.existsSync(indexPath(projectPath))) {
5202
6855
  return (0, import_types.Ok)(void 0);
5203
6856
  }
5204
- const filesToMove = STATE_FILES.filter((f) => fs3.existsSync(path2.join(harnessDir, f)));
6857
+ const filesToMove = STATE_FILES.filter((f) => fs5.existsSync(path2.join(harnessDir, f)));
5205
6858
  if (filesToMove.length === 0) {
5206
6859
  return (0, import_types.Ok)(void 0);
5207
6860
  }
5208
6861
  const defaultDir = path2.join(streamsDir(projectPath), "default");
5209
6862
  try {
5210
- fs3.mkdirSync(defaultDir, { recursive: true });
6863
+ fs5.mkdirSync(defaultDir, { recursive: true });
5211
6864
  for (const file of filesToMove) {
5212
- fs3.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
6865
+ fs5.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
5213
6866
  }
5214
6867
  } catch (error) {
5215
6868
  return (0, import_types.Err)(
@@ -5250,7 +6903,7 @@ function evictIfNeeded(map) {
5250
6903
  }
5251
6904
  async function getStateDir(projectPath, stream) {
5252
6905
  const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
5253
- const hasStreams = fs4.existsSync(streamsIndexPath);
6906
+ const hasStreams = fs6.existsSync(streamsIndexPath);
5254
6907
  if (stream || hasStreams) {
5255
6908
  const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
5256
6909
  if (result.ok) {
@@ -5268,10 +6921,10 @@ async function loadState(projectPath, stream) {
5268
6921
  if (!dirResult.ok) return dirResult;
5269
6922
  const stateDir = dirResult.value;
5270
6923
  const statePath = path3.join(stateDir, STATE_FILE);
5271
- if (!fs4.existsSync(statePath)) {
6924
+ if (!fs6.existsSync(statePath)) {
5272
6925
  return (0, import_types.Ok)({ ...DEFAULT_STATE });
5273
6926
  }
5274
- const raw = fs4.readFileSync(statePath, "utf-8");
6927
+ const raw = fs6.readFileSync(statePath, "utf-8");
5275
6928
  const parsed = JSON.parse(raw);
5276
6929
  const result = HarnessStateSchema.safeParse(parsed);
5277
6930
  if (!result.success) {
@@ -5290,8 +6943,8 @@ async function saveState(projectPath, state, stream) {
5290
6943
  if (!dirResult.ok) return dirResult;
5291
6944
  const stateDir = dirResult.value;
5292
6945
  const statePath = path3.join(stateDir, STATE_FILE);
5293
- fs4.mkdirSync(stateDir, { recursive: true });
5294
- fs4.writeFileSync(statePath, JSON.stringify(state, null, 2));
6946
+ fs6.mkdirSync(stateDir, { recursive: true });
6947
+ fs6.writeFileSync(statePath, JSON.stringify(state, null, 2));
5295
6948
  return (0, import_types.Ok)(void 0);
5296
6949
  } catch (error) {
5297
6950
  return (0, import_types.Err)(
@@ -5305,7 +6958,7 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5305
6958
  if (!dirResult.ok) return dirResult;
5306
6959
  const stateDir = dirResult.value;
5307
6960
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5308
- fs4.mkdirSync(stateDir, { recursive: true });
6961
+ fs6.mkdirSync(stateDir, { recursive: true });
5309
6962
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5310
6963
  let entry;
5311
6964
  if (skillName && outcome) {
@@ -5321,11 +6974,11 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5321
6974
  - **${timestamp}:** ${learning}
5322
6975
  `;
5323
6976
  }
5324
- if (!fs4.existsSync(learningsPath)) {
5325
- fs4.writeFileSync(learningsPath, `# Learnings
6977
+ if (!fs6.existsSync(learningsPath)) {
6978
+ fs6.writeFileSync(learningsPath, `# Learnings
5326
6979
  ${entry}`);
5327
6980
  } else {
5328
- fs4.appendFileSync(learningsPath, entry);
6981
+ fs6.appendFileSync(learningsPath, entry);
5329
6982
  }
5330
6983
  learningsCacheMap.delete(learningsPath);
5331
6984
  return (0, import_types.Ok)(void 0);
@@ -5343,17 +6996,17 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
5343
6996
  if (!dirResult.ok) return dirResult;
5344
6997
  const stateDir = dirResult.value;
5345
6998
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5346
- if (!fs4.existsSync(learningsPath)) {
6999
+ if (!fs6.existsSync(learningsPath)) {
5347
7000
  return (0, import_types.Ok)([]);
5348
7001
  }
5349
- const stats = fs4.statSync(learningsPath);
7002
+ const stats = fs6.statSync(learningsPath);
5350
7003
  const cacheKey = learningsPath;
5351
7004
  const cached = learningsCacheMap.get(cacheKey);
5352
7005
  let entries;
5353
7006
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5354
7007
  entries = cached.entries;
5355
7008
  } else {
5356
- const content = fs4.readFileSync(learningsPath, "utf-8");
7009
+ const content = fs6.readFileSync(learningsPath, "utf-8");
5357
7010
  const lines = content.split("\n");
5358
7011
  entries = [];
5359
7012
  let currentBlock = [];
@@ -5396,16 +7049,16 @@ async function appendFailure(projectPath, description, skillName, type, stream)
5396
7049
  if (!dirResult.ok) return dirResult;
5397
7050
  const stateDir = dirResult.value;
5398
7051
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5399
- fs4.mkdirSync(stateDir, { recursive: true });
7052
+ fs6.mkdirSync(stateDir, { recursive: true });
5400
7053
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5401
7054
  const entry = `
5402
7055
  - **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
5403
7056
  `;
5404
- if (!fs4.existsSync(failuresPath)) {
5405
- fs4.writeFileSync(failuresPath, `# Failures
7057
+ if (!fs6.existsSync(failuresPath)) {
7058
+ fs6.writeFileSync(failuresPath, `# Failures
5406
7059
  ${entry}`);
5407
7060
  } else {
5408
- fs4.appendFileSync(failuresPath, entry);
7061
+ fs6.appendFileSync(failuresPath, entry);
5409
7062
  }
5410
7063
  failuresCacheMap.delete(failuresPath);
5411
7064
  return (0, import_types.Ok)(void 0);
@@ -5423,16 +7076,16 @@ async function loadFailures(projectPath, stream) {
5423
7076
  if (!dirResult.ok) return dirResult;
5424
7077
  const stateDir = dirResult.value;
5425
7078
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5426
- if (!fs4.existsSync(failuresPath)) {
7079
+ if (!fs6.existsSync(failuresPath)) {
5427
7080
  return (0, import_types.Ok)([]);
5428
7081
  }
5429
- const stats = fs4.statSync(failuresPath);
7082
+ const stats = fs6.statSync(failuresPath);
5430
7083
  const cacheKey = failuresPath;
5431
7084
  const cached = failuresCacheMap.get(cacheKey);
5432
7085
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5433
7086
  return (0, import_types.Ok)(cached.entries);
5434
7087
  }
5435
- const content = fs4.readFileSync(failuresPath, "utf-8");
7088
+ const content = fs6.readFileSync(failuresPath, "utf-8");
5436
7089
  const entries = [];
5437
7090
  for (const line of content.split("\n")) {
5438
7091
  const match = line.match(FAILURE_LINE_REGEX);
@@ -5462,19 +7115,19 @@ async function archiveFailures(projectPath, stream) {
5462
7115
  if (!dirResult.ok) return dirResult;
5463
7116
  const stateDir = dirResult.value;
5464
7117
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5465
- if (!fs4.existsSync(failuresPath)) {
7118
+ if (!fs6.existsSync(failuresPath)) {
5466
7119
  return (0, import_types.Ok)(void 0);
5467
7120
  }
5468
7121
  const archiveDir = path3.join(stateDir, "archive");
5469
- fs4.mkdirSync(archiveDir, { recursive: true });
7122
+ fs6.mkdirSync(archiveDir, { recursive: true });
5470
7123
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5471
7124
  let archiveName = `failures-${date}.md`;
5472
7125
  let counter = 2;
5473
- while (fs4.existsSync(path3.join(archiveDir, archiveName))) {
7126
+ while (fs6.existsSync(path3.join(archiveDir, archiveName))) {
5474
7127
  archiveName = `failures-${date}-${counter}.md`;
5475
7128
  counter++;
5476
7129
  }
5477
- fs4.renameSync(failuresPath, path3.join(archiveDir, archiveName));
7130
+ fs6.renameSync(failuresPath, path3.join(archiveDir, archiveName));
5478
7131
  failuresCacheMap.delete(failuresPath);
5479
7132
  return (0, import_types.Ok)(void 0);
5480
7133
  } catch (error) {
@@ -5491,8 +7144,8 @@ async function saveHandoff(projectPath, handoff, stream) {
5491
7144
  if (!dirResult.ok) return dirResult;
5492
7145
  const stateDir = dirResult.value;
5493
7146
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5494
- fs4.mkdirSync(stateDir, { recursive: true });
5495
- fs4.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
7147
+ fs6.mkdirSync(stateDir, { recursive: true });
7148
+ fs6.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
5496
7149
  return (0, import_types.Ok)(void 0);
5497
7150
  } catch (error) {
5498
7151
  return (0, import_types.Err)(
@@ -5506,10 +7159,10 @@ async function loadHandoff(projectPath, stream) {
5506
7159
  if (!dirResult.ok) return dirResult;
5507
7160
  const stateDir = dirResult.value;
5508
7161
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5509
- if (!fs4.existsSync(handoffPath)) {
7162
+ if (!fs6.existsSync(handoffPath)) {
5510
7163
  return (0, import_types.Ok)(null);
5511
7164
  }
5512
- const raw = fs4.readFileSync(handoffPath, "utf-8");
7165
+ const raw = fs6.readFileSync(handoffPath, "utf-8");
5513
7166
  const parsed = JSON.parse(raw);
5514
7167
  const result = HandoffSchema.safeParse(parsed);
5515
7168
  if (!result.success) {
@@ -5527,8 +7180,8 @@ async function runMechanicalGate(projectPath) {
5527
7180
  const gateConfigPath = path3.join(harnessDir, GATE_CONFIG_FILE);
5528
7181
  try {
5529
7182
  let checks = [];
5530
- if (fs4.existsSync(gateConfigPath)) {
5531
- const raw = JSON.parse(fs4.readFileSync(gateConfigPath, "utf-8"));
7183
+ if (fs6.existsSync(gateConfigPath)) {
7184
+ const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
5532
7185
  const config = GateConfigSchema.safeParse(raw);
5533
7186
  if (config.success && config.data.checks) {
5534
7187
  checks = config.data.checks;
@@ -5536,19 +7189,19 @@ async function runMechanicalGate(projectPath) {
5536
7189
  }
5537
7190
  if (checks.length === 0) {
5538
7191
  const packageJsonPath = path3.join(projectPath, "package.json");
5539
- if (fs4.existsSync(packageJsonPath)) {
5540
- const pkg = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
7192
+ if (fs6.existsSync(packageJsonPath)) {
7193
+ const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
5541
7194
  const scripts = pkg.scripts || {};
5542
7195
  if (scripts.test) checks.push({ name: "test", command: "npm test" });
5543
7196
  if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
5544
7197
  if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
5545
7198
  if (scripts.build) checks.push({ name: "build", command: "npm run build" });
5546
7199
  }
5547
- if (fs4.existsSync(path3.join(projectPath, "go.mod"))) {
7200
+ if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
5548
7201
  checks.push({ name: "test", command: "go test ./..." });
5549
7202
  checks.push({ name: "build", command: "go build ./..." });
5550
7203
  }
5551
- if (fs4.existsSync(path3.join(projectPath, "pyproject.toml")) || fs4.existsSync(path3.join(projectPath, "setup.py"))) {
7204
+ if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
5552
7205
  checks.push({ name: "test", command: "python -m pytest" });
5553
7206
  }
5554
7207
  }
@@ -5751,7 +7404,7 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
5751
7404
  }
5752
7405
 
5753
7406
  // src/security/scanner.ts
5754
- var fs6 = __toESM(require("fs/promises"));
7407
+ var fs8 = __toESM(require("fs/promises"));
5755
7408
 
5756
7409
  // src/security/rules/registry.ts
5757
7410
  var RuleRegistry = class {
@@ -5782,7 +7435,7 @@ var RuleRegistry = class {
5782
7435
  };
5783
7436
 
5784
7437
  // src/security/config.ts
5785
- var import_zod4 = require("zod");
7438
+ var import_zod6 = require("zod");
5786
7439
 
5787
7440
  // src/security/types.ts
5788
7441
  var DEFAULT_SECURITY_CONFIG = {
@@ -5793,19 +7446,19 @@ var DEFAULT_SECURITY_CONFIG = {
5793
7446
  };
5794
7447
 
5795
7448
  // src/security/config.ts
5796
- var RuleOverrideSchema = import_zod4.z.enum(["off", "error", "warning", "info"]);
5797
- var SecurityConfigSchema = import_zod4.z.object({
5798
- enabled: import_zod4.z.boolean().default(true),
5799
- strict: import_zod4.z.boolean().default(false),
5800
- rules: import_zod4.z.record(import_zod4.z.string(), RuleOverrideSchema).optional().default({}),
5801
- exclude: import_zod4.z.array(import_zod4.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
5802
- external: import_zod4.z.object({
5803
- semgrep: import_zod4.z.object({
5804
- enabled: import_zod4.z.union([import_zod4.z.literal("auto"), import_zod4.z.boolean()]).default("auto"),
5805
- rulesets: import_zod4.z.array(import_zod4.z.string()).optional()
7449
+ var RuleOverrideSchema = import_zod6.z.enum(["off", "error", "warning", "info"]);
7450
+ var SecurityConfigSchema = import_zod6.z.object({
7451
+ enabled: import_zod6.z.boolean().default(true),
7452
+ strict: import_zod6.z.boolean().default(false),
7453
+ rules: import_zod6.z.record(import_zod6.z.string(), RuleOverrideSchema).optional().default({}),
7454
+ exclude: import_zod6.z.array(import_zod6.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
7455
+ external: import_zod6.z.object({
7456
+ semgrep: import_zod6.z.object({
7457
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto"),
7458
+ rulesets: import_zod6.z.array(import_zod6.z.string()).optional()
5806
7459
  }).optional(),
5807
- gitleaks: import_zod4.z.object({
5808
- enabled: import_zod4.z.union([import_zod4.z.literal("auto"), import_zod4.z.boolean()]).default("auto")
7460
+ gitleaks: import_zod6.z.object({
7461
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto")
5809
7462
  }).optional()
5810
7463
  }).optional()
5811
7464
  });
@@ -5838,15 +7491,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
5838
7491
  }
5839
7492
 
5840
7493
  // src/security/stack-detector.ts
5841
- var fs5 = __toESM(require("fs"));
7494
+ var fs7 = __toESM(require("fs"));
5842
7495
  var path4 = __toESM(require("path"));
5843
7496
  function detectStack(projectRoot) {
5844
7497
  const stacks = [];
5845
7498
  const pkgJsonPath = path4.join(projectRoot, "package.json");
5846
- if (fs5.existsSync(pkgJsonPath)) {
7499
+ if (fs7.existsSync(pkgJsonPath)) {
5847
7500
  stacks.push("node");
5848
7501
  try {
5849
- const pkgJson = JSON.parse(fs5.readFileSync(pkgJsonPath, "utf-8"));
7502
+ const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
5850
7503
  const allDeps = {
5851
7504
  ...pkgJson.dependencies,
5852
7505
  ...pkgJson.devDependencies
@@ -5862,12 +7515,12 @@ function detectStack(projectRoot) {
5862
7515
  }
5863
7516
  }
5864
7517
  const goModPath = path4.join(projectRoot, "go.mod");
5865
- if (fs5.existsSync(goModPath)) {
7518
+ if (fs7.existsSync(goModPath)) {
5866
7519
  stacks.push("go");
5867
7520
  }
5868
7521
  const requirementsPath = path4.join(projectRoot, "requirements.txt");
5869
7522
  const pyprojectPath = path4.join(projectRoot, "pyproject.toml");
5870
- if (fs5.existsSync(requirementsPath) || fs5.existsSync(pyprojectPath)) {
7523
+ if (fs7.existsSync(requirementsPath) || fs7.existsSync(pyprojectPath)) {
5871
7524
  stacks.push("python");
5872
7525
  }
5873
7526
  return stacks;
@@ -6294,7 +7947,7 @@ var SecurityScanner = class {
6294
7947
  }
6295
7948
  async scanFile(filePath) {
6296
7949
  if (!this.config.enabled) return [];
6297
- const content = await fs6.readFile(filePath, "utf-8");
7950
+ const content = await fs8.readFile(filePath, "utf-8");
6298
7951
  return this.scanContent(content, filePath, 1);
6299
7952
  }
6300
7953
  async scanFiles(filePaths) {
@@ -6327,7 +7980,8 @@ var ALL_CHECKS = [
6327
7980
  "entropy",
6328
7981
  "security",
6329
7982
  "perf",
6330
- "phase-gate"
7983
+ "phase-gate",
7984
+ "arch"
6331
7985
  ];
6332
7986
  async function runSingleCheck(name, projectRoot, config) {
6333
7987
  const start = Date.now();
@@ -6391,7 +8045,17 @@ async function runSingleCheck(name, projectRoot, config) {
6391
8045
  }
6392
8046
  case "docs": {
6393
8047
  const docsDir = path5.join(projectRoot, config.docsDir ?? "docs");
6394
- const result = await checkDocCoverage("project", { docsDir });
8048
+ const entropyConfig = config.entropy || {};
8049
+ const result = await checkDocCoverage("project", {
8050
+ docsDir,
8051
+ sourceDir: projectRoot,
8052
+ excludePatterns: entropyConfig.excludePatterns || [
8053
+ "**/node_modules/**",
8054
+ "**/dist/**",
8055
+ "**/*.test.ts",
8056
+ "**/fixtures/**"
8057
+ ]
8058
+ });
6395
8059
  if (!result.ok) {
6396
8060
  issues.push({ severity: "warning", message: result.error.message });
6397
8061
  } else if (result.value.gaps.length > 0) {
@@ -6466,11 +8130,13 @@ async function runSingleCheck(name, projectRoot, config) {
6466
8130
  break;
6467
8131
  }
6468
8132
  case "perf": {
8133
+ const perfConfig = config.performance || {};
6469
8134
  const perfAnalyzer = new EntropyAnalyzer({
6470
8135
  rootDir: projectRoot,
6471
8136
  analyze: {
6472
- complexity: true,
6473
- coupling: true
8137
+ complexity: perfConfig.complexity || true,
8138
+ coupling: perfConfig.coupling || true,
8139
+ sizeBudget: perfConfig.sizeBudget || false
6474
8140
  }
6475
8141
  });
6476
8142
  const perfResult = await perfAnalyzer.analyze();
@@ -6511,6 +8177,43 @@ async function runSingleCheck(name, projectRoot, config) {
6511
8177
  });
6512
8178
  break;
6513
8179
  }
8180
+ case "arch": {
8181
+ const rawArchConfig = config.architecture;
8182
+ const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
8183
+ if (!archConfig.enabled) break;
8184
+ const results = await runAll(archConfig, projectRoot);
8185
+ const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
8186
+ const baseline = baselineManager.load();
8187
+ if (baseline) {
8188
+ const diffResult = diff(results, baseline);
8189
+ if (!diffResult.passed) {
8190
+ for (const v of diffResult.newViolations) {
8191
+ issues.push({
8192
+ severity: v.severity,
8193
+ message: `[${v.category || "arch"}] NEW: ${v.detail}`,
8194
+ file: v.file
8195
+ });
8196
+ }
8197
+ for (const r of diffResult.regressions) {
8198
+ issues.push({
8199
+ severity: "error",
8200
+ message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
8201
+ });
8202
+ }
8203
+ }
8204
+ } else {
8205
+ for (const result of results) {
8206
+ for (const v of result.violations) {
8207
+ issues.push({
8208
+ severity: v.severity,
8209
+ message: `[${result.category}] ${v.detail}`,
8210
+ file: v.file
8211
+ });
8212
+ }
8213
+ }
8214
+ }
8215
+ break;
8216
+ }
6514
8217
  }
6515
8218
  } catch (error) {
6516
8219
  issues.push({
@@ -6845,22 +8548,22 @@ var PREFIX_PATTERNS = [
6845
8548
  ];
6846
8549
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|js|jsx|mts|cts)$/;
6847
8550
  var MD_FILE_PATTERN = /\.md$/;
6848
- function detectChangeType(commitMessage, diff) {
8551
+ function detectChangeType(commitMessage, diff2) {
6849
8552
  const trimmed = commitMessage.trim();
6850
8553
  for (const { pattern, type } of PREFIX_PATTERNS) {
6851
8554
  if (pattern.test(trimmed)) {
6852
8555
  return type;
6853
8556
  }
6854
8557
  }
6855
- if (diff.changedFiles.length > 0 && diff.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
8558
+ if (diff2.changedFiles.length > 0 && diff2.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
6856
8559
  return "docs";
6857
8560
  }
6858
- const newNonTestFiles = diff.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
8561
+ const newNonTestFiles = diff2.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
6859
8562
  if (newNonTestFiles.length > 0) {
6860
8563
  return "feature";
6861
8564
  }
6862
- const hasNewTestFile = diff.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
6863
- if (diff.totalDiffLines < 20 && hasNewTestFile) {
8565
+ const hasNewTestFile = diff2.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
8566
+ if (diff2.totalDiffLines < 20 && hasNewTestFile) {
6864
8567
  return "bugfix";
6865
8568
  }
6866
8569
  return "feature";
@@ -6889,7 +8592,7 @@ async function readContextFile(projectRoot, filePath, reason) {
6889
8592
  const relPath = path7.isAbsolute(filePath) ? path7.relative(projectRoot, filePath) : filePath;
6890
8593
  return { path: relPath, content, reason, lines };
6891
8594
  }
6892
- function extractImportSources(content) {
8595
+ function extractImportSources2(content) {
6893
8596
  const sources = [];
6894
8597
  const importRegex = /(?:import\s+(?:.*?\s+from\s+)?['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]\s*\))/g;
6895
8598
  let match;
@@ -6931,7 +8634,7 @@ async function gatherImportContext(projectRoot, changedFiles, budget) {
6931
8634
  const seen = new Set(changedFiles.map((f) => f.path));
6932
8635
  for (const cf of changedFiles) {
6933
8636
  if (linesGathered >= budget) break;
6934
- const sources = extractImportSources(cf.content);
8637
+ const sources = extractImportSources2(cf.content);
6935
8638
  for (const source of sources) {
6936
8639
  if (linesGathered >= budget) break;
6937
8640
  const resolved = await resolveImportPath2(projectRoot, cf.path, source);
@@ -7098,11 +8801,11 @@ async function scopeArchitectureContext(projectRoot, changedFiles, budget, optio
7098
8801
  return contextFiles;
7099
8802
  }
7100
8803
  async function scopeContext(options) {
7101
- const { projectRoot, diff, commitMessage } = options;
7102
- const changeType = detectChangeType(commitMessage, diff);
7103
- const budget = computeContextBudget(diff.totalDiffLines);
8804
+ const { projectRoot, diff: diff2, commitMessage } = options;
8805
+ const changeType = detectChangeType(commitMessage, diff2);
8806
+ const budget = computeContextBudget(diff2.totalDiffLines);
7104
8807
  const changedFiles = [];
7105
- for (const filePath of diff.changedFiles) {
8808
+ for (const filePath of diff2.changedFiles) {
7106
8809
  const cf = await readContextFile(projectRoot, filePath, "changed");
7107
8810
  if (cf) changedFiles.push(cf);
7108
8811
  }
@@ -7122,7 +8825,7 @@ async function scopeContext(options) {
7122
8825
  changedFiles: [...changedFiles],
7123
8826
  contextFiles,
7124
8827
  commitHistory: options.commitHistory ?? [],
7125
- diffLines: diff.totalDiffLines,
8828
+ diffLines: diff2.totalDiffLines,
7126
8829
  contextLines
7127
8830
  });
7128
8831
  }
@@ -8132,7 +9835,7 @@ function formatGitHubSummary(options) {
8132
9835
  async function runReviewPipeline(options) {
8133
9836
  const {
8134
9837
  projectRoot,
8135
- diff,
9838
+ diff: diff2,
8136
9839
  commitMessage,
8137
9840
  flags,
8138
9841
  graph,
@@ -8166,7 +9869,7 @@ async function runReviewPipeline(options) {
8166
9869
  const mechResult = await runMechanicalChecks({
8167
9870
  projectRoot,
8168
9871
  config,
8169
- changedFiles: diff.changedFiles
9872
+ changedFiles: diff2.changedFiles
8170
9873
  });
8171
9874
  if (mechResult.ok) {
8172
9875
  mechanicalResult = mechResult.value;
@@ -8205,7 +9908,7 @@ async function runReviewPipeline(options) {
8205
9908
  try {
8206
9909
  contextBundles = await scopeContext({
8207
9910
  projectRoot,
8208
- diff,
9911
+ diff: diff2,
8209
9912
  commitMessage,
8210
9913
  ...graph != null ? { graph } : {},
8211
9914
  ...conventionFiles != null ? { conventionFiles } : {},
@@ -8219,14 +9922,14 @@ async function runReviewPipeline(options) {
8219
9922
  changedFiles: [],
8220
9923
  contextFiles: [],
8221
9924
  commitHistory: [],
8222
- diffLines: diff.totalDiffLines,
9925
+ diffLines: diff2.totalDiffLines,
8223
9926
  contextLines: 0
8224
9927
  }));
8225
9928
  }
8226
9929
  const agentResults = await fanOutReview({ bundles: contextBundles });
8227
9930
  const rawFindings = agentResults.flatMap((r) => r.findings);
8228
9931
  const fileContents = /* @__PURE__ */ new Map();
8229
- for (const [file, content] of diff.fileDiffs) {
9932
+ for (const [file, content] of diff2.fileDiffs) {
8230
9933
  fileContents.set(file, content);
8231
9934
  }
8232
9935
  const validatedFindings = await validateFindings({
@@ -8262,7 +9965,7 @@ async function runReviewPipeline(options) {
8262
9965
  }
8263
9966
 
8264
9967
  // src/roadmap/parse.ts
8265
- var import_types9 = require("@harness-engineering/types");
9968
+ var import_types16 = require("@harness-engineering/types");
8266
9969
  var VALID_STATUSES = /* @__PURE__ */ new Set([
8267
9970
  "backlog",
8268
9971
  "planned",
@@ -8274,14 +9977,14 @@ var EM_DASH = "\u2014";
8274
9977
  function parseRoadmap(markdown) {
8275
9978
  const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
8276
9979
  if (!fmMatch) {
8277
- return (0, import_types9.Err)(new Error("Missing or malformed YAML frontmatter"));
9980
+ return (0, import_types16.Err)(new Error("Missing or malformed YAML frontmatter"));
8278
9981
  }
8279
9982
  const fmResult = parseFrontmatter(fmMatch[1]);
8280
9983
  if (!fmResult.ok) return fmResult;
8281
9984
  const body = markdown.slice(fmMatch[0].length);
8282
9985
  const milestonesResult = parseMilestones(body);
8283
9986
  if (!milestonesResult.ok) return milestonesResult;
8284
- return (0, import_types9.Ok)({
9987
+ return (0, import_types16.Ok)({
8285
9988
  frontmatter: fmResult.value,
8286
9989
  milestones: milestonesResult.value
8287
9990
  });
@@ -8301,7 +10004,7 @@ function parseFrontmatter(raw) {
8301
10004
  const lastSynced = map.get("last_synced");
8302
10005
  const lastManualEdit = map.get("last_manual_edit");
8303
10006
  if (!project || !versionStr || !lastSynced || !lastManualEdit) {
8304
- return (0, import_types9.Err)(
10007
+ return (0, import_types16.Err)(
8305
10008
  new Error(
8306
10009
  "Frontmatter missing required fields: project, version, last_synced, last_manual_edit"
8307
10010
  )
@@ -8309,9 +10012,9 @@ function parseFrontmatter(raw) {
8309
10012
  }
8310
10013
  const version = parseInt(versionStr, 10);
8311
10014
  if (isNaN(version)) {
8312
- return (0, import_types9.Err)(new Error("Frontmatter version must be a number"));
10015
+ return (0, import_types16.Err)(new Error("Frontmatter version must be a number"));
8313
10016
  }
8314
- return (0, import_types9.Ok)({ project, version, lastSynced, lastManualEdit });
10017
+ return (0, import_types16.Ok)({ project, version, lastSynced, lastManualEdit });
8315
10018
  }
8316
10019
  function parseMilestones(body) {
8317
10020
  const milestones = [];
@@ -8335,7 +10038,7 @@ function parseMilestones(body) {
8335
10038
  features: featuresResult.value
8336
10039
  });
8337
10040
  }
8338
- return (0, import_types9.Ok)(milestones);
10041
+ return (0, import_types16.Ok)(milestones);
8339
10042
  }
8340
10043
  function parseFeatures(sectionBody) {
8341
10044
  const features = [];
@@ -8356,7 +10059,7 @@ function parseFeatures(sectionBody) {
8356
10059
  if (!featureResult.ok) return featureResult;
8357
10060
  features.push(featureResult.value);
8358
10061
  }
8359
- return (0, import_types9.Ok)(features);
10062
+ return (0, import_types16.Ok)(features);
8360
10063
  }
8361
10064
  function parseFeatureFields(name, body) {
8362
10065
  const fieldMap = /* @__PURE__ */ new Map();
@@ -8367,7 +10070,7 @@ function parseFeatureFields(name, body) {
8367
10070
  }
8368
10071
  const statusRaw = fieldMap.get("Status");
8369
10072
  if (!statusRaw || !VALID_STATUSES.has(statusRaw)) {
8370
- return (0, import_types9.Err)(
10073
+ return (0, import_types16.Err)(
8371
10074
  new Error(
8372
10075
  `Feature "${name}" has invalid status: "${statusRaw ?? "(missing)"}". Valid statuses: ${[...VALID_STATUSES].join(", ")}`
8373
10076
  )
@@ -8381,7 +10084,7 @@ function parseFeatureFields(name, body) {
8381
10084
  const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
8382
10085
  const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
8383
10086
  const summary = fieldMap.get("Summary") ?? "";
8384
- return (0, import_types9.Ok)({ name, status, spec, plans, blockedBy, summary });
10087
+ return (0, import_types16.Ok)({ name, status, spec, plans, blockedBy, summary });
8385
10088
  }
8386
10089
 
8387
10090
  // src/roadmap/serialize.ts
@@ -8425,9 +10128,9 @@ function serializeFeature(feature) {
8425
10128
  }
8426
10129
 
8427
10130
  // src/roadmap/sync.ts
8428
- var fs7 = __toESM(require("fs"));
10131
+ var fs9 = __toESM(require("fs"));
8429
10132
  var path9 = __toESM(require("path"));
8430
- var import_types10 = require("@harness-engineering/types");
10133
+ var import_types17 = require("@harness-engineering/types");
8431
10134
  function inferStatus(feature, projectPath, allFeatures) {
8432
10135
  if (feature.blockedBy.length > 0) {
8433
10136
  const blockerNotDone = feature.blockedBy.some((blockerName) => {
@@ -8442,9 +10145,9 @@ function inferStatus(feature, projectPath, allFeatures) {
8442
10145
  const useRootState = featuresWithPlans.length <= 1;
8443
10146
  if (useRootState) {
8444
10147
  const rootStatePath = path9.join(projectPath, ".harness", "state.json");
8445
- if (fs7.existsSync(rootStatePath)) {
10148
+ if (fs9.existsSync(rootStatePath)) {
8446
10149
  try {
8447
- const raw = fs7.readFileSync(rootStatePath, "utf-8");
10150
+ const raw = fs9.readFileSync(rootStatePath, "utf-8");
8448
10151
  const state = JSON.parse(raw);
8449
10152
  if (state.progress) {
8450
10153
  for (const status of Object.values(state.progress)) {
@@ -8456,15 +10159,15 @@ function inferStatus(feature, projectPath, allFeatures) {
8456
10159
  }
8457
10160
  }
8458
10161
  const sessionsDir = path9.join(projectPath, ".harness", "sessions");
8459
- if (fs7.existsSync(sessionsDir)) {
10162
+ if (fs9.existsSync(sessionsDir)) {
8460
10163
  try {
8461
- const sessionDirs = fs7.readdirSync(sessionsDir, { withFileTypes: true });
10164
+ const sessionDirs = fs9.readdirSync(sessionsDir, { withFileTypes: true });
8462
10165
  for (const entry of sessionDirs) {
8463
10166
  if (!entry.isDirectory()) continue;
8464
10167
  const autopilotPath = path9.join(sessionsDir, entry.name, "autopilot-state.json");
8465
- if (!fs7.existsSync(autopilotPath)) continue;
10168
+ if (!fs9.existsSync(autopilotPath)) continue;
8466
10169
  try {
8467
- const raw = fs7.readFileSync(autopilotPath, "utf-8");
10170
+ const raw = fs9.readFileSync(autopilotPath, "utf-8");
8468
10171
  const autopilot = JSON.parse(raw);
8469
10172
  if (!autopilot.phases) continue;
8470
10173
  const linkedPhases = autopilot.phases.filter(
@@ -8511,46 +10214,214 @@ function syncRoadmap(options) {
8511
10214
  to: inferred
8512
10215
  });
8513
10216
  }
8514
- return (0, import_types10.Ok)(changes);
10217
+ return (0, import_types17.Ok)(changes);
8515
10218
  }
8516
10219
 
8517
10220
  // src/interaction/types.ts
8518
- var import_zod5 = require("zod");
8519
- var InteractionTypeSchema = import_zod5.z.enum(["question", "confirmation", "transition"]);
8520
- var QuestionSchema = import_zod5.z.object({
8521
- text: import_zod5.z.string(),
8522
- options: import_zod5.z.array(import_zod5.z.string()).optional(),
8523
- default: import_zod5.z.string().optional()
10221
+ var import_zod7 = require("zod");
10222
+ var InteractionTypeSchema = import_zod7.z.enum(["question", "confirmation", "transition"]);
10223
+ var QuestionSchema = import_zod7.z.object({
10224
+ text: import_zod7.z.string(),
10225
+ options: import_zod7.z.array(import_zod7.z.string()).optional(),
10226
+ default: import_zod7.z.string().optional()
8524
10227
  });
8525
- var ConfirmationSchema = import_zod5.z.object({
8526
- text: import_zod5.z.string(),
8527
- context: import_zod5.z.string()
10228
+ var ConfirmationSchema = import_zod7.z.object({
10229
+ text: import_zod7.z.string(),
10230
+ context: import_zod7.z.string()
8528
10231
  });
8529
- var TransitionSchema = import_zod5.z.object({
8530
- completedPhase: import_zod5.z.string(),
8531
- suggestedNext: import_zod5.z.string(),
8532
- reason: import_zod5.z.string(),
8533
- artifacts: import_zod5.z.array(import_zod5.z.string()),
8534
- requiresConfirmation: import_zod5.z.boolean(),
8535
- summary: import_zod5.z.string()
10232
+ var TransitionSchema = import_zod7.z.object({
10233
+ completedPhase: import_zod7.z.string(),
10234
+ suggestedNext: import_zod7.z.string(),
10235
+ reason: import_zod7.z.string(),
10236
+ artifacts: import_zod7.z.array(import_zod7.z.string()),
10237
+ requiresConfirmation: import_zod7.z.boolean(),
10238
+ summary: import_zod7.z.string()
8536
10239
  });
8537
- var EmitInteractionInputSchema = import_zod5.z.object({
8538
- path: import_zod5.z.string(),
10240
+ var EmitInteractionInputSchema = import_zod7.z.object({
10241
+ path: import_zod7.z.string(),
8539
10242
  type: InteractionTypeSchema,
8540
- stream: import_zod5.z.string().optional(),
10243
+ stream: import_zod7.z.string().optional(),
8541
10244
  question: QuestionSchema.optional(),
8542
10245
  confirmation: ConfirmationSchema.optional(),
8543
10246
  transition: TransitionSchema.optional()
8544
10247
  });
8545
10248
 
8546
- // src/update-checker.ts
8547
- var fs8 = __toESM(require("fs"));
10249
+ // src/blueprint/scanner.ts
10250
+ var fs10 = __toESM(require("fs/promises"));
8548
10251
  var path10 = __toESM(require("path"));
10252
+ var ProjectScanner = class {
10253
+ constructor(rootDir) {
10254
+ this.rootDir = rootDir;
10255
+ }
10256
+ async scan() {
10257
+ let projectName = path10.basename(this.rootDir);
10258
+ try {
10259
+ const pkgPath = path10.join(this.rootDir, "package.json");
10260
+ const pkgRaw = await fs10.readFile(pkgPath, "utf-8");
10261
+ const pkg = JSON.parse(pkgRaw);
10262
+ if (pkg.name) projectName = pkg.name;
10263
+ } catch {
10264
+ }
10265
+ return {
10266
+ projectName,
10267
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10268
+ modules: [
10269
+ {
10270
+ id: "foundations",
10271
+ title: "Foundations",
10272
+ description: "Utility files and basic types.",
10273
+ files: []
10274
+ },
10275
+ {
10276
+ id: "core-logic",
10277
+ title: "Core Logic",
10278
+ description: "Mid-level services and domain logic.",
10279
+ files: []
10280
+ },
10281
+ {
10282
+ id: "interaction-surface",
10283
+ title: "Interaction Surface",
10284
+ description: "APIs, CLIs, and Entry Points.",
10285
+ files: []
10286
+ },
10287
+ {
10288
+ id: "cross-cutting",
10289
+ title: "Cross-Cutting Concerns",
10290
+ description: "Security, Logging, and Observability.",
10291
+ files: []
10292
+ }
10293
+ ],
10294
+ hotspots: [],
10295
+ dependencies: []
10296
+ };
10297
+ }
10298
+ };
10299
+
10300
+ // src/blueprint/generator.ts
10301
+ var fs11 = __toESM(require("fs/promises"));
10302
+ var path11 = __toESM(require("path"));
10303
+ var ejs = __toESM(require("ejs"));
10304
+
10305
+ // src/blueprint/templates.ts
10306
+ var SHELL_TEMPLATE = `
10307
+ <!DOCTYPE html>
10308
+ <html lang="en">
10309
+ <head>
10310
+ <meta charset="UTF-8">
10311
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10312
+ <title>Blueprint: <%= projectName %></title>
10313
+ <style><%- styles %></style>
10314
+ </head>
10315
+ <body>
10316
+ <div id="app">
10317
+ <header>
10318
+ <h1>Blueprint: <%= projectName %></h1>
10319
+ <p>Generated at: <%= generatedAt %></p>
10320
+ </header>
10321
+ <main>
10322
+ <section class="modules">
10323
+ <% modules.forEach(module => { %>
10324
+ <article class="module" id="<%= module.id %>">
10325
+ <h2><%= module.title %></h2>
10326
+ <p><%= module.description %></p>
10327
+ <div class="content">
10328
+ <h3>Code Translation</h3>
10329
+ <div class="translation"><%- module.content.codeTranslation %></div>
10330
+ <h3>Knowledge Check</h3>
10331
+ <div class="quiz">
10332
+ <% module.content.quiz.questions.forEach((q, i) => { %>
10333
+ <div class="question">
10334
+ <p><%= q.question %></p>
10335
+ <button onclick="reveal(this)">Reveal Answer</button>
10336
+ <p class="answer" style="display:none;"><%= q.answer %></p>
10337
+ </div>
10338
+ <% }) %>
10339
+ </div>
10340
+ </div>
10341
+ </article>
10342
+
10343
+ <% }) %>
10344
+ </section>
10345
+ </main>
10346
+ </div>
10347
+ <script><%- scripts %></script>
10348
+ </body>
10349
+ </html>
10350
+ `;
10351
+ var STYLES = `
10352
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; margin: 0; padding: 20px; color: #333; }
10353
+ header { border-bottom: 2px solid #eee; margin-bottom: 20px; padding-bottom: 10px; }
10354
+ .module { background: #f9f9f9; border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; border-radius: 4px; }
10355
+ .module h2 { margin-top: 0; color: #0066cc; }
10356
+ `;
10357
+ var SCRIPTS = `
10358
+ function reveal(btn) {
10359
+ btn.nextElementSibling.style.display = 'block';
10360
+ btn.style.display = 'none';
10361
+ }
10362
+ console.log('Blueprint player initialized.');
10363
+ `;
10364
+
10365
+ // src/shared/llm.ts
10366
+ var MockLLMService = class {
10367
+ async generate(prompt) {
10368
+ return "This is a mock LLM response for: " + prompt;
10369
+ }
10370
+ };
10371
+ var llmService = new MockLLMService();
10372
+
10373
+ // src/blueprint/content-pipeline.ts
10374
+ var ContentPipeline = class {
10375
+ async generateModuleContent(module2) {
10376
+ const codeContext = module2.files.join("\n");
10377
+ const translation = await llmService.generate(
10378
+ `You are a technical educator. Explain the following code clearly and concisely: ${codeContext}`
10379
+ );
10380
+ const quizJson = await llmService.generate(
10381
+ `Create 3 technical quiz questions for this code. Return ONLY valid JSON in this format: { "questions": [{ "question": "...", "answer": "..." }] }. Code: ${codeContext}`
10382
+ );
10383
+ let quiz;
10384
+ try {
10385
+ const cleanJson = quizJson.replace(/```json/g, "").replace(/```/g, "").trim();
10386
+ quiz = JSON.parse(cleanJson);
10387
+ } catch (e) {
10388
+ console.error("Failed to parse quiz JSON", e);
10389
+ quiz = { questions: [{ question: "Failed to generate quiz", answer: "N/A" }] };
10390
+ }
10391
+ return {
10392
+ codeTranslation: translation,
10393
+ quiz
10394
+ };
10395
+ }
10396
+ };
10397
+
10398
+ // src/blueprint/generator.ts
10399
+ var BlueprintGenerator = class {
10400
+ contentPipeline = new ContentPipeline();
10401
+ async generate(data, options) {
10402
+ await Promise.all(
10403
+ data.modules.map(async (module2) => {
10404
+ module2.content = await this.contentPipeline.generateModuleContent(module2);
10405
+ })
10406
+ );
10407
+ const html = ejs.render(SHELL_TEMPLATE, {
10408
+ ...data,
10409
+ styles: STYLES,
10410
+ scripts: SCRIPTS
10411
+ });
10412
+ await fs11.mkdir(options.outputDir, { recursive: true });
10413
+ await fs11.writeFile(path11.join(options.outputDir, "index.html"), html);
10414
+ }
10415
+ };
10416
+
10417
+ // src/update-checker.ts
10418
+ var fs12 = __toESM(require("fs"));
10419
+ var path12 = __toESM(require("path"));
8549
10420
  var os = __toESM(require("os"));
8550
10421
  var import_child_process3 = require("child_process");
8551
10422
  function getStatePath() {
8552
10423
  const home = process.env["HOME"] || os.homedir();
8553
- return path10.join(home, ".harness", "update-check.json");
10424
+ return path12.join(home, ".harness", "update-check.json");
8554
10425
  }
8555
10426
  function isUpdateCheckEnabled(configInterval) {
8556
10427
  if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
@@ -8563,7 +10434,7 @@ function shouldRunCheck(state, intervalMs) {
8563
10434
  }
8564
10435
  function readCheckState() {
8565
10436
  try {
8566
- const raw = fs8.readFileSync(getStatePath(), "utf-8");
10437
+ const raw = fs12.readFileSync(getStatePath(), "utf-8");
8567
10438
  const parsed = JSON.parse(raw);
8568
10439
  if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
8569
10440
  const state = parsed;
@@ -8580,7 +10451,7 @@ function readCheckState() {
8580
10451
  }
8581
10452
  function spawnBackgroundCheck(currentVersion) {
8582
10453
  const statePath = getStatePath();
8583
- const stateDir = path10.dirname(statePath);
10454
+ const stateDir = path12.dirname(statePath);
8584
10455
  const script = `
8585
10456
  const { execSync } = require('child_process');
8586
10457
  const fs = require('fs');
@@ -8634,39 +10505,64 @@ Run "harness update" to upgrade.`;
8634
10505
  }
8635
10506
 
8636
10507
  // src/index.ts
8637
- var VERSION = "1.8.2";
10508
+ var VERSION = "0.11.0";
8638
10509
  // Annotate the CommonJS export names for ESM import in node:
8639
10510
  0 && (module.exports = {
8640
10511
  AGENT_DESCRIPTORS,
8641
10512
  ARCHITECTURE_DESCRIPTOR,
8642
10513
  AgentActionEmitter,
10514
+ ArchBaselineManager,
10515
+ ArchBaselineSchema,
10516
+ ArchConfigSchema,
10517
+ ArchDiffResultSchema,
10518
+ ArchMetricCategorySchema,
8643
10519
  BUG_DETECTION_DESCRIPTOR,
8644
10520
  BaselineManager,
8645
10521
  BenchmarkRunner,
10522
+ BlueprintGenerator,
10523
+ BundleConstraintsSchema,
10524
+ BundleSchema,
8646
10525
  COMPLIANCE_DESCRIPTOR,
10526
+ CategoryBaselineSchema,
10527
+ CategoryRegressionSchema,
8647
10528
  ChecklistBuilder,
10529
+ CircularDepsCollector,
10530
+ ComplexityCollector,
8648
10531
  ConfirmationSchema,
8649
10532
  ConsoleSink,
10533
+ ConstraintRuleSchema,
10534
+ ContentPipeline,
10535
+ ContributionsSchema,
10536
+ CouplingCollector,
8650
10537
  CriticalPathResolver,
8651
10538
  DEFAULT_PROVIDER_TIERS,
8652
10539
  DEFAULT_SECURITY_CONFIG,
8653
10540
  DEFAULT_STATE,
8654
10541
  DEFAULT_STREAM_INDEX,
10542
+ DepDepthCollector,
8655
10543
  EmitInteractionInputSchema,
8656
10544
  EntropyAnalyzer,
8657
10545
  EntropyConfigSchema,
8658
10546
  ExclusionSet,
8659
10547
  FailureEntrySchema,
8660
10548
  FileSink,
10549
+ ForbiddenImportCollector,
8661
10550
  GateConfigSchema,
8662
10551
  GateResultSchema,
8663
10552
  HandoffSchema,
8664
10553
  HarnessStateSchema,
8665
10554
  InteractionTypeSchema,
10555
+ LayerViolationCollector,
10556
+ LockfilePackageSchema,
10557
+ LockfileSchema,
10558
+ ManifestSchema,
10559
+ MetricResultSchema,
10560
+ ModuleSizeCollector,
8666
10561
  NoOpExecutor,
8667
10562
  NoOpSink,
8668
10563
  NoOpTelemetryAdapter,
8669
10564
  PatternConfigSchema,
10565
+ ProjectScanner,
8670
10566
  QuestionSchema,
8671
10567
  REQUIRED_SECTIONS,
8672
10568
  RegressionDetector,
@@ -8674,16 +10570,26 @@ var VERSION = "1.8.2";
8674
10570
  SECURITY_DESCRIPTOR,
8675
10571
  SecurityConfigSchema,
8676
10572
  SecurityScanner,
10573
+ SharableBoundaryConfigSchema,
10574
+ SharableForbiddenImportSchema,
10575
+ SharableLayerSchema,
10576
+ SharableSecurityRulesSchema,
8677
10577
  StreamIndexSchema,
8678
10578
  StreamInfoSchema,
10579
+ ThresholdConfigSchema,
8679
10580
  TransitionSchema,
8680
10581
  TypeScriptParser,
8681
10582
  VERSION,
10583
+ ViolationSchema,
10584
+ addProvenance,
8682
10585
  analyzeDiff,
8683
10586
  appendFailure,
8684
10587
  appendLearning,
8685
10588
  applyFixes,
8686
10589
  applyHotspotDowngrade,
10590
+ archMatchers,
10591
+ archModule,
10592
+ architecture,
8687
10593
  archiveFailures,
8688
10594
  archiveStream,
8689
10595
  buildDependencyGraph,
@@ -8693,6 +10599,7 @@ var VERSION = "1.8.2";
8693
10599
  checkEligibility,
8694
10600
  classifyFinding,
8695
10601
  configureFeedback,
10602
+ constraintRuleId,
8696
10603
  contextBudget,
8697
10604
  contextFilter,
8698
10605
  createBoundaryValidator,
@@ -8707,6 +10614,8 @@ var VERSION = "1.8.2";
8707
10614
  cryptoRules,
8708
10615
  deduplicateCleanupFindings,
8709
10616
  deduplicateFindings,
10617
+ deepMergeConstraints,
10618
+ defaultCollectors,
8710
10619
  defineLayer,
8711
10620
  deserializationRules,
8712
10621
  detectChangeType,
@@ -8719,9 +10628,12 @@ var VERSION = "1.8.2";
8719
10628
  detectPatternViolations,
8720
10629
  detectSizeBudgetViolations,
8721
10630
  detectStack,
10631
+ detectStaleConstraints,
8722
10632
  determineAssessment,
10633
+ diff,
8723
10634
  executeWorkflow,
8724
10635
  expressRules,
10636
+ extractBundle,
8725
10637
  extractMarkdownLinks,
8726
10638
  extractSections,
8727
10639
  fanOutReview,
@@ -8752,6 +10664,7 @@ var VERSION = "1.8.2";
8752
10664
  networkRules,
8753
10665
  nodeRules,
8754
10666
  parseDiff,
10667
+ parseManifest,
8755
10668
  parseRoadmap,
8756
10669
  parseSecurityConfig,
8757
10670
  parseSize,
@@ -8759,6 +10672,8 @@ var VERSION = "1.8.2";
8759
10672
  previewFix,
8760
10673
  reactRules,
8761
10674
  readCheckState,
10675
+ readLockfile,
10676
+ removeProvenance,
8762
10677
  requestMultiplePeerReviews,
8763
10678
  requestPeerReview,
8764
10679
  resetFeedbackConfig,
@@ -8766,6 +10681,8 @@ var VERSION = "1.8.2";
8766
10681
  resolveModelTier,
8767
10682
  resolveRuleSeverity,
8768
10683
  resolveStreamPath,
10684
+ resolveThresholds,
10685
+ runAll,
8769
10686
  runArchitectureAgent,
8770
10687
  runBugDetectionAgent,
8771
10688
  runCIChecks,
@@ -8785,6 +10702,7 @@ var VERSION = "1.8.2";
8785
10702
  setActiveStream,
8786
10703
  shouldRunCheck,
8787
10704
  spawnBackgroundCheck,
10705
+ syncConstraintNodes,
8788
10706
  syncRoadmap,
8789
10707
  touchStream,
8790
10708
  trackAction,
@@ -8797,6 +10715,9 @@ var VERSION = "1.8.2";
8797
10715
  validateFindings,
8798
10716
  validateKnowledgeMap,
8799
10717
  validatePatternConfig,
10718
+ violationId,
10719
+ writeConfig,
10720
+ writeLockfile,
8800
10721
  xssRules,
8801
10722
  ...require("@harness-engineering/types")
8802
10723
  });