@archpilotlabs/archpilot 0.2.4 → 0.2.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,65 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.6
4
+
5
+ ### Improved
6
+
7
+ - Improved Smart Init component and repository topology classification across complex monorepos and multi-module repositories.
8
+ - Improved component classification accuracy across TypeScript/NestJS, Java/Spring, frontend, backend, library, multi-module, and mixed repository structures.
9
+ - Improved API-design analysis precision for terminal or unconditional collection endpoints.
10
+ - Improved event-architecture analysis for Spring listener declarations and inherited event types.
11
+ - Improved data-query-risk handling for real-world SQL, JPQL, Spring Data repository, and query-builder patterns.
12
+ - Bounded data-query-risk analysis for complex Java/Spring repository declarations to avoid pathological validation time.
13
+ - Expanded regression coverage from validation against multiple real public repository shapes.
14
+
15
+ ### Fixed
16
+
17
+ - Fixed runtime Nx projects and shared libraries being missed or misclassified in package-layout workspaces.
18
+ - Fixed Maven runtime, library, support, distribution, and deploy-skipped modules being over- or under-promoted during Smart Init.
19
+ - Fixed file-like candidate paths aborting Smart Init traversal.
20
+ - Fixed terminal throw-only collection endpoints being reported as missing pagination.
21
+ - Fixed Spring event listeners using annotation attributes, `ApplicationListener<T>`, or parent event types being treated as unhandled.
22
+ - Fixed bounded Spring Data repository queries with `Pageable` or `Limit` parameters being reported as unbounded collection reads.
23
+ - Fixed SQL-looking comments, non-query files, and Spring configuration files contributing noisy data-query-risk findings.
24
+
25
+ ### Notes
26
+
27
+ - This release focuses on public-repository model accuracy and analyzer correctness.
28
+ - These changes improve model and finding accuracy without changing rule IDs, severities, score weights, scoring caps, or normalization behavior.
29
+ - Existing architecture configurations remain backward compatible.
30
+
31
+ ---
32
+
33
+ ## 0.2.5
34
+
35
+ ### Improved
36
+
37
+ - Improved Smart Init classification for mixed backend, frontend, and library monorepos.
38
+ - Improved NestJS detection so runtime applications with package publishing metadata are classified as backend services, while reusable NestJS packages remain libraries.
39
+ - Improved component discovery so test-only packages, build and lint tooling, and scaffold templates are excluded from default governed architecture boundaries.
40
+ - Improved backend resource detection for GraphQL APIs and PostgreSQL databases.
41
+ - Improved data-query-risk analysis for parameterized and interpolated query limits, PostgreSQL catalog lookups, and existence queries.
42
+ - Improved source-scope handling for authorization analysis and controller-role detection.
43
+ - Improved CLI version reporting in bundled and packed executables.
44
+
45
+ ### Fixed
46
+
47
+ - Fixed NestJS backend applications being classified only as Node.js libraries when package entry points or publishing metadata were present.
48
+ - Fixed Playwright test packages, lint-rule packages, build tooling, and nested scaffold templates being proposed as governed components.
49
+ - Fixed authorization findings being emitted from confirmed specification and test source.
50
+ - Fixed bounded SQL queries using parameterized or interpolated `LIMIT` values being reported as unbounded collection reads.
51
+ - Fixed PostgreSQL catalog identity and existence lookups being treated as application collection queries.
52
+ - Fixed generic `handlers` directories being interpreted as controller layers without structural controller evidence.
53
+ - Fixed bundled CLI artifacts reporting a stale version because package metadata could not be resolved at runtime.
54
+
55
+ ### Notes
56
+
57
+ - These changes improve model and finding accuracy without changing rule IDs, severities, score weights, scoring caps, or normalization behavior.
58
+ - Oversized services, direct persistence access, unbounded collection processing, repeated query patterns, and dependency-boundary violations remain reportable when supported by credible evidence.
59
+ - Documentation, examples, integration tests, and tooling remain governed when explicitly included by the generated or reviewed architecture configuration.
60
+
61
+ ---
62
+
3
63
  ## 0.2.4
4
64
 
5
65
  ### Improved
@@ -12907,7 +12907,7 @@ ${lanes.join("\n")}
12907
12907
  return process.memoryUsage().heapUsed;
12908
12908
  },
12909
12909
  getFileSize(path74) {
12910
- const stat = statSync(path74);
12910
+ const stat = statSync2(path74);
12911
12911
  if (stat == null ? void 0 : stat.isFile()) {
12912
12912
  return stat.size;
12913
12913
  }
@@ -12951,7 +12951,7 @@ ${lanes.join("\n")}
12951
12951
  }
12952
12952
  };
12953
12953
  return nodeSystem;
