@archpilotlabs/archpilot 0.2.5 → 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,35 @@
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
+
3
33
  ## 0.2.5
4
34
 
5
35
  ### 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;
@@ -225666,10 +225668,15 @@ function hasPaginationEvidence(statement) {
225666
225668
  );
225667
225669
  }
225668
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);
225669
225676
  return new RegExp(
225670
- `\\bwhere\\b[\\s\\S]*\\b(?:${columnPattern})\\b\\s*=\\s*(?:[\\w$:@?]+|'[^']+'|"[^"]+"|\\d+)`,
225677
+ `\\b(?:${columnPattern})\\b\\s*=\\s*(?:[\\w$:@?]+|'[^']+'|"[^"]+"|\\d+)`,
225671
225678
  "iu"
225672
- ).test(statement);
225679
+ ).test(predicateWindow);
225673
225680
  }
225674
225681
  function isCatalogIdentifierPointLookup(statement) {
225675
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;
@@ -226270,6 +226277,9 @@ function detectRepeatedQueryPattern(method) {
226270
226277
  }
226271
226278
  function extractSqlQueryLiterals(content) {
226272
226279
  const queries = [];
226280
+ if (!sqlQueryInvocationHintPattern.test(content)) {
226281
+ return queries;
226282
+ }
226273
226283
  const searchableContent = maskSourceComments(content);
226274
226284
  const patterns = [
226275
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,
@@ -226291,7 +226301,7 @@ function extractSqlQueryLiterals(content) {
226291
226301
  return queries;
226292
226302
  }
226293
226303
  function maskSourceComments(content) {
226294
- let masked = "";
226304
+ const masked = [];
226295
226305
  let index = 0;
226296
226306
  let state = "code";
226297
226307
  let escaped = false;
@@ -226300,27 +226310,27 @@ function maskSourceComments(content) {
226300
226310
  const next = content[index + 1] ?? "";
226301
226311
  if (state === "line-comment") {
226302
226312
  if (current === "\r" || current === "\n") {
226303
- masked += current;
226313
+ masked.push(current);
226304
226314
  state = "code";
226305
226315
  } else {
226306
- masked += " ";
226316
+ masked.push(" ");
226307
226317
  }
226308
226318
  index += 1;
226309
226319
  continue;
226310
226320
  }
226311
226321
  if (state === "block-comment") {
226312
226322
  if (current === "*" && next === "/") {
226313
- masked += " ";
226323
+ masked.push(" ");
226314
226324
  index += 2;
226315
226325
  state = "code";
226316
226326
  continue;
226317
226327
  }
226318
- masked += current === "\r" || current === "\n" ? current : " ";
226328
+ masked.push(current === "\r" || current === "\n" ? current : " ");
226319
226329
  index += 1;
226320
226330
  continue;
226321
226331
  }
226322
226332
  if (state === "single" || state === "double" || state === "template") {
226323
- masked += current;
226333
+ masked.push(current);
226324
226334
  if (escaped) {
226325
226335
  escaped = false;
226326
226336
  } else if (current === "\\") {
@@ -226332,13 +226342,13 @@ function maskSourceComments(content) {
226332
226342
  continue;
226333
226343
  }
226334
226344
  if (current === "/" && next === "/") {
226335
- masked += " ";
226345
+ masked.push(" ");
226336
226346
  index += 2;
226337
226347
  state = "line-comment";
226338
226348
  continue;
226339
226349
  }
226340
226350
  if (current === "/" && next === "*") {
226341
- masked += " ";
226351
+ masked.push(" ");
226342
226352
  index += 2;
226343
226353
  state = "block-comment";
226344
226354
  continue;
@@ -226350,10 +226360,10 @@ function maskSourceComments(content) {
226350
226360
  } else if (current === "`") {
226351
226361
  state = "template";
226352
226362
  }
226353
- masked += current;
226363
+ masked.push(current);
226354
226364
  index += 1;
226355
226365
  }
226356
- return masked;
226366
+ return masked.join("");
226357
226367
  }
226358
226368
  function detectCodeSqlQueryShapeFindings(file, config) {
226359
226369
  const findings = [];
@@ -226383,7 +226393,7 @@ function detectCodeSqlQueryShapeFindings(file, config) {
226383
226393
  target: firstTable ?? "sql-query"
226384
226394
  }));
226385
226395
  }
226386
- 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)) {
226387
226397
  findings.push(createCodeFinding({
226388
226398
  id: "AP-DQR-003",
226389
226399
  message: `Potential unbounded query risk: SELECT query${firstTable ? ` on table '${firstTable}'` : ""} has no LIMIT/OFFSET/FETCH pagination evidence.`,
@@ -226397,6 +226407,18 @@ function detectCodeSqlQueryShapeFindings(file, config) {
226397
226407
  }
226398
226408
  return findings;
226399
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
+ }
226400
226422
  function collectOrmReadEvidence(method) {
226401
226423
  const patterns = [
226402
226424
  [/\b(?:this\.)?prisma\.\w+\.findMany\s*\(/giu, "prisma.findMany"],
@@ -226514,7 +226536,11 @@ function detectCodeQueryRiskFindings(files, config, astCache) {
226514
226536
  if (isProductionBackend) {
226515
226537
  findings.push(...detectCodeSqlQueryShapeFindings(file, config));
226516
226538
  }
226517
- 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) {
226518
226544
  if (!isProductionBackend) {
226519
226545
  continue;
226520
226546
  }
@@ -229970,8 +229996,15 @@ function isStaticCollectionBackedEndpoint(endpoint) {
229970
229996
  }
229971
229997
  return isStaticCollectionExpression(stripStaticLiteralNoise(expression));
229972
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
+ }
229973
230006
  function detectPaginationFindings(endpoints, contractEndpoints) {
229974
- 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({
229975
230008
  id: RuleIds3.MISSING_PAGINATION,
229976
230009
  message: `Collection endpoint ${endpoint.httpMethod.toUpperCase()} ${endpoint.path} lacks visible pagination evidence.`,
229977
230010
  endpoint
@@ -230731,12 +230764,14 @@ function extractTypeScriptSignals(file, astCache) {
230731
230764
  ts6.forEachChild(node, visit);
230732
230765
  };
230733
230766
  visit(sourceFile);
230734
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230767
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230735
230768
  }
230736
230769
  function extractJavaSignals(file) {
230737
230770
  const publishes = [];
230738
230771
  const handlers2 = [];
230739
- 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
+ )) {
230740
230775
  const eventName = match[1] ?? "ApplicationEvent";
230741
230776
  handlers2.push({
230742
230777
  eventName,
@@ -230748,6 +230783,21 @@ function extractJavaSignals(file) {
230748
230783
  publishes: []
230749
230784
  });
230750
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
+ }
230751
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)) {
230752
230802
  const constructedType = match[2]?.split(".").at(-1);
230753
230803
  const eventName = constructedType ?? match[4] ?? match[5];
@@ -230765,7 +230815,23 @@ function extractJavaSignals(file) {
230765
230815
  assignedOrReturned: false
230766
230816
  });
230767
230817
  }
230768
- 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;
230769
230835
  }
230770
230836
  function extractPublishSignalsFromText(file, body, baseOffset, pattern) {
230771
230837
  const publishes = [];
@@ -230811,7 +230877,7 @@ function extractCsharpSignals(file) {
230811
230877
  publishes: extractPublishSignalsFromText(file, classBody, classOpenBrace >= 0 ? classOpenBrace : classStart, csharpPublishPattern)
230812
230878
  });
230813
230879
  }
230814
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230880
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230815
230881
  }
230816
230882
  function extractPythonSignals(file) {
230817
230883
  const publishes = extractPublishSignalsFromText(file, file.content, 0, pythonPublishPattern);
@@ -230834,7 +230900,7 @@ function extractPythonSignals(file) {
230834
230900
  publishes: extractPublishSignalsFromText(file, block, blockStart, pythonPublishPattern)
230835
230901
  });
230836
230902
  }
230837
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230903
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230838
230904
  }
230839
230905
  function extractPhpSignals(file) {
230840
230906
  const publishes = extractPublishSignalsFromText(file, file.content, 0, phpEventPublishPattern);
@@ -230859,7 +230925,7 @@ function extractPhpSignals(file) {
230859
230925
  publishes: extractPublishSignalsFromText(file, classBody, classOpenBrace >= 0 ? classOpenBrace : classStart, phpEventPublishPattern)
230860
230926
  });
230861
230927
  }
230862
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230928
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230863
230929
  }
