nexarch 0.1.28 → 0.1.30

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.28";
7
+ const CLI_VERSION = "0.1.30";
8
8
  const AGENT_ENTITY_TYPE = "agent";
9
9
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
10
10
  function parseFlag(args, flag) {
@@ -249,7 +249,6 @@ function scanProject(dir) {
249
249
  const relativePath = pkgPath.replace(dir + "/", "").replace("/package.json", "");
250
250
  const packageName = pkg.name ?? relativePath;
251
251
  const { entityType, subtype } = classifySubPackage(pkgPath, relativePath);
252
- const externalKey = `${entityType}:${slugify(packageName)}`;
253
252
  subPackages.push({
254
253
  name: packageName,
255
254
  relativePath,
@@ -257,7 +256,7 @@ function scanProject(dir) {
257
256
  depNames: deps,
258
257
  entityType,
259
258
  subtype,
260
- externalKey,
259
+ externalKey: "", // computed after scan once projectSlug is known
261
260
  });
262
261
  }
263
262
  for (const dep of deps)
@@ -315,6 +314,15 @@ export async function initProject(args) {
315
314
  const displayName = nameOverride ?? projectName;
316
315
  const projectSlug = slugify(displayName);
317
316
  const projectExternalKey = `${entityTypeOverride}:${projectSlug}`;
317
+ // Compute sub-package external keys now that projectSlug is known.
318
+ // Unscoped names (no "/") get prefixed with the project slug to avoid ambiguous keys
319
+ // like "application:crawler" — they become "application:whatsontap-crawler" instead,
320
+ // matching what the enrichment agent would naturally choose.
321
+ for (const sp of subPackages) {
322
+ const nameSlug = slugify(sp.name);
323
+ const needsPrefix = !sp.name.includes("/") && !nameSlug.startsWith(projectSlug);
324
+ sp.externalKey = `${sp.entityType}:${needsPrefix ? `${projectSlug}-${nameSlug}` : nameSlug}`;
325
+ }
318
326
  if (!asJson) {
319
327
  console.log(` Project : ${displayName} (${entityTypeOverride})`);
320
328
  console.log(` Packages: ${packageJsonCount} package.json file(s)`);
@@ -532,11 +540,13 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
532
540
  (FROM = ${projectExternalKey}, TO = the library)
533
541
 
534
542
  ⚠️ WIRE DEPENDENCIES TO THE SUB-APP THAT DECLARES THEM, NOT TO THE PARENT.
535
- After registering each sub-app/library entity, wire its npm dependencies to IT:
536
- npx nexarch add-relationship \\
537
- --from "<sub-app-or-library-key>" \\
538
- --to "<resolved-tech-ref>" \\
539
- --type "depends_on"
543
+ After registering each sub-app/library entity, wire its npm dependencies to IT.
544
+
545
+ RELATIONSHIP TYPE RULES:
546
+ application → technology_component : --type "depends_on"
547
+ technology_component → technology_component : --type "uses" (NOT depends_on)
548
+ any → platform / platform_component : --type "uses"
549
+ any → model : --type "uses_model"
540
550
 
541
551
  Pre-resolved dependencies per sub-package (wire these after registering each entity):
542
552
  ${subPackages.map((sp) => {
@@ -545,9 +555,47 @@ ${subPackages.map((sp) => {
545
555
  .filter((r) => !!r?.canonicalExternalRef);
546
556
  if (resolved.length === 0)
547
557
  return ` • ${sp.name}: (no pre-resolved deps — check package.json manually)`;
548
- const lines = resolved.map((r) => ` --to "${r.canonicalExternalRef}" # ${r.canonicalName}`);
549
- return ` • ${sp.name} (<sub-app-key> depends_on):\n${lines.join("\n")}`;
558
+ const lines = resolved.map((r) => {
559
+ const relType = pickRelationshipType(r.entityTypeCode, sp.entityType);
560
+ return ` --to "${r.canonicalExternalRef}" --type "${relType}" # ${r.canonicalName}`;
561
+ });
562
+ return ` • ${sp.name} (--from "${sp.externalKey}"):\n${lines.join("\n")}`;
550
563
  }).join("\n\n")}
564
+ `;
565
+ const finalStep = subPackages.length > 0 ? "STEP 4" : "STEP 3";
566
+ const gapCheckSection = `
567
+ ${finalStep} — Identify architecturally significant components not auto-detected.
568
+
569
+ The mechanical scan only finds npm packages, env var names, and a few config files.
570
+ It systematically misses hosting platforms, managed infrastructure, SaaS integrations,
571
+ and external APIs that are only mentioned in README or deployment config.
572
+
573
+ Review what you've read and ask yourself: are there any of the following that are
574
+ clearly part of this system's architecture but weren't auto-detected?
575
+
576
+ • Hosting / deployment platforms (Vercel, Railway, Fly.io, AWS, GCP, Azure…)
577
+ • Managed data services (Neon, PlanetScale, Supabase, Upstash, Atlas…)
578
+ • Auth / identity providers (Clerk, Auth0, WorkOS, Okta…)
579
+ • External SaaS integrations (Stripe, Resend, SendGrid, Twilio, Sentry…)
580
+ • CDN / storage / queues (Cloudflare, S3, CloudFront, SQS, Redis…)
581
+ • CI/CD platforms (GitHub Actions, GitLab CI, CircleCI…)
582
+
583
+ ONLY register entries you are highly confident about — i.e. explicitly mentioned in
584
+ the README, a config file, or a deployment manifest. Do not guess.
585
+
586
+ For each one you identify:
587
+
588
+ 1. Resolve it to a canonical reference:
589
+ npx nexarch resolve-names --names "<platform name>"
590
+
591
+ 2. If resolved, wire the relationship from the relevant entity:
592
+ npx nexarch add-relationship \\
593
+ --from "${projectExternalKey}" \\
594
+ --to "<resolved-ref>" \\
595
+ --type "uses"
596
+
597
+ 3. If unresolved (not in the reference library), skip it for now — it will appear
598
+ as a reference candidate on the next scan once you register an alias for it.
551
599
  `;
552
600
  return `
553
601
  ╔══════════════════════════════════════════════════════════════════╗
@@ -574,7 +622,7 @@ STEP 2 — Enrich the project entity. Run this command with the description you'
574
622
  --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? " \\\n --subtype \"app_custom_built\"" : ""} \\
575
623
  --name "<proper product name from README>" \\
576
624
  --description "<2–4 sentence summary of what it does and why>"
577
- ${subPkgSection}`;
625
+ ${subPkgSection}${gapCheckSection}`;
578
626
  }
579
627
  const enrichmentTask = {
580
628
  instructions: buildEnrichmentInstructions(),
@@ -0,0 +1,52 @@
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 resolveNames(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ const namesRaw = parseOptionValue(args, "--names");
23
+ if (!namesRaw) {
24
+ console.error("error: --names <comma-separated list> is required");
25
+ process.exit(1);
26
+ }
27
+ const names = namesRaw
28
+ .split(",")
29
+ .map((n) => n.trim())
30
+ .filter(Boolean);
31
+ if (names.length === 0) {
32
+ console.error("error: --names must contain at least one name");
33
+ process.exit(1);
34
+ }
35
+ const creds = requireCredentials();
36
+ const mcpOpts = { companyId: creds.companyId };
37
+ const raw = await callMcpTool("nexarch_resolve_reference", { names, companyId: creds.companyId }, mcpOpts);
38
+ const result = parseToolText(raw);
39
+ if (asJson) {
40
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
41
+ return;
42
+ }
43
+ for (const entry of result.resolved ?? []) {
44
+ if (entry.resolved) {
45
+ console.log(`✓ ${entry.input} → ${entry.canonicalExternalRef} (${entry.entityTypeCode}${entry.entitySubtypeCode ? `/${entry.entitySubtypeCode}` : ""}) "${entry.canonicalName}"`);
46
+ }
47
+ else {
48
+ console.log(`✗ ${entry.input} — not found in reference library`);
49
+ }
50
+ }
51
+ console.log(`\n${result.totalResolved}/${result.totalRequested} resolved`);
52
+ }
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ 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
16
  import { registerAlias } from "./commands/register-alias.js";
17
+ import { resolveNames } from "./commands/resolve-names.js";
17
18
  const [, , command, ...args] = process.argv;
18
19
  const commands = {
19
20
  login,
@@ -28,6 +29,7 @@ const commands = {
28
29
  "update-entity": updateEntity,
29
30
  "add-relationship": addRelationship,
30
31
  "register-alias": registerAlias,
32
+ "resolve-names": resolveNames,
31
33
  };
32
34
  async function main() {
33
35
  if (command === "agent") {
@@ -114,6 +116,13 @@ Usage:
114
116
  --subtype <code>
115
117
  --description <text>
116
118
  --json
119
+ nexarch resolve-names
120
+ Look up one or more raw names (package names, platform
121
+ names) against the global reference library and return
122
+ their canonical external keys. Useful for gap-check
123
+ results before calling add-relationship.
124
+ Options: --names <csv> (required, e.g. "vercel,neon")
125
+ --json
117
126
  `);
118
127
  process.exit(command ? 1 : 0);
119
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",