nexarch 0.6.2 → 0.6.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.
@@ -1,5 +1,5 @@
1
1
  import process from "process";
2
- import { existsSync, readFileSync, readdirSync, statSync } from "fs";
2
+ import { existsSync, readFileSync } from "fs";
3
3
  import { join } from "path";
4
4
  import { homedir } from "os";
5
5
  import { requireCredentials } from "../lib/credentials.js";
@@ -35,66 +35,6 @@ function loadIdentity() {
35
35
  return { agentKey: null, projectKeys: [] };
36
36
  }
37
37
  }
38
- function safeReadText(path, maxChars = 6000) {
39
- if (!existsSync(path))
40
- return null;
41
- try {
42
- return readFileSync(path, "utf8").slice(0, maxChars);
43
- }
44
- catch {
45
- return null;
46
- }
47
- }
48
- function collectEvidenceFromRepo() {
49
- const cwd = process.cwd();
50
- const readme = safeReadText(join(cwd, "README.md"), 8000)
51
- ?? safeReadText(join(cwd, "readme.md"), 8000)
52
- ?? undefined;
53
- const packageJson = safeReadText(join(cwd, "package.json"), 5000);
54
- const notes = [];
55
- if (packageJson)
56
- notes.push("package.json detected");
57
- const files = [];
58
- if (packageJson)
59
- files.push({ path: "package.json", content: packageJson });
60
- const srcDir = join(cwd, "src");
61
- if (existsSync(srcDir)) {
62
- try {
63
- const entries = readdirSync(srcDir)
64
- .filter((name) => /\.(ts|tsx|js|jsx)$/i.test(name))
65
- .slice(0, 8);
66
- for (const name of entries) {
67
- const full = join(srcDir, name);
68
- try {
69
- if (!statSync(full).isFile())
70
- continue;
71
- const content = safeReadText(full, 4000);
72
- if (content)
73
- files.push({ path: `src/${name}`, content });
74
- }
75
- catch {
76
- // ignore per-file failures
77
- }
78
- }
79
- if (entries.length > 0)
80
- notes.push(`included ${entries.length} source file snippet(s)`);
81
- }
82
- catch {
83
- // ignore directory read issues
84
- }
85
- }
86
- const summary = [
87
- `cwd=${cwd}`,
88
- readme ? "README included" : "README missing",
89
- files.length > 0 ? `evidence files=${files.length}` : "no evidence files collected",
90
- ].join("; ");
91
- return {
92
- summary,
93
- ...(readme ? { readme } : {}),
94
- ...(notes.length ? { notes: notes.join("; ") } : {}),
95
- ...(files.length ? { files } : {}),
96
- };
97
- }
98
38
  export async function checkIn(args) {
99
39
  const asJson = parseFlag(args, "--json");
100
40
  const agentKeyArg = parseOptionValue(args, "--agent-key");
@@ -102,7 +42,6 @@ export async function checkIn(args) {
102
42
  const creds = requireCredentials();
103
43
  const identity = loadIdentity();
104
44
  const agentKey = agentKeyArg ?? identity.agentKey;
105
- // Use --project flag as a single override, otherwise send all stored keys
106
45
  const resolvedProjectKeys = projectKey ? [projectKey] : identity.projectKeys;
107
46
  if (!agentKey) {
108
47
  if (asJson) {
@@ -113,46 +52,46 @@ export async function checkIn(args) {
113
52
  }
114
53
  return;
115
54
  }
116
- const mcpOpts = { companyId: creds.companyId };
117
- const evidence = collectEvidenceFromRepo();
118
55
  const raw = await callMcpTool("nexarch_claim_command", {
119
56
  agentKey,
120
57
  ...(resolvedProjectKeys.length > 0 ? { projectKeys: resolvedProjectKeys } : {}),
121
- evidence,
122
58
  companyId: creds.companyId,
123
- }, mcpOpts);
59
+ }, { companyId: creds.companyId });
124
60
  const result = parseToolText(raw);
125
61
  if (asJson) {
126
62
  process.stdout.write(JSON.stringify(result) + "\n");
127
63
  return;
128
64
  }
129
- if (!result.command) {
130
- console.log("No pending commands. Nothing to do.");
65
+ const commands = result.commands ?? [];
66
+ if (commands.length === 0) {
67
+ console.log("No pending application-target commands found.");
131
68
  return;
132
69
  }
133
- const cmd = result.command;
134
- console.log(`\n╔══════════════════════════════════════════════════════════════════╗`);
135
- console.log(`║ COMMAND QUEUED action required ║`);
136
- console.log(`╚══════════════════════════════════════════════════════════════════╝`);
137
- console.log(`\nCommand ID : ${cmd.id}`);
138
- console.log(`Type : ${cmd.command_type}`);
139
- console.log(`Target : ${cmd.target_entity_key ?? "(any)"}`);
140
- console.log(`Priority : ${cmd.priority}`);
141
- if (cmd.resolved_runtime_handler)
142
- console.log(`Runtime : ${cmd.resolved_runtime_handler}`);
143
- if (cmd.command_type_version != null)
144
- console.log(`Type ver : ${cmd.command_type_version}`);
145
- if (Object.keys(cmd.command_params).length > 0) {
146
- console.log(`Params : ${JSON.stringify(cmd.command_params)}`);
70
+ console.log("\nPending application-target commands (preview only; nothing claimed):");
71
+ for (const cmd of commands) {
72
+ const claimable = cmd.claimable ? "yes" : "no";
73
+ const reason = cmd.claimReason ?? (cmd.claimable ? "in_project_scope" : "missing_project_scope");
74
+ console.log(`\n- ID : ${cmd.id}`);
75
+ console.log(` Type : ${cmd.command_type}`);
76
+ console.log(` Target : ${cmd.target_entity_key ?? "(none)"}`);
77
+ console.log(` Priority : ${cmd.priority}`);
78
+ console.log(` Claimable : ${claimable} (${reason})`);
79
+ if (cmd.command_type_version != null)
80
+ console.log(` Type ver : ${cmd.command_type_version}`);
81
+ if (cmd.resolved_runtime_handler)
82
+ console.log(` Runtime : ${cmd.resolved_runtime_handler}`);
83
+ if (cmd.claimable) {
84
+ console.log(` Claim cmd : npx nexarch command-claim --id "${cmd.id}"${cmd.target_entity_key ? ` --project "${cmd.target_entity_key}"` : ""}`);
85
+ }
147
86
  }
148
- if (cmd.resolved_playbook_text?.trim()) {
149
- console.log(`\nPlaybook:\n${cmd.resolved_playbook_text}`);
87
+ console.log("\nCurrent project scope:");
88
+ if (resolvedProjectKeys.length === 0) {
89
+ console.log(" (none) — use --project <application:key> to scope claims");
150
90
  }
151
- if (cmd.instructions) {
152
- console.log(`\nInstructions:\n${cmd.instructions}`);
91
+ else {
92
+ for (const key of resolvedProjectKeys)
93
+ console.log(` - ${key}`);
153
94
  }
154
- console.log(`\nWhen done, run:`);
155
- console.log(` npx nexarch command-done --id "${cmd.id}" --summary "..."`);
156
- console.log(`\nIf it fails, run:`);
157
- console.log(` npx nexarch command-fail --id "${cmd.id}" --error "..."`);
95
+ console.log("\nTo validate an application target is accessible in Nexarch before claiming:");
96
+ console.log(" npx nexarch policy-controls --entity <application:key> --json");
158
97
  }
@@ -0,0 +1,97 @@
1
+ import process from "process";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { requireCredentials } from "../lib/credentials.js";
6
+ import { callMcpTool } from "../lib/mcp.js";
7
+ function parseFlag(args, flag) {
8
+ return args.includes(flag);
9
+ }
10
+ function parseOptionValue(args, option) {
11
+ const idx = args.indexOf(option);
12
+ if (idx === -1)
13
+ return null;
14
+ const v = args[idx + 1];
15
+ if (!v || v.startsWith("--"))
16
+ return null;
17
+ return v;
18
+ }
19
+ function parseToolText(result) {
20
+ const text = result.content?.[0]?.text ?? "{}";
21
+ return JSON.parse(text);
22
+ }
23
+ function loadIdentity() {
24
+ const identityPath = join(homedir(), ".nexarch", "identity.json");
25
+ if (!existsSync(identityPath))
26
+ return { agentKey: null, projectKeys: [] };
27
+ try {
28
+ const data = JSON.parse(readFileSync(identityPath, "utf8"));
29
+ return {
30
+ agentKey: data.agentKey ?? null,
31
+ projectKeys: Array.isArray(data.projectKeys) ? data.projectKeys : [],
32
+ };
33
+ }
34
+ catch {
35
+ return { agentKey: null, projectKeys: [] };
36
+ }
37
+ }
38
+ export async function commandClaim(args) {
39
+ const asJson = parseFlag(args, "--json");
40
+ const id = parseOptionValue(args, "--id");
41
+ const agentKeyArg = parseOptionValue(args, "--agent-key");
42
+ const projectKey = parseOptionValue(args, "--project");
43
+ if (!id) {
44
+ console.error("error: --id <commandId> is required");
45
+ process.exit(1);
46
+ }
47
+ const creds = requireCredentials();
48
+ const identity = loadIdentity();
49
+ const agentKey = agentKeyArg ?? identity.agentKey;
50
+ const resolvedProjectKeys = projectKey ? [projectKey] : identity.projectKeys;
51
+ if (!agentKey) {
52
+ console.error("error: no agent key found. Pass --agent-key or run nexarch init-agent first.");
53
+ process.exit(1);
54
+ }
55
+ const raw = await callMcpTool("nexarch_claim_command_by_id", {
56
+ commandId: id,
57
+ agentKey,
58
+ ...(resolvedProjectKeys.length > 0 ? { projectKeys: resolvedProjectKeys } : {}),
59
+ companyId: creds.companyId,
60
+ }, { companyId: creds.companyId });
61
+ const result = parseToolText(raw);
62
+ if (asJson) {
63
+ process.stdout.write(JSON.stringify(result) + "\n");
64
+ return;
65
+ }
66
+ if (!result.command) {
67
+ console.log(`Command ${id} was not claimed.`);
68
+ if (result.reason)
69
+ console.log(`Reason: ${result.reason}`);
70
+ return;
71
+ }
72
+ const cmd = result.command;
73
+ console.log(`\n╔══════════════════════════════════════════════════════════════════╗`);
74
+ console.log(`║ COMMAND CLAIMED — action required ║`);
75
+ console.log(`╚══════════════════════════════════════════════════════════════════╝`);
76
+ console.log(`\nCommand ID : ${cmd.id}`);
77
+ console.log(`Type : ${cmd.command_type}`);
78
+ console.log(`Target : ${cmd.target_entity_key ?? "(any)"}`);
79
+ console.log(`Priority : ${cmd.priority}`);
80
+ if (cmd.resolved_runtime_handler)
81
+ console.log(`Runtime : ${cmd.resolved_runtime_handler}`);
82
+ if (cmd.command_type_version != null)
83
+ console.log(`Type ver : ${cmd.command_type_version}`);
84
+ if (Object.keys(cmd.command_params).length > 0) {
85
+ console.log(`Params : ${JSON.stringify(cmd.command_params)}`);
86
+ }
87
+ if (cmd.resolved_playbook_text?.trim()) {
88
+ console.log(`\nPlaybook:\n${cmd.resolved_playbook_text}`);
89
+ }
90
+ if (cmd.instructions) {
91
+ console.log(`\nInstructions:\n${cmd.instructions}`);
92
+ }
93
+ console.log(`\nWhen done, run:`);
94
+ console.log(` npx nexarch command-done --id "${cmd.id}" --summary "..."`);
95
+ console.log(`\nIf it fails, run:`);
96
+ console.log(` npx nexarch command-fail --id "${cmd.id}" --error "..."`);
97
+ }
@@ -0,0 +1,138 @@
1
+ import process from "process";
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { requireCredentials } from "../lib/credentials.js";
6
+ import { callMcpTool } from "../lib/mcp.js";
7
+ function parseFlag(args, flag) {
8
+ return args.includes(flag);
9
+ }
10
+ function parseOptionValue(args, option) {
11
+ const idx = args.indexOf(option);
12
+ if (idx === -1)
13
+ return null;
14
+ const v = args[idx + 1];
15
+ if (!v || v.startsWith("--"))
16
+ return null;
17
+ return v;
18
+ }
19
+ function parseMultiOptionValues(args, option) {
20
+ const values = [];
21
+ for (let i = 0; i < args.length; i += 1) {
22
+ if (args[i] !== option)
23
+ continue;
24
+ const v = args[i + 1];
25
+ if (!v || v.startsWith("--"))
26
+ continue;
27
+ values.push(v);
28
+ }
29
+ return values;
30
+ }
31
+ function parseToolText(result) {
32
+ const text = result.content?.[0]?.text ?? "{}";
33
+ return JSON.parse(text);
34
+ }
35
+ function loadIdentityAgentKey() {
36
+ const identityPath = join(homedir(), ".nexarch", "identity.json");
37
+ if (!existsSync(identityPath))
38
+ return null;
39
+ try {
40
+ const data = JSON.parse(readFileSync(identityPath, "utf8"));
41
+ return data.agentKey ?? null;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function parseFindingToken(token) {
48
+ const [policyControlId, policyRuleId, resultRaw, rationaleRaw, missingRaw] = token.split("|");
49
+ const result = (resultRaw ?? "").trim().toLowerCase();
50
+ if (!policyControlId?.trim() || !policyRuleId?.trim() || !result) {
51
+ throw new Error("Each --finding must be: <policyControlId>|<policyRuleId>|<pass|partial|fail>|<rationale>|<missing1;missing2>");
52
+ }
53
+ if (result !== "pass" && result !== "partial" && result !== "fail") {
54
+ throw new Error(`Invalid finding result '${resultRaw}'. Use pass|partial|fail.`);
55
+ }
56
+ return {
57
+ policyControlId: policyControlId.trim(),
58
+ policyRuleId: policyRuleId.trim(),
59
+ result,
60
+ ...(rationaleRaw?.trim() ? { rationale: rationaleRaw.trim() } : {}),
61
+ ...(missingRaw?.trim() ? { missingRequirements: missingRaw.split(";").map((m) => m.trim()).filter(Boolean) } : {}),
62
+ };
63
+ }
64
+ function parseFindings(args) {
65
+ const findingsJson = parseOptionValue(args, "--findings-json");
66
+ if (findingsJson) {
67
+ let parsed;
68
+ try {
69
+ parsed = JSON.parse(findingsJson);
70
+ }
71
+ catch {
72
+ throw new Error("--findings-json must be valid JSON array");
73
+ }
74
+ if (!Array.isArray(parsed) || parsed.length === 0) {
75
+ throw new Error("--findings-json must be a non-empty JSON array");
76
+ }
77
+ return parsed.map((item) => {
78
+ const value = item;
79
+ const result = String(value.result ?? "").toLowerCase();
80
+ if (!value.policyControlId || !value.policyRuleId || !result) {
81
+ throw new Error("Each finding must include policyControlId, policyRuleId, result");
82
+ }
83
+ if (result !== "pass" && result !== "partial" && result !== "fail") {
84
+ throw new Error(`Invalid finding result '${String(value.result)}'. Use pass|partial|fail.`);
85
+ }
86
+ return {
87
+ policyControlId: String(value.policyControlId),
88
+ policyRuleId: String(value.policyRuleId),
89
+ result,
90
+ ...(value.rationale ? { rationale: String(value.rationale) } : {}),
91
+ ...(Array.isArray(value.missingRequirements) ? { missingRequirements: value.missingRequirements.map(String) } : {}),
92
+ };
93
+ });
94
+ }
95
+ const tokens = parseMultiOptionValues(args, "--finding");
96
+ if (tokens.length === 0) {
97
+ throw new Error("Provide findings via --finding (repeatable) or --findings-json '<json-array>'");
98
+ }
99
+ return tokens.map(parseFindingToken);
100
+ }
101
+ export async function policyAuditSubmit(args) {
102
+ const asJson = parseFlag(args, "--json");
103
+ const commandId = parseOptionValue(args, "--command-id") ?? parseOptionValue(args, "--id");
104
+ const applicationEntityKey = parseOptionValue(args, "--application-key") ?? parseOptionValue(args, "--entity");
105
+ const agentKey = parseOptionValue(args, "--agent-key") ?? loadIdentityAgentKey();
106
+ if (!commandId) {
107
+ console.error("error: --command-id <uuid> is required");
108
+ process.exit(1);
109
+ }
110
+ if (!applicationEntityKey) {
111
+ console.error("error: --application-key <externalKey> is required (e.g. application:bad-driving)");
112
+ process.exit(1);
113
+ }
114
+ const findings = parseFindings(args);
115
+ const creds = requireCredentials();
116
+ const raw = await callMcpTool("nexarch_submit_policy_audit", {
117
+ commandId,
118
+ applicationEntityKey,
119
+ ...(agentKey ? { agentKey } : {}),
120
+ findings,
121
+ companyId: creds.companyId,
122
+ }, { companyId: creds.companyId });
123
+ const result = parseToolText(raw);
124
+ if (asJson) {
125
+ process.stdout.write(JSON.stringify(result) + "\n");
126
+ return;
127
+ }
128
+ if (!result.ok) {
129
+ console.error(`Policy audit submit failed${result.error ? `: ${result.error}` : ""}`);
130
+ process.exit(1);
131
+ }
132
+ console.log(`Policy audit submitted for ${applicationEntityKey}.`);
133
+ if (result.runId)
134
+ console.log(`Run ID: ${result.runId}`);
135
+ if (result.summary) {
136
+ console.log(`Summary: ${result.summary.passCount ?? 0} pass, ${result.summary.partialCount ?? 0} partial, ${result.summary.failCount ?? 0} fail`);
137
+ }
138
+ }
@@ -0,0 +1,52 @@
1
+ import process from "process";
2
+ import { requireCredentials } from "../lib/credentials.js";
3
+ import { callMcpTool } from "../lib/mcp.js";
4
+ function parseFlag(args, flag) {
5
+ return args.includes(flag);
6
+ }
7
+ function parseOptionValue(args, option) {
8
+ const idx = args.indexOf(option);
9
+ if (idx === -1)
10
+ return null;
11
+ const v = args[idx + 1];
12
+ if (!v || v.startsWith("--"))
13
+ return null;
14
+ return v;
15
+ }
16
+ function parseToolText(result) {
17
+ const text = result.content?.[0]?.text ?? "{}";
18
+ return JSON.parse(text);
19
+ }
20
+ export async function policyControls(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ const entity = parseOptionValue(args, "--entity") ?? parseOptionValue(args, "--application-key");
23
+ if (!entity) {
24
+ console.error("error: --entity <externalKey> is required (e.g. application:bad-driving)");
25
+ process.exit(1);
26
+ }
27
+ const creds = requireCredentials();
28
+ const raw = await callMcpTool("nexarch_get_entity_policy_controls", { entityExternalKey: entity, companyId: creds.companyId }, { companyId: creds.companyId });
29
+ const result = parseToolText(raw);
30
+ if (asJson) {
31
+ process.stdout.write(JSON.stringify(result) + "\n");
32
+ return;
33
+ }
34
+ if (!result.found) {
35
+ console.log(`Entity not found: ${entity}`);
36
+ return;
37
+ }
38
+ console.log(`Policy controls for ${result.entityExternalKey ?? entity}`);
39
+ console.log(`Controls: ${result.controlCount ?? 0}`);
40
+ for (const control of result.controls ?? []) {
41
+ const rules = control.rules ?? [];
42
+ console.log(`\n- ${control.name} (${control.id})`);
43
+ if (rules.length === 0) {
44
+ console.log(" (no rules)");
45
+ continue;
46
+ }
47
+ for (const rule of rules) {
48
+ const level = rule.requirementLevel ? ` [${rule.requirementLevel}]` : "";
49
+ console.log(` • ${rule.name} (${rule.id})${level}`);
50
+ }
51
+ }
52
+ }
package/dist/index.js CHANGED
@@ -15,6 +15,9 @@ import { resolveNames } from "./commands/resolve-names.js";
15
15
  import { checkIn } from "./commands/check-in.js";
16
16
  import { commandDone } from "./commands/command-done.js";
17
17
  import { commandFail } from "./commands/command-fail.js";
18
+ import { commandClaim } from "./commands/command-claim.js";
19
+ import { policyControls } from "./commands/policy-controls.js";
20
+ import { policyAuditSubmit } from "./commands/policy-audit-submit.js";
18
21
  const [, , command, ...args] = process.argv;
19
22
  const commands = {
20
23
  login,
@@ -33,6 +36,9 @@ const commands = {
33
36
  "check-in": checkIn,
34
37
  "command-done": commandDone,
35
38
  "command-fail": commandFail,
39
+ "command-claim": commandClaim,
40
+ "policy-controls": policyControls,
41
+ "policy-audit-submit": policyAuditSubmit,
36
42
  };
37
43
  async function main() {
38
44
  if (command === "agent") {
@@ -113,12 +119,18 @@ Usage:
113
119
  results before calling add-relationship.
114
120
  Options: --names <csv> (required, e.g. "vercel,neon")
115
121
  --json
116
- nexarch check-in Poll the command queue and claim the next pending command
117
- for this agent. Reads agent key from ~/.nexarch/identity.json
122
+ nexarch check-in Preview pending application-target commands for this agent scope
123
+ without claiming. Reads agent key from ~/.nexarch/identity.json
118
124
  (written by init-agent) or via --agent-key.
119
125
  Options: --agent-key <key> override stored agent key
120
126
  --project <key> application entity key
121
127
  --json
128
+ nexarch command-claim
129
+ Explicitly claim a pending command by ID.
130
+ Options: --id <commandId> (required)
131
+ --agent-key <key> override stored agent key
132
+ --project <key> application entity key
133
+ --json
122
134
  nexarch command-done
123
135
  Mark a claimed command as completed.
124
136
  Options: --id <commandId> (required)
@@ -129,6 +141,18 @@ Usage:
129
141
  Options: --id <commandId> (required)
130
142
  --error <message> (required)
131
143
  --json
144
+ nexarch policy-controls
145
+ Fetch policy controls/rules assigned to an entity (for policy audits).
146
+ Options: --entity <externalKey> (required, e.g. application:bad-driving)
147
+ --json
148
+ nexarch policy-audit-submit
149
+ Submit structured policy findings (writes policy_audit_finding rows).
150
+ Options: --command-id <id> (required)
151
+ --application-key <key> (required)
152
+ --agent-key <key> (optional; defaults from identity)
153
+ --finding <controlId|ruleId|result|rationale|missing1;missing2> (repeatable)
154
+ --findings-json <json-array>
155
+ --json
132
156
  `);
133
157
  process.exit(command ? 1 : 0);
134
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",