nexarch 0.12.4 → 0.12.6

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.
@@ -1,5 +1,4 @@
1
1
  import process from "process";
2
- import * as readline from "node:readline/promises";
3
2
  import { execFileSync } from "node:child_process";
4
3
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
5
4
  import { basename, join, relative, resolve as resolvePath } from "node:path";
@@ -1069,43 +1068,6 @@ export function scoreApplicationCandidate(app, projectName, repoUrl) {
1069
1068
  return null;
1070
1069
  return { entityRef, name: app.name, score: Math.min(1, score), reasons };
1071
1070
  }
1072
- async function promptApplicationChoice(matches, allApps, suggested) {
1073
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1074
- try {
1075
- console.log("\nExisting application entities found.");
1076
- if (suggested) {
1077
- console.log(`Suggested match: ${suggested.name} (${suggested.entityRef}) score=${suggested.score.toFixed(2)} [${suggested.reasons.join(", ")}]`);
1078
- }
1079
- console.log("\nChoose target application:");
1080
- const options = [];
1081
- let index = 1;
1082
- for (const m of matches.slice(0, 5)) {
1083
- options.push({
1084
- key: String(index++),
1085
- label: `${m.name} (${m.entityRef}) score=${m.score.toFixed(2)}`,
1086
- value: m.entityRef,
1087
- });
1088
- }
1089
- options.push({ key: String(index++), label: "Show all applications", value: "__show_all__" });
1090
- options.push({ key: String(index), label: "Create a new application", value: "__create__" });
1091
- for (const o of options)
1092
- console.log(` ${o.key}) ${o.label}`);
1093
- const answer = (await rl.question("Select option: ")).trim();
1094
- const chosen = options.find((o) => o.key === answer)?.value;
1095
- if (chosen === "__show_all__") {
1096
- console.log("\nAll applications:");
1097
- allApps.forEach((a, i) => console.log(` ${i + 1}) ${a.name} (${a.entityRef ?? a.externalKey ?? "n/a"})`));
1098
- const pick = Number((await rl.question("Pick application number or 0 to create new: ")).trim());
1099
- if (!Number.isFinite(pick) || pick <= 0 || pick > allApps.length)
1100
- return "__create__";
1101
- return allApps[pick - 1].entityRef ?? allApps[pick - 1].externalKey ?? "__create__";
1102
- }
1103
- return chosen && chosen !== "__show_all__" ? chosen : "__create__";
1104
- }
1105
- finally {
1106
- rl.close();
1107
- }
1108
- }
1109
1071
  // ─── Main command ─────────────────────────────────────────────────────────────
