nexarch 0.1.21 → 0.1.23

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.
@@ -0,0 +1,80 @@
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 addRelationship(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ const fromKey = parseOptionValue(args, "--from");
23
+ const toKey = parseOptionValue(args, "--to");
24
+ const relType = parseOptionValue(args, "--type");
25
+ if (!fromKey) {
26
+ console.error("error: --from <externalKey> is required");
27
+ process.exit(1);
28
+ }
29
+ if (!toKey) {
30
+ console.error("error: --to <externalKey> is required");
31
+ process.exit(1);
32
+ }
33
+ if (!relType) {
34
+ console.error("error: --type <relationshipTypeCode> is required");
35
+ process.exit(1);
36
+ }
37
+ const creds = requireCredentials();
38
+ const mcpOpts = { companyId: creds.companyId };
39
+ const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, mcpOpts);
40
+ const policies = parseToolText(policiesRaw);
41
+ const policyBundleHash = policies.policyBundleHash ?? null;
42
+ const nowIso = new Date().toISOString();
43
+ const agentContext = {
44
+ agentId: "nexarch-cli:add-relationship",
45
+ agentRunId: `add-relationship-${Date.now()}`,
46
+ repoRef: fromKey,
47
+ observedAt: nowIso,
48
+ source: "nexarch-cli",
49
+ model: "n/a",
50
+ provider: "n/a",
51
+ };
52
+ const policyContext = policyBundleHash
53
+ ? { policyBundleHash, alignmentSummary: { score: 1, violations: [], waivers: [] } }
54
+ : undefined;
55
+ const relationship = {
56
+ relationshipTypeCode: relType,
57
+ fromEntityExternalKey: fromKey,
58
+ toEntityExternalKey: toKey,
59
+ confidence: 1,
60
+ };
61
+ const raw = await callMcpTool("nexarch_upsert_relationships", { relationships: [relationship], agentContext, policyContext }, mcpOpts);
62
+ const result = parseToolText(raw);
63
+ if (asJson) {
64
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
65
+ if (Number(result.summary?.failed ?? 0) > 0)
66
+ process.exitCode = 1;
67
+ return;
68
+ }
69
+ const succeeded = result.summary?.succeeded ?? 0;
70
+ const failed = result.summary?.failed ?? 0;
71
+ if (failed > 0) {
72
+ console.error(`Failed to add relationship: ${fromKey} -[${relType}]-> ${toKey}`);
73
+ for (const err of result.errors ?? []) {
74
+ console.error(` ${err.error} — ${err.message}`);
75
+ }
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ console.log(`Added ${succeeded} relationship: ${fromKey} -[${relType}]-> ${toKey}`);
80
+ }
@@ -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.21";
7
+ const CLI_VERSION = "0.1.23";
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
  }
@@ -465,7 +465,7 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
465
465
  Type definitions or utility package with no runtime
466
466
  → --entity-type technology_component --subtype tech_library
467
467
 
468
- For each sub-package, run:
468
+ For each sub-package, run update-entity to register it:
469
469
 
470
470
  npx nexarch update-entity \\
471
471
  --key "<entity-type>:<slugified-package-name>" \\
@@ -474,10 +474,28 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
474
474
  --name "<human readable name>" \\
475
475
  --description "<what this package does and its role in the project>"
476
476
 
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)
477
+ Then register it as a resolvable alias so future scans don't re-surface it as a candidate:
480
478
 
481
- (Note: add-relationship command is coming — skip wiring for now if unavailable)
479
+ npx nexarch register-alias \\
480
+ --alias "<original package name e.g. @scope/name>" \\
481
+ --key "<same entity-type>:<slugified-package-name>" \\
482
+ --name "<same human readable name>" \\
483
+ --entity-type "<same entity type>"
484
+
485
+ ⚠️ DIRECTION MATTERS — wire relationships as follows:
486
+
487
+ Deployable sub-apps (apps/*) are PART OF the parent — child points to parent:
488
+ npx nexarch add-relationship \\
489
+ --from "<sub-app-key>" \\
490
+ --to "${projectExternalKey}" \\
491
+ --type "part_of"
492
+ (FROM = the sub-app, TO = ${projectExternalKey})
493
+
494
+ Shared libraries (packages/*) are depended on by the parent — parent points to library:
495
+ npx nexarch add-relationship \\
496
+ --from "${projectExternalKey}" \\
497
+ --to "<library-key>" \\
498
+ --type "depends_on"
499
+ (FROM = ${projectExternalKey}, TO = the library)
482
500
  ` : ""}`);
483
501
  }
@@ -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
@@ -12,6 +12,8 @@ import { initAgent } from "./commands/init-agent.js";
12
12
  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
+ import { addRelationship } from "./commands/add-relationship.js";
16
+ import { registerAlias } from "./commands/register-alias.js";
15
17
  const [, , command, ...args] = process.argv;
16
18
  const commands = {
17
19
  login,
@@ -24,6 +26,8 @@ const commands = {
24
26
  "agent-identify": agentIdentify,
25
27
  "init-project": initProject,
26
28
  "update-entity": updateEntity,
29
+ "add-relationship": addRelationship,
30
+ "register-alias": registerAlias,
27
31
  };
28
32
  async function main() {
29
33
  if (command === "agent") {
@@ -93,6 +97,23 @@ Usage:
93
97
  --entity-type <code> (default: application)
94
98
  --subtype <code>
95
99
  --json
100
+ nexarch add-relationship
101
+ Add a relationship between two existing graph entities.
102
+ Options: --from <externalKey> (required)
103
+ --to <externalKey> (required)
104
+ --type <code> (required, e.g. part_of, depends_on)
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
96
117
  `);
97
118
  process.exit(command ? 1 : 0);
98
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",