nexarch 0.8.11 → 0.8.13

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.
@@ -1,12 +1,13 @@
1
1
  import { arch, homedir, hostname, platform, release, type as osType, userInfo } from "os";
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3
3
  import { join } from "path";
4
+ import * as readline from "node:readline/promises";
4
5
  import process from "process";
5
6
  import { requireCredentials } from "../lib/credentials.js";
6
7
  import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
7
8
  import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
8
9
  import { buildVersionAttributes } from "../lib/version-normalization.js";
9
- const CLI_VERSION = "0.8.4";
10
+ const CLI_VERSION = "0.8.12";
10
11
  const AGENT_ENTITY_TYPE = "agent";
11
12
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
12
13
  function parseFlag(args, flag) {
@@ -25,6 +26,42 @@ function parseToolText(result) {
25
26
  const text = result.content?.[0]?.text ?? "{}";
26
27
  return JSON.parse(text);
27
28
  }
29
+ function parseCsv(value) {
30
+ if (!value)
31
+ return [];
32
+ return value.split(",").map((v) => v.trim()).filter(Boolean);
33
+ }
34
+ async function promptForValue(promptText, required = false) {
35
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
36
+ return null;
37
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
38
+ try {
39
+ // eslint-disable-next-line no-constant-condition
40
+ while (true) {
41
+ const value = (await rl.question(promptText)).trim();
42
+ if (value)
43
+ return value;
44
+ if (!required)
45
+ return null;
46
+ console.log("A value is required.");
47
+ }
48
+ }
49
+ finally {
50
+ rl.close();
51
+ }
52
+ }
53
+ async function confirmInstructionWrite() {
54
+ if (!process.stdin.isTTY || !process.stdout.isTTY)
55
+ return false;
56
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
57
+ try {
58
+ const answer = (await rl.question("Allow nexarch init-agent to write/update AGENTS.md/CLAUDE.md registration instructions? [y/N]: ")).trim().toLowerCase();
59
+ return answer === "y" || answer === "yes";
60
+ }
61
+ finally {
62
+ rl.close();
63
+ }
64
+ }
28
65
  function renderChecksTable(checks) {
29
66
  const headers = ["Status", "Check", "Details"];
30
67
  const rows = checks.map((check) => [
@@ -360,6 +397,16 @@ export async function initAgent(args) {
360
397
  const explicitAgentId = parseOptionValue(args, "--agent-id");
361
398
  const bindToExternalKey = parseOptionValue(args, "--bind-to-external-key");
362
399
  const bindRelationshipType = parseOptionValue(args, "--bind-relationship-type") ?? "depends_on";
400
+ const allowInstructionWriteFlag = parseFlag(args, "--allow-instruction-write");
401
+ const denyInstructionWriteFlag = parseFlag(args, "--deny-instruction-write");
402
+ const providerArg = parseOptionValue(args, "--provider");
403
+ const modelArg = parseOptionValue(args, "--model");
404
+ const clientArg = parseOptionValue(args, "--client");
405
+ const frameworkArg = parseOptionValue(args, "--framework");
406
+ const sessionIdArg = parseOptionValue(args, "--session-id");
407
+ const toolVersionArg = parseOptionValue(args, "--tool-version");
408
+ const capabilitiesArg = parseCsv(parseOptionValue(args, "--capabilities"));
409
+ const notesArg = parseOptionValue(args, "--notes");
363
410
  const agentId = explicitAgentId ?? getDefaultAgentId();
364
411
  const runtime = {
365
412
  osPlatform: platform(),
@@ -813,7 +860,93 @@ export async function initAgent(args) {
813
860
  }
814
861
  }
815
862
  }
863
+ let identityCapture = {
864
+ attempted: false,
865
+ ok: false,
866
+ detail: "skipped",
867
+ missingRequired: [],
868
+ };
869
+ if (registration.ok) {
870
+ let provider = providerArg;
871
+ let model = modelArg;
872
+ let client = clientArg;
873
+ if (!asJson) {
874
+ provider = provider ?? await promptForValue("Provider (e.g. anthropic/openai/google): ", true);
875
+ model = model ?? await promptForValue("Model id (e.g. claude-sonnet-4-6): ", true);
876
+ client = client ?? await promptForValue("Client (e.g. claude-code/cursor/codex-cli): ", true);
877
+ }
878
+ const missingRequired = [
879
+ !provider ? "provider" : null,
880
+ !model ? "model" : null,
881
+ !client ? "client" : null,
882
+ ].filter(Boolean);
883
+ if (missingRequired.length > 0) {
884
+ identityCapture = {
885
+ attempted: false,
886
+ ok: false,
887
+ detail: `identity input needed: ${missingRequired.join(", ")}`,
888
+ missingRequired,
889
+ };
890
+ }
891
+ else {
892
+ const nowIso = new Date().toISOString();
893
+ identityCapture.attempted = true;
894
+ try {
895
+ const identityRaw = await callMcpTool("nexarch_upsert_entities", {
896
+ entities: [
897
+ {
898
+ externalKey: `agent:${agentId}`,
899
+ entityTypeCode: "agent",
900
+ name: `Agent ${agentId}`,
901
+ confidence: 1,
902
+ attributes: {
903
+ identity: {
904
+ source: "nexarch init-agent",
905
+ capturedAt: nowIso,
906
+ provider,
907
+ model,
908
+ client,
909
+ framework: frameworkArg,
910
+ sessionId: sessionIdArg,
911
+ toolVersion: toolVersionArg,
912
+ capabilities: capabilitiesArg,
913
+ notes: notesArg,
914
+ },
915
+ },
916
+ },
917
+ ],
918
+ agentContext: {
919
+ agentId,
920
+ agentRunId: `init-agent-identify-${Date.now()}`,
921
+ repoRef: "nexarch-cli/init-agent",
922
+ observedAt: nowIso,
923
+ source: "nexarch-cli",
924
+ model,
925
+ provider,
926
+ },
927
+ policyContext: policies.policyBundleHash
928
+ ? {
929
+ policyBundleHash: policies.policyBundleHash,
930
+ alignmentSummary: { score: 1, violations: [], waivers: [] },
931
+ }
932
+ : undefined,
933
+ dryRun: false,
934
+ }, { companyId: selectedCompanyId });
935
+ const identity = parseToolText(identityRaw);
936
+ const failed = Number(identity.summary?.failed ?? 0) > 0;
937
+ identityCapture.ok = !failed;
938
+ identityCapture.detail = failed
939
+ ? `identity capture failed (${identity.errors?.[0]?.error ?? "unknown"})`
940
+ : "identity captured";
941
+ }
942
+ catch (error) {
943
+ identityCapture.ok = false;
944
+ identityCapture.detail = error instanceof Error ? error.message : "identity capture failed";
945
+ }
946
+ }
947
+ }
816
948
  let agentConfigResults = [];
949
+ let instructionsWriteAllowed = false;
817
950
  if (registration.ok) {
818
951
  try {
819
952
  // Save identity so check-in can find the agent key
@@ -824,9 +957,20 @@ export async function initAgent(args) {
824
957
  catch {
825
958
  // non-fatal
826
959
  }
827
- agentConfigResults = injectAgentConfigs(registry);
828
- if (agentConfigResults.length === 0) {
829
- agentConfigResults = injectGenericAgentConfig(registry);
960
+ if (denyInstructionWriteFlag) {
961
+ instructionsWriteAllowed = false;
962
+ }
963
+ else if (allowInstructionWriteFlag) {
964
+ instructionsWriteAllowed = true;
965
+ }
966
+ else if (!asJson) {
967
+ instructionsWriteAllowed = await confirmInstructionWrite();
968
+ }
969
+ if (instructionsWriteAllowed) {
970
+ agentConfigResults = injectAgentConfigs(registry);
971
+ if (agentConfigResults.length === 0) {
972
+ agentConfigResults = injectGenericAgentConfig(registry);
973
+ }
830
974
  }
831
975
  }
832
976
  checks.push({
@@ -834,14 +978,26 @@ export async function initAgent(args) {
834
978
  ok: registration.ok,
835
979
  detail: registration.detail,
836
980
  });
981
+ const needsIdentityInput = registration.ok && identityCapture.missingRequired.length > 0;
982
+ checks.push({
983
+ name: "agent.identity.capture",
984
+ ok: !registration.ok || identityCapture.ok || needsIdentityInput,
985
+ detail: !registration.ok
986
+ ? "skipped (registration failed)"
987
+ : needsIdentityInput
988
+ ? `awaiting identity input (${identityCapture.missingRequired.join(", ")})`
989
+ : identityCapture.detail,
990
+ });
837
991
  checks.push({
838
992
  name: "agent.instructions.injection",
839
- ok: !registration.ok || agentConfigResults.length > 0,
993
+ ok: !registration.ok || !instructionsWriteAllowed || agentConfigResults.length > 0,
840
994
  detail: !registration.ok
841
995
  ? "skipped (registration failed)"
842
- : agentConfigResults.length > 0
843
- ? `updated ${agentConfigResults.length} instruction target file(s)`
844
- : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
996
+ : !instructionsWriteAllowed
997
+ ? "skipped (consent not granted)"
998
+ : agentConfigResults.length > 0
999
+ ? `updated ${agentConfigResults.length} instruction target file(s)`
1000
+ : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
845
1001
  });
846
1002
  checks.push({
847
1003
  name: "technology.components",
@@ -862,17 +1018,12 @@ export async function initAgent(args) {
862
1018
  }
863
1019
  const allPassed = checks.every((c) => c.ok);
864
1020
  const appDetection = detectLikelyApplicationInCwd();
865
- const identifyPrompt = [
866
- "Please identify your runtime so the workspace can register richer metadata.",
867
- "Return ONLY the completed command below with real values (no markdown):",
868
- `nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
869
- ].join("\n");
870
1021
  if (asJson) {
871
1022
  const output = {
872
1023
  ok: strict ? allPassed : true,
873
1024
  passed: allPassed,
874
1025
  strict,
875
- status: allPassed ? "needs_agent_identity_followup" : "handshake_completed_with_issues",
1026
+ status: needsIdentityInput ? "needs_identity_input" : (allPassed ? "completed" : "completed_with_issues"),
876
1027
  checks,
877
1028
  summary: {
878
1029
  total: checks.length,
@@ -890,11 +1041,9 @@ export async function initAgent(args) {
890
1041
  },
891
1042
  binding,
892
1043
  followUp: {
893
- required: allPassed,
894
- type: "agent_identity",
895
- detail: "Prompt the coding agent to self-identify and submit via nexarch agent identify",
896
- prompt: identifyPrompt,
897
- submitCommandTemplate: `nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
1044
+ required: appDetection.detected,
1045
+ type: "init_project_recommendation",
1046
+ detail: appDetection.detected ? "Project detected; request consent before running init-project" : "No project detected",
898
1047
  initProjectSuggestion: appDetection.detected
899
1048
  ? {
900
1049
  detected: true,
@@ -909,6 +1058,13 @@ export async function initAgent(args) {
909
1058
  reason: "No common application manifest files detected in current directory.",
910
1059
  },
911
1060
  },
1061
+ identityInputGuidance: needsIdentityInput
1062
+ ? {
1063
+ missingRequired: identityCapture.missingRequired,
1064
+ guidance: "Collect provider, model, and client, then rerun init-agent with those flags.",
1065
+ preferredCommandTemplate: "npx nexarch@latest init-agent --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<optional>\" --tool-version \"<optional>\" --capabilities \"<optional,comma,separated>\" --notes \"<optional>\" --json",
1066
+ }
1067
+ : null,
912
1068
  companyId: creds.companyId,
913
1069
  registry: { version: registry.release.version, registryVersion: registry.registryVersion, publishedAt: registry.release.publishedAt },
914
1070
  agentConfigs: agentConfigResults,
@@ -940,11 +1096,14 @@ export async function initAgent(args) {
940
1096
  }
941
1097
  }
942
1098
  const hasInjectedAgentInstructions = agentConfigResults.some((r) => r.status === "injected" || r.status === "updated");
943
- console.log("\n➡ Next step (required): complete agent identity in your coding agent");
944
- console.log(" Paste this and fill in real values:");
945
- console.log(`\nrun npx nexarch@latest agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --json\n`);
946
1099
  if (hasInjectedAgentInstructions) {
947
- console.log(" (Registration instructions were already written into your agent config file.)");
1100
+ console.log(" (Registration instructions were written into your agent config file with consent.)");
1101
+ }
1102
+ if (needsIdentityInput) {
1103
+ console.log("\nℹ Additional identity details are still needed to complete agent profile enrichment.");
1104
+ console.log(` Missing: ${identityCapture.missingRequired.join(", ")}`);
1105
+ console.log(" In an interactive terminal, rerun 'npx nexarch@latest init-agent' and answer prompts.");
1106
+ console.log(" In non-interactive mode, provide: --provider --model --client");
948
1107
  }
949
1108
  if (!fromSetup && appDetection.detected) {
950
1109
  console.log("\n➡ Optional next step: likely application detected in current directory.");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.8.11",
3
+ "version": "0.8.13",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",