@harness-engineering/core 0.10.1 → 0.12.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,9 @@ __export(index_exports, {
153
195
  previewFix: () => previewFix,
154
196
  reactRules: () => reactRules,
155
197
  readCheckState: () => readCheckState,
198
+ readLockfile: () => readLockfile,
199
+ removeContributions: () => removeContributions,
200
+ removeProvenance: () => removeProvenance,
156
201
  requestMultiplePeerReviews: () => requestMultiplePeerReviews,
157
202
  requestPeerReview: () => requestPeerReview,
158
203
  resetFeedbackConfig: () => resetFeedbackConfig,
@@ -160,6 +205,8 @@ __export(index_exports, {
160
205
  resolveModelTier: () => resolveModelTier,
161
206
  resolveRuleSeverity: () => resolveRuleSeverity,
162
207
  resolveStreamPath: () => resolveStreamPath,
208
+ resolveThresholds: () => resolveThresholds,
209
+ runAll: () => runAll,
163
210
  runArchitectureAgent: () => runArchitectureAgent,
164
211
  runBugDetectionAgent: () => runBugDetectionAgent,
165
212
  runCIChecks: () => runCIChecks,
@@ -179,6 +226,7 @@ __export(index_exports, {
179
226
  setActiveStream: () => setActiveStream,
180
227
  shouldRunCheck: () => shouldRunCheck,
181
228
  spawnBackgroundCheck: () => spawnBackgroundCheck,
229
+ syncConstraintNodes: () => syncConstraintNodes,
182
230
  syncRoadmap: () => syncRoadmap,
183
231
  touchStream: () => touchStream,
184
232
  trackAction: () => trackAction,
@@ -191,6 +239,9 @@ __export(index_exports, {
191
239
  validateFindings: () => validateFindings,
192
240
  validateKnowledgeMap: () => validateKnowledgeMap,
193
241
  validatePatternConfig: () => validatePatternConfig,
242
+ violationId: () => violationId,
243
+ writeConfig: () => writeConfig,
244
+ writeLockfile: () => writeLockfile,
194
245
  xssRules: () => xssRules
195
246
  });
196
247
  module.exports = __toCommonJS(index_exports);
@@ -213,17 +264,17 @@ var import_util = require("util");
213
264
  var import_glob = require("glob");
214
265
  var accessAsync = (0, import_util.promisify)(import_fs.access);
215
266
  var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
216
- async function fileExists(path11) {
267
+ async function fileExists(path13) {
217
268
  try {
218
- await accessAsync(path11, import_fs.constants.F_OK);
269
+ await accessAsync(path13, import_fs.constants.F_OK);
219
270
  return true;
220
271
  } catch {
221
272
  return false;
222
273
  }
223
274
  }
224
- async function readFileContent(path11) {
275
+ async function readFileContent(path13) {
225
276
  try {
226
- const content = await readFileAsync(path11, "utf-8");
277
+ const content = await readFileAsync(path13, "utf-8");
227
278
  return (0, import_types.Ok)(content);
228
279
  } catch (error) {
229
280
  return (0, import_types.Err)(error);
@@ -271,15 +322,15 @@ function validateConfig(data, schema) {
271
322
  let message = "Configuration validation failed";
272
323
  const suggestions = [];
273
324
  if (firstError) {
274
- const path11 = firstError.path.join(".");
275
- const pathDisplay = path11 ? ` at "${path11}"` : "";
325
+ const path13 = firstError.path.join(".");
326
+ const pathDisplay = path13 ? ` at "${path13}"` : "";
276
327
  if (firstError.code === "invalid_type") {
277
328
  const received = firstError.received;
278
329
  const expected = firstError.expected;
279
330
  if (received === "undefined") {
280
331
  code = "MISSING_FIELD";
281
332
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
282
- suggestions.push(`Field "${path11}" is required and must be of type "${expected}"`);
333
+ suggestions.push(`Field "${path13}" is required and must be of type "${expected}"`);
283
334
  } else {
284
335
  code = "INVALID_TYPE";
285
336
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -492,30 +543,30 @@ function extractSections(content) {
492
543
  return result;
493
544
  });
494
545
  }
495
- function isExternalLink(path11) {
496
- return path11.startsWith("http://") || path11.startsWith("https://") || path11.startsWith("#") || path11.startsWith("mailto:");
546
+ function isExternalLink(path13) {
547
+ return path13.startsWith("http://") || path13.startsWith("https://") || path13.startsWith("#") || path13.startsWith("mailto:");
497
548
  }
498
549
  function resolveLinkPath(linkPath, baseDir) {
499
550
  return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
500
551
  }
501
- async function validateAgentsMap(path11 = "./AGENTS.md") {
552
+ async function validateAgentsMap(path13 = "./AGENTS.md") {
502
553
  console.warn(
503
554
  "[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
504
555
  );
505
- const contentResult = await readFileContent(path11);
556
+ const contentResult = await readFileContent(path13);
506
557
  if (!contentResult.ok) {
507
558
  return (0, import_types.Err)(
508
559
  createError(
509
560
  "PARSE_ERROR",
510
561
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
511
- { path: path11 },
562
+ { path: path13 },
512
563
  ["Ensure the file exists", "Check file permissions"]
513
564
  )
514
565
  );
515
566
  }
516
567
  const content = contentResult.value;
517
568
  const sections = extractSections(content);
518
- const baseDir = (0, import_path.dirname)(path11);
569
+ const baseDir = (0, import_path.dirname)(path13);
519
570
  const sectionTitles = sections.map((s) => s.title);
520
571
  const missingSections = REQUIRED_SECTIONS.filter(
521
572
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -555,6 +606,7 @@ async function validateAgentsMap(path11 = "./AGENTS.md") {
555
606
  }
556
607
 
557
608
  // src/context/doc-coverage.ts
609
+ var import_minimatch = require("minimatch");
558
610
  var import_path2 = require("path");
559
611
  function determineImportance(filePath) {
560
612
  const name = (0, import_path2.basename)(filePath).toLowerCase();
@@ -577,7 +629,7 @@ function suggestSection(filePath, domain) {
577
629
  return `${domain} Reference`;
578
630
  }
579
631
  async function checkDocCoverage(domain, options = {}) {
580
- const { docsDir = "./docs", sourceDir = "./src", excludePatterns = [], graphCoverage } = options;
632
+ const { docsDir = "./docs", sourceDir = ".", excludePatterns = [], graphCoverage } = options;
581
633
  if (graphCoverage) {
582
634
  const gaps = graphCoverage.undocumented.map((file) => ({
583
635
  file,
@@ -597,9 +649,7 @@ async function checkDocCoverage(domain, options = {}) {
597
649
  const filteredSourceFiles = sourceFiles.filter((file) => {
598
650
  const relativePath = (0, import_path2.relative)(sourceDir, file);
599
651
  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);
652
+ return (0, import_minimatch.minimatch)(relativePath, pattern, { dot: true }) || (0, import_minimatch.minimatch)(file, pattern, { dot: true });
603
653
  });
604
654
  });
605
655
  const docFiles = await findFiles("**/*.md", docsDir);
@@ -657,8 +707,8 @@ async function checkDocCoverage(domain, options = {}) {
657
707
 
658
708
  // src/context/knowledge-map.ts
659
709
  var import_path3 = require("path");
660
- function suggestFix(path11, existingFiles) {
661
- const targetName = (0, import_path3.basename)(path11).toLowerCase();
710
+ function suggestFix(path13, existingFiles) {
711
+ const targetName = (0, import_path3.basename)(path13).toLowerCase();
662
712
  const similar = existingFiles.find((file) => {
663
713
  const fileName = (0, import_path3.basename)(file).toLowerCase();
664
714
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -666,7 +716,7 @@ function suggestFix(path11, existingFiles) {
666
716
  if (similar) {
667
717
  return `Did you mean "${similar}"?`;
668
718
  }
669
- return `Create the file "${path11}" or remove the link`;
719
+ return `Create the file "${path13}" or remove the link`;
670
720
  }
671
721
  async function validateKnowledgeMap(rootDir = process.cwd()) {
672
722
  console.warn(
@@ -1011,7 +1061,7 @@ function getPhaseCategories(phase) {
1011
1061
  }
1012
1062
 
1013
1063
  // src/constraints/layers.ts
1014
- var import_minimatch = require("minimatch");
1064
+ var import_minimatch2 = require("minimatch");
1015
1065
  function defineLayer(name, patterns, allowedDependencies) {
1016
1066
  return {
1017
1067
  name,
@@ -1022,7 +1072,7 @@ function defineLayer(name, patterns, allowedDependencies) {
1022
1072
  function resolveFileToLayer(file, layers) {
1023
1073
  for (const layer of layers) {
1024
1074
  for (const pattern of layer.patterns) {
1025
- if ((0, import_minimatch.minimatch)(file, pattern)) {
1075
+ if ((0, import_minimatch2.minimatch)(file, pattern)) {
1026
1076
  return layer;
1027
1077
  }
1028
1078
  }
@@ -1269,8 +1319,8 @@ function createBoundaryValidator(schema, name) {
1269
1319
  return (0, import_types.Ok)(result.data);
1270
1320
  }
1271
1321
  const suggestions = result.error.issues.map((issue) => {
1272
- const path11 = issue.path.join(".");
1273
- return path11 ? `${path11}: ${issue.message}` : issue.message;
1322
+ const path13 = issue.path.join(".");
1323
+ return path13 ? `${path13}: ${issue.message}` : issue.message;
1274
1324
  });
1275
1325
  return (0, import_types.Err)(
1276
1326
  createError(
@@ -1314,6 +1364,491 @@ function validateBoundaries(boundaries, data) {
1314
1364
  });
1315
1365
  }
1316
1366
 
1367
+ // src/constraints/sharing/types.ts
1368
+ var import_zod = require("zod");
1369
+ var ManifestSchema = import_zod.z.object({
1370
+ name: import_zod.z.string(),
1371
+ version: import_zod.z.string(),
1372
+ description: import_zod.z.string().optional(),
1373
+ include: import_zod.z.array(import_zod.z.string()).min(1),
1374
+ minHarnessVersion: import_zod.z.string().optional(),
1375
+ keywords: import_zod.z.array(import_zod.z.string()).optional().default([]),
1376
+ layers: import_zod.z.record(import_zod.z.array(import_zod.z.string())).optional(),
1377
+ boundaries: import_zod.z.array(
1378
+ import_zod.z.object({
1379
+ name: import_zod.z.string(),
1380
+ layer: import_zod.z.string(),
1381
+ direction: import_zod.z.enum(["input", "output"]),
1382
+ schema: import_zod.z.string()
1383
+ })
1384
+ ).optional()
1385
+ });
1386
+ var BundleConstraintsSchema = import_zod.z.object({
1387
+ layers: import_zod.z.array(
1388
+ import_zod.z.object({
1389
+ name: import_zod.z.string(),
1390
+ pattern: import_zod.z.string(),
1391
+ allowedDependencies: import_zod.z.array(import_zod.z.string())
1392
+ })
1393
+ ).optional(),
1394
+ forbiddenImports: import_zod.z.array(
1395
+ import_zod.z.object({
1396
+ from: import_zod.z.string(),
1397
+ disallow: import_zod.z.array(import_zod.z.string()),
1398
+ message: import_zod.z.string().optional()
1399
+ })
1400
+ ).optional(),
1401
+ boundaries: import_zod.z.object({
1402
+ requireSchema: import_zod.z.array(import_zod.z.string()).optional()
1403
+ }).optional(),
1404
+ architecture: import_zod.z.object({
1405
+ thresholds: import_zod.z.record(import_zod.z.unknown()).optional(),
1406
+ modules: import_zod.z.record(import_zod.z.record(import_zod.z.unknown())).optional()
1407
+ }).optional(),
1408
+ security: import_zod.z.object({
1409
+ rules: import_zod.z.record(import_zod.z.string()).optional()
1410
+ }).optional()
1411
+ });
1412
+ var BundleSchema = import_zod.z.object({
1413
+ name: import_zod.z.string(),
1414
+ version: import_zod.z.string(),
1415
+ description: import_zod.z.string().optional(),
1416
+ minHarnessVersion: import_zod.z.string().optional(),
1417
+ manifest: ManifestSchema,
1418
+ constraints: BundleConstraintsSchema,
1419
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional()
1420
+ });
1421
+ var ContributionsSchema = import_zod.z.record(import_zod.z.unknown());
1422
+ var LockfilePackageSchema = import_zod.z.object({
1423
+ version: import_zod.z.string(),
1424
+ source: import_zod.z.string(),
1425
+ installedAt: import_zod.z.string(),
1426
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional().nullable(),
1427
+ resolved: import_zod.z.string().optional(),
1428
+ integrity: import_zod.z.string().optional(),
1429
+ provenance: import_zod.z.array(import_zod.z.string()).optional()
1430
+ });
1431
+ var LockfileSchema = import_zod.z.object({
1432
+ version: import_zod.z.literal(1).optional(),
1433
+ // Used by some tests
1434
+ lockfileVersion: import_zod.z.literal(1).optional(),
1435
+ // Standard field
1436
+ packages: import_zod.z.record(LockfilePackageSchema)
1437
+ }).refine((data) => data.version !== void 0 || data.lockfileVersion !== void 0, {
1438
+ message: "Either 'version' or 'lockfileVersion' must be present and equal to 1"
1439
+ });
1440
+ var SharableLayerSchema = import_zod.z.unknown();
1441
+ var SharableForbiddenImportSchema = import_zod.z.unknown();
1442
+ var SharableBoundaryConfigSchema = import_zod.z.unknown();
1443
+ var SharableSecurityRulesSchema = import_zod.z.unknown();
1444
+
1445
+ // src/constraints/sharing/write-config.ts
1446
+ var fs = __toESM(require("fs/promises"));
1447
+ async function writeConfig(filePath, content) {
1448
+ try {
1449
+ const json = JSON.stringify(content, null, 2) + "\n";
1450
+ await fs.writeFile(filePath, json, "utf-8");
1451
+ return (0, import_types.Ok)(void 0);
1452
+ } catch (error) {
1453
+ return (0, import_types.Err)(error instanceof Error ? error : new Error(String(error)));
1454
+ }
1455
+ }
1456
+
1457
+ // src/constraints/sharing/manifest.ts
1458
+ function parseManifest(parsed) {
1459
+ const result = ManifestSchema.safeParse(parsed);
1460
+ if (!result.success) {
1461
+ const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1462
+ return { ok: false, error: `Invalid manifest: ${issues}` };
1463
+ }
1464
+ return { ok: true, value: result.data };
1465
+ }
1466
+
1467
+ // src/constraints/sharing/bundle.ts
1468
+ function resolveDotPath(obj, dotPath) {
1469
+ const segments = dotPath.split(".");
1470
+ let current = obj;
1471
+ for (const segment of segments) {
1472
+ if (current === null || typeof current !== "object") {
1473
+ return void 0;
1474
+ }
1475
+ current = current[segment];
1476
+ }
1477
+ return current;
1478
+ }
1479
+ function setDotPath(obj, dotPath, value) {
1480
+ const segments = dotPath.split(".");
1481
+ const lastSegment = segments[segments.length - 1];
1482
+ const parentSegments = segments.slice(0, -1);
1483
+ let current = obj;
1484
+ for (const segment of parentSegments) {
1485
+ if (current[segment] === void 0 || current[segment] === null || typeof current[segment] !== "object") {
1486
+ current[segment] = {};
1487
+ }
1488
+ current = current[segment];
1489
+ }
1490
+ if (lastSegment !== void 0) {
1491
+ current[lastSegment] = value;
1492
+ }
1493
+ }
1494
+ function extractBundle(manifest, config) {
1495
+ const constraints = {};
1496
+ for (const includePath of manifest.include) {
1497
+ const value = resolveDotPath(config, includePath);
1498
+ if (value !== void 0) {
1499
+ setDotPath(constraints, includePath, value);
1500
+ }
1501
+ }
1502
+ const bundle = {
1503
+ name: manifest.name,
1504
+ version: manifest.version,
1505
+ ...manifest.minHarnessVersion !== void 0 && {
1506
+ minHarnessVersion: manifest.minHarnessVersion
1507
+ },
1508
+ ...manifest.description !== void 0 && {
1509
+ description: manifest.description
1510
+ },
1511
+ manifest,
1512
+ constraints
1513
+ };
1514
+ const parsed = BundleSchema.safeParse(bundle);
1515
+ if (!parsed.success) {
1516
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1517
+ return { ok: false, error: `Invalid bundle: ${issues}` };
1518
+ }
1519
+ return { ok: true, value: parsed.data };
1520
+ }
1521
+
1522
+ // src/constraints/sharing/merge.ts
1523
+ function deepEqual(a, b) {
1524
+ if (a === b) return true;
1525
+ if (typeof a !== typeof b) return false;
1526
+ if (typeof a !== "object" || a === null || b === null) return false;
1527
+ if (Array.isArray(a) && Array.isArray(b)) {
1528
+ if (a.length !== b.length) return false;
1529
+ return a.every((val, i) => deepEqual(val, b[i]));
1530
+ }
1531
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1532
+ const keysA = Object.keys(a);
1533
+ const keysB = Object.keys(b);
1534
+ if (keysA.length !== keysB.length) return false;
1535
+ return keysA.every(
1536
+ (key) => deepEqual(a[key], b[key])
1537
+ );
1538
+ }
1539
+ function stringArraysEqual(a, b) {
1540
+ if (a.length !== b.length) return false;
1541
+ const sortedA = [...a].sort();
1542
+ const sortedB = [...b].sort();
1543
+ return sortedA.every((val, i) => val === sortedB[i]);
1544
+ }
1545
+ function deepMergeConstraints(localConfig, bundleConstraints, _existingContributions) {
1546
+ const config = { ...localConfig };
1547
+ const contributions = {};
1548
+ const conflicts = [];
1549
+ if (bundleConstraints.layers && bundleConstraints.layers.length > 0) {
1550
+ const localLayers = Array.isArray(localConfig.layers) ? localConfig.layers : [];
1551
+ const mergedLayers = [...localLayers];
1552
+ const contributedLayerNames = [];
1553
+ for (const bundleLayer of bundleConstraints.layers) {
1554
+ const existing = localLayers.find((l) => l.name === bundleLayer.name);
1555
+ if (!existing) {
1556
+ mergedLayers.push(bundleLayer);
1557
+ contributedLayerNames.push(bundleLayer.name);
1558
+ } else {
1559
+ const same = existing.pattern === bundleLayer.pattern && stringArraysEqual(existing.allowedDependencies, bundleLayer.allowedDependencies);
1560
+ if (!same) {
1561
+ conflicts.push({
1562
+ section: "layers",
1563
+ key: bundleLayer.name,
1564
+ localValue: existing,
1565
+ packageValue: bundleLayer,
1566
+ description: `Layer '${bundleLayer.name}' already exists locally with different configuration`
1567
+ });
1568
+ }
1569
+ }
1570
+ }
1571
+ config.layers = mergedLayers;
1572
+ if (contributedLayerNames.length > 0) {
1573
+ contributions.layers = contributedLayerNames;
1574
+ }
1575
+ }
1576
+ if (bundleConstraints.forbiddenImports && bundleConstraints.forbiddenImports.length > 0) {
1577
+ const localFI = Array.isArray(localConfig.forbiddenImports) ? localConfig.forbiddenImports : [];
1578
+ const mergedFI = [...localFI];
1579
+ const contributedFromKeys = [];
1580
+ for (const bundleRule of bundleConstraints.forbiddenImports) {
1581
+ const existing = localFI.find((r) => r.from === bundleRule.from);
1582
+ if (!existing) {
1583
+ const entry = {
1584
+ from: bundleRule.from,
1585
+ disallow: bundleRule.disallow
1586
+ };
1587
+ if (bundleRule.message !== void 0) {
1588
+ entry.message = bundleRule.message;
1589
+ }
1590
+ mergedFI.push(entry);
1591
+ contributedFromKeys.push(bundleRule.from);
1592
+ } else {
1593
+ const same = stringArraysEqual(existing.disallow, bundleRule.disallow);
1594
+ if (!same) {
1595
+ conflicts.push({
1596
+ section: "forbiddenImports",
1597
+ key: bundleRule.from,
1598
+ localValue: existing,
1599
+ packageValue: bundleRule,
1600
+ description: `Forbidden import rule for '${bundleRule.from}' already exists locally with different disallow list`
1601
+ });
1602
+ }
1603
+ }
1604
+ }
1605
+ config.forbiddenImports = mergedFI;
1606
+ if (contributedFromKeys.length > 0) {
1607
+ contributions.forbiddenImports = contributedFromKeys;
1608
+ }
1609
+ }
1610
+ if (bundleConstraints.boundaries) {
1611
+ const localBoundaries = localConfig.boundaries ?? { requireSchema: [] };
1612
+ const localSchemas = new Set(localBoundaries.requireSchema ?? []);
1613
+ const bundleSchemas = bundleConstraints.boundaries.requireSchema ?? [];
1614
+ const newSchemas = [];
1615
+ for (const schema of bundleSchemas) {
1616
+ if (!localSchemas.has(schema)) {
1617
+ newSchemas.push(schema);
1618
+ localSchemas.add(schema);
1619
+ }
1620
+ }
1621
+ config.boundaries = {
1622
+ requireSchema: [...localBoundaries.requireSchema ?? [], ...newSchemas]
1623
+ };
1624
+ if (newSchemas.length > 0) {
1625
+ contributions.boundaries = newSchemas;
1626
+ }
1627
+ }
1628
+ if (bundleConstraints.architecture) {
1629
+ const localArch = localConfig.architecture ?? {
1630
+ thresholds: {},
1631
+ modules: {}
1632
+ };
1633
+ const mergedThresholds = { ...localArch.thresholds };
1634
+ const contributedThresholdKeys = [];
1635
+ const bundleThresholds = bundleConstraints.architecture.thresholds ?? {};
1636
+ for (const [category, value] of Object.entries(bundleThresholds)) {
1637
+ if (!(category in mergedThresholds)) {
1638
+ mergedThresholds[category] = value;
1639
+ contributedThresholdKeys.push(category);
1640
+ } else if (!deepEqual(mergedThresholds[category], value)) {
1641
+ conflicts.push({
1642
+ section: "architecture.thresholds",
1643
+ key: category,
1644
+ localValue: mergedThresholds[category],
1645
+ packageValue: value,
1646
+ description: `Architecture threshold '${category}' already exists locally with a different value`
1647
+ });
1648
+ }
1649
+ }
1650
+ const mergedModules = { ...localArch.modules };
1651
+ const contributedModuleKeys = [];
1652
+ const bundleModules = bundleConstraints.architecture.modules ?? {};
1653
+ for (const [modulePath, bundleCategoryMap] of Object.entries(bundleModules)) {
1654
+ if (!(modulePath in mergedModules)) {
1655
+ mergedModules[modulePath] = bundleCategoryMap;
1656
+ for (const cat of Object.keys(bundleCategoryMap)) {
1657
+ contributedModuleKeys.push(`${modulePath}:${cat}`);
1658
+ }
1659
+ } else {
1660
+ const localCategoryMap = mergedModules[modulePath];
1661
+ const mergedCategoryMap = { ...localCategoryMap };
1662
+ for (const [category, value] of Object.entries(bundleCategoryMap)) {
1663
+ if (!(category in mergedCategoryMap)) {
1664
+ mergedCategoryMap[category] = value;
1665
+ contributedModuleKeys.push(`${modulePath}:${category}`);
1666
+ } else if (!deepEqual(mergedCategoryMap[category], value)) {
1667
+ conflicts.push({
1668
+ section: "architecture.modules",
1669
+ key: `${modulePath}:${category}`,
1670
+ localValue: mergedCategoryMap[category],
1671
+ packageValue: value,
1672
+ description: `Architecture module override '${modulePath}' category '${category}' already exists locally with a different value`
1673
+ });
1674
+ }
1675
+ }
1676
+ mergedModules[modulePath] = mergedCategoryMap;
1677
+ }
1678
+ }
1679
+ config.architecture = {
1680
+ ...localArch,
1681
+ thresholds: mergedThresholds,
1682
+ modules: mergedModules
1683
+ };
1684
+ if (contributedThresholdKeys.length > 0) {
1685
+ contributions["architecture.thresholds"] = contributedThresholdKeys;
1686
+ }
1687
+ if (contributedModuleKeys.length > 0) {
1688
+ contributions["architecture.modules"] = contributedModuleKeys;
1689
+ }
1690
+ }
1691
+ if (bundleConstraints.security?.rules) {
1692
+ const localSecurity = localConfig.security ?? { rules: {} };
1693
+ const localRules = localSecurity.rules ?? {};
1694
+ const mergedRules = { ...localRules };
1695
+ const contributedRuleIds = [];
1696
+ for (const [ruleId, severity] of Object.entries(bundleConstraints.security.rules)) {
1697
+ if (!(ruleId in mergedRules)) {
1698
+ mergedRules[ruleId] = severity;
1699
+ contributedRuleIds.push(ruleId);
1700
+ } else if (mergedRules[ruleId] !== severity) {
1701
+ conflicts.push({
1702
+ section: "security.rules",
1703
+ key: ruleId,
1704
+ localValue: mergedRules[ruleId],
1705
+ packageValue: severity,
1706
+ description: `Security rule '${ruleId}' already exists locally with severity '${mergedRules[ruleId]}', bundle has '${severity}'`
1707
+ });
1708
+ }
1709
+ }
1710
+ config.security = { ...localSecurity, rules: mergedRules };
1711
+ if (contributedRuleIds.length > 0) {
1712
+ contributions["security.rules"] = contributedRuleIds;
1713
+ }
1714
+ }
1715
+ return { config, contributions, conflicts };
1716
+ }
1717
+
1718
+ // src/constraints/sharing/lockfile.ts
1719
+ var fs2 = __toESM(require("fs/promises"));
1720
+ async function readLockfile(lockfilePath) {
1721
+ let raw;
1722
+ try {
1723
+ raw = await fs2.readFile(lockfilePath, "utf-8");
1724
+ } catch (err) {
1725
+ if (isNodeError(err) && err.code === "ENOENT") {
1726
+ return { ok: true, value: null };
1727
+ }
1728
+ return {
1729
+ ok: false,
1730
+ error: `Failed to read lockfile: ${err instanceof Error ? err.message : String(err)}`
1731
+ };
1732
+ }
1733
+ let parsed;
1734
+ try {
1735
+ parsed = JSON.parse(raw);
1736
+ } catch {
1737
+ return {
1738
+ ok: false,
1739
+ error: `Failed to parse lockfile as JSON: file contains invalid JSON`
1740
+ };
1741
+ }
1742
+ const result = LockfileSchema.safeParse(parsed);
1743
+ if (!result.success) {
1744
+ return {
1745
+ ok: false,
1746
+ error: `Lockfile schema validation failed: ${result.error.issues.map((i) => i.message).join(", ")}`
1747
+ };
1748
+ }
1749
+ return { ok: true, value: result.data };
1750
+ }
1751
+ async function writeLockfile(lockfilePath, lockfile) {
1752
+ return writeConfig(lockfilePath, lockfile);
1753
+ }
1754
+ function addProvenance(lockfile, packageName, entry) {
1755
+ return {
1756
+ ...lockfile,
1757
+ packages: {
1758
+ ...lockfile.packages,
1759
+ [packageName]: entry
1760
+ }
1761
+ };
1762
+ }
1763
+ function removeProvenance(lockfile, packageName) {
1764
+ const existing = lockfile.packages[packageName];
1765
+ if (!existing) {
1766
+ return { lockfile, contributions: null };
1767
+ }
1768
+ const { [packageName]: _removed, ...remaining } = lockfile.packages;
1769
+ return {
1770
+ lockfile: {
1771
+ ...lockfile,
1772
+ packages: remaining
1773
+ },
1774
+ contributions: existing.contributions ?? null
1775
+ };
1776
+ }
1777
+ function isNodeError(err) {
1778
+ return err instanceof Error && "code" in err;
1779
+ }
1780
+
1781
+ // src/constraints/sharing/remove.ts
1782
+ function removeContributions(config, contributions) {
1783
+ const result = { ...config };
1784
+ const layerNames = contributions.layers;
1785
+ if (layerNames && layerNames.length > 0 && Array.isArray(result.layers)) {
1786
+ const nameSet = new Set(layerNames);
1787
+ result.layers = result.layers.filter((l) => !nameSet.has(l.name));
1788
+ }
1789
+ const fromKeys = contributions.forbiddenImports;
1790
+ if (fromKeys && fromKeys.length > 0 && Array.isArray(result.forbiddenImports)) {
1791
+ const fromSet = new Set(fromKeys);
1792
+ result.forbiddenImports = result.forbiddenImports.filter(
1793
+ (r) => !fromSet.has(r.from)
1794
+ );
1795
+ }
1796
+ const boundarySchemas = contributions.boundaries;
1797
+ if (boundarySchemas && boundarySchemas.length > 0 && result.boundaries) {
1798
+ const boundaries = result.boundaries;
1799
+ if (boundaries.requireSchema) {
1800
+ const schemaSet = new Set(boundarySchemas);
1801
+ result.boundaries = {
1802
+ ...boundaries,
1803
+ requireSchema: boundaries.requireSchema.filter((s) => !schemaSet.has(s))
1804
+ };
1805
+ }
1806
+ }
1807
+ const thresholdKeys = contributions["architecture.thresholds"];
1808
+ if (thresholdKeys && thresholdKeys.length > 0 && result.architecture) {
1809
+ const arch = { ...result.architecture };
1810
+ const thresholds = { ...arch.thresholds };
1811
+ for (const key of thresholdKeys) {
1812
+ delete thresholds[key];
1813
+ }
1814
+ arch.thresholds = thresholds;
1815
+ result.architecture = arch;
1816
+ }
1817
+ const moduleKeys = contributions["architecture.modules"];
1818
+ if (moduleKeys && moduleKeys.length > 0 && result.architecture) {
1819
+ const arch = { ...result.architecture };
1820
+ const modules = { ...arch.modules };
1821
+ for (const key of moduleKeys) {
1822
+ const colonIdx = key.indexOf(":");
1823
+ if (colonIdx === -1) continue;
1824
+ const modulePath = key.substring(0, colonIdx);
1825
+ const category = key.substring(colonIdx + 1);
1826
+ if (modules[modulePath]) {
1827
+ const moduleCategories = { ...modules[modulePath] };
1828
+ delete moduleCategories[category];
1829
+ if (Object.keys(moduleCategories).length === 0) {
1830
+ delete modules[modulePath];
1831
+ } else {
1832
+ modules[modulePath] = moduleCategories;
1833
+ }
1834
+ }
1835
+ }
1836
+ arch.modules = modules;
1837
+ result.architecture = arch;
1838
+ }
1839
+ const ruleIds = contributions["security.rules"];
1840
+ if (ruleIds && ruleIds.length > 0 && result.security) {
1841
+ const security = { ...result.security };
1842
+ const rules = { ...security.rules };
1843
+ for (const id of ruleIds) {
1844
+ delete rules[id];
1845
+ }
1846
+ security.rules = rules;
1847
+ result.security = security;
1848
+ }
1849
+ return result;
1850
+ }
1851
+
1317
1852
  // src/shared/parsers/typescript.ts
1318
1853
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
1319
1854
 
@@ -1339,11 +1874,11 @@ function walk(node, visitor) {
1339
1874
  var TypeScriptParser = class {
1340
1875
  name = "typescript";
1341
1876
  extensions = [".ts", ".tsx", ".mts", ".cts"];
1342
- async parseFile(path11) {
1343
- const contentResult = await readFileContent(path11);
1877
+ async parseFile(path13) {
1878
+ const contentResult = await readFileContent(path13);
1344
1879
  if (!contentResult.ok) {
1345
1880
  return (0, import_types.Err)(
1346
- createParseError("NOT_FOUND", `File not found: ${path11}`, { path: path11 }, [
1881
+ createParseError("NOT_FOUND", `File not found: ${path13}`, { path: path13 }, [
1347
1882
  "Check that the file exists",
1348
1883
  "Verify the path is correct"
1349
1884
  ])
@@ -1353,7 +1888,7 @@ var TypeScriptParser = class {
1353
1888
  const ast = (0, import_typescript_estree.parse)(contentResult.value, {
1354
1889
  loc: true,
1355
1890
  range: true,
1356
- jsx: path11.endsWith(".tsx"),
1891
+ jsx: path13.endsWith(".tsx"),
1357
1892
  errorOnUnknownASTType: false
1358
1893
  });
1359
1894
  return (0, import_types.Ok)({
@@ -1364,7 +1899,7 @@ var TypeScriptParser = class {
1364
1899
  } catch (e) {
1365
1900
  const error = e;
1366
1901
  return (0, import_types.Err)(
1367
- createParseError("SYNTAX_ERROR", `Failed to parse ${path11}: ${error.message}`, { path: path11 }, [
1902
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path13}: ${error.message}`, { path: path13 }, [
1368
1903
  "Check for syntax errors in the file",
1369
1904
  "Ensure valid TypeScript syntax"
1370
1905
  ])
@@ -1531,7 +2066,7 @@ var TypeScriptParser = class {
1531
2066
 
1532
2067
  // src/entropy/snapshot.ts
1533
2068
  var import_path6 = require("path");
1534
- var import_minimatch2 = require("minimatch");
2069
+ var import_minimatch3 = require("minimatch");
1535
2070
  async function resolveEntryPoints(rootDir, explicitEntries) {
1536
2071
  if (explicitEntries && explicitEntries.length > 0) {
1537
2072
  const resolved = explicitEntries.map((e) => (0, import_path6.resolve)(rootDir, e));
@@ -1648,22 +2183,22 @@ function extractInlineRefs(content) {
1648
2183
  }
1649
2184
  return refs;
1650
2185
  }
1651
- async function parseDocumentationFile(path11) {
1652
- const contentResult = await readFileContent(path11);
2186
+ async function parseDocumentationFile(path13) {
2187
+ const contentResult = await readFileContent(path13);
1653
2188
  if (!contentResult.ok) {
1654
2189
  return (0, import_types.Err)(
1655
2190
  createEntropyError(
1656
2191
  "PARSE_ERROR",
1657
- `Failed to read documentation file: ${path11}`,
1658
- { file: path11 },
2192
+ `Failed to read documentation file: ${path13}`,
2193
+ { file: path13 },
1659
2194
  ["Check that the file exists"]
1660
2195
  )
1661
2196
  );
1662
2197
  }
1663
2198
  const content = contentResult.value;
1664
- const type = path11.endsWith(".md") ? "markdown" : "text";
2199
+ const type = path13.endsWith(".md") ? "markdown" : "text";
1665
2200
  return (0, import_types.Ok)({
1666
- path: path11,
2201
+ path: path13,
1667
2202
  type,
1668
2203
  content,
1669
2204
  codeBlocks: extractCodeBlocks(content),
@@ -1795,7 +2330,7 @@ async function buildSnapshot(config) {
1795
2330
  }
1796
2331
  sourceFilePaths = sourceFilePaths.filter((f) => {
1797
2332
  const rel = (0, import_path6.relative)(rootDir, f);
1798
- return !excludePatterns.some((p) => (0, import_minimatch2.minimatch)(rel, p));
2333
+ return !excludePatterns.some((p) => (0, import_minimatch3.minimatch)(rel, p));
1799
2334
  });
1800
2335
  const files = [];
1801
2336
  for (const filePath of sourceFilePaths) {
@@ -2325,11 +2860,11 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
2325
2860
  }
2326
2861
 
2327
2862
  // src/entropy/detectors/patterns.ts
2328
- var import_minimatch3 = require("minimatch");
2863
+ var import_minimatch4 = require("minimatch");
2329
2864
  var import_path9 = require("path");
2330
2865
  function fileMatchesPattern(filePath, pattern, rootDir) {
2331
2866
  const relativePath = (0, import_path9.relative)(rootDir, filePath);
2332
- return (0, import_minimatch3.minimatch)(relativePath, pattern);
2867
+ return (0, import_minimatch4.minimatch)(relativePath, pattern);
2333
2868
  }
2334
2869
  function checkConfigPattern(pattern, file, rootDir) {
2335
2870
  const matches = [];
@@ -3331,14 +3866,14 @@ var EntropyAnalyzer = class {
3331
3866
  };
3332
3867
 
3333
3868
  // src/entropy/fixers/safe-fixes.ts
3334
- var fs = __toESM(require("fs"));
3869
+ var fs3 = __toESM(require("fs"));
3335
3870
  var import_util2 = require("util");
3336
3871
  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);
3872
+ var readFile5 = (0, import_util2.promisify)(fs3.readFile);
3873
+ var writeFile3 = (0, import_util2.promisify)(fs3.writeFile);
3874
+ var unlink2 = (0, import_util2.promisify)(fs3.unlink);
3875
+ var mkdir2 = (0, import_util2.promisify)(fs3.mkdir);
3876
+ var copyFile2 = (0, import_util2.promisify)(fs3.copyFile);
3342
3877
  var DEFAULT_FIX_CONFIG = {
3343
3878
  dryRun: false,
3344
3879
  fixTypes: ["unused-imports", "dead-files"],
@@ -3465,25 +4000,25 @@ async function applySingleFix(fix, config) {
3465
4000
  break;
3466
4001
  case "delete-lines":
3467
4002
  if (fix.line !== void 0) {
3468
- const content = await readFile4(fix.file, "utf-8");
4003
+ const content = await readFile5(fix.file, "utf-8");
3469
4004
  const lines = content.split("\n");
3470
4005
  lines.splice(fix.line - 1, 1);
3471
- await writeFile2(fix.file, lines.join("\n"));
4006
+ await writeFile3(fix.file, lines.join("\n"));
3472
4007
  }
3473
4008
  break;
3474
4009
  case "replace":
3475
4010
  if (fix.oldContent && fix.newContent !== void 0) {
3476
- const content = await readFile4(fix.file, "utf-8");
4011
+ const content = await readFile5(fix.file, "utf-8");
3477
4012
  const newContent = content.replace(fix.oldContent, fix.newContent);
3478
- await writeFile2(fix.file, newContent);
4013
+ await writeFile3(fix.file, newContent);
3479
4014
  }
3480
4015
  break;
3481
4016
  case "insert":
3482
4017
  if (fix.line !== void 0 && fix.newContent) {
3483
- const content = await readFile4(fix.file, "utf-8");
4018
+ const content = await readFile5(fix.file, "utf-8");
3484
4019
  const lines = content.split("\n");
3485
4020
  lines.splice(fix.line - 1, 0, fix.newContent);
3486
- await writeFile2(fix.file, lines.join("\n"));
4021
+ await writeFile3(fix.file, lines.join("\n"));
3487
4022
  }
3488
4023
  break;
3489
4024
  }
@@ -3660,46 +4195,46 @@ function deduplicateCleanupFindings(findings) {
3660
4195
  }
3661
4196
 
3662
4197
  // 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())
4198
+ var import_zod2 = require("zod");
4199
+ var MustExportRuleSchema = import_zod2.z.object({
4200
+ type: import_zod2.z.literal("must-export"),
4201
+ names: import_zod2.z.array(import_zod2.z.string())
3667
4202
  });
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()
4203
+ var MustExportDefaultRuleSchema = import_zod2.z.object({
4204
+ type: import_zod2.z.literal("must-export-default"),
4205
+ kind: import_zod2.z.enum(["class", "function", "object"]).optional()
3671
4206
  });
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())
4207
+ var NoExportRuleSchema = import_zod2.z.object({
4208
+ type: import_zod2.z.literal("no-export"),
4209
+ names: import_zod2.z.array(import_zod2.z.string())
3675
4210
  });
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()
4211
+ var MustImportRuleSchema = import_zod2.z.object({
4212
+ type: import_zod2.z.literal("must-import"),
4213
+ from: import_zod2.z.string(),
4214
+ names: import_zod2.z.array(import_zod2.z.string()).optional()
3680
4215
  });
3681
- var NoImportRuleSchema = import_zod.z.object({
3682
- type: import_zod.z.literal("no-import"),
3683
- from: import_zod.z.string()
4216
+ var NoImportRuleSchema = import_zod2.z.object({
4217
+ type: import_zod2.z.literal("no-import"),
4218
+ from: import_zod2.z.string()
3684
4219
  });
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"])
4220
+ var NamingRuleSchema = import_zod2.z.object({
4221
+ type: import_zod2.z.literal("naming"),
4222
+ match: import_zod2.z.string(),
4223
+ convention: import_zod2.z.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
3689
4224
  });
3690
- var MaxExportsRuleSchema = import_zod.z.object({
3691
- type: import_zod.z.literal("max-exports"),
3692
- count: import_zod.z.number().positive()
4225
+ var MaxExportsRuleSchema = import_zod2.z.object({
4226
+ type: import_zod2.z.literal("max-exports"),
4227
+ count: import_zod2.z.number().positive()
3693
4228
  });
3694
- var MaxLinesRuleSchema = import_zod.z.object({
3695
- type: import_zod.z.literal("max-lines"),
3696
- count: import_zod.z.number().positive()
4229
+ var MaxLinesRuleSchema = import_zod2.z.object({
4230
+ type: import_zod2.z.literal("max-lines"),
4231
+ count: import_zod2.z.number().positive()
3697
4232
  });
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"]))
4233
+ var RequireJSDocRuleSchema = import_zod2.z.object({
4234
+ type: import_zod2.z.literal("require-jsdoc"),
4235
+ for: import_zod2.z.array(import_zod2.z.enum(["function", "class", "export"]))
3701
4236
  });
3702
- var RuleSchema = import_zod.z.discriminatedUnion("type", [
4237
+ var RuleSchema = import_zod2.z.discriminatedUnion("type", [
3703
4238
  MustExportRuleSchema,
3704
4239
  MustExportDefaultRuleSchema,
3705
4240
  NoExportRuleSchema,
@@ -3710,47 +4245,47 @@ var RuleSchema = import_zod.z.discriminatedUnion("type", [
3710
4245
  MaxLinesRuleSchema,
3711
4246
  RequireJSDocRuleSchema
3712
4247
  ]);
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()),
4248
+ var ConfigPatternSchema = import_zod2.z.object({
4249
+ name: import_zod2.z.string().min(1),
4250
+ description: import_zod2.z.string(),
4251
+ severity: import_zod2.z.enum(["error", "warning"]),
4252
+ files: import_zod2.z.array(import_zod2.z.string()),
3718
4253
  rule: RuleSchema,
3719
- message: import_zod.z.string().optional()
4254
+ message: import_zod2.z.string().optional()
3720
4255
  });
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(),
4256
+ var PatternConfigSchema = import_zod2.z.object({
4257
+ patterns: import_zod2.z.array(ConfigPatternSchema),
4258
+ customPatterns: import_zod2.z.array(import_zod2.z.any()).optional(),
3724
4259
  // Code patterns are functions, can't validate
3725
- ignoreFiles: import_zod.z.array(import_zod.z.string()).optional()
4260
+ ignoreFiles: import_zod2.z.array(import_zod2.z.string()).optional()
3726
4261
  });
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()
4262
+ var DriftConfigSchema = import_zod2.z.object({
4263
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional(),
4264
+ checkApiSignatures: import_zod2.z.boolean().optional(),
4265
+ checkExamples: import_zod2.z.boolean().optional(),
4266
+ checkStructure: import_zod2.z.boolean().optional(),
4267
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional()
3733
4268
  });
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()
4269
+ var DeadCodeConfigSchema = import_zod2.z.object({
4270
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4271
+ includeTypes: import_zod2.z.boolean().optional(),
4272
+ includeInternals: import_zod2.z.boolean().optional(),
4273
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional(),
4274
+ treatDynamicImportsAs: import_zod2.z.enum(["used", "unknown"]).optional()
3740
4275
  });
3741
- var EntropyConfigSchema = import_zod.z.object({
3742
- rootDir: import_zod.z.string(),
3743
- parser: import_zod.z.any().optional(),
4276
+ var EntropyConfigSchema = import_zod2.z.object({
4277
+ rootDir: import_zod2.z.string(),
4278
+ parser: import_zod2.z.any().optional(),
3744
4279
  // 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()
4280
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4281
+ analyze: import_zod2.z.object({
4282
+ drift: import_zod2.z.union([import_zod2.z.boolean(), DriftConfigSchema]).optional(),
4283
+ deadCode: import_zod2.z.union([import_zod2.z.boolean(), DeadCodeConfigSchema]).optional(),
4284
+ patterns: import_zod2.z.union([import_zod2.z.boolean(), PatternConfigSchema]).optional()
3750
4285
  }),
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()
4286
+ include: import_zod2.z.array(import_zod2.z.string()).optional(),
4287
+ exclude: import_zod2.z.array(import_zod2.z.string()).optional(),
4288
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional()
3754
4289
  });
3755
4290
  function validatePatternConfig(config) {
3756
4291
  const result = PatternConfigSchema.safeParse(config);
@@ -4007,7 +4542,7 @@ var RegressionDetector = class {
4007
4542
  };
4008
4543
 
4009
4544
  // src/performance/critical-path.ts
4010
- var fs2 = __toESM(require("fs"));
4545
+ var fs4 = __toESM(require("fs"));
4011
4546
  var path = __toESM(require("path"));
4012
4547
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git"]);
4013
4548
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
@@ -4059,7 +4594,7 @@ var CriticalPathResolver = class {
4059
4594
  walkDir(dir, entries) {
4060
4595
  let items;
4061
4596
  try {
4062
- items = fs2.readdirSync(dir, { withFileTypes: true });
4597
+ items = fs4.readdirSync(dir, { withFileTypes: true });
4063
4598
  } catch {
4064
4599
  return;
4065
4600
  }
@@ -4075,7 +4610,7 @@ var CriticalPathResolver = class {
4075
4610
  scanFile(filePath, entries) {
4076
4611
  let content;
4077
4612
  try {
4078
- content = fs2.readFileSync(filePath, "utf-8");
4613
+ content = fs4.readFileSync(filePath, "utf-8");
4079
4614
  } catch {
4080
4615
  return;
4081
4616
  }
@@ -4254,17 +4789,17 @@ function resetFeedbackConfig() {
4254
4789
  }
4255
4790
 
4256
4791
  // src/feedback/review/diff-analyzer.ts
4257
- function parseDiff(diff) {
4792
+ function parseDiff(diff2) {
4258
4793
  try {
4259
- if (!diff.trim()) {
4260
- return (0, import_types.Ok)({ diff, files: [] });
4794
+ if (!diff2.trim()) {
4795
+ return (0, import_types.Ok)({ diff: diff2, files: [] });
4261
4796
  }
4262
4797
  const files = [];
4263
4798
  const newFileRegex = /new file mode/;
4264
4799
  const deletedFileRegex = /deleted file mode/;
4265
4800
  const additionRegex = /^\+(?!\+\+)/gm;
4266
4801
  const deletionRegex = /^-(?!--)/gm;
4267
- const diffParts = diff.split(/(?=diff --git)/);
4802
+ const diffParts = diff2.split(/(?=diff --git)/);
4268
4803
  for (const part of diffParts) {
4269
4804
  if (!part.trim()) continue;
4270
4805
  const headerMatch = /diff --git a\/(.+?) b\/(.+?)(?:\n|$)/.exec(part);
@@ -4287,7 +4822,7 @@ function parseDiff(diff) {
4287
4822
  deletions
4288
4823
  });
4289
4824
  }
4290
- return (0, import_types.Ok)({ diff, files });
4825
+ return (0, import_types.Ok)({ diff: diff2, files });
4291
4826
  } catch (error) {
4292
4827
  return (0, import_types.Err)({
4293
4828
  code: "DIFF_PARSE_ERROR",
@@ -4891,77 +5426,1265 @@ var NoOpSink = class {
4891
5426
  }
4892
5427
  };
4893
5428
 
5429
+ // src/architecture/types.ts
5430
+ var import_zod3 = require("zod");
5431
+ var ArchMetricCategorySchema = import_zod3.z.enum([
5432
+ "circular-deps",
5433
+ "layer-violations",
5434
+ "complexity",
5435
+ "coupling",
5436
+ "forbidden-imports",
5437
+ "module-size",
5438
+ "dependency-depth"
5439
+ ]);
5440
+ var ViolationSchema = import_zod3.z.object({
5441
+ id: import_zod3.z.string(),
5442
+ // stable hash: sha256(relativePath + ':' + category + ':' + normalizedDetail)
5443
+ file: import_zod3.z.string(),
5444
+ // relative to project root
5445
+ category: ArchMetricCategorySchema.optional(),
5446
+ // context for baseline reporting
5447
+ detail: import_zod3.z.string(),
5448
+ // human-readable description
5449
+ severity: import_zod3.z.enum(["error", "warning"])
5450
+ });
5451
+ var MetricResultSchema = import_zod3.z.object({
5452
+ category: ArchMetricCategorySchema,
5453
+ scope: import_zod3.z.string(),
5454
+ // e.g., 'project', 'src/services', 'src/api/routes.ts'
5455
+ value: import_zod3.z.number(),
5456
+ // numeric metric (violation count, complexity score, etc.)
5457
+ violations: import_zod3.z.array(ViolationSchema),
5458
+ metadata: import_zod3.z.record(import_zod3.z.unknown()).optional()
5459
+ });
5460
+ var CategoryBaselineSchema = import_zod3.z.object({
5461
+ value: import_zod3.z.number(),
5462
+ // aggregate metric value at baseline time
5463
+ violationIds: import_zod3.z.array(import_zod3.z.string())
5464
+ // stable IDs of known violations (the allowlist)
5465
+ });
5466
+ var ArchBaselineSchema = import_zod3.z.object({
5467
+ version: import_zod3.z.literal(1),
5468
+ updatedAt: import_zod3.z.string().datetime(),
5469
+ // ISO 8601
5470
+ updatedFrom: import_zod3.z.string(),
5471
+ // commit hash
5472
+ metrics: import_zod3.z.record(ArchMetricCategorySchema, CategoryBaselineSchema)
5473
+ });
5474
+ var CategoryRegressionSchema = import_zod3.z.object({
5475
+ category: ArchMetricCategorySchema,
5476
+ baselineValue: import_zod3.z.number(),
5477
+ currentValue: import_zod3.z.number(),
5478
+ delta: import_zod3.z.number()
5479
+ });
5480
+ var ArchDiffResultSchema = import_zod3.z.object({
5481
+ passed: import_zod3.z.boolean(),
5482
+ newViolations: import_zod3.z.array(ViolationSchema),
5483
+ // in current but not in baseline -> FAIL
5484
+ resolvedViolations: import_zod3.z.array(import_zod3.z.string()),
5485
+ // in baseline but not in current -> celebrate
5486
+ preExisting: import_zod3.z.array(import_zod3.z.string()),
5487
+ // in both -> allowed, tracked
5488
+ regressions: import_zod3.z.array(CategoryRegressionSchema)
5489
+ // aggregate value exceeded baseline
5490
+ });
5491
+ var ThresholdConfigSchema = import_zod3.z.record(
5492
+ ArchMetricCategorySchema,
5493
+ import_zod3.z.union([import_zod3.z.number(), import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number())])
5494
+ );
5495
+ var ArchConfigSchema = import_zod3.z.object({
5496
+ enabled: import_zod3.z.boolean().default(true),
5497
+ baselinePath: import_zod3.z.string().default(".harness/arch/baselines.json"),
5498
+ thresholds: ThresholdConfigSchema.default({}),
5499
+ modules: import_zod3.z.record(import_zod3.z.string(), ThresholdConfigSchema).default({})
5500
+ });
5501
+ var ConstraintRuleSchema = import_zod3.z.object({
5502
+ id: import_zod3.z.string(),
5503
+ // stable hash: sha256(category + ':' + scope + ':' + description)
5504
+ category: ArchMetricCategorySchema,
5505
+ description: import_zod3.z.string(),
5506
+ // e.g., "Layer 'services' must not import from 'ui'"
5507
+ scope: import_zod3.z.string(),
5508
+ // e.g., 'src/services/', 'project'
5509
+ targets: import_zod3.z.array(import_zod3.z.string()).optional()
5510
+ // forward-compat for governs edges
5511
+ });
5512
+
5513
+ // src/architecture/collectors/circular-deps.ts
5514
+ var import_node_path3 = require("path");
5515
+
5516
+ // src/architecture/collectors/hash.ts
5517
+ var import_node_crypto = require("crypto");
5518
+ function violationId(relativePath, category, normalizedDetail) {
5519
+ const path13 = relativePath.replace(/\\/g, "/");
5520
+ const input = `${path13}:${category}:${normalizedDetail}`;
5521
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5522
+ }
5523
+ function constraintRuleId(category, scope, description) {
5524
+ const input = `${category}:${scope}:${description}`;
5525
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5526
+ }
5527
+
5528
+ // src/architecture/collectors/circular-deps.ts
5529
+ var CircularDepsCollector = class {
5530
+ category = "circular-deps";
5531
+ getRules(_config, _rootDir) {
5532
+ const description = "No circular dependencies allowed";
5533
+ return [
5534
+ {
5535
+ id: constraintRuleId(this.category, "project", description),
5536
+ category: this.category,
5537
+ description,
5538
+ scope: "project"
5539
+ }
5540
+ ];
5541
+ }
5542
+ async collect(_config, rootDir) {
5543
+ const files = await findFiles("**/*.ts", rootDir);
5544
+ const stubParser = {
5545
+ name: "typescript",
5546
+ extensions: [".ts", ".tsx"],
5547
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "not needed" } }),
5548
+ extractImports: () => ({
5549
+ ok: false,
5550
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5551
+ }),
5552
+ extractExports: () => ({
5553
+ ok: false,
5554
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5555
+ }),
5556
+ health: async () => ({ ok: true, value: { available: true } })
5557
+ };
5558
+ const graphResult = await buildDependencyGraph(files, stubParser);
5559
+ if (!graphResult.ok) {
5560
+ return [
5561
+ {
5562
+ category: this.category,
5563
+ scope: "project",
5564
+ value: 0,
5565
+ violations: [],
5566
+ metadata: { error: "Failed to build dependency graph" }
5567
+ }
5568
+ ];
5569
+ }
5570
+ const result = detectCircularDeps(graphResult.value);
5571
+ if (!result.ok) {
5572
+ return [
5573
+ {
5574
+ category: this.category,
5575
+ scope: "project",
5576
+ value: 0,
5577
+ violations: [],
5578
+ metadata: { error: "Failed to detect circular deps" }
5579
+ }
5580
+ ];
5581
+ }
5582
+ const { cycles, largestCycle } = result.value;
5583
+ const violations = cycles.map((cycle) => {
5584
+ const cyclePath = cycle.cycle.map((f) => (0, import_node_path3.relative)(rootDir, f)).join(" -> ");
5585
+ const firstFile = (0, import_node_path3.relative)(rootDir, cycle.cycle[0]);
5586
+ return {
5587
+ id: violationId(firstFile, this.category, cyclePath),
5588
+ file: firstFile,
5589
+ detail: `Circular dependency: ${cyclePath}`,
5590
+ severity: cycle.severity
5591
+ };
5592
+ });
5593
+ return [
5594
+ {
5595
+ category: this.category,
5596
+ scope: "project",
5597
+ value: cycles.length,
5598
+ violations,
5599
+ metadata: { largestCycle, cycleCount: cycles.length }
5600
+ }
5601
+ ];
5602
+ }
5603
+ };
5604
+
5605
+ // src/architecture/collectors/layer-violations.ts
5606
+ var import_node_path4 = require("path");
5607
+ var LayerViolationCollector = class {
5608
+ category = "layer-violations";
5609
+ getRules(_config, _rootDir) {
5610
+ const description = "No layer boundary violations allowed";
5611
+ return [
5612
+ {
5613
+ id: constraintRuleId(this.category, "project", description),
5614
+ category: this.category,
5615
+ description,
5616
+ scope: "project"
5617
+ }
5618
+ ];
5619
+ }
5620
+ async collect(_config, rootDir) {
5621
+ const stubParser = {
5622
+ name: "typescript",
5623
+ extensions: [".ts", ".tsx"],
5624
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5625
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5626
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5627
+ health: async () => ({ ok: true, value: { available: true } })
5628
+ };
5629
+ const result = await validateDependencies({
5630
+ layers: [],
5631
+ rootDir,
5632
+ parser: stubParser,
5633
+ fallbackBehavior: "skip"
5634
+ });
5635
+ if (!result.ok) {
5636
+ return [
5637
+ {
5638
+ category: this.category,
5639
+ scope: "project",
5640
+ value: 0,
5641
+ violations: [],
5642
+ metadata: { error: "Failed to validate dependencies" }
5643
+ }
5644
+ ];
5645
+ }
5646
+ const layerViolations = result.value.violations.filter(
5647
+ (v) => v.reason === "WRONG_LAYER"
5648
+ );
5649
+ const violations = layerViolations.map((v) => {
5650
+ const relFile = (0, import_node_path4.relative)(rootDir, v.file);
5651
+ const relImport = (0, import_node_path4.relative)(rootDir, v.imports);
5652
+ const detail = `${v.fromLayer} -> ${v.toLayer}: ${relFile} imports ${relImport}`;
5653
+ return {
5654
+ id: violationId(relFile, this.category, detail),
5655
+ file: relFile,
5656
+ category: this.category,
5657
+ detail,
5658
+ severity: "error"
5659
+ };
5660
+ });
5661
+ return [
5662
+ {
5663
+ category: this.category,
5664
+ scope: "project",
5665
+ value: violations.length,
5666
+ violations
5667
+ }
5668
+ ];
5669
+ }
5670
+ };
5671
+
5672
+ // src/architecture/collectors/complexity.ts
5673
+ var import_node_path5 = require("path");
5674
+ var ComplexityCollector = class {
5675
+ category = "complexity";
5676
+ getRules(_config, _rootDir) {
5677
+ const description = "Cyclomatic complexity must stay within thresholds";
5678
+ return [
5679
+ {
5680
+ id: constraintRuleId(this.category, "project", description),
5681
+ category: this.category,
5682
+ description,
5683
+ scope: "project"
5684
+ }
5685
+ ];
5686
+ }
5687
+ async collect(_config, rootDir) {
5688
+ const files = await findFiles("**/*.ts", rootDir);
5689
+ const snapshot = {
5690
+ files: files.map((f) => ({
5691
+ path: f,
5692
+ ast: { type: "Program", body: null, language: "typescript" },
5693
+ imports: [],
5694
+ exports: [],
5695
+ internalSymbols: [],
5696
+ jsDocComments: []
5697
+ })),
5698
+ dependencyGraph: { nodes: [], edges: [] },
5699
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5700
+ docs: [],
5701
+ codeReferences: [],
5702
+ entryPoints: [],
5703
+ rootDir,
5704
+ config: { rootDir, analyze: {} },
5705
+ buildTime: 0
5706
+ };
5707
+ const complexityThreshold = _config.thresholds.complexity;
5708
+ const maxComplexity = typeof complexityThreshold === "number" ? complexityThreshold : complexityThreshold?.max ?? 15;
5709
+ const complexityConfig = {
5710
+ thresholds: {
5711
+ cyclomaticComplexity: {
5712
+ error: maxComplexity,
5713
+ warn: Math.floor(maxComplexity * 0.7)
5714
+ }
5715
+ }
5716
+ };
5717
+ const result = await detectComplexityViolations(snapshot, complexityConfig);
5718
+ if (!result.ok) {
5719
+ return [
5720
+ {
5721
+ category: this.category,
5722
+ scope: "project",
5723
+ value: 0,
5724
+ violations: [],
5725
+ metadata: { error: "Failed to detect complexity violations" }
5726
+ }
5727
+ ];
5728
+ }
5729
+ const { violations: complexityViolations, stats } = result.value;
5730
+ const filtered = complexityViolations.filter(
5731
+ (v) => v.severity === "error" || v.severity === "warning"
5732
+ );
5733
+ const violations = filtered.map((v) => {
5734
+ const relFile = (0, import_node_path5.relative)(rootDir, v.file);
5735
+ const idDetail = `${v.metric}:${v.function}`;
5736
+ return {
5737
+ id: violationId(relFile, this.category, idDetail),
5738
+ file: relFile,
5739
+ category: this.category,
5740
+ detail: `${v.metric}=${v.value} in ${v.function} (threshold: ${v.threshold})`,
5741
+ severity: v.severity
5742
+ };
5743
+ });
5744
+ return [
5745
+ {
5746
+ category: this.category,
5747
+ scope: "project",
5748
+ value: violations.length,
5749
+ violations,
5750
+ metadata: {
5751
+ filesAnalyzed: stats.filesAnalyzed,
5752
+ functionsAnalyzed: stats.functionsAnalyzed
5753
+ }
5754
+ }
5755
+ ];
5756
+ }
5757
+ };
5758
+
5759
+ // src/architecture/collectors/coupling.ts
5760
+ var import_node_path6 = require("path");
5761
+ var CouplingCollector = class {
5762
+ category = "coupling";
5763
+ getRules(_config, _rootDir) {
5764
+ const description = "Coupling metrics must stay within thresholds";
5765
+ return [
5766
+ {
5767
+ id: constraintRuleId(this.category, "project", description),
5768
+ category: this.category,
5769
+ description,
5770
+ scope: "project"
5771
+ }
5772
+ ];
5773
+ }
5774
+ async collect(_config, rootDir) {
5775
+ const files = await findFiles("**/*.ts", rootDir);
5776
+ const snapshot = {
5777
+ files: files.map((f) => ({
5778
+ path: f,
5779
+ ast: { type: "Program", body: null, language: "typescript" },
5780
+ imports: [],
5781
+ exports: [],
5782
+ internalSymbols: [],
5783
+ jsDocComments: []
5784
+ })),
5785
+ dependencyGraph: { nodes: [], edges: [] },
5786
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5787
+ docs: [],
5788
+ codeReferences: [],
5789
+ entryPoints: [],
5790
+ rootDir,
5791
+ config: { rootDir, analyze: {} },
5792
+ buildTime: 0
5793
+ };
5794
+ const result = await detectCouplingViolations(snapshot);
5795
+ if (!result.ok) {
5796
+ return [
5797
+ {
5798
+ category: this.category,
5799
+ scope: "project",
5800
+ value: 0,
5801
+ violations: [],
5802
+ metadata: { error: "Failed to detect coupling violations" }
5803
+ }
5804
+ ];
5805
+ }
5806
+ const { violations: couplingViolations, stats } = result.value;
5807
+ const filtered = couplingViolations.filter(
5808
+ (v) => v.severity === "error" || v.severity === "warning"
5809
+ );
5810
+ const violations = filtered.map((v) => {
5811
+ const relFile = (0, import_node_path6.relative)(rootDir, v.file);
5812
+ const idDetail = `${v.metric}`;
5813
+ return {
5814
+ id: violationId(relFile, this.category, idDetail),
5815
+ file: relFile,
5816
+ category: this.category,
5817
+ detail: `${v.metric}=${v.value} (threshold: ${v.threshold})`,
5818
+ severity: v.severity
5819
+ };
5820
+ });
5821
+ return [
5822
+ {
5823
+ category: this.category,
5824
+ scope: "project",
5825
+ value: violations.length,
5826
+ violations,
5827
+ metadata: { filesAnalyzed: stats.filesAnalyzed }
5828
+ }
5829
+ ];
5830
+ }
5831
+ };
5832
+
5833
+ // src/architecture/collectors/forbidden-imports.ts
5834
+ var import_node_path7 = require("path");
5835
+ var ForbiddenImportCollector = class {
5836
+ category = "forbidden-imports";
5837
+ getRules(_config, _rootDir) {
5838
+ const description = "No forbidden imports allowed";
5839
+ return [
5840
+ {
5841
+ id: constraintRuleId(this.category, "project", description),
5842
+ category: this.category,
5843
+ description,
5844
+ scope: "project"
5845
+ }
5846
+ ];
5847
+ }
5848
+ async collect(_config, rootDir) {
5849
+ const stubParser = {
5850
+ name: "typescript",
5851
+ extensions: [".ts", ".tsx"],
5852
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5853
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5854
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5855
+ health: async () => ({ ok: true, value: { available: true } })
5856
+ };
5857
+ const result = await validateDependencies({
5858
+ layers: [],
5859
+ rootDir,
5860
+ parser: stubParser,
5861
+ fallbackBehavior: "skip"
5862
+ });
5863
+ if (!result.ok) {
5864
+ return [
5865
+ {
5866
+ category: this.category,
5867
+ scope: "project",
5868
+ value: 0,
5869
+ violations: [],
5870
+ metadata: { error: "Failed to validate dependencies" }
5871
+ }
5872
+ ];
5873
+ }
5874
+ const forbidden = result.value.violations.filter(
5875
+ (v) => v.reason === "FORBIDDEN_IMPORT"
5876
+ );
5877
+ const violations = forbidden.map((v) => {
5878
+ const relFile = (0, import_node_path7.relative)(rootDir, v.file);
5879
+ const relImport = (0, import_node_path7.relative)(rootDir, v.imports);
5880
+ const detail = `forbidden import: ${relFile} -> ${relImport}`;
5881
+ return {
5882
+ id: violationId(relFile, this.category, detail),
5883
+ file: relFile,
5884
+ category: this.category,
5885
+ detail,
5886
+ severity: "error"
5887
+ };
5888
+ });
5889
+ return [
5890
+ {
5891
+ category: this.category,
5892
+ scope: "project",
5893
+ value: violations.length,
5894
+ violations
5895
+ }
5896
+ ];
5897
+ }
5898
+ };
5899
+
5900
+ // src/architecture/collectors/module-size.ts
5901
+ var import_promises2 = require("fs/promises");
5902
+ var import_node_path8 = require("path");
5903
+ async function discoverModules(rootDir) {
5904
+ const modules = [];
5905
+ async function scanDir(dir) {
5906
+ let entries;
5907
+ try {
5908
+ entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
5909
+ } catch {
5910
+ return;
5911
+ }
5912
+ const tsFiles = [];
5913
+ const subdirs = [];
5914
+ for (const entry of entries) {
5915
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") {
5916
+ continue;
5917
+ }
5918
+ const fullPath = (0, import_node_path8.join)(dir, entry.name);
5919
+ if (entry.isDirectory()) {
5920
+ subdirs.push(fullPath);
5921
+ } 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")) {
5922
+ tsFiles.push(fullPath);
5923
+ }
5924
+ }
5925
+ if (tsFiles.length > 0) {
5926
+ let totalLoc = 0;
5927
+ for (const f of tsFiles) {
5928
+ try {
5929
+ const content = await (0, import_promises2.readFile)(f, "utf-8");
5930
+ totalLoc += content.split("\n").filter((line) => line.trim().length > 0).length;
5931
+ } catch {
5932
+ }
5933
+ }
5934
+ modules.push({
5935
+ modulePath: (0, import_node_path8.relative)(rootDir, dir),
5936
+ fileCount: tsFiles.length,
5937
+ totalLoc,
5938
+ files: tsFiles.map((f) => (0, import_node_path8.relative)(rootDir, f))
5939
+ });
5940
+ }
5941
+ for (const sub of subdirs) {
5942
+ await scanDir(sub);
5943
+ }
5944
+ }
5945
+ await scanDir(rootDir);
5946
+ return modules;
5947
+ }
5948
+ var ModuleSizeCollector = class {
5949
+ category = "module-size";
5950
+ getRules(config, _rootDir) {
5951
+ const thresholds = config.thresholds["module-size"];
5952
+ let maxLoc = Infinity;
5953
+ let maxFiles = Infinity;
5954
+ if (typeof thresholds === "object" && thresholds !== null) {
5955
+ const t = thresholds;
5956
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5957
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5958
+ }
5959
+ const rules = [];
5960
+ if (maxLoc < Infinity) {
5961
+ const desc = `Module LOC must not exceed ${maxLoc}`;
5962
+ rules.push({
5963
+ id: constraintRuleId(this.category, "project", desc),
5964
+ category: this.category,
5965
+ description: desc,
5966
+ scope: "project"
5967
+ });
5968
+ }
5969
+ if (maxFiles < Infinity) {
5970
+ const desc = `Module file count must not exceed ${maxFiles}`;
5971
+ rules.push({
5972
+ id: constraintRuleId(this.category, "project", desc),
5973
+ category: this.category,
5974
+ description: desc,
5975
+ scope: "project"
5976
+ });
5977
+ }
5978
+ if (rules.length === 0) {
5979
+ const desc = "Module size must stay within thresholds";
5980
+ rules.push({
5981
+ id: constraintRuleId(this.category, "project", desc),
5982
+ category: this.category,
5983
+ description: desc,
5984
+ scope: "project"
5985
+ });
5986
+ }
5987
+ return rules;
5988
+ }
5989
+ async collect(config, rootDir) {
5990
+ const modules = await discoverModules(rootDir);
5991
+ const thresholds = config.thresholds["module-size"];
5992
+ let maxLoc = Infinity;
5993
+ let maxFiles = Infinity;
5994
+ if (typeof thresholds === "object" && thresholds !== null) {
5995
+ const t = thresholds;
5996
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5997
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5998
+ }
5999
+ return modules.map((mod) => {
6000
+ const violations = [];
6001
+ if (mod.totalLoc > maxLoc) {
6002
+ violations.push({
6003
+ id: violationId(mod.modulePath, this.category, "totalLoc-exceeded"),
6004
+ file: mod.modulePath,
6005
+ detail: `Module has ${mod.totalLoc} lines of code (threshold: ${maxLoc})`,
6006
+ severity: "warning"
6007
+ });
6008
+ }
6009
+ if (mod.fileCount > maxFiles) {
6010
+ violations.push({
6011
+ id: violationId(mod.modulePath, this.category, "fileCount-exceeded"),
6012
+ file: mod.modulePath,
6013
+ detail: `Module has ${mod.fileCount} files (threshold: ${maxFiles})`,
6014
+ severity: "warning"
6015
+ });
6016
+ }
6017
+ return {
6018
+ category: this.category,
6019
+ scope: mod.modulePath,
6020
+ value: mod.totalLoc,
6021
+ violations,
6022
+ metadata: { fileCount: mod.fileCount, totalLoc: mod.totalLoc }
6023
+ };
6024
+ });
6025
+ }
6026
+ };
6027
+
6028
+ // src/architecture/collectors/dep-depth.ts
6029
+ var import_promises3 = require("fs/promises");
6030
+ var import_node_path9 = require("path");
6031
+ function extractImportSources(content, filePath) {
6032
+ const importRegex = /(?:import|export)\s+.*?from\s+['"](\.[^'"]+)['"]/g;
6033
+ const dynamicRegex = /import\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g;
6034
+ const sources = [];
6035
+ const dir = (0, import_node_path9.dirname)(filePath);
6036
+ for (const regex of [importRegex, dynamicRegex]) {
6037
+ let match;
6038
+ while ((match = regex.exec(content)) !== null) {
6039
+ let resolved = (0, import_node_path9.resolve)(dir, match[1]);
6040
+ if (!resolved.endsWith(".ts") && !resolved.endsWith(".tsx")) {
6041
+ resolved += ".ts";
6042
+ }
6043
+ sources.push(resolved);
6044
+ }
6045
+ }
6046
+ return sources;
6047
+ }
6048
+ async function collectTsFiles(dir) {
6049
+ const results = [];
6050
+ async function scan(d) {
6051
+ let entries;
6052
+ try {
6053
+ entries = await (0, import_promises3.readdir)(d, { withFileTypes: true });
6054
+ } catch {
6055
+ return;
6056
+ }
6057
+ for (const entry of entries) {
6058
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist")
6059
+ continue;
6060
+ const fullPath = (0, import_node_path9.join)(d, entry.name);
6061
+ if (entry.isDirectory()) {
6062
+ await scan(fullPath);
6063
+ } 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")) {
6064
+ results.push(fullPath);
6065
+ }
6066
+ }
6067
+ }
6068
+ await scan(dir);
6069
+ return results;
6070
+ }
6071
+ function computeLongestChain(file, graph, visited, memo) {
6072
+ if (memo.has(file)) return memo.get(file);
6073
+ if (visited.has(file)) return 0;
6074
+ visited.add(file);
6075
+ const deps = graph.get(file) || [];
6076
+ let maxDepth = 0;
6077
+ for (const dep of deps) {
6078
+ const depth = 1 + computeLongestChain(dep, graph, visited, memo);
6079
+ if (depth > maxDepth) maxDepth = depth;
6080
+ }
6081
+ visited.delete(file);
6082
+ memo.set(file, maxDepth);
6083
+ return maxDepth;
6084
+ }
6085
+ var DepDepthCollector = class {
6086
+ category = "dependency-depth";
6087
+ getRules(config, _rootDir) {
6088
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : null;
6089
+ const desc = threshold !== null ? `Dependency chain depth must not exceed ${threshold}` : "Dependency chain depth must stay within thresholds";
6090
+ return [
6091
+ {
6092
+ id: constraintRuleId(this.category, "project", desc),
6093
+ category: this.category,
6094
+ description: desc,
6095
+ scope: "project"
6096
+ }
6097
+ ];
6098
+ }
6099
+ async collect(config, rootDir) {
6100
+ const allFiles = await collectTsFiles(rootDir);
6101
+ const graph = /* @__PURE__ */ new Map();
6102
+ const fileSet = new Set(allFiles);
6103
+ for (const file of allFiles) {
6104
+ try {
6105
+ const content = await (0, import_promises3.readFile)(file, "utf-8");
6106
+ const imports = extractImportSources(content, file).filter((imp) => fileSet.has(imp));
6107
+ graph.set(file, imports);
6108
+ } catch {
6109
+ graph.set(file, []);
6110
+ }
6111
+ }
6112
+ const moduleMap = /* @__PURE__ */ new Map();
6113
+ for (const file of allFiles) {
6114
+ const relDir = (0, import_node_path9.relative)(rootDir, (0, import_node_path9.dirname)(file));
6115
+ if (!moduleMap.has(relDir)) moduleMap.set(relDir, []);
6116
+ moduleMap.get(relDir).push(file);
6117
+ }
6118
+ const memo = /* @__PURE__ */ new Map();
6119
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : Infinity;
6120
+ const results = [];
6121
+ for (const [modulePath, files] of moduleMap) {
6122
+ let longestChain = 0;
6123
+ for (const file of files) {
6124
+ const depth = computeLongestChain(file, graph, /* @__PURE__ */ new Set(), memo);
6125
+ if (depth > longestChain) longestChain = depth;
6126
+ }
6127
+ const violations = [];
6128
+ if (longestChain > threshold) {
6129
+ violations.push({
6130
+ id: violationId(modulePath, this.category, "depth-exceeded"),
6131
+ file: modulePath,
6132
+ detail: `Import chain depth is ${longestChain} (threshold: ${threshold})`,
6133
+ severity: "warning"
6134
+ });
6135
+ }
6136
+ results.push({
6137
+ category: this.category,
6138
+ scope: modulePath,
6139
+ value: longestChain,
6140
+ violations,
6141
+ metadata: { longestChain }
6142
+ });
6143
+ }
6144
+ return results;
6145
+ }
6146
+ };
6147
+
6148
+ // src/architecture/collectors/index.ts
6149
+ var defaultCollectors = [
6150
+ new CircularDepsCollector(),
6151
+ new LayerViolationCollector(),
6152
+ new ComplexityCollector(),
6153
+ new CouplingCollector(),
6154
+ new ForbiddenImportCollector(),
6155
+ new ModuleSizeCollector(),
6156
+ new DepDepthCollector()
6157
+ ];
6158
+ async function runAll(config, rootDir, collectors = defaultCollectors) {
6159
+ const results = await Promise.allSettled(collectors.map((c) => c.collect(config, rootDir)));
6160
+ const allResults = [];
6161
+ for (let i = 0; i < results.length; i++) {
6162
+ const result = results[i];
6163
+ if (result.status === "fulfilled") {
6164
+ allResults.push(...result.value);
6165
+ } else {
6166
+ allResults.push({
6167
+ category: collectors[i].category,
6168
+ scope: "project",
6169
+ value: 0,
6170
+ violations: [],
6171
+ metadata: { error: String(result.reason) }
6172
+ });
6173
+ }
6174
+ }
6175
+ return allResults;
6176
+ }
6177
+
6178
+ // src/architecture/sync-constraints.ts
6179
+ function syncConstraintNodes(store, rules, violations) {
6180
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6181
+ const ruleIds = new Set(rules.map((r) => r.id));
6182
+ const violationsByCategory = /* @__PURE__ */ new Map();
6183
+ for (const result of violations) {
6184
+ const files = result.violations.map((v) => v.file);
6185
+ const existing = violationsByCategory.get(result.category) ?? [];
6186
+ violationsByCategory.set(result.category, [...existing, ...files]);
6187
+ }
6188
+ const existingNodesById = /* @__PURE__ */ new Map();
6189
+ for (const node of store.findNodes({ type: "constraint" })) {
6190
+ existingNodesById.set(node.id, node);
6191
+ }
6192
+ for (const rule of rules) {
6193
+ const existing = existingNodesById.get(rule.id);
6194
+ const createdAt = existing?.createdAt ?? now;
6195
+ const previousLastViolatedAt = existing?.lastViolatedAt ?? null;
6196
+ const hasViolation = hasMatchingViolation(rule, violationsByCategory);
6197
+ const lastViolatedAt = hasViolation ? now : previousLastViolatedAt;
6198
+ store.upsertNode({
6199
+ id: rule.id,
6200
+ type: "constraint",
6201
+ name: rule.description,
6202
+ category: rule.category,
6203
+ scope: rule.scope,
6204
+ createdAt,
6205
+ lastViolatedAt
6206
+ });
6207
+ }
6208
+ const existingConstraints = store.findNodes({ type: "constraint" });
6209
+ for (const node of existingConstraints) {
6210
+ if (!ruleIds.has(node.id)) {
6211
+ store.removeNode(node.id);
6212
+ }
6213
+ }
6214
+ }
6215
+ function hasMatchingViolation(rule, violationsByCategory) {
6216
+ const files = violationsByCategory.get(rule.category);
6217
+ if (!files || files.length === 0) return false;
6218
+ if (rule.scope === "project") return true;
6219
+ return files.some((file) => file.startsWith(rule.scope));
6220
+ }
6221
+
6222
+ // src/architecture/detect-stale.ts
6223
+ function detectStaleConstraints(store, windowDays = 30, category) {
6224
+ const now = Date.now();
6225
+ const windowMs = windowDays * 24 * 60 * 60 * 1e3;
6226
+ const cutoff = now - windowMs;
6227
+ let constraints = store.findNodes({ type: "constraint" });
6228
+ if (category) {
6229
+ constraints = constraints.filter((n) => n.category === category);
6230
+ }
6231
+ const totalConstraints = constraints.length;
6232
+ const staleConstraints = [];
6233
+ for (const node of constraints) {
6234
+ const lastViolatedAt = node.lastViolatedAt ?? null;
6235
+ const createdAt = node.createdAt;
6236
+ const comparisonTimestamp = lastViolatedAt ?? createdAt;
6237
+ if (!comparisonTimestamp) continue;
6238
+ const timestampMs = new Date(comparisonTimestamp).getTime();
6239
+ if (timestampMs < cutoff) {
6240
+ const daysSince = Math.floor((now - timestampMs) / (24 * 60 * 60 * 1e3));
6241
+ staleConstraints.push({
6242
+ id: node.id,
6243
+ category: node.category,
6244
+ description: node.name ?? "",
6245
+ scope: node.scope ?? "project",
6246
+ lastViolatedAt,
6247
+ daysSinceLastViolation: daysSince
6248
+ });
6249
+ }
6250
+ }
6251
+ staleConstraints.sort((a, b) => b.daysSinceLastViolation - a.daysSinceLastViolation);
6252
+ return { staleConstraints, totalConstraints, windowDays };
6253
+ }
6254
+
6255
+ // src/architecture/baseline-manager.ts
6256
+ var import_node_fs3 = require("fs");
6257
+ var import_node_crypto2 = require("crypto");
6258
+ var import_node_path10 = require("path");
6259
+ var ArchBaselineManager = class {
6260
+ baselinesPath;
6261
+ constructor(projectRoot, baselinePath) {
6262
+ this.baselinesPath = baselinePath ? (0, import_node_path10.join)(projectRoot, baselinePath) : (0, import_node_path10.join)(projectRoot, ".harness", "arch", "baselines.json");
6263
+ }
6264
+ /**
6265
+ * Snapshot the current metric results into an ArchBaseline.
6266
+ * Aggregates multiple MetricResults for the same category by summing values
6267
+ * and concatenating violation IDs.
6268
+ */
6269
+ capture(results, commitHash) {
6270
+ const metrics = {};
6271
+ for (const result of results) {
6272
+ const existing = metrics[result.category];
6273
+ if (existing) {
6274
+ existing.value += result.value;
6275
+ existing.violationIds.push(...result.violations.map((v) => v.id));
6276
+ } else {
6277
+ metrics[result.category] = {
6278
+ value: result.value,
6279
+ violationIds: result.violations.map((v) => v.id)
6280
+ };
6281
+ }
6282
+ }
6283
+ return {
6284
+ version: 1,
6285
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6286
+ updatedFrom: commitHash,
6287
+ metrics
6288
+ };
6289
+ }
6290
+ /**
6291
+ * Load the baselines file from disk.
6292
+ * Returns null if the file does not exist, contains invalid JSON,
6293
+ * or fails ArchBaselineSchema validation.
6294
+ */
6295
+ load() {
6296
+ if (!(0, import_node_fs3.existsSync)(this.baselinesPath)) {
6297
+ console.error(`Baseline file not found at: ${this.baselinesPath}`);
6298
+ return null;
6299
+ }
6300
+ try {
6301
+ const raw = (0, import_node_fs3.readFileSync)(this.baselinesPath, "utf-8");
6302
+ const data = JSON.parse(raw);
6303
+ const parsed = ArchBaselineSchema.safeParse(data);
6304
+ if (!parsed.success) {
6305
+ console.error(
6306
+ `Baseline validation failed for ${this.baselinesPath}:`,
6307
+ parsed.error.format()
6308
+ );
6309
+ return null;
6310
+ }
6311
+ return parsed.data;
6312
+ } catch (error) {
6313
+ console.error(`Error loading baseline from ${this.baselinesPath}:`, error);
6314
+ return null;
6315
+ }
6316
+ }
6317
+ /**
6318
+ * Save an ArchBaseline to disk.
6319
+ * Creates parent directories if they do not exist.
6320
+ * Uses atomic write (write to temp file, then rename) to prevent corruption.
6321
+ */
6322
+ save(baseline) {
6323
+ const dir = (0, import_node_path10.dirname)(this.baselinesPath);
6324
+ if (!(0, import_node_fs3.existsSync)(dir)) {
6325
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
6326
+ }
6327
+ const tmp = this.baselinesPath + "." + (0, import_node_crypto2.randomBytes)(4).toString("hex") + ".tmp";
6328
+ (0, import_node_fs3.writeFileSync)(tmp, JSON.stringify(baseline, null, 2));
6329
+ (0, import_node_fs3.renameSync)(tmp, this.baselinesPath);
6330
+ }
6331
+ };
6332
+
6333
+ // src/architecture/diff.ts
6334
+ function aggregateByCategory(results) {
6335
+ const map = /* @__PURE__ */ new Map();
6336
+ for (const result of results) {
6337
+ const existing = map.get(result.category);
6338
+ if (existing) {
6339
+ existing.value += result.value;
6340
+ existing.violations.push(...result.violations);
6341
+ } else {
6342
+ map.set(result.category, {
6343
+ value: result.value,
6344
+ violations: [...result.violations]
6345
+ });
6346
+ }
6347
+ }
6348
+ return map;
6349
+ }
6350
+ function diff(current, baseline) {
6351
+ const aggregated = aggregateByCategory(current);
6352
+ const newViolations = [];
6353
+ const resolvedViolations = [];
6354
+ const preExisting = [];
6355
+ const regressions = [];
6356
+ const visitedCategories = /* @__PURE__ */ new Set();
6357
+ for (const [category, agg] of aggregated) {
6358
+ visitedCategories.add(category);
6359
+ const baselineCategory = baseline.metrics[category];
6360
+ const baselineViolationIds = new Set(baselineCategory?.violationIds ?? []);
6361
+ const baselineValue = baselineCategory?.value ?? 0;
6362
+ for (const violation of agg.violations) {
6363
+ if (baselineViolationIds.has(violation.id)) {
6364
+ preExisting.push(violation.id);
6365
+ } else {
6366
+ newViolations.push(violation);
6367
+ }
6368
+ }
6369
+ const currentViolationIds = new Set(agg.violations.map((v) => v.id));
6370
+ if (baselineCategory) {
6371
+ for (const id of baselineCategory.violationIds) {
6372
+ if (!currentViolationIds.has(id)) {
6373
+ resolvedViolations.push(id);
6374
+ }
6375
+ }
6376
+ }
6377
+ if (baselineCategory && agg.value > baselineValue) {
6378
+ regressions.push({
6379
+ category,
6380
+ baselineValue,
6381
+ currentValue: agg.value,
6382
+ delta: agg.value - baselineValue
6383
+ });
6384
+ }
6385
+ }
6386
+ for (const [category, baselineCategory] of Object.entries(baseline.metrics)) {
6387
+ if (!visitedCategories.has(category) && baselineCategory) {
6388
+ for (const id of baselineCategory.violationIds) {
6389
+ resolvedViolations.push(id);
6390
+ }
6391
+ }
6392
+ }
6393
+ const passed = newViolations.length === 0 && regressions.length === 0;
6394
+ return {
6395
+ passed,
6396
+ newViolations,
6397
+ resolvedViolations,
6398
+ preExisting,
6399
+ regressions
6400
+ };
6401
+ }
6402
+
6403
+ // src/architecture/config.ts
6404
+ function resolveThresholds(scope, config) {
6405
+ const projectThresholds = {};
6406
+ for (const [key, val] of Object.entries(config.thresholds)) {
6407
+ projectThresholds[key] = typeof val === "object" && val !== null && !Array.isArray(val) ? { ...val } : val;
6408
+ }
6409
+ if (scope === "project") {
6410
+ return projectThresholds;
6411
+ }
6412
+ const moduleOverrides = config.modules[scope];
6413
+ if (!moduleOverrides) {
6414
+ return projectThresholds;
6415
+ }
6416
+ const merged = { ...projectThresholds };
6417
+ for (const [category, moduleValue] of Object.entries(moduleOverrides)) {
6418
+ const projectValue = projectThresholds[category];
6419
+ if (projectValue !== void 0 && typeof projectValue === "object" && !Array.isArray(projectValue) && typeof moduleValue === "object" && !Array.isArray(moduleValue)) {
6420
+ merged[category] = {
6421
+ ...projectValue,
6422
+ ...moduleValue
6423
+ };
6424
+ } else {
6425
+ merged[category] = moduleValue;
6426
+ }
6427
+ }
6428
+ return merged;
6429
+ }
6430
+
6431
+ // src/architecture/matchers.ts
6432
+ function architecture(options) {
6433
+ return {
6434
+ kind: "arch-handle",
6435
+ scope: "project",
6436
+ rootDir: options?.rootDir ?? process.cwd(),
6437
+ config: options?.config
6438
+ };
6439
+ }
6440
+ function archModule(modulePath, options) {
6441
+ return {
6442
+ kind: "arch-handle",
6443
+ scope: modulePath,
6444
+ rootDir: options?.rootDir ?? process.cwd(),
6445
+ config: options?.config
6446
+ };
6447
+ }
6448
+ function resolveConfig(handle) {
6449
+ return ArchConfigSchema.parse(handle.config ?? {});
6450
+ }
6451
+ async function collectCategory(handle, collector) {
6452
+ if ("_mockResults" in handle && handle._mockResults) {
6453
+ return handle._mockResults;
6454
+ }
6455
+ const config = resolveConfig(handle);
6456
+ return collector.collect(config, handle.rootDir);
6457
+ }
6458
+ function formatViolationList(violations, limit = 10) {
6459
+ const lines = violations.slice(0, limit).map((v) => ` - ${v.file}: ${v.detail}`);
6460
+ if (violations.length > limit) {
6461
+ lines.push(` ... and ${violations.length - limit} more`);
6462
+ }
6463
+ return lines.join("\n");
6464
+ }
6465
+ async function toHaveNoCircularDeps(received) {
6466
+ const results = await collectCategory(received, new CircularDepsCollector());
6467
+ const violations = results.flatMap((r) => r.violations);
6468
+ const pass = violations.length === 0;
6469
+ return {
6470
+ pass,
6471
+ message: () => pass ? "Expected circular dependencies but found none" : `Found ${violations.length} circular dependenc${violations.length === 1 ? "y" : "ies"}:
6472
+ ${formatViolationList(violations)}`
6473
+ };
6474
+ }
6475
+ async function toHaveNoLayerViolations(received) {
6476
+ const results = await collectCategory(received, new LayerViolationCollector());
6477
+ const violations = results.flatMap((r) => r.violations);
6478
+ const pass = violations.length === 0;
6479
+ return {
6480
+ pass,
6481
+ message: () => pass ? "Expected layer violations but found none" : `Found ${violations.length} layer violation${violations.length === 1 ? "" : "s"}:
6482
+ ${formatViolationList(violations)}`
6483
+ };
6484
+ }
6485
+ async function toMatchBaseline(received, options) {
6486
+ let diffResult;
6487
+ if ("_mockDiff" in received && received._mockDiff) {
6488
+ diffResult = received._mockDiff;
6489
+ } else {
6490
+ const config = resolveConfig(received);
6491
+ const results = await runAll(config, received.rootDir);
6492
+ const manager = new ArchBaselineManager(received.rootDir, config.baselinePath);
6493
+ const baseline = manager.load();
6494
+ if (!baseline) {
6495
+ return {
6496
+ pass: false,
6497
+ message: () => "No baseline found. Run `harness check-arch --update-baseline` to create one."
6498
+ };
6499
+ }
6500
+ diffResult = diff(results, baseline);
6501
+ }
6502
+ const tolerance = options?.tolerance ?? 0;
6503
+ const effectiveNewCount = Math.max(0, diffResult.newViolations.length - tolerance);
6504
+ const pass = effectiveNewCount === 0 && diffResult.regressions.length === 0;
6505
+ return {
6506
+ pass,
6507
+ message: () => {
6508
+ if (pass) {
6509
+ return "Expected baseline regression but architecture matches baseline";
6510
+ }
6511
+ const parts = [];
6512
+ if (diffResult.newViolations.length > 0) {
6513
+ parts.push(
6514
+ `${diffResult.newViolations.length} new violation${diffResult.newViolations.length === 1 ? "" : "s"}${tolerance > 0 ? ` (tolerance: ${tolerance})` : ""}:
6515
+ ${formatViolationList(diffResult.newViolations)}`
6516
+ );
6517
+ }
6518
+ if (diffResult.regressions.length > 0) {
6519
+ const regLines = diffResult.regressions.map(
6520
+ (r) => ` - ${r.category}: ${r.baselineValue} -> ${r.currentValue} (+${r.delta})`
6521
+ );
6522
+ parts.push(`Regressions:
6523
+ ${regLines.join("\n")}`);
6524
+ }
6525
+ return `Baseline check failed:
6526
+ ${parts.join("\n\n")}`;
6527
+ }
6528
+ };
6529
+ }
6530
+ function filterByScope(results, scope) {
6531
+ return results.filter(
6532
+ (r) => r.scope === scope || r.scope.startsWith(scope + "/") || r.scope === "project"
6533
+ );
6534
+ }
6535
+ async function toHaveMaxComplexity(received, maxComplexity) {
6536
+ const results = await collectCategory(received, new ComplexityCollector());
6537
+ const scoped = filterByScope(results, received.scope);
6538
+ const violations = scoped.flatMap((r) => r.violations);
6539
+ const totalValue = scoped.reduce((sum, r) => sum + r.value, 0);
6540
+ const pass = totalValue <= maxComplexity && violations.length === 0;
6541
+ return {
6542
+ pass,
6543
+ 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"}):
6544
+ ${formatViolationList(violations)}`
6545
+ };
6546
+ }
6547
+ async function toHaveMaxCoupling(received, limits) {
6548
+ const config = resolveConfig(received);
6549
+ if (limits.fanIn !== void 0 || limits.fanOut !== void 0) {
6550
+ config.thresholds.coupling = {
6551
+ ...typeof config.thresholds.coupling === "object" ? config.thresholds.coupling : {},
6552
+ ...limits.fanIn !== void 0 ? { maxFanIn: limits.fanIn } : {},
6553
+ ...limits.fanOut !== void 0 ? { maxFanOut: limits.fanOut } : {}
6554
+ };
6555
+ }
6556
+ const collector = new CouplingCollector();
6557
+ const results = "_mockResults" in received && received._mockResults ? received._mockResults : await collector.collect(config, received.rootDir);
6558
+ const scoped = filterByScope(results, received.scope);
6559
+ const violations = scoped.flatMap((r) => r.violations);
6560
+ const pass = violations.length === 0;
6561
+ return {
6562
+ pass,
6563
+ 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"}):
6564
+ ${formatViolationList(violations)}`
6565
+ };
6566
+ }
6567
+ async function toHaveMaxFileCount(received, maxFiles) {
6568
+ const results = await collectCategory(received, new ModuleSizeCollector());
6569
+ const scoped = filterByScope(results, received.scope);
6570
+ const fileCount = scoped.reduce((max, r) => {
6571
+ const meta = r.metadata;
6572
+ const fc = typeof meta?.fileCount === "number" ? meta.fileCount : 0;
6573
+ return fc > max ? fc : max;
6574
+ }, 0);
6575
+ const pass = fileCount <= maxFiles;
6576
+ return {
6577
+ pass,
6578
+ message: () => pass ? `Expected file count in '${received.scope}' to exceed ${maxFiles} but it was ${fileCount}` : `Module '${received.scope}' has ${fileCount} files (limit: ${maxFiles})`
6579
+ };
6580
+ }
6581
+ async function toNotDependOn(received, forbiddenModule) {
6582
+ const results = await collectCategory(received, new ForbiddenImportCollector());
6583
+ const allViolations = results.flatMap((r) => r.violations);
6584
+ const scopePrefix = received.scope.replace(/\/+$/, "");
6585
+ const forbiddenPrefix = forbiddenModule.replace(/\/+$/, "");
6586
+ const relevantViolations = allViolations.filter(
6587
+ (v) => (v.file === scopePrefix || v.file.startsWith(scopePrefix + "/")) && (v.detail.includes(forbiddenPrefix + "/") || v.detail.endsWith(forbiddenPrefix))
6588
+ );
6589
+ const pass = relevantViolations.length === 0;
6590
+ return {
6591
+ pass,
6592
+ 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"}):
6593
+ ${formatViolationList(relevantViolations)}`
6594
+ };
6595
+ }
6596
+ async function toHaveMaxDepDepth(received, maxDepth) {
6597
+ const results = await collectCategory(received, new DepDepthCollector());
6598
+ const scoped = filterByScope(results, received.scope);
6599
+ const maxActual = scoped.reduce((max, r) => r.value > max ? r.value : max, 0);
6600
+ const pass = maxActual <= maxDepth;
6601
+ return {
6602
+ pass,
6603
+ message: () => pass ? `Expected dependency depth in '${received.scope}' to exceed ${maxDepth} but it was ${maxActual}` : `Module '${received.scope}' has dependency depth ${maxActual} (limit: ${maxDepth})`
6604
+ };
6605
+ }
6606
+ var archMatchers = {
6607
+ toHaveNoCircularDeps,
6608
+ toHaveNoLayerViolations,
6609
+ toMatchBaseline,
6610
+ toHaveMaxComplexity,
6611
+ toHaveMaxCoupling,
6612
+ toHaveMaxFileCount,
6613
+ toNotDependOn,
6614
+ toHaveMaxDepDepth
6615
+ };
6616
+
4894
6617
  // 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()
6618
+ var import_zod4 = require("zod");
6619
+ var FailureEntrySchema = import_zod4.z.object({
6620
+ date: import_zod4.z.string(),
6621
+ skill: import_zod4.z.string(),
6622
+ type: import_zod4.z.string(),
6623
+ description: import_zod4.z.string()
4901
6624
  });
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()
6625
+ var HandoffSchema = import_zod4.z.object({
6626
+ timestamp: import_zod4.z.string(),
6627
+ fromSkill: import_zod4.z.string(),
6628
+ phase: import_zod4.z.string(),
6629
+ summary: import_zod4.z.string(),
6630
+ completed: import_zod4.z.array(import_zod4.z.string()).default([]),
6631
+ pending: import_zod4.z.array(import_zod4.z.string()).default([]),
6632
+ concerns: import_zod4.z.array(import_zod4.z.string()).default([]),
6633
+ decisions: import_zod4.z.array(
6634
+ import_zod4.z.object({
6635
+ what: import_zod4.z.string(),
6636
+ why: import_zod4.z.string()
4914
6637
  })
4915
6638
  ).default([]),
4916
- blockers: import_zod2.z.array(import_zod2.z.string()).default([]),
4917
- contextKeywords: import_zod2.z.array(import_zod2.z.string()).default([])
6639
+ blockers: import_zod4.z.array(import_zod4.z.string()).default([]),
6640
+ contextKeywords: import_zod4.z.array(import_zod4.z.string()).default([])
4918
6641
  });
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()
6642
+ var GateCheckSchema = import_zod4.z.object({
6643
+ name: import_zod4.z.string(),
6644
+ passed: import_zod4.z.boolean(),
6645
+ command: import_zod4.z.string(),
6646
+ output: import_zod4.z.string().optional(),
6647
+ duration: import_zod4.z.number().optional()
4925
6648
  });
4926
- var GateResultSchema = import_zod2.z.object({
4927
- passed: import_zod2.z.boolean(),
4928
- checks: import_zod2.z.array(GateCheckSchema)
6649
+ var GateResultSchema = import_zod4.z.object({
6650
+ passed: import_zod4.z.boolean(),
6651
+ checks: import_zod4.z.array(GateCheckSchema)
4929
6652
  });
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()
6653
+ var GateConfigSchema = import_zod4.z.object({
6654
+ checks: import_zod4.z.array(
6655
+ import_zod4.z.object({
6656
+ name: import_zod4.z.string(),
6657
+ command: import_zod4.z.string()
4935
6658
  })
4936
6659
  ).optional(),
4937
- trace: import_zod2.z.boolean().optional()
6660
+ trace: import_zod4.z.boolean().optional()
4938
6661
  });
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()
6662
+ var HarnessStateSchema = import_zod4.z.object({
6663
+ schemaVersion: import_zod4.z.literal(1),
6664
+ position: import_zod4.z.object({
6665
+ phase: import_zod4.z.string().optional(),
6666
+ task: import_zod4.z.string().optional()
4944
6667
  }).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()
6668
+ decisions: import_zod4.z.array(
6669
+ import_zod4.z.object({
6670
+ date: import_zod4.z.string(),
6671
+ decision: import_zod4.z.string(),
6672
+ context: import_zod4.z.string()
4950
6673
  })
4951
6674
  ).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"])
6675
+ blockers: import_zod4.z.array(
6676
+ import_zod4.z.object({
6677
+ id: import_zod4.z.string(),
6678
+ description: import_zod4.z.string(),
6679
+ status: import_zod4.z.enum(["open", "resolved"])
4957
6680
  })
4958
6681
  ).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()
6682
+ progress: import_zod4.z.record(import_zod4.z.enum(["pending", "in_progress", "complete"])).default({}),
6683
+ lastSession: import_zod4.z.object({
6684
+ date: import_zod4.z.string(),
6685
+ summary: import_zod4.z.string(),
6686
+ lastSkill: import_zod4.z.string().optional(),
6687
+ pendingTasks: import_zod4.z.array(import_zod4.z.string()).optional()
4965
6688
  }).optional()
4966
6689
  });
4967
6690
  var DEFAULT_STATE = {
@@ -4973,27 +6696,27 @@ var DEFAULT_STATE = {
4973
6696
  };
4974
6697
 
4975
6698
  // src/state/state-manager.ts
4976
- var fs4 = __toESM(require("fs"));
6699
+ var fs6 = __toESM(require("fs"));
4977
6700
  var path3 = __toESM(require("path"));
4978
6701
  var import_child_process2 = require("child_process");
4979
6702
 
4980
6703
  // src/state/stream-resolver.ts
4981
- var fs3 = __toESM(require("fs"));
6704
+ var fs5 = __toESM(require("fs"));
4982
6705
  var path2 = __toESM(require("path"));
4983
6706
  var import_child_process = require("child_process");
4984
6707
 
4985
6708
  // 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()
6709
+ var import_zod5 = require("zod");
6710
+ var StreamInfoSchema = import_zod5.z.object({
6711
+ name: import_zod5.z.string(),
6712
+ branch: import_zod5.z.string().optional(),
6713
+ createdAt: import_zod5.z.string(),
6714
+ lastActiveAt: import_zod5.z.string()
4992
6715
  });
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)
6716
+ var StreamIndexSchema = import_zod5.z.object({
6717
+ schemaVersion: import_zod5.z.literal(1),
6718
+ activeStream: import_zod5.z.string().nullable(),
6719
+ streams: import_zod5.z.record(StreamInfoSchema)
4997
6720
  });
4998
6721
  var DEFAULT_STREAM_INDEX = {
4999
6722
  schemaVersion: 1,
@@ -5024,11 +6747,11 @@ function validateStreamName(name) {
5024
6747
  }
5025
6748
  async function loadStreamIndex(projectPath) {
5026
6749
  const idxPath = indexPath(projectPath);
5027
- if (!fs3.existsSync(idxPath)) {
6750
+ if (!fs5.existsSync(idxPath)) {
5028
6751
  return (0, import_types.Ok)({ ...DEFAULT_STREAM_INDEX, streams: {} });
5029
6752
  }
5030
6753
  try {
5031
- const raw = fs3.readFileSync(idxPath, "utf-8");
6754
+ const raw = fs5.readFileSync(idxPath, "utf-8");
5032
6755
  const parsed = JSON.parse(raw);
5033
6756
  const result = StreamIndexSchema.safeParse(parsed);
5034
6757
  if (!result.success) {
@@ -5046,8 +6769,8 @@ async function loadStreamIndex(projectPath) {
5046
6769
  async function saveStreamIndex(projectPath, index) {
5047
6770
  const dir = streamsDir(projectPath);
5048
6771
  try {
5049
- fs3.mkdirSync(dir, { recursive: true });
5050
- fs3.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
6772
+ fs5.mkdirSync(dir, { recursive: true });
6773
+ fs5.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
5051
6774
  return (0, import_types.Ok)(void 0);
5052
6775
  } catch (error) {
5053
6776
  return (0, import_types.Err)(
@@ -5129,7 +6852,7 @@ async function createStream(projectPath, name, branch) {
5129
6852
  }
5130
6853
  const streamPath = path2.join(streamsDir(projectPath), name);
5131
6854
  try {
5132
- fs3.mkdirSync(streamPath, { recursive: true });
6855
+ fs5.mkdirSync(streamPath, { recursive: true });
5133
6856
  } catch (error) {
5134
6857
  return (0, import_types.Err)(
5135
6858
  new Error(
@@ -5173,9 +6896,9 @@ async function archiveStream(projectPath, name) {
5173
6896
  const streamPath = path2.join(streamsDir(projectPath), name);
5174
6897
  const archiveDir = path2.join(projectPath, HARNESS_DIR, "archive", "streams");
5175
6898
  try {
5176
- fs3.mkdirSync(archiveDir, { recursive: true });
6899
+ fs5.mkdirSync(archiveDir, { recursive: true });
5177
6900
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5178
- fs3.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
6901
+ fs5.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
5179
6902
  } catch (error) {
5180
6903
  return (0, import_types.Err)(
5181
6904
  new Error(
@@ -5198,18 +6921,18 @@ function getStreamForBranch(index, branch) {
5198
6921
  var STATE_FILES = ["state.json", "handoff.json", "learnings.md", "failures.md"];
5199
6922
  async function migrateToStreams(projectPath) {
5200
6923
  const harnessDir = path2.join(projectPath, HARNESS_DIR);
5201
- if (fs3.existsSync(indexPath(projectPath))) {
6924
+ if (fs5.existsSync(indexPath(projectPath))) {
5202
6925
  return (0, import_types.Ok)(void 0);
5203
6926
  }
5204
- const filesToMove = STATE_FILES.filter((f) => fs3.existsSync(path2.join(harnessDir, f)));
6927
+ const filesToMove = STATE_FILES.filter((f) => fs5.existsSync(path2.join(harnessDir, f)));
5205
6928
  if (filesToMove.length === 0) {
5206
6929
  return (0, import_types.Ok)(void 0);
5207
6930
  }
5208
6931
  const defaultDir = path2.join(streamsDir(projectPath), "default");
5209
6932
  try {
5210
- fs3.mkdirSync(defaultDir, { recursive: true });
6933
+ fs5.mkdirSync(defaultDir, { recursive: true });
5211
6934
  for (const file of filesToMove) {
5212
- fs3.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
6935
+ fs5.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
5213
6936
  }
5214
6937
  } catch (error) {
5215
6938
  return (0, import_types.Err)(
@@ -5250,7 +6973,7 @@ function evictIfNeeded(map) {
5250
6973
  }
5251
6974
  async function getStateDir(projectPath, stream) {
5252
6975
  const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
5253
- const hasStreams = fs4.existsSync(streamsIndexPath);
6976
+ const hasStreams = fs6.existsSync(streamsIndexPath);
5254
6977
  if (stream || hasStreams) {
5255
6978
  const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
5256
6979
  if (result.ok) {
@@ -5268,10 +6991,10 @@ async function loadState(projectPath, stream) {
5268
6991
  if (!dirResult.ok) return dirResult;
5269
6992
  const stateDir = dirResult.value;
5270
6993
  const statePath = path3.join(stateDir, STATE_FILE);
5271
- if (!fs4.existsSync(statePath)) {
6994
+ if (!fs6.existsSync(statePath)) {
5272
6995
  return (0, import_types.Ok)({ ...DEFAULT_STATE });
5273
6996
  }
5274
- const raw = fs4.readFileSync(statePath, "utf-8");
6997
+ const raw = fs6.readFileSync(statePath, "utf-8");
5275
6998
  const parsed = JSON.parse(raw);
5276
6999
  const result = HarnessStateSchema.safeParse(parsed);
5277
7000
  if (!result.success) {
@@ -5290,8 +7013,8 @@ async function saveState(projectPath, state, stream) {
5290
7013
  if (!dirResult.ok) return dirResult;
5291
7014
  const stateDir = dirResult.value;
5292
7015
  const statePath = path3.join(stateDir, STATE_FILE);
5293
- fs4.mkdirSync(stateDir, { recursive: true });
5294
- fs4.writeFileSync(statePath, JSON.stringify(state, null, 2));
7016
+ fs6.mkdirSync(stateDir, { recursive: true });
7017
+ fs6.writeFileSync(statePath, JSON.stringify(state, null, 2));
5295
7018
  return (0, import_types.Ok)(void 0);
5296
7019
  } catch (error) {
5297
7020
  return (0, import_types.Err)(
@@ -5305,7 +7028,7 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5305
7028
  if (!dirResult.ok) return dirResult;
5306
7029
  const stateDir = dirResult.value;
5307
7030
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5308
- fs4.mkdirSync(stateDir, { recursive: true });
7031
+ fs6.mkdirSync(stateDir, { recursive: true });
5309
7032
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5310
7033
  let entry;
5311
7034
  if (skillName && outcome) {
@@ -5321,11 +7044,11 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5321
7044
  - **${timestamp}:** ${learning}
5322
7045
  `;
5323
7046
  }
5324
- if (!fs4.existsSync(learningsPath)) {
5325
- fs4.writeFileSync(learningsPath, `# Learnings
7047
+ if (!fs6.existsSync(learningsPath)) {
7048
+ fs6.writeFileSync(learningsPath, `# Learnings
5326
7049
  ${entry}`);
5327
7050
  } else {
5328
- fs4.appendFileSync(learningsPath, entry);
7051
+ fs6.appendFileSync(learningsPath, entry);
5329
7052
  }
5330
7053
  learningsCacheMap.delete(learningsPath);
5331
7054
  return (0, import_types.Ok)(void 0);
@@ -5343,17 +7066,17 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
5343
7066
  if (!dirResult.ok) return dirResult;
5344
7067
  const stateDir = dirResult.value;
5345
7068
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5346
- if (!fs4.existsSync(learningsPath)) {
7069
+ if (!fs6.existsSync(learningsPath)) {
5347
7070
  return (0, import_types.Ok)([]);
5348
7071
  }
5349
- const stats = fs4.statSync(learningsPath);
7072
+ const stats = fs6.statSync(learningsPath);
5350
7073
  const cacheKey = learningsPath;
5351
7074
  const cached = learningsCacheMap.get(cacheKey);
5352
7075
  let entries;
5353
7076
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5354
7077
  entries = cached.entries;
5355
7078
  } else {
5356
- const content = fs4.readFileSync(learningsPath, "utf-8");
7079
+ const content = fs6.readFileSync(learningsPath, "utf-8");
5357
7080
  const lines = content.split("\n");
5358
7081
  entries = [];
5359
7082
  let currentBlock = [];
@@ -5396,16 +7119,16 @@ async function appendFailure(projectPath, description, skillName, type, stream)
5396
7119
  if (!dirResult.ok) return dirResult;
5397
7120
  const stateDir = dirResult.value;
5398
7121
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5399
- fs4.mkdirSync(stateDir, { recursive: true });
7122
+ fs6.mkdirSync(stateDir, { recursive: true });
5400
7123
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5401
7124
  const entry = `
5402
7125
  - **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
5403
7126
  `;
5404
- if (!fs4.existsSync(failuresPath)) {
5405
- fs4.writeFileSync(failuresPath, `# Failures
7127
+ if (!fs6.existsSync(failuresPath)) {
7128
+ fs6.writeFileSync(failuresPath, `# Failures
5406
7129
  ${entry}`);
5407
7130
  } else {
5408
- fs4.appendFileSync(failuresPath, entry);
7131
+ fs6.appendFileSync(failuresPath, entry);
5409
7132
  }
5410
7133
  failuresCacheMap.delete(failuresPath);
5411
7134
  return (0, import_types.Ok)(void 0);
@@ -5423,16 +7146,16 @@ async function loadFailures(projectPath, stream) {
5423
7146
  if (!dirResult.ok) return dirResult;
5424
7147
  const stateDir = dirResult.value;
5425
7148
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5426
- if (!fs4.existsSync(failuresPath)) {
7149
+ if (!fs6.existsSync(failuresPath)) {
5427
7150
  return (0, import_types.Ok)([]);
5428
7151
  }
5429
- const stats = fs4.statSync(failuresPath);
7152
+ const stats = fs6.statSync(failuresPath);
5430
7153
  const cacheKey = failuresPath;
5431
7154
  const cached = failuresCacheMap.get(cacheKey);
5432
7155
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5433
7156
  return (0, import_types.Ok)(cached.entries);
5434
7157
  }
5435
- const content = fs4.readFileSync(failuresPath, "utf-8");
7158
+ const content = fs6.readFileSync(failuresPath, "utf-8");
5436
7159
  const entries = [];
5437
7160
  for (const line of content.split("\n")) {
5438
7161
  const match = line.match(FAILURE_LINE_REGEX);
@@ -5462,19 +7185,19 @@ async function archiveFailures(projectPath, stream) {
5462
7185
  if (!dirResult.ok) return dirResult;
5463
7186
  const stateDir = dirResult.value;
5464
7187
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5465
- if (!fs4.existsSync(failuresPath)) {
7188
+ if (!fs6.existsSync(failuresPath)) {
5466
7189
  return (0, import_types.Ok)(void 0);
5467
7190
  }
5468
7191
  const archiveDir = path3.join(stateDir, "archive");
5469
- fs4.mkdirSync(archiveDir, { recursive: true });
7192
+ fs6.mkdirSync(archiveDir, { recursive: true });
5470
7193
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5471
7194
  let archiveName = `failures-${date}.md`;
5472
7195
  let counter = 2;
5473
- while (fs4.existsSync(path3.join(archiveDir, archiveName))) {
7196
+ while (fs6.existsSync(path3.join(archiveDir, archiveName))) {
5474
7197
  archiveName = `failures-${date}-${counter}.md`;
5475
7198
  counter++;
5476
7199
  }
5477
- fs4.renameSync(failuresPath, path3.join(archiveDir, archiveName));
7200
+ fs6.renameSync(failuresPath, path3.join(archiveDir, archiveName));
5478
7201
  failuresCacheMap.delete(failuresPath);
5479
7202
  return (0, import_types.Ok)(void 0);
5480
7203
  } catch (error) {
@@ -5491,8 +7214,8 @@ async function saveHandoff(projectPath, handoff, stream) {
5491
7214
  if (!dirResult.ok) return dirResult;
5492
7215
  const stateDir = dirResult.value;
5493
7216
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5494
- fs4.mkdirSync(stateDir, { recursive: true });
5495
- fs4.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
7217
+ fs6.mkdirSync(stateDir, { recursive: true });
7218
+ fs6.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
5496
7219
  return (0, import_types.Ok)(void 0);
5497
7220
  } catch (error) {
5498
7221
  return (0, import_types.Err)(
@@ -5506,10 +7229,10 @@ async function loadHandoff(projectPath, stream) {
5506
7229
  if (!dirResult.ok) return dirResult;
5507
7230
  const stateDir = dirResult.value;
5508
7231
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5509
- if (!fs4.existsSync(handoffPath)) {
7232
+ if (!fs6.existsSync(handoffPath)) {
5510
7233
  return (0, import_types.Ok)(null);
5511
7234
  }
5512
- const raw = fs4.readFileSync(handoffPath, "utf-8");
7235
+ const raw = fs6.readFileSync(handoffPath, "utf-8");
5513
7236
  const parsed = JSON.parse(raw);
5514
7237
  const result = HandoffSchema.safeParse(parsed);
5515
7238
  if (!result.success) {
@@ -5527,8 +7250,8 @@ async function runMechanicalGate(projectPath) {
5527
7250
  const gateConfigPath = path3.join(harnessDir, GATE_CONFIG_FILE);
5528
7251
  try {
5529
7252
  let checks = [];
5530
- if (fs4.existsSync(gateConfigPath)) {
5531
- const raw = JSON.parse(fs4.readFileSync(gateConfigPath, "utf-8"));
7253
+ if (fs6.existsSync(gateConfigPath)) {
7254
+ const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
5532
7255
  const config = GateConfigSchema.safeParse(raw);
5533
7256
  if (config.success && config.data.checks) {
5534
7257
  checks = config.data.checks;
@@ -5536,19 +7259,19 @@ async function runMechanicalGate(projectPath) {
5536
7259
  }
5537
7260
  if (checks.length === 0) {
5538
7261
  const packageJsonPath = path3.join(projectPath, "package.json");
5539
- if (fs4.existsSync(packageJsonPath)) {
5540
- const pkg = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
7262
+ if (fs6.existsSync(packageJsonPath)) {
7263
+ const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
5541
7264
  const scripts = pkg.scripts || {};
5542
7265
  if (scripts.test) checks.push({ name: "test", command: "npm test" });
5543
7266
  if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
5544
7267
  if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
5545
7268
  if (scripts.build) checks.push({ name: "build", command: "npm run build" });
5546
7269
  }
5547
- if (fs4.existsSync(path3.join(projectPath, "go.mod"))) {
7270
+ if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
5548
7271
  checks.push({ name: "test", command: "go test ./..." });
5549
7272
  checks.push({ name: "build", command: "go build ./..." });
5550
7273
  }
5551
- if (fs4.existsSync(path3.join(projectPath, "pyproject.toml")) || fs4.existsSync(path3.join(projectPath, "setup.py"))) {
7274
+ if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
5552
7275
  checks.push({ name: "test", command: "python -m pytest" });
5553
7276
  }
5554
7277
  }
@@ -5751,7 +7474,7 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
5751
7474
  }
5752
7475
 
5753
7476
  // src/security/scanner.ts
5754
- var fs6 = __toESM(require("fs/promises"));
7477
+ var fs8 = __toESM(require("fs/promises"));
5755
7478
 
5756
7479
  // src/security/rules/registry.ts
5757
7480
  var RuleRegistry = class {
@@ -5782,7 +7505,7 @@ var RuleRegistry = class {
5782
7505
  };
5783
7506
 
5784
7507
  // src/security/config.ts
5785
- var import_zod4 = require("zod");
7508
+ var import_zod6 = require("zod");
5786
7509
 
5787
7510
  // src/security/types.ts
5788
7511
  var DEFAULT_SECURITY_CONFIG = {
@@ -5793,19 +7516,19 @@ var DEFAULT_SECURITY_CONFIG = {
5793
7516
  };
5794
7517
 
5795
7518
  // 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()
7519
+ var RuleOverrideSchema = import_zod6.z.enum(["off", "error", "warning", "info"]);
7520
+ var SecurityConfigSchema = import_zod6.z.object({
7521
+ enabled: import_zod6.z.boolean().default(true),
7522
+ strict: import_zod6.z.boolean().default(false),
7523
+ rules: import_zod6.z.record(import_zod6.z.string(), RuleOverrideSchema).optional().default({}),
7524
+ exclude: import_zod6.z.array(import_zod6.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
7525
+ external: import_zod6.z.object({
7526
+ semgrep: import_zod6.z.object({
7527
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto"),
7528
+ rulesets: import_zod6.z.array(import_zod6.z.string()).optional()
5806
7529
  }).optional(),
5807
- gitleaks: import_zod4.z.object({
5808
- enabled: import_zod4.z.union([import_zod4.z.literal("auto"), import_zod4.z.boolean()]).default("auto")
7530
+ gitleaks: import_zod6.z.object({
7531
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto")
5809
7532
  }).optional()
5810
7533
  }).optional()
5811
7534
  });
@@ -5838,15 +7561,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
5838
7561
  }
5839
7562
 
5840
7563
  // src/security/stack-detector.ts
5841
- var fs5 = __toESM(require("fs"));
7564
+ var fs7 = __toESM(require("fs"));
5842
7565
  var path4 = __toESM(require("path"));
5843
7566
  function detectStack(projectRoot) {
5844
7567
  const stacks = [];
5845
7568
  const pkgJsonPath = path4.join(projectRoot, "package.json");
5846
- if (fs5.existsSync(pkgJsonPath)) {
7569
+ if (fs7.existsSync(pkgJsonPath)) {
5847
7570
  stacks.push("node");
5848
7571
  try {
5849
- const pkgJson = JSON.parse(fs5.readFileSync(pkgJsonPath, "utf-8"));
7572
+ const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
5850
7573
  const allDeps = {
5851
7574
  ...pkgJson.dependencies,
5852
7575
  ...pkgJson.devDependencies
@@ -5862,12 +7585,12 @@ function detectStack(projectRoot) {
5862
7585
  }
5863
7586
  }
5864
7587
  const goModPath = path4.join(projectRoot, "go.mod");
5865
- if (fs5.existsSync(goModPath)) {
7588
+ if (fs7.existsSync(goModPath)) {
5866
7589
  stacks.push("go");
5867
7590
  }
5868
7591
  const requirementsPath = path4.join(projectRoot, "requirements.txt");
5869
7592
  const pyprojectPath = path4.join(projectRoot, "pyproject.toml");
5870
- if (fs5.existsSync(requirementsPath) || fs5.existsSync(pyprojectPath)) {
7593
+ if (fs7.existsSync(requirementsPath) || fs7.existsSync(pyprojectPath)) {
5871
7594
  stacks.push("python");
5872
7595
  }
5873
7596
  return stacks;
@@ -6294,7 +8017,7 @@ var SecurityScanner = class {
6294
8017
  }
6295
8018
  async scanFile(filePath) {
6296
8019
  if (!this.config.enabled) return [];
6297
- const content = await fs6.readFile(filePath, "utf-8");
8020
+ const content = await fs8.readFile(filePath, "utf-8");
6298
8021
  return this.scanContent(content, filePath, 1);
6299
8022
  }
6300
8023
  async scanFiles(filePaths) {
@@ -6327,7 +8050,8 @@ var ALL_CHECKS = [
6327
8050
  "entropy",
6328
8051
  "security",
6329
8052
  "perf",
6330
- "phase-gate"
8053
+ "phase-gate",
8054
+ "arch"
6331
8055
  ];
6332
8056
  async function runSingleCheck(name, projectRoot, config) {
6333
8057
  const start = Date.now();
@@ -6391,7 +8115,17 @@ async function runSingleCheck(name, projectRoot, config) {
6391
8115
  }
6392
8116
  case "docs": {
6393
8117
  const docsDir = path5.join(projectRoot, config.docsDir ?? "docs");
6394
- const result = await checkDocCoverage("project", { docsDir });
8118
+ const entropyConfig = config.entropy || {};
8119
+ const result = await checkDocCoverage("project", {
8120
+ docsDir,
8121
+ sourceDir: projectRoot,
8122
+ excludePatterns: entropyConfig.excludePatterns || [
8123
+ "**/node_modules/**",
8124
+ "**/dist/**",
8125
+ "**/*.test.ts",
8126
+ "**/fixtures/**"
8127
+ ]
8128
+ });
6395
8129
  if (!result.ok) {
6396
8130
  issues.push({ severity: "warning", message: result.error.message });
6397
8131
  } else if (result.value.gaps.length > 0) {
@@ -6466,11 +8200,13 @@ async function runSingleCheck(name, projectRoot, config) {
6466
8200
  break;
6467
8201
  }
6468
8202
  case "perf": {
8203
+ const perfConfig = config.performance || {};
6469
8204
  const perfAnalyzer = new EntropyAnalyzer({
6470
8205
  rootDir: projectRoot,
6471
8206
  analyze: {
6472
- complexity: true,
6473
- coupling: true
8207
+ complexity: perfConfig.complexity || true,
8208
+ coupling: perfConfig.coupling || true,
8209
+ sizeBudget: perfConfig.sizeBudget || false
6474
8210
  }
6475
8211
  });
6476
8212
  const perfResult = await perfAnalyzer.analyze();
@@ -6511,6 +8247,43 @@ async function runSingleCheck(name, projectRoot, config) {
6511
8247
  });
6512
8248
  break;
6513
8249
  }
8250
+ case "arch": {
8251
+ const rawArchConfig = config.architecture;
8252
+ const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
8253
+ if (!archConfig.enabled) break;
8254
+ const results = await runAll(archConfig, projectRoot);
8255
+ const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
8256
+ const baseline = baselineManager.load();
8257
+ if (baseline) {
8258
+ const diffResult = diff(results, baseline);
8259
+ if (!diffResult.passed) {
8260
+ for (const v of diffResult.newViolations) {
8261
+ issues.push({
8262
+ severity: v.severity,
8263
+ message: `[${v.category || "arch"}] NEW: ${v.detail}`,
8264
+ file: v.file
8265
+ });
8266
+ }
8267
+ for (const r of diffResult.regressions) {
8268
+ issues.push({
8269
+ severity: "error",
8270
+ message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
8271
+ });
8272
+ }
8273
+ }
8274
+ } else {
8275
+ for (const result of results) {
8276
+ for (const v of result.violations) {
8277
+ issues.push({
8278
+ severity: v.severity,
8279
+ message: `[${result.category}] ${v.detail}`,
8280
+ file: v.file
8281
+ });
8282
+ }
8283
+ }
8284
+ }
8285
+ break;
8286
+ }
6514
8287
  }
6515
8288
  } catch (error) {
6516
8289
  issues.push({
@@ -6845,22 +8618,22 @@ var PREFIX_PATTERNS = [
6845
8618
  ];
6846
8619
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|js|jsx|mts|cts)$/;
6847
8620
  var MD_FILE_PATTERN = /\.md$/;
6848
- function detectChangeType(commitMessage, diff) {
8621
+ function detectChangeType(commitMessage, diff2) {
6849
8622
  const trimmed = commitMessage.trim();
6850
8623
  for (const { pattern, type } of PREFIX_PATTERNS) {
6851
8624
  if (pattern.test(trimmed)) {
6852
8625
  return type;
6853
8626
  }
6854
8627
  }
6855
- if (diff.changedFiles.length > 0 && diff.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
8628
+ if (diff2.changedFiles.length > 0 && diff2.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
6856
8629
  return "docs";
6857
8630
  }
6858
- const newNonTestFiles = diff.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
8631
+ const newNonTestFiles = diff2.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
6859
8632
  if (newNonTestFiles.length > 0) {
6860
8633
  return "feature";
6861
8634
  }
6862
- const hasNewTestFile = diff.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
6863
- if (diff.totalDiffLines < 20 && hasNewTestFile) {
8635
+ const hasNewTestFile = diff2.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
8636
+ if (diff2.totalDiffLines < 20 && hasNewTestFile) {
6864
8637
  return "bugfix";
6865
8638
  }
6866
8639
  return "feature";
@@ -6889,7 +8662,7 @@ async function readContextFile(projectRoot, filePath, reason) {
6889
8662
  const relPath = path7.isAbsolute(filePath) ? path7.relative(projectRoot, filePath) : filePath;
6890
8663
  return { path: relPath, content, reason, lines };
6891
8664
  }
6892
- function extractImportSources(content) {
8665
+ function extractImportSources2(content) {
6893
8666
  const sources = [];
6894
8667
  const importRegex = /(?:import\s+(?:.*?\s+from\s+)?['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]\s*\))/g;
6895
8668
  let match;
@@ -6931,7 +8704,7 @@ async function gatherImportContext(projectRoot, changedFiles, budget) {
6931
8704
  const seen = new Set(changedFiles.map((f) => f.path));
6932
8705
  for (const cf of changedFiles) {
6933
8706
  if (linesGathered >= budget) break;
6934
- const sources = extractImportSources(cf.content);
8707
+ const sources = extractImportSources2(cf.content);
6935
8708
  for (const source of sources) {
6936
8709
  if (linesGathered >= budget) break;
6937
8710
  const resolved = await resolveImportPath2(projectRoot, cf.path, source);
@@ -7098,11 +8871,11 @@ async function scopeArchitectureContext(projectRoot, changedFiles, budget, optio
7098
8871
  return contextFiles;
7099
8872
  }
7100
8873
  async function scopeContext(options) {
7101
- const { projectRoot, diff, commitMessage } = options;
7102
- const changeType = detectChangeType(commitMessage, diff);
7103
- const budget = computeContextBudget(diff.totalDiffLines);
8874
+ const { projectRoot, diff: diff2, commitMessage } = options;
8875
+ const changeType = detectChangeType(commitMessage, diff2);
8876
+ const budget = computeContextBudget(diff2.totalDiffLines);
7104
8877
  const changedFiles = [];
7105
- for (const filePath of diff.changedFiles) {
8878
+ for (const filePath of diff2.changedFiles) {
7106
8879
  const cf = await readContextFile(projectRoot, filePath, "changed");
7107
8880
  if (cf) changedFiles.push(cf);
7108
8881
  }
@@ -7122,7 +8895,7 @@ async function scopeContext(options) {
7122
8895
  changedFiles: [...changedFiles],
7123
8896
  contextFiles,
7124
8897
  commitHistory: options.commitHistory ?? [],
7125
- diffLines: diff.totalDiffLines,
8898
+ diffLines: diff2.totalDiffLines,
7126
8899
  contextLines
7127
8900
  });
7128
8901
  }
@@ -8132,7 +9905,7 @@ function formatGitHubSummary(options) {
8132
9905
  async function runReviewPipeline(options) {
8133
9906
  const {
8134
9907
  projectRoot,
8135
- diff,
9908
+ diff: diff2,
8136
9909
  commitMessage,
8137
9910
  flags,
8138
9911
  graph,
@@ -8166,7 +9939,7 @@ async function runReviewPipeline(options) {
8166
9939
  const mechResult = await runMechanicalChecks({
8167
9940
  projectRoot,
8168
9941
  config,
8169
- changedFiles: diff.changedFiles
9942
+ changedFiles: diff2.changedFiles
8170
9943
  });
8171
9944
  if (mechResult.ok) {
8172
9945
  mechanicalResult = mechResult.value;
@@ -8205,7 +9978,7 @@ async function runReviewPipeline(options) {
8205
9978
  try {
8206
9979
  contextBundles = await scopeContext({
8207
9980
  projectRoot,
8208
- diff,
9981
+ diff: diff2,
8209
9982
  commitMessage,
8210
9983
  ...graph != null ? { graph } : {},
8211
9984
  ...conventionFiles != null ? { conventionFiles } : {},
@@ -8219,14 +9992,14 @@ async function runReviewPipeline(options) {
8219
9992
  changedFiles: [],
8220
9993
  contextFiles: [],
8221
9994
  commitHistory: [],
8222
- diffLines: diff.totalDiffLines,
9995
+ diffLines: diff2.totalDiffLines,
8223
9996
  contextLines: 0
8224
9997
  }));
8225
9998
  }
8226
9999
  const agentResults = await fanOutReview({ bundles: contextBundles });
8227
10000
  const rawFindings = agentResults.flatMap((r) => r.findings);
8228
10001
  const fileContents = /* @__PURE__ */ new Map();
8229
- for (const [file, content] of diff.fileDiffs) {
10002
+ for (const [file, content] of diff2.fileDiffs) {
8230
10003
  fileContents.set(file, content);
8231
10004
  }
8232
10005
  const validatedFindings = await validateFindings({
@@ -8262,7 +10035,7 @@ async function runReviewPipeline(options) {
8262
10035
  }
8263
10036
 
8264
10037
  // src/roadmap/parse.ts
8265
- var import_types9 = require("@harness-engineering/types");
10038
+ var import_types16 = require("@harness-engineering/types");
8266
10039
  var VALID_STATUSES = /* @__PURE__ */ new Set([
8267
10040
  "backlog",
8268
10041
  "planned",
@@ -8274,14 +10047,14 @@ var EM_DASH = "\u2014";
8274
10047
  function parseRoadmap(markdown) {
8275
10048
  const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
8276
10049
  if (!fmMatch) {
8277
- return (0, import_types9.Err)(new Error("Missing or malformed YAML frontmatter"));
10050
+ return (0, import_types16.Err)(new Error("Missing or malformed YAML frontmatter"));
8278
10051
  }
8279
10052
  const fmResult = parseFrontmatter(fmMatch[1]);
8280
10053
  if (!fmResult.ok) return fmResult;
8281
10054
  const body = markdown.slice(fmMatch[0].length);
8282
10055
  const milestonesResult = parseMilestones(body);
8283
10056
  if (!milestonesResult.ok) return milestonesResult;
8284
- return (0, import_types9.Ok)({
10057
+ return (0, import_types16.Ok)({
8285
10058
  frontmatter: fmResult.value,
8286
10059
  milestones: milestonesResult.value
8287
10060
  });
@@ -8301,7 +10074,7 @@ function parseFrontmatter(raw) {
8301
10074
  const lastSynced = map.get("last_synced");
8302
10075
  const lastManualEdit = map.get("last_manual_edit");
8303
10076
  if (!project || !versionStr || !lastSynced || !lastManualEdit) {
8304
- return (0, import_types9.Err)(
10077
+ return (0, import_types16.Err)(
8305
10078
  new Error(
8306
10079
  "Frontmatter missing required fields: project, version, last_synced, last_manual_edit"
8307
10080
  )
@@ -8309,9 +10082,9 @@ function parseFrontmatter(raw) {
8309
10082
  }
8310
10083
  const version = parseInt(versionStr, 10);
8311
10084
  if (isNaN(version)) {
8312
- return (0, import_types9.Err)(new Error("Frontmatter version must be a number"));
10085
+ return (0, import_types16.Err)(new Error("Frontmatter version must be a number"));
8313
10086
  }
8314
- return (0, import_types9.Ok)({ project, version, lastSynced, lastManualEdit });
10087
+ return (0, import_types16.Ok)({ project, version, lastSynced, lastManualEdit });
8315
10088
  }
8316
10089
  function parseMilestones(body) {
8317
10090
  const milestones = [];
@@ -8335,7 +10108,7 @@ function parseMilestones(body) {
8335
10108
  features: featuresResult.value
8336
10109
  });
8337
10110
  }
8338
- return (0, import_types9.Ok)(milestones);
10111
+ return (0, import_types16.Ok)(milestones);
8339
10112
  }
8340
10113
  function parseFeatures(sectionBody) {
8341
10114
  const features = [];
@@ -8356,7 +10129,7 @@ function parseFeatures(sectionBody) {
8356
10129
  if (!featureResult.ok) return featureResult;
8357
10130
  features.push(featureResult.value);
8358
10131
  }
8359
- return (0, import_types9.Ok)(features);
10132
+ return (0, import_types16.Ok)(features);
8360
10133
  }
8361
10134
  function parseFeatureFields(name, body) {
8362
10135
  const fieldMap = /* @__PURE__ */ new Map();
@@ -8367,7 +10140,7 @@ function parseFeatureFields(name, body) {
8367
10140
  }
8368
10141
  const statusRaw = fieldMap.get("Status");
8369
10142
  if (!statusRaw || !VALID_STATUSES.has(statusRaw)) {
8370
- return (0, import_types9.Err)(
10143
+ return (0, import_types16.Err)(
8371
10144
  new Error(
8372
10145
  `Feature "${name}" has invalid status: "${statusRaw ?? "(missing)"}". Valid statuses: ${[...VALID_STATUSES].join(", ")}`
8373
10146
  )
@@ -8381,7 +10154,7 @@ function parseFeatureFields(name, body) {
8381
10154
  const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
8382
10155
  const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
8383
10156
  const summary = fieldMap.get("Summary") ?? "";
8384
- return (0, import_types9.Ok)({ name, status, spec, plans, blockedBy, summary });
10157
+ return (0, import_types16.Ok)({ name, status, spec, plans, blockedBy, summary });
8385
10158
  }
8386
10159
 
8387
10160
  // src/roadmap/serialize.ts
@@ -8425,9 +10198,9 @@ function serializeFeature(feature) {
8425
10198
  }
8426
10199
 
8427
10200
  // src/roadmap/sync.ts
8428
- var fs7 = __toESM(require("fs"));
10201
+ var fs9 = __toESM(require("fs"));
8429
10202
  var path9 = __toESM(require("path"));
8430
- var import_types10 = require("@harness-engineering/types");
10203
+ var import_types17 = require("@harness-engineering/types");
8431
10204
  function inferStatus(feature, projectPath, allFeatures) {
8432
10205
  if (feature.blockedBy.length > 0) {
8433
10206
  const blockerNotDone = feature.blockedBy.some((blockerName) => {
@@ -8442,9 +10215,9 @@ function inferStatus(feature, projectPath, allFeatures) {
8442
10215
  const useRootState = featuresWithPlans.length <= 1;
8443
10216
  if (useRootState) {
8444
10217
  const rootStatePath = path9.join(projectPath, ".harness", "state.json");
8445
- if (fs7.existsSync(rootStatePath)) {
10218
+ if (fs9.existsSync(rootStatePath)) {
8446
10219
  try {
8447
- const raw = fs7.readFileSync(rootStatePath, "utf-8");
10220
+ const raw = fs9.readFileSync(rootStatePath, "utf-8");
8448
10221
  const state = JSON.parse(raw);
8449
10222
  if (state.progress) {
8450
10223
  for (const status of Object.values(state.progress)) {
@@ -8456,15 +10229,15 @@ function inferStatus(feature, projectPath, allFeatures) {
8456
10229
  }
8457
10230
  }
8458
10231
  const sessionsDir = path9.join(projectPath, ".harness", "sessions");
8459
- if (fs7.existsSync(sessionsDir)) {
10232
+ if (fs9.existsSync(sessionsDir)) {
8460
10233
  try {
8461
- const sessionDirs = fs7.readdirSync(sessionsDir, { withFileTypes: true });
10234
+ const sessionDirs = fs9.readdirSync(sessionsDir, { withFileTypes: true });
8462
10235
  for (const entry of sessionDirs) {
8463
10236
  if (!entry.isDirectory()) continue;
8464
10237
  const autopilotPath = path9.join(sessionsDir, entry.name, "autopilot-state.json");
8465
- if (!fs7.existsSync(autopilotPath)) continue;
10238
+ if (!fs9.existsSync(autopilotPath)) continue;
8466
10239
  try {
8467
- const raw = fs7.readFileSync(autopilotPath, "utf-8");
10240
+ const raw = fs9.readFileSync(autopilotPath, "utf-8");
8468
10241
  const autopilot = JSON.parse(raw);
8469
10242
  if (!autopilot.phases) continue;
8470
10243
  const linkedPhases = autopilot.phases.filter(
@@ -8511,46 +10284,188 @@ function syncRoadmap(options) {
8511
10284
  to: inferred
8512
10285
  });
8513
10286
  }
8514
- return (0, import_types10.Ok)(changes);
10287
+ return (0, import_types17.Ok)(changes);
8515
10288
  }
8516
10289
 
8517
10290
  // 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()
10291
+ var import_zod7 = require("zod");
10292
+ var InteractionTypeSchema = import_zod7.z.enum(["question", "confirmation", "transition"]);
10293
+ var QuestionSchema = import_zod7.z.object({
10294
+ text: import_zod7.z.string(),
10295
+ options: import_zod7.z.array(import_zod7.z.string()).optional(),
10296
+ default: import_zod7.z.string().optional()
8524
10297
  });
8525
- var ConfirmationSchema = import_zod5.z.object({
8526
- text: import_zod5.z.string(),
8527
- context: import_zod5.z.string()
10298
+ var ConfirmationSchema = import_zod7.z.object({
10299
+ text: import_zod7.z.string(),
10300
+ context: import_zod7.z.string()
8528
10301
  });
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()
10302
+ var TransitionSchema = import_zod7.z.object({
10303
+ completedPhase: import_zod7.z.string(),
10304
+ suggestedNext: import_zod7.z.string(),
10305
+ reason: import_zod7.z.string(),
10306
+ artifacts: import_zod7.z.array(import_zod7.z.string()),
10307
+ requiresConfirmation: import_zod7.z.boolean(),
10308
+ summary: import_zod7.z.string()
8536
10309
  });
8537
- var EmitInteractionInputSchema = import_zod5.z.object({
8538
- path: import_zod5.z.string(),
10310
+ var EmitInteractionInputSchema = import_zod7.z.object({
10311
+ path: import_zod7.z.string(),
8539
10312
  type: InteractionTypeSchema,
8540
- stream: import_zod5.z.string().optional(),
10313
+ stream: import_zod7.z.string().optional(),
8541
10314
  question: QuestionSchema.optional(),
8542
10315
  confirmation: ConfirmationSchema.optional(),
8543
10316
  transition: TransitionSchema.optional()
8544
10317
  });
8545
10318
 
8546
- // src/update-checker.ts
8547
- var fs8 = __toESM(require("fs"));
10319
+ // src/blueprint/scanner.ts
10320
+ var fs10 = __toESM(require("fs/promises"));
8548
10321
  var path10 = __toESM(require("path"));
10322
+ var ProjectScanner = class {
10323
+ constructor(rootDir) {
10324
+ this.rootDir = rootDir;
10325
+ }
10326
+ async scan() {
10327
+ let projectName = path10.basename(this.rootDir);
10328
+ try {
10329
+ const pkgPath = path10.join(this.rootDir, "package.json");
10330
+ const pkgRaw = await fs10.readFile(pkgPath, "utf-8");
10331
+ const pkg = JSON.parse(pkgRaw);
10332
+ if (pkg.name) projectName = pkg.name;
10333
+ } catch {
10334
+ }
10335
+ return {
10336
+ projectName,
10337
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10338
+ modules: [
10339
+ {
10340
+ id: "foundations",
10341
+ title: "Foundations",
10342
+ description: "Utility files and basic types.",
10343
+ files: []
10344
+ },
10345
+ {
10346
+ id: "core-logic",
10347
+ title: "Core Logic",
10348
+ description: "Mid-level services and domain logic.",
10349
+ files: []
10350
+ },
10351
+ {
10352
+ id: "interaction-surface",
10353
+ title: "Interaction Surface",
10354
+ description: "APIs, CLIs, and Entry Points.",
10355
+ files: []
10356
+ },
10357
+ {
10358
+ id: "cross-cutting",
10359
+ title: "Cross-Cutting Concerns",
10360
+ description: "Security, Logging, and Observability.",
10361
+ files: []
10362
+ }
10363
+ ],
10364
+ hotspots: [],
10365
+ dependencies: []
10366
+ };
10367
+ }
10368
+ };
10369
+
10370
+ // src/blueprint/generator.ts
10371
+ var fs11 = __toESM(require("fs/promises"));
10372
+ var path11 = __toESM(require("path"));
10373
+ var ejs = __toESM(require("ejs"));
10374
+
10375
+ // src/blueprint/templates.ts
10376
+ var SHELL_TEMPLATE = `
10377
+ <!DOCTYPE html>
10378
+ <html lang="en">
10379
+ <head>
10380
+ <meta charset="UTF-8">
10381
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10382
+ <title>Blueprint: <%= projectName %></title>
10383
+ <style><%- styles %></style>
10384
+ </head>
10385
+ <body>
10386
+ <div id="app">
10387
+ <header>
10388
+ <h1>Blueprint: <%= projectName %></h1>
10389
+ <p>Generated at: <%= generatedAt %></p>
10390
+ </header>
10391
+ <main>
10392
+ <section class="modules">
10393
+ <% modules.forEach(module => { %>
10394
+ <article class="module" id="<%= module.id %>">
10395
+ <h2><%= module.title %></h2>
10396
+ <p><%= module.description %></p>
10397
+ <div class="content">
10398
+ <h3>Code Translation</h3>
10399
+ <div class="translation"><%- module.content.codeTranslation %></div>
10400
+ </div>
10401
+ </article>
10402
+
10403
+ <% }) %>
10404
+ </section>
10405
+ </main>
10406
+ </div>
10407
+ <script><%- scripts %></script>
10408
+ </body>
10409
+ </html>
10410
+ `;
10411
+ var STYLES = `
10412
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; margin: 0; padding: 20px; color: #333; }
10413
+ header { border-bottom: 2px solid #eee; margin-bottom: 20px; padding-bottom: 10px; }
10414
+ .module { background: #f9f9f9; border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; border-radius: 4px; }
10415
+ .module h2 { margin-top: 0; color: #0066cc; }
10416
+ `;
10417
+ var SCRIPTS = `
10418
+ console.log('Blueprint player initialized.');
10419
+ `;
10420
+
10421
+ // src/shared/llm.ts
10422
+ var MockLLMService = class {
10423
+ async generate(prompt) {
10424
+ return "This is a mock LLM response for: " + prompt;
10425
+ }
10426
+ };
10427
+ var llmService = new MockLLMService();
10428
+
10429
+ // src/blueprint/content-pipeline.ts
10430
+ var ContentPipeline = class {
10431
+ async generateModuleContent(module2) {
10432
+ const codeContext = module2.files.join("\n");
10433
+ const translation = await llmService.generate(
10434
+ `You are a technical educator. Explain the following code clearly and concisely: ${codeContext}`
10435
+ );
10436
+ return {
10437
+ codeTranslation: translation
10438
+ };
10439
+ }
10440
+ };
10441
+
10442
+ // src/blueprint/generator.ts
10443
+ var BlueprintGenerator = class {
10444
+ contentPipeline = new ContentPipeline();
10445
+ async generate(data, options) {
10446
+ await Promise.all(
10447
+ data.modules.map(async (module2) => {
10448
+ module2.content = await this.contentPipeline.generateModuleContent(module2);
10449
+ })
10450
+ );
10451
+ const html = ejs.render(SHELL_TEMPLATE, {
10452
+ ...data,
10453
+ styles: STYLES,
10454
+ scripts: SCRIPTS
10455
+ });
10456
+ await fs11.mkdir(options.outputDir, { recursive: true });
10457
+ await fs11.writeFile(path11.join(options.outputDir, "index.html"), html);
10458
+ }
10459
+ };
10460
+
10461
+ // src/update-checker.ts
10462
+ var fs12 = __toESM(require("fs"));
10463
+ var path12 = __toESM(require("path"));
8549
10464
  var os = __toESM(require("os"));
8550
10465
  var import_child_process3 = require("child_process");
8551
10466
  function getStatePath() {
8552
10467
  const home = process.env["HOME"] || os.homedir();
8553
- return path10.join(home, ".harness", "update-check.json");
10468
+ return path12.join(home, ".harness", "update-check.json");
8554
10469
  }
8555
10470
  function isUpdateCheckEnabled(configInterval) {
8556
10471
  if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
@@ -8563,7 +10478,7 @@ function shouldRunCheck(state, intervalMs) {
8563
10478
  }
8564
10479
  function readCheckState() {
8565
10480
  try {
8566
- const raw = fs8.readFileSync(getStatePath(), "utf-8");
10481
+ const raw = fs12.readFileSync(getStatePath(), "utf-8");
8567
10482
  const parsed = JSON.parse(raw);
8568
10483
  if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
8569
10484
  const state = parsed;
@@ -8580,7 +10495,7 @@ function readCheckState() {
8580
10495
  }
8581
10496
  function spawnBackgroundCheck(currentVersion) {
8582
10497
  const statePath = getStatePath();
8583
- const stateDir = path10.dirname(statePath);
10498
+ const stateDir = path12.dirname(statePath);
8584
10499
  const script = `
8585
10500
  const { execSync } = require('child_process');
8586
10501
  const fs = require('fs');
@@ -8634,39 +10549,64 @@ Run "harness update" to upgrade.`;
8634
10549
  }
8635
10550
 
8636
10551
  // src/index.ts
8637
- var VERSION = "1.8.2";
10552
+ var VERSION = "0.11.0";
8638
10553
  // Annotate the CommonJS export names for ESM import in node:
8639
10554
  0 && (module.exports = {
8640
10555
  AGENT_DESCRIPTORS,
8641
10556
  ARCHITECTURE_DESCRIPTOR,
8642
10557
  AgentActionEmitter,
10558
+ ArchBaselineManager,
10559
+ ArchBaselineSchema,
10560
+ ArchConfigSchema,
10561
+ ArchDiffResultSchema,
10562
+ ArchMetricCategorySchema,
8643
10563
  BUG_DETECTION_DESCRIPTOR,
8644
10564
  BaselineManager,
8645
10565
  BenchmarkRunner,
10566
+ BlueprintGenerator,
10567
+ BundleConstraintsSchema,
10568
+ BundleSchema,
8646
10569
  COMPLIANCE_DESCRIPTOR,
10570
+ CategoryBaselineSchema,
10571
+ CategoryRegressionSchema,
8647
10572
  ChecklistBuilder,
10573
+ CircularDepsCollector,
10574
+ ComplexityCollector,
8648
10575
  ConfirmationSchema,
8649
10576
  ConsoleSink,
10577
+ ConstraintRuleSchema,
10578
+ ContentPipeline,
10579
+ ContributionsSchema,
10580
+ CouplingCollector,
8650
10581
  CriticalPathResolver,
8651
10582
  DEFAULT_PROVIDER_TIERS,
8652
10583
  DEFAULT_SECURITY_CONFIG,
8653
10584
  DEFAULT_STATE,
8654
10585
  DEFAULT_STREAM_INDEX,
10586
+ DepDepthCollector,
8655
10587
  EmitInteractionInputSchema,
8656
10588
  EntropyAnalyzer,
8657
10589
  EntropyConfigSchema,
8658
10590
  ExclusionSet,
8659
10591
  FailureEntrySchema,
8660
10592
  FileSink,
10593
+ ForbiddenImportCollector,
8661
10594
  GateConfigSchema,
8662
10595
  GateResultSchema,
8663
10596
  HandoffSchema,
8664
10597
  HarnessStateSchema,
8665
10598
  InteractionTypeSchema,
10599
+ LayerViolationCollector,
10600
+ LockfilePackageSchema,
10601
+ LockfileSchema,
10602
+ ManifestSchema,
10603
+ MetricResultSchema,
10604
+ ModuleSizeCollector,
8666
10605
  NoOpExecutor,
8667
10606
  NoOpSink,
8668
10607
  NoOpTelemetryAdapter,
8669
10608
  PatternConfigSchema,
10609
+ ProjectScanner,
8670
10610
  QuestionSchema,
8671
10611
  REQUIRED_SECTIONS,
8672
10612
  RegressionDetector,
@@ -8674,16 +10614,26 @@ var VERSION = "1.8.2";
8674
10614
  SECURITY_DESCRIPTOR,
8675
10615
  SecurityConfigSchema,
8676
10616
  SecurityScanner,
10617
+ SharableBoundaryConfigSchema,
10618
+ SharableForbiddenImportSchema,
10619
+ SharableLayerSchema,
10620
+ SharableSecurityRulesSchema,
8677
10621
  StreamIndexSchema,
8678
10622
  StreamInfoSchema,
10623
+ ThresholdConfigSchema,
8679
10624
  TransitionSchema,
8680
10625
  TypeScriptParser,
8681
10626
  VERSION,
10627
+ ViolationSchema,
10628
+ addProvenance,
8682
10629
  analyzeDiff,
8683
10630
  appendFailure,
8684
10631
  appendLearning,
8685
10632
  applyFixes,
8686
10633
  applyHotspotDowngrade,
10634
+ archMatchers,
10635
+ archModule,
10636
+ architecture,
8687
10637
  archiveFailures,
8688
10638
  archiveStream,
8689
10639
  buildDependencyGraph,
@@ -8693,6 +10643,7 @@ var VERSION = "1.8.2";
8693
10643
  checkEligibility,
8694
10644
  classifyFinding,
8695
10645
  configureFeedback,
10646
+ constraintRuleId,
8696
10647
  contextBudget,
8697
10648
  contextFilter,
8698
10649
  createBoundaryValidator,
@@ -8707,6 +10658,8 @@ var VERSION = "1.8.2";
8707
10658
  cryptoRules,
8708
10659
  deduplicateCleanupFindings,
8709
10660
  deduplicateFindings,
10661
+ deepMergeConstraints,
10662
+ defaultCollectors,
8710
10663
  defineLayer,
8711
10664
  deserializationRules,
8712
10665
  detectChangeType,
@@ -8719,9 +10672,12 @@ var VERSION = "1.8.2";
8719
10672
  detectPatternViolations,
8720
10673
  detectSizeBudgetViolations,
8721
10674
  detectStack,
10675
+ detectStaleConstraints,
8722
10676
  determineAssessment,
10677
+ diff,
8723
10678
  executeWorkflow,
8724
10679
  expressRules,
10680
+ extractBundle,
8725
10681
  extractMarkdownLinks,
8726
10682
  extractSections,
8727
10683
  fanOutReview,
@@ -8752,6 +10708,7 @@ var VERSION = "1.8.2";
8752
10708
  networkRules,
8753
10709
  nodeRules,
8754
10710
  parseDiff,
10711
+ parseManifest,
8755
10712
  parseRoadmap,
8756
10713
  parseSecurityConfig,
8757
10714
  parseSize,
@@ -8759,6 +10716,9 @@ var VERSION = "1.8.2";
8759
10716
  previewFix,
8760
10717
  reactRules,
8761
10718
  readCheckState,
10719
+ readLockfile,
10720
+ removeContributions,
10721
+ removeProvenance,
8762
10722
  requestMultiplePeerReviews,
8763
10723
  requestPeerReview,
8764
10724
  resetFeedbackConfig,
@@ -8766,6 +10726,8 @@ var VERSION = "1.8.2";
8766
10726
  resolveModelTier,
8767
10727
  resolveRuleSeverity,
8768
10728
  resolveStreamPath,
10729
+ resolveThresholds,
10730
+ runAll,
8769
10731
  runArchitectureAgent,
8770
10732
  runBugDetectionAgent,
8771
10733
  runCIChecks,
@@ -8785,6 +10747,7 @@ var VERSION = "1.8.2";
8785
10747
  setActiveStream,
8786
10748
  shouldRunCheck,
8787
10749
  spawnBackgroundCheck,
10750
+ syncConstraintNodes,
8788
10751
  syncRoadmap,
8789
10752
  touchStream,
8790
10753
  trackAction,
@@ -8797,6 +10760,9 @@ var VERSION = "1.8.2";
8797
10760
  validateFindings,
8798
10761
  validateKnowledgeMap,
8799
10762
  validatePatternConfig,
10763
+ violationId,
10764
+ writeConfig,
10765
+ writeLockfile,
8800
10766
  xssRules,
8801
10767
  ...require("@harness-engineering/types")
8802
10768
  });