nexarch 0.8.10 → 0.8.12

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: `missing required identity fields: ${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,21 @@ export async function initAgent(args) {
834
978
  ok: registration.ok,
835
979
  detail: registration.detail,
836
980
  });
981
+ checks.push({
982
+ name: "agent.identity.capture",
983
+ ok: !registration.ok || identityCapture.ok,
984
+ detail: !registration.ok ? "skipped (registration failed)" : identityCapture.detail,
985
+ });
837
986
  checks.push({
838
987
  name: "agent.instructions.injection",
839
- ok: !registration.ok || agentConfigResults.length > 0,
988
+ ok: !registration.ok || !instructionsWriteAllowed || agentConfigResults.length > 0,
840
989
  detail: !registration.ok
841
990
  ? "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)",
991
+ : !instructionsWriteAllowed
992
+ ? "skipped (consent not granted)"
993
+ : agentConfigResults.length > 0
994
+ ? `updated ${agentConfigResults.length} instruction target file(s)`
995
+ : "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)",
845
996
  });
846
997
  checks.push({
847
998
  name: "technology.components",
@@ -862,17 +1013,12 @@ export async function initAgent(args) {
862
1013
  }
863
1014
  const allPassed = checks.every((c) => c.ok);
864
1015
  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
1016
  if (asJson) {
871
1017
  const output = {
872
1018
  ok: strict ? allPassed : true,
873
1019
  passed: allPassed,
874
1020
  strict,
875
- status: allPassed ? "needs_agent_identity_followup" : "handshake_completed_with_issues",
1021
+ status: allPassed ? "completed" : "completed_with_issues",
876
1022
  checks,
877
1023
  summary: {
878
1024
  total: checks.length,
@@ -890,20 +1036,20 @@ export async function initAgent(args) {
890
1036
  },
891
1037
  binding,
892
1038
  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`,
1039
+ required: appDetection.detected,
1040
+ type: "init_project_recommendation",
1041
+ detail: appDetection.detected ? "Project detected; request consent before running init-project" : "No project detected",
898
1042
  initProjectSuggestion: appDetection.detected
899
1043
  ? {
900
- shouldPromptUser: true,
1044
+ detected: true,
901
1045
  reason: `Detected likely project manifests in cwd: ${appDetection.evidence.join(", ")}`,
902
- userPrompt: "I found a likely application in this directory. Do you want me to run init-project next?",
903
- suggestedCommand: "npx nexarch@latest init-project --dir . --json",
1046
+ consentRequired: true,
1047
+ consentPrompt: "I detected a likely project in this directory. Do you want me to run init-project now?",
1048
+ actionIfApproved: "run_init_project",
1049
+ command: "npx nexarch@latest init-project --dir . --json",
904
1050
  }
905
1051
  : {
906
- shouldPromptUser: false,
1052
+ detected: false,
907
1053
  reason: "No common application manifest files detected in current directory.",
908
1054
  },
909
1055
  },
@@ -938,17 +1084,15 @@ export async function initAgent(args) {
938
1084
  }
939
1085
  }
940
1086
  const hasInjectedAgentInstructions = agentConfigResults.some((r) => r.status === "injected" || r.status === "updated");
941
- console.log("\n➡ Next step (required): complete agent identity in your coding agent");
942
- console.log(" Paste this and fill in real values:");
943
- 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`);
944
1087
  if (hasInjectedAgentInstructions) {
945
- console.log(" (Registration instructions were already written into your agent config file.)");
1088
+ console.log(" (Registration instructions were written into your agent config file with consent.)");
946
1089
  }
947
1090
  if (!fromSetup && appDetection.detected) {
948
1091
  console.log("\n➡ Optional next step: likely application detected in current directory.");
949
1092
  console.log(` Detected manifests: ${appDetection.evidence.join(", ")}`);
950
- console.log(" Ask the user: 'I found a likely application here. Do you want me to run init-project next?'");
951
- console.log(" Suggested command: npx nexarch@latest init-project --dir . --json");
1093
+ console.log(" Ask for user consent before running init-project.");
1094
+ console.log(" Consent prompt: 'I detected a likely project in this directory. Do you want me to run init-project now?'");
1095
+ console.log(" If approved, run: npx nexarch@latest init-project --dir . --json");
952
1096
  }
953
1097
  }
954
1098
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.8.10",
3
+ "version": "0.8.12",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",