12954
- function statSync(path74) {
12954
+ function statSync2(path74) {
12955
12955
  try {
12956
12956
  return _fs.statSync(path74, statSyncOptions);
12957
12957
  } catch {
@@ -13010,7 +13010,7 @@ ${lanes.join("\n")}
13010
13010
  activeSession.post("Profiler.stop", (err, { profile }) => {
13011
13011
  var _a;
13012
13012
  if (!err) {
13013
- if ((_a = statSync(profilePath)) == null ? void 0 : _a.isDirectory()) {
13013
+ if ((_a = statSync2(profilePath)) == null ? void 0 : _a.isDirectory()) {
13014
13014
  profilePath = _path.join(profilePath, `${(/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-")}+P${process.pid}.cpuprofile`);
13015
13015
  }
13016
13016
  try {
@@ -13130,7 +13130,7 @@ ${lanes.join("\n")}
13130
13130
  let stat;
13131
13131
  if (typeof dirent === "string" || dirent.isSymbolicLink()) {
13132
13132
  const name = combinePaths(path74, entry);
13133
- stat = statSync(name);
13133
+ stat = statSync2(name);
13134
13134
  if (!stat) {
13135
13135
  continue;
13136
13136
  }
@@ -13154,7 +13154,7 @@ ${lanes.join("\n")}
13154
13154
  return matchFiles(path74, extensions, excludes, includes, useCaseSensitiveFileNames2, process.cwd(), depth, getAccessibleFileSystemEntries, realpath);
13155
13155
  }
13156
13156
  function fileSystemEntryExists(path74, entryKind) {
13157
- const stat = statSync(path74);
13157
+ const stat = statSync2(path74);
13158
13158
  if (!stat) {
13159
13159
  return false;
13160
13160
  }
@@ -13196,7 +13196,7 @@ ${lanes.join("\n")}
13196
13196
  }
13197
13197
  function getModifiedTime3(path74) {
13198
13198
  var _a;
13199
- return (_a = statSync(path74)) == null ? void 0 : _a.mtime;
13199
+ return (_a = statSync2(path74)) == null ? void 0 : _a.mtime;
13200
13200
  }
13201
13201
  function setModifiedTime(path74, time) {
13202
13202
  try {
@@ -225435,6 +225435,7 @@ init_sourceScopeClassification();
225435
225435
  var maxFileSizeBytes = 256 * 1024;
225436
225436
  var supplementalRiskScanExtensions = [".json", ".yaml", ".yml", ".sql"];
225437
225437
  var backendCodeExtensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".java", ".cs", ".py", ".php", ".go"];
225438
+ var methodBodyScanExtensions = new Set(backendCodeExtensions);
225438
225439
  var supplementalIgnoredDirectoryNames = [".vscode-test", ".tmp"];
225439
225440
  var batchingEvidencePattern = /\b(?:DataLoader|Promise\.all|findMany\s*\([\s\S]{0,300}\bin\b|where\s*:\s*\{[\s\S]{0,300}\bin\s*:|include\s*:|join\s*\(|leftJoin|innerJoin|JOIN\b)\b/iu;
225440
225441
  var queryReadPattern = /\b((?:this\.)?(?:prisma\.(\w+)|([A-Za-z_$][\w$]*Repository|repository))\.(find\w*|count|aggregate|groupBy)\s*\()/giu;
@@ -225443,6 +225444,7 @@ var boundEvidencePattern = /\b(?:take|limit|page|cursor|skip|offset|PageRequest|
225443
225444
  var repeatedQueryPattern = /\b((?:this\.)?(?:prisma\.(\w+)|(\w*Repository|repository))\.(find\w*|count|aggregate|groupBy)\s*\(\s*(?:\{[\s\S]{0,300}?\}|\w+)\s*\))/giu;
225444
225445
  var tenantPredicatePattern = /\b(?:tenantId|tenant_id|organizationId|organization_id|orgId|org_id|accountId|account_id|workspaceId|workspace_id|companyId|company_id|customerId|customer_id)\b/u;
225445
225446
  var codePaginationEvidencePattern = /\b(?:take|skip|limit|offset|page|perPage|pageSize|cursor|Pageable|PageRequest|paginate|simplePaginate|Limit|Offset|Take|Skip|TOP\s+\d+)\b/iu;
225447
+ var sqlQueryInvocationHintPattern = /\b(?:query|execute|executeQuery|createNativeQuery|createQuery|FromSqlRaw|FromSqlInterpolated|Query|QueryContext|QueryRow|QueryRowContext|Exec|ExecContext|Raw|text|DB::select|DB::statement|DB::raw|DB::table)\s*\(|\$queryRaw\s*\(|\$executeRaw\s*\(|@Query\s*\(/u;
225446
225448
  function getLineStarts(file) {
225447
225449
  if (file.lineStarts) {
225448
225450
  return file.lineStarts;
@@ -225656,21 +225658,38 @@ function hasSelectStar(statement) {
225656
225658
  return /\bselect\s+(distinct\s+)?\*/i.test(statement);
225657
225659
  }
225658
225660
  function hasPaginationEvidence(statement) {
225659
- return /\blimit\s+\d+\b|\boffset\s+\d+\b|\bfetch\s+(first|next)\b|\btop\s+\d+\b/i.test(
225661
+ const boundValuePattern = "(?:\\d+|\\$\\d+|\\?|:[A-Za-z_]\\w*|@[A-Za-z_]\\w*|\\$\\{[^}]{1,120}\\}|[A-Za-z_]\\w*)";
225662
+ const valueTerminatorPattern = "(?=\\s|,|;|\\)|$)";
225663
+ return new RegExp(
225664
+ `\\blimit\\s+${boundValuePattern}${valueTerminatorPattern}|\\boffset\\s+${boundValuePattern}${valueTerminatorPattern}|\\bfetch\\s+(first|next)\\b|\\btop\\s+${boundValuePattern}${valueTerminatorPattern}`,
225665
+ "i"
225666
+ ).test(
225660
225667
  statement
225661
225668
  );
225662
225669
  }
225663
225670
  function hasEqualityPredicate(statement, columnPattern) {
225671
+ const whereMatch = /\bwhere\b/iu.exec(statement);
225672
+ if (!whereMatch) {
225673
+ return false;
225674
+ }
225675
+ const predicateWindow = statement.slice(whereMatch.index, whereMatch.index + 1200);
225664
225676
  return new RegExp(
225665
- `\\bwhere\\b[\\s\\S]*\\b(?:${columnPattern})\\b\\s*=\\s*(?:[\\w$:@?]+|'[^']+'|"[^"]+"|\\d+)`,
225677
+ `\\b(?:${columnPattern})\\b\\s*=\\s*(?:[\\w$:@?]+|'[^']+'|"[^"]+"|\\d+)`,
225666
225678
  "iu"
225667
- ).test(statement);
225679
+ ).test(predicateWindow);
225668
225680
  }
225669
225681
  function isCatalogIdentifierPointLookup(statement) {
225670
- return /\bfrom\s+information_schema\.schemata\b/iu.test(statement) && hasEqualityPredicate(statement, "schema_name");
225682
+ const metadataSourcePattern = /\bfrom\s+(?:information_schema\.(?:schemata|tables|columns|key_column_usage|table_constraints)|pg_catalog\.(?:pg_class|pg_namespace|pg_attribute|pg_constraint|pg_type|pg_enum|pg_tables|pg_indexes)|pg_(?:class|namespace|attribute|constraint|type|enum|tables|indexes))\b/iu;
225683
+ if (!metadataSourcePattern.test(statement)) {
225684
+ return false;
225685
+ }
225686
+ return hasEqualityPredicate(statement, "schema_name|table_schema|table_name|column_name|constraint_name") || hasEqualityPredicate(statement, "schemaname|tablename|relname|nspname|attname|conname|typname|enumlabel|indexname");
225671
225687
  }
225672
225688
  function isObviousPointLookup(statement) {
225673
225689
  const normalized = statement.toLowerCase();
225690
+ if (/\bselect\s+exists\s*\(/i.test(normalized)) {
225691
+ return true;
225692
+ }
225674
225693
  if (!normalized.includes("where")) {
225675
225694
  return false;
225676
225695
  }
@@ -226258,6 +226277,9 @@ function detectRepeatedQueryPattern(method) {
226258
226277
  }
226259
226278
  function extractSqlQueryLiterals(content) {
226260
226279
  const queries = [];
226280
+ if (!sqlQueryInvocationHintPattern.test(content)) {
226281
+ return queries;
226282
+ }
226261
226283
  const searchableContent = maskSourceComments(content);
226262
226284
  const patterns = [
226263
226285
  /(?:\b(?:[\w$]+\.)?(?:query|execute|executeQuery|createNativeQuery|createQuery|FromSqlRaw|FromSqlInterpolated|Query|QueryContext|QueryRow|QueryRowContext|Exec|ExecContext|Raw|text)|\$queryRaw|\$executeRaw)\s*\([^'"`)]{0,180}?(['"`])([\s\S]{0,900}?)\1/giu,
@@ -226279,7 +226301,7 @@ function extractSqlQueryLiterals(content) {
226279
226301
  return queries;
226280
226302
  }
226281
226303
  function maskSourceComments(content) {
226282
- let masked = "";
226304
+ const masked = [];
226283
226305
  let index = 0;
226284
226306
  let state = "code";
226285
226307
  let escaped = false;
@@ -226288,27 +226310,27 @@ function maskSourceComments(content) {
226288
226310
  const next = content[index + 1] ?? "";
226289
226311
  if (state === "line-comment") {
226290
226312
  if (current === "\r" || current === "\n") {
226291
- masked += current;
226313
+ masked.push(current);
226292
226314
  state = "code";
226293
226315
  } else {
226294
- masked += " ";
226316
+ masked.push(" ");
226295
226317
  }
226296
226318
  index += 1;
226297
226319
  continue;
226298
226320
  }
226299
226321
  if (state === "block-comment") {
226300
226322
  if (current === "*" && next === "/") {
226301
- masked += " ";
226323
+ masked.push(" ");
226302
226324
  index += 2;
226303
226325
  state = "code";
226304
226326
  continue;
226305
226327
  }
226306
- masked += current === "\r" || current === "\n" ? current : " ";
226328
+ masked.push(current === "\r" || current === "\n" ? current : " ");
226307
226329
  index += 1;
226308
226330
  continue;
226309
226331
  }
226310
226332
  if (state === "single" || state === "double" || state === "template") {
226311
- masked += current;
226333
+ masked.push(current);
226312
226334
  if (escaped) {
226313
226335
  escaped = false;
226314
226336
  } else if (current === "\\") {
@@ -226320,13 +226342,13 @@ function maskSourceComments(content) {
226320
226342
  continue;
226321
226343
  }
226322
226344
  if (current === "/" && next === "/") {
226323
- masked += " ";
226345
+ masked.push(" ");
226324
226346
  index += 2;
226325
226347
  state = "line-comment";
226326
226348
  continue;
226327
226349
  }
226328
226350
  if (current === "/" && next === "*") {
226329
- masked += " ";
226351
+ masked.push(" ");
226330
226352
  index += 2;
226331
226353
  state = "block-comment";
226332
226354
  continue;
@@ -226338,10 +226360,10 @@ function maskSourceComments(content) {
226338
226360
  } else if (current === "`") {
226339
226361
  state = "template";
226340
226362
  }
226341
- masked += current;
226363
+ masked.push(current);
226342
226364
  index += 1;
226343
226365
  }
226344
- return masked;
226366
+ return masked.join("");
226345
226367
  }
226346
226368
  function detectCodeSqlQueryShapeFindings(file, config) {
226347
226369
  const findings = [];
@@ -226371,7 +226393,7 @@ function detectCodeSqlQueryShapeFindings(file, config) {
226371
226393
  target: firstTable ?? "sql-query"
226372
226394
  }));
226373
226395
  }
226374
- if (statement.queryType === "select" && !isOperationalOrBatchQueryContext(file) && !hasPaginationEvidence(statement.statement) && !isObviousPointLookup(statement.statement) && !/\bcount\s*\(/i.test(statement.statement)) {
226396
+ if (statement.queryType === "select" && !isOperationalOrBatchQueryContext(file) && !hasPaginationEvidence(statement.statement) && !hasSpringDataRepositoryMethodBoundEvidence(file.content, query.offset) && !isObviousPointLookup(statement.statement) && !/\bcount\s*\(/i.test(statement.statement)) {
226375
226397
  findings.push(createCodeFinding({
226376
226398
  id: "AP-DQR-003",
226377
226399
  message: `Potential unbounded query risk: SELECT query${firstTable ? ` on table '${firstTable}'` : ""} has no LIMIT/OFFSET/FETCH pagination evidence.`,
@@ -226385,6 +226407,18 @@ function detectCodeSqlQueryShapeFindings(file, config) {
226385
226407
  }
226386
226408
  return findings;
226387
226409
  }
226410
+ function hasSpringDataRepositoryMethodBoundEvidence(content, queryOffset) {
226411
+ const annotationOffset = content.lastIndexOf("@Query", queryOffset);
226412
+ if (annotationOffset < 0 || queryOffset - annotationOffset > 2400) {
226413
+ return false;
226414
+ }
226415
+ const semicolonOffset = content.indexOf(";", queryOffset);
226416
+ if (semicolonOffset < queryOffset || semicolonOffset - annotationOffset > 3200) {
226417
+ return false;
226418
+ }
226419
+ const declarationWindow = content.slice(queryOffset, semicolonOffset + 1);
226420
+ return /\b(?:Page|Slice|Window)\s*</u.test(declarationWindow) && /\b(?:Pageable|Limit)\s+\w+\b/u.test(declarationWindow);
226421
+ }
226388
226422
  function collectOrmReadEvidence(method) {
226389
226423
  const patterns = [
226390
226424
  [/\b(?:this\.)?prisma\.\w+\.findMany\s*\(/giu, "prisma.findMany"],
@@ -226502,7 +226536,11 @@ function detectCodeQueryRiskFindings(files, config, astCache) {
226502
226536
  if (isProductionBackend) {
226503
226537
  findings.push(...detectCodeSqlQueryShapeFindings(file, config));
226504
226538
  }
226505
- for (const method of extractMethodBodies(file, astCache)) {
226539
+ if (!methodBodyScanExtensions.has(file.extension)) {
226540
+ continue;
226541
+ }
226542
+ const methods = extractMethodBodies(file, astCache);
226543
+ for (const method of methods) {
226506
226544
  if (!isProductionBackend) {
226507
226545
  continue;
226508
226546
  }
@@ -227467,6 +227505,7 @@ var import_node_fs26 = require("node:fs");
227467
227505
  var ts4 = __toESM(require_typescript());
227468
227506
  init_moduleDiscovery();
227469
227507
  init_archpilotIgnore();
227508
+ init_sourceScopeClassification();
227470
227509
  var RuleIds = {
227471
227510
  AUTH_MISSING_ENFORCEMENT: "AP-AUTH-001",
227472
227511
  AUTH_INCONSISTENT_ENFORCEMENT: "AP-AUTH-002",
@@ -227897,7 +227936,10 @@ function isProtectedLooking(endpoint) {
227897
227936
  }
227898
227937
  function detectAuthorizationFindings(files, contract, astCache) {
227899
227938
  const findings = [];
227900
- for (const file of files) {
227939
+ const productionFiles = files.filter(
227940
+ (file) => isProductionBackendGovernanceScope(classifyValidationSourceScope(file.relativePath))
227941
+ );
227942
+ for (const file of productionFiles) {
227901
227943
  const moduleName = inferModuleForPath3(contract, file.relativePath);
227902
227944
  const endpoints = extractEndpoints(file, moduleName);
227903
227945
  if (endpoints.length === 0 && !isControllerFile(file, astCache)) {
@@ -227932,7 +227974,7 @@ function detectAuthorizationFindings(files, contract, astCache) {
227932
227974
  }
227933
227975
  }
227934
227976
  }
227935
- for (const file of files.filter((candidate) => isRepositoryFile(candidate, astCache))) {
227977
+ for (const file of productionFiles.filter((candidate) => isRepositoryFile(candidate, astCache))) {
227936
227978
  const roleMatch = executableAuthorizationDecisionPattern.exec(file.content);
227937
227979
  if (!roleMatch) {
227938
227980
  continue;
@@ -228415,7 +228457,7 @@ function classifySourceFile2(file, contract, astCache) {
228415
228457
  const scope = classifyValidationSourceScope(file.relativePath);
228416
228458
  const roles = new Set(getDeclaredRoles2(file, astCache));
228417
228459
  const lowerPath = file.relativePath.toLowerCase();
228418
- if (/(^|[./_-])controller\.(ts|tsx|js|jsx|mjs|cjs|java|cs|py|php|go)$/u.test(lowerPath) || /(^|\/)(controllers?|routers?|routes?|handlers?)\//u.test(lowerPath)) {
228460
+ if (/(^|[./_-])controller\.(ts|tsx|js|jsx|mjs|cjs|java|cs|py|php|go)$/u.test(lowerPath) || /(^|\/)(controllers?|routers?|routes?)\//u.test(lowerPath)) {
228419
228461
  roles.add("controller");
228420
228462
  }
228421
228463
  if (/(^|[./_-])service\.(ts|tsx|js|jsx|mjs|cjs|java|cs|py|php|go)$/u.test(lowerPath) || /(^|\/)(services?)\//u.test(lowerPath)) {
@@ -229954,8 +229996,15 @@ function isStaticCollectionBackedEndpoint(endpoint) {
229954
229996
  }
229955
229997
  return isStaticCollectionExpression(stripStaticLiteralNoise(expression));
229956
229998
  }
229999
+ function isUnconditionallyTerminalThrowEndpoint(endpoint) {
230000
+ const body = endpoint.body.trim().replace(/^\{\s*/u, "").replace(/\s*\}$/u, "").trim();
230001
+ if (!/^throw\s+new\s+[A-Z]\w*(?:Exception|Error)\s*\([^;{}]*\)\s*;?$/u.test(body)) {
230002
+ return false;
230003
+ }
230004
+ return !/\b(?:if|for|while|switch|try|catch|return|await|yield)\b/u.test(body);
230005
+ }
229957
230006
  function detectPaginationFindings(endpoints, contractEndpoints) {
229958
- return endpoints.filter((endpoint) => isCollectionGet(endpoint) && !hasPaginationEvidence2(endpoint, contractEndpoints) && !isStaticCollectionBackedEndpoint(endpoint)).map((endpoint) => createFinding4({
230007
+ return endpoints.filter((endpoint) => isCollectionGet(endpoint) && !hasPaginationEvidence2(endpoint, contractEndpoints) && !isStaticCollectionBackedEndpoint(endpoint) && !isUnconditionallyTerminalThrowEndpoint(endpoint)).map((endpoint) => createFinding4({
229959
230008
  id: RuleIds3.MISSING_PAGINATION,
229960
230009
  message: `Collection endpoint ${endpoint.httpMethod.toUpperCase()} ${endpoint.path} lacks visible pagination evidence.`,
229961
230010
  endpoint
@@ -230715,12 +230764,14 @@ function extractTypeScriptSignals(file, astCache) {
230715
230764
  ts6.forEachChild(node, visit);
230716
230765
  };
230717
230766
  visit(sourceFile);
230718
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230767
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230719
230768
  }
230720
230769
  function extractJavaSignals(file) {
230721
230770
  const publishes = [];
230722
230771
  const handlers2 = [];
230723
- for (const match of file.content.matchAll(/@EventListener\s*(?:\(\s*([A-Za-z]\w*Event)\.class\s*\))?/gu)) {
230772
+ for (const match of file.content.matchAll(
230773
+ /@EventListener\s*(?:\(\s*(?:(?:classes|value)\s*=\s*)?(?:\{\s*)?([A-Za-z_]\w*)\.class\s*(?:\})?\s*\))?/gu
230774
+ )) {
230724
230775
  const eventName = match[1] ?? "ApplicationEvent";
230725
230776
  handlers2.push({
230726
230777
  eventName,
@@ -230732,6 +230783,21 @@ function extractJavaSignals(file) {
230732
230783
  publishes: []
230733
230784
  });
230734
230785
  }
230786
+ for (const match of file.content.matchAll(/\b(?:class|interface)\s+\w+[^{;]*\bApplicationListener\s*<\s*([A-Za-z_]\w*)\s*>/gu)) {
230787
+ const eventName = match[1];
230788
+ if (!eventName) {
230789
+ continue;
230790
+ }
230791
+ handlers2.push({
230792
+ eventName,
230793
+ normalizedName: normalizeEventName(eventName),
230794
+ file,
230795
+ line: lineNumberForOffset5(file, match.index ?? 0),
230796
+ module: file.module,
230797
+ style: classifyEventStyle(eventName),
230798
+ publishes: []
230799
+ });
230800
+ }
230735
230801
  for (const match of file.content.matchAll(/(?:await\s+)?(?:\w+\.)?(?:publishEvent|publish|emit)\s*\(\s*(?:(new)\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*\(|(["'])([A-Za-z][\w.]*Event?|[a-z][\w.]+)\3|([A-Z][A-Za-z0-9_]*Event)\b)/gu)) {
230736
230802
  const constructedType = match[2]?.split(".").at(-1);
230737
230803
  const eventName = constructedType ?? match[4] ?? match[5];
@@ -230749,7 +230815,23 @@ function extractJavaSignals(file) {
230749
230815
  assignedOrReturned: false
230750
230816
  });
230751
230817
  }
230752
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230818
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: extractJavaEventTypeParents(file) };
230819
+ }
230820
+ function extractJavaEventTypeParents(file) {
230821
+ const parentsByType = /* @__PURE__ */ new Map();
230822
+ for (const match of file.content.matchAll(/\b(?:class|record)\s+([A-Za-z_]\w*)(?:\s*<[^>{}]+>)?\s+extends\s+([A-Za-z_]\w*)/gu)) {
230823
+ const child = match[1];
230824
+ const parent = match[2];
230825
+ if (!child || !parent || !child.endsWith("Event")) {
230826
+ continue;
230827
+ }
230828
+ const normalizedChild = normalizeEventName(child);
230829
+ const normalizedParent = normalizeEventName(parent);
230830
+ const parents = parentsByType.get(normalizedChild) ?? /* @__PURE__ */ new Set();
230831
+ parents.add(normalizedParent);
230832
+ parentsByType.set(normalizedChild, parents);
230833
+ }
230834
+ return parentsByType;
230753
230835
  }
230754
230836
  function extractPublishSignalsFromText(file, body, baseOffset, pattern) {
230755
230837
  const publishes = [];
@@ -230795,7 +230877,7 @@ function extractCsharpSignals(file) {
230795
230877
  publishes: extractPublishSignalsFromText(file, classBody, classOpenBrace >= 0 ? classOpenBrace : classStart, csharpPublishPattern)
230796
230878
  });
230797
230879
  }
230798
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230880
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230799
230881
  }
230800
230882
  function extractPythonSignals(file) {
230801
230883
  const publishes = extractPublishSignalsFromText(file, file.content, 0, pythonPublishPattern);
@@ -230818,7 +230900,7 @@ function extractPythonSignals(file) {
230818
230900
  publishes: extractPublishSignalsFromText(file, block, blockStart, pythonPublishPattern)
230819
230901
  });
230820
230902
  }
230821
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230903
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230822
230904
  }
230823
230905
  function extractPhpSignals(file) {
230824
230906
  const publishes = extractPublishSignalsFromText(file, file.content, 0, phpEventPublishPattern);
@@ -230843,7 +230925,7 @@ function extractPhpSignals(file) {
230843
230925
  publishes: extractPublishSignalsFromText(file, classBody, classOpenBrace >= 0 ? classOpenBrace : classStart, phpEventPublishPattern)
230844
230926
  });
230845
230927
  }
230846
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230928
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230847
230929
  }
230848
230930
  function extractGoSignals(file) {
230849
230931
  const publishes = extractPublishSignalsFromText(file, file.content, 0, goPublishPattern);
@@ -230863,7 +230945,7 @@ function extractGoSignals(file) {
230863
230945
  publishes: []
230864
230946
  });
230865
230947
  }
230866
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230948
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230867
230949
  }
230868
230950
  function isTypeScriptLikeExtension(extension) {
230869
230951
  return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(extension);
@@ -230963,6 +231045,7 @@ function collectPublicTypeScriptEventNames(files, contract, astCache) {
230963
231045
  function collectSignals(files, contract, astCache) {
230964
231046
  const publishes = [];
230965
231047
  const handlers2 = [];
231048
+ const eventTypeParents = /* @__PURE__ */ new Map();
230966
231049
  const publicEventNames = collectPublicTypeScriptEventNames(files, contract, astCache);
230967
231050
  for (const file of files) {
230968
231051
  let signals;
@@ -230988,14 +231071,41 @@ function collectSignals(files, contract, astCache) {
230988
231071
  }
230989
231072
  publishes.push(...signals.publishes);
230990
231073
  handlers2.push(...signals.handlers);
231074
+ for (const [eventName, parents] of signals.eventTypeParents) {
231075
+ const existing = eventTypeParents.get(eventName) ?? /* @__PURE__ */ new Set();
231076
+ for (const parent of parents) {
231077
+ existing.add(parent);
231078
+ }
231079
+ eventTypeParents.set(eventName, existing);
231080
+ }
231081
+ }
231082
+ return { publishes, handlers: handlers2, publicEventNames, eventTypeParents };
231083
+ }
231084
+ function isHandledEventName(normalizedName, handledNames, eventTypeParents) {
231085
+ const queue = [normalizedName];
231086
+ const visited = /* @__PURE__ */ new Set();
231087
+ while (queue.length > 0) {
231088
+ const current = queue.shift();
231089
+ if (!current || visited.has(current)) {
231090
+ continue;
231091
+ }
231092
+ if (handledNames.has(current)) {
231093
+ return true;
231094
+ }
231095
+ visited.add(current);
231096
+ for (const parent of eventTypeParents.get(current) ?? []) {
231097
+ if (!visited.has(parent)) {
231098
+ queue.push(parent);
231099
+ }
231100
+ }
230991
231101
  }
230992
- return { publishes, handlers: handlers2, publicEventNames };
231102
+ return false;
230993
231103
  }
230994
231104
  function buildEventFindings(signals) {
230995
231105
  const findings = [];
230996
231106
  const handledNames = new Set(signals.handlers.map((handler) => handler.normalizedName));
230997
231107
  for (const publish of signals.publishes) {
230998
- if (!handledNames.has(publish.normalizedName)) {
231108
+ if (!isHandledEventName(publish.normalizedName, handledNames, signals.eventTypeParents)) {
230999
231109
  if (signals.publicEventNames.has(publish.normalizedName)) {
231000
231110
  continue;
231001
231111
  }
@@ -242997,8 +243107,6 @@ var ignoredRootPathPrefixes = [
242997
243107
  "static",
242998
243108
  "assets",
242999
243109
  "vendor",
243000
- "plugins",
243001
- "plugin",
243002
243110
  "themes",
243003
243111
  "theme",
243004
243112
  "generated",
@@ -243021,8 +243129,12 @@ function pushEvidence(evidence, message) {
243021
243129
  function safeReadDirectoryEntries(directoryPath) {
243022
243130
  try {
243023
243131
  return fs52.readdirSync(directoryPath, { withFileTypes: true });
243024
- } catch {
243025
- return [];
243132
+ } catch (error) {
243133
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
243134
+ if (code === "ENOENT" || code === "ENOTDIR") {
243135
+ return [];
243136
+ }
243137
+ throw error;
243026
243138
  }
243027
243139
  }
243028
243140
  function safeReadJsonFile(jsonPath) {
@@ -245596,8 +245708,6 @@ function isIgnoredComponentCandidatePath(componentPath) {
245596
245708
  "static",
245597
245709
  "assets",
245598
245710
  "vendor",
245599
- "plugins",
245600
- "plugin",
245601
245711
  "themes",
245602
245712
  "theme",
245603
245713
  "generated",
@@ -245618,6 +245728,36 @@ function pathExists19(pathValue) {
245618
245728
  return false;
245619
245729
  }
245620
245730
  }
245731
+ function isSafeMissingPathError(error) {
245732
+ const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
245733
+ return code === "ENOENT" || code === "ENOTDIR";
245734
+ }
245735
+ function isDirectoryPath(pathValue) {
245736
+ try {
245737
+ return fs58.statSync(pathValue).isDirectory();
245738
+ } catch (error) {
245739
+ if (isSafeMissingPathError(error)) {
245740
+ return false;
245741
+ }
245742
+ throw error;
245743
+ }
245744
+ }
245745
+ function readDirectoryEntriesForTraversal(directoryPath, options) {
245746
+ if (!isDirectoryPath(directoryPath)) {
245747
+ return [];
245748
+ }
245749
+ try {
245750
+ return fs58.readdirSync(directoryPath, {
245751
+ withFileTypes: true,
245752
+ ...options?.recursive ? { recursive: true } : {}
245753
+ });
245754
+ } catch (error) {
245755
+ if (isSafeMissingPathError(error)) {
245756
+ return [];
245757
+ }
245758
+ throw error;
245759
+ }
245760
+ }
245621
245761
  function hasManifest2(workspaceRoot, componentPath) {
245622
245762
  const absoluteRoot = componentPath === "." ? workspaceRoot : path64.join(workspaceRoot, ...componentPath.split("/"));
245623
245763
  return [
@@ -245625,7 +245765,8 @@ function hasManifest2(workspaceRoot, componentPath) {
245625
245765
  "pom.xml",
245626
245766
  "requirements.txt",
245627
245767
  "go.mod",
245628
- "composer.json"
245768
+ "composer.json",
245769
+ "project.json"
245629
245770
  ].some((fileName) => pathExists19(path64.join(absoluteRoot, fileName)));
245630
245771
  }
245631
245772
  function safeReadJson(filePath) {
@@ -245648,9 +245789,21 @@ function recordKeys(value) {
245648
245789
  function hasObject(value) {
245649
245790
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
245650
245791
  }
245792
+ function readStringProperty(value, key) {
245793
+ const field = value?.[key];
245794
+ return typeof field === "string" && field.trim().length > 0 ? field.trim() : void 0;
245795
+ }
245651
245796
  function hasPackagePublishSurface(packageJson) {
245652
245797
  return hasObject(packageJson?.exports) || typeof packageJson?.main === "string" || typeof packageJson?.module === "string" || typeof packageJson?.types === "string" || hasObject(packageJson?.publishConfig) || Array.isArray(packageJson?.files);
245653
245798
  }
245799
+ function packageDependencyNames(packageJson) {
245800
+ return [
245801
+ ...recordKeys(packageJson?.dependencies),
245802
+ ...recordKeys(packageJson?.devDependencies),
245803
+ ...recordKeys(packageJson?.peerDependencies),
245804
+ ...recordKeys(packageJson?.optionalDependencies)
245805
+ ];
245806
+ }
245654
245807
  function absoluteComponentRoot(workspaceRoot, componentPath) {
245655
245808
  return componentPath === "." ? workspaceRoot : path64.join(workspaceRoot, ...componentPath.split("/"));
245656
245809
  }
@@ -245658,6 +245811,136 @@ function readPackageJson(workspaceRoot, componentPath) {
245658
245811
  const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
245659
245812
  return safeReadJson(path64.join(absoluteRoot, "package.json"));
245660
245813
  }
245814
+ function readProjectJson(workspaceRoot, componentPath) {
245815
+ const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
245816
+ return safeReadJson(path64.join(absoluteRoot, "project.json"));
245817
+ }
245818
+ function readNxWorkspaceLayout(workspaceRoot) {
245819
+ const nxJson = safeReadJson(path64.join(workspaceRoot, "nx.json"));
245820
+ const workspaceLayout = nxJson?.workspaceLayout;
245821
+ if (!hasObject(workspaceLayout)) {
245822
+ return {};
245823
+ }
245824
+ const layout = workspaceLayout;
245825
+ return {
245826
+ ...typeof layout.appsDir === "string" && layout.appsDir.trim().length > 0 ? { appsDir: normalizePath25(layout.appsDir.trim()) } : {},
245827
+ ...typeof layout.libsDir === "string" && layout.libsDir.trim().length > 0 ? { libsDir: normalizePath25(layout.libsDir.trim()) } : {}
245828
+ };
245829
+ }
245830
+ function readTargetMap(projectJson) {
245831
+ const targets = projectJson?.targets ?? projectJson?.architect;
245832
+ return hasObject(targets) ? targets : {};
245833
+ }
245834
+ function targetExecutorText(target) {
245835
+ if (!hasObject(target)) {
245836
+ return "";
245837
+ }
245838
+ const typed = target;
245839
+ const executor = typeof typed.executor === "string" ? typed.executor : "";
245840
+ const builder = typeof typed.builder === "string" ? typed.builder : "";
245841
+ const options = hasObject(typed.options) ? typed.options : {};
245842
+ const main = typeof options.main === "string" ? options.main : "";
245843
+ const index = typeof options.index === "string" ? options.index : "";
245844
+ const outputPath = typeof options.outputPath === "string" ? options.outputPath : "";
245845
+ return `${executor} ${builder} ${main} ${index} ${outputPath}`.toLowerCase();
245846
+ }
245847
+ function hasNxRuntimeProjectEvidence(projectJson) {
245848
+ const projectType = readStringProperty(projectJson, "projectType");
245849
+ if (projectType === "library") {
245850
+ return false;
245851
+ }
245852
+ const targets = readTargetMap(projectJson);
245853
+ const targetNames = Object.keys(targets);
245854
+ const targetText = targetNames.map((targetName) => `${targetName} ${targetExecutorText(targets[targetName])}`).join(" ");
245855
+ const hasRuntimeTarget = /\b(?:serve|start|preview|worker|package:container|deploy:static)\b/u.test(targetText);
245856
+ const hasBuildEntrypoint = /\bbuild\b/u.test(targetNames.join(" ")) && /(?:\bmain\b|src\/main|src\/index|public\/index|webpack|vite|next|node|container|browser|dev-server)/u.test(targetText);
245857
+ return projectType === "application" || hasRuntimeTarget || hasBuildEntrypoint;
245858
+ }
245859
+ function hasNxSupportOnlyProjectEvidence(projectJson) {
245860
+ const projectType = readStringProperty(projectJson, "projectType");
245861
+ const targets = readTargetMap(projectJson);
245862
+ const targetNames = Object.keys(targets);
245863
+ if (targetNames.length === 0) {
245864
+ return false;
245865
+ }
245866
+ const targetText = targetNames.map((targetName) => `${targetName} ${targetExecutorText(targets[targetName])}`).join(" ");
245867
+ const hasOnlySupportTargets = targetNames.every(
245868
+ (targetName) => /^(?:e2e|test|unit-test|integration-test|lint|typecheck|format|smoke|bench|benchmark)$/u.test(targetName)
245869
+ );
245870
+ const hasTestExecutor = /(?:cypress|playwright|jest|vitest|mocha|karma|protractor)/u.test(targetText);
245871
+ return projectType !== "library" && (hasOnlySupportTargets || hasTestExecutor);
245872
+ }
245873
+ function classifyNxRuntimeKind(projectJson) {
245874
+ if (!hasNxRuntimeProjectEvidence(projectJson)) {
245875
+ return void 0;
245876
+ }
245877
+ const targets = readTargetMap(projectJson);
245878
+ const targetText = Object.entries(targets).map(([targetName, target]) => `${targetName} ${targetExecutorText(target)}`).join(" ");
245879
+ if (/(?:webpack:dev-server|webpack-dev-server|src\/index\.(?:tsx|jsx|ts|js)|public\/index\.html|browser|react|vite|next|angular|deploy:static)/u.test(targetText)) {
245880
+ return "frontend";
245881
+ }
245882
+ if (/(?:@nx\/js:node|@nrwl\/node|src\/main\.(?:ts|js)|target:\s*node|node\b|container|worker|queue)/u.test(targetText)) {
245883
+ return /\bworker\b/u.test(targetText) ? "worker" : "backend";
245884
+ }
245885
+ return void 0;
245886
+ }
245887
+ function collectNxProjectCandidates(workspaceRoot) {
245888
+ if (!pathExists19(path64.join(workspaceRoot, "nx.json"))) {
245889
+ return [];
245890
+ }
245891
+ const candidates = /* @__PURE__ */ new Map();
245892
+ const workspaceLayout = readNxWorkspaceLayout(workspaceRoot);
245893
+ const preferredRoots = new Set(
245894
+ ["apps", "packages", "services", "libs", workspaceLayout.appsDir, workspaceLayout.libsDir].filter((entry) => typeof entry === "string" && entry.length > 0).map(normalizePath25)
245895
+ );
245896
+ const maxDepth = 5;
245897
+ const queue = [
245898
+ { absolutePath: workspaceRoot, relativePath: "", depth: 0 }
245899
+ ];
245900
+ const addCandidate = (candidatePath, evidence) => {
245901
+ const normalized = normalizePath25(candidatePath);
245902
+ const existing = candidates.get(normalized) ?? { path: normalized, evidence: [] };
245903
+ addEvidence6(existing.evidence, evidence);
245904
+ candidates.set(normalized, existing);
245905
+ };
245906
+ while (queue.length > 0) {
245907
+ const current = queue.shift();
245908
+ if (!current) {
245909
+ continue;
245910
+ }
245911
+ for (const entry of readDirectoryEntriesForTraversal(current.absolutePath)) {
245912
+ if (!entry.isDirectory()) {
245913
+ continue;
245914
+ }
245915
+ if ([".git", ".archpilot", "node_modules", "dist", "build", "out", "coverage"].includes(entry.name)) {
245916
+ continue;
245917
+ }
245918
+ const relativePath = normalizePath25(current.relativePath ? `${current.relativePath}/${entry.name}` : entry.name);
245919
+ if (current.depth >= maxDepth) {
245920
+ continue;
245921
+ }
245922
+ const projectJsonPath = path64.join(current.absolutePath, entry.name, "project.json");
245923
+ if (pathExists19(projectJsonPath)) {
245924
+ const projectJson = safeReadJson(projectJsonPath);
245925
+ const projectRoot = normalizePath25(readStringProperty(projectJson, "root") ?? relativePath);
245926
+ if (hasNxRuntimeProjectEvidence(projectJson)) {
245927
+ addCandidate(projectRoot, "Detected Nx runtime project metadata");
245928
+ } else if (readStringProperty(projectJson, "sourceRoot")) {
245929
+ addCandidate(projectRoot, "Detected Nx source-bearing project metadata");
245930
+ }
245931
+ }
245932
+ const firstSegment = relativePath.split("/")[0] ?? "";
245933
+ if (current.depth === 0 || preferredRoots.has(firstSegment) || preferredRoots.has(relativePath.split("/").slice(0, 2).join("/"))) {
245934
+ queue.push({
245935
+ absolutePath: path64.join(current.absolutePath, entry.name),
245936
+ relativePath,
245937
+ depth: current.depth + 1
245938
+ });
245939
+ }
245940
+ }
245941
+ }
245942
+ return [...candidates.values()].sort((left, right) => left.path.localeCompare(right.path));
245943
+ }
245661
245944
  function readTextFileSafe(filePath) {
245662
245945
  try {
245663
245946
  return fs58.readFileSync(filePath, { encoding: "utf8" });
@@ -245704,6 +245987,8 @@ function readMavenMetadata(workspaceRoot, componentPath) {
245704
245987
  testScopedDependencyCount: dependencyBlocks.filter((block) => /<scope>\s*test\s*<\/scope>/iu.test(block)).length,
245705
245988
  totalDependencyCount: dependencyBlocks.length,
245706
245989
  hasMavenPluginConfiguration: packaging === "maven-plugin" || /maven-plugin-plugin|<goalPrefix>|<mojo/iu.test(normalizedPom),
245990
+ hasAssemblyPackagingConfiguration: /maven-assembly-plugin|<goal>\s*single\s*<\/goal>|<descriptor>[^<]*(?:assembly|dist|distribution)[^<]*<\/descriptor>/iu.test(normalizedPom),
245991
+ hasDeploySkipConfiguration: /<maven\.deploy\.skip>\s*true\s*<\/maven\.deploy\.skip>|<artifactId>\s*maven-deploy-plugin\s*<\/artifactId>[\s\S]*?<skip>\s*true\s*<\/skip>/iu.test(normalizedPom),
245707
245992
  hasTestLifecycleConfiguration: /maven-surefire-plugin|maven-failsafe-plugin|integration-test|failsafe|surefire/iu.test(normalizedPom),
245708
245993
  hasArquillianEvidence: /arquillian/iu.test(normalizedPom),
245709
245994
  hasMainJava: pathExists19(path64.join(absoluteRoot, "src", "main", "java")),
@@ -245836,6 +246121,12 @@ function buildRootCandidates(input2) {
245836
246121
  addCandidate(root, `Detected project root ${root}`);
245837
246122
  }
245838
246123
  if (input2.workspaceRoot) {
246124
+ for (const candidate of collectNxProjectCandidates(input2.workspaceRoot)) {
246125
+ addCandidate(candidate.path, "Detected Nx project metadata");
246126
+ for (const evidence of candidate.evidence) {
246127
+ addCandidate(candidate.path, evidence);
246128
+ }
246129
+ }
245839
246130
  const mavenRoots = [".", ...input2.topology.detectedRoots ?? []];
245840
246131
  for (const candidate of collectMavenReactorCandidates(input2.workspaceRoot, mavenRoots)) {
245841
246132
  addCandidate(
@@ -245919,10 +246210,7 @@ function isUnderJvmResourceRoot4(componentPath) {
245919
246210
  return /(^|\/)src\/main\/resources(?:\/|$)/u.test(normalizePath25(componentPath).toLowerCase());
245920
246211
  }
245921
246212
  function hasFileUnderRoot(root, pattern) {
245922
- if (!pathExists19(root)) {
245923
- return false;
245924
- }
245925
- const entries = fs58.readdirSync(root, { recursive: true, withFileTypes: true });
246213
+ const entries = readDirectoryEntriesForTraversal(root, { recursive: true });
245926
246214
  return entries.some((entry) => entry.isFile() && pattern.test(entry.name));
245927
246215
  }
245928
246216
  var javaMainMethodPattern = /\bpublic\s+static\s+void\s+main\s*\(\s*String\s*(?:(?:\[\s*\]\s*)|\.\.\.\s*)[A-Za-z_$][\w$]*\s*(?:\[\s*\])?\s*\)/u;
@@ -246040,10 +246328,7 @@ function readMavenProductionSourceFiles(workspaceRoot, componentPath) {
246040
246328
  ];
246041
246329
  const files = [];
246042
246330
  for (const sourceRoot of mainRoots) {
246043
- if (!pathExists19(sourceRoot)) {
246044
- continue;
246045
- }
246046
- const entries = fs58.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
246331
+ const entries = readDirectoryEntriesForTraversal(sourceRoot, { recursive: true });
246047
246332
  for (const entry of entries) {
246048
246333
  if (!entry.isFile() || !/\.(?:java|kt|scala)$/iu.test(entry.name)) {
246049
246334
  continue;
@@ -246215,24 +246500,67 @@ function hasRuntimeSourceFiles(workspaceRoot, componentPath) {
246215
246500
  path64.join(absoluteRoot, "server")
246216
246501
  ];
246217
246502
  for (const sourceRoot of sourceRoots) {
246218
- if (!pathExists19(sourceRoot)) {
246219
- continue;
246220
- }
246221
- const entries = fs58.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
246503
+ const entries = readDirectoryEntriesForTraversal(sourceRoot, { recursive: true });
246222
246504
  if (entries.some((entry) => entry.isFile() && /\.(?:ts|tsx|js|jsx|java|cs|py|php|go)$/iu.test(entry.name))) {
246223
246505
  return true;
246224
246506
  }
246225
246507
  }
246226
246508
  return false;
246227
246509
  }
246510
+ function hasFileMatchingUnderRoot(root, pattern) {
246511
+ const entries = readDirectoryEntriesForTraversal(root, { recursive: true });
246512
+ return entries.some((entry) => entry.isFile() && pattern.test(normalizePath25(path64.join(entry.parentPath, entry.name))));
246513
+ }
246514
+ function hasNestRuntimeApplicationEvidence(workspaceRoot, componentPath, packageJson = readPackageJson(workspaceRoot, componentPath)) {
246515
+ const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
246516
+ const dependencies = new Set(packageDependencyNames(packageJson));
246517
+ const scripts = recordKeys(packageJson?.scripts);
246518
+ const hasNestCore = dependencies.has("@nestjs/core");
246519
+ if (!hasNestCore) {
246520
+ return false;
246521
+ }
246522
+ const hasRuntimePlatform = dependencies.has("@nestjs/platform-express") || dependencies.has("@nestjs/platform-fastify") || dependencies.has("@nestjs/microservices") || dependencies.has("@nestjs/graphql") || dependencies.has("@nestjs/apollo");
246523
+ const hasRunnableScript = scripts.some((script) => /^(?:start|serve|dev|worker)(?::|$)/u.test(script));
246524
+ const hasNestCliConfig = ["nest-cli.json", "nest.config.js", "nest.config.ts"].some(
246525
+ (fileName) => pathExists19(path64.join(absoluteRoot, fileName))
246526
+ );
246527
+ const hasBootstrapEntrypoint = [
246528
+ path64.join(absoluteRoot, "src", "main.ts"),
246529
+ path64.join(absoluteRoot, "src", "main.js"),
246530
+ path64.join(absoluteRoot, "main.ts"),
246531
+ path64.join(absoluteRoot, "main.js")
246532
+ ].some((filePath) => {
246533
+ const contents = readTextFileSafe(filePath);
246534
+ return Boolean(contents && /\bNestFactory\.create(?:ApplicationContext)?\s*\(/u.test(contents));
246535
+ });
246536
+ const srcRoot = path64.join(absoluteRoot, "src");
246537
+ const hasRuntimeSourceShape = hasFileMatchingUnderRoot(srcRoot, /\.(?:controller|resolver|gateway|processor|module|worker|queue)\.(?:ts|js)$/iu) || hasFileMatchingUnderRoot(srcRoot, /[/\\](?:controllers?|resolvers?|queues?|workers?)[/\\][^/\\]+\.(?:ts|js)$/iu);
246538
+ const hasDatabaseRuntimeIntegration = [...dependencies].some(
246539
+ (dependency) => /^(?:typeorm|@nestjs\/typeorm|@prisma\/client|prisma|mongoose|@nestjs\/mongoose|sequelize|@mikro-orm\/core)$/u.test(dependency)
246540
+ );
246541
+ const structuralEvidenceCount = [
246542
+ hasRuntimePlatform,
246543
+ hasRunnableScript,
246544
+ hasNestCliConfig,
246545
+ hasBootstrapEntrypoint,
246546
+ hasRuntimeSourceShape,
246547
+ hasDatabaseRuntimeIntegration
246548
+ ].filter(Boolean).length;
246549
+ return hasBootstrapEntrypoint || hasRuntimePlatform && structuralEvidenceCount >= 2 || hasRunnableScript && hasRuntimeSourceShape && structuralEvidenceCount >= 3;
246550
+ }
246228
246551
  function resolveKind(workspaceRoot, candidate, localProjectKinds) {
246229
246552
  const lowerPath = candidate.path.toLowerCase();
246230
246553
  const packageJson = readPackageJson(workspaceRoot, candidate.path);
246554
+ const projectJson = readProjectJson(workspaceRoot, candidate.path);
246555
+ const nxProjectType = readStringProperty(projectJson, "projectType");
246231
246556
  const maven = readMavenMetadata(workspaceRoot, candidate.path);
246557
+ const dependencies = new Set(packageDependencyNames(packageJson));
246232
246558
  const hasRunnableFrontendEvidence = hasRunnableFrontendComponentEvidence(workspaceRoot, candidate.path);
246559
+ const hasNestRuntimeEvidence = hasNestRuntimeApplicationEvidence(workspaceRoot, candidate.path, packageJson);
246560
+ const nxRuntimeKind = classifyNxRuntimeKind(projectJson);
246233
246561
  const hasPublishSurface = hasPackagePublishSurface(packageJson);
246234
246562
  const hasMavenRunnableEvidence = Boolean(
246235
- maven && (hasMavenMainEntrypoint(workspaceRoot, candidate.path) || hasMavenConfiguredMainClassEntrypoint(workspaceRoot, candidate.path, maven.configuredMainClass) || maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypoint(workspaceRoot, candidate.path) || maven.packaging === "war" && hasMavenMainEntrypoint(workspaceRoot, candidate.path))
246563
+ maven && (hasMavenMainEntrypoint(workspaceRoot, candidate.path) || hasMavenConfiguredMainClassEntrypoint(workspaceRoot, candidate.path, maven.configuredMainClass) || maven.configuredMainClass && maven.hasSpringBootStarterDependency || maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypoint(workspaceRoot, candidate.path) || maven.packaging === "war" && hasMavenMainEntrypoint(workspaceRoot, candidate.path))
246236
246564
  );
246237
246565
  const hasMavenLibraryEvidence = Boolean(
246238
246566
  maven && !hasMavenRunnableEvidence && (maven.packaging === "jar" || maven.packaging === "maven-plugin" || maven.packaging === "pom") && (hasMavenMainJvmSource(workspaceRoot, candidate.path) || maven.hasMainResources || maven.hasDependencyManagement || maven.hasSpringBootAutoconfigureDependency || maven.hasSpringBootAutoconfigureProcessor || hasMavenAutoConfigurationMetadata(workspaceRoot, candidate.path) || maven.hasDocumentationTooling || maven.hasTestJava || maven.hasTestResources)
@@ -246240,6 +246568,12 @@ function resolveKind(workspaceRoot, candidate, localProjectKinds) {
246240
246568
  if (lowerPath.endsWith("/db") || lowerPath.endsWith("/database") || lowerPath.endsWith("/data")) {
246241
246569
  return "data";
246242
246570
  }
246571
+ if (nxRuntimeKind) {
246572
+ return nxRuntimeKind;
246573
+ }
246574
+ if (nxProjectType === "library") {
246575
+ return "library";
246576
+ }
246243
246577
  if (hasMavenLibraryEvidence) {
246244
246578
  return "library";
246245
246579
  }
@@ -246251,6 +246585,12 @@ function resolveKind(workspaceRoot, candidate, localProjectKinds) {
246251
246585
  return hasPublishSurface && !hasRunnableFrontendEvidence ? "library" : "frontend";
246252
246586
  }
246253
246587
  if (classification === "backend_app") {
246588
+ if (hasPublishSurface && dependencies.has("@nestjs/core") && !hasNestRuntimeEvidence) {
246589
+ return "library";
246590
+ }
246591
+ return "backend";
246592
+ }
246593
+ if (hasNestRuntimeEvidence) {
246254
246594
  return "backend";
246255
246595
  }
246256
246596
  if (hasRunnableFrontendEvidence && localProjectKinds.primaryKind !== "backend" && localProjectKinds.primaryKind !== "service" && localProjectKinds.primaryKind !== "cli") {
@@ -246314,6 +246654,7 @@ function hasAnyToken(value, patterns) {
246314
246654
  }
246315
246655
  function classifyGovernanceRole(input2) {
246316
246656
  const packageJson = readPackageJson(input2.workspaceRoot, input2.candidate.path);
246657
+ const projectJson = readProjectJson(input2.workspaceRoot, input2.candidate.path);
246317
246658
  const maven = readMavenMetadata(input2.workspaceRoot, input2.candidate.path);
246318
246659
  const evidence = [];
246319
246660
  let supportScore = 0;
@@ -246327,11 +246668,18 @@ function classifyGovernanceRole(input2) {
246327
246668
  const scripts = recordKeys(packageJson?.scripts);
246328
246669
  const dependencies = recordKeys(packageJson?.dependencies);
246329
246670
  const devDependencies = recordKeys(packageJson?.devDependencies);
246671
+ const projectTags = toStringArray(projectJson?.tags);
246672
+ const projectTargets = recordKeys(projectJson?.targets);
246330
246673
  const scriptText = scripts.join(" ").toLowerCase();
246674
+ const projectMetadataText = `${projectTags.join(" ")} ${projectTargets.join(" ")}`.toLowerCase();
246331
246675
  const dependencyText = [...dependencies, ...devDependencies].join(" ").toLowerCase();
246676
+ const hasRunnableFrontendEvidence = hasRunnableFrontendComponentEvidence(input2.workspaceRoot, input2.candidate.path);
246677
+ const hasNestRuntimeEvidence = hasNestRuntimeApplicationEvidence(input2.workspaceRoot, input2.candidate.path, packageJson);
246678
+ const nxRuntimeKind = classifyNxRuntimeKind(projectJson);
246679
+ const hasNxSupportOnlyEvidence = hasNxSupportOnlyProjectEvidence(projectJson);
246332
246680
  const hasStrongSupportPath = hasAnyToken(pathText, [
246333
- /(^|\/)(testing|test-plugins|cypress-tests|fixtures?|examples?|benchmarks?|smoke)(\/|$)/u,
246334
- /(^|\/)(dev-server|scaffold|scaffolding|generators?|devkit|tooling|tools)(\/|$)/u
246681
+ /(^|\/)(testing|e2e-testing|test-plugins|cypress-tests|fixtures?|benchmarks?|smoke)(\/|$)/u,
246682
+ /(^|\/)(dev-server|scaffold|scaffolding|generators?|devkit|tooling|tools|templates?)(\/|$)/u
246335
246683
  ]);
246336
246684
  const isPlainResourcesSurface = isUnderJvmResourceRoot4(pathText) && !hasRuntimeSourceFiles(input2.workspaceRoot, input2.candidate.path) && !packageJson && !maven;
246337
246685
  const hasPublishSurface = hasPackagePublishSurface(packageJson);
@@ -246343,9 +246691,11 @@ function classifyGovernanceRole(input2) {
246343
246691
  const hasMavenMainEntrypointEvidence = Boolean(maven && hasMavenMainEntrypoint(input2.workspaceRoot, input2.candidate.path));
246344
246692
  const hasMavenMainJvmSourceEvidence = Boolean(maven && hasMavenMainJvmSource(input2.workspaceRoot, input2.candidate.path));
246345
246693
  const hasMavenAutoConfigurationEvidence = Boolean(maven && (hasMavenAutoConfigurationMetadata(input2.workspaceRoot, input2.candidate.path) || maven.hasSpringBootAutoconfigureDependency || maven.hasSpringBootAutoconfigureProcessor));
246346
- const hasMavenExecutableConfiguration = Boolean(maven && (hasMavenConfiguredMainClassEntrypoint(input2.workspaceRoot, input2.candidate.path, maven.configuredMainClass) || maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypointEvidence || maven.packaging === "war" && hasMavenMainEntrypointEvidence));
246694
+ const hasMavenExecutableConfiguration = Boolean(maven && (hasMavenConfiguredMainClassEntrypoint(input2.workspaceRoot, input2.candidate.path, maven.configuredMainClass) || maven.configuredMainClass && maven.hasSpringBootStarterDependency || maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypointEvidence || maven.packaging === "war" && hasMavenMainEntrypointEvidence));
246347
246695
  const hasMavenRunnableApplicationEvidence = Boolean(hasMavenMainEntrypointEvidence || hasMavenExecutableConfiguration);
246348
- const isMavenPomOnly = Boolean(maven?.packaging === "pom" && !hasMavenMainSource && !hasMavenTestSource);
246696
+ const isMavenPomOnly = Boolean(
246697
+ maven?.packaging === "pom" && !hasMavenRunnableApplicationEvidence && !hasMavenMainSource && !hasMavenTestSource
246698
+ );
246349
246699
  const hasMavenTestContext = hasAnyToken(pathText, [
246350
246700
  /(^|\/)(test-framework|testsuite|integration-tests?|test-apps)(\/|$)/u
246351
246701
  ]) && (hasAnyToken(mavenMetadataText, [/\b(test|testsuite|test framework|arquillian|integration test)\b/u]) || Boolean(maven?.hasTestLifecycleConfiguration || maven?.hasArquillianEvidence || hasMavenTestSource));
@@ -246353,7 +246703,7 @@ function classifyGovernanceRole(input2) {
246353
246703
  maven?.packaging === "pom" && maven.hasDependencyManagement && !hasMavenMainSource && hasAnyToken(mavenMetadataText, [/\b(bom|bill of materials|dependency management|platform)\b/u])
246354
246704
  );
246355
246705
  const hasMavenDistributionPackagingEvidence = Boolean(
246356
- maven?.packaging === "pom" && !hasMavenMainSource && hasAnyToken(pathText, [/(^|\/)(distribution|dist|downloads|assembl(y|ies)|feature-packs?)(\/|$)/u])
246706
+ maven && !hasMavenMainSource && (maven.hasAssemblyPackagingConfiguration || maven.hasDeploySkipConfiguration && maven.hasDocumentationTooling || hasAnyToken(mavenMetadataText, [/\b(distribution|assembly|release package|packaging bundle)\b/u]))
246357
246707
  );
246358
246708
  const hasMavenStarterPackagingEvidence = Boolean(
246359
246709
  maven && !hasMavenMainJvmSourceEvidence && hasAnyToken(`${pathText} ${mavenMetadataText}`, [/\bstarters?\b|\bspring-boot-starter\b|\bstarter[-\s]/u])
@@ -246376,6 +246726,22 @@ function classifyGovernanceRole(input2) {
246376
246726
  runtimeScore += 1;
246377
246727
  addEvidence6(evidence, `Runtime framework stack signal: ${input2.stack}`);
246378
246728
  }
246729
+ if (hasNestRuntimeEvidence) {
246730
+ runtimeScore += 3;
246731
+ addEvidence6(evidence, "NestJS runtime application evidence");
246732
+ }
246733
+ if (input2.kind === "frontend" && hasRunnableFrontendEvidence) {
246734
+ runtimeScore += 3;
246735
+ addEvidence6(evidence, "Runnable frontend application evidence");
246736
+ }
246737
+ if (nxRuntimeKind) {
246738
+ runtimeScore += 3;
246739
+ addEvidence6(evidence, `Nx runtime project evidence: ${nxRuntimeKind}`);
246740
+ }
246741
+ if (hasNxSupportOnlyEvidence) {
246742
+ supportScore += 4;
246743
+ addEvidence6(evidence, "Nx project metadata indicates test/tooling-only workspace");
246744
+ }
246379
246745
  if (hasPublishSurface && !isPrivate) {
246380
246746
  runtimeScore += 1;
246381
246747
  addEvidence6(evidence, "Published package surface signal");
@@ -246386,7 +246752,7 @@ function classifyGovernanceRole(input2) {
246386
246752
  if (maven?.hasPom) {
246387
246753
  addEvidence6(evidence, `Maven packaging ${maven.packaging ?? "jar"}`);
246388
246754
  }
246389
- if (maven && (maven.packaging === "jar" || maven.packaging === "war") && hasMavenRunnableApplicationEvidence) {
246755
+ if (maven && hasMavenRunnableApplicationEvidence) {
246390
246756
  runtimeScore += 3;
246391
246757
  addEvidence6(evidence, "Maven executable application evidence");
246392
246758
  }
@@ -246400,25 +246766,41 @@ function classifyGovernanceRole(input2) {
246400
246766
  if (hasMavenAutoConfigurationEvidence && !hasMavenRunnableApplicationEvidence) {
246401
246767
  addEvidence6(evidence, "Maven Spring Boot auto-configuration/starter library evidence");
246402
246768
  }
246403
- if (hasAnyToken(descriptiveManifestText, [/\b(e2e|end-to-end|testing|test helper|test utilities|fixtures?)\b/u])) {
246769
+ if (hasAnyToken(descriptiveManifestText, [/\b(e2e|end-to-end|testing|test helper|test utilities|fixtures?|test harness)\b/u])) {
246404
246770
  supportScore += 3;
246405
246771
  addEvidence6(evidence, "Package metadata describes test/support usage");
246406
246772
  }
246773
+ if (hasAnyToken(`${packageName.toLowerCase()} ${projectMetadataText}`, [/\b(e2e|end-to-end|testing|test harness|scope:testing|test:ui|test:debug|test:report)\b/u]) && hasAnyToken(dependencyText, [/\b(?:playwright|@playwright\/test|cypress|vitest|jest|mocha)\b/u])) {
246774
+ supportScore += 4;
246775
+ addEvidence6(evidence, "Project metadata and dependencies indicate test-only workspace");
246776
+ }
246407
246777
  if (hasAnyToken(descriptiveManifestText, [/\b(scaffold|scaffolding|generator|codegen|template|starter|create)\b/u])) {
246408
246778
  supportScore += 3;
246409
246779
  addEvidence6(evidence, "Package metadata describes scaffolding/generation usage");
246410
246780
  }
246411
- if (hasAnyToken(descriptiveManifestText, [/\b(dev server|development server|development tooling|tooling|devkit)\b/u])) {
246781
+ if (hasAnyToken(descriptiveManifestText, [/\b(dev server|development server|development tooling|tooling|devkit|lint rules?|linter|build tooling|repository tooling)\b/u])) {
246412
246782
  supportScore += 2;
246413
246783
  addEvidence6(evidence, "Package metadata describes development/tooling usage");
246414
246784
  }
246415
246785
  if (hasAnyToken(pathText, [
246416
- /(^|\/)(test|tests|testing|__tests__|cypress|cypress-tests|fixtures?|examples?|benchmarks?|smoke)(\/|$)/u,
246417
- /(^|\/)(dev-server|test-plugins|scaffold|scaffolding|generators?|devkit|tooling|tools)(\/|$)/u
246786
+ /(^|\/)(test|tests|testing|e2e-testing|__tests__|cypress|cypress-tests|fixtures?|benchmarks?|smoke)(\/|$)/u,
246787
+ /(^|\/)(dev-server|test-plugins|scaffold|scaffolding|generators?|devkit|tooling|tools|templates?)(\/|$)/u
246418
246788
  ])) {
246419
246789
  supportScore += 1;
246420
246790
  addEvidence6(evidence, "Path contains support/tooling role tokens");
246421
246791
  }
246792
+ if (maven && hasStrongSupportPath && !hasMavenRunnableApplicationEvidence) {
246793
+ supportScore += 3;
246794
+ addEvidence6(evidence, "Maven module path and metadata indicate support/tooling role");
246795
+ }
246796
+ if (hasAnyToken(pathText, [/(^|\/)(?:src\/constants\/)?templates?(\/|$)/u])) {
246797
+ supportScore += 4;
246798
+ addEvidence6(evidence, "Nested scaffold/template source surface");
246799
+ }
246800
+ if (hasAnyToken(dependencyText, [/\b(?:eslint|oxlint|prettier|typescript-eslint|ts-morph|jscodeshift|schematics|yeoman)\b/u])) {
246801
+ supportScore += 2;
246802
+ addEvidence6(evidence, "Package dependencies indicate lint/build/generation tooling");
246803
+ }
246422
246804
  if (scripts.length > 0 && scripts.every((script) => /^(test|e2e|smoke|bench|benchmark|lint|typecheck|dev|dev:|load-test|generate|scaffold)/u.test(script))) {
246423
246805
  supportScore += 1;
246424
246806
  addEvidence6(evidence, "Package scripts are dominated by development/test workflows");
@@ -246443,6 +246825,10 @@ function classifyGovernanceRole(input2) {
246443
246825
  supportScore += 4;
246444
246826
  addEvidence6(evidence, "Maven documentation tooling without JVM runtime source");
246445
246827
  }
246828
+ if (maven?.hasDeploySkipConfiguration && !hasMavenMainJvmSourceEvidence && !hasMavenRunnableApplicationEvidence) {
246829
+ supportScore += 4;
246830
+ addEvidence6(evidence, "Maven deploy-skipped artifact without runtime source");
246831
+ }
246446
246832
  if (hasMavenBomEvidence) {
246447
246833
  supportScore += 5;
246448
246834
  addEvidence6(evidence, "Maven BOM/dependency-management module without runtime source");
@@ -246471,6 +246857,10 @@ function classifyGovernanceRole(input2) {
246471
246857
  supportScore += 4;
246472
246858
  addEvidence6(evidence, "Maven starter/resource artifact without JVM runtime source");
246473
246859
  }
246860
+ if (hasMavenDistributionPackagingEvidence) {
246861
+ supportScore += 4;
246862
+ addEvidence6(evidence, "Maven distribution/release packaging without runtime source");
246863
+ }
246474
246864
  if (isPlainResourcesSurface) {
246475
246865
  supportScore += 4;
246476
246866
  addEvidence6(evidence, "Resources-only surface without runtime source or manifest evidence");
@@ -246493,7 +246883,7 @@ function classifyGovernanceRole(input2) {
246493
246883
  supportScore += 3;
246494
246884
  addEvidence6(evidence, "Maven documentation/build/distribution packaging evidence without runtime source");
246495
246885
  }
246496
- if (supportScore >= 3 && supportScore > runtimeScore || hasStrongSupportPath && supportScore >= 2 && runtimeScore <= 5 || maven?.packaging === "maven-plugin" && input2.candidate.path !== "." && supportScore >= 5 || maven && supportScore >= 5 && !hasMavenMainSource || maven && supportScore >= 4 && hasMavenTestContext || maven && supportScore >= 4 && hasMavenDistributionPackagingEvidence && hasAnyToken(mavenMetadataText, [/\b(docs?|documentation|metadata tests?|licenses?|maven plugins?|builds?)\b/u])) {
246886
+ if (supportScore >= 3 && supportScore > runtimeScore || hasStrongSupportPath && supportScore >= 2 && runtimeScore <= 5 || maven?.packaging === "maven-plugin" && input2.candidate.path !== "." && supportScore >= 5 || maven && supportScore >= 5 && !hasMavenMainJvmSourceEvidence && !hasMavenRunnableApplicationEvidence || maven && supportScore >= 4 && maven.hasDeploySkipConfiguration && !hasMavenMainJvmSourceEvidence && !hasMavenRunnableApplicationEvidence || maven && supportScore >= 4 && hasStrongSupportPath && !hasMavenMainJvmSourceEvidence && !hasMavenRunnableApplicationEvidence || maven && supportScore >= 4 && hasMavenTestContext || maven && supportScore >= 4 && hasMavenDistributionPackagingEvidence && hasAnyToken(mavenMetadataText, [/\b(docs?|documentation|metadata tests?|licenses?|maven plugins?|builds?)\b/u])) {
246497
246887
  return {
246498
246888
  governanceRole: "support",
246499
246889
  governanceEvidence: uniqueSorted4(evidence),
@@ -250154,6 +250544,9 @@ function readFlagValue(args, flag) {
250154
250544
  return value && value.length > 0 ? value : void 0;
250155
250545
  }
250156
250546
  async function readCliPackageVersion() {
250547
+ if ("0.2.6".trim().length > 0) {
250548
+ return "0.2.6".trim();
250549
+ }
250157
250550
  const candidatePackageJsonPaths = [
250158
250551
  path73.join(__dirname, "..", "package.json"),
250159
250552
  path73.join(__dirname, "..", "..", "..", "..", "cli", "package.json")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archpilotlabs/archpilot",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Executable architecture governance CLI for Smart Init, validation, drift detection, and CI.",
5
5
  "homepage": "https://archpilot.org",
6
6
  "bugs": {