nexarch 0.8.3 → 0.8.9
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,12 +1,12 @@
|
|
|
1
1
|
import { arch, homedir, hostname, platform, release, type as osType, userInfo } from "os";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import process from "process";
|
|
5
5
|
import { requireCredentials } from "../lib/credentials.js";
|
|
6
6
|
import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
|
|
7
7
|
import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
|
|
8
8
|
import { buildVersionAttributes } from "../lib/version-normalization.js";
|
|
9
|
-
const CLI_VERSION = "0.
|
|
9
|
+
const CLI_VERSION = "0.8.4";
|
|
10
10
|
const AGENT_ENTITY_TYPE = "agent";
|
|
11
11
|
const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
|
|
12
12
|
function parseFlag(args, flag) {
|
|
@@ -62,17 +62,80 @@ function detectLikelyApplicationInCwd() {
|
|
|
62
62
|
"build.gradle",
|
|
63
63
|
"build.gradle.kts",
|
|
64
64
|
];
|
|
65
|
-
const
|
|
65
|
+
const skipDirs = new Set([
|
|
66
|
+
".git",
|
|
67
|
+
"node_modules",
|
|
68
|
+
"dist",
|
|
69
|
+
"build",
|
|
70
|
+
".next",
|
|
71
|
+
".turbo",
|
|
72
|
+
"coverage",
|
|
73
|
+
"tmp",
|
|
74
|
+
".cache",
|
|
75
|
+
"target",
|
|
76
|
+
"vendor",
|
|
77
|
+
".venv",
|
|
78
|
+
"venv",
|
|
79
|
+
"__pycache__",
|
|
80
|
+
]);
|
|
81
|
+
const evidence = new Set();
|
|
82
|
+
const checkDir = (baseDir, relativePrefix = "") => {
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
if (existsSync(join(baseDir, file))) {
|
|
85
|
+
evidence.add(`${relativePrefix}${file}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const entries = readdirSync(baseDir);
|
|
90
|
+
if (entries.some((name) => name.toLowerCase().endsWith(".csproj"))) {
|
|
91
|
+
evidence.add(`${relativePrefix}*.csproj`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// non-fatal; ignore
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
// Level 0: current directory
|
|
99
|
+
checkDir(cwd);
|
|
100
|
+
// Levels 1-2: monorepo children
|
|
66
101
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
102
|
+
for (const l1 of readdirSync(cwd)) {
|
|
103
|
+
if (l1.startsWith(".") || skipDirs.has(l1))
|
|
104
|
+
continue;
|
|
105
|
+
const l1Path = join(cwd, l1);
|
|
106
|
+
try {
|
|
107
|
+
if (!statSync(l1Path).isDirectory())
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
checkDir(l1Path, `${l1}/`);
|
|
114
|
+
try {
|
|
115
|
+
for (const l2 of readdirSync(l1Path)) {
|
|
116
|
+
if (l2.startsWith(".") || skipDirs.has(l2))
|
|
117
|
+
continue;
|
|
118
|
+
const l2Path = join(l1Path, l2);
|
|
119
|
+
try {
|
|
120
|
+
if (!statSync(l2Path).isDirectory())
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
checkDir(l2Path, `${l1}/${l2}/`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// ignore
|
|
131
|
+
}
|
|
70
132
|
}
|
|
71
133
|
}
|
|
72
134
|
catch {
|
|
73
|
-
//
|
|
135
|
+
// ignore
|
|
74
136
|
}
|
|
75
|
-
|
|
137
|
+
const evidenceList = Array.from(evidence);
|
|
138
|
+
return { detected: evidenceList.length > 0, evidence: evidenceList };
|
|
76
139
|
}
|
|
77
140
|
function getSanitizedHostname(redactHostname) {
|
|
78
141
|
const raw = hostname() || "unknown-host";
|
|
@@ -891,7 +891,13 @@ export async function initProject(args) {
|
|
|
891
891
|
for (const sp of subPackages) {
|
|
892
892
|
const nameSlug = slugify(sp.name);
|
|
893
893
|
const needsPrefix = !sp.name.includes("/") && !nameSlug.startsWith(projectSlug);
|
|
894
|
-
|
|
894
|
+
let keySlug = needsPrefix ? `${projectSlug}-${nameSlug}` : nameSlug;
|
|
895
|
+
// Avoid key collision with the top-level project (e.g. sub-package named "nexarch").
|
|
896
|
+
if (`${sp.entityType}:${keySlug}` === projectExternalKey) {
|
|
897
|
+
const relSlug = slugify(sp.relativePath.replace(/\//g, "-"));
|
|
898
|
+
keySlug = relSlug ? `${projectSlug}-${relSlug}` : `${projectSlug}-${nameSlug}-sub`;
|
|
899
|
+
}
|
|
900
|
+
sp.externalKey = `${sp.entityType}:${keySlug}`;
|
|
895
901
|
}
|
|
896
902
|
if (!asJson) {
|
|
897
903
|
console.log(` Project : ${displayName} (${entityTypeOverride})`);
|
|
@@ -1249,7 +1255,55 @@ ${subPackages.map((sp) => {
|
|
|
1249
1255
|
return ` • ${sp.name} (--from "${sp.externalKey}"):\n${lines.join("\n")}`;
|
|
1250
1256
|
}).join("\n\n")}
|
|
1251
1257
|
`;
|
|
1252
|
-
const
|
|
1258
|
+
const adrStepNumber = subPackages.length > 0 ? "STEP 4" : "STEP 3";
|
|
1259
|
+
const finalStep = subPackages.length > 0 ? "STEP 5" : "STEP 4";
|
|
1260
|
+
const adrSection = `
|
|
1261
|
+
${adrStepNumber} — Ask the enrichment agent to extract ADRs and register decision records.
|
|
1262
|
+
|
|
1263
|
+
Do NOT rely on init-project to parse ADR files. The enrichment agent should read ADR markdown and
|
|
1264
|
+
explicitly register decision records with relationships.
|
|
1265
|
+
|
|
1266
|
+
Agent task:
|
|
1267
|
+
• Find ADR files (e.g. docs/adr, adrs, decisions, ADR-*.md)
|
|
1268
|
+
• For each ADR, create/update a decision record entity
|
|
1269
|
+
• Link it using: decision_record --decides--> application/sub-app
|
|
1270
|
+
|
|
1271
|
+
REQUIRED fields to pass per decision:
|
|
1272
|
+
• key: decision_record:<project>-<adr-slug>
|
|
1273
|
+
• subtype: decision_architecture
|
|
1274
|
+
• name: ADR title
|
|
1275
|
+
• description: MUST be rich text (Markdown), not plain key/value text.
|
|
1276
|
+
Use this exact Markdown structure so the web portal renders cleanly:
|
|
1277
|
+
|
|
1278
|
+
## ADR Metadata
|
|
1279
|
+
- **ADR-ID:** <id/filename slug>
|
|
1280
|
+
- **Status:** <proposed|accepted|superseded|deprecated|...>
|
|
1281
|
+
- **Date:** <YYYY-MM-DD or source value>
|
|
1282
|
+
- **Deciders:** <comma-separated names/roles>
|
|
1283
|
+
- **Source Path:** <repo-relative path>
|
|
1284
|
+
|
|
1285
|
+
## Context
|
|
1286
|
+
<1-2 lines>
|
|
1287
|
+
|
|
1288
|
+
## Decision
|
|
1289
|
+
<1-3 lines>
|
|
1290
|
+
|
|
1291
|
+
## Consequences
|
|
1292
|
+
<1-3 lines>
|
|
1293
|
+
|
|
1294
|
+
Command pattern:
|
|
1295
|
+
npx nexarch update-entity \\
|
|
1296
|
+
--key "decision_record:<project>-<adr-slug>" \\
|
|
1297
|
+
--entity-type "decision_record" \\
|
|
1298
|
+
--subtype "decision_architecture" \\
|
|
1299
|
+
--name "<ADR title>" \\
|
|
1300
|
+
--description "## ADR Metadata\n- **ADR-ID:** <id>\n- **Status:** <status>\n- **Date:** <date>\n- **Deciders:** <deciders>\n- **Source Path:** <path>\n\n## Context\n<context>\n\n## Decision\n<decision>\n\n## Consequences\n<consequences>"
|
|
1301
|
+
|
|
1302
|
+
npx nexarch add-relationship \\
|
|
1303
|
+
--from "decision_record:<project>-<adr-slug>" \\
|
|
1304
|
+
--to "<application-or-sub-app-key>" \\
|
|
1305
|
+
--type "decides"
|
|
1306
|
+
`;
|
|
1253
1307
|
const gapCheckSection = `
|
|
1254
1308
|
${finalStep} — Identify architecturally significant components not auto-detected.
|
|
1255
1309
|
|
|
@@ -1318,7 +1372,7 @@ STEP 2 — Enrich the project entity. Run this command with the description you'
|
|
|
1318
1372
|
--entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? " \\\n --subtype \"app_custom_built\" \\\n --icon \"<curated icon>\"" : ""} \\
|
|
1319
1373
|
--name "<proper product name from README>" \\
|
|
1320
1374
|
--description "<2–4 sentence summary of what it does and why>"
|
|
1321
|
-
${subPkgSection}${gapCheckSection}`;
|
|
1375
|
+
${subPkgSection}${adrSection}${gapCheckSection}`;
|
|
1322
1376
|
}
|
|
1323
1377
|
const enrichmentTask = {
|
|
1324
1378
|
instructions: buildEnrichmentInstructions(),
|