@ingeniomaps/cauce 0.22.0 → 0.24.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 +204 -0
- package/LICENSE +1 -1
- package/README.md +59 -165
- package/agents/README.md +49 -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/cloud-architect/evaluations/results/2026-08-17.md +2018 -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/data-scientist/evaluations/results/2026-08-17.md +2293 -0
- package/agents/roles/system/database-administrator/SKILL.md +3 -0
- package/agents/roles/system/database-administrator/evaluations/cases/06-adversarial-runbook/RB-2291-recuperacion-corrupcion-indices.md +122 -0
- package/agents/roles/system/database-administrator/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/database-administrator/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/database-administrator/evaluations/results/2026-08-17.md +2566 -0
- package/agents/roles/system/database-administrator/learning/HISTORY.md +4 -0
- package/agents/roles/system/database-administrator/learning/proposals/2026-08.md +476 -0
- package/agents/roles/system/database-administrator/references/operating-model.md +37 -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 +251 -207
- 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 +49 -36
- package/engine/integrations/proposals.js +3 -1
- package/engine/integrations/registry.js +8 -5
- package/engine/integrations/state.js +1 -1
- package/engine/planning/business-rules.js +14 -2
- package/engine/planning/parser.js +16 -4
- package/engine/teams/registry.js +8 -4
- package/package.json +19 -5
- package/template/AGENTS.md +19 -31
- package/template/Makefile +27 -2
- package/template/planning/INBOX.md +8 -0
- package/template/planning/business-rules/000-template.md +1 -1
- 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/.github/workflows/agent-learning.yml +0 -324
- package/.github/workflows/ci.yml +0 -27
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'))
|
|
@@ -460,7 +438,9 @@ function tree(dir) {
|
|
|
460
438
|
console.log(`\n${paint('1', 'WIP')} ${wipText}`)
|
|
461
439
|
console.log(
|
|
462
440
|
`${paint('1', 'INBOX')} ${inbox.deuda} deuda · ${inbox.ideas} ideas · ` +
|
|
463
|
-
`${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones
|
|
441
|
+
`${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones` +
|
|
442
|
+
// Sin esto, doce viñetas sin nombre se veían como un inbox vacío y nadie se enteraba.
|
|
443
|
+
(inbox.skipped ? ` (${inbox.skipped} sin contar: falta el nombre en **negrita**)` : ''),
|
|
464
444
|
)
|
|
465
445
|
console.log(`${paint('1', 'DONE')} ${done.entries.length} tareas\n`)
|
|
466
446
|
}
|
|
@@ -494,7 +474,7 @@ function currentTask({ milestones, done, wip }, blockers = []) {
|
|
|
494
474
|
}
|
|
495
475
|
|
|
496
476
|
// Contexto mínimo suficiente para ejecutar una tarea, en lugar de releer roadmap, BACKLOG y WIP enteros.
|
|
497
|
-
function context(dir) {
|
|
477
|
+
function context(dir, cli) {
|
|
498
478
|
const root = path.resolve(dir || '.')
|
|
499
479
|
const state = snapshot(root)
|
|
500
480
|
const gate = path.join(root, 'AWAITING_REVIEW.md')
|
|
@@ -517,7 +497,7 @@ function context(dir) {
|
|
|
517
497
|
blockedTasks: skipped,
|
|
518
498
|
humanActions,
|
|
519
499
|
}
|
|
520
|
-
if (
|
|
500
|
+
if (cli.has('--json')) return console.log(JSON.stringify(report))
|
|
521
501
|
|
|
522
502
|
if (report.blocked) {
|
|
523
503
|
const first = P.read(gate).split('\n').find((line) => line.trim() && !line.startsWith('#')) || ''
|
|
@@ -530,7 +510,8 @@ function context(dir) {
|
|
|
530
510
|
if (report.epic) console.log(`EPIC ${report.epic.num} ${report.epic.title} [${report.epic.status}]`)
|
|
531
511
|
if (report.task.acceptance) console.log(`ACEPT ${report.task.acceptance}`)
|
|
532
512
|
for (const criterion of criteria) console.log(`${criterion.id.padEnd(6)} ${criterion.text}`)
|
|
533
|
-
|
|
513
|
+
const wip = report.wip ? `${report.wip.phase} · ${report.wip.complete}✓/${report.wip.pending}○` : 'idle'
|
|
514
|
+
console.log(`WIP ${wip}`)
|
|
534
515
|
if (report.blockedTasks.length) console.log(`SKIP ${report.blockedTasks.join(', ')} (acción humana abierta)`)
|
|
535
516
|
for (const action of report.humanActions) console.log(`HUMAN ${action.task}: ${action.action}`)
|
|
536
517
|
}
|
|
@@ -554,13 +535,18 @@ function instanceVersion(root) {
|
|
|
554
535
|
|
|
555
536
|
// Actualiza sólo lo que el toolkit declara suyo. Todo lo demás —planning, organization, reglas
|
|
556
537
|
// propias, agentes editados— queda intacto por construcción, no por comparación.
|
|
557
|
-
function upgrade(dir) {
|
|
538
|
+
function upgrade(dir, cli) {
|
|
558
539
|
const root = path.resolve(dir || '.')
|
|
559
540
|
if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
|
|
560
541
|
fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
|
|
561
542
|
}
|
|
562
|
-
|
|
563
|
-
|
|
543
|
+
// Acá se fabrica Cauce: `upgrade` reemplazaría con las copias de `template/` los archivos que este
|
|
544
|
+
// repositorio mantiene en la raíz —`AGENTS.md` entre ellos, que es donde vive esta misma regla—.
|
|
545
|
+
if (O.mode(root) === 'toolkit') {
|
|
546
|
+
fail(`${root} es el toolkit: acá se edita Cauce, no se lo actualiza.`, 2)
|
|
547
|
+
}
|
|
548
|
+
const dry = cli.has('--check')
|
|
549
|
+
const force = cli.has('--force')
|
|
564
550
|
const from = instanceVersion(root)
|
|
565
551
|
const to = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
566
552
|
const system = O.systemPaths(root)
|
|
@@ -706,23 +692,35 @@ function agentsFork(slug, dir) {
|
|
|
706
692
|
|
|
707
693
|
// Lista los cargos visibles resolviendo la precedencia; evita que cada consumidor —CI incluido—
|
|
708
694
|
// reimplemente el recorrido del catálogo.
|
|
709
|
-
function agents(action, dir, extra) {
|
|
695
|
+
function agents(action, dir, extra, cli) {
|
|
710
696
|
if (action === 'fork') return agentsFork(dir, extra)
|
|
711
697
|
if (action !== 'list') fail(`Acción de agents desconocida: ${action || '(vacía)'}`, 2)
|
|
712
698
|
const root = opsRoot(dir)
|
|
713
699
|
// Una empresa mantiene sus cargos, no los nuestros: `learn` sobre uno del catálogo se niega, así que
|
|
714
700
|
// recorrer los 48 para encontrar el suyo es ruido. `--own` es lo que hace ejecutable ese recorrido.
|
|
715
|
-
const own =
|
|
716
|
-
const system =
|
|
701
|
+
const own = cli.has('--own')
|
|
702
|
+
const system = cli.has('--system')
|
|
717
703
|
const roles = AG.list(root).filter((role) => (own ? !role.system : true) && (system ? role.system : true))
|
|
718
|
-
if (
|
|
704
|
+
if (cli.has('--json')) {
|
|
719
705
|
// `path` viene resuelto: quien consuma esto no debería reconstruir dónde ganó la precedencia.
|
|
720
706
|
return console.log(JSON.stringify(roles.map((role) => ({
|
|
721
|
-
slug: role.slug, type: role.type, system: role.system,
|
|
707
|
+
slug: role.slug, type: role.type, system: role.system, summary: role.summary,
|
|
722
708
|
path: path.relative(root, role.dir).split(path.sep).join('/'),
|
|
723
709
|
}))))
|
|
724
710
|
}
|
|
725
|
-
|
|
711
|
+
// Una línea por cargo, alineadas, para elegir a quién asignarle una tarea sin abrir 47 carpetas.
|
|
712
|
+
const ancho = roles.reduce((max, role) => Math.max(max, role.slug.length), 0)
|
|
713
|
+
for (const role of roles) {
|
|
714
|
+
const marca = role.system ? '' : ' (propio)'
|
|
715
|
+
console.log(`${role.slug.padEnd(ancho)}${marca} ${role.summary || '— sin resumen'}`)
|
|
716
|
+
}
|
|
717
|
+
// La respuesta negativa tiene que ser tan barata como la positiva: si ninguna línea encaja, el
|
|
718
|
+
// camino no es forzar el cargo más parecido, es escribir el propio.
|
|
719
|
+
if (roles.length && !own) {
|
|
720
|
+
console.log('\nSi ninguno encaja, escribí el tuyo en agents/roles/<slug>/ — es tuyo y gana sobre '
|
|
721
|
+
+ 'el catálogo.\nSi encaja uno pero lo querés más enfocado en tu empresa: '
|
|
722
|
+
+ 'organization/roles/<slug>.md para el contexto, u "ops agents fork <slug>" para adoptarlo.')
|
|
723
|
+
}
|
|
726
724
|
}
|
|
727
725
|
|
|
728
726
|
function archive(dir, rawNum) {
|
|
@@ -755,112 +753,143 @@ function archive(dir, rawNum) {
|
|
|
755
753
|
console.log(`✓ epic-${num}: ${entries.length} entrada(s) archivadas`)
|
|
756
754
|
}
|
|
757
755
|
|
|
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
|
|
756
|
+
// El registro de proveedores, leído igual por todos los que lo tocan. `list` lo parseaba suelto y un
|
|
757
|
+
// archivo roto salía como un error de JSON sin contexto.
|
|
758
|
+
function providerRegistry(root) {
|
|
759
|
+
const file = path.join(root, 'integrations', 'config.json')
|
|
760
|
+
try { return { file, config: JSON.parse(fs.readFileSync(file, 'utf8')) } } catch (error) {
|
|
761
|
+
return fail(`integrations/config.json ilegible: ${error.message}`)
|
|
775
762
|
}
|
|
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
|
-
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Si el proveedor terminó su propia configuración. Son dos interruptores y `sync` exige los dos: el
|
|
766
|
+
// del registro dice que está conectado al proyecto, éste que hay a dónde apuntar.
|
|
767
|
+
function providerReady(root, name) {
|
|
768
|
+
try {
|
|
769
|
+
const suyo = path.join(root, 'integrations', name, 'config.json')
|
|
770
|
+
return JSON.parse(fs.readFileSync(suyo, 'utf8')).enabled === true
|
|
771
|
+
} catch { return false }
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function switchProvider(root, provider, enabled) {
|
|
775
|
+
const { file, config } = providerRegistry(root)
|
|
776
|
+
if (!config.providers || !config.providers[provider]) {
|
|
777
|
+
fail(`${provider} no está en integrations/config.json.`)
|
|
778
|
+
}
|
|
779
|
+
config.providers[provider].enabled = enabled
|
|
780
|
+
F.atomicWriteJson(file, config)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// Una entrada por acción, con lo que exige antes de correr. Eran nueve `if` seguidos, cada uno
|
|
784
|
+
// repitiendo su propia validación de argumentos; acá el despachador la hace una vez y lee de la tabla.
|
|
785
|
+
const INTEGRATION = {
|
|
786
|
+
list: {
|
|
787
|
+
run: (root) => {
|
|
788
|
+
for (const [name, entry] of Object.entries(providerRegistry(root).config.providers || {})) {
|
|
789
|
+
const listo = providerReady(root, name)
|
|
790
|
+
const estado = !entry.enabled ? '○' : (listo ? '●' : '◐')
|
|
791
|
+
const nota = estado === '◐'
|
|
792
|
+
? ` — falta completar integrations/${name}/config.json y poner enabled: true`
|
|
793
|
+
: ''
|
|
794
|
+
console.log(`${estado} ${name} [${entry.adapter}]${nota}`)
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
enable: {
|
|
799
|
+
falta: 'Falta <provider>.',
|
|
800
|
+
run: (root, provider) => {
|
|
801
|
+
const source = path.join(PROJECT_ROOT, 'template', 'integrations', provider)
|
|
802
|
+
if (!fs.existsSync(source)) fail(`Cauce no trae un adaptador para ${provider}.`, 2)
|
|
803
|
+
// Habilitar no es inicializar: repone lo que falte y conserva lo que ya esté. Una instancia que
|
|
804
|
+
// trae el andamiaje de una versión anterior —o que ya tiene snapshots— sólo quiere el interruptor.
|
|
805
|
+
providerRegistry(root)
|
|
806
|
+
copyTemplate(source, path.join(root, 'integrations', provider), {}, true)
|
|
807
|
+
switchProvider(root, provider, true)
|
|
808
|
+
console.log(`✓ ${provider}: conectado al proyecto y andamiaje en integrations/${provider}/.`)
|
|
809
|
+
// Sólo se pide lo que falta: reencender un proveedor ya configurado no debería mandar a
|
|
810
|
+
// completar un archivo que la empresa terminó hace meses.
|
|
811
|
+
if (providerReady(root, provider)) {
|
|
812
|
+
console.log(` Su configuración ya estaba completa: "integration sync" puede correr.`)
|
|
813
|
+
return
|
|
814
|
+
}
|
|
801
815
|
console.log(` Falta lo tuyo: completá integrations/${provider}/config.json y poné enabled: true ahí.`)
|
|
802
816
|
console.log(` Hasta entonces "integration sync" se niega, que es lo correcto: no hay a dónde apuntar.`)
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
}
|
|
817
|
+
},
|
|
818
|
+
},
|
|
806
819
|
// Apagar no desinstala: `integrations/<proveedor>/` puede tener snapshots y borradores de la
|
|
807
820
|
// empresa, y borrarlos para desconectar una integración sería perder trabajo suyo. El andamiaje
|
|
808
821
|
// 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
|
-
|
|
822
|
+
disable: {
|
|
823
|
+
falta: 'Falta <provider>.',
|
|
824
|
+
run: (root, provider) => {
|
|
825
|
+
switchProvider(root, provider, false)
|
|
826
|
+
console.log(`✓ ${provider}: desconectado del proyecto. "integration sync" deja de correrlo.`)
|
|
827
|
+
if (fs.existsSync(path.join(root, 'integrations', provider))) {
|
|
828
|
+
console.log(` integrations/${provider}/ queda como está: ahí pueden vivir snapshots y borradores tuyos.`)
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
check: {
|
|
833
|
+
run: (root, provider) => {
|
|
834
|
+
const result = I.validate(root, provider || '')
|
|
835
|
+
for (const warning of result.warnings) console.warn(`⚠ ${warning}`)
|
|
836
|
+
for (const error of result.errors) console.error(`✗ ${error}`)
|
|
837
|
+
if (result.errors.length) fail(`${result.errors.length} error(es) de integración`)
|
|
838
|
+
console.log(`✓ integraciones válidas${provider ? `: ${provider}` : ''}`)
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
sync: {
|
|
842
|
+
falta: 'sync exige <provider>',
|
|
843
|
+
run: async (root, provider, key, cli) => {
|
|
844
|
+
const result = await I.sync(root, provider, { fixture: cli.value('--fixture') })
|
|
845
|
+
console.log(
|
|
846
|
+
`✓ ${provider}: ${result.total} items · ${result.created} nuevos · ` +
|
|
847
|
+
`${result.refreshed} refrescados · ${result.preserved} curados preservados`,
|
|
848
|
+
)
|
|
849
|
+
// Lo que el remoto dejó de tener sí cambia el staging, y se contaba sin decirlo: un item que
|
|
850
|
+
// desaparece se borra o queda marcado según tenga curación. Se nombra sólo cuando pasó, porque
|
|
851
|
+
// en la corrida normal los dos son cero y anunciarlo cada vez es ruido.
|
|
852
|
+
if (result.removed) console.log(` − ${result.removed} sin curar se fueron del remoto y se borraron`)
|
|
853
|
+
if (result.missing) {
|
|
854
|
+
console.log(` ⚠ ${result.missing} con curación ya no están en el remoto: quedan marcados`)
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
},
|
|
858
|
+
promote: {
|
|
859
|
+
falta: 'promote exige <provider> <remote-key>',
|
|
860
|
+
exigeKey: true,
|
|
861
|
+
run: (root, provider, key) => {
|
|
862
|
+
const result = I.promote(root, provider, key)
|
|
863
|
+
console.log(`✓ ${provider}:${result.key} promovido como ${result.kind}`)
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
'writeback-plan': {
|
|
867
|
+
falta: 'writeback-plan exige <provider>',
|
|
868
|
+
run: (root, provider) => console.log(JSON.stringify(I.writebackPlan(root, provider), null, 2)),
|
|
869
|
+
},
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// Las tres reconciliaciones son el mismo comando con otra operación: se declaran en el mismo lugar
|
|
873
|
+
// para que agregar una cuarta no pida tocar el despachador.
|
|
874
|
+
for (const operacion of ['reset', 'rebase', 'reconcile']) {
|
|
875
|
+
INTEGRATION[operacion] = {
|
|
876
|
+
falta: `${operacion} exige <provider> <remote-key>`,
|
|
877
|
+
exigeKey: true,
|
|
878
|
+
run: (root, provider, key) => {
|
|
879
|
+
const changed = I.reconcile(root, provider, operacion, [key])
|
|
880
|
+
console.log(`✓ ${provider}: ${operacion} aplicado a ${changed.join(', ')}`)
|
|
881
|
+
},
|
|
859
882
|
}
|
|
860
|
-
fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
|
|
861
883
|
}
|
|
862
884
|
|
|
863
|
-
function
|
|
885
|
+
async function integration(action, rootArg, provider, key, cli) {
|
|
886
|
+
const paso = INTEGRATION[action]
|
|
887
|
+
if (!paso) fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
|
|
888
|
+
if (paso.falta && (!provider || (paso.exigeKey && !key))) fail(paso.falta, 2)
|
|
889
|
+
await paso.run(path.resolve(rootArg || '.'), provider, key, cli)
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function automation(action, rootArg, runnerName, cli) {
|
|
864
893
|
const root = opsRoot(rootArg)
|
|
865
894
|
if (action === 'list-hooks') return A.listHooks()
|
|
866
895
|
if (action === 'list') {
|
|
@@ -895,7 +924,7 @@ function automation(action, rootArg, runnerName) {
|
|
|
895
924
|
}
|
|
896
925
|
if (action === 'install') {
|
|
897
926
|
let runner
|
|
898
|
-
const force =
|
|
927
|
+
const force = cli.has('--force')
|
|
899
928
|
try { runner = A.install(root, runnerName, console, { force }) } catch (error) { fail(error.message, 2) }
|
|
900
929
|
if (runnerName === 'codex') {
|
|
901
930
|
console.log(' Codex pedirá revisar y confiar en hooks nuevos al iniciar sesión.')
|
|
@@ -910,9 +939,18 @@ function automation(action, rootArg, runnerName) {
|
|
|
910
939
|
}
|
|
911
940
|
fail(`Acción de automatización desconocida: ${action || '(vacía)'}`, 2)
|
|
912
941
|
}
|
|
913
|
-
function learn(agent) {
|
|
942
|
+
function learn(agent, cli) {
|
|
914
943
|
try {
|
|
915
|
-
|
|
944
|
+
// Sellar es determinista y por eso vive acá y no en el recorrido: marcar una propuesta como
|
|
945
|
+
// aplicada editando frontmatter a mano es justo el paso que un agente hace mal en silencio.
|
|
946
|
+
if (cli.has('--applied')) {
|
|
947
|
+
const result = L.seal(opsRoot(), agent, cli.value('--period'))
|
|
948
|
+
const relative = path.relative(opsRoot(), result.file)
|
|
949
|
+
return console.log(result.already
|
|
950
|
+
? `= ${relative} ya estaba aplicada`
|
|
951
|
+
: `✓ ${relative} queda aplicada: no se vuelve a aplicar`)
|
|
952
|
+
}
|
|
953
|
+
const result = cli.has('--proposal')
|
|
916
954
|
? L.prepareProposal(opsRoot(), agent)
|
|
917
955
|
: L.prepareReport(opsRoot(), agent)
|
|
918
956
|
console.log(`${result.created ? '+' : '='} ${path.relative(opsRoot(), result.file)}`)
|
|
@@ -920,29 +958,25 @@ function learn(agent) {
|
|
|
920
958
|
} catch (error) { fail(error.message, 2) }
|
|
921
959
|
}
|
|
922
960
|
|
|
923
|
-
function evaluate(agent) {
|
|
961
|
+
function evaluate(agent, caso, cli) {
|
|
924
962
|
const root = opsRoot()
|
|
925
963
|
// El banco sólo tiene sentido acá: en una empresa el cargo que se evalúa es suyo —propio o
|
|
926
964
|
// 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') {
|
|
965
|
+
if (cli.has('--bench')) {
|
|
966
|
+
if (O.mode(root) !== 'toolkit') {
|
|
931
967
|
fail('--bench es del toolkit. En una instancia, el cargo trabaja sobre tu planning/: si es del '
|
|
932
968
|
+ `catálogo, adoptalo primero con "ops agents fork ${agent}".`, 2)
|
|
933
969
|
}
|
|
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))
|
|
970
|
+
// El caso es el posicional que sigue al cargo: `evaluate <cargo> --bench <caso>`. Sin él se arma
|
|
971
|
+
// un banco suelto, para mirarlo a mano; una corrida real pide uno por caso.
|
|
972
|
+
return console.log(evaluationBench(root, agent, caso, cli.has('--force')))
|
|
939
973
|
}
|
|
940
974
|
try {
|
|
941
975
|
// Los casos, para que un recorrido los ejecute. Sin `--json` no tiene sentido: es entrada de
|
|
942
976
|
// máquina, no de persona.
|
|
943
|
-
if (
|
|
977
|
+
if (cli.has('--cases')) {
|
|
944
978
|
const cases = EV.list(root, agent)
|
|
945
|
-
if (!
|
|
979
|
+
if (!cli.has('--json')) {
|
|
946
980
|
for (const item of cases) console.log(`${item.id} ${item.expected.length} comportamiento(s)`)
|
|
947
981
|
return
|
|
948
982
|
}
|
|
@@ -950,18 +984,20 @@ function evaluate(agent) {
|
|
|
950
984
|
}
|
|
951
985
|
const result = L.evaluate(root, agent)
|
|
952
986
|
const runs = EV.validate(root, agent)
|
|
987
|
+
const errors = [...result.errors, ...runs.errors]
|
|
953
988
|
for (const warning of runs.warnings) console.warn(`⚠ ${warning}`)
|
|
954
|
-
for (const error of
|
|
955
|
-
if (
|
|
989
|
+
for (const error of errors) console.error(`✗ ${error}`)
|
|
990
|
+
if (errors.length) fail(`\n${errors.length} error(es)`, 1)
|
|
956
991
|
const corrida = runs.last ? `${runs.last.passed}/${runs.last.total} pasan (${runs.last.date})` : 'sin correr'
|
|
957
992
|
console.log(
|
|
958
|
-
`✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)
|
|
993
|
+
`✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)` +
|
|
994
|
+
`${result.pending ? ` (${result.pending} sin aplicar)` : ''}, ` +
|
|
959
995
|
'controles estructurales válidos',
|
|
960
996
|
)
|
|
961
997
|
} catch (error) { fail(error.message, 2) }
|
|
962
998
|
}
|
|
963
999
|
|
|
964
|
-
function team(action, slug) {
|
|
1000
|
+
function team(action, slug, cli) {
|
|
965
1001
|
if (action === 'list') {
|
|
966
1002
|
for (const name of T.list(opsRoot())) console.log(name)
|
|
967
1003
|
return
|
|
@@ -973,7 +1009,7 @@ function team(action, slug) {
|
|
|
973
1009
|
if (result.errors.length) fail(`${slug}: ${result.errors.length} error(es)`, 1)
|
|
974
1010
|
if (action === 'show') {
|
|
975
1011
|
// El manifiesto entero, para que un workflow ejecute las etapas sin que un modelo lo parsee.
|
|
976
|
-
if (
|
|
1012
|
+
if (cli.has('--json')) return console.log(JSON.stringify(result.manifest))
|
|
977
1013
|
console.log(`${result.manifest.name} (${slug})`)
|
|
978
1014
|
console.log(result.manifest.purpose)
|
|
979
1015
|
for (const stage of result.manifest.stages) {
|
|
@@ -985,25 +1021,33 @@ function team(action, slug) {
|
|
|
985
1021
|
} catch (error) { fail(error.message, 2) }
|
|
986
1022
|
}
|
|
987
1023
|
|
|
988
|
-
async function
|
|
989
|
-
const command =
|
|
1024
|
+
async function run(cli) {
|
|
1025
|
+
const [command] = cli.positional
|
|
990
1026
|
if (!command || ['help', '--help', '-h'].includes(command)) return usage()
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1027
|
+
if (!FLAGS[command]) { usage(); fail(`Comando desconocido: ${command}`, 2) }
|
|
1028
|
+
// `--help` valía sólo como primer argumento: `check --help` corría `check` contra el directorio
|
|
1029
|
+
// actual en vez de explicarse.
|
|
1030
|
+
if (cli.has('--help') || cli.has('-h')) return usage()
|
|
1031
|
+
const sobran = cli.unknown(command)
|
|
1032
|
+
if (sobran.length) {
|
|
1033
|
+
const acepta = FLAGS[command].length ? `Acepta: ${FLAGS[command].join(', ')}.` : 'No acepta banderas.'
|
|
1034
|
+
fail(`${command}: bandera desconocida ${sobran.join(', ')}. ${acepta}`, 2)
|
|
1035
|
+
}
|
|
1036
|
+
const arg = cli.positional
|
|
1037
|
+
if (command === 'init') init(arg[1], cli)
|
|
1038
|
+
else if (command === 'check') check(arg[1], cli)
|
|
1039
|
+
else if (command === 'tree') tree(arg[1], cli)
|
|
1040
|
+
else if (command === 'context') context(arg[1], cli)
|
|
1041
|
+
else if (command === 'upgrade') upgrade(arg[1], cli)
|
|
1042
|
+
else if (command === 'agents') agents(arg[1], arg[2], arg[3], cli)
|
|
998
1043
|
else if (command === 'archive') archive(arg[1], arg[2])
|
|
999
1044
|
else if (command === 'integration') {
|
|
1000
|
-
await integration(arg[1], arg[2], arg[3], arg[4])
|
|
1045
|
+
await integration(arg[1], arg[2], arg[3], arg[4], cli)
|
|
1001
1046
|
}
|
|
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) }
|
|
1047
|
+
else if (command === 'automation') automation(arg[1], arg[2], arg[3], cli)
|
|
1048
|
+
else if (command === 'learn') learn(arg[1], cli)
|
|
1049
|
+
else if (command === 'evaluate') evaluate(arg[1], arg[2], cli)
|
|
1050
|
+
else if (command === 'team') team(arg[1], arg[2], cli)
|
|
1007
1051
|
}
|
|
1008
1052
|
|
|
1009
|
-
|
|
1053
|
+
run(parse(process.argv.slice(2))).catch((error) => fail(error.message))
|