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.
- package/dist/commands/init-agent.js +182 -23
- package/package.json +1 -1
|
@@ -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.
|
|
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
|
-
|
|
828
|
-
|
|
829
|
-
|
|
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
|
-
:
|
|
843
|
-
?
|
|
844
|
-
:
|
|
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 ? "
|
|
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:
|
|
894
|
-
type: "
|
|
895
|
-
detail: "
|
|
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
|
|
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.");
|