mirror of
https://github.com/affaan-m/ECC.git
synced 2026-08-17 21:15:40 +02:00
The `.opencode/commands/*.md` frontmatter referenced agents with the Claude Code plugin namespace (`agent: everything-claude-code:<name>`), but ECC's opencode integration registers its agents unscoped in `opencode.json`'s `agent` map (`code-reviewer`, `planner`, ...), and that file's own `command` section already references them unscoped. The `everything-claude-code:` scope resolves under no opencode config (the opencode plugin package is `ecc-universal`, and inline-config agents are bare), so subtask commands like `/code-review` hard-fail with `Agent not found: everything-claude-code:code-reviewer`. Non-subtask commands fall back to the default agent and appear to work — which is why only some commands failed. Strip the `everything-claude-code:` prefix from all 30 command frontmatter agent ids so they match the registered agents, fix the MIGRATION.md example, and replace the test that enforced the broken scoped invariant with one that asserts each command agent id is a registered opencode agent (fails on the old scoped ids, passes on the fix). Fixes #2477
122 lines
3.5 KiB
JavaScript
122 lines
3.5 KiB
JavaScript
/**
|
|
* Tests for .opencode/opencode.json local file references.
|
|
*
|
|
* Run with: node tests/opencode-config.test.js
|
|
*/
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
function test(name, fn) {
|
|
try {
|
|
fn();
|
|
console.log(` ✓ ${name}`);
|
|
return true;
|
|
} catch (err) {
|
|
console.log(` ✗ ${name}`);
|
|
console.log(` Error: ${err.message}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const repoRoot = path.join(__dirname, '..');
|
|
const opencodeDir = path.join(repoRoot, '.opencode');
|
|
const configPath = path.join(opencodeDir, 'opencode.json');
|
|
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
if (
|
|
test('plugin paths do not duplicate the .opencode directory', () => {
|
|
const plugins = config.plugin || [];
|
|
for (const pluginPath of plugins) {
|
|
assert.ok(!pluginPath.includes('.opencode/'), `Plugin path should be config-relative, got: ${pluginPath}`);
|
|
assert.ok(fs.existsSync(path.resolve(opencodeDir, pluginPath)), `Plugin path should resolve from .opencode/: ${pluginPath}`);
|
|
}
|
|
})
|
|
)
|
|
passed++;
|
|
else failed++;
|
|
|
|
if (
|
|
test('file references are config-relative and resolve to existing files', () => {
|
|
const refs = [];
|
|
|
|
function walk(value) {
|
|
if (typeof value === 'string') {
|
|
const matches = value.matchAll(/\{file:([^}]+)\}/g);
|
|
for (const match of matches) {
|
|
refs.push(match[1]);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (Array.isArray(value)) {
|
|
value.forEach(walk);
|
|
return;
|
|
}
|
|
|
|
if (value && typeof value === 'object') {
|
|
Object.values(value).forEach(walk);
|
|
}
|
|
}
|
|
|
|
walk(config);
|
|
|
|
assert.ok(refs.length > 0, 'Expected to find file references in opencode.json');
|
|
|
|
for (const ref of refs) {
|
|
assert.ok(!ref.startsWith('.opencode/'), `File ref should not duplicate .opencode/: ${ref}`);
|
|
assert.ok(fs.existsSync(path.resolve(opencodeDir, ref)), `File ref should resolve from .opencode/: ${ref}`);
|
|
}
|
|
})
|
|
)
|
|
passed++;
|
|
else failed++;
|
|
|
|
if (
|
|
test('command markdown frontmatter agent ids resolve to a registered opencode agent', () => {
|
|
const commandsDir = path.join(opencodeDir, 'commands');
|
|
const registeredAgents = new Set(Object.keys(config.agent || {}));
|
|
assert.ok(registeredAgents.size > 0, 'Expected opencode.json to register at least one agent');
|
|
|
|
for (const entry of fs.readdirSync(commandsDir)) {
|
|
if (!entry.endsWith('.md')) {
|
|
continue;
|
|
}
|
|
|
|
const body = fs.readFileSync(path.join(commandsDir, entry), 'utf8');
|
|
const match = body.match(/^agent:\s*(.+)$/m);
|
|
|
|
if (!match) {
|
|
continue;
|
|
}
|
|
|
|
const agentId = match[1].trim().replace(/^['"]|['"]$/g, '');
|
|
|
|
// Regression guard for #2477: the Claude Code plugin namespace
|
|
// (`everything-claude-code:`) is not a valid opencode agent scope.
|
|
// OpenCode registers these agents unscoped in opencode.json's `agent`
|
|
// map, so a scoped id fails to resolve ("Agent not found") and hard-
|
|
// breaks subtask commands like /code-review on opencode.
|
|
assert.ok(
|
|
!agentId.includes('everything-claude-code:'),
|
|
`${entry}: command agent must be an unscoped opencode agent id, got: ${agentId}`
|
|
);
|
|
|
|
assert.ok(
|
|
registeredAgents.has(agentId),
|
|
`${entry}: command agent "${agentId}" is not registered in opencode.json's agent map`
|
|
);
|
|
}
|
|
})
|
|
)
|
|
passed++;
|
|
else failed++;
|
|
|
|
console.log(`\nPassed: ${passed}`);
|
|
console.log(`Failed: ${failed}`);
|
|
process.exit(failed > 0 ? 1 : 0);
|