230864
230930
  function extractGoSignals(file) {
230865
230931
  const publishes = extractPublishSignalsFromText(file, file.content, 0, goPublishPattern);
@@ -230879,7 +230945,7 @@ function extractGoSignals(file) {
230879
230945
  publishes: []
230880
230946
  });
230881
230947
  }
230882
- return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set() };
230948
+ return { publishes, handlers: handlers2, publicEventNames: /* @__PURE__ */ new Set(), eventTypeParents: /* @__PURE__ */ new Map() };
230883
230949
  }
230884
230950
  function isTypeScriptLikeExtension(extension) {
230885
230951
  return [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(extension);
@@ -230979,6 +231045,7 @@ function collectPublicTypeScriptEventNames(files, contract, astCache) {
230979
231045
  function collectSignals(files, contract, astCache) {
230980
231046
  const publishes = [];
230981
231047
  const handlers2 = [];
231048
+ const eventTypeParents = /* @__PURE__ */ new Map();
230982
231049
  const publicEventNames = collectPublicTypeScriptEventNames(files, contract, astCache);
230983
231050
  for (const file of files) {
230984
231051
  let signals;
@@ -231004,14 +231071,41 @@ function collectSignals(files, contract, astCache) {
231004
231071
  }
231005
231072
  publishes.push(...signals.publishes);
231006
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
+ }
231007
231081
  }
231008
- return { publishes, handlers: handlers2, publicEventNames };
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
+ }
231101
+ }
231102
+ return false;
231009
231103
  }
