@memnexus-ai/mx-agent-cli 0.1.165 → 0.1.166
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,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md.template compose guard (Platform v2.35 Phase 2, #4017).
|
|
3
|
+
*
|
|
4
|
+
* The deployed artifact `mx-agent-system/agent-config/CLAUDE.md.template` is
|
|
5
|
+
* composed from a generic core (CLAUDE.core.md) + a MemNexus overlay
|
|
6
|
+
* (CLAUDE.overlay.memnexus.md) + variables (vars.memnexus.json). `deployClaudeMd`
|
|
7
|
+
* reads the composed artifact verbatim, so the artifact must stay byte-identical
|
|
8
|
+
* to compose(core + overlay + vars) or the source pieces silently drift from what
|
|
9
|
+
* actually ships. This test reproduces Phase 1's zero-behavior-change proof: it
|
|
10
|
+
* runs the real compose.mjs and asserts byte-equivalence with the checked-in
|
|
11
|
+
* template. compose.mjs lives next to the sources in agent-config (portable, no
|
|
12
|
+
* CLI dependency), so it is exercised via a Node subprocess rather than imported
|
|
13
|
+
* across the package boundary.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=claude-md-compose.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-md-compose.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/claude-md-compose.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLAUDE.md.template compose guard (Platform v2.35 Phase 2, #4017).
|
|
3
|
+
*
|
|
4
|
+
* The deployed artifact `mx-agent-system/agent-config/CLAUDE.md.template` is
|
|
5
|
+
* composed from a generic core (CLAUDE.core.md) + a MemNexus overlay
|
|
6
|
+
* (CLAUDE.overlay.memnexus.md) + variables (vars.memnexus.json). `deployClaudeMd`
|
|
7
|
+
* reads the composed artifact verbatim, so the artifact must stay byte-identical
|
|
8
|
+
* to compose(core + overlay + vars) or the source pieces silently drift from what
|
|
9
|
+
* actually ships. This test reproduces Phase 1's zero-behavior-change proof: it
|
|
10
|
+
* runs the real compose.mjs and asserts byte-equivalence with the checked-in
|
|
11
|
+
* template. compose.mjs lives next to the sources in agent-config (portable, no
|
|
12
|
+
* CLI dependency), so it is exercised via a Node subprocess rather than imported
|
|
13
|
+
* across the package boundary.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, expect } from 'vitest';
|
|
16
|
+
import { readFileSync, existsSync } from 'fs';
|
|
17
|
+
import { execFileSync } from 'child_process';
|
|
18
|
+
import { dirname, join } from 'path';
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
function findRepoRoot(start) {
|
|
22
|
+
let dir = start;
|
|
23
|
+
for (let i = 0; i < 20; i++) {
|
|
24
|
+
if (existsSync(join(dir, 'mx-agent-system', 'agent-config')))
|
|
25
|
+
return dir;
|
|
26
|
+
const parent = dirname(dir);
|
|
27
|
+
if (parent === dir)
|
|
28
|
+
break;
|
|
29
|
+
dir = parent;
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`claude-md-compose: could not locate repo root from ${start}`);
|
|
32
|
+
}
|
|
33
|
+
const REPO_ROOT = findRepoRoot(__dirname);
|
|
34
|
+
const CLAUDE_MD_DIR = join(REPO_ROOT, 'mx-agent-system', 'agent-config', 'claude-md');
|
|
35
|
+
const COMPOSE = join(CLAUDE_MD_DIR, 'compose.mjs');
|
|
36
|
+
const TEMPLATE_PATH = join(REPO_ROOT, 'mx-agent-system', 'agent-config', 'CLAUDE.md.template');
|
|
37
|
+
/** Run `node -e <script>` and return { status, stderr }. Never throws. */
|
|
38
|
+
function runNode(script) {
|
|
39
|
+
try {
|
|
40
|
+
execFileSync('node', ['--input-type=module', '-e', script], { encoding: 'utf-8', stdio: 'pipe' });
|
|
41
|
+
return { status: 0, stderr: '' };
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const e = err;
|
|
45
|
+
return { status: e.status ?? 1, stderr: e.stderr ?? '' };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const composeUrl = pathToFileURL(COMPOSE).href;
|
|
49
|
+
describe('CLAUDE.md.template compose', () => {
|
|
50
|
+
it('source pieces exist alongside the artifact', () => {
|
|
51
|
+
expect(existsSync(join(CLAUDE_MD_DIR, 'CLAUDE.core.md'))).toBe(true);
|
|
52
|
+
expect(existsSync(join(CLAUDE_MD_DIR, 'CLAUDE.overlay.memnexus.md'))).toBe(true);
|
|
53
|
+
expect(existsSync(join(CLAUDE_MD_DIR, 'vars.memnexus.json'))).toBe(true);
|
|
54
|
+
expect(existsSync(COMPOSE)).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
it('compose(core + overlay + vars) is byte-identical to the checked-in CLAUDE.md.template', () => {
|
|
57
|
+
const composed = execFileSync('node', [COMPOSE], { encoding: 'utf-8' });
|
|
58
|
+
const artifact = readFileSync(TEMPLATE_PATH, 'utf-8');
|
|
59
|
+
expect(composed).toBe(artifact);
|
|
60
|
+
});
|
|
61
|
+
it('compose --check reports in-sync (exit 0)', () => {
|
|
62
|
+
// Throws on non-zero exit; a passing call proves --check exits 0.
|
|
63
|
+
execFileSync('node', [COMPOSE, '--check'], { encoding: 'utf-8', stdio: 'pipe' });
|
|
64
|
+
});
|
|
65
|
+
it('the core is variable-free (all {{VAR}} tokens live in the overlay)', () => {
|
|
66
|
+
const coreText = readFileSync(join(CLAUDE_MD_DIR, 'CLAUDE.core.md'), 'utf-8');
|
|
67
|
+
// Strip all HTML comments (the banner + the core:NAME markers) so only the
|
|
68
|
+
// section body text remains, then assert it carries no template variables.
|
|
69
|
+
const body = coreText.replace(/<!--[\s\S]*?-->/g, '');
|
|
70
|
+
expect(body.match(/\{\{[^}]*\}\}/)).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
it('compose throws on an unresolved variable', () => {
|
|
73
|
+
const r = runNode(`import { compose } from ${JSON.stringify(composeUrl)};` +
|
|
74
|
+
`compose({ coreText: '', overlayText: 'hi {{MISSING}}', vars: {} });`);
|
|
75
|
+
expect(r.status).not.toBe(0);
|
|
76
|
+
expect(r.stderr).toMatch(/unresolved template variable/);
|
|
77
|
+
});
|
|
78
|
+
it('compose throws on an unknown core include', () => {
|
|
79
|
+
const r = runNode(`import { compose } from ${JSON.stringify(composeUrl)};` +
|
|
80
|
+
`compose({ coreText: '', overlayText: '{{> core:nope}}', vars: {} });`);
|
|
81
|
+
expect(r.status).not.toBe(0);
|
|
82
|
+
expect(r.stderr).toMatch(/unknown core block/);
|
|
83
|
+
});
|
|
84
|
+
it('parseCore throws on a duplicate core block name', () => {
|
|
85
|
+
const dupCore = '<!-- core:dup -->\nfirst\n<!-- core:dup -->\nsecond\n';
|
|
86
|
+
const r = runNode(`import { parseCore } from ${JSON.stringify(composeUrl)};` +
|
|
87
|
+
`parseCore(${JSON.stringify(dupCore)});`);
|
|
88
|
+
expect(r.status).not.toBe(0);
|
|
89
|
+
expect(r.stderr).toMatch(/duplicate core block name "dup"/);
|
|
90
|
+
});
|
|
91
|
+
it('parseCore throws when a core block contains a template token', () => {
|
|
92
|
+
const leakyCore = '<!-- core:leaky -->\nhas a {{VAR}} in it\n';
|
|
93
|
+
const r = runNode(`import { parseCore } from ${JSON.stringify(composeUrl)};` +
|
|
94
|
+
`parseCore(${JSON.stringify(leakyCore)});`);
|
|
95
|
+
expect(r.status).not.toBe(0);
|
|
96
|
+
expect(r.stderr).toMatch(/must be variable-free/);
|
|
97
|
+
});
|
|
98
|
+
it('gives a purposeful error (no raw stack) for a missing vars file', () => {
|
|
99
|
+
const r = runNode(`import { loadVars } from ${JSON.stringify(composeUrl)};` +
|
|
100
|
+
`loadVars('/nonexistent/vars.json');`);
|
|
101
|
+
expect(r.status).not.toBe(0);
|
|
102
|
+
expect(r.stderr).toMatch(/cannot read vars file \/nonexistent\/vars\.json/);
|
|
103
|
+
});
|
|
104
|
+
it('gives a purposeful error for malformed vars JSON', () => {
|
|
105
|
+
const r = runNode(`import { loadVars } from ${JSON.stringify(composeUrl)};` +
|
|
106
|
+
`import { writeFileSync } from 'fs';` +
|
|
107
|
+
`import { tmpdir } from 'os';` +
|
|
108
|
+
`import { join } from 'path';` +
|
|
109
|
+
`const p = join(tmpdir(), 'bad-vars-' + process.pid + '.json');` +
|
|
110
|
+
`writeFileSync(p, '{ not json');` +
|
|
111
|
+
`loadVars(p);`);
|
|
112
|
+
expect(r.status).not.toBe(0);
|
|
113
|
+
expect(r.stderr).toMatch(/is not valid JSON/);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=claude-md-compose.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-md-compose.test.js","sourceRoot":"","sources":["../../src/__tests__/claude-md-compose.test.ts"],"names":[],"mappings":"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"}
|