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.
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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) =>
|
|
549
|
-
|
|
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
|
}
|