nexarch 0.10.1 → 0.11.0

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.
@@ -0,0 +1,57 @@
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 appliedPolicies(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ const showMarkdown = parseFlag(args, "--markdown");
23
+ if (parseFlag(args, "--help") || parseFlag(args, "-h")) {
24
+ console.log(`
25
+ Usage:
26
+ nexarch applied-policies [--pack <packCode>] [--markdown] [--json]
27
+
28
+ Options:
29
+ --pack <code> Filter to a specific policy pack code
30
+ --markdown Include full document markdown in human output
31
+ --json Print JSON response (always includes markdown)
32
+ `);
33
+ return;
34
+ }
35
+ const packCode = parseOptionValue(args, "--pack") ?? parseOptionValue(args, "--pack-code");
36
+ const creds = requireCredentials();
37
+ const raw = await callMcpTool("nexarch_get_applied_policies", { ...(packCode ? { packCode } : {}) }, { companyId: creds.companyId });
38
+ const result = parseToolText(raw);
39
+ if (asJson) {
40
+ process.stdout.write(JSON.stringify(result) + "\n");
41
+ return;
42
+ }
43
+ console.log(`Applied policies${packCode ? ` (pack: ${packCode})` : ""}`);
44
+ if (result.policyBundleHash)
45
+ console.log(`Bundle hash: ${result.policyBundleHash}`);
46
+ console.log(`Documents: ${result.policyCount ?? 0}`);
47
+ for (const pack of result.policies ?? []) {
48
+ console.log(`\n[${pack.packCode}] ${pack.packName} — v${pack.installedVersion}`);
49
+ for (const doc of pack.documents ?? []) {
50
+ console.log(` - ${doc.title} (${doc.code})`);
51
+ if (showMarkdown && doc.contentMarkdown) {
52
+ const indented = doc.contentMarkdown.split("\n").map((l) => ` ${l}`).join("\n");
53
+ console.log(indented);
54
+ }
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,56 @@
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 parseToolText(result) {
8
+ const text = result.content?.[0]?.text ?? "{}";
9
+ return JSON.parse(text);
10
+ }
11
+ export async function governanceSummary(args) {
12
+ const asJson = parseFlag(args, "--json");
13
+ if (parseFlag(args, "--help") || parseFlag(args, "-h")) {
14
+ console.log(`
15
+ Usage:
16
+ nexarch governance-summary [--json]
17
+
18
+ Returns review queue counts, graph stats, and a per-application policy
19
+ audit rollup (latest run status, pass/partial/fail counts).
20
+ `);
21
+ return;
22
+ }
23
+ const creds = requireCredentials();
24
+ const raw = await callMcpTool("nexarch_get_governance_summary", { companyId: creds.companyId }, { companyId: creds.companyId });
25
+ const result = parseToolText(raw);
26
+ if (asJson) {
27
+ process.stdout.write(JSON.stringify(result) + "\n");
28
+ return;
29
+ }
30
+ console.log("Governance summary");
31
+ const review = result.reviewQueue ?? {};
32
+ console.log(` Review queue: ${review.pending_entities ?? 0} entities, ${review.pending_relationships ?? 0} relationships`);
33
+ console.log(` Graph: ${result.graphEntityCount ?? 0} entities, ${result.graphRelationshipCount ?? 0} relationships (${result.architectureFactCount ?? 0} facts)`);
34
+ console.log(` Canonical coverage: ${result.canonicalCoverageOverallPct ?? 0}% overall ` +
35
+ `(entities ${result.canonicalCoverageEntityPct ?? 0}%, relationships ${result.canonicalCoverageRelationshipPct ?? 0}%)`);
36
+ console.log(` Canonical registry size: ${result.canonicalRegistryCount ?? 0}`);
37
+ const audit = result.policyAudit;
38
+ if (audit) {
39
+ console.log("\nPolicy audit rollup");
40
+ console.log(` Applications: ${audit.totalApplications} (audited ${audit.appsAudited}, missing audit ${audit.appsWithoutAudit})`);
41
+ console.log(` With failures: ${audit.appsWithFailures}, with partials only: ${audit.appsWithPartials}, clean: ${audit.appsClean}`);
42
+ if (audit.applications.length > 0) {
43
+ console.log("\n Per application:");
44
+ for (const app of audit.applications) {
45
+ if (!app.latestRunId) {
46
+ console.log(` - ${app.applicationName} (${app.applicationEntityRef}): no audit run`);
47
+ continue;
48
+ }
49
+ console.log(` - ${app.applicationName} (${app.applicationEntityRef}): ${app.latestRunStatus} — ` +
50
+ `${app.passCount ?? 0} pass, ${app.partialCount ?? 0} partial, ${app.failCount ?? 0} fail` +
51
+ (app.totalRules ? ` of ${app.totalRules}` : "") +
52
+ (app.completedAt ? ` (completed ${app.completedAt})` : ""));
53
+ }
54
+ }
55
+ }
56
+ }
@@ -1625,11 +1625,17 @@ export async function initProject(args) {
1625
1625
  },
1626
1626
  });
1627
1627
  const preservedEntities = entitiesResult.preserved ?? [];
1628
+ const nothingWritten = (entitiesResult.summary?.succeeded ?? 0) === 0;
1629
+ const status = refreshMode && nothingWritten && preservedEntities.length > 0
1630
+ ? "review_enrichment"
1631
+ : "enrichment_required";
1628
1632
  return {
1629
- status: "enrichment_required",
1630
- note: refreshMode
1631
- ? "Execute ALL pendingSteps. In refresh mode, compare graphState vs scanState to identify gaps do not blindly remove relationships the scanner missed; the scanner cannot detect platforms, integrations, data assets, or application_functions."
1632
- : "Execute ALL pendingSteps before reporting the project as registered. Do not pause or ask for confirmation between steps.",
1633
+ status,
1634
+ note: status === "review_enrichment"
1635
+ ? "No new skeletons were written all entities were already enriched and preserved. Review the preservedEntities list and confirm scan values match. Update with nexarch update-entity only if something has genuinely changed."
1636
+ : refreshMode
1637
+ ? "Execute ALL pendingSteps. In refresh mode, compare graphState vs scanState to identify gaps — do not blindly remove relationships the scanner missed; the scanner cannot detect platforms, integrations, data assets, or application_functions."
1638
+ : "Execute ALL pendingSteps before reporting the project as registered. Do not pause or ask for confirmation between steps.",
1633
1639
  ...(refreshMode && preservedEntities.length > 0
1634
1640
  ? {
1635
1641
  preservedEntities: {
@@ -1842,9 +1848,10 @@ export async function initProject(args) {
1842
1848
  return lines.join("\n");
1843
1849
  }
1844
1850
  const enrichmentRequired = buildEnrichmentPayload();
1851
+ const outputStatus = enrichmentRequired.status;
1845
1852
  const output = {
1846
1853
  ok: Number(entitiesResult.summary?.failed ?? 0) === 0,
1847
- status: "enrichment_required",
1854
+ status: outputStatus,
1848
1855
  mode: refreshMode ? "refresh" : "init",
1849
1856
  project: { name: displayName, externalKey: projectExternalKey, entityType: entityTypeOverride, detectedEcosystems },
1850
1857
  entities: entitiesResult.summary ?? {},
@@ -1885,7 +1892,7 @@ export async function initProject(args) {
1885
1892
  const preservedCount = output.entities.preserved ?? 0;
1886
1893
  console.log(` Entities : ${output.entities.succeeded ?? 0} written, ${preservedCount > 0 ? `${preservedCount} preserved (enriched), ` : ""}${output.entities.failed ?? 0} failed`);
1887
1894
  console.log(` Relationships: ${output.relationships.succeeded ?? 0} written`);
1888
- console.log(" Status : skeleton created; enrichment pending");
1895
+ console.log(` Status : ${output.status === "review_enrichment" ? "all enriched; review preserved entities" : "skeleton created; enrichment pending"}`);
1889
1896
  if (output.metrics.relationshipsSkippedAsDuplicate > 0) {
1890
1897
  console.log(` Deduped rels : ${output.metrics.relationshipsSkippedAsDuplicate} skipped as duplicates before upsert`);
1891
1898
  }
@@ -0,0 +1,103 @@
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 policyAuditResults(args) {
21
+ const asJson = parseFlag(args, "--json");
22
+ if (parseFlag(args, "--help") || parseFlag(args, "-h")) {
23
+ console.log(`
24
+ Usage:
25
+ nexarch policy-audit-results --entity <applicationEntityRef> [--limit <1-10>] [--json]
26
+
27
+ Options:
28
+ --entity <key> Required application reference (e.g. application:my-service).
29
+ Aliases: --entity-ref, --application-ref, --application-key
30
+ --limit <n> Number of most recent runs to return (default 1, max 10)
31
+ --json Print JSON response
32
+ `);
33
+ return;
34
+ }
35
+ const entity = parseOptionValue(args, "--entity") ??
36
+ parseOptionValue(args, "--entity-ref") ??
37
+ parseOptionValue(args, "--application-ref") ??
38
+ parseOptionValue(args, "--application-key");
39
+ if (!entity) {
40
+ console.error("error: --entity <applicationEntityRef> is required (e.g. application:my-service)");
41
+ process.exit(1);
42
+ }
43
+ const limitRaw = parseOptionValue(args, "--limit");
44
+ const limit = limitRaw ? Number.parseInt(limitRaw, 10) : null;
45
+ if (limitRaw && (!Number.isFinite(limit) || (limit ?? 0) < 1)) {
46
+ console.error("error: --limit must be a positive integer");
47
+ process.exit(1);
48
+ }
49
+ const creds = requireCredentials();
50
+ const raw = await callMcpTool("nexarch_get_policy_audit_results", {
51
+ applicationEntityRef: entity,
52
+ ...(limit ? { limit } : {}),
53
+ companyId: creds.companyId,
54
+ }, { companyId: creds.companyId });
55
+ const result = parseToolText(raw);
56
+ if (asJson) {
57
+ process.stdout.write(JSON.stringify(result) + "\n");
58
+ return;
59
+ }
60
+ if (result.ok === false) {
61
+ console.error(`Policy audit results failed${result.error ? `: ${result.error}` : ""}`);
62
+ if (result.hint)
63
+ console.error(result.hint);
64
+ process.exit(1);
65
+ }
66
+ const label = result.applicationName
67
+ ? `${result.applicationName} (${result.applicationEntityRef ?? entity})`
68
+ : (result.applicationEntityRef ?? entity);
69
+ console.log(`Policy audit results for ${label}`);
70
+ const runs = result.runs ?? [];
71
+ if (runs.length === 0) {
72
+ console.log("No audit runs found.");
73
+ if (result.hint)
74
+ console.log(result.hint);
75
+ return;
76
+ }
77
+ for (const run of runs) {
78
+ const summary = run.summary ?? {};
79
+ console.log(`\nRun ${run.runId} — ${run.status}`);
80
+ if (run.startedAt)
81
+ console.log(` Started: ${run.startedAt}`);
82
+ if (run.completedAt)
83
+ console.log(` Completed: ${run.completedAt}`);
84
+ if (summary && (summary.passCount !== undefined || summary.failCount !== undefined)) {
85
+ console.log(` Summary: ${summary.passCount ?? 0} pass, ${summary.partialCount ?? 0} partial, ${summary.failCount ?? 0} fail` +
86
+ (summary.totalRules !== undefined ? ` (of ${summary.totalRules})` : ""));
87
+ }
88
+ for (const control of run.controls ?? []) {
89
+ console.log(`\n - ${control.controlName} (${control.controlId})`);
90
+ for (const rule of control.rules ?? []) {
91
+ const level = rule.requirementLevel ? ` [${rule.requirementLevel}]` : "";
92
+ console.log(` • [${rule.result}] ${rule.ruleName}${level} (${rule.ruleId})`);
93
+ if (rule.rationale)
94
+ console.log(` ${rule.rationale}`);
95
+ if (Array.isArray(rule.missingRequirements) && rule.missingRequirements.length > 0) {
96
+ for (const m of rule.missingRequirements) {
97
+ console.log(` - missing: ${String(m)}`);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
package/dist/index.js CHANGED
@@ -21,6 +21,9 @@ import { commandClaim } from "./commands/command-claim.js";
21
21
  import { policyControls } from "./commands/policy-controls.js";
22
22
  import { policyAuditTemplate } from "./commands/policy-audit-template.js";
23
23
  import { policyAuditSubmit } from "./commands/policy-audit-submit.js";
24
+ import { policyAuditResults } from "./commands/policy-audit-results.js";
25
+ import { appliedPolicies } from "./commands/applied-policies.js";
26
+ import { governanceSummary } from "./commands/governance-summary.js";
24
27
  const [, , command, ...args] = process.argv;
25
28
  const commands = {
26
29
  login,
@@ -46,6 +49,9 @@ const commands = {
46
49
  "policy-controls": policyControls,
47
50
  "policy-audit-template": policyAuditTemplate,
48
51
  "policy-audit-submit": policyAuditSubmit,
52
+ "policy-audit-results": policyAuditResults,
53
+ "applied-policies": appliedPolicies,
54
+ "governance-summary": governanceSummary,
49
55
  };
50
56
  async function main() {
51
57
  if (command === "agent") {
@@ -206,6 +212,20 @@ Usage:
206
212
  --findings-json <json-array>
207
213
  --findings-file <path.json>
208
214
  --json
215
+ nexarch policy-audit-results
216
+ Retrieve stored results of previous policy audits for an application.
217
+ Options: --entity <applicationEntityRef> (required)
218
+ --limit <1-10> (default 1)
219
+ --json
220
+ nexarch applied-policies
221
+ List policy documents applied to this company account.
222
+ Options: --pack <packCode> filter to a specific pack
223
+ --markdown include full document markdown
224
+ --json
225
+ nexarch governance-summary
226
+ Print review queue, graph stats, and per-application policy
227
+ audit rollup (latest run status, pass/partial/fail counts).
228
+ Options: --json
209
229
  `);
210
230
  process.exit(command ? 1 : 0);
211
231
  }
package/dist/lib/mcp.js CHANGED
@@ -1,6 +1,21 @@
1
1
  import https from "https";
2
+ import { readFileSync } from "fs";
3
+ import { fileURLToPath } from "url";
4
+ import { dirname, join } from "path";
2
5
  import { requireCredentials } from "./credentials.js";
3
6
  const MCP_GATEWAY_URL = "https://mcp.nexarch.ai";
7
+ function readCliVersion() {
8
+ try {
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const pkgPath = join(here, "..", "..", "package.json");
11
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
12
+ return pkg.version ?? "0.0.0";
13
+ }
14
+ catch {
15
+ return "0.0.0";
16
+ }
17
+ }
18
+ const CLI_VERSION = readCliVersion();
4
19
  const REQUEST_TIMEOUT_MS = Number.parseInt(process.env.NEXARCH_MCP_TIMEOUT_MS ?? "90000", 10) || 90_000;
5
20
  const REQUEST_RETRIES = Math.max(0, Number.parseInt(process.env.NEXARCH_MCP_RETRIES ?? "2", 10) || 2);
6
21
  function sleep(ms) {
@@ -97,7 +112,7 @@ export async function mcpInitialize(options = {}) {
97
112
  return callMcpRpc("initialize", {
98
113
  protocolVersion: "2024-11-05",
99
114
  capabilities: {},
100
- clientInfo: { name: "nexarch-cli", version: "0.5.12" },
115
+ clientInfo: { name: "nexarch-cli", version: CLI_VERSION },
101
116
  }, options);
102
117
  }
103
118
  export async function mcpListTools(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",