nexarch 0.13.2 → 0.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/init-agent.js +3 -2
- package/dist/commands/init-project.js +69 -15
- package/dist/commands/search-icons.js +64 -0
- package/dist/index.js +8 -0
- package/dist/lib/skills.js +18 -1
- package/package.json +2 -2
|
@@ -613,8 +613,9 @@ function injectInitProjectReportingContract(path) {
|
|
|
613
613
|
"**One exception: a possible duplicate.** If init-project returns `status: \"review_required\"`",
|
|
614
614
|
"or prints `STOP — POSSIBLE DUPLICATE`, an application already in the workspace looks like",
|
|
615
615
|
"one it would register, and it wrote nothing. Tell the human which one you think matches and",
|
|
616
|
-
"why, wait for their answer, then
|
|
617
|
-
"(`decision.commands` in the output).
|
|
616
|
+
"why, wait for their answer, then run the command for their choice yourself",
|
|
617
|
+
"(`decision.commands` in the output) and continue. If it is the same system, enrich the",
|
|
618
|
+
"existing application — nothing needs merging.",
|
|
618
619
|
"",
|
|
619
620
|
"Stop early only when a step genuinely cannot run — missing cloud credentials, a root",
|
|
620
621
|
"module that has not been initialised, or an environment you should not touch without",
|
|
@@ -1315,7 +1315,8 @@ export function enrichmentStatusFor(params) {
|
|
|
1315
1315
|
}
|
|
1316
1316
|
export const REVIEW_REQUIRED_NOTE = "STOP. Nothing was written. Existing applications in this workspace resemble what this scan would register"
|
|
1317
1317
|
+ " (see similarApplications). Tell the human, say which one you think is the same system and why, and wait for"
|
|
1318
|
-
+ " their answer. Then
|
|
1318
|
+
+ " their answer. Then run the command in `decision.commands` for their choice yourself and carry on. If it is the"
|
|
1319
|
+
+ " same system, enrich the existing application: nothing needs merging.";
|
|
1319
1320
|
/**
|
|
1320
1321
|
* The possible-duplicate warning for the text directive, first thing in it.
|
|
1321
1322
|
*
|
|
@@ -1338,6 +1339,7 @@ export function possibleDuplicateDirective(matches, isMonorepo) {
|
|
|
1338
1339
|
? " same system npx nexarch@latest init-project --dir . --map-deployable <deployable>=<entityRef>"
|
|
1339
1340
|
: " same application npx nexarch@latest init-project --dir . --application-ref <entityRef>",
|
|
1340
1341
|
" all different npx nexarch@latest init-project --dir . --create-application",
|
|
1342
|
+
"Run the command for their answer yourself. If it is the same system, enrich the existing application — nothing needs merging.",
|
|
1341
1343
|
];
|
|
1342
1344
|
}
|
|
1343
1345
|
/**
|
|
@@ -1684,6 +1686,11 @@ export async function initProject(args) {
|
|
|
1684
1686
|
let stopForReview = null;
|
|
1685
1687
|
// Names of applications already in the workspace, by ref, for mapped deployables.
|
|
1686
1688
|
const existingApplicationNames = new Map();
|
|
1689
|
+
// Refs of those that are still proposals: a mapped deployable's leftover
|
|
1690
|
+
// duplicate from an earlier scan is folded away only if nobody reviewed it.
|
|
1691
|
+
const proposedApplicationRefs = new Set();
|
|
1692
|
+
// What happened to each leftover duplicate, for the output.
|
|
1693
|
+
const foldedDuplicates = [];
|
|
1687
1694
|
if (projectConstruct && isMonorepo && (applicationRefOverride || forceCreateApplication) && !asJson) {
|
|
1688
1695
|
console.log("\nNote: monorepo detected — no root application is created (ADR 8a); --application-ref/--create-application apply only to single-package repositories.");
|
|
1689
1696
|
}
|
|
@@ -1780,8 +1787,11 @@ export async function initProject(args) {
|
|
|
1780
1787
|
const listed = parseToolText(appsRaw).entities ?? [];
|
|
1781
1788
|
for (const entity of listed) {
|
|
1782
1789
|
const ref = entity.entityRef ?? entity.externalKey;
|
|
1783
|
-
if (ref)
|
|
1784
|
-
|
|
1790
|
+
if (!ref)
|
|
1791
|
+
continue;
|
|
1792
|
+
existingApplicationNames.set(ref, entity.name);
|
|
1793
|
+
if (String(entity.attributes?.workflow_state ?? "").toLowerCase() === "proposed")
|
|
1794
|
+
proposedApplicationRefs.add(ref);
|
|
1785
1795
|
}
|
|
1786
1796
|
// A mapped deployable writes no stub of its own, so a mapping to an
|
|
1787
1797
|
// application that is not there would leave its relationships pointing at
|
|
@@ -2172,6 +2182,42 @@ export async function initProject(args) {
|
|
|
2172
2182
|
// ability to resolve via tier 2; the scan itself already succeeded.
|
|
2173
2183
|
}
|
|
2174
2184
|
}
|
|
2185
|
+
// "There should be nothing to merge" (Paul, 16 September). When the human has
|
|
2186
|
+
// confirmed a deployable is an existing application, an earlier scan may
|
|
2187
|
+
// already have registered that deployable as a proposal of its own -- the
|
|
2188
|
+
// 0.13.1 behaviour, or any run before the duplicate check stopped the scan.
|
|
2189
|
+
// Fold it into the existing application here, so the agent carries on
|
|
2190
|
+
// enriching the real one and nobody has to press Merge. Only unreviewed
|
|
2191
|
+
// proposals: the gateway refuses anything a human has activated.
|
|
2192
|
+
for (const sp of subPackages) {
|
|
2193
|
+
if (sp.mappedBy !== "flag" || !sp.mappedFrom || !proposedApplicationRefs.has(sp.mappedFrom))
|
|
2194
|
+
continue;
|
|
2195
|
+
try {
|
|
2196
|
+
const raw = await callMcpTool("nexarch_merge_proposed_application", {
|
|
2197
|
+
proposedApplicationRef: sp.mappedFrom,
|
|
2198
|
+
intoApplicationRef: sp.externalKey,
|
|
2199
|
+
reason: `The human confirmed the ${sp.relativePath} deployable is ${existingApplicationNames.get(sp.externalKey) ?? sp.externalKey}.`,
|
|
2200
|
+
companyId: creds.companyId,
|
|
2201
|
+
}, { companyId: creds.companyId });
|
|
2202
|
+
const result = parseToolText(raw);
|
|
2203
|
+
foldedDuplicates.push({
|
|
2204
|
+
proposedApplicationRef: sp.mappedFrom,
|
|
2205
|
+
intoApplicationRef: sp.externalKey,
|
|
2206
|
+
folded: true,
|
|
2207
|
+
detail: `folded in; ${result.relationshipsRepointed ?? 0} relationship(s) moved to ${sp.externalKey}`,
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
catch (error) {
|
|
2211
|
+
// An older gateway without the tool, or a credential without the scope.
|
|
2212
|
+
// Say so plainly: the duplicate is still there and the human should know.
|
|
2213
|
+
foldedDuplicates.push({
|
|
2214
|
+
proposedApplicationRef: sp.mappedFrom,
|
|
2215
|
+
intoApplicationRef: sp.externalKey,
|
|
2216
|
+
folded: false,
|
|
2217
|
+
detail: error instanceof Error ? error.message : String(error),
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2175
2221
|
// Remember an explicit --map-deployable, so the next scan does not ask again.
|
|
2176
2222
|
// A merge registers the same alias; this is the same decision made up front.
|
|
2177
2223
|
for (const sp of subPackages) {
|
|
@@ -2249,8 +2295,7 @@ export async function initProject(args) {
|
|
|
2249
2295
|
if (similarApplications.length > 0 && isMonorepo) {
|
|
2250
2296
|
// --application-ref does not apply to a monorepo (the CLI says so above),
|
|
2251
2297
|
// so the single-repo remedy would hand the agent a command that does
|
|
2252
|
-
// nothing. The resolution
|
|
2253
|
-
// application page.
|
|
2298
|
+
// nothing. The resolution is --map-deployable, which the agent runs.
|
|
2254
2299
|
pendingSteps.push({
|
|
2255
2300
|
step: stepNum++,
|
|
2256
2301
|
action: "review_similar_applications",
|
|
@@ -2258,8 +2303,9 @@ export async function initProject(args) {
|
|
|
2258
2303
|
+ ` workspace resemble${similarApplications.length === 1 ? "s" : ""} this repository: ${matchList}.`
|
|
2259
2304
|
+ " One of the deployables just registered may be the same system — for example a web app registered"
|
|
2260
2305
|
+ " earlier from its public website. Tell the human which deployable you think matches and why, before"
|
|
2261
|
-
+ " enriching anything.
|
|
2262
|
-
+ "
|
|
2306
|
+
+ " enriching anything. If the human confirms one is the same system, re-run with"
|
|
2307
|
+
+ " --map-deployable <deployable>=<entityRef> yourself and enrich the existing application: the scan"
|
|
2308
|
+
+ " folds this proposal into it, so there is nothing for anyone to merge.",
|
|
2263
2309
|
notes: [
|
|
2264
2310
|
"Do not report the registration as complete without mentioning these.",
|
|
2265
2311
|
"The match is on the repository, not a specific deployable: say which one you believe it is, and why.",
|
|
@@ -2297,7 +2343,7 @@ export async function initProject(args) {
|
|
|
2297
2343
|
pendingSteps.push({
|
|
2298
2344
|
step: stepNum++,
|
|
2299
2345
|
action: "enrich_entity",
|
|
2300
|
-
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.`,
|
|
2301
2347
|
command: `nexarch update-entity --key "${projectExternalKey}" --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? ' --subtype "<subtype>" --icon "<lucide-icon>"' : ""} --name "..." --description "..."`,
|
|
2302
2348
|
...(entityTypeOverride === "application"
|
|
2303
2349
|
? { notes: [APPLICATION_SUBTYPE_HINT, "Choose app_custom_built as the default if none of the others clearly apply."] }
|
|
@@ -2338,9 +2384,11 @@ export async function initProject(args) {
|
|
|
2338
2384
|
instruction: `Review the codebase to identify discrete application functions (what the application does). ` +
|
|
2339
2385
|
`Examine named modules, route layout, service boundaries, and any architecture documentation. ` +
|
|
2340
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). ` +
|
|
2341
|
-
`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.`,
|
|
2342
2389
|
commandTemplates: {
|
|
2343
|
-
|
|
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 "..."`,
|
|
2344
2392
|
addRelationship: `nexarch add-relationship --from "application_function:${projectSlug}_<function_slug>" --to ${projectConstruct && isMonorepo ? '"<owning application key from classifyPackages>"' : `"${projectExternalKey}"`} --type part_of`,
|
|
2345
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.",
|
|
2346
2394
|
},
|
|
@@ -2359,7 +2407,7 @@ export async function initProject(args) {
|
|
|
2359
2407
|
pendingSteps.push({
|
|
2360
2408
|
step: stepNum++,
|
|
2361
2409
|
action: "register_decision_records",
|
|
2362
|
-
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.`,
|
|
2363
2411
|
commandTemplates: {
|
|
2364
2412
|
updateEntity: `nexarch update-entity --key "decision_record:${projectSlug}_<adr_slug>" --entity-type decision_record --subtype decision_architecture --name "..." --attributes-json '{"decision":{"summary":"...","detail":"..."}}'`,
|
|
2365
2413
|
addRelationship: `nexarch add-relationship --from "decision_record:${projectSlug}_<adr_slug>" --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`,
|
|
@@ -2473,7 +2521,7 @@ export async function initProject(args) {
|
|
|
2473
2521
|
unresolvedNames: unresolvedItems.map((r) => r.input),
|
|
2474
2522
|
iconHints: {
|
|
2475
2523
|
provider: "lucide",
|
|
2476
|
-
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.",
|
|
2477
2525
|
},
|
|
2478
2526
|
};
|
|
2479
2527
|
}
|
|
@@ -2532,7 +2580,8 @@ export async function initProject(args) {
|
|
|
2532
2580
|
// "Confirm type, then update" is right for a new skeleton and wrong
|
|
2533
2581
|
// here: this is an application a human already registered and named.
|
|
2534
2582
|
lines.push(` MAPPED : this deployable is the existing application ${sp.externalKey}${sp.mappedBy === "alias" ? " (from an earlier merge)" : ""}.`);
|
|
2535
|
-
lines.push("
|
|
2583
|
+
lines.push(" It is this deployable's application: enrich it — name, description, type and");
|
|
2584
|
+
lines.push(" functions — from the code, as you would a new one.");
|
|
2536
2585
|
}
|
|
2537
2586
|
lines.push(` inferred : ${sp.entityType} / ${sp.subtype} (confidence ${conf.toFixed(2)})`);
|
|
2538
2587
|
const unresolvedDeps = sp.depSpecs.map((d) => d.name).filter((d) => !resolvedByInput.has(d));
|
|
@@ -2596,15 +2645,19 @@ export async function initProject(args) {
|
|
|
2596
2645
|
lines.push(` Use subtype core_function (primary business function), supporting_function (auxiliary/enablement),`);
|
|
2597
2646
|
lines.push(` integration_function (external connectivity), or data_function (data processing).`);
|
|
2598
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>"`);
|
|
2599
2650
|
lines.push(` For each one found:`);
|
|
2600
2651
|
const fnOwnerTarget = projectConstruct && isMonorepo ? '"<owning application key from CLASSIFY_THESE>"' : `"${projectExternalKey}"`;
|
|
2601
|
-
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 "..."`);
|
|
2602
2653
|
lines.push(` nexarch add-relationship --from "application_function:${projectExternalKey.split(":")[1] ?? "project"}_<function_slug>" --to ${fnOwnerTarget} --type part_of`);
|
|
2603
2654
|
lines.push(" Functions are structural only: model runtime/service dependencies between application or application_component boundaries, never from an application_function.");
|
|
2604
2655
|
}
|
|
2605
2656
|
lines.push(` ${step++}. Scan the READMEs for platforms/SaaS not auto-detected (Vercel, Neon, Stripe, etc.).`);
|
|
2606
2657
|
lines.push(` For each found: nexarch resolve-names --names "..." --json → nexarch update-entity → nexarch add-relationship`);
|
|
2607
|
-
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.`);
|
|
2608
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":"..."}}'`);
|
|
2609
2662
|
lines.push(` nexarch add-relationship --from "decision_record:..." --to ${projectConstruct && isMonorepo ? '"<the relevant application key>"' : `"${projectExternalKey}"`} --type decides`);
|
|
2610
2663
|
lines.push("");
|
|
@@ -2666,6 +2719,7 @@ export async function initProject(args) {
|
|
|
2666
2719
|
resolved: resolvedItems.length,
|
|
2667
2720
|
unresolved: unresolvedItems.length,
|
|
2668
2721
|
similarApplications,
|
|
2722
|
+
...(foldedDuplicates.length > 0 ? { foldedDuplicates } : {}),
|
|
2669
2723
|
entityErrors: entitiesResult.errors ?? [],
|
|
2670
2724
|
relationshipErrors: relsResult?.errors ?? [],
|
|
2671
2725
|
enrichmentRequired,
|
|
@@ -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,
|
|
@@ -232,6 +234,12 @@ Usage:
|
|
|
232
234
|
results before calling add-relationship.
|
|
233
235
|
Options: --names <csv> (required, e.g. "vercel,neon")
|
|
234
236
|
--json
|
|
237
|
+
nexarch search-icons <words>
|
|
238
|
+
Find real icon names for update-entity --icon. An icon
|
|
239
|
+
name that doesn't exist is dropped without an error, so
|
|
240
|
+
look one up rather than guessing.
|
|
241
|
+
Options: --limit <n> (default 20, max 50)
|
|
242
|
+
--json
|
|
235
243
|
nexarch feedback
|
|
236
244
|
Report a Nexarch usability problem or tool error.
|
|
237
245
|
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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexarch",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "Your architecture workspace for AI delivery.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nexarch",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"prepublishOnly": "tsc",
|
|
27
27
|
"dev": "tsx src/index.ts",
|
|
28
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"
|
|
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"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22",
|