nexarch 0.9.8 → 0.9.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.
@@ -458,8 +458,9 @@ function injectInitProjectReportingContract(path) {
458
458
  "Hard rule:",
459
459
  '- If `enrichment_completed=false`, you MUST include: `Enrichment pending: run update-entity/enrichment steps.`',
460
460
  '- If `enrichment_completed=false`, NEVER say: "fully registered", "fully done", or equivalent.',
461
+ '- For enrichment, use explicit per-entity `update-entity` runs (no bulk entity-update shortcut).',
461
462
  "",
462
- "You may set `registration_status=\"enriched\"` only after executing enrichment actions (e.g. update-entity for project/sub-packages, aliases, and any chosen gap wiring).",
463
+ "You may set `registration_status=\"enriched\"` only after executing enrichment actions (e.g. update-entity for project/sub-packages, aliases, and any chosen gap wiring), with evidence-based per-entity descriptions/subtypes.",
463
464
  "",
464
465
  ].join("\n");
465
466
  const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
@@ -1254,6 +1255,7 @@ export async function initAgent(args) {
1254
1255
  allowedRegistrationStatus: ["skeleton_only", "enriched"],
1255
1256
  forbiddenWhenEnrichmentPending: ["fully registered", "fully done"],
1256
1257
  mandatoryLineWhenEnrichmentPending: "Enrichment pending: run update-entity/enrichment steps.",
1258
+ enrichmentMode: "per_entity_updates_only",
1257
1259
  },
1258
1260
  }
1259
1261
  : {
@@ -1667,6 +1667,11 @@ STEP 1 — Read the project README / docs and build your understanding of:
1667
1667
 
1668
1668
  STEP 2 — Enrich the project entity. Run this command with the description you've written:
1669
1669
 
1670
+ ENRICHMENT QUALITY RULE:
1671
+ • Do enrichment as explicit per-entity updates.
1672
+ • Do NOT shortcut with bulk entity updates for semantic enrichment.
1673
+ • The goal is accurate, evidence-based descriptions/subtypes per entity, not just write throughput.
1674
+
1670
1675
  npx nexarch update-entity \\
1671
1676
  --key "${projectExternalKey}" \\
1672
1677
  --entity-type "${entityTypeOverride}"${entityTypeOverride === "application" ? " \\\n --subtype \"app_custom_built\" \\\n --icon \"<curated icon>\"" : ""} \\
@@ -22,124 +22,52 @@ function parseAttributesInput(jsonText, filePath) {
22
22
  if (!jsonText && !filePath)
23
23
  return null;
24
24
  if (jsonText && filePath) {
25
- throw new Error("Use only one of --attributes-json or --attributes-file");
25
+ console.error("error: use only one of --attributes-json or --attributes-file");
26
+ process.exit(1);
26
27
  }
27
28
  let raw = jsonText;
28
29
  if (filePath) {
29
30
  if (!existsSync(filePath)) {
30
- throw new Error(`--attributes-file not found: ${filePath}`);
31
+ console.error(`error: --attributes-file not found: ${filePath}`);
32
+ process.exit(1);
31
33
  }
32
34
  raw = readFileSync(filePath, "utf8");
33
35
  }
34
36
  try {
35
37
  const parsed = JSON.parse(raw ?? "{}");
36
38
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
37
- throw new Error("Attributes input must be a JSON object");
39
+ console.error("error: attributes input must be a JSON object");
40
+ process.exit(1);
38
41
  }
39
42
  return parsed;
40
43
  }
41
44
  catch (error) {
42
- throw new Error(`Invalid attributes JSON (${error instanceof Error ? error.message : "parse failed"})`);
45
+ console.error(`error: invalid attributes JSON (${error instanceof Error ? error.message : "parse failed"})`);
46
+ process.exit(1);
43
47
  }
44
48
  }
