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.
- package/dist/commands/init-agent.js +1 -1
- package/dist/commands/init-project.js +63 -13
- package/dist/commands/register-alias.js +61 -0
- package/dist/index.js +13 -0
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
}
|