nexarch 0.1.30 → 0.1.32

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.
@@ -17,6 +17,29 @@ function parseToolText(result) {
17
17
  const text = result.content?.[0]?.text ?? "{}";
18
18
  return JSON.parse(text);
19
19
  }
20
+ async function tryCreateStubEntity(externalKey, mcpOpts, agentContext, policyContext) {
21
+ // Only auto-create global reference entities — company entities must be created explicitly
22
+ if (!externalKey.startsWith("global:"))
23
+ return false;
24
+ // Derive a normalised alias from the external key (e.g. "global:platform:vercel" → "vercel")
25
+ const parts = externalKey.split(":");
26
+ const alias = parts[parts.length - 1];
27
+ const resolveRaw = await callMcpTool("nexarch_resolve_reference", { names: [alias], companyId: mcpOpts.companyId }, mcpOpts);
28
+ const resolveResult = parseToolText(resolveRaw);
29
+ const match = resolveResult.results?.find((r) => r.resolved && r.canonicalExternalRef === externalKey);
30
+ if (!match)
31
+ return false;
32
+ const entity = {
33
+ externalKey,
34
+ entityTypeCode: match.entityTypeCode,
35
+ name: match.canonicalName,
36
+ confidence: 1,
37
+ };
38
+ if (match.entitySubtypeCode)
39
+ entity.entitySubtypeCode = match.entitySubtypeCode;
40
+ await callMcpTool("nexarch_upsert_entities", { entities: [entity], agentContext, policyContext }, mcpOpts);
41
+ return true;
42
+ }
20
43
  export async function addRelationship(args) {
21
44
  const asJson = parseFlag(args, "--json");
22
45
  const fromKey = parseOptionValue(args, "--from");
@@ -58,8 +81,33 @@ export async function addRelationship(args) {
58
81
  toEntityExternalKey: toKey,
59
82
  confidence: 1,
60
83
  };
61
- const raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
62
- const result = parseToolText(raw);
84
+ let raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
85
+ let result = parseToolText(raw);
86
+ // Auto-create global reference stubs if endpoints are missing, then retry once
87
+ const missingEndpoint = result.errors?.some((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND");
88
+ if (missingEndpoint) {
89
+ const failedFromKeys = result.errors
90
+ ?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.fromEntityExternalKey)
91
+ .map((e) => e.fromEntityExternalKey);
92
+ const failedToKeys = result.errors
93
+ ?.filter((e) => e.error === "RELATIONSHIP_ENDPOINT_NOT_FOUND" && e.toEntityExternalKey)
94
+ .map((e) => e.toEntityExternalKey);
95
+ const keysToCreate = [...new Set([...(failedFromKeys ?? []), ...(failedToKeys ?? [])])];
96
+ // If no specific endpoint keys returned, fall back to trying both
97
+ if (keysToCreate.length === 0) {
98
+ keysToCreate.push(fromKey, toKey);
99
+ }
100
+ let createdAny = false;
101
+ for (const key of keysToCreate) {
102
+ const created = await tryCreateStubEntity(key, mcpOpts, agentContext, policyContext);
103
+ if (created)
104
+ createdAny = true;
105
+ }
106
+ if (createdAny) {
107
+ raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
108
+ result = parseToolText(raw);
109
+ }
110
+ }
63
111
  if (asJson) {
64
112
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
65
113
  if (Number(result.summary?.failed ?? 0) > 0)
@@ -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.30";
7
+ const CLI_VERSION = "0.1.32";
8
8
  const AGENT_ENTITY_TYPE = "agent";
9
9
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
10
10
  function parseFlag(args, flag) {
@@ -281,18 +281,17 @@ function scanProject(dir) {
281
281
  return { projectName, packageJsonCount: pkgPaths.length, detectedNames: Array.from(names), rootDepNames, subPackages };
282
282
  }
283
283
  // ─── Relationship type selection ──────────────────────────────────────────────
284
- function pickRelationshipType(toEntityTypeCode, fromEntityTypeCode = "application") {
284
+ function pickRelationshipType(toEntityTypeCode, _fromEntityTypeCode = "application") {
285
285
  switch (toEntityTypeCode) {
286
286
  case "model":
287
287
  return "uses_model";
288
288
  case "platform":
289
289
  case "platform_component":
290
+ // runs_on is the valid ontology relationship from application/tech to platform
291
+ return "runs_on";
290
292
  case "skill":
291
293
  return "uses";
292
294
  case "technology_component":
293
- // tech→tech depends_on is not allowed by ontology; use "uses" instead
294
- if (fromEntityTypeCode === "technology_component")
295
- return "uses";
296
295
  return "depends_on";
297
296
  default:
298
297
  return "depends_on";
@@ -544,8 +543,8 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
544
543
 
545
544
  RELATIONSHIP TYPE RULES:
546
545
  application → technology_component : --type "depends_on"
547
- technology_component → technology_component : --type "uses" (NOT depends_on)
548
- any → platform / platform_component : --type "uses"
546
+ technology_component → technology_component : --type "depends_on"
547
+ any → platform / platform_component : --type "runs_on" (NOT uses or depends_on)
549
548
  any → model : --type "uses_model"
550
549
 
551
550
  Pre-resolved dependencies per sub-package (wire these after registering each entity):
@@ -586,15 +585,22 @@ ${finalStep} — Identify architecturally significant components not auto-detect
586
585
  For each one you identify:
587
586
 
588
587
  1. Resolve it to a canonical reference:
589
- npx nexarch resolve-names --names "<platform name>"
588
+ npx nexarch resolve-names --names "<platform name>" --json
590
589
 
591
- 2. If resolved, wire the relationship from the relevant entity:
590
+ 2. If resolved, instantiate the entity in the graph (REQUIRED before add-relationship):
591
+ npx nexarch update-entity \\
592
+ --key "<resolved-ref>" \\
593
+ --entity-type "<entityTypeCode from resolve-names>" \\
594
+ --name "<canonicalName from resolve-names>"
595
+
596
+ 3. Wire the relationship:
592
597
  npx nexarch add-relationship \\
593
598
  --from "${projectExternalKey}" \\
594
599
  --to "<resolved-ref>" \\
595
- --type "uses"
600
+ --type "runs_on" # for platforms (Vercel, Neon, etc.)
601
+ # use --type "depends_on" for external APIs/SaaS (Stripe, Resend, etc.)
596
602
 
597
- 3. If unresolved (not in the reference library), skip it for now — it will appear
603
+ 4. If unresolved (not in the reference library), skip it for now — it will appear
598
604
  as a reference candidate on the next scan once you register an alias for it.
599
605
  `;
600
606
  return `
@@ -40,7 +40,7 @@ export async function resolveNames(args) {
40
40
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
41
41
  return;
42
42
  }
43
- for (const entry of result.resolved ?? []) {
43
+ for (const entry of result.results ?? []) {
44
44
  if (entry.resolved) {
45
45
  console.log(`✓ ${entry.input} → ${entry.canonicalExternalRef} (${entry.entityTypeCode}${entry.entitySubtypeCode ? `/${entry.entitySubtypeCode}` : ""}) "${entry.canonicalName}"`);
46
46
  }
@@ -48,5 +48,5 @@ export async function resolveNames(args) {
48
48
  console.log(`✗ ${entry.input} — not found in reference library`);
49
49
  }
50
50
  }
51
- console.log(`\n${result.totalResolved}/${result.totalRequested} resolved`);
51
+ console.log(`\n${result.summary?.resolved ?? 0}/${result.summary?.requested ?? 0} resolved`);
52
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",