nexarch 0.1.22 → 0.1.24

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.
@@ -4,7 +4,7 @@ import { join } from "path";
4
4
  import process from "process";
5
5
  import { requireCredentials } from "../lib/credentials.js";
6
6
  import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
7
- const CLI_VERSION = "0.1.22";
7
+ const CLI_VERSION = "0.1.24";
8
8
  const AGENT_ENTITY_TYPE = "agent";
9
9
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
10
10
  function parseFlag(args, flag) {
@@ -25,7 +25,7 @@ function slugify(name) {
25
25
  }
26
26
  // ─── Project scanning ─────────────────────────────────────────────────────────
27
27
  // Noise patterns for env var keys that are internal config, not external service references
28
- const ENV_KEY_NOISE = /^(NODE_ENV|PORT|HOST|DEBUG|LOG_LEVEL|TZ|LANG|PATH|HOME|USER|SHELL|TERM)$|(_LOG_LEVEL|_MAX_|_MIN_|_DEFAULT_|_TIMEOUT|_DELAY|_JOBS|_INTERVAL|_LIMIT|_RETRIES|_CONCURREN|_WORKERS)$|^NEXT_PUBLIC_(APP|ADMIN|API|SITE|BASE|WEB)_URL$/;
28
+ const ENV_KEY_NOISE = /^(NODE_ENV|PORT|HOST|DEBUG|LOG_LEVEL|TZ|LANG|PATH|HOME|USER|SHELL|TERM)$|(_LOG_LEVEL|_MAX_|_MIN_|_DEFAULT_|_TIMEOUT|_DELAY|_JOBS|_INTERVAL|_LIMIT|_RETRIES|_CONCURREN|_WORKERS)$|^NEXT_PUBLIC_(APP|ADMIN|API|SITE|BASE|WEB)_URL$|(_URL|_SECRET|_TOKEN|_KEY|_PASSWORD|_CREDENTIAL|_DSN|_URI)$/;
29
29
  function parseEnvKeys(content) {
30
30
  return content
31
31
  .split("\n")
@@ -97,9 +97,9 @@ const SKIP_DIRS = new Set(["node_modules", "dist", ".next", ".turbo", "build", "
97
97
  function collectPackageDeps(pkgPath) {
98
98
  try {
99
99
  const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
100
+ // devDependencies are build/test/type tooling — no architectural value
100
101
  return Object.keys({
101
102
  ...pkg.dependencies,
102
- ...pkg.devDependencies,
103
103
  ...pkg.peerDependencies,
104
104
  });
105
105
  }
@@ -205,13 +205,16 @@ function scanProject(dir) {
205
205
  const names = new Set();
206
206
  let projectName = basename(dir);
207
207
  const subPackages = [];
208
+ let rootDepNames = new Set();
208
209
  const pkgPaths = findPackageJsonPaths(dir);
209
210
  const rootPkgPath = join(dir, "package.json");
210
211
  for (const pkgPath of pkgPaths) {
211
212
  const pkg = readRootPackage(pkgPath);
213
+ const deps = collectPackageDeps(pkgPath);
212
214
  if (pkgPath === rootPkgPath) {
213
215
  if (pkg.name)
214
216
  projectName = pkg.name;
217
+ rootDepNames = new Set(deps);
215
218
  }
216
219
  else {
217
220
  // Record as a sub-package for enrichment task
@@ -220,9 +223,10 @@ function scanProject(dir) {
220
223
  name: pkg.name ?? relativePath,
221
224
  relativePath,
222
225
  packageJsonPath: pkgPath,
226
+ depNames: deps,
223
227
  });
224
228
  }
225
- for (const dep of collectPackageDeps(pkgPath))
229
+ for (const dep of deps)
226
230
  names.add(dep);
227
231
  }
228
232
  // .env files at root
@@ -241,7 +245,7 @@ function scanProject(dir) {
241
245
  names.add(name);
242
246
  for (const name of detectFromFilesystem(dir))
243
247
  names.add(name);
244
- return { projectName, packageJsonCount: pkgPaths.length, detectedNames: Array.from(names), subPackages };
248
+ return { projectName, packageJsonCount: pkgPaths.length, detectedNames: Array.from(names), rootDepNames, subPackages };
245
249
  }
246
250
  // ─── Relationship type selection ──────────────────────────────────────────────
247
251
  function pickRelationshipType(entityTypeCode) {
@@ -268,7 +272,7 @@ export async function initProject(args) {
268
272
  const mcpOpts = { companyId: creds.companyId };
269
273
  if (!asJson)
270
274
  console.log(`Scanning ${dir}…`);
271
- const { projectName, packageJsonCount, detectedNames, subPackages } = scanProject(dir);
275
+ const { projectName, packageJsonCount, detectedNames, rootDepNames, subPackages } = scanProject(dir);
272
276
  const displayName = nameOverride ?? projectName;
273
277
  const projectSlug = slugify(displayName);
274
278
  const projectExternalKey = `${entityTypeOverride}:${projectSlug}`;
@@ -358,12 +362,24 @@ export async function initProject(args) {
358
362
  confidence: 0.95,
359
363
  });
360
364
  }
361
- // Build relationships from project entity to each resolved dependency
365
+ // Build a lookup from input name to resolved result for enrichment task use
366
+ const resolvedByInput = new Map();
367
+ for (const r of resolvedItems) {
368
+ if (r.canonicalExternalRef)
369
+ resolvedByInput.set(r.input, r);
370
+ if (r.normalised && r.canonicalExternalRef)
371
+ resolvedByInput.set(r.normalised, r);
372
+ }
373
+ // Build relationships — only wire root-level deps to the project entity.
374
+ // Sub-package deps are wired during enrichment (the agent does it per sub-app).
362
375
  const relationships = [];
363
376
  const seenRelPairs = new Set();
364
377
  for (const r of resolvedItems) {
365
378
  if (!r.canonicalExternalRef || !r.entityTypeCode)
366
379
  continue;
380
+ // Skip deps that don't belong to the root package — only wire root-level deps here
381
+ if (!rootDepNames.has(r.input) && !rootDepNames.has(r.normalised))
382
+ continue;
367
383
  const relType = pickRelationshipType(r.entityTypeCode);
368
384
  const pairKey = `${relType}::${projectExternalKey}::${r.canonicalExternalRef}`;
369
385
  if (seenRelPairs.has(pairKey))
@@ -465,7 +481,7 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
465
481
  Type definitions or utility package with no runtime
466
482
  → --entity-type technology_component --subtype tech_library
467
483
 
468
- For each sub-package, run:
484
+ For each sub-package, run update-entity to register it:
469
485
 
470
486
  npx nexarch update-entity \\
471
487
  --key "<entity-type>:<slugified-package-name>" \\
@@ -474,12 +490,46 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
474
490
  --name "<human readable name>" \\
475
491
  --description "<what this package does and its role in the project>"
476
492
 
477
- Then wire it to the parent project using the appropriate relationship:
478
- • Deployable apps → relationship type: part_of
479
- • Shared libraries → relationship type: depends_on (parent depends on the library)
493
+ Then register it as a resolvable alias so future scans don't re-surface it as a candidate:
494
+
495
+ npx nexarch register-alias \\
496
+ --alias "<original package name e.g. @scope/name>" \\
497
+ --key "<same entity-type>:<slugified-package-name>" \\
498
+ --name "<same human readable name>" \\
499
+ --entity-type "<same entity type>"
500
+
501
+ ⚠️ DIRECTION MATTERS — wire relationships as follows:
502
+
503
+ Deployable sub-apps (apps/*) are PART OF the parent — child points to parent:
504
+ npx nexarch add-relationship \\
505
+ --from "<sub-app-key>" \\
506
+ --to "${projectExternalKey}" \\
507
+ --type "part_of"
508
+ (FROM = the sub-app, TO = ${projectExternalKey})
509
+
510
+ Shared libraries (packages/*) are depended on by the parent — parent points to library:
511
+ npx nexarch add-relationship \\
512
+ --from "${projectExternalKey}" \\
513
+ --to "<library-key>" \\
514
+ --type "depends_on"
515
+ (FROM = ${projectExternalKey}, TO = the library)
516
+
517
+ ⚠️ WIRE DEPENDENCIES TO THE SUB-APP THAT DECLARES THEM, NOT TO THE PARENT.
518
+ After registering each sub-app/library entity, wire its npm dependencies to IT:
519
+ npx nexarch add-relationship \\
520
+ --from "<sub-app-or-library-key>" \\
521
+ --to "<resolved-tech-ref>" \\
522
+ --type "depends_on"
480
523
 
481
- Relationship type reference:
482
- part_of — deployable sub-app is structurally part of the parent product
483
- depends_on — parent product depends on a shared library at runtime
524
+ Pre-resolved dependencies per sub-package (wire these after registering each entity):
525
+ ${subPackages.map((sp) => {
526
+ const resolved = sp.depNames
527
+ .map((d) => resolvedByInput.get(d))
528
+ .filter((r) => !!r?.canonicalExternalRef);
529
+ if (resolved.length === 0)
530
+ return ` • ${sp.name}: (no pre-resolved deps — check package.json manually)`;
531
+ const lines = resolved.map((r) => ` --to "${r.canonicalExternalRef}" # ${r.canonicalName}`);
532
+ return ` • ${sp.name} (<sub-app-key> depends_on):\n${lines.join("\n")}`;
533
+ }).join("\n\n")}
484
534
  ` : ""}`);
485
535
  }
@@ -0,0 +1,61 @@
1
+ import process from "process";
2
+ import { requireCredentials } from "../lib/credentials.js";
3
+ import { callMcpTool } from "../lib/mcp.js";
4
+ function parseFlag(args, flag) {
5
+ return args.includes(flag);
6
+ }
7
+ function parseOptionValue(args, option) {
8
+ const idx = args.indexOf(option);
9
+ if (idx === -1)
10
+ return null;
11
+ const value = args[idx + 1];
12
+ if (!value || value.startsWith("--"))
13
+ return null;
14
+ return value;
15
+ }
16
+ function parseToolText(result) {
17
+ const text = result.content?.[0]?.text ?? "{}";
18
+ return JSON.parse(text);
19
+ }
20
+ export async function registerAlias(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ const alias = parseOptionValue(args, "--alias");
23
+ const key = parseOptionValue(args, "--key");
24
+ const name = parseOptionValue(args, "--name");
25
+ const entityTypeCode = parseOptionValue(args, "--entity-type");
26
+ const entitySubtypeCode = parseOptionValue(args, "--subtype");
27
+ const description = parseOptionValue(args, "--description");
28
+ if (!alias) {
29
+ console.error("error: --alias <value> is required");
30
+ process.exit(1);
31
+ }
32
+ if (!key) {
33
+ console.error("error: --key <externalKey> is required");
34
+ process.exit(1);
35
+ }
36
+ if (!name) {
37
+ console.error("error: --name <name> is required");
38
+ process.exit(1);
39
+ }
40
+ if (!entityTypeCode) {
41
+ console.error("error: --entity-type <code> is required");
42
+ process.exit(1);
43
+ }
44
+ const creds = requireCredentials();
45
+ const mcpOpts = { companyId: creds.companyId };
46
+ const raw = await callMcpTool("nexarch_register_alias", {
47
+ alias,
48
+ canonicalExternalKey: key,
49
+ canonicalName: name,
50
+ entityTypeCode,
51
+ ...(entitySubtypeCode ? { entitySubtypeCode } : {}),
52
+ ...(description ? { description } : {}),
53
+ companyId: creds.companyId,
54
+ }, mcpOpts);
55
+ const result = parseToolText(raw);
56
+ if (asJson) {
57
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
58
+ return;
59
+ }
60
+ console.log(`Registered alias "${result.alias}" → ${result.canonicalExternalKey}`);
61
+ }
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { agentIdentify } from "./commands/agent-identify.js";
13
13
  import { initProject } from "./commands/init-project.js";
14
14
  import { updateEntity } from "./commands/update-entity.js";
15
15
  import { addRelationship } from "./commands/add-relationship.js";
16
+ import { registerAlias } from "./commands/register-alias.js";
16
17
  const [, , command, ...args] = process.argv;
17
18
  const commands = {
18
19
  login,
@@ -26,6 +27,7 @@ const commands = {
26
27
  "init-project": initProject,
27
28
  "update-entity": updateEntity,
28
29
  "add-relationship": addRelationship,
30
+ "register-alias": registerAlias,
29
31
  };
30
32
  async function main() {
31
33
  if (command === "agent") {
@@ -101,6 +103,17 @@ Usage:
101
103
  --to <externalKey> (required)
102
104
  --type <code> (required, e.g. part_of, depends_on)
103
105
  --json
106
+ nexarch register-alias
107
+ Register a company-scoped alias for an entity so future
108
+ scans resolve it instead of logging it as a candidate.
109
+ Use after enriching internal monorepo packages.
110
+ Options: --alias <value> (required, e.g. @scope/name)
111
+ --key <externalKey> (required)
112
+ --name <name> (required)
113
+ --entity-type <code> (required)
114
+ --subtype <code>
115
+ --description <text>
116
+ --json
104
117
  `);
105
118
  process.exit(command ? 1 : 0);
106
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",