mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
Merge pull request #1000 from affaan-m/fix/codex-context7-compat-tests
fix(codex): broaden context7 config checks
This commit is contained in:
@@ -86,7 +86,8 @@ startup_timeout_sec = 30
|
|||||||
# args = ["-y", "@cloudflare/mcp-server-cloudflare"]
|
# args = ["-y", "@cloudflare/mcp-server-cloudflare"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Codex multi-agent support is experimental as of March 2026.
|
# Codex multi-agent collaboration is stable and on by default in current builds.
|
||||||
|
# Keep the explicit toggle here so the repo documents its expectation clearly.
|
||||||
multi_agent = true
|
multi_agent = true
|
||||||
|
|
||||||
# Profiles — switch with `codex -p <name>`
|
# Profiles — switch with `codex -p <name>`
|
||||||
@@ -101,6 +102,9 @@ sandbox_mode = "workspace-write"
|
|||||||
web_search = "live"
|
web_search = "live"
|
||||||
|
|
||||||
[agents]
|
[agents]
|
||||||
|
[agents]
|
||||||
|
# Multi-agent role limits and local role definitions.
|
||||||
|
# These map to `.codex/agents/*.toml` and mirror the repo's explorer/reviewer/docs workflow.
|
||||||
max_threads = 6
|
max_threads = 6
|
||||||
max_depth = 1
|
max_depth = 1
|
||||||
|
|
||||||
|
|||||||
@@ -112,10 +112,25 @@ if [[ -f "$CONFIG_FILE" ]]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
has_context7_legacy=0
|
||||||
|
has_context7_current=0
|
||||||
|
|
||||||
|
if rg -n '^\[mcp_servers\.context7\]' "$CONFIG_FILE" >/dev/null 2>&1; then
|
||||||
|
has_context7_legacy=1
|
||||||
|
fi
|
||||||
|
|
||||||
if rg -n '^\[mcp_servers\.context7-mcp\]' "$CONFIG_FILE" >/dev/null 2>&1; then
|
if rg -n '^\[mcp_servers\.context7-mcp\]' "$CONFIG_FILE" >/dev/null 2>&1; then
|
||||||
warn "Legacy [mcp_servers.context7-mcp] exists (context7 is preferred)"
|
has_context7_current=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$has_context7_legacy" -eq 1 || "$has_context7_current" -eq 1 ]]; then
|
||||||
|
ok "MCP section [mcp_servers.context7] or [mcp_servers.context7-mcp] exists"
|
||||||
else
|
else
|
||||||
ok "No legacy [mcp_servers.context7-mcp] section"
|
fail "MCP section [mcp_servers.context7] or [mcp_servers.context7-mcp] missing"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$has_context7_legacy" -eq 1 && "$has_context7_current" -eq 1 ]]; then
|
||||||
|
warn "Both [mcp_servers.context7] and [mcp_servers.context7-mcp] exist; prefer one name"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,22 @@ const configPath = path.join(repoRoot, '.codex', 'config.toml');
|
|||||||
const config = fs.readFileSync(configPath, 'utf8');
|
const config = fs.readFileSync(configPath, 'utf8');
|
||||||
const codexAgentsDir = path.join(repoRoot, '.codex', 'agents');
|
const codexAgentsDir = path.join(repoRoot, '.codex', 'agents');
|
||||||
|
|
||||||
|
function escapeRegExp(value) {
|
||||||
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTomlSection(text, sectionName) {
|
||||||
|
const escapedSection = escapeRegExp(sectionName);
|
||||||
|
const headerPattern = new RegExp(`^\\s*\\[${escapedSection}\\]\\s*$`, 'm');
|
||||||
|
const headerMatch = headerPattern.exec(text);
|
||||||
|
|
||||||
|
assert.ok(headerMatch, `Expected TOML section to exist: [${sectionName}]`);
|
||||||
|
|
||||||
|
const afterHeader = text.slice(headerMatch.index + headerMatch[0].length);
|
||||||
|
const nextHeaderIndex = afterHeader.search(/^\s*\[/m);
|
||||||
|
return nextHeaderIndex === -1 ? afterHeader : afterHeader.slice(0, nextHeaderIndex);
|
||||||
|
}
|
||||||
|
|
||||||
let passed = 0;
|
let passed = 0;
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
|
||||||
@@ -47,6 +63,37 @@ if (
|
|||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
else failed++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
test('reference config enables Codex multi-agent support', () => {
|
||||||
|
assert.ok(
|
||||||
|
/^\s*multi_agent\s*=\s*true\s*$/m.test(config),
|
||||||
|
'Expected `.codex/config.toml` to opt into Codex multi-agent collaboration',
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
passed++;
|
||||||
|
else failed++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
test('reference config wires the sample Codex role files', () => {
|
||||||
|
for (const roleFile of ['explorer.toml', 'reviewer.toml', 'docs-researcher.toml']) {
|
||||||
|
const rolePath = path.join(codexAgentsDir, roleFile);
|
||||||
|
const roleSection = roleFile.replace(/\.toml$/, '').replace(/-/g, '_');
|
||||||
|
const sectionBody = getTomlSection(config, `agents.${roleSection}`);
|
||||||
|
|
||||||
|
assert.ok(fs.existsSync(rolePath), `Expected role config to exist: ${roleFile}`);
|
||||||
|
assert.ok(
|
||||||
|
new RegExp(`^\\s*config_file\\s*=\\s*"agents\\/${escapeRegExp(roleFile)}"\\s*$`, 'm').test(
|
||||||
|
sectionBody,
|
||||||
|
),
|
||||||
|
`Expected \`.codex/config.toml\` to reference ${roleFile} inside [agents.${roleSection}]`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
passed++;
|
||||||
|
else failed++;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
test('sample Codex role configs do not use o4-mini', () => {
|
test('sample Codex role configs do not use o4-mini', () => {
|
||||||
const roleFiles = fs.readdirSync(codexAgentsDir).filter(file => file.endsWith('.toml'));
|
const roleFiles = fs.readdirSync(codexAgentsDir).filter(file => file.endsWith('.toml'));
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ const { spawnSync } = require('child_process');
|
|||||||
|
|
||||||
const repoRoot = path.join(__dirname, '..', '..');
|
const repoRoot = path.join(__dirname, '..', '..');
|
||||||
const installScript = path.join(repoRoot, 'scripts', 'codex', 'install-global-git-hooks.sh');
|
const installScript = path.join(repoRoot, 'scripts', 'codex', 'install-global-git-hooks.sh');
|
||||||
const installSource = fs.readFileSync(installScript, 'utf8');
|
const syncScript = path.join(repoRoot, 'scripts', 'sync-ecc-to-codex.sh');
|
||||||
|
const checkScript = path.join(repoRoot, 'scripts', 'codex', 'check-codex-global-state.sh');
|
||||||
|
|
||||||
function test(name, fn) {
|
function test(name, fn) {
|
||||||
try {
|
try {
|
||||||
@@ -32,25 +33,15 @@ function cleanup(dirPath) {
|
|||||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
function toBashPath(filePath) {
|
|
||||||
if (process.platform !== 'win32') {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String(filePath)
|
|
||||||
.replace(/^([A-Za-z]):/, (_, driveLetter) => `/${driveLetter.toLowerCase()}`)
|
|
||||||
.replace(/\\/g, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
function runBash(scriptPath, args = [], env = {}, cwd = repoRoot) {
|
function runBash(scriptPath, args = [], env = {}, cwd = repoRoot) {
|
||||||
return spawnSync('bash', [toBashPath(scriptPath), ...args], {
|
return spawnSync('bash', [scriptPath, ...args], {
|
||||||
cwd,
|
cwd,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
...env
|
...env,
|
||||||
},
|
},
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
stdio: ['pipe', 'pipe', 'pipe']
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,24 +49,14 @@ let passed = 0;
|
|||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
test('install-global-git-hooks.sh does not use eval and executes argv directly', () => {
|
test('install-global-git-hooks.sh handles quoted hook paths without shell injection', () => {
|
||||||
assert.ok(!installSource.includes('eval "$*"'), 'Expected installer to avoid eval');
|
|
||||||
assert.ok(installSource.includes(' "$@"'), 'Expected installer to execute argv directly');
|
|
||||||
assert.ok(installSource.includes(`printf ' %q' "$@"`), 'Expected dry-run logging to shell-escape argv');
|
|
||||||
})
|
|
||||||
)
|
|
||||||
passed++;
|
|
||||||
else failed++;
|
|
||||||
|
|
||||||
if (
|
|
||||||
test('install-global-git-hooks.sh handles shell-sensitive hook paths without shell injection', () => {
|
|
||||||
const homeDir = createTempDir('codex-hooks-home-');
|
const homeDir = createTempDir('codex-hooks-home-');
|
||||||
const weirdHooksDir = path.join(homeDir, "git-hooks 'quoted' & spaced");
|
const weirdHooksDir = path.join(homeDir, 'git-hooks "quoted"');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = runBash(installScript, [], {
|
const result = runBash(installScript, [], {
|
||||||
HOME: toBashPath(homeDir),
|
HOME: homeDir,
|
||||||
ECC_GLOBAL_HOOKS_DIR: toBashPath(weirdHooksDir)
|
ECC_GLOBAL_HOOKS_DIR: weirdHooksDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.strictEqual(result.status, 0, result.stderr || result.stdout);
|
assert.strictEqual(result.status, 0, result.stderr || result.stdout);
|
||||||
@@ -89,6 +70,66 @@ if (
|
|||||||
passed++;
|
passed++;
|
||||||
else failed++;
|
else failed++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
test('sync and global sanity checks accept the legacy context7 MCP section', () => {
|
||||||
|
const homeDir = createTempDir('codex-sync-home-');
|
||||||
|
const codexDir = path.join(homeDir, '.codex');
|
||||||
|
const configPath = path.join(codexDir, 'config.toml');
|
||||||
|
const config = [
|
||||||
|
'approval_policy = "on-request"',
|
||||||
|
'sandbox_mode = "workspace-write"',
|
||||||
|
'web_search = "live"',
|
||||||
|
'persistent_instructions = ""',
|
||||||
|
'',
|
||||||
|
'[features]',
|
||||||
|
'multi_agent = true',
|
||||||
|
'',
|
||||||
|
'[profiles.strict]',
|
||||||
|
'approval_policy = "on-request"',
|
||||||
|
'sandbox_mode = "read-only"',
|
||||||
|
'web_search = "cached"',
|
||||||
|
'',
|
||||||
|
'[profiles.yolo]',
|
||||||
|
'approval_policy = "never"',
|
||||||
|
'sandbox_mode = "workspace-write"',
|
||||||
|
'web_search = "live"',
|
||||||
|
'',
|
||||||
|
'[mcp_servers.context7]',
|
||||||
|
'command = "npx"',
|
||||||
|
'args = ["-y", "@upstash/context7-mcp"]',
|
||||||
|
'',
|
||||||
|
'[mcp_servers.github]',
|
||||||
|
'command = "npx"',
|
||||||
|
'args = ["-y", "@modelcontextprotocol/server-github"]',
|
||||||
|
'',
|
||||||
|
'[mcp_servers.memory]',
|
||||||
|
'command = "npx"',
|
||||||
|
'args = ["-y", "@modelcontextprotocol/server-memory"]',
|
||||||
|
'',
|
||||||
|
'[mcp_servers.sequential-thinking]',
|
||||||
|
'command = "npx"',
|
||||||
|
'args = ["-y", "@modelcontextprotocol/server-sequential-thinking"]',
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(codexDir, { recursive: true });
|
||||||
|
fs.writeFileSync(configPath, config);
|
||||||
|
|
||||||
|
const syncResult = runBash(syncScript, [], { HOME: homeDir, CODEX_HOME: codexDir });
|
||||||
|
assert.strictEqual(syncResult.status, 0, syncResult.stderr || syncResult.stdout);
|
||||||
|
|
||||||
|
const checkResult = runBash(checkScript, [], { HOME: homeDir, CODEX_HOME: codexDir });
|
||||||
|
assert.strictEqual(checkResult.status, 0, checkResult.stderr || checkResult.stdout);
|
||||||
|
assert.match(checkResult.stdout, /MCP section \[mcp_servers\.context7\] or \[mcp_servers\.context7-mcp\] exists/);
|
||||||
|
} finally {
|
||||||
|
cleanup(homeDir);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
passed++;
|
||||||
|
else failed++;
|
||||||
|
|
||||||
console.log(`\nPassed: ${passed}`);
|
console.log(`\nPassed: ${passed}`);
|
||||||
console.log(`Failed: ${failed}`);
|
console.log(`Failed: ${failed}`);
|
||||||
process.exit(failed > 0 ? 1 : 0);
|
process.exit(failed > 0 ? 1 : 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user