nexarch 0.1.24 → 0.1.25

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.24";
7
+ const CLI_VERSION = "0.1.25";
8
8
  const AGENT_ENTITY_TYPE = "agent";
9
9
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
10
10
  function parseFlag(args, flag) {
@@ -115,6 +115,34 @@ function readRootPackage(pkgPath) {
115
115
  return {};
116
116
  }
117
117
  }
118
+ // Guess entity type + subtype for a sub-package based on its path and package.json scripts.
119
+ function classifySubPackage(pkgPath, relativePath) {
120
+ const topDir = relativePath.split("/")[0] ?? "";
121
+ // packages/* → shared library/component
122
+ if (topDir === "packages") {
123
+ const name = relativePath.split("/")[1] ?? "";
124
+ if (name.includes("ui") || name.includes("components"))
125
+ return { entityType: "technology_component", subtype: "tech_framework" };
126
+ if (name.includes("types") || name.includes("shared"))
127
+ return { entityType: "technology_component", subtype: "tech_library" };
128
+ return { entityType: "technology_component", subtype: "tech_library" };
129
+ }
130
+ // apps/* or src/* → deployable application; check scripts
131
+ try {
132
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
133
+ const scripts = Object.keys(pkg.scripts ?? {});
134
+ const hasServer = scripts.some((s) => ["start", "dev", "serve", "build"].includes(s));
135
+ if (hasServer) {
136
+ const name = relativePath.toLowerCase();
137
+ if (name.includes("crawl") || name.includes("worker") || name.includes("job") || name.includes("etl")) {
138
+ return { entityType: "application", subtype: "app_integration_service" };
139
+ }
140
+ return { entityType: "application", subtype: "app_custom_built" };
141
+ }
142
+ }
143
+ catch { }
144
+ return { entityType: "technology_component", subtype: "tech_library" };
145
+ }
118
146
  // Resolve workspace glob patterns (e.g. "apps/*", "packages/*") to actual directories.
119
147
  // Handles the common case of a single wildcard — no full glob library needed.
120
148
  function resolveWorkspaceDirs(rootDir, workspaces) {
@@ -219,11 +247,17 @@ function scanProject(dir) {
219
247
  else {
220
248
  // Record as a sub-package for enrichment task
221
249
  const relativePath = pkgPath.replace(dir + "/", "").replace("/package.json", "");
250
+ const packageName = pkg.name ?? relativePath;
251
+ const { entityType, subtype } = classifySubPackage(pkgPath, relativePath);
252
+ const externalKey = `${entityType}:${slugify(packageName)}`;
222
253
  subPackages.push({
223
- name: pkg.name ?? relativePath,
254
+ name: packageName,
224
255
  relativePath,
225
256
  packageJsonPath: pkgPath,
226
257
  depNames: deps,
258
+ entityType,
259
+ subtype,
260
+ externalKey,
227
261
  });
228
262
  }
229
263
  for (const dep of deps)
@@ -362,7 +396,22 @@ export async function initProject(args) {
362
396
  confidence: 0.95,
363
397
  });
364
398
  }
365
- // Build a lookup from input name to resolved result for enrichment task use
399
+ // Auto-create stub entities for each sub-package
400
+ const seenSubKeys = new Set();
401
+ for (const sp of subPackages) {
402
+ if (seenSubKeys.has(sp.externalKey))
403
+ continue;
404
+ seenSubKeys.add(sp.externalKey);
405
+ entities.push({
406
+ externalKey: sp.externalKey,
407
+ entityTypeCode: sp.entityType,
408
+ entitySubtypeCode: sp.subtype,
409
+ name: sp.name,
410
+ confidence: 0.8,
411
+ attributes: { source_dir: `${dir}/${sp.relativePath}`, scanned_at: nowIso },
412
+ });
413
+ }
414
+ // Build a lookup from input name to resolved result for enrichment task and sub-app wiring
366
415
  const resolvedByInput = new Map();
367
416
  for (const r of resolvedItems) {
368
417
  if (r.canonicalExternalRef)
@@ -370,27 +419,43 @@ export async function initProject(args) {
370
419
  if (r.normalised && r.canonicalExternalRef)
371
420
  resolvedByInput.set(r.normalised, r);
372
421
  }
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).
422
+ // Build relationships:
423
+ // - Root-level deps wired to the top-level project entity
424
+ // - Sub-package deps → wired to each sub-package entity
425
+ // - Sub-packages that are apps → part_of the top-level project
375
426
  const relationships = [];
376
427
  const seenRelPairs = new Set();
428
+ function addRel(type, from, to, confidence = 0.9) {
429
+ const key = `${type}::${from}::${to}`;
430
+ if (seenRelPairs.has(key))
431
+ return;
432
+ seenRelPairs.add(key);
433
+ relationships.push({ relationshipTypeCode: type, fromEntityExternalKey: from, toEntityExternalKey: to, confidence });
434
+ }
435
+ // Root-level deps → top-level project
377
436
  for (const r of resolvedItems) {
378
437
  if (!r.canonicalExternalRef || !r.entityTypeCode)
379
438
  continue;
380
- // Skip deps that don't belong to the root package — only wire root-level deps here
381
439
  if (!rootDepNames.has(r.input) && !rootDepNames.has(r.normalised))
382
440
  continue;
383
- const relType = pickRelationshipType(r.entityTypeCode);
384
- const pairKey = `${relType}::${projectExternalKey}::${r.canonicalExternalRef}`;
385
- if (seenRelPairs.has(pairKey))
441
+ addRel(pickRelationshipType(r.entityTypeCode), projectExternalKey, r.canonicalExternalRef);
442
+ }
443
+ // Sub-package relationships
444
+ for (const sp of subPackages) {
445
+ if (!sp.externalKey || seenSubKeys.has(`rel:${sp.externalKey}`))
386
446
  continue;
387
- seenRelPairs.add(pairKey);
388
- relationships.push({
389
- relationshipTypeCode: relType,
390
- fromEntityExternalKey: projectExternalKey,
391
- toEntityExternalKey: r.canonicalExternalRef,
392
- confidence: 0.9,
393
- });
447
+ seenSubKeys.add(`rel:${sp.externalKey}`);
448
+ // Apps are part_of the top-level project
449
+ if (sp.entityType === "application") {
450
+ addRel("part_of", sp.externalKey, projectExternalKey);
451
+ }
452
+ // Wire each sub-package's resolved deps to itself
453
+ for (const depName of sp.depNames) {
454
+ const r = resolvedByInput.get(depName);
455
+ if (!r?.canonicalExternalRef || !r.entityTypeCode)
456
+ continue;
457
+ addRel(pickRelationshipType(r.entityTypeCode), sp.externalKey, r.canonicalExternalRef);
458
+ }
394
459
  }
395
460
  if (!asJson)
396
461
  console.log(`\nWriting to graph…`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Connect AI coding tools to your Nexarch architecture workspace",
5
5
  "keywords": [
6
6
  "nexarch",