nexarch 0.13.5 → 0.13.8

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.
@@ -344,7 +344,8 @@ export function canonicalTargetKey(filePath) {
344
344
  * consent from the dry-run result before calling again with `dryRun: false`
345
345
  * to actually write — see the ADR-0112 note at the call site.
346
346
  */
347
- function injectAgentConfigs(registry, runtimeCodes, dryRun) {
347
+ /** Exported for regression coverage of the empty-return bug documented at its tail. */
348
+ export function injectAgentConfigs(registry, runtimeCodes, dryRun) {
348
349
  const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
349
350
  const sortedTargets = [...registry.instructionTargets]
350
351
  .filter((target) => target.matchMode === "exact")
@@ -437,7 +438,17 @@ function injectAgentConfigs(registry, runtimeCodes, dryRun) {
437
438
  if (perRuntimeResults.length > 0)
438
439
  return [...existingResults, ...perRuntimeResults.filter((result, index, arr) => arr.findIndex((item) => canonicalTargetKey(item.path) === canonicalTargetKey(result.path)) === index)];
439
440
  }
440
- return [];
441
+ // Not `[]`. When every runtime's target was already collected into
442
+ // `existingMatches` — which is the steady state once a repo has been set up
443
+ // once — the per-runtime loop `continue`s on all of them and
444
+ // `perRuntimeResults` comes back empty. Returning [] here threw away the
445
+ // results already computed above, the caller read that as "no instruction
446
+ // files" and fell back to injectGenericAgentConfig, and from then on only
447
+ // the generic target (AGENTS.md) was ever seen. Paul's CLAUDE.md and
448
+ // .cursorrules were invisible to staleness checking and attestation refresh
449
+ // from the second setup run onward, which is how they kept an expired
450
+ // `instruction_injection` block while the handshake reported all green.
451
+ return existingResults;
441
452
  }
442
453
  function trustAttestationStaleness(path) {
443
454
  if (!existsSync(path))
@@ -507,6 +518,75 @@ export function instructionInjectionDetail(registrationOk, staleAttestationTarge
507
518
  return `updated ${agentConfigResults.length} instruction target file(s)`;
508
519
  return "no runtime instruction target matched this repository (non-fatal; create AGENTS.md/CLAUDE.md or configure a generic target)";
509
520
  }
521
+ /**
522
+ * Every instruction file the registry recognises that actually exists in this
523
+ * repo, deduplicated the way the rest of this module keys paths.
524
+ *
525
+ * This is the denominator for the coverage guard below. It is deliberately
526
+ * computed straight from the registry and the filesystem rather than from
527
+ * whatever the injection path happened to return, because the bug it guards
528
+ * against was the injection path silently returning fewer files than exist.
529
+ */
530
+ export function existingInstructionFilePaths(registry) {
531
+ const seen = new Set();
532
+ const paths = [];
533
+ for (const target of registry.instructionTargets) {
534
+ if (target.matchMode !== "exact")
535
+ continue;
536
+ const filePath = join(process.cwd(), target.filePathPattern);
537
+ const key = canonicalTargetKey(filePath);
538
+ if (seen.has(key))
539
+ continue;
540
+ seen.add(key);
541
+ if (existsSync(filePath))
542
+ paths.push(filePath);
543
+ }
544
+ return paths;
545
+ }
546
+ /**
547
+ * Instruction files that exist on disk but were never evaluated for staleness.
548
+ *
549
+ * "already current (no refresh needed)" is only honest about files somebody
550
+ * looked at. When the injection path under-reports its targets, the attestation
551
+ * check evaluates a subset and reports green for the whole repo — which is
552
+ * exactly how two of Paul's three files kept an expired, wrongly-scoped block
553
+ * through a clean setup run. A non-empty result here fails the check rather
554
+ * than letting it speak for files it never opened.
555
+ */
556
+ export function unevaluatedInstructionFiles(onDisk, evaluated) {
557
+ const evaluatedKeys = new Set(evaluated.map((result) => canonicalTargetKey(result.path)));
558
+ return onDisk.filter((path) => !evaluatedKeys.has(canonicalTargetKey(path)));
559
+ }
560
+ /**
561
+ * Summarises the per-target attestation writes into the check's ok/detail.
562
+ *
563
+ * This used to be `trustAttestation?.ok`, which was the *first* target's mint
564
+ * result, reported under plural wording ("instruction file(s)") as if it
565
+ * covered them all. On Paul's 20 September setup run that printed a green
566
+ * "minted and injected into instruction file(s)" while only AGENTS.md was
567
+ * refreshed: CLAUDE.md and .cursorrules kept an attestation that had expired
568
+ * 22 minutes earlier, under the pre-ADR-0112 `instruction_injection` scope.
569
+ * The two agents that then read CLAUDE.md cold reported it as an injection
570
+ * attempt, which is the correct reading of what was in front of them.
571
+ *
572
+ * Green here now requires every target to have been verified on disk after
573
+ * writing, and a failure names the files rather than hiding behind a count.
574
+ */
575
+ export function summariseAttestationOutcomes(outcomes) {
576
+ if (outcomes.length === 0)
577
+ return { ok: true, detail: "" };
578
+ const failures = outcomes.filter((outcome) => !outcome.ok);
579
+ if (failures.length === 0) {
580
+ return { ok: true, detail: `minted and injected into ${outcomes.length} instruction file(s)` };
581
+ }
582
+ const named = failures
583
+ .map((failure) => `${basename(failure.path)} (${failure.reason ?? "unknown"})`)
584
+ .join(", ");
585
+ return {
586
+ ok: false,
587
+ detail: `${outcomes.length - failures.length}/${outcomes.length} instruction file(s) attested; failed: ${named}`,
588
+ };
589
+ }
510
590
  export function buildTrustAttestationSection(instructionFile, attestation) {
511
591
  if (!attestation.token || !attestation.payload)
512
592
  return null;
@@ -1292,6 +1372,7 @@ export async function initAgent(args) {
1292
1372
  let unsafeReattestationTargetCount = 0;
1293
1373
  let trustAttestation = null;
1294
1374
  let trustAttestationAttempted = false;
1375
+ const attestationOutcomes = [];
1295
1376
  if (registration.ok) {
1296
1377
  try {
1297
1378
  // Save identity so check-in can find the agent key
@@ -1385,7 +1466,9 @@ export async function initAgent(args) {
1385
1466
  // section can carry different text (different templateCode per
1386
1467
  // runtime), and the attestation has to bind to the exact bytes it
1387
1468
  // covers — see ADR-0112. `trustAttestation` keeps the first result for
1388
- // the JSON summary field; every target still gets its own token.
1469
+ // the JSON summary field; every target still gets its own token, and
1470
+ // every target's write is recorded and verified independently so one
1471
+ // success can no longer stand in for the batch.
1389
1472
  for (const r of attestationTargets) {
1390
1473
  let targetAttestation;
1391
1474
  try {
@@ -1397,18 +1480,38 @@ export async function initAgent(args) {
1397
1480
  }
1398
1481
  if (!trustAttestation)
1399
1482
  trustAttestation = targetAttestation;
1400
- try {
1401
- if (targetAttestation.ok) {
1402
- injectTrustAttestationBlock(r.path, targetAttestation);
1483
+ if (!targetAttestation.ok) {
1484
+ const reason = targetAttestation.reason ?? "unknown";
1485
+ try {
1486
+ injectTrustAttestationUnavailableBlock(r.path, reason);
1403
1487
  }
1404
- else {
1405
- injectTrustAttestationUnavailableBlock(r.path, targetAttestation.reason ?? "unknown");
1488
+ catch {
1489
+ // Best-effort notice only; the recorded outcome below is what the check reports.
1406
1490
  }
1491
+ attestationOutcomes.push({ path: r.path, ok: false, reason: `mint failed (${reason})` });
1492
+ continue;
1493
+ }
1494
+ try {
1495
+ injectTrustAttestationBlock(r.path, targetAttestation);
1407
1496
  injectInitProjectReportingContract(r.path);
1408
1497
  }
1409
- catch {
1410
- // non-fatal
1498
+ catch (error) {
1499
+ // Previously swallowed as "non-fatal", which is how a failed write
1500
+ // reported as a minted-and-injected success.
1501
+ attestationOutcomes.push({
1502
+ path: r.path,
1503
+ ok: false,
1504
+ reason: `write failed (${error instanceof Error ? error.message : "unknown"})`,
1505
+ });
1506
+ continue;
1411
1507
  }
1508
+ // Returning without throwing is not proof the block landed: a replace
1509
+ // that matched nothing writes the file unchanged. Re-read and confirm
1510
+ // the file now carries a current, correctly-scoped attestation.
1511
+ const staleness = trustAttestationStaleness(r.path);
1512
+ attestationOutcomes.push(staleness
1513
+ ? { path: r.path, ok: false, reason: `write did not take (${staleness})` }
1514
+ : { path: r.path, ok: true });
1412
1515
  }
1413
1516
  }
1414
1517
  }
@@ -1428,32 +1531,40 @@ export async function initAgent(args) {
1428
1531
  : identityCapture.detail,
1429
1532
  });
1430
1533
  const instructionsAlreadyConfigured = agentConfigResults.length > 0 && agentConfigResults.every((r) => r.status === "already_present");
1534
+ const uncoveredInstructionFiles = registration.ok
1535
+ ? unevaluatedInstructionFiles(existingInstructionFilePaths(registry), agentConfigResults)
1536
+ : [];
1537
+ const attestationSummary = summariseAttestationOutcomes(attestationOutcomes);
1538
+ /** Renewed means every attested target verified on disk, not that one mint succeeded. */
1539
+ const attestationRenewed = attestationOutcomes.length > 0 && attestationSummary.ok;
1431
1540
  checks.push({
1432
1541
  name: "agent.instructions.injection",
1433
1542
  ok: !registration.ok || unsafeReattestationTargetCount === 0 && (!instructionsWriteAllowed || agentConfigResults.length > 0),
1434
- detail: instructionInjectionDetail(registration.ok, staleAttestationTargetCount, mismatchedAttestationTargetCount, unsafeReattestationTargetCount, instructionsWriteAllowed, Boolean(trustAttestation?.ok), instructionsAlreadyConfigured, agentConfigResults),
1543
+ detail: instructionInjectionDetail(registration.ok, staleAttestationTargetCount, mismatchedAttestationTargetCount, unsafeReattestationTargetCount, instructionsWriteAllowed, attestationRenewed, instructionsAlreadyConfigured, agentConfigResults),
1435
1544
  });
1436
1545
  checks.push({
1437
1546
  name: "agent.trust.attestation",
1438
1547
  ok: !registration.ok || (unsafeReattestationTargetCount === 0
1548
+ && uncoveredInstructionFiles.length === 0
1549
+ && attestationSummary.ok
1439
1550
  && ((staleAttestationTargetCount === 0 && !trustAttestationAttempted) || Boolean(trustAttestation?.ok))),
1440
1551
  detail: !registration.ok
1441
1552
  ? "skipped (registration failed)"
1442
- : unsafeReattestationTargetCount > 0
1443
- ? "managed section does not match the published template; re-run with --allow-instruction-write"
1444
- : staleAttestationTargetCount > 0 && !attestationWriteAllowed
1445
- ? `${mismatchedAttestationTargetCount > 0 ? "mismatched" : "stale"}; re-run with --reattest`
1446
- : instructionsAlreadyConfigured && !attestationWriteAllowed
1447
- ? "skipped (already configured)"
1448
- : !attestationWriteAllowed
1449
- ? "skipped (consent not granted)"
1450
- : !trustAttestationAttempted
1451
- ? agentConfigResults.length > 0
1452
- ? "already current (no refresh needed)"
1453
- : "skipped (no instruction target written)"
1454
- : trustAttestation?.ok
1455
- ? "minted and injected into instruction file(s)"
1456
- : `unavailable (${trustAttestation?.reason ?? "unknown"})`,
1553
+ : uncoveredInstructionFiles.length > 0
1554
+ ? `${uncoveredInstructionFiles.length} instruction file(s) on disk were never evaluated (${uncoveredInstructionFiles.map((path) => basename(path)).join(", ")}); their attestations are unverified`
1555
+ : unsafeReattestationTargetCount > 0
1556
+ ? "managed section does not match the published template; re-run with --allow-instruction-write"
1557
+ : staleAttestationTargetCount > 0 && !attestationWriteAllowed
1558
+ ? `${mismatchedAttestationTargetCount > 0 ? "mismatched" : "stale"}; re-run with --reattest`
1559
+ : instructionsAlreadyConfigured && !attestationWriteAllowed
1560
+ ? "skipped (already configured)"
1561
+ : !attestationWriteAllowed
1562
+ ? "skipped (consent not granted)"
1563
+ : !trustAttestationAttempted
1564
+ ? agentConfigResults.length > 0
1565
+ ? "already current (no refresh needed)"
1566
+ : "skipped (no instruction target written)"
1567
+ : attestationSummary.detail,
1457
1568
  });
1458
1569
  checks.push({
1459
1570
  name: "technology.components",
@@ -1537,6 +1648,10 @@ export async function initAgent(args) {
1537
1648
  }
1538
1649
  : null,
1539
1650
  trustAttestation,
1651
+ // Per-target truth. `trustAttestation` above is only the first target's
1652
+ // mint result, kept for back-compat; this is what says whether each
1653
+ // instruction file actually ends up carrying a current attestation.
1654
+ trustAttestationTargets: attestationOutcomes,
1540
1655
  companyId: creds.companyId,
1541
1656
  registry: { version: registry.release.version, registryVersion: registry.registryVersion, publishedAt: registry.release.publishedAt },
1542
1657
  agentConfigs: agentConfigResults,
@@ -91,13 +91,20 @@ Options:
91
91
  console.log(` State: ${run.auditState} — required rules ${requiredPct} pass` +
92
92
  (required ? ` (${required.pass ?? 0} of ${required.total ?? 0}, ${required.partial ?? 0} partial, ${required.fail ?? 0} fail)` : "") +
93
93
  `; advisory findings: ${run.advisoryIssueCount ?? 0} (do not affect state)`);
94
+ if ((run.criticalIssueCount ?? 0) > 0 || (run.highIssueCount ?? 0) > 0) {
95
+ console.log(` Severity: ${run.criticalIssueCount ?? 0} critical, ${run.highIssueCount ?? 0} high open finding(s)`);
96
+ }
97
+ if ((run.unscoredSeverityCount ?? 0) > 0) {
98
+ console.log(` Unrated: ${run.unscoredSeverityCount} finding(s) have no severity assigned`);
99
+ }
94
100
  }
95
101
  for (const control of run.controls ?? []) {
96
102
  console.log(`\n - ${control.controlName} (${control.controlId})`);
97
103
  for (const rule of control.rules ?? []) {
98
104
  const qualifier = rule.requirementQualifier ? ` ${rule.requirementQualifier}` : "";
99
105
  const level = rule.requirementLevel ? ` [${rule.requirementLevel}${qualifier}]` : "";
100
- console.log(` • [${rule.result}] ${rule.ruleName}${level} (${rule.ruleId})`);
106
+ const ranking = [rule.severity, rule.effort, rule.exploitability].filter(Boolean).join(", ");
107
+ console.log(` • [${rule.result}] ${rule.ruleName}${level} (${rule.ruleId})${ranking ? ` {${ranking}}` : ""}`);
101
108
  if (rule.rationale)
102
109
  console.log(` ${rule.rationale}`);
103
110
  if (Array.isArray(rule.missingRequirements) && rule.missingRequirements.length > 0) {
@@ -105,6 +112,12 @@ Options:
105
112
  console.log(` - missing: ${String(m)}`);
106
113
  }
107
114
  }
115
+ if (Array.isArray(rule.fileRefs) && rule.fileRefs.length > 0) {
116
+ for (const ref of rule.fileRefs) {
117
+ const loc = ref.line !== undefined && ref.line !== null ? `${ref.path}:${ref.line}` : ref.path;
118
+ console.log(` - ${loc}${ref.note ? ` — ${ref.note}` : ""}`);
119
+ }
120
+ }
108
121
  }
109
122
  }
110
123
  }
@@ -44,21 +44,36 @@ function loadIdentityAgentKey() {
44
44
  return null;
45
45
  }
46
46
  }
47
+ function isSeverity(value) {
48
+ return value === "low" || value === "medium" || value === "high" || value === "critical";
49
+ }
50
+ function isEffort(value) {
51
+ return value === "hours" || value === "days" || value === "weeks";
52
+ }
53
+ function isExploitability(value) {
54
+ return value === "theoretical" || value === "plausible" || value === "confirmed";
55
+ }
47
56
  function parseFindingToken(token) {
48
- const [policyControlId, policyRuleId, resultRaw, rationaleRaw, missingRaw] = token.split("|");
57
+ const [policyControlId, policyRuleId, resultRaw, rationaleRaw, missingRaw, severityRaw] = token.split("|");
49
58
  const result = (resultRaw ?? "").trim().toLowerCase();
50
59
  if (!policyControlId?.trim() || !policyRuleId?.trim() || !result) {
51
- throw new Error("Each --finding must be: <policyControlId>|<policyRuleId>|<pass|partial|fail>|<rationale>|<missing1;missing2>");
60
+ throw new Error("Each --finding must be: <policyControlId>|<policyRuleId>|<pass|partial|fail>|<rationale>|<missing1;missing2>|<severity>");
52
61
  }
53
62
  if (result !== "pass" && result !== "partial" && result !== "fail") {
54
63
  throw new Error(`Invalid finding result '${resultRaw}'. Use pass|partial|fail.`);
55
64
  }
65
+ const severityInput = severityRaw?.trim().toLowerCase();
66
+ if (severityInput && !isSeverity(severityInput)) {
67
+ throw new Error(`Invalid severity '${severityRaw}'. Use low|medium|high|critical.`);
68
+ }
69
+ const severity = severityInput && isSeverity(severityInput) ? severityInput : undefined;
56
70
  return {
57
71
  policyControlId: policyControlId.trim(),
58
72
  policyRuleId: policyRuleId.trim(),
59
73
  result,
60
74
  ...(rationaleRaw?.trim() ? { rationale: rationaleRaw.trim() } : {}),
61
75
  ...(missingRaw?.trim() ? { missingRequirements: missingRaw.split(";").map((m) => m.trim()).filter(Boolean) } : {}),
76
+ ...(severity ? { severity } : {}),
62
77
  };
63
78
  }
64
79
  function parseFindings(args) {
@@ -100,12 +115,26 @@ function parseFindings(args) {
100
115
  : Array.isArray(value.missing_requirements)
101
116
  ? value.missing_requirements.map(String)
102
117
  : undefined;
118
+ const severity = isSeverity(value.severity) ? value.severity : undefined;
119
+ const effort = isEffort(value.effort) ? value.effort : undefined;
120
+ const exploitability = isExploitability(value.exploitability) ? value.exploitability : undefined;
121
+ const confidence = typeof value.confidence === "number" ? value.confidence : undefined;
122
+ const fileRefs = Array.isArray(value.fileRefs)
123
+ ? value.fileRefs
124
+ : Array.isArray(value.file_refs)
125
+ ? value.file_refs
126
+ : undefined;
103
127
  return {
104
128
  policyControlId,
105
129
  policyRuleId,
106
130
  result,
107
131
  ...(rationale ? { rationale } : {}),
108
132
  ...(missingRequirements ? { missingRequirements } : {}),
133
+ ...(severity ? { severity } : {}),
134
+ ...(effort ? { effort } : {}),
135
+ ...(exploitability ? { exploitability } : {}),
136
+ ...(confidence !== undefined ? { confidence } : {}),
137
+ ...(fileRefs ? { fileRefs } : {}),
109
138
  };
110
139
  });
111
140
  }
@@ -126,7 +155,7 @@ Options:
126
155
  --command-id <id> Required command id
127
156
  --application-ref <key> Required application reference key (e.g. application:bad-driving)
128
157
  --agent-key <key> Optional agent key (defaults from identity)
129
- --finding <controlId|ruleId|result|rationale|missing1;missing2> Repeatable
158
+ --finding <controlId|ruleId|result|rationale|missing1;missing2|severity> Repeatable
130
159
  --findings-json <json> JSON array of findings
131
160
  --findings-file <path> Path to JSON array of findings
132
161
  --json Print JSON response
@@ -135,6 +164,12 @@ Notes:
135
164
  - Findings are rule-level (policyRuleId is required).
136
165
  - You can submit partial findings multiple times for the same command.
137
166
  - Get valid rule ids with: nexarch policy-controls --entity <application:key> --json
167
+ - Rate severity (low|medium|high|critical), effort (hours|days|weeks) and
168
+ exploitability (theoretical|plausible|confirmed) per finding where you can —
169
+ these decide how findings get ranked. --finding's severity is its 6th field;
170
+ --findings-json/--findings-file also accept effort, exploitability,
171
+ confidence (0-1) and fileRefs ([{path, line, note}]) per finding.
172
+ Omit rather than guess.
138
173
  `);
139
174
  return;
140
175
  }
@@ -180,6 +215,12 @@ Notes:
180
215
  console.log(`Required rules: ${pct} pass, ${result.summary.requiredIssueCount} issue(s) — these decide the audit state`);
181
216
  console.log(`Advisory findings: ${result.summary.advisoryIssueCount ?? 0} (recommended/informational; recorded, not gating)`);
182
217
  }
218
+ if ((result.summary.criticalIssueCount ?? 0) > 0 || (result.summary.highIssueCount ?? 0) > 0) {
219
+ console.log(`Severity: ${result.summary.criticalIssueCount ?? 0} critical, ${result.summary.highIssueCount ?? 0} high (open findings rated by the submitting agent)`);
220
+ }
221
+ if ((result.summary.unscoredSeverityCount ?? 0) > 0) {
222
+ console.log(`Unrated for severity: ${result.summary.unscoredSeverityCount} finding(s) — consider adding severity so this run can be ranked`);
223
+ }
183
224
  if ((result.summary.remainingRules ?? 0) > 0) {
184
225
  console.log(`Remaining rules to submit: ${result.summary.remainingRules}`);
185
226
  }
@@ -37,19 +37,24 @@ export async function policyAuditTemplate(args) {
37
37
  const defaultResult = (parseOptionValue(args, "--default-result") ?? "fail").toLowerCase();
38
38
  const selectedControlIds = new Set(parseMultiOptionValues(args, "--control-id"));
39
39
  if (parseFlag(args, "--help") || parseFlag(args, "-h")) {
40
- console.log(`
41
- Usage:
42
- nexarch policy-audit-template --entity <application:key> [options]
43
-
44
- Options:
45
- --entity <key> Required (e.g. application:bad-driving)
46
- --control-id <uuid> Optional repeatable filter (selected controls only)
47
- --default-result <value> pass|partial|fail (default: fail)
48
- --output, --out <path.json> Write findings array to file
49
- --json Print JSON to stdout
50
-
51
- Output shape is ready for:
52
- nexarch policy-audit-submit --findings-file <path.json>
40
+ console.log(`
41
+ Usage:
42
+ nexarch policy-audit-template --entity <application:key> [options]
43
+
44
+ Options:
45
+ --entity <key> Required (e.g. application:bad-driving)
46
+ --control-id <uuid> Optional repeatable filter (selected controls only)
47
+ --default-result <value> pass|partial|fail (default: fail)
48
+ --output, --out <path.json> Write findings array to file
49
+ --json Print JSON to stdout
50
+
51
+ Output shape is ready for:
52
+ nexarch policy-audit-submit --findings-file <path.json>
53
+
54
+ Each entry includes empty severity/effort/exploitability/confidence/fileRefs
55
+ placeholders. Fill them in for every partial/fail finding -- they decide how
56
+ findings get ranked and reported. Leave a field blank/empty rather than
57
+ guess; policy-audit-submit drops anything that isn't a valid value.
53
58
  `);
54
59
  return;
55
60
  }
@@ -76,6 +81,13 @@ Output shape is ready for:
76
81
  result: defaultResult,
77
82
  rationale: "",
78
83
  missingRequirements: [],
84
+ // Fill in for partial/fail findings -- see --help. Left blank rather
85
+ // than defaulted, so an unrated finding stays honestly unrated.
86
+ severity: "",
87
+ effort: "",
88
+ exploitability: "",
89
+ confidence: null,
90
+ fileRefs: [],
79
91
  })));
80
92
  if (findings.length === 0) {
81
93
  console.error("error: no rules found for selected controls/entity");
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
- {
2
- "name": "nexarch",
3
- "version": "0.13.5",
4
- "description": "Your architecture workspace for AI delivery.",
5
- "keywords": [
6
- "nexarch",
7
- "mcp",
8
- "architecture",
9
- "ai"
10
- ],
11
- "license": "MIT",
12
- "author": "Nexarch <hello@nexarch.ai>",
13
- "homepage": "https://nexarch.ai",
14
- "engines": {
15
- "node": ">=18"
16
- },
17
- "type": "module",
18
- "bin": {
19
- "nexarch": "dist/index.js"
20
- },
21
- "files": [
22
- "dist"
23
- ],
24
- "scripts": {
25
- "build": "tsc",
26
- "prepublishOnly": "tsc",
27
- "dev": "tsx src/index.ts",
28
- "typecheck": "tsc --noEmit",
29
- "test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts && tsx scripts/test-search-icons.ts && tsx scripts/test-paste-login.ts"
30
- },
31
- "devDependencies": {
32
- "@types/node": "^22",
33
- "tsx": "^4",
34
- "typescript": "^5"
35
- },
36
- "dependencies": {
37
- "yaml": "^2.9.0"
38
- }
39
- }
1
+ {
2
+ "name": "nexarch",
3
+ "version": "0.13.8",
4
+ "description": "Your architecture workspace for AI delivery.",
5
+ "keywords": [
6
+ "nexarch",
7
+ "mcp",
8
+ "architecture",
9
+ "ai"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "Nexarch <hello@nexarch.ai>",
13
+ "homepage": "https://nexarch.ai",
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "type": "module",
18
+ "bin": {
19
+ "nexarch": "dist/index.js"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "prepublishOnly": "tsc",
27
+ "dev": "tsx src/index.ts",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "tsx scripts/test-mcp-proxy-response.ts && tsx scripts/test-trust-reattest.ts && tsx scripts/test-similar-applications.ts && tsx scripts/test-iac-tool.ts && tsx scripts/test-ansible.ts && tsx scripts/test-reference-sightings.ts && tsx scripts/test-search-icons.ts && tsx scripts/test-paste-login.ts"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22",
33
+ "tsx": "^4",
34
+ "typescript": "^5"
35
+ },
36
+ "dependencies": {
37
+ "yaml": "^2.9.0"
38
+ }
39
+ }