@memnexus-ai/mx-agent-cli 0.1.209 → 0.1.210
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/__tests__/claude-sync.test.js +77 -5
- package/dist/__tests__/claude-sync.test.js.map +1 -1
- package/dist/__tests__/config-generation-gate.test.js +18 -9
- package/dist/__tests__/config-generation-gate.test.js.map +1 -1
- package/dist/__tests__/generate-claude.test.js +90 -6
- package/dist/__tests__/generate-claude.test.js.map +1 -1
- package/dist/__tests__/security-posture.test.d.ts +8 -0
- package/dist/__tests__/security-posture.test.d.ts.map +1 -0
- package/dist/__tests__/security-posture.test.js +82 -0
- package/dist/__tests__/security-posture.test.js.map +1 -0
- package/dist/__tests__/start-report-generation-capture.test.d.ts +17 -0
- package/dist/__tests__/start-report-generation-capture.test.d.ts.map +1 -0
- package/dist/__tests__/start-report-generation-capture.test.js +127 -0
- package/dist/__tests__/start-report-generation-capture.test.js.map +1 -0
- package/dist/agent-config/settings.json +3 -3
- package/dist/lib/claude.d.ts.map +1 -1
- package/dist/lib/claude.js +31 -6
- package/dist/lib/claude.js.map +1 -1
- package/dist/lib/security-posture.d.ts +69 -0
- package/dist/lib/security-posture.d.ts.map +1 -0
- package/dist/lib/security-posture.js +102 -0
- package/dist/lib/security-posture.js.map +1 -0
- package/package.json +1 -1
- package/scripts/generate-claude.mjs +45 -1
- package/dist/__tests__/config-mirror-integrity.test.d.ts +0 -32
- package/dist/__tests__/config-mirror-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/config-mirror-integrity.test.js +0 -248
- package/dist/__tests__/config-mirror-integrity.test.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-posture.js","sourceRoot":"","sources":["../../src/lib/security-posture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,uBAAuB;IACvB,kBAAkB;IAClB,sBAAsB;IACtB,mBAAmB;CACX,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsB;IACxD,uBAAuB;IACvB,kBAAkB;CACnB,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,sBAAsB,GACjC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAEhD,wFAAwF;AACxF,MAAM,4BAA4B,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAU,CAAC;AAEhF;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAA2B;IACjE,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAC3B,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAChE,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA2B,EAAE,IAAY;IAC5E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,iCAAiC,CAAC;IAC3C,CAAC;IACD,MAAM,IAAI,GAAI,MAAsD,EAAE,WAAW,EAAE,IAAI,CAAC;IACxF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO,sCAAsC,CAAC;IAChD,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,oBAAoB,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,qCAAqC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/package.json
CHANGED
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
renameSync, rmSync, unlinkSync, writeFileSync,
|
|
69
69
|
} from 'fs';
|
|
70
70
|
import { homedir, tmpdir } from 'os';
|
|
71
|
-
import { dirname, join, relative, resolve, sep } from 'path';
|
|
71
|
+
import { basename, dirname, join, relative, resolve, sep } from 'path';
|
|
72
72
|
import { pathToFileURL } from 'url';
|
|
73
73
|
import { spawnSync } from 'child_process';
|
|
74
74
|
import { createHash } from 'crypto';
|
|
@@ -189,6 +189,46 @@ function pruneExtras(destDir, expected) {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
// ── ORPHAN-SIBLING SWEEP (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED) ───────────────
|
|
193
|
+
//
|
|
194
|
+
// A crash mid-generation can only ever leave a `.claude.staging-*` sibling
|
|
195
|
+
// behind, and a failed prev-cleanup can leave a `.claude.prev-*` sibling — stale
|
|
196
|
+
// settings.json + hooks copies at an unguarded path (same-UID residual, not an
|
|
197
|
+
// escalation). Sweep any such orphan siblings of destRoot at generation ENTRY,
|
|
198
|
+
// before a new staging dir is created, so debris never accumulates across runs.
|
|
199
|
+
//
|
|
200
|
+
// Single-writer assumption: generation is serialized per worktree by the leader
|
|
201
|
+
// lock, so any staging/prev sibling present at entry is orphaned from a prior
|
|
202
|
+
// crashed run, never an in-flight concurrent generation. Best-effort per entry
|
|
203
|
+
// (an un-removable orphan never blocks generation). FAILS SAFE on a symlink
|
|
204
|
+
// sibling: unlink the LINK itself, never rmSync THROUGH it (a crafted link could
|
|
205
|
+
// otherwise delete outside the parent).
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Remove stale `.claude.staging-*` / `.claude.prev-*` siblings of `destRoot`.
|
|
209
|
+
* @param destRoot absolute path to the `.claude/` dir about to be generated
|
|
210
|
+
* @returns names of the siblings that were swept (for observability/tests)
|
|
211
|
+
*/
|
|
212
|
+
export function sweepOrphanSiblings(destRoot) {
|
|
213
|
+
const dest = resolve(destRoot);
|
|
214
|
+
const parent = dirname(dest);
|
|
215
|
+
const base = basename(dest);
|
|
216
|
+
const stagingPrefix = `${base}.staging-`;
|
|
217
|
+
const prevPrefix = `${base}.prev-`;
|
|
218
|
+
const swept = [];
|
|
219
|
+
for (const ent of listDir(parent)) {
|
|
220
|
+
if (!ent.name.startsWith(stagingPrefix) && !ent.name.startsWith(prevPrefix)) continue;
|
|
221
|
+
const p = join(parent, ent.name);
|
|
222
|
+
if (ent.isSymbolicLink()) {
|
|
223
|
+
// Never follow/delete THROUGH a link — remove only the link entry itself.
|
|
224
|
+
try { unlinkSync(p); swept.push(ent.name); } catch { /* best-effort */ }
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
try { rmSync(p, { recursive: true, force: true }); swept.push(ent.name); } catch { /* best-effort */ }
|
|
228
|
+
}
|
|
229
|
+
return swept;
|
|
230
|
+
}
|
|
231
|
+
|
|
192
232
|
// ── GENERATE-TO-STAGING + ATOMIC SWAP (Phase 4 prereq; Red Team finding 1) ────
|
|
193
233
|
//
|
|
194
234
|
// Generation writes the WHOLE tree into a staging dir (a sibling of destRoot so
|
|
@@ -488,6 +528,10 @@ export function generateClaudeConfig(opts) {
|
|
|
488
528
|
// reach the write phase); the existing dest is guarded separately at swap time.
|
|
489
529
|
const parent = dirname(destRoot);
|
|
490
530
|
mkdirSync(parent, { recursive: true });
|
|
531
|
+
// Sweep stale staging/prev siblings from a prior crashed run BEFORE creating a
|
|
532
|
+
// fresh staging dir (KI-GENERATOR-SWAP-SIBLINGS-UNGUARDED). The just-created
|
|
533
|
+
// staging dir below cannot be a sweep target: it does not exist yet.
|
|
534
|
+
sweepOrphanSiblings(destRoot);
|
|
491
535
|
const staging = mkdtempSync(join(parent, '.claude.staging-'));
|
|
492
536
|
|
|
493
537
|
try {
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mirror-drift guard for the agent-config → .claude config bundle.
|
|
3
|
-
*
|
|
4
|
-
* The repo keeps two copies of certain hook/skill/agent files:
|
|
5
|
-
* - mx-agent-system/agent-config/* — the source of truth that ships in the
|
|
6
|
-
* CLI's synced bundle (what `mx-agent` writes into new worktrees).
|
|
7
|
-
* - .claude/* — the copy that actually runs in this
|
|
8
|
-
* repo's own worktrees.
|
|
9
|
-
*
|
|
10
|
-
* These were hand-synced three separate times on 2026-07-02 (issue #3728,
|
|
11
|
-
* known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
|
|
12
|
-
* silently runs stale hook or skill logic. This test asserts byte-identity of
|
|
13
|
-
* the known mirror pairs so drift fails locally before it ships.
|
|
14
|
-
*
|
|
15
|
-
* ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
|
|
16
|
-
* This entire file — both the MIRROR_PAIRS byte-identity suite and the
|
|
17
|
-
* `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
|
|
18
|
-
* only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
|
|
19
|
-
* gitignores `.claude/`, deleting the second committed copy; at that point there
|
|
20
|
-
* is nothing to compare and these assertions become vacuous (every pair SKIPs on
|
|
21
|
-
* the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
|
|
22
|
-
* suite short-circuits on `bothPresent`).
|
|
23
|
-
*
|
|
24
|
-
* DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
|
|
25
|
-
* generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
|
|
26
|
-
* which proves the single remaining source generates correct, stable output —
|
|
27
|
-
* see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
|
|
28
|
-
* KEEP these assertions: they are the live guard against a PR editing one side of
|
|
29
|
-
* the mirror while `.claude/` is still tracked.
|
|
30
|
-
*/
|
|
31
|
-
export {};
|
|
32
|
-
//# sourceMappingURL=config-mirror-integrity.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-mirror-integrity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG"}
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mirror-drift guard for the agent-config → .claude config bundle.
|
|
3
|
-
*
|
|
4
|
-
* The repo keeps two copies of certain hook/skill/agent files:
|
|
5
|
-
* - mx-agent-system/agent-config/* — the source of truth that ships in the
|
|
6
|
-
* CLI's synced bundle (what `mx-agent` writes into new worktrees).
|
|
7
|
-
* - .claude/* — the copy that actually runs in this
|
|
8
|
-
* repo's own worktrees.
|
|
9
|
-
*
|
|
10
|
-
* These were hand-synced three separate times on 2026-07-02 (issue #3728,
|
|
11
|
-
* known-issues v68 / KI-HOOK-MIRROR-DRIFT-CHECK). When they drift, a worktree
|
|
12
|
-
* silently runs stale hook or skill logic. This test asserts byte-identity of
|
|
13
|
-
* the known mirror pairs so drift fails locally before it ships.
|
|
14
|
-
*
|
|
15
|
-
* ── PHASE 4 RETIREMENT (config-single-source migration #4304) ────────────────
|
|
16
|
-
* This entire file — both the MIRROR_PAIRS byte-identity suite and the
|
|
17
|
-
* `settings.json permissions parity` suite — is a TWO-COPY drift gate. It exists
|
|
18
|
-
* only because `.claude/` is still COMMITTED alongside `agent-config/`. Phase 4
|
|
19
|
-
* gitignores `.claude/`, deleting the second committed copy; at that point there
|
|
20
|
-
* is nothing to compare and these assertions become vacuous (every pair SKIPs on
|
|
21
|
-
* the `existsSync(claudeAbs)` guard once `.claude/` is untracked, and the parity
|
|
22
|
-
* suite short-circuits on `bothPresent`).
|
|
23
|
-
*
|
|
24
|
-
* DELETE this file in the Phase 4 PR. Its guarantee is replaced by the
|
|
25
|
-
* generation-correctness gate in `config-generation-gate.test.ts` (Phase 3),
|
|
26
|
-
* which proves the single remaining source generates correct, stable output —
|
|
27
|
-
* see that file's header for the kept-vs-added rationale. Until Phase 4 lands,
|
|
28
|
-
* KEEP these assertions: they are the live guard against a PR editing one side of
|
|
29
|
-
* the mirror while `.claude/` is still tracked.
|
|
30
|
-
*/
|
|
31
|
-
import { describe, it, expect } from 'vitest';
|
|
32
|
-
import { readFileSync, existsSync } from 'fs';
|
|
33
|
-
import { dirname, join } from 'path';
|
|
34
|
-
import { fileURLToPath } from 'url';
|
|
35
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
36
|
-
/**
|
|
37
|
-
* Ascend from this test file to the repo root — identified by the presence of
|
|
38
|
-
* the mx-agent-system/agent-config tree — instead of hard-coding a fixed number
|
|
39
|
-
* of `..` segments. Keeps the test correct if the file is ever relocated.
|
|
40
|
-
*/
|
|
41
|
-
function findRepoRoot(start) {
|
|
42
|
-
let dir = start;
|
|
43
|
-
for (let i = 0; i < 20; i++) {
|
|
44
|
-
if (existsSync(join(dir, 'mx-agent-system', 'agent-config')))
|
|
45
|
-
return dir;
|
|
46
|
-
const parent = dirname(dir);
|
|
47
|
-
if (parent === dir)
|
|
48
|
-
break;
|
|
49
|
-
dir = parent;
|
|
50
|
-
}
|
|
51
|
-
throw new Error(`config-mirror-integrity: could not locate repo root from ${start}`);
|
|
52
|
-
}
|
|
53
|
-
const REPO_ROOT = findRepoRoot(__dirname);
|
|
54
|
-
// [ .claude runtime copy, agent-config source of truth ] — repo-root-relative.
|
|
55
|
-
const MIRROR_PAIRS = [
|
|
56
|
-
[
|
|
57
|
-
'.claude/hooks/deploy-verification-reminder.sh',
|
|
58
|
-
'mx-agent-system/agent-config/hooks/deploy-verification-reminder.sh',
|
|
59
|
-
],
|
|
60
|
-
[
|
|
61
|
-
'.claude/hooks/deploy-verification-reminder.test.sh',
|
|
62
|
-
'mx-agent-system/agent-config/hooks/deploy-verification-reminder.test.sh',
|
|
63
|
-
],
|
|
64
|
-
// Shared git guards + push validators (#3912). These were mirror-drifting
|
|
65
|
-
// silently (git-mutation-guard.sh wasn't even present in the source-of-truth
|
|
66
|
-
// copy). Enforce byte-identity so a hook fix lands in both places.
|
|
67
|
-
[
|
|
68
|
-
'.claude/hooks/git-mutation-guard.sh',
|
|
69
|
-
'mx-agent-system/agent-config/hooks/git-mutation-guard.sh',
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
'.claude/hooks/git-mutation-guard.test.sh',
|
|
73
|
-
'mx-agent-system/agent-config/hooks/git-mutation-guard.test.sh',
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
'.claude/hooks/push-matcher.test.sh',
|
|
77
|
-
'mx-agent-system/agent-config/hooks/push-matcher.test.sh',
|
|
78
|
-
],
|
|
79
|
-
[
|
|
80
|
-
'.claude/hooks/mx-guard-hook.sh',
|
|
81
|
-
'mx-agent-system/agent-config/hooks/mx-guard-hook.sh',
|
|
82
|
-
],
|
|
83
|
-
[
|
|
84
|
-
'.claude/hooks/ci-validate-hook.sh',
|
|
85
|
-
'mx-agent-system/agent-config/hooks/ci-validate-hook.sh',
|
|
86
|
-
],
|
|
87
|
-
// Package→validate-script routing table extracted from ci-validate-hook.sh
|
|
88
|
-
// (Platform v2.35, #4017). Shipped default = the MemNexus map; the hook reads
|
|
89
|
-
// it at push time and fails open if absent. Guard it against mirror drift.
|
|
90
|
-
[
|
|
91
|
-
'.claude/hooks/ci-validate-routes.conf',
|
|
92
|
-
'mx-agent-system/agent-config/hooks/ci-validate-routes.conf',
|
|
93
|
-
],
|
|
94
|
-
[
|
|
95
|
-
'.claude/hooks/worktree-guard.sh',
|
|
96
|
-
'mx-agent-system/agent-config/hooks/worktree-guard.sh',
|
|
97
|
-
],
|
|
98
|
-
[
|
|
99
|
-
'.claude/hooks/worktree-guard.test.sh',
|
|
100
|
-
'mx-agent-system/agent-config/hooks/worktree-guard.test.sh',
|
|
101
|
-
],
|
|
102
|
-
[
|
|
103
|
-
'.claude/hooks/workflow-dispatch-guard.sh',
|
|
104
|
-
'mx-agent-system/agent-config/hooks/workflow-dispatch-guard.sh',
|
|
105
|
-
],
|
|
106
|
-
// Named-memory truncating-write guard (Platform v2.33, #3996). Blocks
|
|
107
|
-
// full-content `mx memories update|create --name X --content <NEW>` writes
|
|
108
|
-
// that shrink a shared named memory past a threshold — two real clobber
|
|
109
|
-
// incidents (2026-07-07 known-issues, 2026-07-09 escalations).
|
|
110
|
-
[
|
|
111
|
-
'.claude/hooks/named-memory-write-guard.sh',
|
|
112
|
-
'mx-agent-system/agent-config/hooks/named-memory-write-guard.sh',
|
|
113
|
-
],
|
|
114
|
-
[
|
|
115
|
-
'.claude/hooks/named-memory-write-guard.test.sh',
|
|
116
|
-
'mx-agent-system/agent-config/hooks/named-memory-write-guard.test.sh',
|
|
117
|
-
],
|
|
118
|
-
[
|
|
119
|
-
'.claude/skills/mx-post-merge-sync/SKILL.md',
|
|
120
|
-
'mx-agent-system/agent-config/skills/mx-post-merge-sync/SKILL.md',
|
|
121
|
-
],
|
|
122
|
-
[
|
|
123
|
-
'.claude/agents/git-expert.md',
|
|
124
|
-
'mx-agent-system/agent-config/agents/git-expert.md',
|
|
125
|
-
],
|
|
126
|
-
// Stop hook that warns when a merged session left its iteration log stale
|
|
127
|
-
// (Platform v2.35, #4017 — product-slug parameterization). Guard the pair.
|
|
128
|
-
[
|
|
129
|
-
'.claude/hooks/iteration-log-check.sh',
|
|
130
|
-
'mx-agent-system/agent-config/hooks/iteration-log-check.sh',
|
|
131
|
-
],
|
|
132
|
-
[
|
|
133
|
-
'.claude/hooks/iteration-log-check.test.sh',
|
|
134
|
-
'mx-agent-system/agent-config/hooks/iteration-log-check.test.sh',
|
|
135
|
-
],
|
|
136
|
-
// Agent/skill instruction files carrying the product-slug named-memory
|
|
137
|
-
// convention (Platform v2.35, #4017). These had been mirror-unguarded; the
|
|
138
|
-
// KI-HOOK-MIRROR-DRIFT-CHECK history (three hand-syncs, #3728) justifies
|
|
139
|
-
// enforcing byte-identity now that they are edited in lockstep.
|
|
140
|
-
[
|
|
141
|
-
'.claude/agents/memory-creator.md',
|
|
142
|
-
'mx-agent-system/agent-config/agents/memory-creator.md',
|
|
143
|
-
],
|
|
144
|
-
[
|
|
145
|
-
'.claude/agents/status-reporter.md',
|
|
146
|
-
'mx-agent-system/agent-config/agents/status-reporter.md',
|
|
147
|
-
],
|
|
148
|
-
[
|
|
149
|
-
'.claude/agents/prior-art-researcher.md',
|
|
150
|
-
'mx-agent-system/agent-config/agents/prior-art-researcher.md',
|
|
151
|
-
],
|
|
152
|
-
[
|
|
153
|
-
'.claude/agents/dogfood-auditor.md',
|
|
154
|
-
'mx-agent-system/agent-config/agents/dogfood-auditor.md',
|
|
155
|
-
],
|
|
156
|
-
[
|
|
157
|
-
'.claude/skills/mx-session-start/SKILL.md',
|
|
158
|
-
'mx-agent-system/agent-config/skills/mx-session-start/SKILL.md',
|
|
159
|
-
],
|
|
160
|
-
[
|
|
161
|
-
'.claude/skills/mx-iteration-close/SKILL.md',
|
|
162
|
-
'mx-agent-system/agent-config/skills/mx-iteration-close/SKILL.md',
|
|
163
|
-
],
|
|
164
|
-
// Session-start coordination brief hook (PR-D). The .claude runtime copy is
|
|
165
|
-
// synced by the normal distribution mechanism; until Gate 3 cutover it may be
|
|
166
|
-
// absent, in which case this pair SKIPS (the loop's existsSync guard). Once
|
|
167
|
-
// synced it is asserted byte-identical.
|
|
168
|
-
[
|
|
169
|
-
'.claude/hooks/coordination-brief.sh',
|
|
170
|
-
'mx-agent-system/agent-config/hooks/coordination-brief.sh',
|
|
171
|
-
],
|
|
172
|
-
// The co-located hook test suite. Not part of the shipped config surface
|
|
173
|
-
// (bundled-config excludes *.test.sh), but both dev copies are edited in
|
|
174
|
-
// lockstep — enforce byte-identity so the slug-validation tests can't drift
|
|
175
|
-
// (v2.40 QA finding, KI-COORDMT-HOOK-EXPORT-ROBUSTNESS).
|
|
176
|
-
[
|
|
177
|
-
'.claude/hooks/coordination-brief.test.sh',
|
|
178
|
-
'mx-agent-system/agent-config/hooks/coordination-brief.test.sh',
|
|
179
|
-
],
|
|
180
|
-
// NOTE: agent-config/settings.json is intentionally NOT a byte-identity mirror
|
|
181
|
-
// pair — the worktree .claude/settings.json has its SessionStart/hook wiring
|
|
182
|
-
// COMPOSED/merged from the source at deploy time (see claude.ts), so the two
|
|
183
|
-
// are not byte-identical by design. But the SECURITY-SENSITIVE part — the
|
|
184
|
-
// `permissions` allow/deny/ask lists and the experimental-teams flag — must
|
|
185
|
-
// never drift, because config-sync/`mx-agent new` ship agent-config's copy
|
|
186
|
-
// into worktrees verbatim. A prior drift (agent-config/settings.json lagged
|
|
187
|
-
// active by the "Monitor" allow entry from #4275, and the git-mutation-guard
|
|
188
|
-
// #4289 hardening) shipped a weakened config to consumer worktrees (#4294).
|
|
189
|
-
// The `settings.json permissions parity` describe below guards that surface
|
|
190
|
-
// semantically rather than by byte-identity.
|
|
191
|
-
];
|
|
192
|
-
describe('config mirror integrity', () => {
|
|
193
|
-
for (const [claudeRel, agentConfigRel] of MIRROR_PAIRS) {
|
|
194
|
-
const claudeAbs = join(REPO_ROOT, claudeRel);
|
|
195
|
-
const agentConfigAbs = join(REPO_ROOT, agentConfigRel);
|
|
196
|
-
// The .claude copy is not present in every checkout (e.g. a package-only
|
|
197
|
-
// export of the CLI). Skip rather than fail spuriously when it's absent.
|
|
198
|
-
if (!existsSync(claudeAbs)) {
|
|
199
|
-
it.skip(`${claudeRel} ↔ ${agentConfigRel} (SKIPPED: ${claudeRel} absent in this checkout)`, () => { });
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
it(`${claudeRel} is byte-identical to ${agentConfigRel}`, () => {
|
|
203
|
-
const claudeBytes = readFileSync(claudeAbs);
|
|
204
|
-
const agentConfigBytes = readFileSync(agentConfigAbs);
|
|
205
|
-
expect(claudeBytes.equals(agentConfigBytes)).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
/**
|
|
210
|
-
* settings.json is composed (SessionStart/hook wiring is merged into the worktree
|
|
211
|
-
* copy at deploy time), so it is not a byte-identity mirror pair above. But its
|
|
212
|
-
* `permissions` block and the experimental-teams flag are shipped verbatim into
|
|
213
|
-
* consumer worktrees by config-sync/`mx-agent new`; drift there ships a weakened
|
|
214
|
-
* security posture. This suite asserts semantic parity of exactly those fields
|
|
215
|
-
* between the active runtime copy and the agent-config source of truth so a
|
|
216
|
-
* regression like #4294 (agent-config lagging the active allow-list) fails
|
|
217
|
-
* locally before it ships.
|
|
218
|
-
*/
|
|
219
|
-
describe('settings.json permissions parity', () => {
|
|
220
|
-
const claudeSettingsAbs = join(REPO_ROOT, '.claude/settings.json');
|
|
221
|
-
const agentConfigSettingsAbs = join(REPO_ROOT, 'mx-agent-system/agent-config/settings.json');
|
|
222
|
-
const bothPresent = existsSync(claudeSettingsAbs) && existsSync(agentConfigSettingsAbs);
|
|
223
|
-
const loadPerms = (abs) => {
|
|
224
|
-
const parsed = JSON.parse(readFileSync(abs, 'utf-8'));
|
|
225
|
-
return parsed;
|
|
226
|
-
};
|
|
227
|
-
(bothPresent ? it : it.skip)('permissions.allow is identical between active and agent-config', () => {
|
|
228
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
229
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
230
|
-
expect(template.permissions?.allow).toEqual(active.permissions?.allow);
|
|
231
|
-
});
|
|
232
|
-
(bothPresent ? it : it.skip)('permissions.deny is identical between active and agent-config', () => {
|
|
233
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
234
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
235
|
-
expect(template.permissions?.deny).toEqual(active.permissions?.deny);
|
|
236
|
-
});
|
|
237
|
-
(bothPresent ? it : it.skip)('permissions.ask is identical between active and agent-config', () => {
|
|
238
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
239
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
240
|
-
expect(template.permissions?.ask).toEqual(active.permissions?.ask);
|
|
241
|
-
});
|
|
242
|
-
(bothPresent ? it : it.skip)('CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS flag is identical between active and agent-config', () => {
|
|
243
|
-
const active = loadPerms(claudeSettingsAbs);
|
|
244
|
-
const template = loadPerms(agentConfigSettingsAbs);
|
|
245
|
-
expect(template.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS).toEqual(active.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS);
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
//# sourceMappingURL=config-mirror-integrity.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-mirror-integrity.test.js","sourceRoot":"","sources":["../../src/__tests__/config-mirror-integrity.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D;;;;GAIG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACzE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,4DAA4D,KAAK,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,YAAY,GAA6C;IAC7D;QACE,+CAA+C;QAC/C,oEAAoE;KACrE;IACD;QACE,oDAAoD;QACpD,yEAAyE;KAC1E;IACD,0EAA0E;IAC1E,6EAA6E;IAC7E,mEAAmE;IACnE;QACE,qCAAqC;QACrC,0DAA0D;KAC3D;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD;QACE,oCAAoC;QACpC,yDAAyD;KAC1D;IACD;QACE,gCAAgC;QAChC,qDAAqD;KACtD;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD,2EAA2E;IAC3E,8EAA8E;IAC9E,2EAA2E;IAC3E;QACE,uCAAuC;QACvC,4DAA4D;KAC7D;IACD;QACE,iCAAiC;QACjC,sDAAsD;KACvD;IACD;QACE,sCAAsC;QACtC,2DAA2D;KAC5D;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD,sEAAsE;IACtE,2EAA2E;IAC3E,wEAAwE;IACxE,+DAA+D;IAC/D;QACE,2CAA2C;QAC3C,gEAAgE;KACjE;IACD;QACE,gDAAgD;QAChD,qEAAqE;KACtE;IACD;QACE,4CAA4C;QAC5C,iEAAiE;KAClE;IACD;QACE,8BAA8B;QAC9B,mDAAmD;KACpD;IACD,0EAA0E;IAC1E,2EAA2E;IAC3E;QACE,sCAAsC;QACtC,2DAA2D;KAC5D;IACD;QACE,2CAA2C;QAC3C,gEAAgE;KACjE;IACD,uEAAuE;IACvE,2EAA2E;IAC3E,yEAAyE;IACzE,gEAAgE;IAChE;QACE,kCAAkC;QAClC,uDAAuD;KACxD;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD;QACE,wCAAwC;QACxC,6DAA6D;KAC9D;IACD;QACE,mCAAmC;QACnC,wDAAwD;KACzD;IACD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD;QACE,4CAA4C;QAC5C,iEAAiE;KAClE;IACD,4EAA4E;IAC5E,8EAA8E;IAC9E,4EAA4E;IAC5E,wCAAwC;IACxC;QACE,qCAAqC;QACrC,0DAA0D;KAC3D;IACD,yEAAyE;IACzE,yEAAyE;IACzE,4EAA4E;IAC5E,yDAAyD;IACzD;QACE,0CAA0C;QAC1C,+DAA+D;KAChE;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,4EAA4E;IAC5E,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,6CAA6C;CAC9C,CAAC;AAEF,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,KAAK,MAAM,CAAC,SAAS,EAAE,cAAc,CAAC,IAAI,YAAY,EAAE,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAEvD,yEAAyE;QACzE,yEAAyE;QACzE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,EAAE,CAAC,IAAI,CAAC,GAAG,SAAS,MAAM,cAAc,cAAc,SAAS,2BAA2B,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACtG,SAAS;QACX,CAAC;QAED,EAAE,CAAC,GAAG,SAAS,yBAAyB,cAAc,EAAE,EAAE,GAAG,EAAE;YAC7D,MAAM,WAAW,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YAC5C,MAAM,gBAAgB,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;YACtD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;IACnE,MAAM,sBAAsB,GAAG,IAAI,CACjC,SAAS,EACT,4CAA4C,CAC7C,CAAC;IAEF,MAAM,WAAW,GACf,UAAU,CAAC,iBAAiB,CAAC,IAAI,UAAU,CAAC,sBAAsB,CAAC,CAAC;IAEtE,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAGnD,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,gEAAgE,EAChE,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACzE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,+DAA+D,EAC/D,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,8DAA8D,EAC9D,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;IACrE,CAAC,CACF,CAAC;IAEF,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAC1B,wFAAwF,EACxF,GAAG,EAAE;QACH,MAAM,MAAM,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACnD,MAAM,CAAC,QAAQ,CAAC,oCAAoC,CAAC,CAAC,OAAO,CAC3D,MAAM,CAAC,oCAAoC,CAC5C,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC,CAAC,CAAC"}
|