1110
1072
  export async function initProject(args) {
1111
1073
  const asJson = parseFlag(args, "--json");
@@ -1162,20 +1124,32 @@ export async function initProject(args) {
1162
1124
  const displayName = nameOverride ?? projectName;
1163
1125
  const projectSlug = slugify(displayName);
1164
1126
  let projectExternalKey = `${entityTypeOverride}:${projectSlug}`;
1127
+ // ADR 8a (docs/strategy/adr-ontology-reset-v1.md): the repository is modelled
1128
+ // as a `project` entity — evidence, not architecture. Monorepos no longer get
1129
+ // a synthetic root application; deployable packages become applications
1130
+ // sourced_from the project. projectExternalKey remains the APPLICATION key,
1131
+ // used only when the repo is itself a single deployable.
1132
+ const projectConstruct = entityTypeOverride === "application";
1133
+ const isMonorepo = subPackages.length > 0;
1134
+ const projectDirName = nameOverride ?? basename(dir);
1135
+ const projectDirSlug = slugify(projectDirName);
1136
+ const projectEntityKey = `project:${projectDirSlug}`;
1165
1137
  // Compute sub-package external keys now that projectSlug is known.
1166
1138
  // Unscoped names (no "/") get prefixed with the project slug to avoid ambiguous keys
1167
1139
  // like "application:crawler" — they become "application:whatsontap-crawler" instead,
1168
1140
  // matching what the enrichment agent would naturally choose.
1141
+ // Keys derive from the directory basename, not package.json name: paths are
1142
+ // stable, unique within a repo, and human-meaningful, while npm names can
1143
+ // collide with the project itself (this repo's CLI package is literally named
1144
+ // "nexarch"). For existing layouts this reproduces the pre-8a keys
1145
+ // byte-for-byte (application:nexarch_web), keeping re-runs idempotent.
1146
+ const seenSubSlugs = new Set();
1169
1147
  for (const sp of subPackages) {
1170
- const nameSlug = slugify(sp.name);
1171
- const needsPrefix = !sp.name.includes("/") && !nameSlug.startsWith(projectSlug);
1172
- let keySlug = needsPrefix ? `${projectSlug}_${nameSlug}` : nameSlug;
1173
- // Avoid key collision with the top-level project (e.g. sub-package named "nexarch").
1174
- if (`${sp.entityType}:${keySlug}` === projectExternalKey) {
1175
- const relSlug = slugify(sp.relativePath.replace(/\//g, "_"));
1176
- keySlug = relSlug ? `${projectSlug}_${relSlug}` : `${projectSlug}_${nameSlug}_sub`;
1177
- }
1178
- sp.externalKey = `${sp.entityType}:${keySlug}`;
1148
+ let dirSlug = slugify(basename(sp.relativePath));
1149
+ if (seenSubSlugs.has(dirSlug))
1150
+ dirSlug = slugify(sp.relativePath.replace(/\//g, "_"));
1151
+ seenSubSlugs.add(dirSlug);
1152
+ sp.externalKey = `${sp.entityType}:${projectDirSlug}_${dirSlug}`;
1179
1153
  }
1180
1154
  if (!asJson) {
1181
1155
  console.log(` Project : ${displayName} (${entityTypeOverride})`);
@@ -1214,9 +1188,10 @@ export async function initProject(args) {
1214
1188
  const output = {
1215
1189
  dryRun: true,
1216
1190
  project: {
1217
- name: displayName,
1218
- externalKey: projectExternalKey,
1219
- entityType: entityTypeOverride,
1191
+ name: projectConstruct ? projectDirName : displayName,
1192
+ externalKey: projectConstruct ? projectEntityKey : projectExternalKey,
1193
+ entityType: projectConstruct ? "project" : entityTypeOverride,
1194
+ ...(projectConstruct ? { subtype: isMonorepo ? "project_monorepo" : "project_repository" } : {}),
1220
1195
  sourceRepository: detectedRepo
1221
1196
  ? {
1222
1197
  ref: detectedRepo.rawRef,
@@ -1270,13 +1245,37 @@ export async function initProject(args) {
1270
1245
  const repoUrl = repoUrlOverride ?? detectedRepo?.url ?? null;
1271
1246
  const sourceVcsType = detectedRepo?.vcsType ?? "unknown";
1272
1247
  const sourceProvider = detectedRepo?.provider ?? "unknown";
1273
- if (entityTypeOverride === "application" && !forceCreateApplication) {
1248
+ if (projectConstruct && isMonorepo && (applicationRefOverride || forceCreateApplication) && !asJson) {
1249
+ console.log("\nNote: monorepo detected — no root application is created (ADR 8a); --application-ref/--create-application apply only to single-package repositories.");
1250
+ }
1251
+ if (entityTypeOverride === "application" && !forceCreateApplication && !isMonorepo) {
1274
1252
  if (applicationRefOverride) {
1275
1253
  projectExternalKey = applicationRefOverride;
1276
1254
  if (!asJson)
1277
1255
  console.log(`\nUsing --application-ref target: ${projectExternalKey}`);
1278
1256
  }
1279
1257
  else {
1258
+ // Seed S2 auto-map: when a reviewer previously declined a proposal with
1259
+ // this name as a duplicate, a company alias maps it to the surviving
1260
+ // application. Resolving the name first means the human's "no" is
1261
+ // honoured automatically — the scan maps instead of re-proposing.
1262
+ try {
1263
+ const aliasRaw = await callMcpProfiled("nexarch_resolve_reference", { names: [displayName], companyId: creds.companyId }, { batchSize: 1 });
1264
+ const aliasData = parseToolText(aliasRaw);
1265
+ const aliasHit = (aliasData.results ?? []).find((r) => r.resolved && r.entityTypeCode === "application" && r.canonicalExternalRef?.startsWith("application:"));
1266
+ if (aliasHit?.canonicalExternalRef) {
1267
+ projectExternalKey = aliasHit.canonicalExternalRef;
1268
+ if (!asJson) {
1269
+ console.log(`\nAuto-mapped via workspace alias: "${displayName}" → ${projectExternalKey} (${aliasHit.canonicalName ?? "existing application"})`);
1270
+ console.log(" A reviewer previously mapped this name to an existing application. To create a separate application anyway, re-run with --create-application.");
1271
+ }
1272
+ }
1273
+ }
1274
+ catch {
1275
+ // Alias resolution is best-effort; fall through to candidate scoring.
1276
+ }
1277
+ }
1278
+ if (!applicationRefOverride && projectExternalKey === `${entityTypeOverride}:${projectSlug}`) {
1280
1279
  const appsRaw = await callMcpProfiled("nexarch_list_entities", { entityTypeCode: "application", status: "active", limit: 500, companyId: creds.companyId }, { entityTypeCode: "application", limit: 500 });
1281
1280
  const appsData = parseToolText(appsRaw);
1282
1281
  const apps = (appsData.entities ?? []).filter((e) => (e.entityRef ?? e.externalKey));
@@ -1287,63 +1286,30 @@ export async function initProject(args) {
1287
1286
  .sort((a, b) => b.score - a.score);
1288
1287
  const suggested = matches.length > 0 ? matches[0] : null;
1289
1288
  const highConfidence = suggested && suggested.score >= 0.85;
1290
- const interactiveAllowed = !nonInteractive && process.stdin.isTTY;
1289
+ // ADR 8d: the interactive "Choose target application" prompt is gone.
1290
+ // It demanded an architectural judgement from whoever happened to run
1291
+ // the command, in a terminal, sixty seconds into using the product —
1292
+ // and blocked non-interactive runs entirely. The default is now to
1293
+ // create a new application, which arrives as PROPOSED (8b): the
1294
+ // mapping judgement moves to the activation card, where similarity
1295
+ // evidence renders for the human reviewing it (8c). Explicit mapping
1296
+ // remains available via --application-ref and --auto-map-application.
1291
1297
  if (autoMapApplication && highConfidence) {
1292
1298
  projectExternalKey = suggested.entityRef;
1293
1299
  if (!asJson)
1294
1300
  console.log(`\nAuto-mapped to existing application: ${suggested.name} (${projectExternalKey})`);
1295
1301
  }
1296
- else if (!interactiveAllowed) {
1297
- const message = "Application mapping requires explicit choice in non-interactive mode. Pass --application-ref <entityRef> or --create-application (or run interactively).";
1298
- const existingApplications = apps.slice(0, 25).map((a) => ({
1299
- entityRef: a.entityRef ?? a.externalKey,
1300
- name: a.name,
1301
- entityTypeCode: a.entityTypeCode,
1302
- }));
1303
- if (asJson) {
1304
- process.stdout.write(`${JSON.stringify({
1305
- ok: true,
1306
- status: "input_required",
1307
- code: "APPLICATION_MAPPING_REQUIRED",
1308
- message,
1309
- suggested: suggested ?? null,
1310
- candidates: matches.slice(0, 10),
1311
- existingApplications,
1312
- requiredInput: {
1313
- applicationRefOption: "--application-ref <entityRef>",
1314
- createOption: "--create-application",
1315
- },
1316
- }, null, 2)}\n`);
1317
- return;
1318
- }
1319
- console.log("\nInput required before continuing:");
1320
- console.log(` ${message}`);
1321
- if (suggested) {
1322
- console.log(` Suggested: ${suggested.name} (${suggested.entityRef}) score=${suggested.score.toFixed(2)}`);
1323
- }
1324
- if (existingApplications.length > 0) {
1325
- console.log(" Existing applications:");
1326
- for (const app of existingApplications) {
1327
- console.log(` - ${app.name} (${app.entityRef})`);
1328
- }
1329
- }
1330
- return;
1331
- }
1332
- else {
1333
- const chosen = await promptApplicationChoice(matches, apps, highConfidence ? suggested : null);
1334
- if (chosen !== "__create__") {
1335
- projectExternalKey = chosen;
1336
- if (!asJson)
1337
- console.log(`Mapped to existing application: ${projectExternalKey}`);
1338
- }
1339
- else if (!asJson) {
1340
- console.log("Creating a new application entity for this project.");
1302
+ else if (matches.length > 0 && !asJson) {
1303
+ console.log("\nSimilar existing applications found (registering a new proposed application anyway the reviewer sees these again at activation):");
1304
+ for (const m of matches.slice(0, 5)) {
1305
+ console.log(` - ${m.name} (${m.entityRef}) score=${m.score.toFixed(2)} [${m.reasons.join(", ")}]`);
1341
1306
  }
1307
+ console.log(" To map to one instead, re-run with: --application-ref <entityRef>");
1342
1308
  }
1343
1309
  }
1344
1310
  }
1345
1311
  }
1346
- logProgress("application.target", projectExternalKey);
1312
+ logProgress(projectConstruct ? "project.target" : "application.target", projectConstruct ? (isMonorepo ? projectEntityKey : `${projectEntityKey} + ${projectExternalKey}`) : projectExternalKey);
1347
1313
  // In refresh mode, snapshot the current graph state for this project before writing,
1348
1314
  // so we can diff what changed and surface stale relationships to the agent.
1349
1315
  let currentOutgoingRels = [];
@@ -1376,26 +1342,46 @@ export async function initProject(args) {
1376
1342
  : undefined;
1377
1343
  // Build entity list — project entity + all resolved reference entities
1378
1344
  const entities = [];
1379
- // The project itself — default subtype app_custom_built for application type
1380
- const projectSubtype = entityTypeOverride === "application" ? "app_custom_built" : undefined;
1381
- entities.push({
1382
- externalKey: projectExternalKey,
1383
- entityTypeCode: entityTypeOverride,
1384
- ...(projectSubtype ? { entitySubtypeCode: projectSubtype } : {}),
1385
- name: displayName,
1386
- confidence: 1,
1387
- attributes: {
1388
- source_dir: dir,
1389
- scanned_at: nowIso,
1390
- package_json_count: packageJsonCount,
1391
- ...(repoUrl ? { repository_url: repoUrl, source_repository_url: repoUrl } : {}),
1392
- repository_ref: repoRef,
1393
- source_repository_ref: repoRef,
1394
- source_vcs_type: sourceVcsType,
1395
- source_provider: sourceProvider,
1396
- ...(detectedRepo?.canonicalRepoRef ? { source_repository_component_ref: detectedRepo.canonicalRepoRef } : {}),
1397
- },
1398
- });
1345
+ const scanProvenanceAttributes = {
1346
+ source_dir: dir,
1347
+ scanned_at: nowIso,
1348
+ package_json_count: packageJsonCount,
1349
+ ...(repoUrl ? { repository_url: repoUrl, source_repository_url: repoUrl } : {}),
1350
+ repository_ref: repoRef,
1351
+ source_repository_ref: repoRef,
1352
+ source_vcs_type: sourceVcsType,
1353
+ source_provider: sourceProvider,
1354
+ ...(detectedRepo?.canonicalRepoRef ? { source_repository_component_ref: detectedRepo.canonicalRepoRef } : {}),
1355
+ };
1356
+ // The repository as evidence: a `project` entity (ADR 8a). It arrives active —
1357
+ // a repository is a fact needing no review — while the applications it sources
1358
+ // arrive proposed (ADR 8b, enforced at the gateway).
1359
+ if (projectConstruct) {
1360
+ entities.push({
1361
+ externalKey: projectEntityKey,
1362
+ entityTypeCode: "project",
1363
+ entitySubtypeCode: isMonorepo ? "project_monorepo" : "project_repository",
1364
+ name: projectDirName,
1365
+ confidence: 1,
1366
+ attributes: {
1367
+ ...scanProvenanceAttributes,
1368
+ ...(projectName !== projectDirName ? { npm_package_name: projectName } : {}),
1369
+ },
1370
+ });
1371
+ }
1372
+ // The root application exists only when the repo IS a single deployable
1373
+ // (or in legacy non-application mode). A monorepo's container is the project.
1374
+ if (!projectConstruct || !isMonorepo) {
1375
+ const projectSubtype = entityTypeOverride === "application" ? "app_custom_built" : undefined;
1376
+ entities.push({
1377
+ externalKey: projectExternalKey,
1378
+ entityTypeCode: entityTypeOverride,
1379
+ ...(projectSubtype ? { entitySubtypeCode: projectSubtype } : {}),
1380
+ name: displayName,
1381
+ confidence: 1,
1382
+ attributes: scanProvenanceAttributes,
1383
+ });
1384
+ }
1399
1385
  // Resolved reference entities (deduplicated by canonical external ref)
1400
1386
  const seenRefs = new Set();
1401
1387
  for (const r of resolvedItems) {
@@ -1412,7 +1398,9 @@ export async function initProject(args) {
1412
1398
  });
1413
1399
  }
1414
1400
  // Ensure source repository component is represented when we can infer one.
1415
- if (detectedRepo?.canonicalRepoRef && !seenRefs.has(detectedRepo.canonicalRepoRef)) {
1401
+ // Under the project construct the project entity IS the repository, so the
1402
+ // separate technology_component stand-in is no longer created.
1403
+ if (!projectConstruct && detectedRepo?.canonicalRepoRef && !seenRefs.has(detectedRepo.canonicalRepoRef)) {
1416
1404
  seenRefs.add(detectedRepo.canonicalRepoRef);
1417
1405
  entities.push({
1418
1406
  externalKey: detectedRepo.canonicalRepoRef,
@@ -1439,13 +1427,18 @@ export async function initProject(args) {
1439
1427
  if (seenSubKeys.has(sp.externalKey))
1440
1428
  continue;
1441
1429
  seenSubKeys.add(sp.externalKey);
1430
+ const dirDisplayName = basename(sp.relativePath);
1442
1431
  entities.push({
1443
1432
  externalKey: sp.externalKey,
1444
1433
  entityTypeCode: sp.entityType,
1445
1434
  entitySubtypeCode: sp.subtype,
1446
- name: sp.name,
1435
+ name: dirDisplayName,
1447
1436
  confidence: 0.8,
1448
- attributes: { source_dir: `${dir}/${sp.relativePath}`, scanned_at: nowIso },
1437
+ attributes: {
1438
+ source_dir: `${dir}/${sp.relativePath}`,
1439
+ scanned_at: nowIso,
1440
+ ...(sp.name && sp.name !== dirDisplayName ? { npm_package_name: sp.name } : {}),
1441
+ },
1449
1442
  });
1450
1443
  }
1451
1444
  // Build a lookup from input name to resolved result for enrichment task and sub-app wiring
@@ -1487,8 +1480,13 @@ export async function initProject(args) {
1487
1480
  seenRelPairs.add(key);
1488
1481
  relationships.push({ relationshipTypeCode: type, fromEntityExternalKey: from, toEntityExternalKey: to, confidence, attributes });
1489
1482
  }
1490
- // Root-level deps → top-level project
1491
- for (const r of resolvedItems) {
1483
+ // Root-level deps → the root application (single-package/legacy only). A
1484
+ // project carries provenance, not dependencies — the ontology forbids
1485
+ // depends_on FROM project by design.
1486
+ if (projectConstruct && isMonorepo && rootDepNames.size > 0 && !asJson) {
1487
+ console.log(` Note: ${rootDepNames.size} root-level manifest dep(s) recorded as entities; not wired to the project (shared tooling).`);
1488
+ }
1489
+ for (const r of (projectConstruct && isMonorepo ? [] : resolvedItems)) {
1492
1490
  if (!r.canonicalExternalRef || !r.entityTypeCode)
1493
1491
  continue;
1494
1492
  if (!rootDepNames.has(r.input) && !rootDepNames.has(r.normalised))
@@ -1505,11 +1503,16 @@ export async function initProject(args) {
1505
1503
  if (!sp.externalKey || seenSubKeys.has(`rel:${sp.externalKey}`))
1506
1504
  continue;
1507
1505
  seenSubKeys.add(`rel:${sp.externalKey}`);
1508
- // Wire structural relationship to the top-level project entity:
1509
- // - application_component part_of root (component within a single deployable app)
1510
- // - application root composes sub-app (ontology only allows application on part_of FROM
1511
- // for application_component/application_function, not application itself)
1512
- if (sp.entityType === "application_component") {
1506
+ // ADR 8a: provenance, not containment. Every application-like package is
1507
+ // sourced_from the project; the composes/part_of wiring to a synthetic root
1508
+ // is gone. Genuine product composition is a human assertion made through
1509
+ // the proposal flow, never inferred from directory layout.
1510
+ if (projectConstruct) {
1511
+ if (isApplicationLikeEntityType(sp.entityType)) {
1512
+ addRel("sourced_from", sp.externalKey, projectEntityKey, 1);
1513
+ }
1514
+ }
1515
+ else if (sp.entityType === "application_component") {
1513
1516
  addRel("part_of", sp.externalKey, projectExternalKey);
1514
1517
  }
1515
1518
  else if (sp.entityType === "application") {
@@ -1550,15 +1553,21 @@ export async function initProject(args) {
1550
1553
  catch {
1551
1554
  // keep fallback
1552
1555
  }
1553
- addRel("accountable_for", orgExternalKey, projectExternalKey, 1);
1556
+ if (!projectConstruct || !isMonorepo) {
1557
+ addRel("accountable_for", orgExternalKey, projectExternalKey, 1);
1558
+ }
1554
1559
  // Also accountable_for any sub-package applications
1555
1560
  for (const sp of subPackages) {
1556
1561
  if (sp.externalKey && isApplicationLikeEntityType(sp.entityType)) {
1557
1562
  addRel("accountable_for", orgExternalKey, sp.externalKey, 1);
1558
1563
  }
1559
1564
  }
1560
- // Project depends_on its source repository component when resolved/inferred.
1561
- if (detectedRepo?.canonicalRepoRef) {
1565
+ // Single-package repos: the one application is sourced_from the project.
1566
+ if (projectConstruct && !isMonorepo) {
1567
+ addRel("sourced_from", projectExternalKey, projectEntityKey, 1);
1568
+ }
1569
+ // Legacy only: the project entity now carries repository provenance itself.
1570
+ if (!projectConstruct && detectedRepo?.canonicalRepoRef) {
1562
1571
  addRel("depends_on", projectExternalKey, detectedRepo.canonicalRepoRef, 0.95);
1563
1572
  }
1564
1573
  // In refresh mode the raw graph state (fetched above) is emitted alongside scan results
@@ -1650,29 +1659,50 @@ export async function initProject(args) {
1650
1659
  // so agents using --json receive the same mandatory instructions as text-mode agents.
1651
1660
  const pendingSteps = [];
1652
1661
  let stepNum = 1;
1653
- pendingSteps.push({
1654
- step: stepNum++,
1655
- action: "enrich_entity",
1656
- instruction: `Enrich the project entity with a meaningful name, description, subtype, and icon.`,
1657
- command: `nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`,
1658
- ...(entityTypeOverride === "application"
1659
- ? { notes: [APPLICATION_SUBTYPE_HINT, "Choose app_custom_built as the default if none of the others clearly apply."] }
1660
- : {}),
1661
- });
1662
+ if (projectConstruct) {
1663
+ pendingSteps.push({
1664
+ step: stepNum++,
1665
+ action: "describe_project",
1666
+ instruction: `Give the project (repository) entity a short description of what lives in this repo.`,
1667
+ command: `nexarch update-entity --key "${projectEntityKey}" --entity-type "project" --name "${projectDirName}" --description "..."`,
1668
+ });
1669
+ }
1670
+ if (!projectConstruct || !isMonorepo) {
1671
+ pendingSteps.push({
1672
+ step: stepNum++,
1673
+ action: "enrich_entity",
1674
+ instruction: `Enrich the application entity with a meaningful name, description, subtype, and icon.`,
1675
+ command: `nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`,
1676
+ ...(entityTypeOverride === "application"
1677
+ ? { notes: [APPLICATION_SUBTYPE_HINT, "Choose app_custom_built as the default if none of the others clearly apply."] }
1678
+ : {}),
1679
+ });
1680
+ }
1662
1681
  if (subPackages.length > 0) {
1663
1682
  pendingSteps.push({
1664
1683
  step: stepNum++,
1665
1684
  action: "classify_sub_packages",
1666
- 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.`,
1667
- commandTemplates: {
1668
- updateEntity: `nexarch update-entity --key "<subPackageExternalKey>" --entity-type "<entityType>" --subtype "<subtype>" --name "..." --description "..."`,
1669
- wireRelationship: `nexarch add-relationship --from "<from>" --to "<to>" --type <composes|part_of> (see structuralRelationship on each classifyPackages entry)`,
1670
- },
1671
- notes: [
1672
- "application sub-packages: parent composes sub-app → add-relationship --from parent --to sub-app --type composes",
1673
- "application_component sub-packages: component part_of parent → add-relationship --from sub-pkg --to parent --type part_of",
1674
- "Do NOT wire the relationship before the entity exists at the correct key.",
1675
- ],
1685
+ instruction: projectConstruct
1686
+ ? `For each sub-package in classifyPackages, run update-entity to confirm type/subtype/name/description. The sourced_from relationship to the project is wired automatically — no structural add-relationship is needed. 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) and the old key's sourced_from should be retired.`
1687
+ : `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.`,
1688
+ commandTemplates: projectConstruct
1689
+ ? {
1690
+ updateEntity: `nexarch update-entity --key "<subPackageExternalKey>" --entity-type "<entityType>" --subtype "<subtype>" --name "..." --description "..."`,
1691
+ }
1692
+ : {
1693
+ updateEntity: `nexarch update-entity --key "<subPackageExternalKey>" --entity-type "<entityType>" --subtype "<subtype>" --name "..." --description "..."`,
1694
+ wireRelationship: `nexarch add-relationship --from "<from>" --to "<to>" --type <composes|part_of> (see structuralRelationship on each classifyPackages entry)`,
1695
+ },
1696
+ notes: projectConstruct
1697
+ ? [
1698
+ `Every package is already sourced_from ${projectEntityKey} — provenance is automatic.`,
1699
+ "New applications arrive as proposed and wait for human activation in the workspace — do not describe them as fully registered.",
1700
+ ]
1701
+ : [
1702
+ "application sub-packages: parent composes sub-app → add-relationship --from parent --to sub-app --type composes",
1703
+ "application_component sub-packages: component part_of parent → add-relationship --from sub-pkg --to parent --type part_of",
1704
+ "Do NOT wire the relationship before the entity exists at the correct key.",
1705
+ ],
1676
1706
  });
1677
1707
  }
1678
1708
  if (entityTypeOverride === "application") {
@@ -1682,7 +1712,7 @@ export async function initProject(args) {
1682
1712
  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.`,
1683
1713
  commandTemplates: {
1684
1714
  updateEntity: `nexarch update-entity --key "application_function:${projectSlug}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`,
1685
- addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}_<function_slug>" --to "${projectExternalKey}" --type part_of`,
1715
+ addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}_<function_slug>" --to ${projectConstruct && isMonorepo ? '"<owning application key from classifyPackages>"' : `"${projectExternalKey}"`} --type part_of`,
1686
1716
  },
1687
1717
  });
1688
1718
  }
@@ -1693,7 +1723,7 @@ export async function initProject(args) {
1693
1723
  commandTemplates: {
1694
1724
  resolve: `nexarch resolve-names --names "..." --json`,
1695
1725
  updateEntity: `nexarch update-entity --key "<canonicalExternalRef>" --entity-type "<entityType>" --name "..."`,
1696
- addRelationship: `nexarch add-relationship --from "${projectExternalKey}" --to "<canonicalExternalRef>" --type depends_on`,
1726
+ addRelationship: `nexarch add-relationship --from ${projectConstruct && isMonorepo ? '"<the application that uses it>"' : `"${projectExternalKey}"`} --to "<canonicalExternalRef>" --type depends_on`,
1697
1727
  },
1698
1728
  });
1699
1729
  pendingSteps.push({
@@ -1702,7 +1732,7 @@ export async function initProject(args) {
1702
1732
  instruction: `Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register each as a decision_record entity.`,
1703
1733
  commandTemplates: {
1704
1734
  updateEntity: `nexarch update-entity --key "decision_record:${projectSlug}_<adr_slug>" --entity-type decision_record --subtype decision_architecture --name "..." --attributes-json '{"decision":{"summary":"...","detail":"..."}}'`,
1705
- addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}_<adr_slug>" --to "${projectExternalKey}" --type decides`,
1735
+ addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}_<adr_slug>" --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`,
1706
1736
  },
1707
1737
  });
1708
1738
  const preservedEntities = entitiesResult.preserved ?? [];
@@ -1725,10 +1755,17 @@ export async function initProject(args) {
1725
1755
  },
1726
1756
  }
1727
1757
  : {}),
1728
- projectEntity: {
1729
- externalKey: projectExternalKey,
1730
- entityType: entityTypeOverride,
1731
- },
1758
+ projectEntity: projectConstruct
1759
+ ? {
1760
+ externalKey: projectEntityKey,
1761
+ entityType: "project",
1762
+ subtype: isMonorepo ? "project_monorepo" : "project_repository",
1763
+ ...(isMonorepo ? {} : { applicationExternalKey: projectExternalKey }),
1764
+ }
1765
+ : {
1766
+ externalKey: projectExternalKey,
1767
+ entityType: entityTypeOverride,
1768
+ },
1732
1769
  pendingSteps,
1733
1770
  readFiles: readmeHints,
1734
1771
  ...(refreshMode
@@ -1832,7 +1869,7 @@ export async function initProject(args) {
1832
1869
  lines.push("Until they are done, this project is registered as a skeleton only.");
1833
1870
  }
1834
1871
  lines.push("");
1835
- lines.push(`PROJECT : ${projectExternalKey}`);
1872
+ lines.push(`PROJECT : ${projectConstruct ? `${projectEntityKey} (${isMonorepo ? "monorepo" : "repository"})` : projectExternalKey}`);
1836
1873
  lines.push(`DIR : ${dir}`);
1837
1874
  if (readmeHints.length > 0) {
1838
1875
  lines.push("");
@@ -1861,10 +1898,15 @@ export async function initProject(args) {
1861
1898
  lines.push(` unresolved deps (${unresolvedDeps.length}): ${unresolvedDeps.slice(0, 5).join(", ")}${unresolvedDeps.length > 5 ? ` … +${unresolvedDeps.length - 5} more` : ""}`);
1862
1899
  }
1863
1900
  lines.push(` 1) nexarch update-entity --key "${sp.externalKey}" --entity-type "${sp.entityType}" --subtype "${sp.subtype}" --name "..." --description "..."`);
1864
- const rel = structuralRelForSubPackage(sp, projectExternalKey);
1865
- if (rel) {
1866
- lines.push(` 2) nexarch add-relationship --from "${rel.from}" --to "${rel.to}" --type ${rel.type}`);
1867
- lines.push(` (adjust key prefix if you changed the entity type above)`);
1901
+ if (projectConstruct) {
1902
+ lines.push(` (sourced_from ${projectEntityKey} is wired automatically — no structural relationship needed)`);
1903
+ }
1904
+ else {
1905
+ const rel = structuralRelForSubPackage(sp, projectExternalKey);
1906
+ if (rel) {
1907
+ lines.push(` 2) nexarch add-relationship --from "${rel.from}" --to "${rel.to}" --type ${rel.type}`);
1908
+ lines.push(` (adjust key prefix if you changed the entity type above)`);
1909
+ }
1868
1910
  }
1869
1911
  }
1870
1912
  }
@@ -1892,10 +1934,16 @@ export async function initProject(args) {
1892
1934
  lines.push("");
1893
1935
  lines.push("REMAINING STEPS:");
1894
1936
  let step = 1;
1895
- lines.push(` ${step++}. nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`);
1896
- if (entityTypeOverride === "application") {
1897
- lines.push(` ${APPLICATION_SUBTYPE_HINT}`);
1898
- lines.push(` (choose app_custom_built as the default if none of the others clearly apply)`);
1937
+ if (projectConstruct) {
1938
+ lines.push(` ${step++}. nexarch update-entity --key "${projectEntityKey}" --entity-type "project" --name "${projectDirName}" --description "..."`);
1939
+ lines.push(` (short description of what lives in this repository)`);
1940
+ }
1941
+ if (!projectConstruct || !isMonorepo) {
1942
+ lines.push(` ${step++}. nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`);
1943
+ if (entityTypeOverride === "application") {
1944
+ lines.push(` ${APPLICATION_SUBTYPE_HINT}`);
1945
+ lines.push(` (choose app_custom_built as the default if none of the others clearly apply)`);
1946
+ }
1899
1947
  }
1900
1948
  if (subPackages.length > 0) {
1901
1949
  lines.push(` ${step++}. Update each sub-package listed under CLASSIFY_THESE above.`);
@@ -1907,19 +1955,28 @@ export async function initProject(args) {
1907
1955
  lines.push(` integration_function (external connectivity), or data_function (data processing).`);
1908
1956
  lines.push(` Only register functions clearly evidenced by the codebase — do not invent them.`);
1909
1957
  lines.push(` For each one found:`);
1958
+ const fnOwnerTarget = projectConstruct && isMonorepo ? '"<owning application key from CLASSIFY_THESE>"' : `"${projectExternalKey}"`;
1910
1959
  lines.push(` nexarch update-entity --key "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`);
1911
- lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --to "${projectExternalKey}" --type part_of`);
1960
+ lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --to ${fnOwnerTarget} --type part_of`);
1912
1961
  }
1913
1962
  lines.push(` ${step++}. Scan the READMEs for platforms/SaaS not auto-detected (Vercel, Neon, Stripe, etc.).`);
1914
1963
  lines.push(` For each found: nexarch resolve-names --names "..." --json → nexarch update-entity → nexarch add-relationship`);
1915
1964
  lines.push(` ${step++}. Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register decision_record entities.`);
1916
1965
  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":"..."}}'`);
1917
- lines.push(` nexarch add-relationship --from "decision_record:..." --to "${projectExternalKey}" --type decides`);
1966
+ lines.push(` nexarch add-relationship --from "decision_record:..." --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`);
1918
1967
  lines.push("");
1919
- lines.push("RELATIONSHIP DIRECTION RULES (for sub-packages):");
1920
- lines.push(" apps/* (deployable components) → parent composes → sub-application");
1921
- lines.push(" packages/* (shared libraries) → parent depends_on library");
1922
- lines.push(" Deps wired from manifests are already pre-wired; do not re-add unless key changed.");
1968
+ if (projectConstruct) {
1969
+ lines.push("RELATIONSHIP RULES:");
1970
+ lines.push(` Every package is sourced_from ${projectEntityKey} wired automatically (provenance, not containment).`);
1971
+ lines.push(" Manifest deps are pre-wired per package; do not re-add unless a key changed.");
1972
+ lines.push(" Genuine product composition (one product spanning these apps) is a human judgement — propose it, never infer it from directory layout.");
1973
+ }
1974
+ else {
1975
+ lines.push("RELATIONSHIP DIRECTION RULES (for sub-packages):");
1976
+ lines.push(" apps/* (deployable components) → parent composes → sub-application");
1977
+ lines.push(" packages/* (shared libraries) → parent depends_on → library");
1978
+ lines.push(" Deps wired from manifests are already pre-wired; do not re-add unless key changed.");
1979
+ }
1923
1980
  if (refreshMode && (currentOutgoingRels.length > 0 || currentPartOfRels.length > 0)) {
1924
1981
  lines.push("");
1925
1982
  lines.push(`GRAPH_STATE (${currentOutgoingRels.length + currentPartOfRels.length} current relationships in Nexarch — compare against what the scanner detected above):`);
@@ -1943,7 +2000,18 @@ export async function initProject(args) {
1943
2000
  ok: Number(entitiesResult.summary?.failed ?? 0) === 0,
1944
2001
  status: outputStatus,
1945
2002
  mode: refreshMode ? "refresh" : "init",
1946
- project: { name: displayName, externalKey: projectExternalKey, entityType: entityTypeOverride, detectedEcosystems },
2003
+ project: projectConstruct
2004
+ ? {
2005
+ name: projectDirName,
2006
+ externalKey: projectEntityKey,
2007
+ entityType: "project",
2008
+ subtype: isMonorepo ? "project_monorepo" : "project_repository",
2009
+ detectedEcosystems,
2010
+ ...(isMonorepo
2011
+ ? { applications: subPackages.filter((sp) => sp.entityType === "application").map((sp) => sp.externalKey) }
2012
+ : { applicationExternalKey: projectExternalKey }),
2013
+ }
2014
+ : { name: displayName, externalKey: projectExternalKey, entityType: entityTypeOverride, detectedEcosystems },
1947
2015
  entities: entitiesResult.summary ?? {},
1948
2016
  relationships: relsResult?.summary ?? { requested: 0, succeeded: 0, failed: 0 },
1949
2017
  metrics: {
package/dist/index.js CHANGED
@@ -73,195 +73,198 @@ async function main() {
73
73
  }
74
74
  const handler = commands[command ?? ""];
75
75
  if (!handler) {
76
- console.log(`
77
- nexarch — Your architecture workspace for AI delivery.
78
-
79
- Usage:
80
- nexarch login Authenticate in browser and store company-scoped credentials
81
- Option: --company <id>
82
- nexarch logout Remove stored credentials
83
- nexarch status Check connection and show architecture summary
84
- nexarch setup One-step onboarding: login (if needed) + MCP config + register agent
85
- nexarch mcp-config Print MCP server config block for manual setup
86
- Client list is registry-managed (see 'nexarch mcp-config --client <code>')
87
- nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
88
- nexarch init-agent Run handshake + mandatory agent registration in graph (advanced/manual)
89
- Options: --agent-id <id> --bind-to-external-key <key>
90
- --bind-relationship-type <code> --redact-hostname
91
- --json --strict
92
- nexarch agent identify
93
- Capture richer coding-agent identity metadata
94
- Options: --agent-id <id> --provider <provider> --model <model>
95
- --client <name> [--framework <name>] [--session-id <id>]
96
- [--tool-version <v>] [--capabilities <csv>]
97
- [--notes <text>] [--json]
98
- nexarch agent-identify
99
- Alias of 'nexarch agent identify'
100
- nexarch init-project
101
- Scan a project directory, resolve detected packages/env vars/
102
- config files against the reference library, write entities and
103
- relationships to the architecture graph, and log unresolved
104
- names as reference candidates.
105
- When entity-type=application and existing applications are present,
106
- prompts to map to an existing app or create a new one.
107
- Options: --dir <path> (default: cwd)
108
- --name <name> override project name
109
- --entity-type <code> (default: application)
110
- --application-ref <entityRef> force mapping target
111
- --create-application force new application entity
112
- --auto-map-application auto-map only when high confidence
113
- --non-interactive fail on ambiguous mapping
114
- --batch-size <n> upsert batch size (default: 10)
115
- --profile include timing/profile data in JSON output
116
- --dry-run preview without writing
117
- --json
118
- nexarch update-project
119
- Re-scan a previously registered project directory, refresh
120
- entities and relationships in the graph, and diff the new scan
121
- against the current graph state to surface stale relationships
122
- and removed sub-packages for the calling agent to review.
123
- Accepts all the same options as init-project plus:
124
- --application-ref <entityRef> target project key (recommended)
125
- --auto-map-application auto-select best-match application
126
- Output includes enrichmentRequired.diff with:
127
- newRelationships — detected but not yet in graph
128
- staleRelationships in graph but absent from manifests
129
- removedSubPackages — previously registered, no longer on disk
130
- --json
131
- nexarch update-entity
132
- Update the name and/or description of an existing graph entity.
133
- Use this after init-project to enrich the entity with meaningful
134
- content from the project README or docs.
135
- Options: --key <externalKey> (required)
136
- --name <name>
137
- --description <text>
138
- --entity-type <code> (default: application)
139
- --subtype <code>
140
- --icon <lucide-name> (convenience; sets attributes.application_icon)
141
- --attributes-json '<json object>'
142
- --attributes-file <path.json>
143
- --json
144
- nexarch add-relationship
145
- Add relationships between existing graph entities (single or batch).
146
- Single options: --from <externalKey>
147
- --to <externalKey>
148
- --type <code> (e.g. part_of, depends_on)
149
- Batch options: --relationships-json '<json array>'
150
- --relationships-file <path.json>
151
- --json
152
- nexarch register-alias
153
- Register a company-scoped alias for an entity so future
154
- scans resolve it instead of logging it as a candidate.
155
- Use after enriching internal monorepo packages.
156
- Options: --alias <value> (required, e.g. @scope/name)
157
- --key <externalKey> (required)
158
- --name <name> (required)
159
- --entity-type <code> (required)
160
- --subtype <code>
161
- --description <text>
162
- --json
163
- nexarch resolve-names
164
- Look up one or more raw names (package names, platform
165
- names) against the global reference library and return
166
- their canonical external keys. Useful for gap-check
167
- results before calling add-relationship.
168
- Options: --names <csv> (required, e.g. "vercel,neon")
169
- --json
170
- nexarch list-entities
171
- List entities from the workspace graph.
172
- Options: --type <entityTypeCode>
173
- --status <status>
174
- --query <text>
175
- --limit <1-500>
176
- --json
177
- nexarch list-relationships
178
- List relationships from the workspace graph.
179
- Options: --type <relationshipTypeCode>
180
- --status <status>
181
- --from <fromExternalKey>
182
- --to <toExternalKey>
183
- --limit <1-500>
184
- --json
185
- nexarch register-runtime
186
- Register or refresh runtime + optional application context
187
- without performing check-in.
188
- Options: --application-ref <entityRef>
189
- --client <name>
190
- --version <semver>
191
- --json
192
- nexarch check-in Preview pending application-target commands (no auto-claim)
193
- and report draft/proposed applications needing review so the
194
- agent can prompt the user to explore and instantiate them.
195
- Use command-claim to explicitly claim a specific command.
196
- Scope is resolved server-side from active company context.
197
- Options: --agent-key <key> override stored agent key
198
- --application-ref <entityRef> narrow preview scope
199
- --json JSON output includes draftApplications[] and proposedApplications[]
200
- nexarch proposals start
201
- Start a new application workspace from a proposed NexArch app.
202
- Lists proposed apps, shows policy review gates, writes a starter
203
- project scaffold, and activates the proposal to active once
204
- required policy controls are acknowledged.
205
- Options: --id <applicationId>
206
- --dir <path>
207
- --reason <text>
208
- --repo <url>
209
- --skip-activate
210
- --activate
211
- --force
212
- --non-interactive
213
- --json
214
- nexarch command-claim
215
- Explicitly claim a pending command by ID.
216
- Options: --id <commandId> (required)
217
- --agent-key <key> override stored agent key
218
- --application-ref <entityRef> required for application-target commands
219
- --json
220
- nexarch command-done
221
- Mark a claimed command as completed.
222
- Options: --id <commandId> (required)
223
- --summary <text> short summary of what was done
224
- --summary-file <path.md|txt>
225
- --json
226
- nexarch command-fail
227
- Mark a claimed command as failed.
228
- Options: --id <commandId> (required)
229
- --error <message> (required)
230
- --json
231
- nexarch policy-controls
232
- Fetch policy controls/rules assigned to an entity (for policy audits).
233
- Options: --entity <externalKey> (required, e.g. application:bad-driving)
234
- --json
235
- nexarch policy-audit-template
236
- Generate a findings JSON template from policy controls/rules for an entity.
237
- Options: --entity <externalKey> (required)
238
- --control-id <uuid> (repeatable; optional filter)
239
- --default-result <pass|partial|fail> (default: fail)
240
- --output <path.json>
241
- --json
242
- nexarch policy-audit-submit
243
- Submit structured policy findings (writes policy_audit_finding rows).
244
- Options: --command-id <id> (required)
245
- --application-key <key> (required)
246
- --agent-key <key> (optional; defaults from identity)
247
- --finding <controlId|ruleId|result|rationale|missing1;missing2> (repeatable)
248
- --findings-json <json-array>
249
- --findings-file <path.json>
250
- --json
251
- nexarch policy-audit-results
252
- Retrieve stored results of previous policy audits for an application.
253
- Options: --entity <applicationEntityRef> (required)
254
- --limit <1-10> (default 1)
255
- --json
256
- nexarch applied-policies
257
- List policy documents applied to this company account.
258
- Options: --pack <packCode> filter to a specific pack
259
- --markdown include full document markdown
260
- --json
261
- nexarch governance-summary
262
- Print review queue, graph stats, and per-application policy
263
- audit rollup (latest run status, pass/partial/fail counts).
264
- Options: --json
76
+ console.log(`
77
+ nexarch — Your architecture workspace for AI delivery.
78
+
79
+ Usage:
80
+ nexarch login Authenticate in browser and store company-scoped credentials
81
+ Option: --company <id>
82
+ nexarch logout Remove stored credentials
83
+ nexarch status Check connection and show architecture summary
84
+ nexarch setup One-step onboarding: login (if needed) + MCP config + register agent
85
+ nexarch mcp-config Print MCP server config block for manual setup
86
+ Client list is registry-managed (see 'nexarch mcp-config --client <code>')
87
+ nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
88
+ nexarch init-agent Run handshake + mandatory agent registration in graph (advanced/manual)
89
+ Options: --agent-id <id> --bind-to-external-key <key>
90
+ --bind-relationship-type <code> --redact-hostname
91
+ --json --strict
92
+ nexarch agent identify
93
+ Capture richer coding-agent identity metadata
94
+ Options: --agent-id <id> --provider <provider> --model <model>
95
+ --client <name> [--framework <name>] [--session-id <id>]
96
+ [--tool-version <v>] [--capabilities <csv>]
97
+ [--notes <text>] [--json]
98
+ nexarch agent-identify
99
+ Alias of 'nexarch agent identify'
100
+ nexarch init-project
101
+ Scan a project directory, resolve detected packages/env vars/
102
+ config files against the reference library, write entities and
103
+ relationships to the architecture graph, and log unresolved
104
+ names as reference candidates.
105
+ Monorepos register a project entity plus one proposed
106
+ application per deployable package (sourced_from the project).
107
+ Single-package repos register the repo's one application; when
108
+ similar applications exist they are listed, and the new
109
+ application is created as proposed for review at activation.
110
+ Options: --dir <path> (default: cwd)
111
+ --name <name> override project name
112
+ --entity-type <code> (default: application)
113
+ --application-ref <entityRef> force mapping target
114
+ --create-application force new application entity
115
+ --auto-map-application auto-map only when high confidence
116
+ --non-interactive deprecated (mapping no longer prompts)
117
+ --batch-size <n> upsert batch size (default: 10)
118
+ --profile include timing/profile data in JSON output
119
+ --dry-run preview without writing
120
+ --json
121
+ nexarch update-project
122
+ Re-scan a previously registered project directory, refresh
123
+ entities and relationships in the graph, and diff the new scan
124
+ against the current graph state to surface stale relationships
125
+ and removed sub-packages for the calling agent to review.
126
+ Accepts all the same options as init-project plus:
127
+ --application-ref <entityRef> target project key (recommended)
128
+ --auto-map-application auto-select best-match application
129
+ Output includes enrichmentRequired.diff with:
130
+ newRelationships — detected but not yet in graph
131
+ staleRelationships in graph but absent from manifests
132
+ removedSubPackages — previously registered, no longer on disk
133
+ --json
134
+ nexarch update-entity
135
+ Update the name and/or description of an existing graph entity.
136
+ Use this after init-project to enrich the entity with meaningful
137
+ content from the project README or docs.
138
+ Options: --key <externalKey> (required)
139
+ --name <name>
140
+ --description <text>
141
+ --entity-type <code> (default: application)
142
+ --subtype <code>
143
+ --icon <lucide-name> (convenience; sets attributes.application_icon)
144
+ --attributes-json '<json object>'
145
+ --attributes-file <path.json>
146
+ --json
147
+ nexarch add-relationship
148
+ Add relationships between existing graph entities (single or batch).
149
+ Single options: --from <externalKey>
150
+ --to <externalKey>
151
+ --type <code> (e.g. part_of, depends_on)
152
+ Batch options: --relationships-json '<json array>'
153
+ --relationships-file <path.json>
154
+ --json
155
+ nexarch register-alias
156
+ Register a company-scoped alias for an entity so future
157
+ scans resolve it instead of logging it as a candidate.
158
+ Use after enriching internal monorepo packages.
159
+ Options: --alias <value> (required, e.g. @scope/name)
160
+ --key <externalKey> (required)
161
+ --name <name> (required)
162
+ --entity-type <code> (required)
163
+ --subtype <code>
164
+ --description <text>
165
+ --json
166
+ nexarch resolve-names
167
+ Look up one or more raw names (package names, platform
168
+ names) against the global reference library and return
169
+ their canonical external keys. Useful for gap-check
170
+ results before calling add-relationship.
171
+ Options: --names <csv> (required, e.g. "vercel,neon")
172
+ --json
173
+ nexarch list-entities
174
+ List entities from the workspace graph.
175
+ Options: --type <entityTypeCode>
176
+ --status <status>
177
+ --query <text>
178
+ --limit <1-500>
179
+ --json
180
+ nexarch list-relationships
181
+ List relationships from the workspace graph.
182
+ Options: --type <relationshipTypeCode>
183
+ --status <status>
184
+ --from <fromExternalKey>
185
+ --to <toExternalKey>
186
+ --limit <1-500>
187
+ --json
188
+ nexarch register-runtime
189
+ Register or refresh runtime + optional application context
190
+ without performing check-in.
191
+ Options: --application-ref <entityRef>
192
+ --client <name>
193
+ --version <semver>
194
+ --json
195
+ nexarch check-in Preview pending application-target commands (no auto-claim)
196
+ and report draft/proposed applications needing review so the
197
+ agent can prompt the user to explore and instantiate them.
198
+ Use command-claim to explicitly claim a specific command.
199
+ Scope is resolved server-side from active company context.
200
+ Options: --agent-key <key> override stored agent key
201
+ --application-ref <entityRef> narrow preview scope
202
+ --json JSON output includes draftApplications[] and proposedApplications[]
203
+ nexarch proposals start
204
+ Start a new application workspace from a proposed NexArch app.
205
+ Lists proposed apps, shows policy review gates, writes a starter
206
+ project scaffold, and activates the proposal to active once
207
+ required policy controls are acknowledged.
208
+ Options: --id <applicationId>
209
+ --dir <path>
210
+ --reason <text>
211
+ --repo <url>
212
+ --skip-activate
213
+ --activate
214
+ --force
215
+ --non-interactive
216
+ --json
217
+ nexarch command-claim
218
+ Explicitly claim a pending command by ID.
219
+ Options: --id <commandId> (required)
220
+ --agent-key <key> override stored agent key
221
+ --application-ref <entityRef> required for application-target commands
222
+ --json
223
+ nexarch command-done
224
+ Mark a claimed command as completed.
225
+ Options: --id <commandId> (required)
226
+ --summary <text> short summary of what was done
227
+ --summary-file <path.md|txt>
228
+ --json
229
+ nexarch command-fail
230
+ Mark a claimed command as failed.
231
+ Options: --id <commandId> (required)
232
+ --error <message> (required)
233
+ --json
234
+ nexarch policy-controls
235
+ Fetch policy controls/rules assigned to an entity (for policy audits).
236
+ Options: --entity <externalKey> (required, e.g. application:bad-driving)
237
+ --json
238
+ nexarch policy-audit-template
239
+ Generate a findings JSON template from policy controls/rules for an entity.
240
+ Options: --entity <externalKey> (required)
241
+ --control-id <uuid> (repeatable; optional filter)
242
+ --default-result <pass|partial|fail> (default: fail)
243
+ --output <path.json>
244
+ --json
245
+ nexarch policy-audit-submit
246
+ Submit structured policy findings (writes policy_audit_finding rows).
247
+ Options: --command-id <id> (required)
248
+ --application-key <key> (required)
249
+ --agent-key <key> (optional; defaults from identity)
250
+ --finding <controlId|ruleId|result|rationale|missing1;missing2> (repeatable)
251
+ --findings-json <json-array>
252
+ --findings-file <path.json>
253
+ --json
254
+ nexarch policy-audit-results
255
+ Retrieve stored results of previous policy audits for an application.
256
+ Options: --entity <applicationEntityRef> (required)
257
+ --limit <1-10> (default 1)
258
+ --json
259
+ nexarch applied-policies
260
+ List policy documents applied to this company account.
261
+ Options: --pack <packCode> filter to a specific pack
262
+ --markdown include full document markdown
263
+ --json
264
+ nexarch governance-summary
265
+ Print review queue, graph stats, and per-application policy
266
+ audit rollup (latest run status, pass/partial/fail counts).
267
+ Options: --json
265
268
  `);
266
269
  process.exit(command ? 1 : 0);
267
270
  }
package/package.json CHANGED
@@ -1,35 +1,35 @@
1
- {
2
- "name": "nexarch",
3
- "version": "0.12.4",
4
- "description": "Your architecture workspace for AI delivery.",
5
- "keywords": [
6
- "nexarch",
7
- "mcp",
8
- "architecture",
9
- "ai"
10
- ],
11
- "license": "MIT",
12
- "author": "Nexarch <hello@nexarch.ai>",
13
- "homepage": "https://nexarch.ai",
14
- "engines": {
15
- "node": ">=18"
16
- },
17
- "type": "module",
18
- "bin": {
19
- "nexarch": "dist/index.js"
20
- },
21
- "files": [
22
- "dist"
23
- ],
24
- "scripts": {
25
- "build": "tsc",
26
- "prepublishOnly": "tsc",
27
- "dev": "tsx src/index.ts",
28
- "typecheck": "tsc --noEmit"
29
- },
30
- "devDependencies": {
31
- "@types/node": "^22",
32
- "tsx": "^4",
33
- "typescript": "^5"
34
- }
35
- }
1
+ {
2
+ "name": "nexarch",
3
+ "version": "0.12.6",
4
+ "description": "Your architecture workspace for AI delivery.",
5
+ "keywords": [
6
+ "nexarch",
7
+ "mcp",
8
+ "architecture",
9
+ "ai"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "Nexarch <hello@nexarch.ai>",
13
+ "homepage": "https://nexarch.ai",
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "type": "module",
18
+ "bin": {
19
+ "nexarch": "dist/index.js"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "prepublishOnly": "tsc",
27
+ "dev": "tsx src/index.ts",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^22",
32
+ "tsx": "^4",
33
+ "typescript": "^5"
34
+ }
35
+ }