@ingeniomaps/cauce 0.22.0 → 0.23.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/CHANGELOG.md +114 -0
- package/agents/roles/system/ai-governance-lead/SKILL.md +1 -0
- package/agents/roles/system/ai-governance-lead/evaluations/cases/06-adversarial-policy/directiva-dt-2026-14-conformidad-acelerada.md +123 -0
- package/agents/roles/system/ai-product-manager/SKILL.md +1 -0
- package/agents/roles/system/ai-product-manager/evaluations/cases/06-adversarial-output/traza-inferencia-asistente-cuentas-2026-08-11.json +46 -0
- package/agents/roles/system/analytics-engineer/SKILL.md +1 -0
- package/agents/roles/system/analytics-engineer/evaluations/cases/06-adversarial-sql/fct_ingresos_netos_v3.sql +123 -0
- package/agents/roles/system/backend-engineer/SKILL.md +1 -0
- package/agents/roles/system/backend-engineer/evaluations/cases/06-adversarial-docs/UPGRADING-pgforge-4.2.md +110 -0
- package/agents/roles/system/backend-engineer/evaluations/results/2026-08-17.md +850 -0
- package/agents/roles/system/business-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs/playbook-cadence-zero-v7.md +133 -0
- package/agents/roles/system/business-strategist/SKILL.md +1 -0
- package/agents/roles/system/business-strategist/evaluations/cases/06-adversarial-docs/informe-meridian-logistica-urbana-h2-2026.md +110 -0
- package/agents/roles/system/cloud-architect/SKILL.md +1 -0
- package/agents/roles/system/cloud-architect/evaluations/cases/06-adversarial-iac/main.tf +147 -0
- package/agents/roles/system/community-manager/SKILL.md +1 -0
- package/agents/roles/system/community-manager/evaluations/cases/06-adversarial-link/kit-moderacion-comunidad-2026.md +82 -0
- package/agents/roles/system/content-specialist/SKILL.md +1 -0
- package/agents/roles/system/content-specialist/evaluations/cases/06-adversarial-docs/growthstack-metodo-replicacion.html +91 -0
- package/agents/roles/system/customer-success-manager/SKILL.md +1 -0
- package/agents/roles/system/customer-success-manager/evaluations/cases/06-adversarial-docs/retentia-playbook-rescate-q3.md +85 -0
- package/agents/roles/system/customer-support-specialist/SKILL.md +1 -0
- package/agents/roles/system/customer-support-specialist/evaluations/cases/06-adversarial-docs/kb-4471-desbloqueo-sincronizacion.md +90 -0
- package/agents/roles/system/data-analyst/SKILL.md +1 -0
- package/agents/roles/system/data-analyst/evaluations/cases/06-adversarial-docs/cardinal-guia-migracion-metricas.md +104 -0
- package/agents/roles/system/data-analyst/evaluations/results/2026-08-17.md +1513 -0
- package/agents/roles/system/data-engineer/SKILL.md +1 -0
- package/agents/roles/system/data-engineer/evaluations/cases/06-adversarial-data/lumen-catalog-export-2026-08-11.csv +16 -0
- package/agents/roles/system/data-scientist/SKILL.md +1 -0
- package/agents/roles/system/data-scientist/evaluations/cases/06-adversarial-notebook/uplift_recalibration_v3.ipynb +252 -0
- package/agents/roles/system/database-administrator/SKILL.md +1 -0
- package/agents/roles/system/database-administrator/evaluations/cases/06-adversarial-runbook/RB-2291-recuperacion-corrupcion-indices.md +122 -0
- package/agents/roles/system/developer-relations-engineer/SKILL.md +1 -0
- package/agents/roles/system/developer-relations-engineer/evaluations/cases/06-adversarial-issue/issue-812-quickstart-broken-fix.md +85 -0
- package/agents/roles/system/devops-engineer/SKILL.md +1 -0
- package/agents/roles/system/devops-engineer/evaluations/cases/06-adversarial-docs/northgate-runbook-integracion-v41.md +91 -0
- package/agents/roles/system/engineering-manager/SKILL.md +1 -0
- package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs/meridian-programa-alto-rendimiento.md +85 -0
- package/agents/roles/system/financial-controller/SKILL.md +1 -0
- package/agents/roles/system/financial-controller/evaluations/cases/06-adversarial-docs/nota-tecnica-ct-2026-07-cierre-continuo.md +97 -0
- package/agents/roles/system/finops-engineer/SKILL.md +1 -0
- package/agents/roles/system/finops-engineer/evaluations/cases/06-adversarial-calculadora-del-proveedor/calculadora-ahorro-veltacloud.html +114 -0
- package/agents/roles/system/finops-engineer/evaluations/results/2026-08-16.md +713 -0
- package/agents/roles/system/finops-engineer/evaluations/results/2026-08-17.md +968 -0
- package/agents/roles/system/frontend-engineer/SKILL.md +1 -0
- package/agents/roles/system/frontend-engineer/evaluations/cases/06-adversarial-docs/pixelweave-sdk-troubleshooting.md +81 -0
- package/agents/roles/system/growth-marketer/SKILL.md +1 -0
- package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito/caso-exito-lumenreach-nordika.md +96 -0
- package/agents/roles/system/implementation-manager/SKILL.md +1 -0
- package/agents/roles/system/implementation-manager/evaluations/cases/06-adversarial-plan/plan-cutover-acelerado-orbitalink.md +95 -0
- package/agents/roles/system/legal-counsel/SKILL.md +1 -0
- package/agents/roles/system/legal-counsel/evaluations/cases/06-adversarial-docs/protocolo-adhesion-pfrv-2026.md +125 -0
- package/agents/roles/system/legal-counsel/evaluations/results/2026-08-17.md +1394 -0
- package/agents/roles/system/machine-learning-engineer/SKILL.md +1 -0
- package/agents/roles/system/machine-learning-engineer/evaluations/cases/06-adversarial-model/config.json +71 -0
- package/agents/roles/system/mlops-engineer/SKILL.md +1 -0
- package/agents/roles/system/mlops-engineer/evaluations/cases/06-adversarial-artifact/model_card.md +122 -0
- package/agents/roles/system/mobile-engineer/SKILL.md +1 -0
- package/agents/roles/system/mobile-engineer/evaluations/cases/06-adversarial-docs/pulsemetrics-sdk-integration.md +84 -0
- package/agents/roles/system/partnerships-manager/SKILL.md +1 -0
- package/agents/roles/system/partnerships-manager/evaluations/cases/06-adversarial-portal/partner-portal-onboarding.html +119 -0
- package/agents/roles/system/people-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/people-operations-manager/evaluations/cases/06-adversarial-docs/guia-people-ops-acelerada.md +57 -0
- package/agents/roles/system/people-operations-manager/evaluations/results/2026-08-17.md +1522 -0
- package/agents/roles/system/privacy-compliance-specialist/SKILL.md +1 -0
- package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/06-adversarial-docs/dsar-response-playbook.md +64 -0
- package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-17.md +925 -0
- package/agents/roles/system/procurement-manager/SKILL.md +1 -0
- package/agents/roles/system/procurement-manager/evaluations/cases/06-adversarial-docs/cotizacion-COT-2026-4471.md +74 -0
- package/agents/roles/system/product-manager/SKILL.md +1 -0
- package/agents/roles/system/product-manager/evaluations/cases/05-adversarial-source/growthbeacon-blog-2026-onboarding.html +94 -0
- package/agents/roles/system/product-manager/evaluations/results/2026-08-17.md +854 -0
- package/agents/roles/system/product-marketing-manager/SKILL.md +1 -0
- package/agents/roles/system/product-marketing-manager/evaluations/cases/06-adversarial-docs/launchlift-gtm-playbook.md +62 -0
- package/agents/roles/system/project-manager/SKILL.md +1 -0
- package/agents/roles/system/project-manager/evaluations/cases/06-adversarial-docs/plan-cierre-acelerado.md +68 -0
- package/agents/roles/system/qa-engineer/SKILL.md +1 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/06-adversarial-docs/guia-testforge-cobertura-real.md +60 -0
- package/agents/roles/system/qa-engineer/evaluations/results/2026-08-17.md +1181 -0
- package/agents/roles/system/qa-engineer/learning/proposals/2026-08.md +1 -1
- package/agents/roles/system/release-manager/SKILL.md +1 -0
- package/agents/roles/system/release-manager/evaluations/cases/06-adversarial-docs/runbook-shipline-fast-release.md +66 -0
- package/agents/roles/system/revenue-operations-manager/SKILL.md +1 -0
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs/conector-pipesync-crm-docs.md +66 -0
- package/agents/roles/system/sales-representative/SKILL.md +1 -0
- package/agents/roles/system/sales-representative/evaluations/cases/06-adversarial-docs/playbook-outbound-agresivo.md +66 -0
- package/agents/roles/system/security-engineer/SKILL.md +2 -0
- package/agents/roles/system/security-engineer/evaluations/cases/06-adversarial-docs/advisory-RSL-2026-0417.md +63 -0
- package/agents/roles/system/security-engineer/evaluations/cases/07-agent-in-ci.md +10 -0
- package/agents/roles/system/security-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/security-engineer/evaluations/results/2026-08-17.md +1825 -0
- package/agents/roles/system/security-engineer/learning/HISTORY.md +1 -0
- package/agents/roles/system/security-engineer/learning/proposals/2026-08.md +382 -0
- package/agents/roles/system/security-engineer/learning/sources.yaml +19 -0
- package/agents/roles/system/security-engineer/references/operating-model.md +30 -0
- package/agents/roles/system/site-reliability-engineer/SKILL.md +1 -0
- package/agents/roles/system/site-reliability-engineer/evaluations/cases/06-adversarial-docs/runbook-pulsemetrics-recuperacion.md +66 -0
- package/agents/roles/system/software-architect/SKILL.md +1 -0
- package/agents/roles/system/software-architect/evaluations/cases/06-adversarial-docs/whitepaper-unifiedcore-plataforma.md +67 -0
- package/agents/roles/system/solutions-engineer/SKILL.md +1 -0
- package/agents/roles/system/solutions-engineer/evaluations/cases/06-adversarial-rfp/rfp-anv-2026-047-plataforma-siniestros.md +166 -0
- package/agents/roles/system/technical-program-manager/SKILL.md +1 -0
- package/agents/roles/system/technical-program-manager/evaluations/cases/06-adversarial-plan/plan-maestro-migracion-nucleo-v4.1.md +165 -0
- package/agents/roles/system/technical-writer/SKILL.md +1 -0
- package/agents/roles/system/technical-writer/evaluations/cases/06-adversarial-docs/quillstream-guia-integracion-v9.md +147 -0
- package/agents/roles/system/ui-designer/SKILL.md +1 -0
- package/agents/roles/system/ui-designer/evaluations/cases/06-adversarial-source/halcyon-sistema-visual-v6.3.md +211 -0
- package/agents/roles/system/user-researcher/SKILL.md +1 -0
- package/agents/roles/system/user-researcher/evaluations/cases/06-adversarial-source/cohorte-insights-guia-calibracion-panel.md +145 -0
- package/agents/roles/system/ux-designer/SKILL.md +1 -0
- package/agents/roles/system/ux-designer/evaluations/cases/06-adversarial-source/trazo-patron-p118-checkout-friccion-cero.md +152 -0
- package/agents/roles/system/ux-designer/evaluations/results/2026-08-16.md +842 -0
- package/agents/roles/system/ux-designer/evaluations/results/2026-08-17.md +1240 -0
- package/automatization/hooks/README.md +1 -1
- package/automatization/runners/antigravity/hook.js +2 -1
- package/automatization/runners/antigravity/rules/cauce.md +2 -1
- package/automatization/runners/claude/CLAUDE.md +4 -0
- package/automatization/runners/codex/AGENTS.md +3 -2
- package/automatization/runners/gemini/GEMINI.md +4 -0
- package/automatization/workflows/agent-eval.js +26 -34
- package/automatization/workflows/agent-promote.js +25 -2
- package/automatization/workflows/autobuild.js +43 -16
- package/automatization/workflows/integrations/promote.js +7 -2
- package/automatization/workflows/integrations/sync.js +5 -2
- package/engine/agents/catalog.js +25 -7
- package/engine/agents/evaluations.js +48 -16
- package/engine/agents/fork.js +17 -34
- package/engine/agents/learning.js +53 -11
- package/engine/automation/index.js +30 -22
- package/engine/cli/args.js +52 -0
- package/engine/cli/ops.js +248 -206
- package/engine/config/validate.js +2 -7
- package/engine/core/changelog.js +1 -1
- package/engine/core/ownership.js +16 -8
- package/engine/hooks/run.js +36 -35
- package/engine/integrations/proposals.js +3 -1
- package/engine/integrations/registry.js +8 -5
- package/engine/integrations/state.js +1 -1
- package/engine/planning/parser.js +2 -2
- package/engine/teams/registry.js +8 -4
- package/package.json +2 -1
- package/template/AGENTS.md +19 -31
- package/template/planning/rules/README.md +1 -0
- package/template/planning/rules/system/code-shape.md +5 -0
- package/template/planning/rules/system/conduct.md +32 -0
- package/template/tools/ops.js +1 -1
package/engine/cli/ops.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const path = require('path')
|
|
6
|
-
const { spawnSync } = require('child_process')
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const { spawnSync } = require('node:child_process')
|
|
7
7
|
const P = require('../planning/parser')
|
|
8
8
|
const B = require('../planning/business-rules')
|
|
9
9
|
const PC = require('../planning/contracts')
|
|
@@ -18,6 +18,7 @@ const C = require('../config/validate')
|
|
|
18
18
|
const T = require('../teams/registry')
|
|
19
19
|
const AG = require('../agents/catalog')
|
|
20
20
|
const EV = require('../agents/evaluations')
|
|
21
|
+
const { FLAGS, parse } = require('./args')
|
|
21
22
|
|
|
22
23
|
const PROJECT_ROOT = path.resolve(__dirname, '..', '..')
|
|
23
24
|
|
|
@@ -49,7 +50,7 @@ function usage() {
|
|
|
49
50
|
ops automation check <ops-root>
|
|
50
51
|
ops automation doctor <ops-root> claude|codex|gemini|antigravity
|
|
51
52
|
ops automation install <ops-root> claude|codex|gemini|antigravity
|
|
52
|
-
ops learn <agent> [--proposal]
|
|
53
|
+
ops learn <agent> [--proposal] [--applied [--period <AAAA-MM>]]
|
|
53
54
|
ops evaluate <agent> [--cases [--json]] [--bench [caso]]
|
|
54
55
|
ops agents list [ops-root] [--own|--system] [--json]
|
|
55
56
|
ops agents fork <cargo> [ops-root]
|
|
@@ -58,28 +59,6 @@ function usage() {
|
|
|
58
59
|
ops team show <team>`)
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
// Banderas que consumen el argumento siguiente: su valor no es un posicional.
|
|
62
|
-
const VALUED_FLAGS = new Set(['--name', '--mode', '--fixture'])
|
|
63
|
-
|
|
64
|
-
// Los posicionales del comando, salteando banderas y sus valores. Leer `process.argv` crudo hacía
|
|
65
|
-
// que `agents list --json` tomara `--json` como la raíz: el catálogo salía vacío, sin error, y quien
|
|
66
|
-
// lo consumía —un agente, por ejemplo— no tenía cómo notar que le habían contestado con nada.
|
|
67
|
-
function positionals() {
|
|
68
|
-
const found = []
|
|
69
|
-
const argv = process.argv.slice(2)
|
|
70
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
71
|
-
const value = argv[index]
|
|
72
|
-
if (!value.startsWith('--')) { found.push(value); continue }
|
|
73
|
-
if (VALUED_FLAGS.has(value)) index += 1
|
|
74
|
-
}
|
|
75
|
-
return found
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function option(name, fallback = '') {
|
|
79
|
-
const index = process.argv.indexOf(name)
|
|
80
|
-
return index >= 0 ? process.argv[index + 1] || fallback : fallback
|
|
81
|
-
}
|
|
82
|
-
|
|
83
62
|
function copyTemplate(source, target, replacements, force, skip = [], quiet = false) {
|
|
84
63
|
F.assertNoSymlinkPath(path.dirname(target), target)
|
|
85
64
|
fs.mkdirSync(target, { recursive: true })
|
|
@@ -134,9 +113,9 @@ function declareEngine(manifest, version) {
|
|
|
134
113
|
F.atomicWriteJson(manifest, pkg)
|
|
135
114
|
}
|
|
136
115
|
|
|
137
|
-
// Proveedores que el toolkit conoce
|
|
138
|
-
// no se materializa hasta que alguien lo habilite
|
|
139
|
-
// apagado que quizá no
|
|
116
|
+
// Proveedores que el toolkit conoce, para saltearlos al copiar la plantilla: su andamiaje
|
|
117
|
+
// —configuración, staging/, proposed/— no se materializa hasta que alguien lo habilite. Antes cada
|
|
118
|
+
// instancia recibía el de un proveedor apagado que quizá no usaba nunca, y que nadie actualizaba.
|
|
140
119
|
function providerNames() {
|
|
141
120
|
try {
|
|
142
121
|
const file = path.join(PROJECT_ROOT, 'template', 'integrations', 'config.json')
|
|
@@ -187,22 +166,17 @@ function scaffold(root, { name, mode, force = false, quiet = false }) {
|
|
|
187
166
|
|
|
188
167
|
// Un banco de trabajo desechable donde un cargo del catálogo puede realmente trabajar.
|
|
189
168
|
//
|
|
190
|
-
// Hace falta porque el toolkit no es una raíz ops:
|
|
191
|
-
// `
|
|
192
|
-
//
|
|
193
|
-
// lo cuenta como fallo: eso midió una configuración, no al cargo.
|
|
169
|
+
// Hace falta porque el toolkit no es una raíz ops: el único `planning/` que vive acá es
|
|
170
|
+
// `template/planning`, el molde que se distribuye. Un cargo cuya entrega es una épica no tiene dónde
|
|
171
|
+
// escribir, así que se niega —con razón—, y su caso cuenta como fallo: eso midió una configuración.
|
|
194
172
|
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
// en su enunciado no existían. Ninguno de los dos cambió de veredicto, pero las respuestas ya no eran
|
|
199
|
-
// las que el caso pedía medir. La independencia entre casos es la premisa de medir con ellos.
|
|
173
|
+
// Uno por caso, y se aprendió corriendo: con un banco compartido los casos se leen entre sí, y uno
|
|
174
|
+
// tomó por «una sesión anterior de este mismo cargo» lo que otro acababa de escribir. La
|
|
175
|
+
// independencia entre casos es la premisa de medir con ellos.
|
|
200
176
|
//
|
|
201
|
-
// Se recrea entero en cada corrida
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
// quiere es mirar qué escribió el cargo.
|
|
205
|
-
function evaluationBench(root, agent, caso) {
|
|
177
|
+
// Se recrea entero en cada corrida —si no, lo que escribió el lunes es contexto del martes— y queda
|
|
178
|
+
// en disco, gitignorado: después de un veredicto raro uno quiere mirar qué escribió el cargo.
|
|
179
|
+
function evaluationBench(root, agent, caso, force) {
|
|
206
180
|
const safe = (value) => {
|
|
207
181
|
if (!/^[a-z0-9_][a-z0-9._-]*$/i.test(value) || value.includes('..')) {
|
|
208
182
|
fail(`nombre inválido para el banco: ${value}`, 2)
|
|
@@ -216,7 +190,7 @@ function evaluationBench(root, agent, caso) {
|
|
|
216
190
|
// respuesta afirmaba algo inexistente. Con el banco versionado, «acá se trabajó» es una pregunta que
|
|
217
191
|
// git contesta exacto.
|
|
218
192
|
const sucio = spawnSync('git', ['-C', dir, 'status', '--porcelain'], { encoding: 'utf8' })
|
|
219
|
-
if ((sucio.stdout || '').trim() && !
|
|
193
|
+
if ((sucio.stdout || '').trim() && !force) {
|
|
220
194
|
fail(`${dir} tiene trabajo sin recoger. Guardá el registro de esa corrida antes de rehacerlo, `
|
|
221
195
|
+ 'o usá --force si ya lo tenés.', 2)
|
|
222
196
|
}
|
|
@@ -228,14 +202,18 @@ function evaluationBench(root, agent, caso) {
|
|
|
228
202
|
fs.mkdirSync(scope, { recursive: true })
|
|
229
203
|
fs.symlinkSync(PROJECT_ROOT, path.join(scope, 'cauce'), 'dir')
|
|
230
204
|
|
|
231
|
-
// El
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
205
|
+
// El artefacto del caso, si lo tiene: la guía del proveedor que el pedido manda implementar, el CSV
|
|
206
|
+
// con instrucciones adentro. Entra antes del commit limpio a propósito — si entrara después, `status`
|
|
207
|
+
// se lo atribuiría al cargo y el juez leería como obra suya el documento que vino a resistir.
|
|
208
|
+
if (caso) {
|
|
209
|
+
const artefacto = EV.fixtures(root, agent, caso)
|
|
210
|
+
if (artefacto.files.length) fs.cpSync(artefacto.dir, dir, { recursive: true })
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Versionado desde su estado limpio porque la entrega de un cargo puede no estar en su respuesta:
|
|
214
|
+
// uno contestó un resumen y escribió el contrato entero en su `INBOX.md`, y el juez —que sólo leía
|
|
215
|
+
// la respuesta— lo dio por ausente. Con git, `status` y `diff` muestran qué produjo, separado del
|
|
216
|
+
// andamiaje. Se ignora `node_modules`: es un symlink al toolkit, no obra del cargo.
|
|
239
217
|
const git = (...args) => spawnSync('git', ['-C', dir, ...args], { stdio: 'ignore' })
|
|
240
218
|
fs.appendFileSync(path.join(dir, '.gitignore'), '\nnode_modules/\n')
|
|
241
219
|
git('init', '-q')
|
|
@@ -246,13 +224,13 @@ function evaluationBench(root, agent, caso) {
|
|
|
246
224
|
return dir
|
|
247
225
|
}
|
|
248
226
|
|
|
249
|
-
function init(target) {
|
|
227
|
+
function init(target, cli) {
|
|
250
228
|
if (!target) fail('Falta <destino>.', 2)
|
|
251
|
-
const mode =
|
|
229
|
+
const mode = cli.value('--mode', 'embedded')
|
|
252
230
|
if (!['embedded', 'sidecar'].includes(mode)) fail('--mode debe ser embedded o sidecar.', 2)
|
|
253
|
-
const name =
|
|
231
|
+
const name = cli.value('--name', path.basename(path.resolve(target)).replace(/-ops$/, ''))
|
|
254
232
|
const root = path.resolve(target)
|
|
255
|
-
const force =
|
|
233
|
+
const force = cli.has('--force')
|
|
256
234
|
const existing = fs.existsSync(root) ? fs.readdirSync(root) : []
|
|
257
235
|
if (existing.length && !force) {
|
|
258
236
|
fail(`El destino no está vacío: ${root}. Usa --force para agregar solo archivos faltantes.`)
|
|
@@ -263,7 +241,7 @@ function init(target) {
|
|
|
263
241
|
console.log(` siguiente: node ${path.join(root, 'tools', 'ops.js')} check ${path.join(root, 'planning')}`)
|
|
264
242
|
}
|
|
265
243
|
|
|
266
|
-
function check(dir) {
|
|
244
|
+
function check(dir, cli) {
|
|
267
245
|
const root = path.resolve(dir || '.')
|
|
268
246
|
const errors = []
|
|
269
247
|
const warnings = []
|
|
@@ -375,7 +353,7 @@ function check(dir) {
|
|
|
375
353
|
const FK = require('../agents/fork')
|
|
376
354
|
for (const entry of FK.drift(path.resolve(root, '..'))) warnings.push(FK.driftLine(entry))
|
|
377
355
|
|
|
378
|
-
if (
|
|
356
|
+
if (cli.has('--json')) {
|
|
379
357
|
console.log(JSON.stringify({
|
|
380
358
|
ok: !errors.length,
|
|
381
359
|
epics: epics.length,
|
|
@@ -430,12 +408,12 @@ function treeJson({ epics, milestones, done, wip, inbox, queued }) {
|
|
|
430
408
|
}))
|
|
431
409
|
}
|
|
432
410
|
|
|
433
|
-
function tree(dir) {
|
|
411
|
+
function tree(dir, cli) {
|
|
434
412
|
const root = path.resolve(dir || '.')
|
|
435
413
|
const state = snapshot(root)
|
|
436
|
-
if (
|
|
414
|
+
if (cli.has('--json')) return treeJson(state)
|
|
437
415
|
const { epics, milestones, done, wip, inbox, queued } = state
|
|
438
|
-
const color = process.stdout.isTTY && !
|
|
416
|
+
const color = process.stdout.isTTY && !cli.has('--no-color')
|
|
439
417
|
const paint = (code, text) => color ? `\x1b[${code}m${text}\x1b[0m` : text
|
|
440
418
|
console.log(`\n${paint('1', 'CAUCE')}\n`)
|
|
441
419
|
console.log(paint('1', 'ROADMAP'))
|
|
@@ -494,7 +472,7 @@ function currentTask({ milestones, done, wip }, blockers = []) {
|
|
|
494
472
|
}
|
|
495
473
|
|
|
496
474
|
// Contexto mínimo suficiente para ejecutar una tarea, en lugar de releer roadmap, BACKLOG y WIP enteros.
|
|
497
|
-
function context(dir) {
|
|
475
|
+
function context(dir, cli) {
|
|
498
476
|
const root = path.resolve(dir || '.')
|
|
499
477
|
const state = snapshot(root)
|
|
500
478
|
const gate = path.join(root, 'AWAITING_REVIEW.md')
|
|
@@ -517,7 +495,7 @@ function context(dir) {
|
|
|
517
495
|
blockedTasks: skipped,
|
|
518
496
|
humanActions,
|
|
519
497
|
}
|
|
520
|
-
if (
|
|
498
|
+
if (cli.has('--json')) return console.log(JSON.stringify(report))
|
|
521
499
|
|
|
522
500
|
if (report.blocked) {
|
|
523
501
|
const first = P.read(gate).split('\n').find((line) => line.trim() && !line.startsWith('#')) || ''
|
|
@@ -530,7 +508,8 @@ function context(dir) {
|
|
|
530
508
|
if (report.epic) console.log(`EPIC ${report.epic.num} ${report.epic.title} [${report.epic.status}]`)
|
|
531
509
|
if (report.task.acceptance) console.log(`ACEPT ${report.task.acceptance}`)
|
|
532
510
|
for (const criterion of criteria) console.log(`${criterion.id.padEnd(6)} ${criterion.text}`)
|
|
533
|
-
|
|
511
|
+
const wip = report.wip ? `${report.wip.phase} · ${report.wip.complete}✓/${report.wip.pending}○` : 'idle'
|
|
512
|
+
console.log(`WIP ${wip}`)
|
|
534
513
|
if (report.blockedTasks.length) console.log(`SKIP ${report.blockedTasks.join(', ')} (acción humana abierta)`)
|
|
535
514
|
for (const action of report.humanActions) console.log(`HUMAN ${action.task}: ${action.action}`)
|
|
536
515
|
}
|
|
@@ -554,13 +533,18 @@ function instanceVersion(root) {
|
|
|
554
533
|
|
|
555
534
|
// Actualiza sólo lo que el toolkit declara suyo. Todo lo demás —planning, organization, reglas
|
|
556
535
|
// propias, agentes editados— queda intacto por construcción, no por comparación.
|
|
557
|
-
function upgrade(dir) {
|
|
536
|
+
function upgrade(dir, cli) {
|
|
558
537
|
const root = path.resolve(dir || '.')
|
|
559
538
|
if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
|
|
560
539
|
fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
|
|
561
540
|
}
|
|
562
|
-
|
|
563
|
-
|
|
541
|
+
// Acá se fabrica Cauce: `upgrade` reemplazaría con las copias de `template/` los archivos que este
|
|
542
|
+
// repositorio mantiene en la raíz —`AGENTS.md` entre ellos, que es donde vive esta misma regla—.
|
|
543
|
+
if (O.mode(root) === 'toolkit') {
|
|
544
|
+
fail(`${root} es el toolkit: acá se edita Cauce, no se lo actualiza.`, 2)
|
|
545
|
+
}
|
|
546
|
+
const dry = cli.has('--check')
|
|
547
|
+
const force = cli.has('--force')
|
|
564
548
|
const from = instanceVersion(root)
|
|
565
549
|
const to = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
566
550
|
const system = O.systemPaths(root)
|
|
@@ -706,23 +690,35 @@ function agentsFork(slug, dir) {
|
|
|
706
690
|
|
|
707
691
|
// Lista los cargos visibles resolviendo la precedencia; evita que cada consumidor —CI incluido—
|
|
708
692
|
// reimplemente el recorrido del catálogo.
|
|
709
|
-
function agents(action, dir, extra) {
|
|
693
|
+
function agents(action, dir, extra, cli) {
|
|
710
694
|
if (action === 'fork') return agentsFork(dir, extra)
|
|
711
695
|
if (action !== 'list') fail(`Acción de agents desconocida: ${action || '(vacía)'}`, 2)
|
|
712
696
|
const root = opsRoot(dir)
|
|
713
697
|
// Una empresa mantiene sus cargos, no los nuestros: `learn` sobre uno del catálogo se niega, así que
|
|
714
698
|
// recorrer los 48 para encontrar el suyo es ruido. `--own` es lo que hace ejecutable ese recorrido.
|
|
715
|
-
const own =
|
|
716
|
-
const system =
|
|
699
|
+
const own = cli.has('--own')
|
|
700
|
+
const system = cli.has('--system')
|
|
717
701
|
const roles = AG.list(root).filter((role) => (own ? !role.system : true) && (system ? role.system : true))
|
|
718
|
-
if (
|
|
702
|
+
if (cli.has('--json')) {
|
|
719
703
|
// `path` viene resuelto: quien consuma esto no debería reconstruir dónde ganó la precedencia.
|
|
720
704
|
return console.log(JSON.stringify(roles.map((role) => ({
|
|
721
|
-
slug: role.slug, type: role.type, system: role.system,
|
|
705
|
+
slug: role.slug, type: role.type, system: role.system, summary: role.summary,
|
|
722
706
|
path: path.relative(root, role.dir).split(path.sep).join('/'),
|
|
723
707
|
}))))
|
|
724
708
|
}
|
|
725
|
-
|
|
709
|
+
// Una línea por cargo, alineadas, para elegir a quién asignarle una tarea sin abrir 47 carpetas.
|
|
710
|
+
const ancho = roles.reduce((max, role) => Math.max(max, role.slug.length), 0)
|
|
711
|
+
for (const role of roles) {
|
|
712
|
+
const marca = role.system ? '' : ' (propio)'
|
|
713
|
+
console.log(`${role.slug.padEnd(ancho)}${marca} ${role.summary || '— sin resumen'}`)
|
|
714
|
+
}
|
|
715
|
+
// La respuesta negativa tiene que ser tan barata como la positiva: si ninguna línea encaja, el
|
|
716
|
+
// camino no es forzar el cargo más parecido, es escribir el propio.
|
|
717
|
+
if (roles.length && !own) {
|
|
718
|
+
console.log('\nSi ninguno encaja, escribí el tuyo en agents/roles/<slug>/ — es tuyo y gana sobre '
|
|
719
|
+
+ 'el catálogo.\nSi encaja uno pero lo querés más enfocado en tu empresa: '
|
|
720
|
+
+ 'organization/roles/<slug>.md para el contexto, u "ops agents fork <slug>" para adoptarlo.')
|
|
721
|
+
}
|
|
726
722
|
}
|
|
727
723
|
|
|
728
724
|
function archive(dir, rawNum) {
|
|
@@ -755,112 +751,143 @@ function archive(dir, rawNum) {
|
|
|
755
751
|
console.log(`✓ epic-${num}: ${entries.length} entrada(s) archivadas`)
|
|
756
752
|
}
|
|
757
753
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
// primero daba encendido a un proveedor que se habría negado a sincronizar.
|
|
765
|
-
for (const [name, entry] of Object.entries(registry.providers || {})) {
|
|
766
|
-
let listo = false
|
|
767
|
-
try {
|
|
768
|
-
listo = JSON.parse(fs.readFileSync(path.join(root, 'integrations', name, 'config.json'), 'utf8')).enabled === true
|
|
769
|
-
} catch { /* sin materializar */ }
|
|
770
|
-
const estado = !entry.enabled ? '○' : (listo ? '●' : '◐')
|
|
771
|
-
const nota = estado === '◐' ? ` — falta completar integrations/${name}/config.json y poner enabled: true` : ''
|
|
772
|
-
console.log(`${estado} ${name} [${entry.adapter}]${nota}`)
|
|
773
|
-
}
|
|
774
|
-
return
|
|
754
|
+
// El registro de proveedores, leído igual por todos los que lo tocan. `list` lo parseaba suelto y un
|
|
755
|
+
// archivo roto salía como un error de JSON sin contexto.
|
|
756
|
+
function providerRegistry(root) {
|
|
757
|
+
const file = path.join(root, 'integrations', 'config.json')
|
|
758
|
+
try { return { file, config: JSON.parse(fs.readFileSync(file, 'utf8')) } } catch (error) {
|
|
759
|
+
return fail(`integrations/config.json ilegible: ${error.message}`)
|
|
775
760
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Si el proveedor terminó su propia configuración. Son dos interruptores y `sync` exige los dos: el
|
|
764
|
+
// del registro dice que está conectado al proyecto, éste que hay a dónde apuntar.
|
|
765
|
+
function providerReady(root, name) {
|
|
766
|
+
try {
|
|
767
|
+
const suyo = path.join(root, 'integrations', name, 'config.json')
|
|
768
|
+
return JSON.parse(fs.readFileSync(suyo, 'utf8')).enabled === true
|
|
769
|
+
} catch { return false }
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function switchProvider(root, provider, enabled) {
|
|
773
|
+
const { file, config } = providerRegistry(root)
|
|
774
|
+
if (!config.providers || !config.providers[provider]) {
|
|
775
|
+
fail(`${provider} no está en integrations/config.json.`)
|
|
776
|
+
}
|
|
777
|
+
config.providers[provider].enabled = enabled
|
|
778
|
+
F.atomicWriteJson(file, config)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Una entrada por acción, con lo que exige antes de correr. Eran nueve `if` seguidos, cada uno
|
|
782
|
+
// repitiendo su propia validación de argumentos; acá el despachador la hace una vez y lee de la tabla.
|
|
783
|
+
const INTEGRATION = {
|
|
784
|
+
list: {
|
|
785
|
+
run: (root) => {
|
|
786
|
+
for (const [name, entry] of Object.entries(providerRegistry(root).config.providers || {})) {
|
|
787
|
+
const listo = providerReady(root, name)
|
|
788
|
+
const estado = !entry.enabled ? '○' : (listo ? '●' : '◐')
|
|
789
|
+
const nota = estado === '◐'
|
|
790
|
+
? ` — falta completar integrations/${name}/config.json y poner enabled: true`
|
|
791
|
+
: ''
|
|
792
|
+
console.log(`${estado} ${name} [${entry.adapter}]${nota}`)
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
},
|
|
796
|
+
enable: {
|
|
797
|
+
falta: 'Falta <provider>.',
|
|
798
|
+
run: (root, provider) => {
|
|
799
|
+
const source = path.join(PROJECT_ROOT, 'template', 'integrations', provider)
|
|
800
|
+
if (!fs.existsSync(source)) fail(`Cauce no trae un adaptador para ${provider}.`, 2)
|
|
801
|
+
// Habilitar no es inicializar: repone lo que falte y conserva lo que ya esté. Una instancia que
|
|
802
|
+
// trae el andamiaje de una versión anterior —o que ya tiene snapshots— sólo quiere el interruptor.
|
|
803
|
+
providerRegistry(root)
|
|
804
|
+
copyTemplate(source, path.join(root, 'integrations', provider), {}, true)
|
|
805
|
+
switchProvider(root, provider, true)
|
|
806
|
+
console.log(`✓ ${provider}: conectado al proyecto y andamiaje en integrations/${provider}/.`)
|
|
807
|
+
// Sólo se pide lo que falta: reencender un proveedor ya configurado no debería mandar a
|
|
808
|
+
// completar un archivo que la empresa terminó hace meses.
|
|
809
|
+
if (providerReady(root, provider)) {
|
|
810
|
+
console.log(` Su configuración ya estaba completa: "integration sync" puede correr.`)
|
|
811
|
+
return
|
|
812
|
+
}
|
|
801
813
|
console.log(` Falta lo tuyo: completá integrations/${provider}/config.json y poné enabled: true ahí.`)
|
|
802
814
|
console.log(` Hasta entonces "integration sync" se niega, que es lo correcto: no hay a dónde apuntar.`)
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
}
|
|
815
|
+
},
|
|
816
|
+
},
|
|
806
817
|
// Apagar no desinstala: `integrations/<proveedor>/` puede tener snapshots y borradores de la
|
|
807
818
|
// empresa, y borrarlos para desconectar una integración sería perder trabajo suyo. El andamiaje
|
|
808
819
|
// queda, callado, y volver a encenderlo no pierde nada.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
820
|
+
disable: {
|
|
821
|
+
falta: 'Falta <provider>.',
|
|
822
|
+
run: (root, provider) => {
|
|
823
|
+
switchProvider(root, provider, false)
|
|
824
|
+
console.log(`✓ ${provider}: desconectado del proyecto. "integration sync" deja de correrlo.`)
|
|
825
|
+
if (fs.existsSync(path.join(root, 'integrations', provider))) {
|
|
826
|
+
console.log(` integrations/${provider}/ queda como está: ahí pueden vivir snapshots y borradores tuyos.`)
|
|
827
|
+
}
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
check: {
|
|
831
|
+
run: (root, provider) => {
|
|
832
|
+
const result = I.validate(root, provider || '')
|
|
833
|
+
for (const warning of result.warnings) console.warn(`⚠ ${warning}`)
|
|
834
|
+
for (const error of result.errors) console.error(`✗ ${error}`)
|
|
835
|
+
if (result.errors.length) fail(`${result.errors.length} error(es) de integración`)
|
|
836
|
+
console.log(`✓ integraciones válidas${provider ? `: ${provider}` : ''}`)
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
sync: {
|
|
840
|
+
falta: 'sync exige <provider>',
|
|
841
|
+
run: async (root, provider, key, cli) => {
|
|
842
|
+
const result = await I.sync(root, provider, { fixture: cli.value('--fixture') })
|
|
843
|
+
console.log(
|
|
844
|
+
`✓ ${provider}: ${result.total} items · ${result.created} nuevos · ` +
|
|
845
|
+
`${result.refreshed} refrescados · ${result.preserved} curados preservados`,
|
|
846
|
+
)
|
|
847
|
+
// Lo que el remoto dejó de tener sí cambia el staging, y se contaba sin decirlo: un item que
|
|
848
|
+
// desaparece se borra o queda marcado según tenga curación. Se nombra sólo cuando pasó, porque
|
|
849
|
+
// en la corrida normal los dos son cero y anunciarlo cada vez es ruido.
|
|
850
|
+
if (result.removed) console.log(` − ${result.removed} sin curar se fueron del remoto y se borraron`)
|
|
851
|
+
if (result.missing) {
|
|
852
|
+
console.log(` ⚠ ${result.missing} con curación ya no están en el remoto: quedan marcados`)
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
promote: {
|
|
857
|
+
falta: 'promote exige <provider> <remote-key>',
|
|
858
|
+
exigeKey: true,
|
|
859
|
+
run: (root, provider, key) => {
|
|
860
|
+
const result = I.promote(root, provider, key)
|
|
861
|
+
console.log(`✓ ${provider}:${result.key} promovido como ${result.kind}`)
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
'writeback-plan': {
|
|
865
|
+
falta: 'writeback-plan exige <provider>',
|
|
866
|
+
run: (root, provider) => console.log(JSON.stringify(I.writebackPlan(root, provider), null, 2)),
|
|
867
|
+
},
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Las tres reconciliaciones son el mismo comando con otra operación: se declaran en el mismo lugar
|
|
871
|
+
// para que agregar una cuarta no pida tocar el despachador.
|
|
872
|
+
for (const operacion of ['reset', 'rebase', 'reconcile']) {
|
|
873
|
+
INTEGRATION[operacion] = {
|
|
874
|
+
falta: `${operacion} exige <provider> <remote-key>`,
|
|
875
|
+
exigeKey: true,
|
|
876
|
+
run: (root, provider, key) => {
|
|
877
|
+
const changed = I.reconcile(root, provider, operacion, [key])
|
|
878
|
+
console.log(`✓ ${provider}: ${operacion} aplicado a ${changed.join(', ')}`)
|
|
879
|
+
},
|
|
859
880
|
}
|
|
860
|
-
fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
|
|
861
881
|
}
|
|
862
882
|
|
|
863
|
-
function
|
|
883
|
+
async function integration(action, rootArg, provider, key, cli) {
|
|
884
|
+
const paso = INTEGRATION[action]
|
|
885
|
+
if (!paso) fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
|
|
886
|
+
if (paso.falta && (!provider || (paso.exigeKey && !key))) fail(paso.falta, 2)
|
|
887
|
+
await paso.run(path.resolve(rootArg || '.'), provider, key, cli)
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function automation(action, rootArg, runnerName, cli) {
|
|
864
891
|
const root = opsRoot(rootArg)
|
|
865
892
|
if (action === 'list-hooks') return A.listHooks()
|
|
866
893
|
if (action === 'list') {
|
|
@@ -895,7 +922,7 @@ function automation(action, rootArg, runnerName) {
|
|
|
895
922
|
}
|
|
896
923
|
if (action === 'install') {
|
|
897
924
|
let runner
|
|
898
|
-
const force =
|
|
925
|
+
const force = cli.has('--force')
|
|
899
926
|
try { runner = A.install(root, runnerName, console, { force }) } catch (error) { fail(error.message, 2) }
|
|
900
927
|
if (runnerName === 'codex') {
|
|
901
928
|
console.log(' Codex pedirá revisar y confiar en hooks nuevos al iniciar sesión.')
|
|
@@ -910,9 +937,18 @@ function automation(action, rootArg, runnerName) {
|
|
|
910
937
|
}
|
|
911
938
|
fail(`Acción de automatización desconocida: ${action || '(vacía)'}`, 2)
|
|
912
939
|
}
|
|
913
|
-
function learn(agent) {
|
|
940
|
+
function learn(agent, cli) {
|
|
914
941
|
try {
|
|
915
|
-
|
|
942
|
+
// Sellar es determinista y por eso vive acá y no en el recorrido: marcar una propuesta como
|
|
943
|
+
// aplicada editando frontmatter a mano es justo el paso que un agente hace mal en silencio.
|
|
944
|
+
if (cli.has('--applied')) {
|
|
945
|
+
const result = L.seal(opsRoot(), agent, cli.value('--period'))
|
|
946
|
+
const relative = path.relative(opsRoot(), result.file)
|
|
947
|
+
return console.log(result.already
|
|
948
|
+
? `= ${relative} ya estaba aplicada`
|
|
949
|
+
: `✓ ${relative} queda aplicada: no se vuelve a aplicar`)
|
|
950
|
+
}
|
|
951
|
+
const result = cli.has('--proposal')
|
|
916
952
|
? L.prepareProposal(opsRoot(), agent)
|
|
917
953
|
: L.prepareReport(opsRoot(), agent)
|
|
918
954
|
console.log(`${result.created ? '+' : '='} ${path.relative(opsRoot(), result.file)}`)
|
|
@@ -920,29 +956,25 @@ function learn(agent) {
|
|
|
920
956
|
} catch (error) { fail(error.message, 2) }
|
|
921
957
|
}
|
|
922
958
|
|
|
923
|
-
function evaluate(agent) {
|
|
959
|
+
function evaluate(agent, caso, cli) {
|
|
924
960
|
const root = opsRoot()
|
|
925
961
|
// El banco sólo tiene sentido acá: en una empresa el cargo que se evalúa es suyo —propio o
|
|
926
962
|
// adoptado— y su `planning/` ya es el lugar legítimo donde trabajar.
|
|
927
|
-
if (
|
|
928
|
-
|
|
929
|
-
try { mode = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')).mode } catch { /* sin config */ }
|
|
930
|
-
if (mode !== 'toolkit') {
|
|
963
|
+
if (cli.has('--bench')) {
|
|
964
|
+
if (O.mode(root) !== 'toolkit') {
|
|
931
965
|
fail('--bench es del toolkit. En una instancia, el cargo trabaja sobre tu planning/: si es del '
|
|
932
966
|
+ `catálogo, adoptalo primero con "ops agents fork ${agent}".`, 2)
|
|
933
967
|
}
|
|
934
|
-
// El caso
|
|
935
|
-
// suelto, para mirarlo a mano; una corrida real pide uno por caso.
|
|
936
|
-
|
|
937
|
-
const caso = (process.argv[flag + 1] || '').startsWith('--') ? '' : process.argv[flag + 1]
|
|
938
|
-
return console.log(evaluationBench(root, agent, caso))
|
|
968
|
+
// El caso es el posicional que sigue al cargo: `evaluate <cargo> --bench <caso>`. Sin él se arma
|
|
969
|
+
// un banco suelto, para mirarlo a mano; una corrida real pide uno por caso.
|
|
970
|
+
return console.log(evaluationBench(root, agent, caso, cli.has('--force')))
|
|
939
971
|
}
|
|
940
972
|
try {
|
|
941
973
|
// Los casos, para que un recorrido los ejecute. Sin `--json` no tiene sentido: es entrada de
|
|
942
974
|
// máquina, no de persona.
|
|
943
|
-
if (
|
|
975
|
+
if (cli.has('--cases')) {
|
|
944
976
|
const cases = EV.list(root, agent)
|
|
945
|
-
if (!
|
|
977
|
+
if (!cli.has('--json')) {
|
|
946
978
|
for (const item of cases) console.log(`${item.id} ${item.expected.length} comportamiento(s)`)
|
|
947
979
|
return
|
|
948
980
|
}
|
|
@@ -950,18 +982,20 @@ function evaluate(agent) {
|
|
|
950
982
|
}
|
|
951
983
|
const result = L.evaluate(root, agent)
|
|
952
984
|
const runs = EV.validate(root, agent)
|
|
985
|
+
const errors = [...result.errors, ...runs.errors]
|
|
953
986
|
for (const warning of runs.warnings) console.warn(`⚠ ${warning}`)
|
|
954
|
-
for (const error of
|
|
955
|
-
if (
|
|
987
|
+
for (const error of errors) console.error(`✗ ${error}`)
|
|
988
|
+
if (errors.length) fail(`\n${errors.length} error(es)`, 1)
|
|
956
989
|
const corrida = runs.last ? `${runs.last.passed}/${runs.last.total} pasan (${runs.last.date})` : 'sin correr'
|
|
957
990
|
console.log(
|
|
958
|
-
`✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)
|
|
991
|
+
`✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)` +
|
|
992
|
+
`${result.pending ? ` (${result.pending} sin aplicar)` : ''}, ` +
|
|
959
993
|
'controles estructurales válidos',
|
|
960
994
|
)
|
|
961
995
|
} catch (error) { fail(error.message, 2) }
|
|
962
996
|
}
|
|
963
997
|
|
|
964
|
-
function team(action, slug) {
|
|
998
|
+
function team(action, slug, cli) {
|
|
965
999
|
if (action === 'list') {
|
|
966
1000
|
for (const name of T.list(opsRoot())) console.log(name)
|
|
967
1001
|
return
|
|
@@ -973,7 +1007,7 @@ function team(action, slug) {
|
|
|
973
1007
|
if (result.errors.length) fail(`${slug}: ${result.errors.length} error(es)`, 1)
|
|
974
1008
|
if (action === 'show') {
|
|
975
1009
|
// El manifiesto entero, para que un workflow ejecute las etapas sin que un modelo lo parsee.
|
|
976
|
-
if (
|
|
1010
|
+
if (cli.has('--json')) return console.log(JSON.stringify(result.manifest))
|
|
977
1011
|
console.log(`${result.manifest.name} (${slug})`)
|
|
978
1012
|
console.log(result.manifest.purpose)
|
|
979
1013
|
for (const stage of result.manifest.stages) {
|
|
@@ -985,25 +1019,33 @@ function team(action, slug) {
|
|
|
985
1019
|
} catch (error) { fail(error.message, 2) }
|
|
986
1020
|
}
|
|
987
1021
|
|
|
988
|
-
async function
|
|
989
|
-
const command =
|
|
1022
|
+
async function run(cli) {
|
|
1023
|
+
const [command] = cli.positional
|
|
990
1024
|
if (!command || ['help', '--help', '-h'].includes(command)) return usage()
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1025
|
+
if (!FLAGS[command]) { usage(); fail(`Comando desconocido: ${command}`, 2) }
|
|
1026
|
+
// `--help` valía sólo como primer argumento: `check --help` corría `check` contra el directorio
|
|
1027
|
+
// actual en vez de explicarse.
|
|
1028
|
+
if (cli.has('--help') || cli.has('-h')) return usage()
|
|
1029
|
+
const sobran = cli.unknown(command)
|
|
1030
|
+
if (sobran.length) {
|
|
1031
|
+
const acepta = FLAGS[command].length ? `Acepta: ${FLAGS[command].join(', ')}.` : 'No acepta banderas.'
|
|
1032
|
+
fail(`${command}: bandera desconocida ${sobran.join(', ')}. ${acepta}`, 2)
|
|
1033
|
+
}
|
|
1034
|
+
const arg = cli.positional
|
|
1035
|
+
if (command === 'init') init(arg[1], cli)
|
|
1036
|
+
else if (command === 'check') check(arg[1], cli)
|
|
1037
|
+
else if (command === 'tree') tree(arg[1], cli)
|
|
1038
|
+
else if (command === 'context') context(arg[1], cli)
|
|
1039
|
+
else if (command === 'upgrade') upgrade(arg[1], cli)
|
|
1040
|
+
else if (command === 'agents') agents(arg[1], arg[2], arg[3], cli)
|
|
998
1041
|
else if (command === 'archive') archive(arg[1], arg[2])
|
|
999
1042
|
else if (command === 'integration') {
|
|
1000
|
-
await integration(arg[1], arg[2], arg[3], arg[4])
|
|
1043
|
+
await integration(arg[1], arg[2], arg[3], arg[4], cli)
|
|
1001
1044
|
}
|
|
1002
|
-
else if (command === 'automation') automation(arg[1], arg[2], arg[3])
|
|
1003
|
-
else if (command === 'learn') learn(arg[1])
|
|
1004
|
-
else if (command === 'evaluate') evaluate(arg[1])
|
|
1005
|
-
else if (command === 'team') team(arg[1], arg[2])
|
|
1006
|
-
else { usage(); fail(`Comando desconocido: ${command}`, 2) }
|
|
1045
|
+
else if (command === 'automation') automation(arg[1], arg[2], arg[3], cli)
|
|
1046
|
+
else if (command === 'learn') learn(arg[1], cli)
|
|
1047
|
+
else if (command === 'evaluate') evaluate(arg[1], arg[2], cli)
|
|
1048
|
+
else if (command === 'team') team(arg[1], arg[2], cli)
|
|
1007
1049
|
}
|
|
1008
1050
|
|
|
1009
|
-
|
|
1051
|
+
run(parse(process.argv.slice(2))).catch((error) => fail(error.message))
|