nexarch 0.9.3 → 0.9.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.
@@ -59,12 +59,12 @@ async function promptForValue(promptText, required = false) {
59
59
  rl.close();
60
60
  }
61
61
  }
62
- async function confirmInstructionWrite() {
62
+ async function confirmInstructionWrite(promptText = "Allow nexarch init-agent to write/update AGENTS.md/CLAUDE.md registration instructions? [y/N]: ") {
63
63
  if (!process.stdin.isTTY || !process.stdout.isTTY)
64
64
  return false;
65
65
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
66
66
  try {
67
- const answer = (await rl.question("Allow nexarch init-agent to write/update AGENTS.md/CLAUDE.md registration instructions? [y/N]: ")).trim().toLowerCase();
67
+ const answer = (await rl.question(promptText)).trim().toLowerCase();
68
68
  return answer === "y" || answer === "yes";
69
69
  }
70
70
  finally {
@@ -1084,20 +1084,25 @@ export async function initAgent(args) {
1084
1084
  catch {
1085
1085
  // non-fatal
1086
1086
  }
1087
+ let existingInstructionTargets = injectAgentConfigs(registry);
1088
+ if (existingInstructionTargets.length === 0) {
1089
+ existingInstructionTargets = injectGenericAgentConfig(registry);
1090
+ }
1091
+ const alreadyConfigured = existingInstructionTargets.length > 0 && existingInstructionTargets.every((r) => r.status === "already_present");
1087
1092
  if (denyInstructionWriteFlag) {
1088
1093
  instructionsWriteAllowed = false;
1089
1094
  }
1090
1095
  else if (allowInstructionWriteFlag) {
1091
1096
  instructionsWriteAllowed = true;
1092
1097
  }
1098
+ else if (alreadyConfigured) {
1099
+ instructionsWriteAllowed = false;
1100
+ }
1093
1101
  else if (!asJson) {
1094
1102
  instructionsWriteAllowed = await confirmInstructionWrite();
1095
1103
  }
1096
1104
  if (instructionsWriteAllowed) {
1097
- agentConfigResults = injectAgentConfigs(registry);
1098
- if (agentConfigResults.length === 0) {
1099
- agentConfigResults = injectGenericAgentConfig(registry);
1100
- }
1105
+ agentConfigResults = existingInstructionTargets;
1101
1106
  if (agentConfigResults.length > 0) {
1102
1107
  trustAttestationAttempted = true;
1103
1108
  trustAttestation = await requestTrustAttestation(agentId);
@@ -1116,6 +1121,9 @@ export async function initAgent(args) {
1116
1121
  }
1117
1122
  }
1118
1123
  }
1124
+ else if (alreadyConfigured) {
1125
+ agentConfigResults = existingInstructionTargets;
1126
+ }
1119
1127
  }
1120
1128
  checks.push({
1121
1129
  name: "agent.registration",
@@ -1132,29 +1140,34 @@ export async function initAgent(args) {
1132
1140
  ? `awaiting identity input (${identityCapture.missingRequired.join(", ")})`
1133
1141
  : identityCapture.detail,
1134
1142
  });
1143
+ const instructionsAlreadyConfigured = agentConfigResults.length > 0 && agentConfigResults.every((r) => r.status === "already_present");
1135
1144
  checks.push({
1136
1145
  name: "agent.instructions.injection",
1137
1146
  ok: !registration.ok || !instructionsWriteAllowed || agentConfigResults.length > 0,
1138
1147
  detail: !registration.ok
1139
1148
  ? "skipped (registration failed)"
1140
- : !instructionsWriteAllowed
1141
- ? "skipped (consent not granted)"
1142
- : agentConfigResults.length > 0
1143
- ? `updated ${agentConfigResults.length} instruction target file(s)`
1144
- : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
1149
+ : instructionsAlreadyConfigured
1150
+ ? "already configured (no write needed)"
1151
+ : !instructionsWriteAllowed
1152
+ ? "skipped (consent not granted)"
1153
+ : agentConfigResults.length > 0
1154
+ ? `updated ${agentConfigResults.length} instruction target file(s)`
1155
+ : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
1145
1156
  });
1146
1157
  checks.push({
1147
1158
  name: "agent.trust.attestation",
1148
1159
  ok: !registration.ok || !instructionsWriteAllowed || !trustAttestationAttempted || Boolean(trustAttestation?.ok),
1149
1160
  detail: !registration.ok
1150
1161
  ? "skipped (registration failed)"
1151
- : !instructionsWriteAllowed
1152
- ? "skipped (consent not granted)"
1153
- : !trustAttestationAttempted
1154
- ? "skipped (no instruction target written)"
1155
- : trustAttestation?.ok
1156
- ? "minted and injected into instruction file(s)"
1157
- : `unavailable (${trustAttestation?.reason ?? "unknown"})`,
1162
+ : instructionsAlreadyConfigured && !instructionsWriteAllowed
1163
+ ? "skipped (already configured)"
1164
+ : !instructionsWriteAllowed
1165
+ ? "skipped (consent not granted)"
1166
+ : !trustAttestationAttempted
1167
+ ? "skipped (no instruction target written)"
1168
+ : trustAttestation?.ok
1169
+ ? "minted and injected into instruction file(s)"
1170
+ : `unavailable (${trustAttestation?.reason ?? "unknown"})`,
1158
1171
  });
1159
1172
  checks.push({
1160
1173
  name: "technology.components",
@@ -28,6 +28,14 @@ function formatMs(ms) {
28
28
  return `${ms}ms`;
29
29
  return `${(ms / 1000).toFixed(2)}s`;
30
30
  }
31
+ function chunkArray(items, size) {
32
+ if (size <= 0)
33
+ return [items];
34
+ const chunks = [];
35
+ for (let i = 0; i < items.length; i += size)
36
+ chunks.push(items.slice(i, i + size));
37
+ return chunks;
38
+ }
31
39
  function slugify(name) {
32
40
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
33
41
  }
@@ -973,6 +981,7 @@ export async function initProject(args) {
973
981
  const forceCreateApplication = parseFlag(args, "--create-application");
974
982
  const autoMapApplication = parseFlag(args, "--auto-map-application");
975
983
  const nonInteractive = parseFlag(args, "--non-interactive");
984
+ const upsertBatchSize = Number(parseOptionValue(args, "--batch-size") ?? "10") || 10;
976
985
  const creds = requireCredentials();
977
986
  const mcpOpts = { companyId: creds.companyId };
978
987
  if (!asJson)
@@ -1312,17 +1321,44 @@ export async function initProject(args) {
1312
1321
  }
1313
1322
  if (!asJson)
1314
1323
  console.log(`\nWriting to graph…`);
1315
- // Upsert entities
1316
- logProgress("upsert.entities.start", `count=${entities.length}`);
1317
- const entitiesRaw = await callMcpTool("nexarch_upsert_entities", { entities, agentContext, policyContext }, mcpOpts);
1318
- const entitiesResult = parseToolText(entitiesRaw);
1324
+ // Upsert entities (chunked for progressive feedback)
1325
+ const entityChunks = chunkArray(entities, Math.max(1, upsertBatchSize));
1326
+ const entitiesResult = {
1327
+ summary: { requested: 0, succeeded: 0, failed: 0 },
1328
+ errors: [],
1329
+ };
1330
+ logProgress("upsert.entities.start", `count=${entities.length}, batches=${entityChunks.length}, batchSize=${Math.max(1, upsertBatchSize)}`);
1331
+ for (let i = 0; i < entityChunks.length; i += 1) {
1332
+ const chunk = entityChunks[i];
1333
+ logProgress("upsert.entities.batch.start", `${i + 1}/${entityChunks.length} size=${chunk.length}`);
1334
+ const entitiesRaw = await callMcpTool("nexarch_upsert_entities", { entities: chunk, agentContext, policyContext }, mcpOpts);
1335
+ const chunkResult = parseToolText(entitiesRaw);
1336
+ entitiesResult.summary.requested = Number(entitiesResult.summary.requested ?? 0) + Number(chunkResult.summary?.requested ?? chunk.length);
1337
+ entitiesResult.summary.succeeded = Number(entitiesResult.summary.succeeded ?? 0) + Number(chunkResult.summary?.succeeded ?? 0);
1338
+ entitiesResult.summary.failed = Number(entitiesResult.summary.failed ?? 0) + Number(chunkResult.summary?.failed ?? 0);
1339
+ if (chunkResult.errors?.length)
1340
+ entitiesResult.errors.push(...chunkResult.errors);
1341
+ logProgress("upsert.entities.batch.done", `${i + 1}/${entityChunks.length} succeeded=${chunkResult.summary?.succeeded ?? 0}, failed=${chunkResult.summary?.failed ?? 0}`);
1342
+ }
1319
1343
  logProgress("upsert.entities.done", `succeeded=${entitiesResult.summary?.succeeded ?? 0}, failed=${entitiesResult.summary?.failed ?? 0}`);
1320
- // Upsert relationships
1344
+ // Upsert relationships (chunked)
1321
1345
  let relsResult = null;
1322
1346
  if (relationships.length > 0) {
1323
- logProgress("upsert.relationships.start", `count=${relationships.length}`);
1324
- const relsRaw = await callMcpTool("nexarch_upsert_relationships", { relationships, agentContext, policyContext }, mcpOpts);
1325
- relsResult = parseToolText(relsRaw);
1347
+ const relationshipChunks = chunkArray(relationships, Math.max(1, upsertBatchSize));
1348
+ relsResult = { summary: { requested: 0, succeeded: 0, failed: 0 }, errors: [] };
1349
+ logProgress("upsert.relationships.start", `count=${relationships.length}, batches=${relationshipChunks.length}, batchSize=${Math.max(1, upsertBatchSize)}`);
1350
+ for (let i = 0; i < relationshipChunks.length; i += 1) {
1351
+ const chunk = relationshipChunks[i];
1352
+ logProgress("upsert.relationships.batch.start", `${i + 1}/${relationshipChunks.length} size=${chunk.length}`);
1353
+ const relsRaw = await callMcpTool("nexarch_upsert_relationships", { relationships: chunk, agentContext, policyContext }, mcpOpts);
1354
+ const chunkResult = parseToolText(relsRaw);
1355
+ relsResult.summary.requested = Number(relsResult.summary.requested ?? 0) + Number(chunkResult.summary?.requested ?? chunk.length);
1356
+ relsResult.summary.succeeded = Number(relsResult.summary.succeeded ?? 0) + Number(chunkResult.summary?.succeeded ?? 0);
1357
+ relsResult.summary.failed = Number(relsResult.summary.failed ?? 0) + Number(chunkResult.summary?.failed ?? 0);
1358
+ if (chunkResult.errors?.length)
1359
+ relsResult.errors.push(...chunkResult.errors);
1360
+ logProgress("upsert.relationships.batch.done", `${i + 1}/${relationshipChunks.length} succeeded=${chunkResult.summary?.succeeded ?? 0}, failed=${chunkResult.summary?.failed ?? 0}`);
1361
+ }
1326
1362
  logProgress("upsert.relationships.done", `succeeded=${relsResult.summary?.succeeded ?? 0}, failed=${relsResult.summary?.failed ?? 0}`);
1327
1363
  }
1328
1364
  // Build structured enrichment task (included in JSON output and printed in human mode)
package/dist/index.js CHANGED
@@ -94,6 +94,7 @@ Usage:
94
94
  --create-application force new application entity
95
95
  --auto-map-application auto-map only when high confidence
96
96
  --non-interactive fail on ambiguous mapping
97
+ --batch-size <n> upsert batch size (default: 10)
97
98
  --dry-run preview without writing
98
99
  --json
99
100
  nexarch update-entity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",