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.
- package/dist/commands/init-project.js +241 -173
- package/dist/index.js +192 -189
- package/package.json +35 -35
|
@@ -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
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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 (
|
|
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
|
-
|
|
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 (!
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
|
|
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
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
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
|
-
|
|
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:
|
|
1435
|
+
name: dirDisplayName,
|
|
1447
1436
|
confidence: 0.8,
|
|
1448
|
-
attributes: {
|
|
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 →
|
|
1491
|
-
|
|
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
|
-
//
|
|
1509
|
-
//
|
|
1510
|
-
//
|
|
1511
|
-
//
|
|
1512
|
-
if (
|
|
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
|
-
|
|
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
|
-
//
|
|
1561
|
-
if (
|
|
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
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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:
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
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
|
-
|
|
1730
|
-
|
|
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
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
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
|
-
|
|
1896
|
-
|
|
1897
|
-
lines.push(`
|
|
1898
|
-
|
|
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
|
|
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
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
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:
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
--
|
|
112
|
-
--
|
|
113
|
-
--
|
|
114
|
-
--
|
|
115
|
-
--
|
|
116
|
-
--
|
|
117
|
-
--
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
--
|
|
140
|
-
--
|
|
141
|
-
--
|
|
142
|
-
--
|
|
143
|
-
--
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
--
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
--
|
|
161
|
-
--
|
|
162
|
-
--
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
--
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
--
|
|
184
|
-
--
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
--
|
|
210
|
-
--
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
--
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
--
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
--
|
|
249
|
-
--
|
|
250
|
-
--
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
"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
|
+
}
|