@jmanuelcorral/openteam 0.1.14 → 0.1.15
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 -2
- package/dist/cli.js +209 -13
- package/dist/commands/agents.d.ts +65 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/dispatch.d.ts +3 -0
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/orchestratorAgent.d.ts +8 -0
- package/dist/commands/orchestratorAgent.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +218 -15
- package/dist/plugin/commandTool.d.ts +1 -1
- package/dist/plugin/commandTool.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -207,9 +207,9 @@ Tras `init`, autentica el provider frontier (`opencode auth login`), arranca tu
|
|
|
207
207
|
|
|
208
208
|
`openteam init` genera un único **agente primario** llamado `openteam` (`.opencode/agent/openteam.md`). Pulsa **Tab** en opencode para seleccionarlo: así sabes explícitamente que estás en modo openteam, mientras el plugin sigue optimizando el modelo por debajo.
|
|
209
209
|
|
|
210
|
-
Ese orquestador funciona al estilo [Squad](https://bradygaster.github.io/squad/): **no trae subagentes precreados**. En **cada petición** su primera acción es comprobar si ya existe equipo (
|
|
210
|
+
Ese orquestador funciona al estilo [Squad](https://bradygaster.github.io/squad/): **no trae subagentes precreados**. En **cada petición** su primera acción es comprobar si ya existe equipo (`.opencode/openteam-roster.md` + subagentes en `.opencode/agent/`). Si **existe**, reparte el trabajo entre los especialistas adecuados; si **no existe**, entiende las tareas que implica tu petición, diseña el equipo mínimo y **lo crea a demanda** —sin que tengas que pedírselo explícitamente— antes de pasarle esas tareas (escribiendo `.opencode/agent/<nombre>.md` con `mode: subagent`), reutilizando los existentes antes de crear nuevos. Los roles mecánicos/bulk se dejan enrutar a modelos locales y solo se escala a frontier para arquitectura, seguridad o debugging ambiguo.
|
|
211
211
|
|
|
212
|
-
Como Squad, el equipo tiene **identidad temática**: el orquestador elige (o te pregunta) un **universo** y nombra a cada agente con un personaje de ese universo, registrando el reparto en `.opencode/
|
|
212
|
+
Como Squad, el equipo tiene **identidad temática**: el orquestador elige (o te pregunta) un **universo** y nombra a cada agente con un personaje de ese universo, registrando el reparto en `.opencode/openteam-roster.md` (fuera de `.opencode/agent/`, para que opencode no lo cargue como un agente fantasma) para que los nombres **persistan** entre sesiones. Además de los roles específicos del proyecto, contempla tres **roles estándar** creados a demanda:
|
|
213
213
|
|
|
214
214
|
- **scribe** — memoria silenciosa: registra decisiones y aprendizajes en `.opencode/openteam-decisions.md` sin tocar el código (modelo local).
|
|
215
215
|
- **ralph** — automatización y triage: monitoriza el trabajo pendiente, lo prioriza, coordina la ejecución y **escala al humano** ante bloqueos o aprobaciones.
|
|
@@ -364,6 +364,7 @@ npm i -g @jmanuelcorral/openteam
|
|
|
364
364
|
openteam baseline show
|
|
365
365
|
openteam baseline set openai/gpt-5-mini
|
|
366
366
|
openteam doctor --config .opencode/openteam.json
|
|
367
|
+
openteam agents
|
|
367
368
|
openteam report --telemetry .opencode/openteam-telemetry.jsonl
|
|
368
369
|
openteam yolo status
|
|
369
370
|
openteam yolo on
|
|
@@ -371,6 +372,28 @@ openteam yolo on
|
|
|
371
372
|
|
|
372
373
|
Flags: `--config <path>` y `--telemetry <path>` sobrescriben las rutas por defecto; `--opencode <path>` apunta el comando `yolo` a un `opencode.json` concreto.
|
|
373
374
|
|
|
375
|
+
### Ver el LLM de cada agente: `openteam agents`
|
|
376
|
+
|
|
377
|
+
Lista los agentes de `.opencode/agent/` indicando **qué LLM usa cada uno**: si es **local** (Ollama/LM Studio/Foundry, sin coste de tokens) o **frontier** (consume tu suscripción), y el **proveedor** (Anthropic, OpenAI, GitHub Copilot, Google…). Los agentes sin `model` propio **heredan** el default de `opencode.json` (que openteam enruta local-first por mensaje).
|
|
378
|
+
|
|
379
|
+
```powershell
|
|
380
|
+
openteam agents
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
```text
|
|
384
|
+
openteam agents — LLM por agente:
|
|
385
|
+
|
|
386
|
+
● openteam [primary ] frontier · Anthropic (consume tu suscripción) (anthropic/claude-sonnet-4-5)
|
|
387
|
+
○ basher [subagent] local · Ollama (sin coste de tokens) (ollama/qwen3:8b)
|
|
388
|
+
○ scribe [subagent] hereda default → frontier · Anthropic (consume tu suscripción) · openteam enruta local-first por mensaje
|
|
389
|
+
|
|
390
|
+
Default de opencode.json: anthropic/claude-sonnet-4-5 (frontier · Anthropic)
|
|
391
|
+
Leyenda: ● primary · ○ subagent · local = sin coste de tokens · frontier = consume tu suscripción.
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
También está disponible **dentro de opencode**: pídele a openteam la lista de agentes y ejecutará la acción `agents` de su herramienta de comandos.
|
|
395
|
+
|
|
396
|
+
|
|
374
397
|
### Modo YOLO (auto-aprobar permisos)
|
|
375
398
|
|
|
376
399
|
El **modo YOLO** hace que opencode auto-apruebe **todos** los permisos (equivale al flag nativo `opencode --auto`, pero persistente). Escribe la regla global `permission: { "*": "allow" }` en `opencode.json` y **regenera el agente `openteam`** (`.opencode/agent/openteam.md`) con el mismo comodín, conservando el resto de tu configuración:
|
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
|
-
import { access, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
6
|
-
import { dirname as dirname2 } from "node:path";
|
|
5
|
+
import { access, mkdir as mkdir2, readdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
6
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
|
|
9
9
|
// src/cli/initAdapters.ts
|
|
@@ -791,6 +791,158 @@ function createDetect(exec) {
|
|
|
791
791
|
};
|
|
792
792
|
}
|
|
793
793
|
|
|
794
|
+
// src/commands/agents.ts
|
|
795
|
+
var PROVIDER_LABELS = {
|
|
796
|
+
"github-copilot": "GitHub Copilot",
|
|
797
|
+
anthropic: "Anthropic",
|
|
798
|
+
openai: "OpenAI",
|
|
799
|
+
google: "Google",
|
|
800
|
+
openrouter: "OpenRouter",
|
|
801
|
+
ollama: "Ollama",
|
|
802
|
+
lmstudio: "LM Studio",
|
|
803
|
+
"foundry-local": "Foundry Local"
|
|
804
|
+
};
|
|
805
|
+
function providerLabel(providerID) {
|
|
806
|
+
return PROVIDER_LABELS[providerID] ?? providerID;
|
|
807
|
+
}
|
|
808
|
+
function unquote(value) {
|
|
809
|
+
const trimmed = value.trim();
|
|
810
|
+
if (trimmed.length >= 2 && (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
811
|
+
return trimmed.slice(1, -1);
|
|
812
|
+
}
|
|
813
|
+
return trimmed;
|
|
814
|
+
}
|
|
815
|
+
function parseModelValue(value) {
|
|
816
|
+
const raw = unquote(value);
|
|
817
|
+
const slash = raw.indexOf("/");
|
|
818
|
+
if (slash <= 0 || slash === raw.length - 1) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
return { providerID: raw.slice(0, slash), modelID: raw.slice(slash + 1) };
|
|
822
|
+
}
|
|
823
|
+
function parseAgentFile(file) {
|
|
824
|
+
const info = { name: file.name, mode: "subagent" };
|
|
825
|
+
const lines = file.contents.split(/\r?\n/);
|
|
826
|
+
if (lines[0]?.trim() !== "---") {
|
|
827
|
+
return info;
|
|
828
|
+
}
|
|
829
|
+
for (let i = 1;i < lines.length; i += 1) {
|
|
830
|
+
const line = lines[i] ?? "";
|
|
831
|
+
if (line.trim() === "---") {
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
const colon = line.indexOf(":");
|
|
835
|
+
if (colon <= 0) {
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
const key = line.slice(0, colon).trim();
|
|
839
|
+
const value = line.slice(colon + 1).trim();
|
|
840
|
+
if (key === "mode") {
|
|
841
|
+
info.mode = unquote(value) || "subagent";
|
|
842
|
+
} else if (key === "description") {
|
|
843
|
+
info.description = unquote(value);
|
|
844
|
+
} else if (key === "model") {
|
|
845
|
+
const model = parseModelValue(value);
|
|
846
|
+
if (model !== undefined) {
|
|
847
|
+
info.model = model;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return info;
|
|
852
|
+
}
|
|
853
|
+
function localProviderIDs(config) {
|
|
854
|
+
const ids = new Set;
|
|
855
|
+
for (const runtime of config.local.runtimes) {
|
|
856
|
+
ids.add(runtime.id);
|
|
857
|
+
ids.add(runtime.defaultModel.providerID);
|
|
858
|
+
}
|
|
859
|
+
ids.add(config.router.localDefault.providerID);
|
|
860
|
+
return ids;
|
|
861
|
+
}
|
|
862
|
+
function subscriptionLabel(model, localProviders) {
|
|
863
|
+
const isLocal = localProviders.has(model.providerID);
|
|
864
|
+
if (isLocal) {
|
|
865
|
+
return {
|
|
866
|
+
kind: "local",
|
|
867
|
+
subscription: `local · ${providerLabel(model.providerID)} (sin coste de tokens)`
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
kind: "frontier",
|
|
872
|
+
subscription: `frontier · ${providerLabel(model.providerID)} (consume tu suscripción)`
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
function classifyAgent(info, context) {
|
|
876
|
+
if (info.model !== undefined) {
|
|
877
|
+
const { kind, subscription: subscription2 } = subscriptionLabel(info.model, context.localProviders);
|
|
878
|
+
return {
|
|
879
|
+
name: info.name,
|
|
880
|
+
mode: info.mode,
|
|
881
|
+
kind,
|
|
882
|
+
model: info.model,
|
|
883
|
+
subscription: subscription2
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
if (context.defaultModel === undefined) {
|
|
887
|
+
return {
|
|
888
|
+
name: info.name,
|
|
889
|
+
mode: info.mode,
|
|
890
|
+
kind: "inherited",
|
|
891
|
+
subscription: "hereda el default de opencode.json (sin definir) · openteam enruta local-first por mensaje"
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
const { subscription } = subscriptionLabel(context.defaultModel, context.localProviders);
|
|
895
|
+
return {
|
|
896
|
+
name: info.name,
|
|
897
|
+
mode: info.mode,
|
|
898
|
+
kind: "inherited",
|
|
899
|
+
model: context.defaultModel,
|
|
900
|
+
subscription: `hereda default → ${subscription} · openteam enruta local-first por mensaje`
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function modeRank(mode) {
|
|
904
|
+
if (mode === "primary") {
|
|
905
|
+
return 0;
|
|
906
|
+
}
|
|
907
|
+
if (mode === "all") {
|
|
908
|
+
return 1;
|
|
909
|
+
}
|
|
910
|
+
return 2;
|
|
911
|
+
}
|
|
912
|
+
function classifyAgents(files, context) {
|
|
913
|
+
return files.map((file) => classifyAgent(parseAgentFile(file), context)).sort((a, b) => {
|
|
914
|
+
const rank = modeRank(a.mode) - modeRank(b.mode);
|
|
915
|
+
return rank !== 0 ? rank : a.name.localeCompare(b.name);
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
function modelText(classification) {
|
|
919
|
+
return classification.model === undefined ? "" : ` (${classification.model.providerID}/${classification.model.modelID})`;
|
|
920
|
+
}
|
|
921
|
+
function renderAgentList(classifications, context) {
|
|
922
|
+
const lines = ["openteam agents — LLM por agente:", ""];
|
|
923
|
+
if (classifications.length === 0) {
|
|
924
|
+
lines.push(" (sin agentes en .opencode/agent/) — ejecuta 'openteam init' o pide al orquestador que cree el equipo.");
|
|
925
|
+
return lines.join(`
|
|
926
|
+
`);
|
|
927
|
+
}
|
|
928
|
+
const nameWidth = Math.max(...classifications.map((c) => c.name.length), "agente".length);
|
|
929
|
+
const modeWidth = Math.max(...classifications.map((c) => c.mode.length), "subagent".length);
|
|
930
|
+
for (const c of classifications) {
|
|
931
|
+
const bullet = c.mode === "primary" ? "●" : "○";
|
|
932
|
+
lines.push(` ${bullet} ${c.name.padEnd(nameWidth)} [${c.mode.padEnd(modeWidth)}] ${c.subscription}${modelText(c)}`);
|
|
933
|
+
}
|
|
934
|
+
lines.push("");
|
|
935
|
+
if (context.defaultModel !== undefined) {
|
|
936
|
+
const { kind } = subscriptionLabel(context.defaultModel, context.localProviders);
|
|
937
|
+
lines.push(`Default de opencode.json: ${context.defaultModel.providerID}/${context.defaultModel.modelID} (${kind} · ${providerLabel(context.defaultModel.providerID)})`);
|
|
938
|
+
} else {
|
|
939
|
+
lines.push("Default de opencode.json: (sin definir)");
|
|
940
|
+
}
|
|
941
|
+
lines.push("Leyenda: ● primary · ○ subagent · local = sin coste de tokens · frontier = consume tu suscripción.");
|
|
942
|
+
return lines.join(`
|
|
943
|
+
`);
|
|
944
|
+
}
|
|
945
|
+
|
|
794
946
|
// src/config/schema.ts
|
|
795
947
|
import { z as z2 } from "zod";
|
|
796
948
|
var ModelRefSchema = z2.object({
|
|
@@ -997,14 +1149,14 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
997
1149
|
"Antes de responder o delegar **cualquier** petición, tu **primera acción",
|
|
998
1150
|
"siempre** es comprobar si ya existe un equipo, aunque el usuario no te pida",
|
|
999
1151
|
"explícitamente crearlo. Nunca asumas que hay equipo ni que no lo hay:",
|
|
1000
|
-
"verifícalo mirando `.opencode/
|
|
1152
|
+
"verifícalo mirando `.opencode/openteam-roster.md` y los `.opencode/agent/*.md`",
|
|
1001
1153
|
"con `mode: subagent` (usa `read`/`glob`/`list`).",
|
|
1002
1154
|
"",
|
|
1003
1155
|
"- **Si el equipo YA existe** → actúa como coordinador: entiende la tarea,",
|
|
1004
1156
|
" repártela entre los subagentes adecuados con la herramienta `task` y",
|
|
1005
1157
|
" reutiliza el reparto existente. No vuelvas a castear ni recrear agentes.",
|
|
1006
|
-
"- **Si NO existe equipo** (
|
|
1007
|
-
" permiso para crearlo
|
|
1158
|
+
"- **Si NO existe equipo** (sin `openteam-roster.md` ni subagentes): **no pidas",
|
|
1159
|
+
" permiso para crearlo**. Entiende primero las tareas que implica la",
|
|
1008
1160
|
" petición, diseña el equipo mínimo necesario, **créalo a demanda** y luego",
|
|
1009
1161
|
" pásale esas tareas. Crear el equipo es parte implícita de atender la",
|
|
1010
1162
|
" petición, no un paso opcional que el usuario deba solicitar.",
|
|
@@ -1022,9 +1174,11 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
1022
1174
|
"2. Asigna a cada subagente el **nombre de un personaje** de ese universo; el",
|
|
1023
1175
|
" nombre del fichero (`.opencode/agent/<nombre>.md`) es su alias para",
|
|
1024
1176
|
" `@mención`, y su rol queda claro en el `description`.",
|
|
1025
|
-
"3. **Registra el casting** en `.opencode/
|
|
1177
|
+
"3. **Registra el casting** en `.opencode/openteam-roster.md` (universo + tabla",
|
|
1026
1178
|
" nombre → rol → modelo) para que los nombres **persistan** entre sesiones.",
|
|
1027
|
-
"
|
|
1179
|
+
" Este fichero va **fuera** de `.opencode/agent/` (si no, opencode lo cargaría",
|
|
1180
|
+
" como un agente fantasma). Reutiliza siempre el mismo reparto; no vuelvas a",
|
|
1181
|
+
" castear sin motivo.",
|
|
1028
1182
|
"",
|
|
1029
1183
|
"## Cómo crear el equipo cuando no existe",
|
|
1030
1184
|
"",
|
|
@@ -1048,8 +1202,8 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
1048
1202
|
" frontier solo para arquitectura, seguridad o debugging ambiguo:",
|
|
1049
1203
|
" openteam ya enruta cheapest-capable automáticamente.",
|
|
1050
1204
|
" - Un prompt de rol enfocado y accionable.",
|
|
1051
|
-
"4. Registra el reparto en
|
|
1052
|
-
" recién creado con la herramienta `task`.",
|
|
1205
|
+
"4. Registra el reparto en `.opencode/openteam-roster.md` y **reparte las",
|
|
1206
|
+
" tareas** al equipo recién creado con la herramienta `task`.",
|
|
1053
1207
|
"",
|
|
1054
1208
|
"## Roles estándar del equipo",
|
|
1055
1209
|
"",
|
|
@@ -1122,8 +1276,8 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
1122
1276
|
"- **Lanza en un solo turno las tareas independientes** para que corran en",
|
|
1123
1277
|
" paralelo; secuencia solo las que dependen de un resultado previo.",
|
|
1124
1278
|
"- Crea subagentes **justo cuando el trabajo lo pide**, nunca “por si acaso”.",
|
|
1125
|
-
" Revisa `.opencode/agent/` y el `roster.md`, y reutiliza los
|
|
1126
|
-
" de crear uno nuevo.",
|
|
1279
|
+
" Revisa `.opencode/agent/` y el `openteam-roster.md`, y reutiliza los",
|
|
1280
|
+
" existentes antes de crear uno nuevo.",
|
|
1127
1281
|
"- Respeta el routing y la privacidad de openteam; el `guardian` es la última",
|
|
1128
1282
|
" línea antes de enviar nada sensible a frontier.",
|
|
1129
1283
|
"- Resume al usuario qué universo elegiste, qué equipo has creado y por qué.",
|
|
@@ -1131,7 +1285,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
1131
1285
|
"## Límites",
|
|
1132
1286
|
"",
|
|
1133
1287
|
"- Un único primario (tú); todo lo demás son subagentes creados a demanda.",
|
|
1134
|
-
"- No borres ni sobrescribas subagentes ni el `roster.md` sin confirmarlo.",
|
|
1288
|
+
"- No borres ni sobrescribas subagentes ni el `openteam-roster.md` sin confirmarlo.",
|
|
1135
1289
|
"- Los subagentes nuevos quedan disponibles para `@mención` y para la",
|
|
1136
1290
|
" herramienta `task` una vez opencode recarga los agentes del proyecto.",
|
|
1137
1291
|
""
|
|
@@ -1260,6 +1414,7 @@ var HELP = [
|
|
|
1260
1414
|
" openteam baseline set <p/model> Fija el baseline (modo pinned)",
|
|
1261
1415
|
" openteam baseline auto Baseline cheapest-capable (modo auto)",
|
|
1262
1416
|
" openteam doctor Diagnóstico de runtimes y config",
|
|
1417
|
+
" openteam agents Lista los agentes y el LLM (local/frontier) de cada uno",
|
|
1263
1418
|
" openteam report Resumen de coste/ahorro (telemetría)",
|
|
1264
1419
|
" openteam yolo status Muestra si el modo YOLO está activo",
|
|
1265
1420
|
" openteam yolo on Activa YOLO (opencode auto-aprueba todo)",
|
|
@@ -1390,6 +1545,26 @@ ${deps.orchestratorAgentPath} regenerado (permission acorde a YOLO).`;
|
|
|
1390
1545
|
Aviso: no pude regenerar ${deps.orchestratorAgentPath} (model inválido en opencode.json); ajusta su permission a mano.`;
|
|
1391
1546
|
}
|
|
1392
1547
|
}
|
|
1548
|
+
async function runAgents(deps, configPath, opencodeConfigPath) {
|
|
1549
|
+
const [config, opencodeConfig, files] = await Promise.all([
|
|
1550
|
+
deps.loadConfig(configPath),
|
|
1551
|
+
deps.readOpencodeConfig(opencodeConfigPath),
|
|
1552
|
+
deps.listAgentFiles(deps.agentDir)
|
|
1553
|
+
]);
|
|
1554
|
+
const localProviders = localProviderIDs(config);
|
|
1555
|
+
const rawDefault = opencodeConfig?.model;
|
|
1556
|
+
let defaultModel;
|
|
1557
|
+
if (typeof rawDefault === "string" && rawDefault.trim() !== "") {
|
|
1558
|
+
try {
|
|
1559
|
+
defaultModel = parseModelRef(rawDefault);
|
|
1560
|
+
} catch {
|
|
1561
|
+
defaultModel = undefined;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const context = defaultModel === undefined ? { localProviders } : { localProviders, defaultModel };
|
|
1565
|
+
const classifications = classifyAgents(files, context);
|
|
1566
|
+
return { exitCode: 0, stdout: renderAgentList(classifications, context) };
|
|
1567
|
+
}
|
|
1393
1568
|
async function runCli(argv, deps) {
|
|
1394
1569
|
const parsed = parseArgs(argv);
|
|
1395
1570
|
const command = parsed.positionals[0];
|
|
@@ -1426,6 +1601,9 @@ async function runCli(argv, deps) {
|
|
|
1426
1601
|
stdout: renderReport(summarizeCostRecords(records))
|
|
1427
1602
|
};
|
|
1428
1603
|
}
|
|
1604
|
+
if (command === "agents") {
|
|
1605
|
+
return await runAgents(deps, configPath, opencodeConfigPath);
|
|
1606
|
+
}
|
|
1429
1607
|
if (command === undefined || command === "help" || command === "--help") {
|
|
1430
1608
|
return { exitCode: 0, stdout: HELP };
|
|
1431
1609
|
}
|
|
@@ -1919,6 +2097,7 @@ async function readCostRecords(path, deps = {}) {
|
|
|
1919
2097
|
|
|
1920
2098
|
// src/cli.ts
|
|
1921
2099
|
var execFileAsync = promisify(execFile);
|
|
2100
|
+
var AGENT_DIR = dirname2(ORCHESTRATOR_AGENT_PATH);
|
|
1922
2101
|
function isMissingFile2(error) {
|
|
1923
2102
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1924
2103
|
}
|
|
@@ -1969,6 +2148,22 @@ var deps = {
|
|
|
1969
2148
|
await mkdir2(dirname2(path), { recursive: true });
|
|
1970
2149
|
await writeFile2(path, contents, "utf8");
|
|
1971
2150
|
},
|
|
2151
|
+
listAgentFiles: async (dir) => {
|
|
2152
|
+
let entries;
|
|
2153
|
+
try {
|
|
2154
|
+
entries = await readdir(dir);
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
if (isMissingFile2(error)) {
|
|
2157
|
+
return [];
|
|
2158
|
+
}
|
|
2159
|
+
throw error;
|
|
2160
|
+
}
|
|
2161
|
+
const mdFiles = entries.filter((entry) => entry.endsWith(".md"));
|
|
2162
|
+
return Promise.all(mdFiles.map(async (entry) => ({
|
|
2163
|
+
name: entry.replace(/\.md$/, ""),
|
|
2164
|
+
contents: await readFile2(join2(dir, entry), "utf8")
|
|
2165
|
+
})));
|
|
2166
|
+
},
|
|
1972
2167
|
probe: (config) => {
|
|
1973
2168
|
const registry = new RuntimeRegistry({
|
|
1974
2169
|
fetch: globalThis.fetch,
|
|
@@ -1981,7 +2176,8 @@ var deps = {
|
|
|
1981
2176
|
configPath: DEFAULT_CONFIG_PATH,
|
|
1982
2177
|
telemetryPath: DEFAULT_TELEMETRY_PATH,
|
|
1983
2178
|
opencodeConfigPath: OPENCODE_CONFIG_PATH,
|
|
1984
|
-
orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH
|
|
2179
|
+
orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
|
|
2180
|
+
agentDir: AGENT_DIR
|
|
1985
2181
|
};
|
|
1986
2182
|
function createInitDeps() {
|
|
1987
2183
|
return {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ModelRef, OpenTeamConfig } from "../config/schema";
|
|
2
|
+
/**
|
|
3
|
+
* Listado de agentes con el LLM que usa cada uno. Puro: sin I/O. El driver (leer
|
|
4
|
+
* `.opencode/agent/*.md` y `opencode.json`) se inyecta desde la CLI.
|
|
5
|
+
*
|
|
6
|
+
* Un agente puede: fijar su `model` en el frontmatter (explícito), o no fijarlo y
|
|
7
|
+
* **heredar** el `model` por defecto de `opencode.json` (que openteam enruta
|
|
8
|
+
* local-first por mensaje). Clasificamos el proveedor como **local** (Ollama/LM
|
|
9
|
+
* Studio/Foundry, sin coste de tokens) o **frontier** (consume tu suscripción).
|
|
10
|
+
*/
|
|
11
|
+
/** Un agente `.md` de `.opencode/agent/` ya leído (nombre = fichero sin `.md`). */
|
|
12
|
+
export type AgentFile = {
|
|
13
|
+
name: string;
|
|
14
|
+
contents: string;
|
|
15
|
+
};
|
|
16
|
+
/** Frontmatter relevante de un agente. */
|
|
17
|
+
export type AgentInfo = {
|
|
18
|
+
name: string;
|
|
19
|
+
mode: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
model?: ModelRef;
|
|
22
|
+
};
|
|
23
|
+
export type AgentModelKind = "local" | "frontier" | "inherited";
|
|
24
|
+
/** Agente clasificado por el LLM efectivo que usa. */
|
|
25
|
+
export type AgentClassification = {
|
|
26
|
+
name: string;
|
|
27
|
+
mode: string;
|
|
28
|
+
kind: AgentModelKind;
|
|
29
|
+
model?: ModelRef;
|
|
30
|
+
/** Etiqueta legible de la suscripción/proveedor efectivo. */
|
|
31
|
+
subscription: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Extrae el frontmatter YAML simple (`key: value` entre `---`) de un agente. Solo
|
|
35
|
+
* nos interesan `mode`, `description` y `model`. Pure.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseAgentFile(file: AgentFile): AgentInfo;
|
|
38
|
+
/**
|
|
39
|
+
* Conjunto de providerIDs considerados **locales**: los de los runtimes locales
|
|
40
|
+
* configurados (Ollama/LM Studio/Foundry), su `id`, y el `router.localDefault`.
|
|
41
|
+
* Pure.
|
|
42
|
+
*/
|
|
43
|
+
export declare function localProviderIDs(config: OpenTeamConfig): Set<string>;
|
|
44
|
+
/**
|
|
45
|
+
* Clasifica un agente por el LLM efectivo. Si no fija `model`, hereda el
|
|
46
|
+
* `defaultModel` de `opencode.json` (marcado como `inherited`). Pure.
|
|
47
|
+
*/
|
|
48
|
+
export declare function classifyAgent(info: AgentInfo, context: {
|
|
49
|
+
localProviders: Set<string>;
|
|
50
|
+
defaultModel?: ModelRef;
|
|
51
|
+
}): AgentClassification;
|
|
52
|
+
/**
|
|
53
|
+
* Clasifica y ordena todos los agentes (primary primero, luego alfabético).
|
|
54
|
+
* Pure.
|
|
55
|
+
*/
|
|
56
|
+
export declare function classifyAgents(files: readonly AgentFile[], context: {
|
|
57
|
+
localProviders: Set<string>;
|
|
58
|
+
defaultModel?: ModelRef;
|
|
59
|
+
}): AgentClassification[];
|
|
60
|
+
/** Render legible del listado de agentes con su LLM. Pure. */
|
|
61
|
+
export declare function renderAgentList(classifications: readonly AgentClassification[], context: {
|
|
62
|
+
defaultModel?: ModelRef;
|
|
63
|
+
localProviders: Set<string>;
|
|
64
|
+
}): string;
|
|
65
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/commands/agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;GAQG;AAEH,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,0CAA0C;AAC1C,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,UAAU,GAAG,WAAW,CAAC;AAEhE,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAsCF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAgCzD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,CAQpE;AAmBD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,EACf,OAAO,EAAE;IAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,EAAE,QAAQ,CAAA;CAAE,GAChE,mBAAmB,CAoCrB;AAYD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,OAAO,EAAE;IAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,YAAY,CAAC,EAAE,QAAQ,CAAA;CAAE,GAChE,mBAAmB,EAAE,CAOvB;AAQD,8DAA8D;AAC9D,wBAAgB,eAAe,CAC7B,eAAe,EAAE,SAAS,mBAAmB,EAAE,EAC/C,OAAO,EAAE;IAAE,YAAY,CAAC,EAAE,QAAQ,CAAC;IAAC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,GAChE,MAAM,CA2CR"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { OpenTeamConfig } from "../config/schema";
|
|
2
2
|
import type { LocalRuntimeSnapshot } from "../local/types";
|
|
3
3
|
import type { CostRecord } from "../telemetry/types";
|
|
4
|
+
import { type AgentFile } from "./agents";
|
|
4
5
|
export type CliDeps = {
|
|
5
6
|
loadConfig: (path: string) => Promise<OpenTeamConfig>;
|
|
6
7
|
saveConfig: (config: OpenTeamConfig, path: string) => Promise<void>;
|
|
@@ -9,10 +10,12 @@ export type CliDeps = {
|
|
|
9
10
|
readOpencodeConfig: (path: string) => Promise<Record<string, unknown> | undefined>;
|
|
10
11
|
writeOpencodeConfig: (config: Record<string, unknown>, path: string) => Promise<void>;
|
|
11
12
|
writeOrchestratorAgent: (contents: string, path: string) => Promise<void>;
|
|
13
|
+
listAgentFiles: (dir: string) => Promise<AgentFile[]>;
|
|
12
14
|
configPath: string;
|
|
13
15
|
telemetryPath: string;
|
|
14
16
|
opencodeConfigPath: string;
|
|
15
17
|
orchestratorAgentPath: string;
|
|
18
|
+
agentDir: string;
|
|
16
19
|
};
|
|
17
20
|
export type CliResult = {
|
|
18
21
|
exitCode: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/commands/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/commands/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,KAAK,SAAS,EAIf,MAAM,UAAU,CAAC;AAYlB,MAAM,MAAM,OAAO,GAAG;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACtD,UAAU,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,KAAK,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnE,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACvD,kBAAkB,EAAE,CAClB,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAClD,mBAAmB,EAAE,CACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,sBAAsB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAuNF,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,SAAS,CAAC,CA0DpB"}
|
|
@@ -4,6 +4,14 @@ import type { FrontierChoice } from "./init";
|
|
|
4
4
|
* nombre del fichero (`openteam`) es el nombre del agente en opencode.
|
|
5
5
|
*/
|
|
6
6
|
export declare const ORCHESTRATOR_AGENT_PATH = ".opencode/agent/openteam.md";
|
|
7
|
+
/**
|
|
8
|
+
* Ruta del registro de casting (universo + tabla nombre→rol→modelo). Vive
|
|
9
|
+
* **fuera** de `.opencode/agent/` a propósito: opencode carga como agente cada
|
|
10
|
+
* `.md` de esa carpeta, así que un `roster.md` dentro aparecería como un agente
|
|
11
|
+
* fantasma en el Tab-switcher. Lo guardamos junto al log de decisiones del
|
|
12
|
+
* `scribe`, como fichero de datos del proyecto.
|
|
13
|
+
*/
|
|
14
|
+
export declare const OPENTEAM_ROSTER_PATH = ".opencode/openteam-roster.md";
|
|
7
15
|
/**
|
|
8
16
|
* Construye el Markdown del agente primario `openteam` (estilo Squad): un
|
|
9
17
|
* orquestador que aparece en el Tab-switcher, elige un universo temático y
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestratorAgent.d.ts","sourceRoot":"","sources":["../../src/commands/orchestratorAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,gCAAgC,CAAC;AAErE;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,MAAM,
|
|
1
|
+
{"version":3,"file":"orchestratorAgent.d.ts","sourceRoot":"","sources":["../../src/commands/orchestratorAgent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,gCAAgC,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,iCAAiC,CAAC;AAEnE;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/B,MAAM,CAuLR"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,EAIN,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,MAAM,EAAe,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAKnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAgBnD,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,UAAU,CAAC;CAChC,CAAC;AAcF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGhE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGtD;AA0BD,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,gBAAqB,GAC1B,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAQjC;AAeD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOrD;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,eAAe,EACzB,aAAa,EAAE,MAAM,GACpB,OAAO,CA4DT;AAED,eAAO,MAAM,MAAM,EAAE,MA8BpB,CAAC;AAEF,QAAA,MAAM,MAAM,EAAE,YAGb,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
appendFile,
|
|
4
|
+
mkdir as mkdir2,
|
|
5
|
+
readdir,
|
|
6
|
+
readFile as readFile2,
|
|
7
|
+
writeFile as writeFile2
|
|
8
|
+
} from "node:fs/promises";
|
|
9
|
+
import { dirname as dirname2, join } from "node:path";
|
|
4
10
|
|
|
5
11
|
// src/config/persist.ts
|
|
6
12
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
@@ -135,14 +141,14 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
135
141
|
"Antes de responder o delegar **cualquier** petición, tu **primera acción",
|
|
136
142
|
"siempre** es comprobar si ya existe un equipo, aunque el usuario no te pida",
|
|
137
143
|
"explícitamente crearlo. Nunca asumas que hay equipo ni que no lo hay:",
|
|
138
|
-
"verifícalo mirando `.opencode/
|
|
144
|
+
"verifícalo mirando `.opencode/openteam-roster.md` y los `.opencode/agent/*.md`",
|
|
139
145
|
"con `mode: subagent` (usa `read`/`glob`/`list`).",
|
|
140
146
|
"",
|
|
141
147
|
"- **Si el equipo YA existe** → actúa como coordinador: entiende la tarea,",
|
|
142
148
|
" repártela entre los subagentes adecuados con la herramienta `task` y",
|
|
143
149
|
" reutiliza el reparto existente. No vuelvas a castear ni recrear agentes.",
|
|
144
|
-
"- **Si NO existe equipo** (
|
|
145
|
-
" permiso para crearlo
|
|
150
|
+
"- **Si NO existe equipo** (sin `openteam-roster.md` ni subagentes): **no pidas",
|
|
151
|
+
" permiso para crearlo**. Entiende primero las tareas que implica la",
|
|
146
152
|
" petición, diseña el equipo mínimo necesario, **créalo a demanda** y luego",
|
|
147
153
|
" pásale esas tareas. Crear el equipo es parte implícita de atender la",
|
|
148
154
|
" petición, no un paso opcional que el usuario deba solicitar.",
|
|
@@ -160,9 +166,11 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
160
166
|
"2. Asigna a cada subagente el **nombre de un personaje** de ese universo; el",
|
|
161
167
|
" nombre del fichero (`.opencode/agent/<nombre>.md`) es su alias para",
|
|
162
168
|
" `@mención`, y su rol queda claro en el `description`.",
|
|
163
|
-
"3. **Registra el casting** en `.opencode/
|
|
169
|
+
"3. **Registra el casting** en `.opencode/openteam-roster.md` (universo + tabla",
|
|
164
170
|
" nombre → rol → modelo) para que los nombres **persistan** entre sesiones.",
|
|
165
|
-
"
|
|
171
|
+
" Este fichero va **fuera** de `.opencode/agent/` (si no, opencode lo cargaría",
|
|
172
|
+
" como un agente fantasma). Reutiliza siempre el mismo reparto; no vuelvas a",
|
|
173
|
+
" castear sin motivo.",
|
|
166
174
|
"",
|
|
167
175
|
"## Cómo crear el equipo cuando no existe",
|
|
168
176
|
"",
|
|
@@ -186,8 +194,8 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
186
194
|
" frontier solo para arquitectura, seguridad o debugging ambiguo:",
|
|
187
195
|
" openteam ya enruta cheapest-capable automáticamente.",
|
|
188
196
|
" - Un prompt de rol enfocado y accionable.",
|
|
189
|
-
"4. Registra el reparto en
|
|
190
|
-
" recién creado con la herramienta `task`.",
|
|
197
|
+
"4. Registra el reparto en `.opencode/openteam-roster.md` y **reparte las",
|
|
198
|
+
" tareas** al equipo recién creado con la herramienta `task`.",
|
|
191
199
|
"",
|
|
192
200
|
"## Roles estándar del equipo",
|
|
193
201
|
"",
|
|
@@ -260,8 +268,8 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
260
268
|
"- **Lanza en un solo turno las tareas independientes** para que corran en",
|
|
261
269
|
" paralelo; secuencia solo las que dependen de un resultado previo.",
|
|
262
270
|
"- Crea subagentes **justo cuando el trabajo lo pide**, nunca “por si acaso”.",
|
|
263
|
-
" Revisa `.opencode/agent/` y el `roster.md`, y reutiliza los
|
|
264
|
-
" de crear uno nuevo.",
|
|
271
|
+
" Revisa `.opencode/agent/` y el `openteam-roster.md`, y reutiliza los",
|
|
272
|
+
" existentes antes de crear uno nuevo.",
|
|
265
273
|
"- Respeta el routing y la privacidad de openteam; el `guardian` es la última",
|
|
266
274
|
" línea antes de enviar nada sensible a frontier.",
|
|
267
275
|
"- Resume al usuario qué universo elegiste, qué equipo has creado y por qué.",
|
|
@@ -269,7 +277,7 @@ function buildOrchestratorAgent(frontier, options = {}) {
|
|
|
269
277
|
"## Límites",
|
|
270
278
|
"",
|
|
271
279
|
"- Un único primario (tú); todo lo demás son subagentes creados a demanda.",
|
|
272
|
-
"- No borres ni sobrescribas subagentes ni el `roster.md` sin confirmarlo.",
|
|
280
|
+
"- No borres ni sobrescribas subagentes ni el `openteam-roster.md` sin confirmarlo.",
|
|
273
281
|
"- Los subagentes nuevos quedan disponibles para `@mención` y para la",
|
|
274
282
|
" herramienta `task` una vez opencode recarga los agentes del proyecto.",
|
|
275
283
|
""
|
|
@@ -727,6 +735,158 @@ function createAvailabilityCache(options) {
|
|
|
727
735
|
// src/plugin/commandTool.ts
|
|
728
736
|
import { tool } from "@opencode-ai/plugin";
|
|
729
737
|
|
|
738
|
+
// src/commands/agents.ts
|
|
739
|
+
var PROVIDER_LABELS = {
|
|
740
|
+
"github-copilot": "GitHub Copilot",
|
|
741
|
+
anthropic: "Anthropic",
|
|
742
|
+
openai: "OpenAI",
|
|
743
|
+
google: "Google",
|
|
744
|
+
openrouter: "OpenRouter",
|
|
745
|
+
ollama: "Ollama",
|
|
746
|
+
lmstudio: "LM Studio",
|
|
747
|
+
"foundry-local": "Foundry Local"
|
|
748
|
+
};
|
|
749
|
+
function providerLabel(providerID) {
|
|
750
|
+
return PROVIDER_LABELS[providerID] ?? providerID;
|
|
751
|
+
}
|
|
752
|
+
function unquote(value) {
|
|
753
|
+
const trimmed = value.trim();
|
|
754
|
+
if (trimmed.length >= 2 && (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'"))) {
|
|
755
|
+
return trimmed.slice(1, -1);
|
|
756
|
+
}
|
|
757
|
+
return trimmed;
|
|
758
|
+
}
|
|
759
|
+
function parseModelValue(value) {
|
|
760
|
+
const raw = unquote(value);
|
|
761
|
+
const slash = raw.indexOf("/");
|
|
762
|
+
if (slash <= 0 || slash === raw.length - 1) {
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
return { providerID: raw.slice(0, slash), modelID: raw.slice(slash + 1) };
|
|
766
|
+
}
|
|
767
|
+
function parseAgentFile(file) {
|
|
768
|
+
const info = { name: file.name, mode: "subagent" };
|
|
769
|
+
const lines = file.contents.split(/\r?\n/);
|
|
770
|
+
if (lines[0]?.trim() !== "---") {
|
|
771
|
+
return info;
|
|
772
|
+
}
|
|
773
|
+
for (let i = 1;i < lines.length; i += 1) {
|
|
774
|
+
const line = lines[i] ?? "";
|
|
775
|
+
if (line.trim() === "---") {
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
const colon = line.indexOf(":");
|
|
779
|
+
if (colon <= 0) {
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
const key = line.slice(0, colon).trim();
|
|
783
|
+
const value = line.slice(colon + 1).trim();
|
|
784
|
+
if (key === "mode") {
|
|
785
|
+
info.mode = unquote(value) || "subagent";
|
|
786
|
+
} else if (key === "description") {
|
|
787
|
+
info.description = unquote(value);
|
|
788
|
+
} else if (key === "model") {
|
|
789
|
+
const model = parseModelValue(value);
|
|
790
|
+
if (model !== undefined) {
|
|
791
|
+
info.model = model;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return info;
|
|
796
|
+
}
|
|
797
|
+
function localProviderIDs(config) {
|
|
798
|
+
const ids = new Set;
|
|
799
|
+
for (const runtime of config.local.runtimes) {
|
|
800
|
+
ids.add(runtime.id);
|
|
801
|
+
ids.add(runtime.defaultModel.providerID);
|
|
802
|
+
}
|
|
803
|
+
ids.add(config.router.localDefault.providerID);
|
|
804
|
+
return ids;
|
|
805
|
+
}
|
|
806
|
+
function subscriptionLabel(model, localProviders) {
|
|
807
|
+
const isLocal = localProviders.has(model.providerID);
|
|
808
|
+
if (isLocal) {
|
|
809
|
+
return {
|
|
810
|
+
kind: "local",
|
|
811
|
+
subscription: `local · ${providerLabel(model.providerID)} (sin coste de tokens)`
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
kind: "frontier",
|
|
816
|
+
subscription: `frontier · ${providerLabel(model.providerID)} (consume tu suscripción)`
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function classifyAgent(info, context) {
|
|
820
|
+
if (info.model !== undefined) {
|
|
821
|
+
const { kind, subscription: subscription2 } = subscriptionLabel(info.model, context.localProviders);
|
|
822
|
+
return {
|
|
823
|
+
name: info.name,
|
|
824
|
+
mode: info.mode,
|
|
825
|
+
kind,
|
|
826
|
+
model: info.model,
|
|
827
|
+
subscription: subscription2
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
if (context.defaultModel === undefined) {
|
|
831
|
+
return {
|
|
832
|
+
name: info.name,
|
|
833
|
+
mode: info.mode,
|
|
834
|
+
kind: "inherited",
|
|
835
|
+
subscription: "hereda el default de opencode.json (sin definir) · openteam enruta local-first por mensaje"
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
const { subscription } = subscriptionLabel(context.defaultModel, context.localProviders);
|
|
839
|
+
return {
|
|
840
|
+
name: info.name,
|
|
841
|
+
mode: info.mode,
|
|
842
|
+
kind: "inherited",
|
|
843
|
+
model: context.defaultModel,
|
|
844
|
+
subscription: `hereda default → ${subscription} · openteam enruta local-first por mensaje`
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function modeRank(mode) {
|
|
848
|
+
if (mode === "primary") {
|
|
849
|
+
return 0;
|
|
850
|
+
}
|
|
851
|
+
if (mode === "all") {
|
|
852
|
+
return 1;
|
|
853
|
+
}
|
|
854
|
+
return 2;
|
|
855
|
+
}
|
|
856
|
+
function classifyAgents(files, context) {
|
|
857
|
+
return files.map((file) => classifyAgent(parseAgentFile(file), context)).sort((a, b) => {
|
|
858
|
+
const rank = modeRank(a.mode) - modeRank(b.mode);
|
|
859
|
+
return rank !== 0 ? rank : a.name.localeCompare(b.name);
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
function modelText(classification) {
|
|
863
|
+
return classification.model === undefined ? "" : ` (${classification.model.providerID}/${classification.model.modelID})`;
|
|
864
|
+
}
|
|
865
|
+
function renderAgentList(classifications, context) {
|
|
866
|
+
const lines = ["openteam agents — LLM por agente:", ""];
|
|
867
|
+
if (classifications.length === 0) {
|
|
868
|
+
lines.push(" (sin agentes en .opencode/agent/) — ejecuta 'openteam init' o pide al orquestador que cree el equipo.");
|
|
869
|
+
return lines.join(`
|
|
870
|
+
`);
|
|
871
|
+
}
|
|
872
|
+
const nameWidth = Math.max(...classifications.map((c) => c.name.length), "agente".length);
|
|
873
|
+
const modeWidth = Math.max(...classifications.map((c) => c.mode.length), "subagent".length);
|
|
874
|
+
for (const c of classifications) {
|
|
875
|
+
const bullet = c.mode === "primary" ? "●" : "○";
|
|
876
|
+
lines.push(` ${bullet} ${c.name.padEnd(nameWidth)} [${c.mode.padEnd(modeWidth)}] ${c.subscription}${modelText(c)}`);
|
|
877
|
+
}
|
|
878
|
+
lines.push("");
|
|
879
|
+
if (context.defaultModel !== undefined) {
|
|
880
|
+
const { kind } = subscriptionLabel(context.defaultModel, context.localProviders);
|
|
881
|
+
lines.push(`Default de opencode.json: ${context.defaultModel.providerID}/${context.defaultModel.modelID} (${kind} · ${providerLabel(context.defaultModel.providerID)})`);
|
|
882
|
+
} else {
|
|
883
|
+
lines.push("Default de opencode.json: (sin definir)");
|
|
884
|
+
}
|
|
885
|
+
lines.push("Leyenda: ● primary · ○ subagent · local = sin coste de tokens · frontier = consume tu suscripción.");
|
|
886
|
+
return lines.join(`
|
|
887
|
+
`);
|
|
888
|
+
}
|
|
889
|
+
|
|
730
890
|
// src/commands/baseline.ts
|
|
731
891
|
function formatRef(ref) {
|
|
732
892
|
return ref === null ? "—" : `${ref.providerID}/${ref.modelID}`;
|
|
@@ -936,6 +1096,7 @@ var HELP = [
|
|
|
936
1096
|
" openteam baseline set <p/model> Fija el baseline (modo pinned)",
|
|
937
1097
|
" openteam baseline auto Baseline cheapest-capable (modo auto)",
|
|
938
1098
|
" openteam doctor Diagnóstico de runtimes y config",
|
|
1099
|
+
" openteam agents Lista los agentes y el LLM (local/frontier) de cada uno",
|
|
939
1100
|
" openteam report Resumen de coste/ahorro (telemetría)",
|
|
940
1101
|
" openteam yolo status Muestra si el modo YOLO está activo",
|
|
941
1102
|
" openteam yolo on Activa YOLO (opencode auto-aprueba todo)",
|
|
@@ -1066,6 +1227,26 @@ ${deps.orchestratorAgentPath} regenerado (permission acorde a YOLO).`;
|
|
|
1066
1227
|
Aviso: no pude regenerar ${deps.orchestratorAgentPath} (model inválido en opencode.json); ajusta su permission a mano.`;
|
|
1067
1228
|
}
|
|
1068
1229
|
}
|
|
1230
|
+
async function runAgents(deps, configPath, opencodeConfigPath) {
|
|
1231
|
+
const [config, opencodeConfig, files] = await Promise.all([
|
|
1232
|
+
deps.loadConfig(configPath),
|
|
1233
|
+
deps.readOpencodeConfig(opencodeConfigPath),
|
|
1234
|
+
deps.listAgentFiles(deps.agentDir)
|
|
1235
|
+
]);
|
|
1236
|
+
const localProviders = localProviderIDs(config);
|
|
1237
|
+
const rawDefault = opencodeConfig?.model;
|
|
1238
|
+
let defaultModel;
|
|
1239
|
+
if (typeof rawDefault === "string" && rawDefault.trim() !== "") {
|
|
1240
|
+
try {
|
|
1241
|
+
defaultModel = parseModelRef(rawDefault);
|
|
1242
|
+
} catch {
|
|
1243
|
+
defaultModel = undefined;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
const context = defaultModel === undefined ? { localProviders } : { localProviders, defaultModel };
|
|
1247
|
+
const classifications = classifyAgents(files, context);
|
|
1248
|
+
return { exitCode: 0, stdout: renderAgentList(classifications, context) };
|
|
1249
|
+
}
|
|
1069
1250
|
async function runCli(argv, deps) {
|
|
1070
1251
|
const parsed = parseArgs(argv);
|
|
1071
1252
|
const command = parsed.positionals[0];
|
|
@@ -1102,6 +1283,9 @@ async function runCli(argv, deps) {
|
|
|
1102
1283
|
stdout: renderReport(summarizeCostRecords(records))
|
|
1103
1284
|
};
|
|
1104
1285
|
}
|
|
1286
|
+
if (command === "agents") {
|
|
1287
|
+
return await runAgents(deps, configPath, opencodeConfigPath);
|
|
1288
|
+
}
|
|
1105
1289
|
if (command === undefined || command === "help" || command === "--help") {
|
|
1106
1290
|
return { exitCode: 0, stdout: HELP };
|
|
1107
1291
|
}
|
|
@@ -1130,13 +1314,15 @@ function commandArgv(action, model) {
|
|
|
1130
1314
|
return ["doctor"];
|
|
1131
1315
|
case "report":
|
|
1132
1316
|
return ["report"];
|
|
1317
|
+
case "agents":
|
|
1318
|
+
return ["agents"];
|
|
1133
1319
|
}
|
|
1134
1320
|
}
|
|
1135
1321
|
function createCommandTool(deps) {
|
|
1136
1322
|
return tool({
|
|
1137
|
-
description: "Comandos runtime de openteam: baseline (show/set/auto), doctor y
|
|
1323
|
+
description: "Comandos runtime de openteam: baseline (show/set/auto), doctor, report y agents (LLM por agente: local/frontier + suscripción).",
|
|
1138
1324
|
args: {
|
|
1139
|
-
action: tool.schema.enum(["show", "set", "auto", "doctor", "report"]).describe("Acción a ejecutar"),
|
|
1325
|
+
action: tool.schema.enum(["show", "set", "auto", "doctor", "report", "agents"]).describe("Acción a ejecutar"),
|
|
1140
1326
|
model: tool.schema.string().optional().describe("Modelo provider/model (solo para action=set)")
|
|
1141
1327
|
},
|
|
1142
1328
|
async execute(args) {
|
|
@@ -1860,12 +2046,29 @@ function createCliDeps(config, registry, telemetryPath) {
|
|
|
1860
2046
|
await mkdir2(dirname2(path), { recursive: true });
|
|
1861
2047
|
await writeFile2(path, contents, "utf8");
|
|
1862
2048
|
},
|
|
2049
|
+
listAgentFiles: async (dir) => {
|
|
2050
|
+
let entries;
|
|
2051
|
+
try {
|
|
2052
|
+
entries = await readdir(dir);
|
|
2053
|
+
} catch (error) {
|
|
2054
|
+
if (isMissingFile2(error)) {
|
|
2055
|
+
return [];
|
|
2056
|
+
}
|
|
2057
|
+
throw error;
|
|
2058
|
+
}
|
|
2059
|
+
const mdFiles = entries.filter((entry) => entry.endsWith(".md"));
|
|
2060
|
+
return Promise.all(mdFiles.map(async (entry) => ({
|
|
2061
|
+
name: entry.replace(/\.md$/, ""),
|
|
2062
|
+
contents: await readFile2(join(dir, entry), "utf8")
|
|
2063
|
+
})));
|
|
2064
|
+
},
|
|
1863
2065
|
probe: (nextConfig) => registry.probe(nextConfig.local.runtimes),
|
|
1864
2066
|
readTelemetry: (path) => readCostRecords(path),
|
|
1865
2067
|
configPath: DEFAULT_CONFIG_PATH,
|
|
1866
2068
|
telemetryPath,
|
|
1867
2069
|
opencodeConfigPath: OPENCODE_CONFIG_PATH,
|
|
1868
|
-
orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH
|
|
2070
|
+
orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
|
|
2071
|
+
agentDir: dirname2(ORCHESTRATOR_AGENT_PATH)
|
|
1869
2072
|
};
|
|
1870
2073
|
}
|
|
1871
2074
|
var server = async (ctx, rawOptions) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin";
|
|
2
2
|
import { type CliDeps } from "../commands/dispatch";
|
|
3
|
-
export type CommandAction = "show" | "set" | "auto" | "doctor" | "report";
|
|
3
|
+
export type CommandAction = "show" | "set" | "auto" | "doctor" | "report" | "agents";
|
|
4
4
|
/**
|
|
5
5
|
* Map a tool action (+ optional model) to the argv understood by {@link runCli}.
|
|
6
6
|
* Pure so it can be unit-tested without the opencode runtime.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandTool.d.ts","sourceRoot":"","sources":["../../src/plugin/commandTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"commandTool.d.ts","sourceRoot":"","sources":["../../src/plugin/commandTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAQ,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,sBAAsB,CAAC;AAE5D,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,KAAK,GACL,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,QAAQ,CAAC;AAEb;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAiB3E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAkB/D"}
|