nexarch 0.13.3 → 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.
@@ -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
- updateEntity: `nexarch update-entity --key "application_function:${projectSlug}_<function_slug>" --entity-type application_function --subtype core_function --name "..." --description "..."`,
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 each as a decision_record entity.`,
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 any Lucide icon name (kebab-case); omit when confidence is low.",
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("");
@@ -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)
@@ -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 is being made (a technology, pattern, or approach chosen over alternatives), 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. Requires the Nexarch MCP tools (nexarch_*).
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",
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",