mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-11 10:45:21 +02:00
Carry the canonical IR, Pi emitter and source-schema doctor from https://github.com/affaan-m/ECC/pull/3022. Cursor and OpenCode emitters remain with the open source PR pending capability fixes. Co-authored-by: alizahidraja <alizahidrajaa@gmail.com>
70 lines
2.4 KiB
JavaScript
70 lines
2.4 KiB
JavaScript
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
/**
|
|
* ECC Agent IR — tool and model mapping tables.
|
|
*
|
|
* The IR stores source (Claude) tool names verbatim. Each emitter maps those
|
|
* names to its own harness primitives through a table. Unmapped tools are
|
|
* never silently dropped: they are surfaced as `unsupported` so the emitter
|
|
* can warn and the conformance test can assert the mapping is complete.
|
|
*
|
|
* Pi tool names are verified against the installed
|
|
* `@earendil-works/pi-coding-agent` `dist/core/tools/index.d.ts`:
|
|
* read | bash | powershell | edit | write | grep | find | ls
|
|
* (`powershell` is Windows-only and not emitted here.)
|
|
*
|
|
* Security invariant: a Claude tool must never map to a Pi tool with MORE
|
|
* authority than the source. Read-only Claude tools (Read, Grep, Glob) map to
|
|
* read-only Pi tools (read, grep, find), never to `bash`.
|
|
*/
|
|
|
|
/** Claude tool name -> Pi tool name. */
|
|
const CLAUDE_TO_PI_TOOLS = Object.freeze({
|
|
Read: 'read',
|
|
Grep: 'grep',
|
|
Glob: 'find',
|
|
Bash: 'bash',
|
|
Edit: 'edit',
|
|
Write: 'write',
|
|
// WebSearch and WebFetch are intentionally NOT mapped: Pi exposes no
|
|
// `web_search` or `fetch_content` built-in. They fall through to the
|
|
// "unmapped tool" warning rather than being guessed.
|
|
});
|
|
|
|
/**
|
|
* Map one Claude tool name to a Pi tool allowlist entry.
|
|
*
|
|
* @param {string} claudeTool
|
|
* @returns {{ tool: string|null, unsupported: boolean, note?: string }}
|
|
* `tool` is the Pi tool name, or null when unsupported. `unsupported` is
|
|
* true only for source tools that have no Pi built-in equivalent in v1.
|
|
*/
|
|
function mapToolToPi(claudeTool) {
|
|
const name = String(claudeTool).trim();
|
|
if (Object.prototype.hasOwnProperty.call(CLAUDE_TO_PI_TOOLS, name)) {
|
|
return { tool: CLAUDE_TO_PI_TOOLS[name], unsupported: false };
|
|
}
|
|
if (name.startsWith('mcp__')) {
|
|
return {
|
|
tool: null,
|
|
unsupported: true,
|
|
note: `MCP tool ${name} not a Pi built-in (configure the MCP server explicitly)`,
|
|
};
|
|
}
|
|
return { tool: null, unsupported: true, note: `unmapped tool: ${name} (not a Pi built-in)` };
|
|
}
|
|
|
|
/**
|
|
* Claude model tiers. Pi resolves its own child model; v1 does not emit a
|
|
* `model` field, so Pi's default applies. The tier is retained in the IR for
|
|
* lossless round-trips and for emitters that do have a model table.
|
|
*/
|
|
const CLAUDE_MODEL_TIERS = Object.freeze(['haiku', 'sonnet', 'opus']);
|
|
|
|
module.exports = {
|
|
CLAUDE_TO_PI_TOOLS,
|
|
CLAUDE_MODEL_TIERS,
|
|
mapToolToPi,
|
|
};
|