nexarch 0.13.3 → 0.13.5
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 +13 -7
- package/dist/commands/login.js +86 -11
- package/dist/commands/search-icons.js +64 -0
- package/dist/index.js +15 -1
- package/dist/lib/skills.js +18 -1
- package/package.json +39 -39
|
@@ -2343,7 +2343,7 @@ export async function initProject(args) {
|
|
|
2343
2343
|
pendingSteps.push({
|
|
2344
2344
|
step: stepNum++,
|
|
2345
2345
|
action: "enrich_entity",
|
|
2346
|
-
instruction: `Enrich the application entity with a meaningful name, description, subtype, and icon.`,
|
|
2346
|
+
instruction: `Enrich the application entity with a meaningful name, description, subtype, and icon. Find a real icon name with \`nexarch search-icons "<what the application is>"\`; a guessed name that doesn't exist is dropped without an error.`,
|
|
2347
2347
|
command: `nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`,
|
|
2348
2348
|
...(entityTypeOverride === "application"
|
|
2349
2349
|
? { notes: [APPLICATION_SUBTYPE_HINT, "Choose app_custom_built as the default if none of the others clearly apply."] }
|
|
@@ -2384,9 +2384,11 @@ export async function initProject(args) {
|
|
|
2384
2384
|
instruction: `Review the codebase to identify discrete application functions (what the application does). ` +
|
|
2385
2385
|
`Examine named modules, route layout, service boundaries, and any architecture documentation. ` +
|
|
2386
2386
|
`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). ` +
|
|
2387
|
-
`Only register functions clearly evidenced by the codebase — do not invent them. ${DESCRIPTION_GUIDANCE}
|
|
2387
|
+
`Only register functions clearly evidenced by the codebase — do not invent them. ${DESCRIPTION_GUIDANCE} ` +
|
|
2388
|
+
`Give each function an icon for what it does: find a real name with \`nexarch search-icons "<what it does>"\` (or nexarch_search_icons) and pass it as --icon. Don't guess a name; one that doesn't exist is dropped without an error. If two searches find nothing fitting, leave the icon off.`,
|
|
2388
2389
|
commandTemplates: {
|
|
2389
|
-
|
|
2390
|
+
searchIcons: `nexarch search-icons "<what the function does>"`,
|
|
2391
|
+
updateEntity: `nexarch update-entity --key "application_function:${projectSlug}_<function_slug>" --entity-type application_function --subtype core_function --icon "<name from search-icons>" --name "..." --description "..."`,
|
|
2390
2392
|
addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}_<function_slug>" --to ${projectConstruct && isMonorepo ? '"<owning application key from classifyPackages>"' : `"${projectExternalKey}"`} --type part_of`,
|
|
2391
2393
|
note: "Functions are structural decomposition only: do not add runtime dependencies from an application_function. Model independently deployable services as applications or application components, then relate those boundaries with depends_on or integrates_with.",
|
|
2392
2394
|
},
|
|
@@ -2405,7 +2407,7 @@ export async function initProject(args) {
|
|
|
2405
2407
|
pendingSteps.push({
|
|
2406
2408
|
step: stepNum++,
|
|
2407
2409
|
action: "register_decision_records",
|
|
2408
|
-
instruction: `Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register
|
|
2410
|
+
instruction: `Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register the architectural ones as decision_record entities: a decision that crosses a boundary between applications, services or teams; changes an integration, data ownership, security or deployment; adopts, replaces or retires a technology others depend on; or is expensive to reverse. Skip design decisions inside one application, even when they are written up as ADRs.`,
|
|
2409
2411
|
commandTemplates: {
|
|
2410
2412
|
updateEntity: `nexarch update-entity --key "decision_record:${projectSlug}_<adr_slug>" --entity-type decision_record --subtype decision_architecture --name "..." --attributes-json '{"decision":{"summary":"...","detail":"..."}}'`,
|
|
2411
2413
|
addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}_<adr_slug>" --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`,
|
|
@@ -2519,7 +2521,7 @@ export async function initProject(args) {
|
|
|
2519
2521
|
unresolvedNames: unresolvedItems.map((r) => r.input),
|
|
2520
2522
|
iconHints: {
|
|
2521
2523
|
provider: "lucide",
|
|
2522
|
-
note: "Use
|
|
2524
|
+
note: "Use a name returned by `nexarch search-icons` (or nexarch_search_icons); a guessed name that doesn't exist is dropped without an error. Omit when nothing fits.",
|
|
2523
2525
|
},
|
|
2524
2526
|
};
|
|
2525
2527
|
}
|
|
@@ -2643,15 +2645,19 @@ export async function initProject(args) {
|
|
|
2643
2645
|
lines.push(` Use subtype core_function (primary business function), supporting_function (auxiliary/enablement),`);
|
|
2644
2646
|
lines.push(` integration_function (external connectivity), or data_function (data processing).`);
|
|
2645
2647
|
lines.push(` Only register functions clearly evidenced by the codebase — do not invent them. ${DESCRIPTION_GUIDANCE}`);
|
|
2648
|
+
lines.push(` Give each function an icon for what it does. Find a real name first; a guessed one is dropped without an error:`);
|
|
2649
|
+
lines.push(` nexarch search-icons "<what the function does>"`);
|
|
2646
2650
|
lines.push(` For each one found:`);
|
|
2647
2651
|
const fnOwnerTarget = projectConstruct && isMonorepo ? '"<owning application key from CLASSIFY_THESE>"' : `"${projectExternalKey}"`;
|
|
2648
|
-
lines.push(` nexarch update-entity --key "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`);
|
|
2652
|
+
lines.push(` nexarch update-entity --key "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --entity-type application_function --subtype core_function --icon "<name from search-icons>" --name "..." --description "..."`);
|
|
2649
2653
|
lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --to ${fnOwnerTarget} --type part_of`);
|
|
2650
2654
|
lines.push(" Functions are structural only: model runtime/service dependencies between application or application_component boundaries, never from an application_function.");
|
|
2651
2655
|
}
|
|
2652
2656
|
lines.push(` ${step++}. Scan the READMEs for platforms/SaaS not auto-detected (Vercel, Neon, Stripe, etc.).`);
|
|
2653
2657
|
lines.push(` For each found: nexarch resolve-names --names "..." --json → nexarch update-entity → nexarch add-relationship`);
|
|
2654
|
-
lines.push(` ${step++}. Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register decision_record entities.`);
|
|
2658
|
+
lines.push(` ${step++}. Look for ADRs (docs/adr/, decisions/, ADR-*.md) and register the architectural ones as decision_record entities.`);
|
|
2659
|
+
lines.push(` Architectural means it crosses a boundary between applications, services or teams; changes an integration, data ownership, security or deployment;`);
|
|
2660
|
+
lines.push(` adopts or retires a technology others depend on; or is expensive to reverse. Skip design decisions inside one application.`);
|
|
2655
2661
|
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":"..."}}'`);
|
|
2656
2662
|
lines.push(` nexarch add-relationship --from "decision_record:..." --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`);
|
|
2657
2663
|
lines.push("");
|
package/dist/commands/login.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createServer } from "http";
|
|
2
2
|
import { createServer as createNetServer } from "net";
|
|
3
|
-
import { randomBytes } from "crypto";
|
|
3
|
+
import { createHash, randomBytes } from "crypto";
|
|
4
|
+
import * as readline from "node:readline/promises";
|
|
4
5
|
import { execFile } from "child_process";
|
|
5
6
|
import { saveCredentials } from "../lib/credentials.js";
|
|
6
7
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -136,6 +137,68 @@ function waitForCallback(port, expectedState) {
|
|
|
136
137
|
});
|
|
137
138
|
});
|
|
138
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether the browser that approves this login can reach the CLI at
|
|
142
|
+
* localhost. It can't over SSH (the browser is on the person's own machine,
|
|
143
|
+
* the CLI on the remote one) or on a Linux machine with no display; the
|
|
144
|
+
* loopback callback then lands on the wrong machine and the login hangs.
|
|
145
|
+
* `--no-browser` forces paste mode, `--browser` forces the loopback flow.
|
|
146
|
+
*/
|
|
147
|
+
export function shouldUsePasteLogin(args, env = process.env, platform = process.platform) {
|
|
148
|
+
if (args.includes("--browser"))
|
|
149
|
+
return false;
|
|
150
|
+
if (args.includes("--no-browser"))
|
|
151
|
+
return true;
|
|
152
|
+
if (env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY)
|
|
153
|
+
return true;
|
|
154
|
+
return platform === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY;
|
|
155
|
+
}
|
|
156
|
+
/** A random verifier the CLI keeps, and the challenge it sends: base64url(SHA-256(verifier)). */
|
|
157
|
+
export function createPasteChallenge() {
|
|
158
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
159
|
+
return { verifier, challenge: createHash("sha256").update(verifier).digest("base64url") };
|
|
160
|
+
}
|
|
161
|
+
/** Mistakes worth letting someone paste again rather than starting over. */
|
|
162
|
+
const RETRYABLE_EXCHANGE_ERRORS = new Set(["malformed", "bad_signature"]);
|
|
163
|
+
async function pasteLogin(qp, verifier) {
|
|
164
|
+
const authUrl = `${NEXARCH_URL}/auth/cli?${qp.toString()}`;
|
|
165
|
+
console.log("\nThis terminal can't receive the browser's reply directly (for example over SSH),");
|
|
166
|
+
console.log("so you'll paste a one-time code instead.\n");
|
|
167
|
+
console.log(" 1. Open this link in a browser on any machine:\n");
|
|
168
|
+
console.log(` ${authUrl}\n`);
|
|
169
|
+
console.log(" 2. Sign in and authorize.");
|
|
170
|
+
console.log(" 3. Copy the code the page shows and paste it here.\n");
|
|
171
|
+
if (!process.stdin.isTTY) {
|
|
172
|
+
throw new Error("Paste login needs an interactive terminal to paste the code into. Run this command directly in a terminal.");
|
|
173
|
+
}
|
|
174
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
175
|
+
try {
|
|
176
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
177
|
+
const code = (await rl.question("Code: ")).trim();
|
|
178
|
+
if (!code)
|
|
179
|
+
continue;
|
|
180
|
+
const response = await fetch(`${NEXARCH_URL}/api/auth/cli/exchange`, {
|
|
181
|
+
method: "POST",
|
|
182
|
+
headers: { "content-type": "application/json" },
|
|
183
|
+
body: JSON.stringify({ code, verifier }),
|
|
184
|
+
});
|
|
185
|
+
const body = (await response.json().catch(() => ({})));
|
|
186
|
+
if (response.ok && body.token && body.companyId) {
|
|
187
|
+
return { token: body.token, companyId: body.companyId, companyName: body.companyName, companyCode: body.companyCode };
|
|
188
|
+
}
|
|
189
|
+
const message = body.message ?? `Login failed (${response.status}).`;
|
|
190
|
+
if (body.error && RETRYABLE_EXCHANGE_ERRORS.has(body.error) && attempt < 3) {
|
|
191
|
+
console.log(`\n${message}\n`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
throw new Error(message);
|
|
195
|
+
}
|
|
196
|
+
throw new Error("No code was entered. Run the command again to get a new link.");
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
rl.close();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
139
202
|
function getArgValue(args, flag) {
|
|
140
203
|
const idx = args.indexOf(flag);
|
|
141
204
|
if (idx === -1)
|
|
@@ -148,21 +211,33 @@ function getArgValue(args, flag) {
|
|
|
148
211
|
export async function login(args) {
|
|
149
212
|
printLoginBanner();
|
|
150
213
|
const state = generateState();
|
|
151
|
-
const port = await findFreePort();
|
|
152
214
|
const requestedCompany = getArgValue(args, "--company");
|
|
153
215
|
const requestedProfile = getArgValue(args, "--profile");
|
|
154
216
|
const isCapabilityProfile = (value) => PROFILE_CHOICES.some((c) => c.value === value);
|
|
155
217
|
const profile = isCapabilityProfile(requestedProfile) ? requestedProfile : await promptCapabilityProfile();
|
|
156
|
-
const qp = new URLSearchParams({ port: String(port), state, profile });
|
|
157
|
-
if (requestedCompany)
|
|
158
|
-
qp.set("company", requestedCompany);
|
|
159
|
-
const authUrl = `${NEXARCH_URL}/auth/cli?${qp.toString()}`;
|
|
160
218
|
console.log(`\nCapability profile: ${profile}`);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
219
|
+
let result;
|
|
220
|
+
if (shouldUsePasteLogin(args)) {
|
|
221
|
+
const { verifier, challenge } = createPasteChallenge();
|
|
222
|
+
const qp = new URLSearchParams({ mode: "paste", challenge, state, profile });
|
|
223
|
+
if (requestedCompany)
|
|
224
|
+
qp.set("company", requestedCompany);
|
|
225
|
+
result = await pasteLogin(qp, verifier);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
const port = await findFreePort();
|
|
229
|
+
const qp = new URLSearchParams({ port: String(port), state, profile });
|
|
230
|
+
if (requestedCompany)
|
|
231
|
+
qp.set("company", requestedCompany);
|
|
232
|
+
const authUrl = `${NEXARCH_URL}/auth/cli?${qp.toString()}`;
|
|
233
|
+
console.log("Opening Nexarch in your browser…");
|
|
234
|
+
console.log(`\n ${authUrl}\n`);
|
|
235
|
+
console.log("If the browser did not open, copy the URL above and paste it in manually.");
|
|
236
|
+
console.log("If your browser is on a different machine (for example over SSH), stop this and run it again with --no-browser.\n");
|
|
237
|
+
openBrowser(authUrl);
|
|
238
|
+
result = await waitForCallback(port, state);
|
|
239
|
+
}
|
|
240
|
+
const { token, companyId, companyName, companyCode } = result;
|
|
166
241
|
const expiresAt = new Date();
|
|
167
242
|
expiresAt.setDate(expiresAt.getDate() + 90);
|
|
168
243
|
saveCredentials({
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
3
|
+
import { callMcpTool } from "../lib/mcp.js";
|
|
4
|
+
function parseOptionValue(args, option) {
|
|
5
|
+
const idx = args.indexOf(option);
|
|
6
|
+
if (idx === -1)
|
|
7
|
+
return null;
|
|
8
|
+
const value = args[idx + 1];
|
|
9
|
+
if (!value || value.startsWith("--"))
|
|
10
|
+
return null;
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The words to search for: everything that isn't an option or an option's
|
|
15
|
+
* value, so both `search-icons database` and `search-icons "event stream"` work.
|
|
16
|
+
*/
|
|
17
|
+
export function searchIconsQuery(args) {
|
|
18
|
+
const valued = new Set(["--limit"]);
|
|
19
|
+
const words = [];
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
if (valued.has(args[i])) {
|
|
22
|
+
i++;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (args[i].startsWith("--"))
|
|
26
|
+
continue;
|
|
27
|
+
words.push(args[i]);
|
|
28
|
+
}
|
|
29
|
+
return words.join(" ").trim();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Finds real icon names for `update-entity --icon`. The gateway silently drops
|
|
33
|
+
* an icon name that doesn't exist, so an agent guessing from memory ends up
|
|
34
|
+
* with no icon and no error; this searches the same validated set it checks.
|
|
35
|
+
*/
|
|
36
|
+
export async function searchIcons(args) {
|
|
37
|
+
const asJson = args.includes("--json");
|
|
38
|
+
const query = searchIconsQuery(args);
|
|
39
|
+
if (!query) {
|
|
40
|
+
console.error('error: say what the icon should show, e.g. nexarch search-icons "credit card"');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const limitRaw = parseOptionValue(args, "--limit");
|
|
44
|
+
const limit = limitRaw ? Number(limitRaw) : undefined;
|
|
45
|
+
if (limitRaw && (!Number.isInteger(limit) || limit < 1)) {
|
|
46
|
+
console.error("error: --limit must be a whole number of 1 or more");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
const creds = requireCredentials();
|
|
50
|
+
const raw = await callMcpTool("nexarch_search_icons", { query, ...(limit ? { limit } : {}) }, { companyId: creds.companyId });
|
|
51
|
+
const result = JSON.parse(raw.content?.[0]?.text ?? "{}");
|
|
52
|
+
if (asJson) {
|
|
53
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!result.matches?.length) {
|
|
57
|
+
console.log(`No icons match "${query}".`);
|
|
58
|
+
console.log("Icon names are literal objects and actions (database, file, box). Try one more concrete word; if that also finds nothing, leave the icon unset.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
for (const name of result.matches)
|
|
62
|
+
console.log(name);
|
|
63
|
+
console.log(`\n${result.matches.length} of ${result.totalMatches} match${result.totalMatches === 1 ? "" : "es"}. Use one with: nexarch update-entity --key "<key>" --icon "${result.matches[0]}"`);
|
|
64
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { updateEntity } from "./commands/update-entity.js";
|
|
|
13
13
|
import { addRelationship } from "./commands/add-relationship.js";
|
|
14
14
|
import { registerAlias } from "./commands/register-alias.js";
|
|
15
15
|
import { resolveNames } from "./commands/resolve-names.js";
|
|
16
|
+
import { searchIcons } from "./commands/search-icons.js";
|
|
16
17
|
import { feedback } from "./commands/feedback.js";
|
|
17
18
|
import { listEntities } from "./commands/list-entities.js";
|
|
18
19
|
import { listRelationships } from "./commands/list-relationships.js";
|
|
@@ -57,6 +58,7 @@ const commands = {
|
|
|
57
58
|
"add-relationship": addRelationship,
|
|
58
59
|
"register-alias": registerAlias,
|
|
59
60
|
"resolve-names": resolveNames,
|
|
61
|
+
"search-icons": searchIcons,
|
|
60
62
|
feedback,
|
|
61
63
|
"list-entities": listEntities,
|
|
62
64
|
"list-relationships": listRelationships,
|
|
@@ -105,7 +107,12 @@ nexarch — Your architecture workspace for AI delivery.
|
|
|
105
107
|
|
|
106
108
|
Usage:
|
|
107
109
|
nexarch login Authenticate in browser and store company-scoped credentials
|
|
108
|
-
|
|
110
|
+
Options: --company <id>
|
|
111
|
+
--no-browser paste a one-time code instead of
|
|
112
|
+
receiving the browser's reply
|
|
113
|
+
locally; automatic over SSH or on
|
|
114
|
+
Linux with no display
|
|
115
|
+
--browser always use the local browser reply
|
|
109
116
|
nexarch --version Print the version and the path this copy runs from
|
|
110
117
|
nexarch enroll Bootstrap a headless (browserless) agent with a one-time
|
|
111
118
|
enrollment code — no login required. Writes the resulting
|
|
@@ -133,6 +140,7 @@ Usage:
|
|
|
133
140
|
registering anything; answer 'n' to pick a different one.
|
|
134
141
|
Options: --company <id|code> target a workspace directly
|
|
135
142
|
--yes accept the stored workspace without asking
|
|
143
|
+
--no-browser log in by pasting a one-time code (see login)
|
|
136
144
|
nexarch mcp-config Print MCP server config block for manual setup
|
|
137
145
|
Client list is registry-managed (see 'nexarch mcp-config --client <code>')
|
|
138
146
|
nexarch mcp-proxy Run as stdio MCP proxy (used by MCP clients)
|
|
@@ -232,6 +240,12 @@ Usage:
|
|
|
232
240
|
results before calling add-relationship.
|
|
233
241
|
Options: --names <csv> (required, e.g. "vercel,neon")
|
|
234
242
|
--json
|
|
243
|
+
nexarch search-icons <words>
|
|
244
|
+
Find real icon names for update-entity --icon. An icon
|
|
245
|
+
name that doesn't exist is dropped without an error, so
|
|
246
|
+
look one up rather than guessing.
|
|
247
|
+
Options: --limit <n> (default 20, max 50)
|
|
248
|
+
--json
|
|
235
249
|
nexarch feedback
|
|
236
250
|
Report a Nexarch usability problem or tool error.
|
|
237
251
|
Options: --kind <usability|error> (required)
|
package/dist/lib/skills.js
CHANGED
|
@@ -240,7 +240,7 @@ evidence — never invent a compliance status that isn't stored.
|
|
|
240
240
|
`;
|
|
241
241
|
const DECISION_RECORDS_SKILL_BODY = `---
|
|
242
242
|
name: nexarch-decision-records
|
|
243
|
-
description: Record an architectural decision in Nexarch, or check what's already been decided about something. Use when a decision
|
|
243
|
+
description: Record an architectural decision in Nexarch, or check what's already been decided about something. Use when a decision crosses a boundary between applications, services or teams, changes an integration, data ownership, security or deployment, adopts a technology others depend on, or is expensive to reverse; when ADR/RFC documents are found in a repository; when asked what's already been decided about a topic; or when a new decision replaces an older one. Not for design decisions inside one application. Requires the Nexarch MCP tools (nexarch_*).
|
|
244
244
|
---
|
|
245
245
|
|
|
246
246
|
# Nexarch Decision Records
|
|
@@ -252,6 +252,23 @@ skill is for recording one as it happens — mining a whole repository's ADR
|
|
|
252
252
|
history systematically is a separate, human-triggered Decision Review
|
|
253
253
|
command from the application's page in the workspace.
|
|
254
254
|
|
|
255
|
+
## Architectural, not design
|
|
256
|
+
|
|
257
|
+
Record only architectural decisions. A decision is architectural when it does
|
|
258
|
+
at least one of these:
|
|
259
|
+
|
|
260
|
+
- crosses a boundary between applications, services or teams
|
|
261
|
+
- changes an integration, data ownership, security or authentication, or how
|
|
262
|
+
the system is deployed
|
|
263
|
+
- adopts, replaces or retires a technology that other systems or teams depend on
|
|
264
|
+
- is expensive to reverse once other work builds on it
|
|
265
|
+
|
|
266
|
+
Anything else is a design decision — screen layouts, interaction patterns,
|
|
267
|
+
component library choices, internal module structure, naming. It belongs in
|
|
268
|
+
the repository's design documentation, not in the graph. When unsure, ask:
|
|
269
|
+
if this decision changed, would another team or system have to change too? If
|
|
270
|
+
not, do not record it, even when it is written up as an ADR.
|
|
271
|
+
|
|
255
272
|
## Before recording
|
|
256
273
|
|
|
257
274
|
\`nexarch_resolve_reference\` / \`nexarch_list_entities\` for an existing
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nexarch",
|
|
3
|
-
"version": "0.13.
|
|
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
|
-
"test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts"
|
|
30
|
-
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@types/node": "^22",
|
|
33
|
-
"tsx": "^4",
|
|
34
|
-
"typescript": "^5"
|
|
35
|
-
},
|
|
36
|
-
"dependencies": {
|
|
37
|
-
"yaml": "^2.9.0"
|
|
38
|
-
}
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "nexarch",
|
|
3
|
+
"version": "0.13.5",
|
|
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
|
+
"test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts && tsx scripts/test-search-icons.ts && tsx scripts/test-paste-login.ts"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22",
|
|
33
|
+
"tsx": "^4",
|
|
34
|
+
"typescript": "^5"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"yaml": "^2.9.0"
|
|
38
|
+
}
|
|
39
|
+
}
|