@ancleto/spec 0.12.0 → 0.12.1
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/README.md +25 -6
- package/package.json +1 -1
- package/src/cli/index.js +111 -7
- package/src/core/adapters/frontmatter.js +69 -2
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Ancleto resuelve esto centralizando las reglas de negocio en un motor de memoria
|
|
|
77
77
|
|
|
78
78
|
**Wizard interactivo** — Banner animado y menús navegables con flechas en `init` e `install`, sin librerías pesadas.
|
|
79
79
|
|
|
80
|
-
**Hosts e IDEs (
|
|
80
|
+
**Hosts e IDEs (8)** — opencode, claude, vscode, antigravity, cursor, roo, copilot y commandcode, cada uno con su layout nativo (Copilot: prompts en `.github/prompts` + MCP en `copilot-mcp.json`; Command Code: `.commandcode/*` + MCP en `.mcp.json`).
|
|
81
81
|
|
|
82
82
|
**Perfil de test automation** — `--profile test`: tester ampliado (planning/generation/healing/coverage), comandos `cleto-test-*` y estructura `testspec/` para proyectos Playwright.
|
|
83
83
|
|
|
@@ -151,6 +151,7 @@ del host (el CLI no deduplica ni elige un "ganador").
|
|
|
151
151
|
| `cursor` | `.cursor/skills/<n>/SKILL.md` | — | — |
|
|
152
152
|
| `roo` | `.roo/skills/<n>/SKILL.md` | — | — |
|
|
153
153
|
| `copilot` | — | `.github/prompts/<n>.prompt.md` | `.github/prompts/<n>.prompt.md` |
|
|
154
|
+
| `commandcode` | `.commandcode/skills/<n>/SKILL.md` | `.commandcode/agents/<n>.md` (adaptado) | `.commandcode/commands/<n>.md` |
|
|
154
155
|
|
|
155
156
|
Cada host recibe sus skills en **su** directorio nativo. **`.agents/skills` es un punto de lectura
|
|
156
157
|
compartido** —lo leen opencode, Cursor, VS Code y Antigravity—, no un destino universal: Claude lee
|
|
@@ -172,18 +173,36 @@ for agent '<n>'`): un id inexistente cuelga el subagent. Los ids que sólo exist
|
|
|
172
173
|
de MCP se expresa por `mcpServers`/`.agents/mcp_config.json`, nunca como tool id. El campo `skill` no
|
|
173
174
|
es una tool: se cubre con el surfaceo automático de skills de Antigravity.
|
|
174
175
|
|
|
176
|
+
**Command Code** (CLI `cmd`) usa su layout nativo por asset bajo `.commandcode/`: skills en
|
|
177
|
+
`.commandcode/skills/<n>/SKILL.md` (Agent Skills, verbatim), agents en `.commandcode/agents/<n>.md`
|
|
178
|
+
(frontmatter adaptado) y commands en `.commandcode/commands/<n>.md` (el body es el prompt, copia
|
|
179
|
+
verbatim). Sus agents adaptan el frontmatter de opencode: `name` inyectado, `description` preservado,
|
|
180
|
+
`mode`/`color`/`temperature`/`permission` eliminados, `model` **omitido** (el host hereda el modelo de
|
|
181
|
+
sesión; elegilo con `/model`, el tier no lo reescribe) y `tools` como **lista** de ids verificados
|
|
182
|
+
(`read`→`read_file`, `write`→`write_file`, `edit`→`edit_file`, `bash`→`shell_command`, `grep`→`grep`,
|
|
183
|
+
`glob`→`glob`, `webfetch`→`web_fetch`, `websearch`→`web_search`, `todowrite`→`todo_write`). Las tools
|
|
184
|
+
de memoria (`searchMemory`/`recordRule`/`recordDecision`) se emiten con la convención MCP de Command
|
|
185
|
+
Code (`mcp__ancleto-memory__<tool>`). Una clave sin id verificado se omite con aviso
|
|
186
|
+
(`skip tool '<k>': no verified Command Code id for agent '<n>'`). A diferencia de otros hosts, en
|
|
187
|
+
Command Code `tools` **omitido** significa "ninguna tool": si el agent de origen no declara `tools`,
|
|
188
|
+
se emite `tools: "*"` (el default de opencode es "todas"). El body de los commands invoca "the Skill
|
|
189
|
+
tool" (no se transforma); en Command Code el equivalente es `/skill:<name>`.
|
|
190
|
+
|
|
175
191
|
Los assets que un host no soporta (por ejemplo `agents`/`commands` de `cursor` y `roo`)
|
|
176
192
|
**no se escriben** y el instalador avisa por `stderr`
|
|
177
193
|
(`skip agents: not supported by host 'cursor'`) sin abortar. La instalación es **aditiva y no
|
|
178
194
|
destructiva**: instalar con otro agente escribe en su base y preserva los layouts previos, y el
|
|
179
195
|
manifiesto `.ancletorc.installedPaths` acumula la unión de los destinos realmente escritos.
|
|
180
196
|
|
|
181
|
-
La **configuración MCP específica de host** está fuera de alcance con **
|
|
197
|
+
La **configuración MCP específica de host** está fuera de alcance con **dos excepciones**: para
|
|
182
198
|
`agent: antigravity` el CLI genera o mergea (no destructivo) el MCP de workspace
|
|
183
|
-
`.agents/mcp_config.json` con esquema `{ "mcpServers": { "<n>": { "command", "args", "env" } } }`,
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
199
|
+
`.agents/mcp_config.json` con esquema `{ "mcpServers": { "<n>": { "command", "args", "env" } } }`, y
|
|
200
|
+
para `agent: commandcode` el MCP de proyecto `.mcp.json` con esquema
|
|
201
|
+
`{ "mcpServers": { "<n>": { "transport": "stdio", "command", "args", "env" } } }`; en ambos casos
|
|
202
|
+
incluye el servidor de memoria `ancleto-memory`, preserva `mcpServers` preexistentes y no pisa
|
|
203
|
+
homónimos. `.mcp.json` sólo lo gestiona `commandcode` (para `claude` sigue fuera de alcance), al igual
|
|
204
|
+
que `.vscode/mcp.json` (VS Code) y el MCP global de Antigravity. `claude` se soporta con adaptación de
|
|
205
|
+
frontmatter obligatoria para `agents`
|
|
187
206
|
(se omiten `model`/`tools` sin equivalencia verificada y las claves
|
|
188
207
|
`mode`/`color`/`temperature`/`permission`), sin MCP propio. **Copilot** instala agents y commands
|
|
189
208
|
como prompt files en `.github/prompts/*.prompt.md` (mismo adapter, sin `model:`) y su MCP en
|
package/package.json
CHANGED
package/src/cli/index.js
CHANGED
|
@@ -36,13 +36,15 @@ Uso:
|
|
|
36
36
|
ancleto install --tier <nivel> normal | minimo | gratis (wizard interactivo en TTY)
|
|
37
37
|
ancleto install --lang <codigo> auto | es | en | pt (idioma de los artifacts; auto = idioma de la conversacion)
|
|
38
38
|
ancleto install --exclude <globs> Paths excluidos del discovery, coma-separados (ej: "**/*.png,docs")
|
|
39
|
-
ancleto install --agent <nombre> opencode | claude | vscode | antigravity | cursor | roo | copilot (wizard si no esta guardado)
|
|
39
|
+
ancleto install --agent <nombre> opencode | claude | vscode | antigravity | cursor | roo | copilot | commandcode (wizard si no esta guardado)
|
|
40
40
|
Elige el layout nativo a materializar (directorio + convencion de
|
|
41
41
|
nombre) y el adapter de frontmatter del host. NO arbitra el
|
|
42
42
|
descubrimiento entre hosts: si dos leen el mismo directorio, eso es
|
|
43
43
|
efecto del host (no se deduplica ni se elige "ganador").
|
|
44
44
|
antigravity instala skills, agents adaptados y commands como skills
|
|
45
45
|
(.agents/*) y mergea el MCP de workspace .agents/mcp_config.json.
|
|
46
|
+
commandcode instala skills, agents adaptados y commands en
|
|
47
|
+
.commandcode/* y mergea el MCP de proyecto .mcp.json.
|
|
46
48
|
ancleto install --profile <perfil> general | test (perfil de test automation con Playwright; persiste en .ancletorc)
|
|
47
49
|
ancleto update [--project <dir>] Alias de install (re-instala sobre lo existente)
|
|
48
50
|
Parado en un proyecto con .ancletorc opera sobre ese proyecto;
|
|
@@ -612,7 +614,7 @@ async function applyTier(agentsDir, tier, models = null) {
|
|
|
612
614
|
}
|
|
613
615
|
}
|
|
614
616
|
|
|
615
|
-
const SUPPORTED_AGENTS = ['opencode', 'claude', 'vscode', 'antigravity', 'cursor', 'roo', 'copilot']
|
|
617
|
+
const SUPPORTED_AGENTS = ['opencode', 'claude', 'vscode', 'antigravity', 'cursor', 'roo', 'copilot', 'commandcode']
|
|
616
618
|
const DEFAULT_AGENT = 'opencode'
|
|
617
619
|
|
|
618
620
|
function askAgent() {
|
|
@@ -855,12 +857,48 @@ async function mergeCopilotMcp(projectDir, mcpMap) {
|
|
|
855
857
|
return { file, added }
|
|
856
858
|
}
|
|
857
859
|
|
|
860
|
+
// Command Code: MCP de proyecto en `.mcp.json` (scope `project`, versionado con
|
|
861
|
+
// el repo). Esquema del host: `{ mcpServers: { <name>: { transport: 'stdio',
|
|
862
|
+
// command, args, env? } } }` (se descartan `type`/`enabled` de opencode). Merge NO
|
|
863
|
+
// destructivo: se preservan `mcpServers` y demas claves top-level, no se pisa un
|
|
864
|
+
// servidor homonimo, y un JSON invalido se avisa sin sobrescribir.
|
|
865
|
+
function toCommandCodeServer(def) {
|
|
866
|
+
const command = Array.isArray(def.command) ? def.command : [def.command]
|
|
867
|
+
const server = { transport: 'stdio', command: command[0], args: command.slice(1) }
|
|
868
|
+
if (def.env) server.env = def.env
|
|
869
|
+
return server
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
async function mergeCommandCodeMcp(projectDir, mcpMap) {
|
|
873
|
+
if (Object.keys(mcpMap).length === 0) return { file: null, added: [] }
|
|
874
|
+
const file = join(projectDir, '.mcp.json')
|
|
875
|
+
let cfg = {}
|
|
876
|
+
if (await exists(file)) {
|
|
877
|
+
try {
|
|
878
|
+
cfg = JSON.parse((await readFile(file, 'utf8')).replace(/^\uFEFF/, ''))
|
|
879
|
+
} catch {
|
|
880
|
+
console.warn('ancleto: no se pudo leer .mcp.json como JSON; MCP de Command Code no se modifico')
|
|
881
|
+
return { file, added: [] }
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
cfg.mcpServers = cfg.mcpServers || {}
|
|
885
|
+
const added = []
|
|
886
|
+
for (const [name, def] of Object.entries(mcpMap)) {
|
|
887
|
+
if (cfg.mcpServers[name]) continue
|
|
888
|
+
cfg.mcpServers[name] = toCommandCodeServer(def)
|
|
889
|
+
added.push(name)
|
|
890
|
+
}
|
|
891
|
+
await writeFile(file, JSON.stringify(cfg, null, 2) + '\n')
|
|
892
|
+
return { file, added }
|
|
893
|
+
}
|
|
894
|
+
|
|
858
895
|
// D6: dueno unico del MCP de host. Antigravity mergea `.agents/mcp_config.json`;
|
|
859
|
-
// el resto conserva `mergeMcp` sobre opencode.json
|
|
860
|
-
// proyecto o config global).
|
|
896
|
+
// Command Code mergea `.mcp.json`; el resto conserva `mergeMcp` sobre opencode.json
|
|
897
|
+
// (targetDir = .opencode del proyecto o config global).
|
|
861
898
|
async function setupHostMcp(targetDir, mcpMap, { projectDir = null, agent = null } = {}) {
|
|
862
899
|
if (Object.keys(mcpMap).length === 0) return { file: null, added: [] }
|
|
863
900
|
if (agent === 'antigravity' && projectDir) return mergeAntigravityMcp(projectDir, mcpMap)
|
|
901
|
+
if (agent === 'commandcode' && projectDir) return mergeCommandCodeMcp(projectDir, mcpMap)
|
|
864
902
|
await mkdir(targetDir, { recursive: true })
|
|
865
903
|
return mergeMcp(targetDir, mcpMap)
|
|
866
904
|
}
|
|
@@ -895,6 +933,39 @@ async function refreshCopilotMcp(projectDir) {
|
|
|
895
933
|
return { file, fixed }
|
|
896
934
|
}
|
|
897
935
|
|
|
936
|
+
// Regenera entradas rotas y agrega ausentes en `.mcp.json` de Command Code (para
|
|
937
|
+
// upgrade). Mismo criterio que `refreshCopilotMcp`, con el esquema del host.
|
|
938
|
+
async function refreshCommandCodeMcp(projectDir) {
|
|
939
|
+
const file = join(projectDir, '.mcp.json')
|
|
940
|
+
let cfg = {}
|
|
941
|
+
if (await exists(file)) {
|
|
942
|
+
try {
|
|
943
|
+
cfg = JSON.parse((await readFile(file, 'utf8')).replace(/^\uFEFF/, ''))
|
|
944
|
+
} catch {
|
|
945
|
+
cfg = {}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
cfg.mcpServers = cfg.mcpServers || {}
|
|
949
|
+
const fresh = buildDefaultMcp({ withEngram: Boolean(cfg.mcpServers.engram) })
|
|
950
|
+
const fixed = []
|
|
951
|
+
for (const [name, def] of Object.entries(cfg.mcpServers)) {
|
|
952
|
+
if (!fresh[name]) continue
|
|
953
|
+
const asArray = { command: [def.command, ...(Array.isArray(def.args) ? def.args : [])] }
|
|
954
|
+
if (mcpCommandBroken(asArray)) {
|
|
955
|
+
Object.assign(cfg.mcpServers[name], toCommandCodeServer(fresh[name]))
|
|
956
|
+
fixed.push(name)
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
for (const [name, def] of Object.entries(fresh)) {
|
|
960
|
+
if (!cfg.mcpServers[name]) {
|
|
961
|
+
cfg.mcpServers[name] = toCommandCodeServer(def)
|
|
962
|
+
fixed.push(`${name} (agregado)`)
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
await writeFile(file, JSON.stringify(cfg, null, 2) + '\n')
|
|
966
|
+
return { file, fixed }
|
|
967
|
+
}
|
|
968
|
+
|
|
898
969
|
async function copyAssets(dest) {
|
|
899
970
|
await mkdir(dest, { recursive: true })
|
|
900
971
|
for (const d of ASSETS) {
|
|
@@ -940,6 +1011,16 @@ const AGENT_TARGETS = {
|
|
|
940
1011
|
skills: null,
|
|
941
1012
|
agents: { dir: '.github/prompts', ext: '.prompt.md' },
|
|
942
1013
|
commands: { dir: '.github/prompts', ext: '.prompt.md' }
|
|
1014
|
+
},
|
|
1015
|
+
// Command Code: layout nativo por asset bajo `.commandcode/`. Los tres assets
|
|
1016
|
+
// son nativos: skills (Agent Skills, identity), agents (frontmatter adaptado,
|
|
1017
|
+
// ver `adaptCommandCodeFrontmatter`) y commands (custom slash commands, el body
|
|
1018
|
+
// es el prompt, copia verbatim). El MCP de proyecto vive en `.mcp.json`
|
|
1019
|
+
// (ver `mergeCommandCodeMcp`).
|
|
1020
|
+
commandcode: {
|
|
1021
|
+
skills: { dir: '.commandcode/skills' },
|
|
1022
|
+
agents: { dir: '.commandcode/agents', ext: MD_EXT },
|
|
1023
|
+
commands: { dir: '.commandcode/commands', ext: MD_EXT }
|
|
943
1024
|
}
|
|
944
1025
|
}
|
|
945
1026
|
|
|
@@ -956,6 +1037,18 @@ normal = flujo completo, minimo = pasos agrupados y económicos, gratis = modelo
|
|
|
956
1037
|
gratis disponible en tu cuenta.
|
|
957
1038
|
`
|
|
958
1039
|
|
|
1040
|
+
// Solo Command Code: los tiers tampoco cambian modelos (el agent no fija `model`,
|
|
1041
|
+
// hereda el de la sesión); la nota lo recuerda al empezar.
|
|
1042
|
+
const COMMANDCODE_MODEL_NOTE = `
|
|
1043
|
+
## Nota de modelo (solo Command Code)
|
|
1044
|
+
|
|
1045
|
+
Elegí el modelo con \`/model\` antes de empezar: los tiers de ancleto
|
|
1046
|
+
(normal|minimo|gratis) no cambian modelos en Command Code, solo el nivel de
|
|
1047
|
+
esfuerzo (normal = flujo completo, minimo = pasos agrupados y económicos,
|
|
1048
|
+
gratis = modelo gratis disponible en tu cuenta). Los subagentes heredan el
|
|
1049
|
+
modelo de la sesión porque su frontmatter no fija \`model\`.
|
|
1050
|
+
`
|
|
1051
|
+
|
|
959
1052
|
// D12: asset no soportado -> no se escribe y se avisa a stderr (no bloqueante).
|
|
960
1053
|
function warnUnsupportedAsset(asset, agent) {
|
|
961
1054
|
console.error(`skip ${asset}: not supported by host '${agent}'`)
|
|
@@ -985,7 +1078,11 @@ async function installAssetFiles(cat, projectDir, spec, host, srcDir = null) {
|
|
|
985
1078
|
const base = e.name.slice(0, -MD_EXT.length)
|
|
986
1079
|
const content = await readFile(join(origin, e.name), 'utf8')
|
|
987
1080
|
const adapted = adaptFrontmatter(content, host, cat, base)
|
|
988
|
-
|
|
1081
|
+
let body = adapted
|
|
1082
|
+
if (cat === 'agents' && base === 'orchestrator') {
|
|
1083
|
+
if (host === 'copilot') body += COPILOT_MODEL_NOTE
|
|
1084
|
+
else if (host === 'commandcode') body += COMMANDCODE_MODEL_NOTE
|
|
1085
|
+
}
|
|
989
1086
|
await writeFile(join(destDir, `${base}${spec.ext}`), body)
|
|
990
1087
|
}
|
|
991
1088
|
}
|
|
@@ -1490,6 +1587,12 @@ async function upgradeCmd(args) {
|
|
|
1490
1587
|
console.log(fixed.length
|
|
1491
1588
|
? ` ✔ MCP regenerados en ${file}: ${fixed.join(', ')}`
|
|
1492
1589
|
: ` ✔ MCP sanos en ${file}, sin cambios`)
|
|
1590
|
+
} else if (agent === 'commandcode') {
|
|
1591
|
+
const { file, fixed } = await refreshCommandCodeMcp(projectDir)
|
|
1592
|
+
console.log(` ✔ skills actualizadas en ${skillsDir} (${ANCLETO_SKILLS.length} skills)`)
|
|
1593
|
+
console.log(fixed.length
|
|
1594
|
+
? ` ✔ MCP regenerados en ${file}: ${fixed.join(', ')}`
|
|
1595
|
+
: ` ✔ MCP sanos en ${file}, sin cambios`)
|
|
1493
1596
|
} else {
|
|
1494
1597
|
console.log(` ✔ skills actualizadas en ${skillsDir} (${ANCLETO_SKILLS.length} skills)`)
|
|
1495
1598
|
}
|
|
@@ -2265,8 +2368,9 @@ async function checkAgentsFrontmatter(cwd, dirRel, actual, expected, fileExt) {
|
|
|
2265
2368
|
source = source.replace(/^model: .*$/m, `model: ${tierModelsMap[name]}`)
|
|
2266
2369
|
}
|
|
2267
2370
|
let expectedContent = adaptFrontmatter(source, host, 'agents', name)
|
|
2268
|
-
if (
|
|
2269
|
-
expectedContent += COPILOT_MODEL_NOTE
|
|
2371
|
+
if (name === 'orchestrator') {
|
|
2372
|
+
if (host === 'copilot') expectedContent += COPILOT_MODEL_NOTE
|
|
2373
|
+
else if (host === 'commandcode') expectedContent += COMMANDCODE_MODEL_NOTE
|
|
2270
2374
|
}
|
|
2271
2375
|
const installed = await readFile(join(cwd, dirRel, f), 'utf8')
|
|
2272
2376
|
if (installed !== expectedContent) {
|
|
@@ -12,12 +12,12 @@ export const AGENT_ADAPTER_DROP = new Set(['mode', 'color', 'temperature', 'perm
|
|
|
12
12
|
// Hosts conocidos por el modulo. Cualquier host fuera de este conjunto cae en la
|
|
13
13
|
// rama `unknown` (no aborta; emite aviso a stderr y retorna el contenido sin
|
|
14
14
|
// cambios).
|
|
15
|
-
const KNOWN_HOSTS = new Set(['opencode', 'claude', 'vscode', 'antigravity', 'cursor', 'roo', 'copilot'])
|
|
15
|
+
const KNOWN_HOSTS = new Set(['opencode', 'claude', 'vscode', 'antigravity', 'cursor', 'roo', 'copilot', 'commandcode'])
|
|
16
16
|
|
|
17
17
|
// Hosts para los que existe una transformacion documentada de agents
|
|
18
18
|
// (distinta de identidad). Cursor y Roo viven fuera: no hay formato de frontmatter
|
|
19
19
|
// propio, así que la adaptación es passthrough con aviso.
|
|
20
|
-
const TRANSFORM_AGENTS = new Set(['claude', 'vscode', 'antigravity', 'copilot'])
|
|
20
|
+
const TRANSFORM_AGENTS = new Set(['claude', 'vscode', 'antigravity', 'copilot', 'commandcode'])
|
|
21
21
|
|
|
22
22
|
// Tabla unica (fijada contra la tabla oficial de frontmatter de Custom
|
|
23
23
|
// Subagents) de ids de Antigravity. Conjunto CERRADO: solo se emiten estos ids.
|
|
@@ -35,6 +35,31 @@ export const ANTIGRAVITY_TOOL_MAP = {
|
|
|
35
35
|
todowrite: 'manage_task'
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Tabla unica (fijada contra la documentacion oficial de Command Code) de ids de
|
|
39
|
+
// tools. Conjunto CERRADO: solo se emiten estos ids. Toda clave del mapa `tools`
|
|
40
|
+
// de opencode sin id aqui se omite con aviso a stderr (nunca en silencio).
|
|
41
|
+
export const COMMANDCODE_TOOL_MAP = {
|
|
42
|
+
read: 'read_file',
|
|
43
|
+
write: 'write_file',
|
|
44
|
+
edit: 'edit_file',
|
|
45
|
+
bash: 'shell_command',
|
|
46
|
+
grep: 'grep',
|
|
47
|
+
glob: 'glob',
|
|
48
|
+
webfetch: 'web_fetch',
|
|
49
|
+
websearch: 'web_search',
|
|
50
|
+
todowrite: 'todo_write'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Tools de la memoria propia del framework. Command Code expone los servers MCP
|
|
54
|
+
// con la convencion documentada `mcp__<server>__<tool>`; `ancleto-memory` es el
|
|
55
|
+
// server que registra `buildDefaultMcp`. Estas claves del mapa `tools` de opencode
|
|
56
|
+
// son sus tools, asi que se emiten con el nombre MCP completo.
|
|
57
|
+
export const COMMANDCODE_MCP_TOOL_MAP = {
|
|
58
|
+
searchMemory: 'mcp__ancleto-memory__searchMemory',
|
|
59
|
+
recordRule: 'mcp__ancleto-memory__recordRule',
|
|
60
|
+
recordDecision: 'mcp__ancleto-memory__recordDecision'
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
// Parser propio sin dependencias. Cada entrada conserva su clave y sus lineas
|
|
39
64
|
// crudas (incluye block scalars y mapas anidados), preservando orden.
|
|
40
65
|
export function parseFrontmatter(content) {
|
|
@@ -147,6 +172,45 @@ function adaptAntigravityFrontmatter(content, name) {
|
|
|
147
172
|
return serializeFrontmatter(parsed)
|
|
148
173
|
}
|
|
149
174
|
|
|
175
|
+
// Adapta el frontmatter de un agent de opencode al de Command Code. `name` (el
|
|
176
|
+
// host lo requiere; default filename) se inyecta desde el nombre del archivo.
|
|
177
|
+
// `description` se preserva. `mode`/`color`/`temperature`/`permission` se eliminan
|
|
178
|
+
// y `model` se omite: Command Code hereda el modelo de sesion cuando no se declara
|
|
179
|
+
// (nunca se mapea un id de `opencode-go/*`). `tools` sale como lista de ids
|
|
180
|
+
// verificados (o `"*"` si el origen no declara tools: el default de opencode es
|
|
181
|
+
// "todas" y el de Command Code es "ninguna"). Una clave sin id verificado se omite
|
|
182
|
+
// con aviso a stderr (nunca en silencio).
|
|
183
|
+
function adaptCommandCodeFrontmatter(content, name) {
|
|
184
|
+
const parsed = parseFrontmatter(content)
|
|
185
|
+
if (!parsed.hasFrontmatter) return content
|
|
186
|
+
const byKey = (k) => parsed.entries.find((e) => e.key === k)
|
|
187
|
+
|
|
188
|
+
const managed = new Set([...AGENT_ADAPTER_DROP, 'name', 'description', 'tools'])
|
|
189
|
+
const kept = parsed.entries.filter((e) => e.key && !managed.has(e.key))
|
|
190
|
+
|
|
191
|
+
const toolsEntry = byKey('tools')
|
|
192
|
+
const toolIds = []
|
|
193
|
+
if (toolsEntry) {
|
|
194
|
+
for (const { key, value } of parseToolFlags(toolsEntry)) {
|
|
195
|
+
if (value === 'false') continue
|
|
196
|
+
const id = COMMANDCODE_TOOL_MAP[key] || COMMANDCODE_MCP_TOOL_MAP[key]
|
|
197
|
+
if (id) {
|
|
198
|
+
toolIds.push(id)
|
|
199
|
+
continue
|
|
200
|
+
}
|
|
201
|
+
console.error(`skip tool '${key}': no verified Command Code id for agent '${name}'`)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const out = [{ key: 'name', lines: [`name: ${name}`] }]
|
|
206
|
+
const description = byKey('description')
|
|
207
|
+
if (description) out.push(description)
|
|
208
|
+
out.push(toolsEntry ? { key: 'tools', lines: [`tools: [${toolIds.join(', ')}]`] } : { key: 'tools', lines: ['tools: "*"'] })
|
|
209
|
+
out.push(...kept)
|
|
210
|
+
parsed.entries = out
|
|
211
|
+
return serializeFrontmatter(parsed)
|
|
212
|
+
}
|
|
213
|
+
|
|
150
214
|
// Elimina las claves gestionadas por el adaptador (mode/color/temperature/
|
|
151
215
|
// permission/model/tools) preservando el resto y el body. Para claude, vscode y
|
|
152
216
|
// copilot. Si no hay frontmatter, retorna el contenido sin cambios.
|
|
@@ -166,6 +230,8 @@ function dropManagedKeys(content) {
|
|
|
166
230
|
// opencode → identity (passthrough)
|
|
167
231
|
// antigravity + agents → adaptAntigravityFrontmatter(content, name)
|
|
168
232
|
// antigravity + (skills/cmds) → identity
|
|
233
|
+
// commandcode + agents → adaptCommandCodeFrontmatter(content, name)
|
|
234
|
+
// commandcode + (skills/cmds) → identity
|
|
169
235
|
// claude/vscode/copilot + ags → dropManagedKeys(content)
|
|
170
236
|
// claude/vscode/copilot + sk → identity
|
|
171
237
|
// cursor/roo → identity + aviso stderr (sin formato documentado)
|
|
@@ -182,5 +248,6 @@ export function adaptFrontmatter(content, host, assetKind, name) {
|
|
|
182
248
|
if (assetKind !== 'agents') return content
|
|
183
249
|
if (!TRANSFORM_AGENTS.has(host)) return content // opencode u otro conocido sin transform
|
|
184
250
|
if (host === 'antigravity') return adaptAntigravityFrontmatter(content, name)
|
|
251
|
+
if (host === 'commandcode') return adaptCommandCodeFrontmatter(content, name)
|
|
185
252
|
return dropManagedKeys(content)
|
|
186
253
|
}
|