xonecode 0.5.1 → 0.8.0
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/apps/web/dist/assets/{index-BB3SamhR.css → index-Boak51UP.css} +1 -1
- package/apps/web/dist/assets/index-Cg28x6nG.js +477 -0
- package/apps/web/dist/assets/{php-D_2--9WK.js → php-Cu6F_QCJ.js} +1 -1
- package/apps/web/dist/assets/{ruby-DSHSDbk1.js → ruby-BiqQPoC6.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/dist/agent/{cloudstudioClient.js → cloudstudio/cloudstudioClient.js} +10 -0
- package/dist/agent/cloudstudio/cloudstudioClient.js.map +1 -0
- package/dist/agent/{cloudstudioMcp.js → cloudstudio/cloudstudioMcp.js} +5 -5
- package/dist/agent/cloudstudio/cloudstudioMcp.js.map +1 -0
- package/dist/agent/{descarga.js → cloudstudio/descarga.js} +17 -7
- package/dist/agent/cloudstudio/descarga.js.map +1 -0
- package/dist/agent/cloudstudio/manifiesto.js.map +1 -0
- package/dist/agent/cloudstudio/ramaActiva.js +11 -0
- package/dist/agent/cloudstudio/ramaActiva.js.map +1 -0
- package/dist/agent/{subida.js → cloudstudio/subida.js} +16 -7
- package/dist/agent/cloudstudio/subida.js.map +1 -0
- package/dist/agent/{zip.js → cloudstudio/zip.js} +1 -1
- package/dist/agent/cloudstudio/zip.js.map +1 -0
- package/dist/agent/{authEnDisco.js → config/authEnDisco.js} +3 -3
- package/dist/agent/config/authEnDisco.js.map +1 -0
- package/dist/agent/{catalogoModelos.js → config/catalogoModelos.js} +47 -1
- package/dist/agent/config/catalogoModelos.js.map +1 -0
- package/dist/agent/{configEnDisco.js → config/configEnDisco.js} +6 -6
- package/dist/agent/config/configEnDisco.js.map +1 -0
- package/dist/agent/{crearProyecto.js → config/crearProyecto.js} +2 -2
- package/dist/agent/config/crearProyecto.js.map +1 -0
- package/dist/agent/config/entorno.js.map +1 -0
- package/dist/agent/config/gemini.js.map +1 -0
- package/dist/agent/config/modelos.js +235 -0
- package/dist/agent/config/modelos.js.map +1 -0
- package/dist/agent/{modelosDeMotor.js → config/modelosDeMotor.js} +1 -1
- package/dist/agent/config/modelosDeMotor.js.map +1 -0
- package/dist/agent/config/mudanzaEnDisco.js +121 -0
- package/dist/agent/config/mudanzaEnDisco.js.map +1 -0
- package/dist/agent/{persona.js → config/persona.js} +1 -1
- package/dist/agent/config/persona.js.map +1 -0
- package/dist/agent/config/selectorEnMaquina.js +45 -0
- package/dist/agent/config/selectorEnMaquina.js.map +1 -0
- package/dist/agent/{settingsEnDisco.js → config/settingsEnDisco.js} +37 -2
- package/dist/agent/config/settingsEnDisco.js.map +1 -0
- package/dist/agent/{versionEnDisco.js → config/versionEnDisco.js} +8 -4
- package/dist/agent/config/versionEnDisco.js.map +1 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js +146 -0
- package/dist/agent/dispositivos/arranqueDeEmulador.js.map +1 -0
- package/dist/agent/{dispositivosEnMaquina.js → dispositivos/dispositivosEnMaquina.js} +93 -6
- package/dist/agent/dispositivos/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/hotswap.js.map +1 -0
- package/dist/agent/dispositivos/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/juezVisual.js +152 -0
- package/dist/agent/dispositivos/juezVisual.js.map +1 -0
- package/dist/agent/{lanzamientoEnMaquina.js → dispositivos/lanzamientoEnMaquina.js} +1 -1
- package/dist/agent/dispositivos/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/{paqueteDelProyecto.js → dispositivos/paqueteDelProyecto.js} +5 -5
- package/dist/agent/dispositivos/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/dispositivos/procesosEnMaquina.js.map +1 -0
- package/dist/agent/dispositivos/sensorDeArranque.js +154 -0
- package/dist/agent/dispositivos/sensorDeArranque.js.map +1 -0
- package/dist/agent/exportarPdf.js +140 -0
- package/dist/agent/exportarPdf.js.map +1 -0
- package/dist/agent/{arbolDeProyecto.js → grafo/arbolDeProyecto.js} +2 -2
- package/dist/agent/grafo/arbolDeProyecto.js.map +1 -0
- package/dist/agent/{artefactosEnDisco.js → grafo/artefactosEnDisco.js} +2 -2
- package/dist/agent/grafo/artefactosEnDisco.js.map +1 -0
- package/dist/agent/{busquedaRegex.js → grafo/busquedaRegex.js} +41 -1
- package/dist/agent/grafo/busquedaRegex.js.map +1 -0
- package/dist/agent/grafo/criticaVisual.js +115 -0
- package/dist/agent/grafo/criticaVisual.js.map +1 -0
- package/dist/agent/grafo/excluirTools.js +82 -0
- package/dist/agent/grafo/excluirTools.js.map +1 -0
- package/dist/agent/{memoriaDeProyecto.js → grafo/memoriaDeProyecto.js} +1 -1
- package/dist/agent/grafo/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/grafo/navegacionXone.js +231 -0
- package/dist/agent/grafo/navegacionXone.js.map +1 -0
- package/dist/agent/{perfiles.js → grafo/perfiles.js} +96 -10
- package/dist/agent/grafo/perfiles.js.map +1 -0
- package/dist/agent/{proyecto.js → grafo/proyecto.js} +274 -24
- package/dist/agent/grafo/proyecto.js.map +1 -0
- package/dist/agent/grafo/skills.js +425 -0
- package/dist/agent/grafo/skills.js.map +1 -0
- package/dist/agent/grafo/xoneAgent.js +566 -0
- package/dist/agent/grafo/xoneAgent.js.map +1 -0
- package/dist/agent/navegacion/estilosEnDisco.js +110 -0
- package/dist/agent/navegacion/estilosEnDisco.js.map +1 -0
- package/dist/agent/navegacion/hechosEnDisco.js +26 -0
- package/dist/agent/navegacion/hechosEnDisco.js.map +1 -0
- package/dist/agent/navegacion/indiceEnDisco.js +36 -0
- package/dist/agent/navegacion/indiceEnDisco.js.map +1 -0
- package/dist/agent/navegacion/modeloDeProyecto.js +228 -0
- package/dist/agent/navegacion/modeloDeProyecto.js.map +1 -0
- package/dist/agent/raizDelPaquete.js +56 -0
- package/dist/agent/raizDelPaquete.js.map +1 -0
- package/dist/agent/{checkpointer.js → sesiones/checkpointer.js} +1 -1
- package/dist/agent/sesiones/checkpointer.js.map +1 -0
- package/dist/agent/sesiones/git.js.map +1 -0
- package/dist/agent/{gitSync.js → sesiones/gitSync.js} +3 -3
- package/dist/agent/sesiones/gitSync.js.map +1 -0
- package/dist/agent/{sesionGit.js → sesiones/sesionGit.js} +2 -2
- package/dist/agent/sesiones/sesionGit.js.map +1 -0
- package/dist/agent/subagentes/agentesEnDisco.js +818 -0
- package/dist/agent/subagentes/agentesEnDisco.js.map +1 -0
- package/dist/agent/subagentes/consumoExterno.js.map +1 -0
- package/dist/agent/subagentes/escrituraDeCodex.js.map +1 -0
- package/dist/agent/{escrituraDeOpencode.js → subagentes/escrituraDeOpencode.js} +1 -1
- package/dist/agent/subagentes/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/{escrituraExterna.js → subagentes/escrituraExterna.js} +59 -6
- package/dist/agent/subagentes/escrituraExterna.js.map +1 -0
- package/dist/agent/subagentes/pluginDeSkills.js +99 -0
- package/dist/agent/subagentes/pluginDeSkills.js.map +1 -0
- package/dist/agent/subagentes/subagenteCodex.js.map +1 -0
- package/dist/agent/{subagenteExterno.js → subagentes/subagenteExterno.js} +48 -0
- package/dist/agent/subagentes/subagenteExterno.js.map +1 -0
- package/dist/agent/{subagenteOpencode.js → subagentes/subagenteOpencode.js} +53 -1
- package/dist/agent/subagentes/subagenteOpencode.js.map +1 -0
- package/dist/agent/{aumentador.js → tareas/aumentador.js} +2 -2
- package/dist/agent/tareas/aumentador.js.map +1 -0
- package/dist/agent/{juezDeTarea.js → tareas/juezDeTarea.js} +6 -6
- package/dist/agent/tareas/juezDeTarea.js.map +1 -0
- package/dist/agent/{tareasEnDisco.js → tareas/tareasEnDisco.js} +53 -4
- package/dist/agent/tareas/tareasEnDisco.js.map +1 -0
- package/dist/agent/{diagnosticoDeTools.js → turno/diagnosticoDeTools.js} +5 -1
- package/dist/agent/turno/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/{guionizado.js → turno/guionizado.js} +1 -1
- package/dist/agent/turno/guionizado.js.map +1 -0
- package/dist/agent/turno/informeDeTraza.js +245 -0
- package/dist/agent/turno/informeDeTraza.js.map +1 -0
- package/dist/agent/turno/inspectorDePrompt.js +205 -0
- package/dist/agent/turno/inspectorDePrompt.js.map +1 -0
- package/dist/agent/{instantanea.js → turno/instantanea.js} +7 -2
- package/dist/agent/turno/instantanea.js.map +1 -0
- package/dist/agent/{interrupts.js → turno/interrupts.js} +2 -2
- package/dist/agent/turno/interrupts.js.map +1 -0
- package/dist/agent/turno/mensajes.js.map +1 -0
- package/dist/agent/turno/normalizar.js.map +1 -0
- package/dist/agent/{puente.js → turno/puente.js} +71 -4
- package/dist/agent/turno/puente.js.map +1 -0
- package/dist/agent/turno/resumenDeContexto.js +260 -0
- package/dist/agent/turno/resumenDeContexto.js.map +1 -0
- package/dist/agent/{resumenDeTool.js → turno/resumenDeTool.js} +33 -0
- package/dist/agent/turno/resumenDeTool.js.map +1 -0
- package/dist/agent/turno/rubrica.js +91 -0
- package/dist/agent/turno/rubrica.js.map +1 -0
- package/dist/agent/turno/textoDeTool.js.map +1 -0
- package/dist/agent/{turnoReal.js → turno/turnoReal.js} +210 -25
- package/dist/agent/turno/turnoReal.js.map +1 -0
- package/dist/agent/turno/verificador.js.map +1 -0
- package/dist/cli/config.js +1 -1
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +72 -9
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/describe.js +2 -2
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/fuentesDeDisco.js +29 -0
- package/dist/cli/fuentesDeDisco.js.map +1 -0
- package/dist/cli/main.js +87 -31
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/run.js +40 -10
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/traza.js +48 -0
- package/dist/cli/traza.js.map +1 -0
- package/dist/cli/tui/correrTui.js +4 -4
- package/dist/cli/tui/correrTui.js.map +1 -1
- package/dist/cli/verify.js +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/core/actos.js +1 -1
- package/dist/core/adjuntos.js +2 -2
- package/dist/core/agentes.js +247 -7
- package/dist/core/agentes.js.map +1 -1
- package/dist/core/artefactos.js +4 -4
- package/dist/core/config.js +1 -1
- package/dist/core/contextos.js +12 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +1 -1
- package/dist/core/dispositivos.js +53 -3
- package/dist/core/dispositivos.js.map +1 -1
- package/dist/core/esfuerzo.js +141 -0
- package/dist/core/esfuerzo.js.map +1 -0
- package/dist/core/esqueleto.js +1 -1
- package/dist/core/estilos.js +78 -0
- package/dist/core/estilos.js.map +1 -0
- package/dist/core/exportacion.js +140 -0
- package/dist/core/exportacion.js.map +1 -0
- package/dist/core/hechosDelProyecto.js +87 -0
- package/dist/core/hechosDelProyecto.js.map +1 -0
- package/dist/core/logDeDispositivo.js +143 -0
- package/dist/core/logDeDispositivo.js.map +1 -0
- package/dist/core/modelos.js +48 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/mudanzaDeWorkspace.js +93 -0
- package/dist/core/mudanzaDeWorkspace.js.map +1 -0
- package/dist/core/navegacion.js +134 -0
- package/dist/core/navegacion.js.map +1 -0
- package/dist/core/notify.js +5 -0
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +1 -1
- package/dist/core/planes.js +70 -0
- package/dist/core/planes.js.map +1 -0
- package/dist/core/ports.js +4 -0
- package/dist/core/ports.js.map +1 -1
- package/dist/core/rubrica.js +90 -0
- package/dist/core/rubrica.js.map +1 -0
- package/dist/core/selectorDeCarpeta.js +87 -0
- package/dist/core/selectorDeCarpeta.js.map +1 -0
- package/dist/core/settings.js +56 -7
- package/dist/core/settings.js.map +1 -1
- package/dist/core/shellDeAgente.js +144 -0
- package/dist/core/shellDeAgente.js.map +1 -0
- package/dist/core/skills.js +158 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/topeAgotado.js +69 -0
- package/dist/core/topeAgotado.js.map +1 -0
- package/dist/core/zipDeSkill.js +107 -0
- package/dist/core/zipDeSkill.js.map +1 -0
- package/dist/web/servidor/arranque.js +853 -54
- package/dist/web/servidor/arranque.js.map +1 -1
- package/dist/web/servidor/corredorDeTareas.js +3 -3
- package/dist/web/servidor/corredorDeTareas.js.map +1 -1
- package/dist/web/servidor/sesiones.js +34 -6
- package/dist/web/servidor/sesiones.js.map +1 -1
- package/dist/web/servidor/transporte.js +1 -1
- package/dist/web/servidor/transporte.js.map +1 -1
- package/dist/web/servidor/vestibulo.js +62 -13
- package/dist/web/servidor/vestibulo.js.map +1 -1
- package/package.json +7 -4
- package/skills/archify/SKILL.md +25 -2
- package/skills/artifacts-builder/SKILL.md +23 -0
- package/skills/xone-hotswap/SKILL.md +96 -0
- package/skills/xone-hotswap/scripts/xone-arrancar-android +202 -0
- package/skills/xone-hotswap/scripts/xone-arrancar-ios +85 -0
- package/skills/xone-hotswap/scripts/xone-captura-android +93 -0
- package/skills/xone-hotswap/scripts/xone-desplegar-android +439 -0
- package/skills/xone-hotswap/scripts/xone-hotswap +201 -0
- package/skills/xone-hotswap/scripts/xone-log-android +102 -0
- package/skills/xone-hotswap/scripts/xone-reiniciar-android +132 -0
- package/skills/xone-plan-builder/SKILL.md +22 -4
- package/skills/xone-spec-builder/SKILL.md +37 -7
- package/apps/web/dist/assets/index-CFYAoHdw.js +0 -477
- package/dist/agent/agentesEnDisco.js +0 -539
- package/dist/agent/agentesEnDisco.js.map +0 -1
- package/dist/agent/arbolDeProyecto.js.map +0 -1
- package/dist/agent/artefactosEnDisco.js.map +0 -1
- package/dist/agent/aumentador.js.map +0 -1
- package/dist/agent/authEnDisco.js.map +0 -1
- package/dist/agent/busquedaRegex.js.map +0 -1
- package/dist/agent/catalogoModelos.js.map +0 -1
- package/dist/agent/checkpointer.js.map +0 -1
- package/dist/agent/cloudstudioClient.js.map +0 -1
- package/dist/agent/cloudstudioMcp.js.map +0 -1
- package/dist/agent/configEnDisco.js.map +0 -1
- package/dist/agent/consumoExterno.js.map +0 -1
- package/dist/agent/crearProyecto.js.map +0 -1
- package/dist/agent/descarga.js.map +0 -1
- package/dist/agent/diagnosticoDeTools.js.map +0 -1
- package/dist/agent/dispositivosEnMaquina.js.map +0 -1
- package/dist/agent/entorno.js.map +0 -1
- package/dist/agent/escrituraDeCodex.js.map +0 -1
- package/dist/agent/escrituraDeOpencode.js.map +0 -1
- package/dist/agent/escrituraExterna.js.map +0 -1
- package/dist/agent/gemini.js.map +0 -1
- package/dist/agent/git.js.map +0 -1
- package/dist/agent/gitSync.js.map +0 -1
- package/dist/agent/guionizado.js.map +0 -1
- package/dist/agent/hotswap.js.map +0 -1
- package/dist/agent/instalacionEnMaquina.js.map +0 -1
- package/dist/agent/instantanea.js.map +0 -1
- package/dist/agent/interrupts.js.map +0 -1
- package/dist/agent/juezDeTarea.js.map +0 -1
- package/dist/agent/lanzamientoEnMaquina.js.map +0 -1
- package/dist/agent/manifiesto.js.map +0 -1
- package/dist/agent/memoriaDeProyecto.js.map +0 -1
- package/dist/agent/mensajes.js.map +0 -1
- package/dist/agent/modelos.js +0 -130
- package/dist/agent/modelos.js.map +0 -1
- package/dist/agent/modelosDeMotor.js.map +0 -1
- package/dist/agent/normalizar.js.map +0 -1
- package/dist/agent/paqueteDelProyecto.js.map +0 -1
- package/dist/agent/perfiles.js.map +0 -1
- package/dist/agent/persona.js.map +0 -1
- package/dist/agent/procesosEnMaquina.js.map +0 -1
- package/dist/agent/proyecto.js.map +0 -1
- package/dist/agent/puente.js.map +0 -1
- package/dist/agent/resumenDeContexto.js +0 -30
- package/dist/agent/resumenDeContexto.js.map +0 -1
- package/dist/agent/resumenDeTool.js.map +0 -1
- package/dist/agent/sesionGit.js.map +0 -1
- package/dist/agent/settingsEnDisco.js.map +0 -1
- package/dist/agent/skills.js +0 -65
- package/dist/agent/skills.js.map +0 -1
- package/dist/agent/subagenteCodex.js.map +0 -1
- package/dist/agent/subagenteExterno.js.map +0 -1
- package/dist/agent/subagenteOpencode.js.map +0 -1
- package/dist/agent/subida.js.map +0 -1
- package/dist/agent/tareasEnDisco.js.map +0 -1
- package/dist/agent/textoDeTool.js.map +0 -1
- package/dist/agent/turnoReal.js.map +0 -1
- package/dist/agent/verificador.js.map +0 -1
- package/dist/agent/versionEnDisco.js.map +0 -1
- package/dist/agent/xoneAgent.js +0 -347
- package/dist/agent/xoneAgent.js.map +0 -1
- package/dist/agent/zip.js.map +0 -1
- /package/dist/agent/{manifiesto.js → cloudstudio/manifiesto.js} +0 -0
- /package/dist/agent/{entorno.js → config/entorno.js} +0 -0
- /package/dist/agent/{gemini.js → config/gemini.js} +0 -0
- /package/dist/agent/{hotswap.js → dispositivos/hotswap.js} +0 -0
- /package/dist/agent/{instalacionEnMaquina.js → dispositivos/instalacionEnMaquina.js} +0 -0
- /package/dist/agent/{procesosEnMaquina.js → dispositivos/procesosEnMaquina.js} +0 -0
- /package/dist/agent/{git.js → sesiones/git.js} +0 -0
- /package/dist/agent/{consumoExterno.js → subagentes/consumoExterno.js} +0 -0
- /package/dist/agent/{escrituraDeCodex.js → subagentes/escrituraDeCodex.js} +0 -0
- /package/dist/agent/{subagenteCodex.js → subagentes/subagenteCodex.js} +0 -0
- /package/dist/agent/{mensajes.js → turno/mensajes.js} +0 -0
- /package/dist/agent/{normalizar.js → turno/normalizar.js} +0 -0
- /package/dist/agent/{textoDeTool.js → turno/textoDeTool.js} +0 -0
- /package/dist/agent/{verificador.js → turno/verificador.js} +0 -0
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Los subagentes en disco: `<base>/.xonecode/agentes/<nombre>.md`, uno por fichero.
|
|
3
|
+
*
|
|
4
|
+
* Ficheros y no una entrada en `config.json` por tres razones. El cuerpo son
|
|
5
|
+
* INSTRUCCIONES, o sea prosa larga, y la prosa dentro de un JSON se vuelve inmanejable en
|
|
6
|
+
* cuanto pasa de dos frases. Es la convención que ya conoce quien viene de Claude Code
|
|
7
|
+
* (`.claude/agents/*.md`), así que el fichero se lee sin explicar nada. Y se edita a mano y
|
|
8
|
+
* se diffea, que es lo que va a pasar de verdad con un prompt que se afina.
|
|
9
|
+
*
|
|
10
|
+
* `.xonecode/` hereda además dos protecciones que aquí importan: está denegada entera al
|
|
11
|
+
* agente (`permisosDe`) y **no sube nunca** a CloudStudio. Un subagente es configuración de
|
|
12
|
+
* la herramienta, no del proyecto XOne — no tiene por qué acabar en Studio.
|
|
13
|
+
*
|
|
14
|
+
* Dos sitios, con la misma precedencia que los modelos: el GLOBAL (`~/.xonecode/agentes/`)
|
|
15
|
+
* y el del PROYECTO, y el de proyecto gana. Un «revisor de XOne» se quiere en todos los
|
|
16
|
+
* proyectos; un «experto en esta app», solo en uno.
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
20
|
+
import { homedir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { escribirAgente, fusionarAgentes, leerAgente, motivoDeNombreInaceptable, nombreSugerido, } from "../../core/agentes.js";
|
|
23
|
+
import { segmentoSeguro } from "../../core/settings.js";
|
|
24
|
+
import { NOMBRE_CARPETA } from "../config/configEnDisco.js";
|
|
25
|
+
const CARPETA = "agentes";
|
|
26
|
+
/** La carpeta de agentes de una raíz cualquiera. */
|
|
27
|
+
export function rutaDeAgentes(base) {
|
|
28
|
+
return join(base, NOMBRE_CARPETA, CARPETA);
|
|
29
|
+
}
|
|
30
|
+
/** La global. `homedir()` en el momento de la llamada, como el resto del repo: cachearla en
|
|
31
|
+
* una constante de módulo la congelaría para los tests que la cambian. */
|
|
32
|
+
export function rutaGlobalDeAgentes() {
|
|
33
|
+
return join(homedir(), NOMBRE_CARPETA, CARPETA);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lee una carpeta. Un fichero que no se puede leer o que no valida NO tumba la carga: se
|
|
37
|
+
* salta, se apunta el motivo y los demás siguen — es la misma postura que `reabrirSesion`
|
|
38
|
+
* con una línea corrupta del `.jsonl`, y la que evita que un `.md` a medio escribir deje la
|
|
39
|
+
* consola sin ningún subagente.
|
|
40
|
+
*/
|
|
41
|
+
export function leerCarpetaDeAgentes(carpeta, origen) {
|
|
42
|
+
if (!existsSync(carpeta))
|
|
43
|
+
return { agentes: [], problemas: [] };
|
|
44
|
+
const agentes = [];
|
|
45
|
+
const problemas = [];
|
|
46
|
+
for (const fichero of readdirSync(carpeta).sort()) {
|
|
47
|
+
if (!fichero.endsWith(".md"))
|
|
48
|
+
continue;
|
|
49
|
+
const nombre = fichero.slice(0, -3);
|
|
50
|
+
let contenido;
|
|
51
|
+
try {
|
|
52
|
+
contenido = readFileSync(join(carpeta, fichero), "utf8");
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
problemas.push(`${fichero}: no se pudo leer (${e.message})`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const r = leerAgente(nombre, contenido, origen);
|
|
59
|
+
if ("error" in r) {
|
|
60
|
+
problemas.push(`${fichero}: ${r.error}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Un nombre que no es un slug se DICE y el agente se CARGA.
|
|
65
|
+
*
|
|
66
|
+
* Rechazarlo aquí haría desaparecer un subagente que funciona, que es lo contrario de lo
|
|
67
|
+
* que hace todo este módulo — y el usuario no tendría ni el nombre para buscarlo. Quien
|
|
68
|
+
* lo rechaza es el guardado (`arranque.ts#atenderAgente`), que es el único momento con
|
|
69
|
+
* alguien delante para arreglarlo. El aviso va por `problemas`, y ahí sí encaja: esto no
|
|
70
|
+
* es un estado del agente, es trabajo pendiente sobre su fichero.
|
|
71
|
+
*
|
|
72
|
+
* Y lleva el nombre que SÍ valdría, porque un aviso que no se puede obedecer no sirve:
|
|
73
|
+
* «renómbralo a `documentador`» se arregla en el mismo formulario donde se lee.
|
|
74
|
+
*/
|
|
75
|
+
const malNombre = motivoDeNombreInaceptable(nombre);
|
|
76
|
+
if (malNombre !== undefined) {
|
|
77
|
+
const sugerido = nombreSugerido(nombre);
|
|
78
|
+
problemas.push(`${fichero}: el nombre ${malNombre}` +
|
|
79
|
+
(sugerido === undefined ? ". Renómbralo." : `. Renómbralo a \`${sugerido}\`.`));
|
|
80
|
+
}
|
|
81
|
+
agentes.push(r.agente);
|
|
82
|
+
}
|
|
83
|
+
return { agentes, problemas };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Los agentes en vigor para un proyecto: los globales, pisados por los del proyecto.
|
|
87
|
+
*
|
|
88
|
+
* `raizDelProyecto` es opcional porque el vestíbulo existe ANTES de que haya proyecto
|
|
89
|
+
* abierto (`web/servidor/vestibulo.ts`) y la ventana de ajustes se abre desde ahí: sin
|
|
90
|
+
* raíz se contestan los globales, que es la verdad, en vez de una lista vacía.
|
|
91
|
+
*/
|
|
92
|
+
export function cargarAgentes(raizDelProyecto) {
|
|
93
|
+
// La siembra se hace AQUÍ, y no en el arranque de cada piel. Medido: estaba en
|
|
94
|
+
// `main.ts#entrarEnConsola` y la rama web devuelve antes de llegar ahí, así que
|
|
95
|
+
// `npm run web` no sembraba nada — la consola arrancaba sin un solo subagente y el
|
|
96
|
+
// orquestador sin nadie a quien delegar, sin que nada diera error. Colgarlo del cargador
|
|
97
|
+
// lo hace imposible de olvidar: quien necesita agentes los pide por aquí, y por
|
|
98
|
+
// construcción hay algo que leer. Es idempotente y no hace nada si la carpeta existe.
|
|
99
|
+
const { desactualizados, retirados } = sembrarAgentes();
|
|
100
|
+
const global = leerCarpetaDeAgentes(rutaGlobalDeAgentes(), "global");
|
|
101
|
+
const proyecto = raizDelProyecto === undefined
|
|
102
|
+
? { agentes: [], problemas: [] }
|
|
103
|
+
: leerCarpetaDeAgentes(rutaDeAgentes(raizDelProyecto), "proyecto");
|
|
104
|
+
// Los que se quedaron atrás NO van a `problemas`, y eso es un cambio medido: iban, y por
|
|
105
|
+
// el mismo canal que un `.md` que no carga, así que la consola pintaba en rojo y arriba del
|
|
106
|
+
// todo dos agentes que están perfectamente. Y su frase —«bórralo si quieres el nuevo»—
|
|
107
|
+
// mandaba a usar una escapatoria que no existe: borrar uno de serie no lo resiembra (la
|
|
108
|
+
// marca recuerda que se entregó), así que dejaba sin ninguno de los dos y para siempre.
|
|
109
|
+
// Ahora el estado viaja POR AGENTE (`marcarSemilla`) y se dice en su tarjeta, que es donde
|
|
110
|
+
// está el botón que lo arregla — `restaurarAgente`. `problemas` vuelve a significar solo
|
|
111
|
+
// «este fichero no se pudo cargar»; comprobado que nadie más lo lee (`turnoReal` y los
|
|
112
|
+
// evals se quedan con `.agentes`).
|
|
113
|
+
// Y los renombrados, por el mismo canal y por la misma razón: el especialista cambia de
|
|
114
|
+
// nombre (o se va con él), y enterarse por la lista sin que nadie lo explique es la clase de
|
|
115
|
+
// sorpresa silenciosa que este módulo existe para no dar. El que se retiró se dice UNA vez
|
|
116
|
+
// —la clave se va con el fichero—; el que se respeta, cada arranque, porque queda decidir.
|
|
117
|
+
const renombrados = retirados.map(({ nombre, ahoraSeLlama, borrado }) => borrado
|
|
118
|
+
? `${nombre}.md: se ha retirado —ese agente se llama ahora \`${ahoraSeLlama}\`— y el nuevo ya está sembrado.`
|
|
119
|
+
: `${nombre}.md: ese agente se llama ahora \`${ahoraSeLlama}\`, y el tuyo se respeta. Bórralo si quieres quedarte solo con el de serie.`);
|
|
120
|
+
return {
|
|
121
|
+
agentes: marcarSemilla(fusionarAgentes(global.agentes, proyecto.agentes), desactualizados),
|
|
122
|
+
problemas: [...renombrados, ...global.problemas, ...proyecto.problemas],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** ¿Es uno de los que trae xonecode? La guarda del `borrar`, y la lista de la pantalla. */
|
|
126
|
+
export function esDeSerie(nombre) {
|
|
127
|
+
return AGENTES_DE_SERIE.some((a) => a.nombre === nombre);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* De quién es cada `.md`: ausente si lo escribió el usuario, `intacta`/`modificada` si es
|
|
131
|
+
* uno de los nuestros. Ver `AgenteCargado` para los tres estados y qué se hace con cada uno.
|
|
132
|
+
*
|
|
133
|
+
* Pura y exportada a propósito, no compuesta dentro de `cargarAgentes`: es el patrón de
|
|
134
|
+
* fallo de esta arquitectura —una regla que vive en un cierre que todos los tests doblan—, y
|
|
135
|
+
* la trampa de abajo es justo la que se queda sin probar así.
|
|
136
|
+
*
|
|
137
|
+
* **La regla es «de serie Y del GLOBAL», y el segundo requisito no es decorativo.** La
|
|
138
|
+
* siembra solo toca el global, así que un `docs.md` en `.xonecode/agentes/` DEL PROYECTO lo
|
|
139
|
+
* escribió el usuario aunque se llame igual que uno nuestro. Con `nombre ∈ AGENTES_DE_SERIE`
|
|
140
|
+
* a secas se quedaría sin botón de borrar —un fichero suyo que no puede borrar— y con un
|
|
141
|
+
* «Restaurar el de serie» que le pisaría el suyo con el global.
|
|
142
|
+
*/
|
|
143
|
+
export function marcarSemilla(agentes, desactualizados) {
|
|
144
|
+
return agentes.map((a) => a.origen === "global" && esDeSerie(a.nombre)
|
|
145
|
+
? { ...a, semilla: desactualizados.includes(a.nombre) ? "modificada" : "intacta" }
|
|
146
|
+
: a);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Escribe un agente.
|
|
150
|
+
*
|
|
151
|
+
* `segmentoSeguro` por lo mismo que en `sesiones.ts`: el nombre llega del cliente por HTTP,
|
|
152
|
+
* y un `../../.env` compondría una ruta fuera de la carpeta. Misma función que usa
|
|
153
|
+
* `rutaDeWorkspace`, no una copia — dos guardas es cómo una se corrige y la otra no. LANZA
|
|
154
|
+
* en vez de limpiar, y está bien: limpiar el nombre hasta hacerlo válido guardaría el
|
|
155
|
+
* agente con uno que nadie pidió, y quien lo mandó creería que se llama de otra forma.
|
|
156
|
+
*
|
|
157
|
+
* La validación va ANTES del `mkdirSync`, y no es cosmético: al revés, un nombre rechazado
|
|
158
|
+
* dejaba la carpeta creada de todas formas — y la carpeta es justo la marca de «ya se
|
|
159
|
+
* sembró» (`sembrarAgentes`), así que un intento fallido en el global habría impedido para
|
|
160
|
+
* siempre que se sembraran los cuatro de serie.
|
|
161
|
+
*/
|
|
162
|
+
export function guardarAgente(base, agente) {
|
|
163
|
+
const seguro = segmentoSeguro(agente.nombre, "nombre de agente");
|
|
164
|
+
const carpeta = rutaDeAgentes(base);
|
|
165
|
+
mkdirSync(carpeta, { recursive: true });
|
|
166
|
+
writeFileSync(join(carpeta, `${seguro}.md`), escribirAgente({ ...agente, nombre: seguro }), "utf8");
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Devuelve un agente de serie a como lo entregamos, pisando lo que el usuario tuviera.
|
|
170
|
+
*
|
|
171
|
+
* Es lo que ocupa el sitio del borrado en un agente sembrado, y existe porque el borrado
|
|
172
|
+
* NO hacía lo que la consola prometía: la marca recuerda que se entregó, así que
|
|
173
|
+
* `sembrarAgentes` no lo resiembra (ver «uno BORRADO no se resucita») — o sea que el
|
|
174
|
+
* «bórralo si quieres el nuevo» dejaba sin ninguno de los dos y para siempre.
|
|
175
|
+
*
|
|
176
|
+
* **No escribe la marca, y es deliberado.** La reanota la siembra siguiente, que corre ANTES
|
|
177
|
+
* de cualquier lectura —`cargarAgentes` la llama primero— y que ya sabe reconocer su propio
|
|
178
|
+
* hash. Escribirla aquí sería un segundo sitio donde decidir sobre la marca, y el único que
|
|
179
|
+
* podría resucitar un agente que el usuario borró a propósito: con una marca ilegible, este
|
|
180
|
+
* camino la reescribiría con una sola clave y las demás pasarían por «nunca entregadas».
|
|
181
|
+
*
|
|
182
|
+
* Devuelve si pudo, como `borrarAgente`: de algo que no es de serie no hay versión nuestra
|
|
183
|
+
* que poner, y la interfaz no puede decir «restaurado» de eso.
|
|
184
|
+
*/
|
|
185
|
+
export function restaurarAgente(base, nombre) {
|
|
186
|
+
const agente = AGENTES_DE_SERIE.find((a) => a.nombre === nombre);
|
|
187
|
+
if (agente === undefined)
|
|
188
|
+
return false;
|
|
189
|
+
const carpeta = rutaDeAgentes(base);
|
|
190
|
+
mkdirSync(carpeta, { recursive: true });
|
|
191
|
+
writeFileSync(join(carpeta, `${agente.nombre}.md`), escribirAgente(agente), "utf8");
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Renombra un subagente, escribiendo a la vez lo que se haya cambiado de él.
|
|
196
|
+
*
|
|
197
|
+
* **`renameSync` y LUEGO escribir**, nunca escribir y luego borrar: un fallo entre los dos
|
|
198
|
+
* pasos deja UN fichero —el renombrado, con el contenido de antes—, nunca dos con el mismo
|
|
199
|
+
* prompt ni cero. Al revés, una excepción entre el `write` y el `rm` deja los DOS, que es
|
|
200
|
+
* justo lo que el campo deshabilitado del formulario evitaba: dos subagentes con la misma
|
|
201
|
+
* descripción y ninguna forma de saber a cuál delega el orquestador.
|
|
202
|
+
*
|
|
203
|
+
* Las dos cosas en el mismo paso porque el formulario permite cambiar el nombre Y el resto en
|
|
204
|
+
* la misma pulsación; guardar solo una dejaría la pantalla mintiendo.
|
|
205
|
+
*
|
|
206
|
+
* **El destino ocupado es un NO**, sea de serie o del usuario: sin esta guarda, renombrar
|
|
207
|
+
* `advisor` a `docs` se llevaba por delante el `docs.md` sembrado —o el otro subagente— sin
|
|
208
|
+
* decir nada. Un caso especial para los de serie diría un motivo menos cierto.
|
|
209
|
+
*
|
|
210
|
+
* **Pero «existe» no es «es otro fichero», y eso está MEDIDO en una máquina de verdad.** APFS
|
|
211
|
+
* es insensible a mayúsculas, así que con un `Documentador.md` en disco el `existsSync` de
|
|
212
|
+
* `documentador.md` contesta SÍ —es el mismo fichero— y esta guarda rechazaba justo el arreglo
|
|
213
|
+
* que el aviso del cargador propone, que es el renombrado más común de todos. Se compara por
|
|
214
|
+
* INODO (`dev` + `ino`): si el destino ES el origen, no hay nada ocupado. En un sistema
|
|
215
|
+
* sensible a mayúsculas son dos ficheros con dos inodos, así que la comparación sigue diciendo
|
|
216
|
+
* la verdad ahí — y si el otro existe de verdad, se sigue rechazando.
|
|
217
|
+
*
|
|
218
|
+
* Que un de serie NO se pueda renombrar no se decide aquí: esto mueve un fichero, y la regla
|
|
219
|
+
* del producto vive donde ya vive la de que no se borra (`arranque.ts#atenderAgente`). Dos
|
|
220
|
+
* sitios donde decidir lo mismo es cómo uno de los dos se queda sin la regla.
|
|
221
|
+
*/
|
|
222
|
+
export function renombrarAgente(base, viejo, agente) {
|
|
223
|
+
// Antes de mover nada: el nombre viene del cliente por HTTP, y `segmentoSeguro` LANZA en
|
|
224
|
+
// vez de limpiarlo — limpiarlo guardaría el agente con un nombre que nadie pidió.
|
|
225
|
+
const seguro = segmentoSeguro(agente.nombre, "nombre de agente");
|
|
226
|
+
const carpeta = rutaDeAgentes(base);
|
|
227
|
+
const origen = join(carpeta, `${segmentoSeguro(viejo, "nombre de agente")}.md`);
|
|
228
|
+
const destino = join(carpeta, `${seguro}.md`);
|
|
229
|
+
if (!existsSync(origen))
|
|
230
|
+
return "sin-origen";
|
|
231
|
+
if (existsSync(destino) && !elMismoFichero(origen, destino))
|
|
232
|
+
return "destino-ocupado";
|
|
233
|
+
renameSync(origen, destino);
|
|
234
|
+
writeFileSync(destino, escribirAgente({ ...agente, nombre: seguro }), "utf8");
|
|
235
|
+
return "hecho";
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* ¿Las dos rutas son el MISMO fichero? Por `dev` + `ino`, no por texto.
|
|
239
|
+
*
|
|
240
|
+
* Por texto no se puede: en un sistema insensible a mayúsculas `Documentador.md` y
|
|
241
|
+
* `documentador.md` son el mismo y sus nombres no se parecen bajo `===`; y comparar en
|
|
242
|
+
* minúsculas mentiría en un sistema sensible, donde sí son dos. El inodo contesta la pregunta
|
|
243
|
+
* de verdad en los dos. Lo que no se puede mirar se da por DISTINTO, que es el lado que no
|
|
244
|
+
* pisa nada.
|
|
245
|
+
*/
|
|
246
|
+
function elMismoFichero(a, b) {
|
|
247
|
+
try {
|
|
248
|
+
const ea = statSync(a);
|
|
249
|
+
const eb = statSync(b);
|
|
250
|
+
return ea.dev === eb.dev && ea.ino === eb.ino;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** Borra uno. Devuelve si existía: la interfaz no puede decir «borrado» de algo que no estaba. */
|
|
257
|
+
export function borrarAgente(base, nombre) {
|
|
258
|
+
const ruta = join(rutaDeAgentes(base), `${segmentoSeguro(nombre, "nombre de agente")}.md`);
|
|
259
|
+
if (!existsSync(ruta))
|
|
260
|
+
return false;
|
|
261
|
+
rmSync(ruta);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Los especialistas de serie, sembrados en el GLOBAL.
|
|
266
|
+
*
|
|
267
|
+
* Dejan de ser un `Record` a fuego en `agent/grafo/perfiles.ts` y pasan a ser los mismos ficheros
|
|
268
|
+
* que puede escribir el usuario. Lo pidió él, y además arregla algo que estaba señalado
|
|
269
|
+
* como provisional en `xoneAgent.ts#promptDe` desde que se escribió: los prompts de los
|
|
270
|
+
* especialistas vivían en código con un `nombre === "planner"` dentro para las
|
|
271
|
+
* particularidades de uno de ellos.
|
|
272
|
+
*
|
|
273
|
+
* Lo que sigue siendo intocable, y por lo que la regla anterior existía: **el prompt que el
|
|
274
|
+
* usuario afina no se pisa, y el agente que borra no se resucita.** Lo que cambió es cómo
|
|
275
|
+
* se sabe cuál es cuál — ver `sembrarAgentes`.
|
|
276
|
+
*/
|
|
277
|
+
/** Lo que la marca guarda de cada agente sembrado: el hash de lo que ESCRIBIMOS nosotros. */
|
|
278
|
+
const AJENO = "ajeno";
|
|
279
|
+
/** El nombre de la marca. Empieza por punto y no acaba en `.md`: el cargador la ignora. */
|
|
280
|
+
export const FICHERO_DE_SEMILLA = ".semilla.json";
|
|
281
|
+
function rutaDeSemilla(carpeta) {
|
|
282
|
+
return join(carpeta, FICHERO_DE_SEMILLA);
|
|
283
|
+
}
|
|
284
|
+
function huella(contenido) {
|
|
285
|
+
return createHash("sha256").update(contenido, "utf8").digest("hex").slice(0, 16);
|
|
286
|
+
}
|
|
287
|
+
/** La marca en disco. Ausente o rota = no hay marca: se ADOPTA lo que haya (ver abajo). */
|
|
288
|
+
function leerSemilla(carpeta) {
|
|
289
|
+
const ruta = rutaDeSemilla(carpeta);
|
|
290
|
+
if (!existsSync(ruta))
|
|
291
|
+
return undefined;
|
|
292
|
+
try {
|
|
293
|
+
const bruto = JSON.parse(readFileSync(ruta, "utf8"));
|
|
294
|
+
if (typeof bruto !== "object" || bruto === null || Array.isArray(bruto))
|
|
295
|
+
return undefined;
|
|
296
|
+
const salida = {};
|
|
297
|
+
for (const [nombre, valor] of Object.entries(bruto)) {
|
|
298
|
+
if (typeof valor === "string")
|
|
299
|
+
salida[nombre] = valor;
|
|
300
|
+
}
|
|
301
|
+
return salida;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Una marca rota se trata como ausente y se REESCRIBE adoptando lo que hay. Es lo
|
|
305
|
+
// conservador: lo contrario —darla por vacía y sembrar— pisaría ficheros del usuario.
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function escribirSemilla(carpeta, marca) {
|
|
310
|
+
writeFileSync(rutaDeSemilla(carpeta), JSON.stringify(marca, null, 2) + "\n", "utf8");
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Los renombrados: nombre viejo → nombre nuevo.
|
|
314
|
+
*
|
|
315
|
+
* Hace falta porque la marca guarda el hash **por nombre**: renombrar deja una clave que ya no
|
|
316
|
+
* es de serie, y `sembrarAgentes` no la miraba — quien ya tuviera `probador.md` se quedaba con
|
|
317
|
+
* los DOS especialistas, uno de ellos sin mantenimiento.
|
|
318
|
+
*
|
|
319
|
+
* La alternativa era retirar toda clave desconocida cuyo hash fuera el nuestro. Funcionaría
|
|
320
|
+
* hoy y sería una trampa mañana: borraría cualquier entrada rara que un fallo dejara en la
|
|
321
|
+
* marca. Aquí solo se retira lo que consta que renombramos.
|
|
322
|
+
*/
|
|
323
|
+
const RENOMBRADOS = {
|
|
324
|
+
// Los cinco de serie pasaron a `<rol>-xone`, en inglés. El sufijo no es decoración: estos
|
|
325
|
+
// nombres viajan como `subagent_type` a los motores externos, donde el hijo tiene sus
|
|
326
|
+
// propios agentes, y ahí es lo que los distingue.
|
|
327
|
+
docs: "consultant-xone",
|
|
328
|
+
planner: "analyst-xone",
|
|
329
|
+
dev: "developer-xone",
|
|
330
|
+
mockup: "designer-xone",
|
|
331
|
+
"xone-device-tester": "device-controller",
|
|
332
|
+
/**
|
|
333
|
+
* Y el de antes se ACTUALIZA, no se deja: apuntaba a `xone-device-tester`, que ya no es de
|
|
334
|
+
* serie, así que a quien conserve un `probador.md` afinado se le habría seguido diciendo
|
|
335
|
+
* que «ese agente se llama ahora `xone-device-tester`» — un nombre que no existe. Esto se
|
|
336
|
+
* lee de un solo salto, no como una cadena.
|
|
337
|
+
*
|
|
338
|
+
* Dos claves pueden apuntar al mismo nombre nuevo sin chocar: cada entrada se resuelve por
|
|
339
|
+
* separado contra el hash de SU fichero.
|
|
340
|
+
*/
|
|
341
|
+
probador: "device-controller",
|
|
342
|
+
/**
|
|
343
|
+
* Y `tester-xone` se convirtió en `device-controller` cuando dejó de escribir pruebas para
|
|
344
|
+
* CONDUCIR el aparato: con `ejecucion: true` ya no es el que dice cómo se probaría, es el
|
|
345
|
+
* que lo hace. El nombre viejo describía lo que podía hacer cuando no tenía manos.
|
|
346
|
+
*
|
|
347
|
+
* **Se sale de la convención `<rol>-xone` a propósito**, y es decisión del usuario: lo que
|
|
348
|
+
* conduce no es propio de XOne —mañana es otro aparato con otra skill—, y el sufijo estaba
|
|
349
|
+
* para distinguir nuestros nombres de los agentes del hijo en un motor externo. Este no
|
|
350
|
+
* viaja a ninguno: con motor externo la ejecución no se concede (`puedeEjecutar`).
|
|
351
|
+
*/
|
|
352
|
+
"tester-xone": "device-controller",
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Siembra los agentes de serie, y ACTUALIZA los que nadie ha tocado.
|
|
356
|
+
*
|
|
357
|
+
* La regla anterior era «la carpeta es la marca»: si existía, no se escribía nada nunca más.
|
|
358
|
+
* Respetaba el prompt afinado por el usuario —que es lo que había que respetar— pero eligió
|
|
359
|
+
* un cuerno del dilema y el otro acabó mordiendo: **ningún agente nuevo, y ninguna
|
|
360
|
+
* corrección a uno existente, alcanzaba a quien ya hubiera arrancado una vez**. Medido: el
|
|
361
|
+
* `docs.md` de un usuario llevaba semanas sin la consulta acotada, y el probador de
|
|
362
|
+
* dispositivos no le habría llegado jamás.
|
|
363
|
+
*
|
|
364
|
+
* Ahora la marca es un fichero, `.semilla.json`, con el HASH DE LO QUE ESCRIBIMOS NOSOTROS
|
|
365
|
+
* para cada agente. Con eso se distinguen los cuatro casos que antes eran uno solo:
|
|
366
|
+
*
|
|
367
|
+
* | en disco | en la marca | qué se hace |
|
|
368
|
+
* |---|---|---|
|
|
369
|
+
* | no está | no está | es un agente NUEVO: se escribe |
|
|
370
|
+
* | no está | está | lo BORRÓ el usuario: no se resucita |
|
|
371
|
+
* | está, y su hash es el nuestro | está | nadie lo tocó: se actualiza |
|
|
372
|
+
* | está, y su hash NO es el nuestro | cualquiera | es suyo: se deja, y se DICE |
|
|
373
|
+
*
|
|
374
|
+
* Y un quinto, que aparece el día que un agente se RENOMBRA y que no puede salir de ese bucle
|
|
375
|
+
* porque habla de una clave que ya no nombra a ninguno: ver `RENOMBRADOS` y `Retirado`.
|
|
376
|
+
*
|
|
377
|
+
* **Y una carpeta sin marca se ADOPTA, no se siembra.** Es la de quien ya venía de la regla
|
|
378
|
+
* vieja, y ahí no se puede saber qué borró a propósito: dar por nuevo lo que falta le
|
|
379
|
+
* resucitaría un agente que eliminó. Así que se anota lo que hay —como nuestro si coincide
|
|
380
|
+
* con la versión de serie de hoy, como `ajeno` si no— y no se escribe ningún `.md` esa vez.
|
|
381
|
+
* Desde la siguiente, todo lo de arriba funciona. El coste es una ronda de retraso para las
|
|
382
|
+
* instalaciones viejas; la alternativa es pisar o resucitar sin permiso.
|
|
383
|
+
*
|
|
384
|
+
* El fichero de marca empieza por punto y no acaba en `.md`, así que `leerCarpetaDeAgentes`
|
|
385
|
+
* ni lo mira — no hace falta excluirlo a mano en dos sitios.
|
|
386
|
+
*/
|
|
387
|
+
export function sembrarAgentes(base = homedir()) {
|
|
388
|
+
const carpeta = rutaDeAgentes(base);
|
|
389
|
+
const escritos = [];
|
|
390
|
+
const desactualizados = [];
|
|
391
|
+
if (!existsSync(carpeta)) {
|
|
392
|
+
mkdirSync(carpeta, { recursive: true });
|
|
393
|
+
const marca = {};
|
|
394
|
+
for (const agente of AGENTES_DE_SERIE) {
|
|
395
|
+
const contenido = escribirAgente(agente);
|
|
396
|
+
writeFileSync(join(carpeta, `${agente.nombre}.md`), contenido, "utf8");
|
|
397
|
+
marca[agente.nombre] = huella(contenido);
|
|
398
|
+
escritos.push(agente.nombre);
|
|
399
|
+
}
|
|
400
|
+
escribirSemilla(carpeta, marca);
|
|
401
|
+
return { escritos, desactualizados, retirados: [] };
|
|
402
|
+
}
|
|
403
|
+
const previa = leerSemilla(carpeta);
|
|
404
|
+
const marca = { ...(previa ?? {}) };
|
|
405
|
+
/**
|
|
406
|
+
* Sin marca hay DOS carpetas distintas, y confundirlas cuesta caro en los dos sentidos.
|
|
407
|
+
*
|
|
408
|
+
* La de quien viene de la regla vieja tiene agentes dentro: ahí se adopta, porque no se
|
|
409
|
+
* puede saber qué borró a propósito. Pero una carpeta sin NINGUNO de los de serie no
|
|
410
|
+
* viene de ninguna siembra —la deja, por ejemplo, un `guardarAgente` con un nombre
|
|
411
|
+
* inválido— y adoptarla anotaría los cinco como entregados sin escribir uno solo: ese
|
|
412
|
+
* usuario se quedaría sin ningún subagente para siempre. Se siembra, que es lo que
|
|
413
|
+
* habría pasado si la carpeta no existiera.
|
|
414
|
+
*/
|
|
415
|
+
const hayDeSerie = AGENTES_DE_SERIE.some((a) => existsSync(join(carpeta, `${a.nombre}.md`)));
|
|
416
|
+
const adoptando = previa === undefined && hayDeSerie;
|
|
417
|
+
for (const agente of AGENTES_DE_SERIE) {
|
|
418
|
+
const ruta = join(carpeta, `${agente.nombre}.md`);
|
|
419
|
+
const contenido = escribirAgente(agente);
|
|
420
|
+
const nuestro = huella(contenido);
|
|
421
|
+
let enDisco;
|
|
422
|
+
try {
|
|
423
|
+
enDisco = existsSync(ruta) ? huella(readFileSync(ruta, "utf8")) : undefined;
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
// Un `.md` que no se puede leer no se puede comparar, y tampoco se pisa: el cargador
|
|
427
|
+
// ya dirá por qué no se pudo leer.
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (adoptando) {
|
|
431
|
+
// La ronda de adopción: se anota lo que hay y no se escribe nada.
|
|
432
|
+
if (enDisco === undefined)
|
|
433
|
+
marca[agente.nombre] = nuestro;
|
|
434
|
+
else if (enDisco === nuestro)
|
|
435
|
+
marca[agente.nombre] = nuestro;
|
|
436
|
+
else {
|
|
437
|
+
marca[agente.nombre] = AJENO;
|
|
438
|
+
desactualizados.push(agente.nombre);
|
|
439
|
+
}
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (enDisco === undefined) {
|
|
443
|
+
// Sin fichero: nuevo si no consta que lo hubiéramos entregado; borrado si consta.
|
|
444
|
+
if (marca[agente.nombre] === undefined) {
|
|
445
|
+
writeFileSync(ruta, contenido, "utf8");
|
|
446
|
+
marca[agente.nombre] = nuestro;
|
|
447
|
+
escritos.push(agente.nombre);
|
|
448
|
+
}
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
if (enDisco === nuestro) {
|
|
452
|
+
marca[agente.nombre] = nuestro;
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
if (marca[agente.nombre] !== undefined && marca[agente.nombre] !== AJENO && marca[agente.nombre] === enDisco) {
|
|
456
|
+
// Es exactamente lo que escribimos la última vez y la versión de serie ha cambiado:
|
|
457
|
+
// nadie lo ha tocado, así que se actualiza.
|
|
458
|
+
writeFileSync(ruta, contenido, "utf8");
|
|
459
|
+
marca[agente.nombre] = nuestro;
|
|
460
|
+
escritos.push(agente.nombre);
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
marca[agente.nombre] = AJENO;
|
|
464
|
+
desactualizados.push(agente.nombre);
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* El quinto caso: una clave de la marca que ya no nombra a ningún agente de serie.
|
|
468
|
+
*
|
|
469
|
+
* Solo la deja un renombrado, y solo se mira la de los nombres que constan en `RENOMBRADOS`.
|
|
470
|
+
* El desenlace se decide con el mismo dato que todo lo demás: si el fichero sigue siendo
|
|
471
|
+
* exactamente lo que escribimos, es nuestra semilla y se retira; si no, es del usuario y se
|
|
472
|
+
* queda. Sin marca no se puede saber —esa es la carpeta que se adopta, y ahí el huérfano se
|
|
473
|
+
* queda y no se dice: es el límite, y es el lado que no borra nada ajeno.
|
|
474
|
+
*/
|
|
475
|
+
const retirados = [];
|
|
476
|
+
for (const [viejo, ahoraSeLlama] of Object.entries(RENOMBRADOS)) {
|
|
477
|
+
const anotado = marca[viejo];
|
|
478
|
+
if (anotado === undefined)
|
|
479
|
+
continue;
|
|
480
|
+
const ruta = join(carpeta, `${viejo}.md`);
|
|
481
|
+
let enDisco;
|
|
482
|
+
try {
|
|
483
|
+
enDisco = existsSync(ruta) ? huella(readFileSync(ruta, "utf8")) : undefined;
|
|
484
|
+
}
|
|
485
|
+
catch {
|
|
486
|
+
// Sin comparación no hay borrado: un `.md` ilegible se queda, y el cargador dirá por qué.
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (enDisco === undefined) {
|
|
490
|
+
// El fichero ya no está: la clave se va con él, y no hay nada que contarle a nadie.
|
|
491
|
+
delete marca[viejo];
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (enDisco === anotado) {
|
|
495
|
+
rmSync(ruta);
|
|
496
|
+
delete marca[viejo];
|
|
497
|
+
retirados.push({ nombre: viejo, ahoraSeLlama, borrado: true });
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
// Afinado por el usuario: se queda, y se DICE cada arranque mientras siga ahí — queda algo
|
|
501
|
+
// que decidir, que es borrarlo o quedarse con los dos.
|
|
502
|
+
marca[viejo] = AJENO;
|
|
503
|
+
retirados.push({ nombre: viejo, ahoraSeLlama, borrado: false });
|
|
504
|
+
}
|
|
505
|
+
escribirSemilla(carpeta, marca);
|
|
506
|
+
return { escritos, desactualizados, retirados };
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* El bloque de skills visuales, que los cuatro comparten.
|
|
510
|
+
*
|
|
511
|
+
* Va en el CUERPO de los sembrados y no en `REGLAS_XONE`: no es una regla del dominio, es
|
|
512
|
+
* cómo usar dos skills concretas. Un agente que el usuario escriba sin `archify` ni
|
|
513
|
+
* `artifacts-builder` no tiene por qué leer instrucciones sobre tools que no tiene —
|
|
514
|
+
* hablarle de una capacidad que no posee es la misma clase de mentira que un botón muerto.
|
|
515
|
+
*/
|
|
516
|
+
/**
|
|
517
|
+
* Las dos skills visuales, dónde se GUARDA lo que dibujan y qué NO funciona donde se VE.
|
|
518
|
+
*
|
|
519
|
+
* La última regla —los almacenes del navegador— está aquí y no en la skill por una razón
|
|
520
|
+
* medida: `artifacts-builder/SKILL.md` la explica con detalle y **el modelo no lo abre**. En
|
|
521
|
+
* las tres delegaciones medidas leyó `archify/SKILL.md` y `reference/diagramas.md`, y ni una
|
|
522
|
+
* vez el `SKILL.md` ni `reference/estilo.md`. El resultado, en vivo el 2026-09-07: un
|
|
523
|
+
* artefacto recién escrito puso un interruptor de tema con `localStorage.getItem` en la
|
|
524
|
+
* última línea de su arranque, lanzó `SecurityError` dentro del iframe y se llevó consigo el
|
|
525
|
+
* `setAttribute` del tema — página perfecta y botón muerto. Es la misma lección que la
|
|
526
|
+
* carpeta `/artefactos/`: lo que tiene que cumplirse va donde el modelo mira SIEMPRE, no en
|
|
527
|
+
* una skill que hay que cargar.
|
|
528
|
+
*/
|
|
529
|
+
/**
|
|
530
|
+
* **Ya no existe, y su hueco se deja escrito a propósito.**
|
|
531
|
+
*
|
|
532
|
+
* Era un bloque de ~1.400 caracteres —cómo elegir entre `archify` y `artifacts-builder`, dónde
|
|
533
|
+
* se guarda un artefacto y qué no funciona dentro del iframe— que iba en el prompt de CUATRO
|
|
534
|
+
* especialistas y por tanto viajaba en cada una de sus llamadas, hablaran o no de diagramas. Se
|
|
535
|
+
* midió lo que costaba con `inspectorDePrompt`: en un turno normal, la cabecera es el 87 % de
|
|
536
|
+
* la petición.
|
|
537
|
+
*
|
|
538
|
+
* Nació de una lección buena: un turno leyó `archify/SKILL.md` y una referencia, se saltó
|
|
539
|
+
* `estilo.md` —donde estaba la regla— y escribió un artefacto con `localStorage` que se mató
|
|
540
|
+
* solo. Pero la conclusión correcta de eso no era «repítelo en todos los prompts», era
|
|
541
|
+
* **ponerlo en el fichero que sí se abre**: hoy la regla encabeza el cuerpo de
|
|
542
|
+
* `skills/archify/SKILL.md` y `skills/artifacts-builder/SKILL.md`.
|
|
543
|
+
*
|
|
544
|
+
* Y hay una segunda razón para no nombrar las skills aquí: `SkillsMiddleware` ya las anuncia
|
|
545
|
+
* con su descripción y su ruta, y nombrarlas otra vez —con instrucciones de uso— es lo que
|
|
546
|
+
* llevaba al especialista a cargarlas antes de saber si le hacían falta.
|
|
547
|
+
*/
|
|
548
|
+
/**
|
|
549
|
+
* La memoria del proyecto, y quién la lee y la escribe.
|
|
550
|
+
*
|
|
551
|
+
* También vivía en `promptDe`, repartida en dos ternarios sobre el nombre del perfil. Va al
|
|
552
|
+
* cuerpo del fichero y no a `REGLAS_XONE` porque es una POLÍTICA de estos cuatro, no una
|
|
553
|
+
* regla del dominio: un agente que el usuario escriba puede querer no leerla, y `docs`
|
|
554
|
+
* —que contesta de la plataforma y no del proyecto— tampoco la lee.
|
|
555
|
+
*/
|
|
556
|
+
/**
|
|
557
|
+
* Trabajar CON un plan, cuando lo hay.
|
|
558
|
+
*
|
|
559
|
+
* El plan no es un documento que alguien entrega y se archiva: es el estado compartido entre
|
|
560
|
+
* quien planifica y quien desarrolla, y por eso vive en `/planes/<nombre>/` —fuera del proyecto,
|
|
561
|
+
* fuera de git y fuera de CloudStudio— y sobrevive a la sesión. Sin esta instrucción el plan se
|
|
562
|
+
* escribía y nadie volvía a mirarlo: medido, los ocho proyectos del usuario tenían CERO.
|
|
563
|
+
*
|
|
564
|
+
* **Se marca lo COMPROBADO, no lo escrito**, que es la misma regla que gobierna todo lo demás
|
|
565
|
+
* aquí: un comando que devuelve 0 no dice que la app arrancara, y un fichero guardado no dice
|
|
566
|
+
* que el criterio se cumpla.
|
|
567
|
+
*/
|
|
568
|
+
const TRABAJAR_CON_PLAN = [
|
|
569
|
+
"SI TU ENCARGO NOMBRA UN PLAN (`/planes/<nombre>/`):",
|
|
570
|
+
"- Lee su `TASKS.md` ANTES de tocar nada y trabaja la tarea que te toque, no el plan entero.",
|
|
571
|
+
" Su `PLAN.md` y su `CONTEXT.md` están al lado si te falta contexto; no los redescubras.",
|
|
572
|
+
"- Y ACTUALÍZALO en el mismo sitio cuando termines: `**Estado:**` a `hecha`, y las casillas",
|
|
573
|
+
" de los criterios a `- [x]` solo si los has COMPROBADO — no por haber escrito el código que",
|
|
574
|
+
" debería cumplirlos. Lo que no puedas comprobar se queda sin marcar y lo dices.",
|
|
575
|
+
"- Si el plan se contradice con lo que ves en el código, no lo sigas a ciegas: dilo y para.",
|
|
576
|
+
].join("\n");
|
|
577
|
+
const MEMORIA_LEER = [
|
|
578
|
+
"Para una tarea sobre este proyecto, lee una sola vez `/MEMORIA_PROYECTO.md` antes de inspeccionarlo.",
|
|
579
|
+
"No la uses para preguntas generales de plataforma.",
|
|
580
|
+
].join(" ");
|
|
581
|
+
const MEMORIA_LEER_CON_HANDOFF = "Lee `/MEMORIA_PROYECTO.md` solo si la tarea NO incluye un `HANDOFF DE ANÁLISIS`. Con handoff, no la leas: sus hechos pertinentes ya vienen resumidos.";
|
|
582
|
+
const MEMORIA_ESCRIBIR = [
|
|
583
|
+
"Al terminar trabajo relevante, actualiza esa memoria solo con hechos comprobados, decisiones aprobadas",
|
|
584
|
+
"o pendientes útiles. Nunca copies transcripciones, salidas de tools, secretos ni ficheros completos.",
|
|
585
|
+
].join(" ");
|
|
586
|
+
const RECONOCIMIENTO_PLANNER = [
|
|
587
|
+
"RECONOCIMIENTO RÁPIDO DEL PROYECTO:",
|
|
588
|
+
"- Para preguntas generales como «qué hace esta app», busca evidencia suficiente, no un inventario completo.",
|
|
589
|
+
"- Lee `/app.xml` y, como máximo, tres ficheros representativos que ese contexto señale.",
|
|
590
|
+
"- En cada primera lectura usa exactamente `offset=0` y `limit=50`; usa otra página solo si una evidencia concreta lo exige.",
|
|
591
|
+
"- No repitas una lectura de la misma ruta y rango, ni hagas búsquedas genéricas como `function ` sin una hipótesis.",
|
|
592
|
+
"- Cuando puedas identificar el propósito y los módulos principales con evidencia, deja de llamar tools y responde.",
|
|
593
|
+
"- Solo amplía la exploración si el usuario pide detalle exhaustivo o si las evidencias son insuficientes o contradictorias; explica brevemente qué faltaba.",
|
|
594
|
+
"- Si el resultado alimenta un diagrama o artefacto, termina con un `HANDOFF DE ANÁLISIS` compacto:",
|
|
595
|
+
" propósito; nodos; aristas `origen → destino`; evidencia `ruta:líneas`; y lagunas. No incluyas transcript ni lecturas crudas.",
|
|
596
|
+
].join("\n");
|
|
597
|
+
/**
|
|
598
|
+
* La consulta acotada de `docs`, medida antes de escribirla.
|
|
599
|
+
*
|
|
600
|
+
* Trazado (`docs/EVALS.md`, «El coste, medido»): para decir que un atributo NO existe,
|
|
601
|
+
* `docs` hizo 26 llamadas y 317k tokens de entrada — tres `ls` y tres `glob` para
|
|
602
|
+
* inventariar `/skills`, ocho `grep` con `max_count: 30` en modo contenido (uno solo metió
|
|
603
|
+
* 1,4k tokens), y seis referencias leídas por páginas. Y nunca abrió
|
|
604
|
+
* `references/indice-completo.md`, que es el índice de las 55 referencias y la primera
|
|
605
|
+
* fila de `SKILL.md`. La regla no le quita capacidad: le dice dónde está el índice y
|
|
606
|
+
* cuándo la respuesta ya es «no está documentado» — que en XOne significa «no existe, no
|
|
607
|
+
* lo uses», y seguir buscando sinónimos no lo hace existir.
|
|
608
|
+
*/
|
|
609
|
+
const CONSULTA_ACOTADA_DOCS = [
|
|
610
|
+
"CONSULTA ACOTADA DE LAS REFERENCIAS:",
|
|
611
|
+
"- `/skills/<skill>/SKILL.md` es la regla corta y `references/indice-completo.md` es el índice de TODAS las",
|
|
612
|
+
" referencias: léelo antes de buscar. No hagas `ls` ni `glob` sobre `/skills`; el índice ya dice qué hay y dónde.",
|
|
613
|
+
"- Abre como máximo tres referencias por pregunta, las que el índice señale para el tema, con `offset=0` y",
|
|
614
|
+
" `limit=100`; pide otra página solo si el índice o el propio fichero dicen que lo buscado sigue ahí.",
|
|
615
|
+
"- Un `grep` por hipótesis concreta (un atributo, una función, una clase), con `max_count=5` y sobre la carpeta",
|
|
616
|
+
" de su familia. Nunca dos sinónimos seguidos de lo mismo, ni una búsqueda sin hipótesis, ni repetir una",
|
|
617
|
+
" lectura de la misma ruta y rango.",
|
|
618
|
+
"- Si el índice y las referencias del tema no nombran lo que se pregunta, la respuesta es que NO está",
|
|
619
|
+
" documentado y por tanto no existe para XOne: dilo, nombra las referencias que miraste, y para.",
|
|
620
|
+
"- En cuanto tengas la evidencia para contestar, deja de llamar tools y responde.",
|
|
621
|
+
].join("\n");
|
|
622
|
+
const HANDOFF_MOCKUP = [
|
|
623
|
+
"HANDOFF PARA DIAGRAMAS:",
|
|
624
|
+
"- Si la descripción de tu tarea incluye `HANDOFF DE ANÁLISIS`, ese bloque es tu evidencia de código real.",
|
|
625
|
+
"- Úsalo como fuente para el diagrama y NO vuelvas a leer, buscar ni reconstruir las rutas ya documentadas.",
|
|
626
|
+
"- Solo inspecciona un fichero si el handoff marca una laguna o dos evidencias se contradicen; explica cuál es la laguna.",
|
|
627
|
+
].join("\n");
|
|
628
|
+
/**
|
|
629
|
+
* El que CONDUCE el aparato. Su conocimiento del protocolo NO va aquí: va en la skill
|
|
630
|
+
* `xone-hotswap`, que son 1.100 líneas de referencia sobre las dos plataformas y se cargan
|
|
631
|
+
* solo cuando hacen falta. Y las reglas de método —por nombre y no por coordenadas, esperar
|
|
632
|
+
* a un control y no a un temporizador, un `result:false` es el resultado— tampoco: están en
|
|
633
|
+
* esa misma skill, y aquí se pagarían en cada llamada, dibuje o no.
|
|
634
|
+
*
|
|
635
|
+
* **Lo que queda es lo que la skill no puede saber: cómo es ESTA casa.** Dónde están sus
|
|
636
|
+
* ficheros cuando se mira el disco de verdad, dónde se deja lo que una persona va a ver, y
|
|
637
|
+
* qué hacer con un comando que no termina. Nada de eso es de XOne ni de Android: es del
|
|
638
|
+
* harness, y por eso vive en código y no en una skill que mañana se sustituye por otra.
|
|
639
|
+
*
|
|
640
|
+
* Este bloque vale para CUALQUIER agente con `ejecucion: true`, no solo para éste. Está
|
|
641
|
+
* escrito así a propósito: el día que haya un segundo, lo hereda sin copiarse.
|
|
642
|
+
*/
|
|
643
|
+
const EJECUCION_EN_LA_MAQUINA = [
|
|
644
|
+
"CONDUCES UN APARATO Y EJECUTAS COMANDOS EN ESTA MÁQUINA (`execute`).",
|
|
645
|
+
"",
|
|
646
|
+
"Los scripts de tus skills están EN EL PATH: se llaman por su nombre, sin ruta ninguna.",
|
|
647
|
+
"Los de `xone-hotswap` son `xone-arrancar-android` (levanta el emulador y ESPERA a que",
|
|
648
|
+
"esté listo; ya arrancado, lo dice y no hace nada), `xone-reiniciar-android` (déjalo en",
|
|
649
|
+
"estado conocido: es lo primero que hay que probar si el canal no contesta),",
|
|
650
|
+
"`xone-desplegar-android`,",
|
|
651
|
+
"`xone-hotswap`, `xone-log-android` (las excepciones del aparato, que es donde está el",
|
|
652
|
+
"porqué cuando algo no se pinta), `xone-captura-android` y `xone-arrancar-ios`;",
|
|
653
|
+
"cualquiera con `--help` o sin argumentos te dice cómo se usa. Úsalos: hacen lo que te piden",
|
|
654
|
+
"y no hay que reescribirlos.",
|
|
655
|
+
"",
|
|
656
|
+
"`read_file`, `ls`, `glob` y `grep` ven rutas VIRTUALES —la raíz `/` es el proyecto—, así que",
|
|
657
|
+
"una ruta absoluta del disco no existe para ellas y no va a funcionar por mucho que la",
|
|
658
|
+
"reintentes. Para mirar un fichero de una skill usa `cat`, que sí ve el disco de verdad.",
|
|
659
|
+
"",
|
|
660
|
+
"SI LA APP PIDE LOGIN Y NO SABES ENTRAR, no lo deduzcas leyendo el código: pregúntaselo al",
|
|
661
|
+
"APARATO, que tiene la base de datos de verdad —`runSql` con",
|
|
662
|
+
"`select LOGIN, PWD from <prefijo>_usuarios`, donde `<prefijo>` es el `prefix` del `<app>`",
|
|
663
|
+
"de `app.xml`—. Mira también si `autologon` está puesto. Y si aun así no entras, PÁRATE:",
|
|
664
|
+
"cuenta qué probaste y qué te falta. Quien lee tu respuesta puede darte las credenciales;",
|
|
665
|
+
"gastar el turno probando contraseñas no.",
|
|
666
|
+
"",
|
|
667
|
+
"PARA LLEGAR A UNA PANTALLA CONCRETA, no vayas a ciegas: `xone_navegacion` con",
|
|
668
|
+
"`operacion: \"referencias\"` y el nombre de la colección te dice QUÉ control lleva hasta ella",
|
|
669
|
+
"y en qué fichero está. Y si ese control no aparece en el árbol, no es que no exista: es que",
|
|
670
|
+
"todavía no se ve — mira el árbol, abre lo que haga falta (un cajón, un menú, la pantalla de",
|
|
671
|
+
"antes) y vuelve a mirar, en vez de repetir el mismo clic.",
|
|
672
|
+
"",
|
|
673
|
+
"CÓMO TRABAJAS, QUE ES LO QUE DECIDE SI ESTO SALE BARATO O CARO:",
|
|
674
|
+
"- UN comando, miras su salida, y entonces decides el siguiente. Nada de disparar diez a la",
|
|
675
|
+
" vez para ver cuál pega: la salida del primero cambia lo que hay que hacer después.",
|
|
676
|
+
"- Pero pide de una vez lo que sabes que vas a necesitar: lo caro no es lo que un comando",
|
|
677
|
+
" devuelve, es cada ida y vuelta, que reenvía toda la conversación. `xone-hotswap` acepta",
|
|
678
|
+
" varios comandos seguidos, y `xone-desplegar-android --captura` lanza Y captura en una.",
|
|
679
|
+
"- Si uno falla, cambia de hipótesis en vez de repetirlo. Dos intentos iguales dan lo mismo.",
|
|
680
|
+
"- No escribas ficheros sueltos en la raíz del proyecto: es la app del usuario, y ahí va a",
|
|
681
|
+
" aprobación, a git y a CloudStudio. Lo que quieras ENSEÑAR va a `$XONECODE_ARTEFACTOS`, y",
|
|
682
|
+
" los scripts ya lo hacen por ti. Una captura es `xone-hotswap '{\"command\":\"getScreenshot\"}'`",
|
|
683
|
+
" y, si el canal no contesta, `xone-captura-android` — nunca un `adb … screencap > fichero`,",
|
|
684
|
+
" que el `cwd` es la raíz del proyecto.",
|
|
685
|
+
"- EMPIEZA por mirar qué hay: `adb devices`. Sin ningún aparato, `xone-arrancar-android`",
|
|
686
|
+
" ANTES de nada más — no lo compongas a mano, que `emulator` no suele estar en el PATH.",
|
|
687
|
+
"- Un comando que no termina cuelga el turno: lo que no vuelve va al fondo (`… &`) y se",
|
|
688
|
+
" espera a una CONDICIÓN acotada, no a un número de segundos.",
|
|
689
|
+
"",
|
|
690
|
+
"LO QUE CUENTAS:",
|
|
691
|
+
"- La salida literal de lo que corriste, no tu interpretación. Un comando que devuelve 0 no",
|
|
692
|
+
" dice que la app arrancara.",
|
|
693
|
+
"- Y no digas que hiciste un paso que no hiciste: si capturaste lo que ya había en pantalla",
|
|
694
|
+
" sin desplegar el proyecto, eso es lo que hay que contar.",
|
|
695
|
+
"- Si no pudiste comprobar algo, dilo en vez de deducirlo.",
|
|
696
|
+
].join("\n");
|
|
697
|
+
/**
|
|
698
|
+
* Los cinco. Nacieron como una mudanza de los textos que había en código; desde entonces
|
|
699
|
+
* `docs` lleva además la consulta acotada, y el que conduce aparatos llegó con la documentación
|
|
700
|
+
* del protocolo hotswap y pasó por dos nombres más antes de `device-controller` (ver
|
|
701
|
+
* `RENOMBRADOS`). Cada regla que
|
|
702
|
+
* se añade aquí se mide antes con los evals, porque un prompt más largo es coste en TODAS las
|
|
703
|
+
* llamadas.
|
|
704
|
+
*/
|
|
705
|
+
export const AGENTES_DE_SERIE = [
|
|
706
|
+
{
|
|
707
|
+
nombre: "consultant-xone",
|
|
708
|
+
descripcion: "Para preguntas técnicas de la plataforma XOne: XML/.xne, JavaScript, CSS, eventos y " +
|
|
709
|
+
"patrones. Úsalo cuando la duda sea «¿cómo se hace esto en XOne?» o «¿este atributo " +
|
|
710
|
+
"existe?», no para averiguar qué hay en el proyecto. Dale la pregunta concreta y el " +
|
|
711
|
+
"contexto que ya hayas visto. Devuelve la respuesta anclada en la documentación de la " +
|
|
712
|
+
"plataforma, y dice cuándo no lo sabe en vez de deducirlo.",
|
|
713
|
+
motor: "modelo",
|
|
714
|
+
soloLectura: true,
|
|
715
|
+
/**
|
|
716
|
+
* **Sin las visuales, y eso es una DECISIÓN de reparto, no un recorte a ciegas.**
|
|
717
|
+
*
|
|
718
|
+
* Cada skill asignada mete en su prompt de sistema el nombre, la descripción y la ruta que
|
|
719
|
+
* pone `SkillsMiddleware`, y la descripción de `archify` sola son ~700 caracteres de inglés
|
|
720
|
+
* sobre diagramas, ETL y Mermaid — en CADA llamada, dibuje o no. Este contesta preguntas de
|
|
721
|
+
* la plataforma; la regla del orquestador ya manda los diagramas a `designer-xone`, así que
|
|
722
|
+
* dárselas era pagar una capacidad que no le toca ejercer.
|
|
723
|
+
*/
|
|
724
|
+
skills: ["xone-development"],
|
|
725
|
+
instrucciones: `${CONSULTA_ACOTADA_DOCS}`,
|
|
726
|
+
origen: "semilla",
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
nombre: "analyst-xone",
|
|
730
|
+
descripcion: "Para enterarte de cómo es ESTE proyecto antes de tocarlo: qué colecciones hay, dónde " +
|
|
731
|
+
"se declara algo, quién usa qué. Úsalo como PRIMER paso de un encargo que vaya a " +
|
|
732
|
+
"cambiar código, y no para preguntas de la plataforma. Dale qué hay que averiguar y " +
|
|
733
|
+
"para qué, que es lo que acota cuánto busca. Devuelve hechos con su fichero y su " +
|
|
734
|
+
"línea, listos para pasárselos al siguiente en un bloque HANDOFF DE ANÁLISIS. Para un " +
|
|
735
|
+
"desarrollo grande puede además dejar un PLAN escrito en `/planes/<nombre>/` —spec, " +
|
|
736
|
+
"tareas y glosario— y entonces te dice el nombre: pásaselo a quien desarrolle.",
|
|
737
|
+
motor: "modelo",
|
|
738
|
+
soloLectura: true,
|
|
739
|
+
skills: ["xone-spec-builder", "xone-plan-builder", "archify", "artifacts-builder"],
|
|
740
|
+
instrucciones: `${RECONOCIMIENTO_PLANNER}\n\n${MEMORIA_LEER}`,
|
|
741
|
+
origen: "semilla",
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
nombre: "developer-xone",
|
|
745
|
+
descripcion: "Para CAMBIAR el proyecto: crear o modificar colecciones, escribir scripts, editar " +
|
|
746
|
+
"ficheros. Dale los hechos ya averiguados —no le hagas redescubrirlos— y QUÉ tiene que " +
|
|
747
|
+
"conseguir, no cómo. Si hay un plan en `/planes/<nombre>/`, dale el nombre y la tarea " +
|
|
748
|
+
"concreta: lo lee y marca ahí lo que deja hecho. Devuelve los ficheros que cambió; cada " +
|
|
749
|
+
"escritura para el turno y pide aprobación, así que un encargo enorme son muchas " +
|
|
750
|
+
"paradas: pártelo.",
|
|
751
|
+
motor: "modelo",
|
|
752
|
+
soloLectura: false,
|
|
753
|
+
// `artifacts-builder` se queda: escribe documentos e informes. `archify` no, que los
|
|
754
|
+
// diagramas son de `designer-xone` y su descripción son ~700 caracteres por llamada.
|
|
755
|
+
skills: ["xone-development", "xone-debugging", "artifacts-builder"],
|
|
756
|
+
instrucciones: `${TRABAJAR_CON_PLAN}\n\n${MEMORIA_LEER}\n\n${MEMORIA_ESCRIBIR}`,
|
|
757
|
+
origen: "semilla",
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
nombre: "device-controller",
|
|
761
|
+
descripcion: "Para PROBAR en un móvil o emulador de verdad: despliega, navega hasta una pantalla y " +
|
|
762
|
+
"mira qué pasa. Es el único que ve lo que ninguna comprobación estática puede ver. " +
|
|
763
|
+
"Dile SIEMPRE a qué pantalla o colección llegar y qué comprobar ahí: «pruébalo» a " +
|
|
764
|
+
"secas le hace improvisar y sale caro. Levanta el emulador si no hay ninguno. Devuelve " +
|
|
765
|
+
"lo que MIDIÓ —salida literal, captura y excepciones del log—, y dice lo que no pudo " +
|
|
766
|
+
"comprobar en vez de deducirlo. No lo uses para cambiar el proyecto: eso es de " +
|
|
767
|
+
"developer-xone. Y NO lo uses para algo que se ve LEYENDO —un texto cortado, un " +
|
|
768
|
+
"atributo, un error de sintaxis—: arrancar un aparato para eso se come un cuarto del " +
|
|
769
|
+
"turno y no contesta nada que no conteste abrir el fichero.",
|
|
770
|
+
motor: "modelo",
|
|
771
|
+
/**
|
|
772
|
+
* **NO es de solo lectura, y decirlo importa por dos cosas distintas.**
|
|
773
|
+
*
|
|
774
|
+
* La primera es honestidad: tiene una shell, así que puede escribir el disco entero. Una
|
|
775
|
+
* pastilla verde de «solo lectura» sobre el único agente que alcanza la máquina es
|
|
776
|
+
* exactamente el tipo de etiqueta que enseña a no mirarlas.
|
|
777
|
+
*
|
|
778
|
+
* La segunda es que ese campo DECIDE EL MODELO: `rapido` para quien solo lee, `trabajo`
|
|
779
|
+
* para quien escribe. Conducir un aparato —leer un log, entender una excepción de Rhino,
|
|
780
|
+
* decidir el siguiente comando— no es trabajo de modelo barato, y medido con uno rápido se
|
|
781
|
+
* quedaba en bucles: probaba lo mismo dos veces, mezclaba rutas virtuales con reales y
|
|
782
|
+
* tardaba tres turnos en hacer lo que hace en uno. `soloLectura` se refería a los FICHEROS
|
|
783
|
+
* y aquí se estaba leyendo como «tarea sencilla», que es otra cosa.
|
|
784
|
+
*
|
|
785
|
+
* Lo que NO cambia: sigue sin `write_file` ni `edit_file` (`TOOLS_CON_EJECUCION`), para que
|
|
786
|
+
* el camino normal de tocar el proyecto siga siendo el que pasa por la aprobación y el diff.
|
|
787
|
+
*/
|
|
788
|
+
soloLectura: false,
|
|
789
|
+
/**
|
|
790
|
+
* **El único de serie con ejecución**, y lo que concede no es «correr un comando»: una
|
|
791
|
+
* shell no pasa por `permisosDe` ni por el `virtualMode`, así que alcanza el disco
|
|
792
|
+
* entero. Se le da porque es lo único que contesta «lanza la app»: el canal del aparato
|
|
793
|
+
* es un WebSocket —`curl` no lo habla— y el despliegue es `adb` más una subida HTTP.
|
|
794
|
+
* Ninguna de las dos cosas se compone leyendo, y un prompt no las suple.
|
|
795
|
+
*/
|
|
796
|
+
ejecucion: true,
|
|
797
|
+
skills: ["xone-hotswap", "xone-debugging"],
|
|
798
|
+
instrucciones: EJECUCION_EN_LA_MAQUINA,
|
|
799
|
+
origen: "semilla",
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
nombre: "designer-xone",
|
|
803
|
+
descripcion: "Para cambiar cómo se VE algo: el layout de una pantalla, el CSS, los recursos, y los " +
|
|
804
|
+
"diagramas de la app. Suyo es TODO síntoma visual, y da igual que suene a avería: algo " +
|
|
805
|
+
"CORTADO, que SE SALE, que NO SE LEE, que NO CABE, apretado, pegado al borde, " +
|
|
806
|
+
"desalineado, del color o del tamaño equivocado — también cuando se arregle en un " +
|
|
807
|
+
"`.xne` y no en el CSS. De developer-xone es qué HACE un botón al pulsarlo, aunque se " +
|
|
808
|
+
"toque el CSS. Dale la pantalla, qué se ve mal y qué tendría que verse — y si el " +
|
|
809
|
+
"arreglo depende del código real, el análisis ya hecho. Devuelve los ficheros que " +
|
|
810
|
+
"cambió, con aprobación como cualquier otra escritura.",
|
|
811
|
+
motor: "modelo",
|
|
812
|
+
soloLectura: false,
|
|
813
|
+
skills: ["xone-development", "archify", "artifacts-builder"],
|
|
814
|
+
instrucciones: `${TRABAJAR_CON_PLAN}\n\n${HANDOFF_MOCKUP}\n\n${MEMORIA_LEER_CON_HANDOFF}\n\n${MEMORIA_ESCRIBIR}`,
|
|
815
|
+
origen: "semilla",
|
|
816
|
+
},
|
|
817
|
+
];
|
|
818
|
+
//# sourceMappingURL=agentesEnDisco.js.map
|