nexarch 0.9.21 → 0.9.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 +3 -4
- package/dist/commands/init-project.js +69 -25
- package/dist/lib/clients.js +12 -4
- package/package.json +1 -1
|
@@ -316,12 +316,11 @@ function replaceManagedSection(existing, key, body) {
|
|
|
316
316
|
const escapedStart = markers.start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
317
317
|
const escapedEnd = markers.end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
318
318
|
const managedRegex = new RegExp(`${escapedStart}[\\s\\S]*?${escapedEnd}\\s*`, "gm");
|
|
319
|
-
|
|
319
|
+
const stripped = existing.replace(managedRegex, "").trimEnd();
|
|
320
|
+
if (stripped === existing.trimEnd())
|
|
320
321
|
return existing;
|
|
321
322
|
const canonicalBlock = wrapManagedSection(key, body);
|
|
322
|
-
|
|
323
|
-
const rebuilt = `${stripped}${stripped ? "\n\n" : ""}${canonicalBlock}\n`;
|
|
324
|
-
return rebuilt;
|
|
323
|
+
return `${stripped}${stripped ? "\n\n" : ""}${canonicalBlock}\n`;
|
|
325
324
|
}
|
|
326
325
|
function replaceInjectedSection(existing, sectionHeading, sectionBody) {
|
|
327
326
|
const escapedHeading = sectionHeading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -444,7 +444,7 @@ function readRootPackage(pkgPath) {
|
|
|
444
444
|
// Guess entity type + subtype for a sub-package based on its path and package.json scripts.
|
|
445
445
|
function classifySubPackage(pkgPath, relativePath) {
|
|
446
446
|
const topDir = relativePath.split("/")[0] ?? "";
|
|
447
|
-
// packages/* → shared library/component
|
|
447
|
+
// packages/* → shared library/component (no server, not independently deployable)
|
|
448
448
|
if (topDir === "packages") {
|
|
449
449
|
const name = relativePath.split("/")[1] ?? "";
|
|
450
450
|
if (name.includes("ui") || name.includes("components"))
|
|
@@ -453,9 +453,31 @@ function classifySubPackage(pkgPath, relativePath) {
|
|
|
453
453
|
return { entityType: "technology_component", subtype: "tech_library" };
|
|
454
454
|
return { entityType: "technology_component", subtype: "tech_library" };
|
|
455
455
|
}
|
|
456
|
-
//
|
|
456
|
+
// Read package.json once for script + bin signals used across all paths below.
|
|
457
|
+
let scripts = [];
|
|
458
|
+
let hasBin = false;
|
|
459
|
+
try {
|
|
460
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
461
|
+
scripts = Object.keys(pkg.scripts ?? {});
|
|
462
|
+
hasBin = pkg.bin !== undefined && pkg.bin !== null;
|
|
463
|
+
}
|
|
464
|
+
catch { }
|
|
465
|
+
const name = relativePath.toLowerCase();
|
|
466
|
+
const hasServerScript = scripts.some((s) => ["start", "dev", "serve"].includes(s));
|
|
467
|
+
const hasBuildScript = scripts.some((s) => s === "build");
|
|
468
|
+
// A bin entry means it is a CLI application regardless of path.
|
|
469
|
+
if (hasBin)
|
|
470
|
+
return { entityType: "application", subtype: "app_cli" };
|
|
471
|
+
// apps/* — treat as full application when it has its own server/dev script
|
|
472
|
+
// (independently deployable); fall back to component classification otherwise.
|
|
457
473
|
if (topDir === "apps") {
|
|
458
|
-
|
|
474
|
+
if (hasServerScript) {
|
|
475
|
+
if (name.includes("worker") || name.includes("job") || name.includes("etl") || name.includes("crawl")) {
|
|
476
|
+
return { entityType: "application", subtype: "app_integration_service" };
|
|
477
|
+
}
|
|
478
|
+
return { entityType: "application", subtype: "app_custom_built" };
|
|
479
|
+
}
|
|
480
|
+
// No server script → treat as a component of the parent application.
|
|
459
481
|
if (name.includes("worker"))
|
|
460
482
|
return { entityType: "application_component", subtype: "app_comp_worker" };
|
|
461
483
|
if (name.includes("job"))
|
|
@@ -475,20 +497,13 @@ function classifySubPackage(pkgPath, relativePath) {
|
|
|
475
497
|
return { entityType: "application_component", subtype: "app_comp_api" };
|
|
476
498
|
return { entityType: "application_component", subtype: "app_comp_api" };
|
|
477
499
|
}
|
|
478
|
-
//
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const hasServer = scripts.some((s) => ["start", "dev", "serve", "build"].includes(s));
|
|
483
|
-
if (hasServer) {
|
|
484
|
-
const name = relativePath.toLowerCase();
|
|
485
|
-
if (name.includes("crawl") || name.includes("worker") || name.includes("job") || name.includes("etl")) {
|
|
486
|
-
return { entityType: "application", subtype: "app_integration_service" };
|
|
487
|
-
}
|
|
488
|
-
return { entityType: "application", subtype: "app_custom_built" };
|
|
500
|
+
// Root-level or other paths — use script signals to detect standalone applications.
|
|
501
|
+
if (hasServerScript || hasBuildScript) {
|
|
502
|
+
if (name.includes("crawl") || name.includes("worker") || name.includes("job") || name.includes("etl")) {
|
|
503
|
+
return { entityType: "application", subtype: "app_integration_service" };
|
|
489
504
|
}
|
|
505
|
+
return { entityType: "application", subtype: "app_custom_built" };
|
|
490
506
|
}
|
|
491
|
-
catch { }
|
|
492
507
|
return { entityType: "technology_component", subtype: "tech_library" };
|
|
493
508
|
}
|
|
494
509
|
// Resolve workspace glob patterns (e.g. "apps/*", "packages/*") to actual directories.
|
|
@@ -794,6 +809,14 @@ function scanEcosystems(dir) {
|
|
|
794
809
|
function isApplicationLikeEntityType(entityType) {
|
|
795
810
|
return entityType === "application" || entityType === "application_component";
|
|
796
811
|
}
|
|
812
|
+
/** Returns the structural relationship that should be wired between the root project and this sub-package. */
|
|
813
|
+
function structuralRelForSubPackage(sp, projectExternalKey) {
|
|
814
|
+
if (sp.entityType === "application")
|
|
815
|
+
return { type: "composes", from: projectExternalKey, to: sp.externalKey };
|
|
816
|
+
if (sp.entityType === "application_component")
|
|
817
|
+
return { type: "part_of", from: sp.externalKey, to: projectExternalKey };
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
797
820
|
function scanProject(dir) {
|
|
798
821
|
const names = new Set();
|
|
799
822
|
let projectName = basename(dir);
|
|
@@ -1566,8 +1589,16 @@ export async function initProject(args) {
|
|
|
1566
1589
|
pendingSteps.push({
|
|
1567
1590
|
step: stepNum++,
|
|
1568
1591
|
action: "classify_sub_packages",
|
|
1569
|
-
instruction: `
|
|
1570
|
-
|
|
1592
|
+
instruction: `For each sub-package in classifyPackages: (1) run update-entity to confirm type/subtype/name/description, then (2) immediately run add-relationship to wire the structural relationship. The external key includes the entity type as a prefix — if you change the entity type, the key changes (e.g. application_component:foo → application:foo). Always run update-entity before add-relationship for each package.`,
|
|
1593
|
+
commandTemplates: {
|
|
1594
|
+
updateEntity: `nexarch update-entity --key "<subPackageExternalKey>" --entity-type "<entityType>" --subtype "<subtype>" --name "..." --description "..."`,
|
|
1595
|
+
wireRelationship: `nexarch add-relationship --from "<from>" --to "<to>" --type <composes|part_of> (see structuralRelationship on each classifyPackages entry)`,
|
|
1596
|
+
},
|
|
1597
|
+
notes: [
|
|
1598
|
+
"application sub-packages: parent composes sub-app → add-relationship --from parent --to sub-app --type composes",
|
|
1599
|
+
"application_component sub-packages: component part_of parent → add-relationship --from sub-pkg --to parent --type part_of",
|
|
1600
|
+
"Do NOT wire the relationship before the entity exists at the correct key.",
|
|
1601
|
+
],
|
|
1571
1602
|
});
|
|
1572
1603
|
}
|
|
1573
1604
|
if (entityTypeOverride === "application") {
|
|
@@ -1576,8 +1607,8 @@ export async function initProject(args) {
|
|
|
1576
1607
|
action: "discover_functions",
|
|
1577
1608
|
instruction: `Review the codebase to identify discrete application functions (what the application does). Examine named modules, route layout, service boundaries, and any architecture documentation. Register functions as application_function entities with subtype core_function (primary business function), supporting_function (auxiliary/enablement), integration_function (external connectivity), or data_function (data processing). Only register functions clearly evidenced by the codebase — do not invent them.`,
|
|
1578
1609
|
commandTemplates: {
|
|
1579
|
-
updateEntity: `nexarch update-entity --key "application_function:${projectSlug}
|
|
1580
|
-
addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}
|
|
1610
|
+
updateEntity: `nexarch update-entity --key "application_function:${projectSlug}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`,
|
|
1611
|
+
addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}_<function_slug>" --to "${projectExternalKey}" --type part_of`,
|
|
1581
1612
|
},
|
|
1582
1613
|
});
|
|
1583
1614
|
}
|
|
@@ -1596,8 +1627,8 @@ export async function initProject(args) {
|
|
|
1596
1627
|
action: "register_decision_records",
|
|
1597
1628
|
instruction: `Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register each as a decision_record entity.`,
|
|
1598
1629
|
commandTemplates: {
|
|
1599
|
-
updateEntity: `nexarch update-entity --key "decision_record:${projectSlug}
|
|
1600
|
-
addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}
|
|
1630
|
+
updateEntity: `nexarch update-entity --key "decision_record:${projectSlug}_<adr_slug>" --entity-type decision_record --subtype decision_architecture --name "..." --attributes-json '{"decision":{"summary":"...","detail":"..."}}'`,
|
|
1631
|
+
addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}_<adr_slug>" --to "${projectExternalKey}" --type decides`,
|
|
1601
1632
|
},
|
|
1602
1633
|
});
|
|
1603
1634
|
return {
|
|
@@ -1624,6 +1655,7 @@ export async function initProject(args) {
|
|
|
1624
1655
|
preWired: preWiredRelationshipKeys.has(relationshipKey),
|
|
1625
1656
|
};
|
|
1626
1657
|
});
|
|
1658
|
+
const structuralRel = structuralRelForSubPackage(sp, projectExternalKey);
|
|
1627
1659
|
return {
|
|
1628
1660
|
name: sp.name,
|
|
1629
1661
|
relativePath: sp.relativePath,
|
|
@@ -1631,6 +1663,9 @@ export async function initProject(args) {
|
|
|
1631
1663
|
heuristicEntityType: sp.entityType,
|
|
1632
1664
|
heuristicSubtype: sp.subtype,
|
|
1633
1665
|
confidence: subPackageConfidence(sp),
|
|
1666
|
+
structuralRelationship: structuralRel
|
|
1667
|
+
? { ...structuralRel, note: "Wire AFTER update-entity. If entity type changes, update the key prefix in from/to before running add-relationship." }
|
|
1668
|
+
: null,
|
|
1634
1669
|
resolvedDeps,
|
|
1635
1670
|
unresolvedDeps: sp.depSpecs.map((d) => d.name).filter((d) => !resolvedByInput.has(d)),
|
|
1636
1671
|
};
|
|
@@ -1686,6 +1721,10 @@ export async function initProject(args) {
|
|
|
1686
1721
|
if (subPackages.length > 0) {
|
|
1687
1722
|
lines.push("");
|
|
1688
1723
|
lines.push(`CLASSIFY_THESE (${subPackages.length} sub-package(s) — read each, confirm type, then update):`);
|
|
1724
|
+
lines.push(` NOTE: The external key includes the entity type as a prefix (e.g. application:foo).`);
|
|
1725
|
+
lines.push(` If you change the entity type, the key changes too — update-entity will create an entity`);
|
|
1726
|
+
lines.push(` at the new key. Always wire the structural relationship AFTER the update-entity call,`);
|
|
1727
|
+
lines.push(` using the confirmed key. Do not wire relationships before the entity exists.`);
|
|
1689
1728
|
for (const sp of subPackages) {
|
|
1690
1729
|
const conf = subPackageConfidence(sp);
|
|
1691
1730
|
lines.push(` ${sp.relativePath}`);
|
|
@@ -1695,7 +1734,12 @@ export async function initProject(args) {
|
|
|
1695
1734
|
if (unresolvedDeps.length > 0) {
|
|
1696
1735
|
lines.push(` unresolved deps (${unresolvedDeps.length}): ${unresolvedDeps.slice(0, 5).join(", ")}${unresolvedDeps.length > 5 ? ` … +${unresolvedDeps.length - 5} more` : ""}`);
|
|
1697
1736
|
}
|
|
1698
|
-
lines.push(`
|
|
1737
|
+
lines.push(` 1) nexarch update-entity --key "${sp.externalKey}" --entity-type "${sp.entityType}" --subtype "${sp.subtype}" --name "..." --description "..."`);
|
|
1738
|
+
const rel = structuralRelForSubPackage(sp, projectExternalKey);
|
|
1739
|
+
if (rel) {
|
|
1740
|
+
lines.push(` 2) nexarch add-relationship --from "${rel.from}" --to "${rel.to}" --type ${rel.type}`);
|
|
1741
|
+
lines.push(` (adjust key prefix if you changed the entity type above)`);
|
|
1742
|
+
}
|
|
1699
1743
|
}
|
|
1700
1744
|
}
|
|
1701
1745
|
if (unresolvedItems.length > 0) {
|
|
@@ -1724,13 +1768,13 @@ export async function initProject(args) {
|
|
|
1724
1768
|
lines.push(` integration_function (external connectivity), or data_function (data processing).`);
|
|
1725
1769
|
lines.push(` Only register functions clearly evidenced by the codebase — do not invent them.`);
|
|
1726
1770
|
lines.push(` For each one found:`);
|
|
1727
|
-
lines.push(` nexarch update-entity --key "application_function:${projectExternalKey.split(":")[1] ?? "project"}
|
|
1728
|
-
lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}
|
|
1771
|
+
lines.push(` nexarch update-entity --key "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`);
|
|
1772
|
+
lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --to "${projectExternalKey}" --type part_of`);
|
|
1729
1773
|
}
|
|
1730
1774
|
lines.push(` ${step++}. Scan the READMEs for platforms/SaaS not auto-detected (Vercel, Neon, Stripe, etc.).`);
|
|
1731
1775
|
lines.push(` For each found: nexarch resolve-names --names "..." --json → nexarch update-entity → nexarch add-relationship`);
|
|
1732
1776
|
lines.push(` ${step++}. Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register decision_record entities.`);
|
|
1733
|
-
lines.push(` nexarch update-entity --key "decision_record:${projectExternalKey.split(":")[1] ?? "project"}
|
|
1777
|
+
lines.push(` nexarch update-entity --key "decision_record:${projectExternalKey.split(":")[1] ?? "project"}_<adr_slug>" --entity-type decision_record --subtype decision_architecture --name "..." --attributes-json '{"decision":{"summary":"...","detail":"..."}}'`);
|
|
1734
1778
|
lines.push(` nexarch add-relationship --from "decision_record:..." --to "${projectExternalKey}" --type decides`);
|
|
1735
1779
|
lines.push("");
|
|
1736
1780
|
lines.push("RELATIONSHIP DIRECTION RULES (for sub-packages):");
|
package/dist/lib/clients.js
CHANGED
|
@@ -106,6 +106,12 @@ export function nexarchServerBlockFromRegistry(registry) {
|
|
|
106
106
|
if (!first) {
|
|
107
107
|
throw new Error("Nexarch integration registry is missing MCP client profiles.");
|
|
108
108
|
}
|
|
109
|
+
if (process.platform === "win32") {
|
|
110
|
+
return {
|
|
111
|
+
command: "cmd",
|
|
112
|
+
args: ["/c", first.serverCommand, ...first.serverArgs],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
109
115
|
return {
|
|
110
116
|
command: first.serverCommand,
|
|
111
117
|
args: first.serverArgs,
|
|
@@ -113,8 +119,10 @@ export function nexarchServerBlockFromRegistry(registry) {
|
|
|
113
119
|
}
|
|
114
120
|
export function findClientProfile(registry, code) {
|
|
115
121
|
const normalized = code.trim().toLowerCase();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
const aliases = {
|
|
123
|
+
"continue": "continue-dev",
|
|
124
|
+
"claude": "claude-code",
|
|
125
|
+
};
|
|
126
|
+
const resolved = aliases[normalized] ?? normalized;
|
|
127
|
+
return registry.mcpClientProfiles.find((c) => c.code === resolved) ?? null;
|
|
120
128
|
}
|