231010
231104
  function buildEventFindings(signals) {
231011
231105
  const findings = [];
231012
231106
  const handledNames = new Set(signals.handlers.map((handler) => handler.normalizedName));
231013
231107
  for (const publish of signals.publishes) {
231014
- if (!handledNames.has(publish.normalizedName)) {
231108
+ if (!isHandledEventName(publish.normalizedName, handledNames, signals.eventTypeParents)) {
231015
231109
  if (signals.publicEventNames.has(publish.normalizedName)) {
231016
231110
  continue;
231017
231111
  }
@@ -243013,8 +243107,6 @@ var ignoredRootPathPrefixes = [
243013
243107
  "static",
243014
243108
  "assets",
243015
243109
  "vendor",
243016
- "plugins",
243017
- "plugin",
243018
243110
  "themes",
243019
243111
  "theme",
243020
243112
  "generated",
@@ -243037,8 +243129,12 @@ function pushEvidence(evidence, message) {
243037
243129
  function safeReadDirectoryEntries(directoryPath) {
243038
243130
  try {
243039
243131
  return fs52.readdirSync(directoryPath, { withFileTypes: true });
243040
- } catch {
243041
- 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;
243042
243138
  }
243043
243139
  }
243044
243140
  function safeReadJsonFile(jsonPath) {
@@ -245612,8 +245708,6 @@ function isIgnoredComponentCandidatePath(componentPath) {
245612
245708
  "static",
245613
245709
  "assets",
245614
245710
  "vendor",
245615
- "plugins",
245616
- "plugin",
245617
245711
  "themes",
245618
245712
  "theme",
245619
245713
  "generated",
@@ -245634,6 +245728,36 @@ function pathExists19(pathValue) {
245634
245728
  return false;
245635
245729
  }
245636
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
+ }
245637
245761
  function hasManifest2(workspaceRoot, componentPath) {
245638
245762
  const absoluteRoot = componentPath === "." ? workspaceRoot : path64.join(workspaceRoot, ...componentPath.split("/"));
245639
245763
  return [
@@ -245641,7 +245765,8 @@ function hasManifest2(workspaceRoot, componentPath) {
245641
245765
  "pom.xml",
245642
245766
  "requirements.txt",
245643
245767
  "go.mod",
245644
- "composer.json"
245768
+ "composer.json",
245769
+ "project.json"
245645
245770
  ].some((fileName) => pathExists19(path64.join(absoluteRoot, fileName)));
245646
245771
  }
245647
245772
  function safeReadJson(filePath) {
@@ -245664,6 +245789,10 @@ function recordKeys(value) {
245664
245789
  function hasObject(value) {
245665
245790
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
245666
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
+ }
245667
245796
  function hasPackagePublishSurface(packageJson) {
245668
245797
  return hasObject(packageJson?.exports) || typeof packageJson?.main === "string" || typeof packageJson?.module === "string" || typeof packageJson?.types === "string" || hasObject(packageJson?.publishConfig) || Array.isArray(packageJson?.files);
245669
245798
  }
@@ -245686,6 +245815,132 @@ function readProjectJson(workspaceRoot, componentPath) {
245686
245815
  const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
245687
245816
  return safeReadJson(path64.join(absoluteRoot, "project.json"));
245688
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
+ }
245689
245944
  function readTextFileSafe(filePath) {
245690
245945
  try {
245691
245946
  return fs58.readFileSync(filePath, { encoding: "utf8" });
@@ -245732,6 +245987,8 @@ function readMavenMetadata(workspaceRoot, componentPath) {
245732
245987
  testScopedDependencyCount: dependencyBlocks.filter((block) => /<scope>\s*test\s*<\/scope>/iu.test(block)).length,
245733
245988
  totalDependencyCount: dependencyBlocks.length,
245734
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),
245735
245992
  hasTestLifecycleConfiguration: /maven-surefire-plugin|maven-failsafe-plugin|integration-test|failsafe|surefire/iu.test(normalizedPom),
245736
245993
  hasArquillianEvidence: /arquillian/iu.test(normalizedPom),
245737
245994
  hasMainJava: pathExists19(path64.join(absoluteRoot, "src", "main", "java")),
@@ -245864,6 +246121,12 @@ function buildRootCandidates(input2) {
245864
246121
  addCandidate(root, `Detected project root ${root}`);
245865
246122
  }
245866
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
+ }
245867
246130
  const mavenRoots = [".", ...input2.topology.detectedRoots ?? []];
245868
246131
  for (const candidate of collectMavenReactorCandidates(input2.workspaceRoot, mavenRoots)) {
245869
246132
  addCandidate(
@@ -245947,10 +246210,7 @@ function isUnderJvmResourceRoot4(componentPath) {
245947
246210
  return /(^|\/)src\/main\/resources(?:\/|$)/u.test(normalizePath25(componentPath).toLowerCase());
245948
246211
  }
245949
246212
  function hasFileUnderRoot(root, pattern) {
245950
- if (!pathExists19(root)) {
245951
- return false;
245952
- }
245953
- const entries = fs58.readdirSync(root, { recursive: true, withFileTypes: true });
246213
+ const entries = readDirectoryEntriesForTraversal(root, { recursive: true });
245954
246214
  return entries.some((entry) => entry.isFile() && pattern.test(entry.name));
245955
246215
  }
245956
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;
@@ -246068,10 +246328,7 @@ function readMavenProductionSourceFiles(workspaceRoot, componentPath) {
246068
246328
  ];
246069
246329
  const files = [];
246070
246330
  for (const sourceRoot of mainRoots) {
246071
- if (!pathExists19(sourceRoot)) {
246072
- continue;
246073
- }
246074
- const entries = fs58.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
246331
+ const entries = readDirectoryEntriesForTraversal(sourceRoot, { recursive: true });
246075
246332
  for (const entry of entries) {
246076
246333
  if (!entry.isFile() || !/\.(?:java|kt|scala)$/iu.test(entry.name)) {
246077
246334
  continue;
@@ -246243,10 +246500,7 @@ function hasRuntimeSourceFiles(workspaceRoot, componentPath) {
246243
246500
  path64.join(absoluteRoot, "server")
246244
246501
  ];
246245
246502
  for (const sourceRoot of sourceRoots) {
246246
- if (!pathExists19(sourceRoot)) {
246247
- continue;
246248
- }
246249
- const entries = fs58.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
246503
+ const entries = readDirectoryEntriesForTraversal(sourceRoot, { recursive: true });
246250
246504
  if (entries.some((entry) => entry.isFile() && /\.(?:ts|tsx|js|jsx|java|cs|py|php|go)$/iu.test(entry.name))) {
246251
246505
  return true;
246252
246506
  }
@@ -246254,10 +246508,7 @@ function hasRuntimeSourceFiles(workspaceRoot, componentPath) {
246254
246508
  return false;
246255
246509
  }
246256
246510
  function hasFileMatchingUnderRoot(root, pattern) {
246257
- if (!pathExists19(root)) {
246258
- return false;
246259
- }
246260
- const entries = fs58.readdirSync(root, { recursive: true, withFileTypes: true });
246511
+ const entries = readDirectoryEntriesForTraversal(root, { recursive: true });
246261
246512
  return entries.some((entry) => entry.isFile() && pattern.test(normalizePath25(path64.join(entry.parentPath, entry.name))));
246262
246513
  }
246263
246514
  function hasNestRuntimeApplicationEvidence(workspaceRoot, componentPath, packageJson = readPackageJson(workspaceRoot, componentPath)) {
@@ -246300,13 +246551,16 @@ function hasNestRuntimeApplicationEvidence(workspaceRoot, componentPath, package
246300
246551
  function resolveKind(workspaceRoot, candidate, localProjectKinds) {
246301
246552
  const lowerPath = candidate.path.toLowerCase();
246302
246553
  const packageJson = readPackageJson(workspaceRoot, candidate.path);
246554
+ const projectJson = readProjectJson(workspaceRoot, candidate.path);
246555
+ const nxProjectType = readStringProperty(projectJson, "projectType");
246303
246556
  const maven = readMavenMetadata(workspaceRoot, candidate.path);
246304
246557
  const dependencies = new Set(packageDependencyNames(packageJson));
246305
246558
  const hasRunnableFrontendEvidence = hasRunnableFrontendComponentEvidence(workspaceRoot, candidate.path);
246306
246559
  const hasNestRuntimeEvidence = hasNestRuntimeApplicationEvidence(workspaceRoot, candidate.path, packageJson);
246560
+ const nxRuntimeKind = classifyNxRuntimeKind(projectJson);
246307
246561
  const hasPublishSurface = hasPackagePublishSurface(packageJson);
246308
246562
  const hasMavenRunnableEvidence = Boolean(
246309
- 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))
246310
246564
  );
246311
246565
  const hasMavenLibraryEvidence = Boolean(
246312
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)
@@ -246314,6 +246568,12 @@ function resolveKind(workspaceRoot, candidate, localProjectKinds) {
246314
246568
  if (lowerPath.endsWith("/db") || lowerPath.endsWith("/database") || lowerPath.endsWith("/data")) {
246315
246569
  return "data";
246316
246570
  }
246571
+ if (nxRuntimeKind) {
246572
+ return nxRuntimeKind;
246573
+ }
246574
+ if (nxProjectType === "library") {
246575
+ return "library";
246576
+ }
246317
246577
  if (hasMavenLibraryEvidence) {
246318
246578
  return "library";
246319
246579
  }
@@ -246413,7 +246673,10 @@ function classifyGovernanceRole(input2) {
246413
246673
  const scriptText = scripts.join(" ").toLowerCase();
246414
246674
  const projectMetadataText = `${projectTags.join(" ")} ${projectTargets.join(" ")}`.toLowerCase();
246415
246675
  const dependencyText = [...dependencies, ...devDependencies].join(" ").toLowerCase();
246676
+ const hasRunnableFrontendEvidence = hasRunnableFrontendComponentEvidence(input2.workspaceRoot, input2.candidate.path);
246416
246677
  const hasNestRuntimeEvidence = hasNestRuntimeApplicationEvidence(input2.workspaceRoot, input2.candidate.path, packageJson);
246678
+ const nxRuntimeKind = classifyNxRuntimeKind(projectJson);
246679
+ const hasNxSupportOnlyEvidence = hasNxSupportOnlyProjectEvidence(projectJson);
246417
246680
  const hasStrongSupportPath = hasAnyToken(pathText, [
246418
246681
  /(^|\/)(testing|e2e-testing|test-plugins|cypress-tests|fixtures?|benchmarks?|smoke)(\/|$)/u,
246419
246682
  /(^|\/)(dev-server|scaffold|scaffolding|generators?|devkit|tooling|tools|templates?)(\/|$)/u
@@ -246428,9 +246691,11 @@ function classifyGovernanceRole(input2) {
246428
246691
  const hasMavenMainEntrypointEvidence = Boolean(maven && hasMavenMainEntrypoint(input2.workspaceRoot, input2.candidate.path));
246429
246692
  const hasMavenMainJvmSourceEvidence = Boolean(maven && hasMavenMainJvmSource(input2.workspaceRoot, input2.candidate.path));
246430
246693
  const hasMavenAutoConfigurationEvidence = Boolean(maven && (hasMavenAutoConfigurationMetadata(input2.workspaceRoot, input2.candidate.path) || maven.hasSpringBootAutoconfigureDependency || maven.hasSpringBootAutoconfigureProcessor));
246431
- 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));
246432
246695
  const hasMavenRunnableApplicationEvidence = Boolean(hasMavenMainEntrypointEvidence || hasMavenExecutableConfiguration);
246433
- const isMavenPomOnly = Boolean(maven?.packaging === "pom" && !hasMavenMainSource && !hasMavenTestSource);
246696
+ const isMavenPomOnly = Boolean(
246697
+ maven?.packaging === "pom" && !hasMavenRunnableApplicationEvidence && !hasMavenMainSource && !hasMavenTestSource
246698
+ );
246434
246699
  const hasMavenTestContext = hasAnyToken(pathText, [
246435
246700
  /(^|\/)(test-framework|testsuite|integration-tests?|test-apps)(\/|$)/u
246436
246701
  ]) && (hasAnyToken(mavenMetadataText, [/\b(test|testsuite|test framework|arquillian|integration test)\b/u]) || Boolean(maven?.hasTestLifecycleConfiguration || maven?.hasArquillianEvidence || hasMavenTestSource));
@@ -246438,7 +246703,7 @@ function classifyGovernanceRole(input2) {
246438
246703
  maven?.packaging === "pom" && maven.hasDependencyManagement && !hasMavenMainSource && hasAnyToken(mavenMetadataText, [/\b(bom|bill of materials|dependency management|platform)\b/u])
246439
246704
  );
246440
246705
  const hasMavenDistributionPackagingEvidence = Boolean(
246441
- 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]))
246442
246707
  );
246443
246708
  const hasMavenStarterPackagingEvidence = Boolean(
246444
246709
  maven && !hasMavenMainJvmSourceEvidence && hasAnyToken(`${pathText} ${mavenMetadataText}`, [/\bstarters?\b|\bspring-boot-starter\b|\bstarter[-\s]/u])
@@ -246465,6 +246730,18 @@ function classifyGovernanceRole(input2) {
246465
246730
  runtimeScore += 3;
246466
246731
  addEvidence6(evidence, "NestJS runtime application evidence");
246467
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
+ }
246468
246745
  if (hasPublishSurface && !isPrivate) {
246469
246746
  runtimeScore += 1;
246470
246747
  addEvidence6(evidence, "Published package surface signal");
@@ -246475,7 +246752,7 @@ function classifyGovernanceRole(input2) {
246475
246752
  if (maven?.hasPom) {
246476
246753
  addEvidence6(evidence, `Maven packaging ${maven.packaging ?? "jar"}`);
246477
246754
  }
246478
- if (maven && (maven.packaging === "jar" || maven.packaging === "war") && hasMavenRunnableApplicationEvidence) {
246755
+ if (maven && hasMavenRunnableApplicationEvidence) {
246479
246756
  runtimeScore += 3;
246480
246757
  addEvidence6(evidence, "Maven executable application evidence");
246481
246758
  }
@@ -246512,6 +246789,10 @@ function classifyGovernanceRole(input2) {
246512
246789
  supportScore += 1;
246513
246790
  addEvidence6(evidence, "Path contains support/tooling role tokens");
246514
246791
  }
246792
+ if (maven && hasStrongSupportPath && !hasMavenRunnableApplicationEvidence) {
246793
+ supportScore += 3;
246794
+ addEvidence6(evidence, "Maven module path and metadata indicate support/tooling role");
246795
+ }
246515
246796
  if (hasAnyToken(pathText, [/(^|\/)(?:src\/constants\/)?templates?(\/|$)/u])) {
246516
246797
  supportScore += 4;
246517
246798
  addEvidence6(evidence, "Nested scaffold/template source surface");
@@ -246544,6 +246825,10 @@ function classifyGovernanceRole(input2) {
246544
246825
  supportScore += 4;
246545
246826
  addEvidence6(evidence, "Maven documentation tooling without JVM runtime source");
246546
246827
  }
246828
+ if (maven?.hasDeploySkipConfiguration && !hasMavenMainJvmSourceEvidence && !hasMavenRunnableApplicationEvidence) {
246829
+ supportScore += 4;
246830
+ addEvidence6(evidence, "Maven deploy-skipped artifact without runtime source");
246831
+ }
246547
246832
  if (hasMavenBomEvidence) {
246548
246833
  supportScore += 5;
246549
246834
  addEvidence6(evidence, "Maven BOM/dependency-management module without runtime source");
@@ -246572,6 +246857,10 @@ function classifyGovernanceRole(input2) {
246572
246857
  supportScore += 4;
246573
246858
  addEvidence6(evidence, "Maven starter/resource artifact without JVM runtime source");
246574
246859
  }
246860
+ if (hasMavenDistributionPackagingEvidence) {
246861
+ supportScore += 4;
246862
+ addEvidence6(evidence, "Maven distribution/release packaging without runtime source");
246863
+ }
246575
246864
  if (isPlainResourcesSurface) {
246576
246865
  supportScore += 4;
246577
246866
  addEvidence6(evidence, "Resources-only surface without runtime source or manifest evidence");
@@ -246594,7 +246883,7 @@ function classifyGovernanceRole(input2) {
246594
246883
  supportScore += 3;
246595
246884
  addEvidence6(evidence, "Maven documentation/build/distribution packaging evidence without runtime source");
246596
246885
  }
246597
- 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])) {
246598
246887
  return {
246599
246888
  governanceRole: "support",
246600
246889
  governanceEvidence: uniqueSorted4(evidence),
@@ -250255,8 +250544,8 @@ function readFlagValue(args, flag) {
250255
250544
  return value && value.length > 0 ? value : void 0;
250256
250545
  }
250257
250546
  async function readCliPackageVersion() {
250258
- if ("0.2.5".trim().length > 0) {
250259
- return "0.2.5".trim();
250547
+ if ("0.2.6".trim().length > 0) {
250548
+ return "0.2.6".trim();
250260
250549
  }
250261
250550
  const candidatePackageJsonPaths = [
250262
250551
  path73.join(__dirname, "..", "package.json"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archpilotlabs/archpilot",
3
- "version": "0.2.5",
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": {