nexarch 0.13.7 → 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.
- package/dist/commands/init-agent.js +71 -15
- package/dist/commands/policy-audit-template.js +18 -18
- package/package.json +39 -39
|
@@ -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
|
-
|
|
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
|
-
|
|
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,45 @@ 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
|
+
}
|
|
510
560
|
/**
|
|
511
561
|
* Summarises the per-target attestation writes into the check's ok/detail.
|
|
512
562
|
*
|
|
@@ -1481,6 +1531,9 @@ export async function initAgent(args) {
|
|
|
1481
1531
|
: identityCapture.detail,
|
|
1482
1532
|
});
|
|
1483
1533
|
const instructionsAlreadyConfigured = agentConfigResults.length > 0 && agentConfigResults.every((r) => r.status === "already_present");
|
|
1534
|
+
const uncoveredInstructionFiles = registration.ok
|
|
1535
|
+
? unevaluatedInstructionFiles(existingInstructionFilePaths(registry), agentConfigResults)
|
|
1536
|
+
: [];
|
|
1484
1537
|
const attestationSummary = summariseAttestationOutcomes(attestationOutcomes);
|
|
1485
1538
|
/** Renewed means every attested target verified on disk, not that one mint succeeded. */
|
|
1486
1539
|
const attestationRenewed = attestationOutcomes.length > 0 && attestationSummary.ok;
|
|
@@ -1492,23 +1545,26 @@ export async function initAgent(args) {
|
|
|
1492
1545
|
checks.push({
|
|
1493
1546
|
name: "agent.trust.attestation",
|
|
1494
1547
|
ok: !registration.ok || (unsafeReattestationTargetCount === 0
|
|
1548
|
+
&& uncoveredInstructionFiles.length === 0
|
|
1495
1549
|
&& attestationSummary.ok
|
|
1496
1550
|
&& ((staleAttestationTargetCount === 0 && !trustAttestationAttempted) || Boolean(trustAttestation?.ok))),
|
|
1497
1551
|
detail: !registration.ok
|
|
1498
1552
|
? "skipped (registration failed)"
|
|
1499
|
-
:
|
|
1500
|
-
?
|
|
1501
|
-
:
|
|
1502
|
-
?
|
|
1503
|
-
:
|
|
1504
|
-
? "
|
|
1505
|
-
: !attestationWriteAllowed
|
|
1506
|
-
? "skipped (
|
|
1507
|
-
: !
|
|
1508
|
-
?
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
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,
|
|
1512
1568
|
});
|
|
1513
1569
|
checks.push({
|
|
1514
1570
|
name: "technology.components",
|
|
@@ -37,24 +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>
|
|
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.
|
|
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.
|
|
58
58
|
`);
|
|
59
59
|
return;
|
|
60
60
|
}
|
package/package.json
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nexarch",
|
|
3
|
-
"version": "0.13.
|
|
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
|
+
}
|