45
- function parseEntityBatch(args) {
46
- const entitiesFile = parseOptionValue(args, "--entities-file");
47
- const entitiesJson = parseOptionValue(args, "--entities-json");
48
- if (!entitiesFile && !entitiesJson)
49
- return null;
50
- if (entitiesFile && entitiesJson)
51
- throw new Error("Use only one of --entities-file or --entities-json");
52
- let raw = entitiesJson;
53
- if (entitiesFile) {
54
- if (!existsSync(entitiesFile))
55
- throw new Error(`--entities-file not found: ${entitiesFile}`);
56
- raw = readFileSync(entitiesFile, "utf8");
57
- }
58
- let parsed;
59
- try {
60
- parsed = JSON.parse(raw ?? "[]");
61
- }
62
- catch {
63
- throw new Error(entitiesFile ? "--entities-file must contain valid JSON array" : "--entities-json must be valid JSON array");
64
- }
65
- if (!Array.isArray(parsed) || parsed.length === 0) {
66
- throw new Error("Entities input must be a non-empty JSON array");
67
- }
68
- return parsed.map((item, index) => {
69
- const v = (item ?? {});
70
- const externalKey = String(v.externalKey ?? v.key ?? "").trim();
71
- if (!externalKey)
72
- throw new Error(`Entity #${index + 1} is missing externalKey/key`);
73
- const entityTypeCode = String(v.entityTypeCode ?? v.entity_type_code ?? "application").trim() || "application";
74
- const entitySubtypeCode = String(v.entitySubtypeCode ?? v.entity_subtype_code ?? "").trim();
75
- const out = {
76
- externalKey,
77
- entityTypeCode,
78
- confidence: 1,
79
- };
80
- if (typeof v.name === "string" && v.name.trim())
81
- out.name = v.name.trim();
82
- if (typeof v.description === "string" && v.description.trim())
83
- out.description = v.description.trim();
84
- if (entitySubtypeCode)
85
- out.entitySubtypeCode = entitySubtypeCode;
86
- const attrs = {
87
- ...(v.attributes && typeof v.attributes === "object" && !Array.isArray(v.attributes) ? v.attributes : {}),
88
- };
89
- if (typeof v.icon === "string" && v.icon.trim()) {
90
- attrs.application_icon = { provider: "lucide", name: v.icon.trim() };
91
- }
92
- if (Object.keys(attrs).length > 0)
93
- out.attributes = attrs;
94
- if (!out.name && !out.description && !out.attributes) {
95
- throw new Error(`Entity #${index + 1} must include at least one of name, description, icon, or attributes`);
96
- }
97
- return out;
98
- });
99
- }
100
49
  export async function updateEntity(args) {
101
50
  const asJson = parseFlag(args, "--json");
102
- const batchEntities = parseEntityBatch(args);
103
- let entities;
104
- if (batchEntities) {
105
- entities = batchEntities;
51
+ if (parseOptionValue(args, "--entities-json") || parseOptionValue(args, "--entities-file")) {
52
+ console.error("error: batch entity updates were removed. Use explicit per-entity update-entity commands for enrichment quality.");
53
+ process.exit(1);
106
54
  }
107
- else {
108
- const externalKey = parseOptionValue(args, "--key");
109
- const name = parseOptionValue(args, "--name");
110
- const description = parseOptionValue(args, "--description");
111
- const entityTypeCode = parseOptionValue(args, "--entity-type") ?? "application";
112
- const entitySubtypeCode = parseOptionValue(args, "--subtype");
113
- const iconName = parseOptionValue(args, "--icon");
114
- const attributesJson = parseOptionValue(args, "--attributes-json");
115
- const attributesFile = parseOptionValue(args, "--attributes-file");
116
- if (!externalKey)
117
- throw new Error("--key <externalKey> is required (or use --entities-file/--entities-json)");
118
- const explicitAttributes = parseAttributesInput(attributesJson, attributesFile);
119
- if (!name && !description && !iconName && !explicitAttributes) {
120
- throw new Error("Provide at least one of --name, --description, --icon, --attributes-json, or --attributes-file");
121
- }
122
- const entity = {
123
- externalKey,
124
- entityTypeCode,
125
- confidence: 1,
126
- };
127
- if (name)
128
- entity.name = name;
129
- if (description)
130
- entity.description = description;
131
- if (entitySubtypeCode)
132
- entity.entitySubtypeCode = entitySubtypeCode;
133
- const attributes = {
134
- ...(explicitAttributes ?? {}),
135
- };
136
- if (iconName) {
137
- attributes.application_icon = { provider: "lucide", name: iconName };
138
- }
139
- if (Object.keys(attributes).length > 0) {
140
- entity.attributes = attributes;
141
- }
142
- entities = [entity];
55
+ const externalKey = parseOptionValue(args, "--key");
56
+ const name = parseOptionValue(args, "--name");
57
+ const description = parseOptionValue(args, "--description");
58
+ const entityTypeCode = parseOptionValue(args, "--entity-type") ?? "application";
59
+ const entitySubtypeCode = parseOptionValue(args, "--subtype");
60
+ const iconName = parseOptionValue(args, "--icon");
61
+ const attributesJson = parseOptionValue(args, "--attributes-json");
62
+ const attributesFile = parseOptionValue(args, "--attributes-file");
63
+ if (!externalKey) {
64
+ console.error("error: --key <externalKey> is required");
65
+ process.exit(1);
66
+ }
67
+ const explicitAttributes = parseAttributesInput(attributesJson, attributesFile);
68
+ if (!name && !description && !iconName && !explicitAttributes) {
69
+ console.error("error: provide at least one of --name, --description, --icon, --attributes-json, or --attributes-file");
70
+ process.exit(1);
143
71
  }
144
72
  const creds = requireCredentials();
145
73
  const mcpOpts = { companyId: creds.companyId };
@@ -150,7 +78,7 @@ export async function updateEntity(args) {
150
78
  const agentContext = {
151
79
  agentId: "nexarch-cli:update-entity",
152
80
  agentRunId: `update-entity-${Date.now()}`,
153
- repoRef: String(entities[0]?.externalKey ?? "batch"),
81
+ repoRef: externalKey,
154
82
  observedAt: nowIso,
155
83
  source: "nexarch-cli",
156
84
  model: "n/a",
@@ -159,7 +87,31 @@ export async function updateEntity(args) {
159
87
  const policyContext = policyBundleHash
160
88
  ? { policyBundleHash, alignmentSummary: { score: 1, violations: [], waivers: [] } }
161
89
  : undefined;
162
- const raw = await callMcpTool("nexarch_upsert_entities", { entities, agentContext, policyContext }, mcpOpts);
90
+ const entity = {
91
+ externalKey,
92
+ entityTypeCode,
93
+ confidence: 1,
94
+ };
95
+ if (name)
96
+ entity.name = name;
97
+ if (description)
98
+ entity.description = description;
99
+ if (entitySubtypeCode)
100
+ entity.entitySubtypeCode = entitySubtypeCode;
101
+ const attributes = {
102
+ ...(explicitAttributes ?? {}),
103
+ };
104
+ // Convenience sugar; still generic because user can fully control payload via --attributes-json/file.
105
+ if (iconName) {
106
+ attributes.application_icon = {
107
+ provider: "lucide",
108
+ name: iconName,
109
+ };
110
+ }
111
+ if (Object.keys(attributes).length > 0) {
112
+ entity.attributes = attributes;
113
+ }
114
+ const raw = await callMcpTool("nexarch_upsert_entities", { entities: [entity], agentContext, policyContext }, mcpOpts);
163
115
  const result = parseToolText(raw);
164
116
  if (asJson) {
165
117
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
@@ -170,17 +122,14 @@ export async function updateEntity(args) {
170
122
  const succeeded = result.summary?.succeeded ?? 0;
171
123
  const failed = result.summary?.failed ?? 0;
172
124
  if (failed > 0) {
173
- console.error(`Failed to update entities (${failed}/${entities.length}).`);
125
+ console.error(`Failed to update entity: ${externalKey}`);
174
126
  for (const err of result.errors ?? []) {
175
127
  console.error(` ${err.externalKey}: ${err.error} — ${err.message}`);
176
128
  }
177
129
  process.exitCode = 1;
178
130
  return;
179
131
  }
180
- if (entities.length === 1) {
181
- console.log(`Updated ${succeeded} entity: ${String(entities[0].externalKey)}`);
182
- }
183
- else {
184
- console.log(`Updated ${succeeded}/${entities.length} entities.`);
132
+ if (!asJson) {
133
+ console.log(`Updated ${succeeded} entity: ${externalKey}`);
185
134
  }
186
135
  }
package/dist/index.js CHANGED
@@ -99,18 +99,18 @@ Usage:
99
99
  --dry-run preview without writing
100
100
  --json
101
101
  nexarch update-entity
102
- Update existing graph entities (single or batch).
103
- Single options: --key <externalKey>
104
- --name <name>
105
- --description <text>
106
- --entity-type <code> (default: application)
107
- --subtype <code>
108
- --icon <lucide-name> (sets attributes.application_icon)
109
- --attributes-json '<json object>'
110
- --attributes-file <path.json>
111
- Batch options: --entities-json '<json array>'
112
- --entities-file <path.json>
113
- --json
102
+ Update the name and/or description of an existing graph entity.
103
+ Use this after init-project to enrich the entity with meaningful
104
+ content from the project README or docs.
105
+ Options: --key <externalKey> (required)
106
+ --name <name>
107
+ --description <text>
108
+ --entity-type <code> (default: application)
109
+ --subtype <code>
110
+ --icon <lucide-name> (convenience; sets attributes.application_icon)
111
+ --attributes-json '<json object>'
112
+ --attributes-file <path.json>
113
+ --json
114
114
  nexarch add-relationship
115
115
  Add relationships between existing graph entities (single or batch).
116
116
  Single options: --from <externalKey>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",