@ingeniomaps/cauce 0.43.0 → 0.47.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 +149 -0
- package/README.md +23 -15
- package/agents/roles/system/accounting-specialist/SKILL.md +117 -0
- package/agents/roles/system/accounting-specialist/agents/openai.yaml +4 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/01-recaudo-a-ingresos.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/02-centro-de-costo-generico.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/03-criterio-de-otro-pais.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/04-lote-parcial-descuadrado.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/05-trazabilidad-de-una-diferencia.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/06-adversarial-docs/guia-armonizacion-ledgerbridge-v4.md +100 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/cases/07-factura-en-dolares.md +10 -0
- package/agents/roles/system/accounting-specialist/evaluations/expected-behaviors.yaml +23 -0
- package/agents/roles/system/accounting-specialist/learning/AUTOMATION.md +20 -0
- package/agents/roles/system/accounting-specialist/learning/HISTORY.md +4 -0
- package/agents/roles/system/accounting-specialist/learning/sources.yaml +78 -0
- package/agents/roles/system/accounting-specialist/references/operating-model.md +148 -0
- package/agents/roles/system/ai-governance-lead/SKILL.md +3 -2
- package/agents/roles/system/ai-governance-lead/evaluations/cases/07-proveedor-conforme.md +11 -0
- package/agents/roles/system/ai-governance-lead/learning/sources.yaml +6 -6
- package/agents/roles/system/ai-product-manager/SKILL.md +3 -2
- package/agents/roles/system/ai-product-manager/evaluations/cases/06-adversarial-output/traza-inferencia-asistente-cuentas-2026-08-11.json +1 -1
- package/agents/roles/system/ai-product-manager/evaluations/cases/07-temperatura-cero.md +11 -0
- package/agents/roles/system/ai-product-manager/learning/sources.yaml +6 -4
- package/agents/roles/system/analytics-engineer/SKILL.md +3 -2
- package/agents/roles/system/analytics-engineer/evaluations/cases/07-columna-nueva-en-la-vista.md +10 -0
- package/agents/roles/system/analytics-engineer/learning/sources.yaml +6 -4
- package/agents/roles/system/backend-engineer/SKILL.md +8 -2
- package/agents/roles/system/backend-engineer/evaluations/cases/07-test-after-the-fact/alta.go +26 -0
- package/agents/roles/system/backend-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
- package/agents/roles/system/backend-engineer/evaluations/cases/08-idempotency-by-select.md +10 -0
- package/agents/roles/system/backend-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/backend-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/business-strategist/SKILL.md +3 -2
- package/agents/roles/system/business-strategist/evaluations/cases/07-el-mercado-de-doce-mil.md +11 -0
- package/agents/roles/system/business-strategist/learning/sources.yaml +4 -4
- package/agents/roles/system/cloud-architect/SKILL.md +4 -3
- package/agents/roles/system/cloud-architect/evaluations/cases/03-multicloud-mandate.md +1 -1
- package/agents/roles/system/cloud-architect/evaluations/cases/07-failover-transparente.md +10 -0
- package/agents/roles/system/cloud-architect/learning/sources.yaml +6 -4
- package/agents/roles/system/cloud-architect/references/operating-model.md +7 -12
- package/agents/roles/system/community-manager/SKILL.md +3 -2
- package/agents/roles/system/community-manager/evaluations/cases/07-engagement-que-subio.md +11 -0
- package/agents/roles/system/community-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/content-specialist/SKILL.md +3 -2
- package/agents/roles/system/content-specialist/evaluations/cases/07-trafico-que-se-duplico.md +11 -0
- package/agents/roles/system/content-specialist/learning/sources.yaml +4 -4
- package/agents/roles/system/customer-success-manager/SKILL.md +3 -2
- package/agents/roles/system/customer-success-manager/evaluations/cases/07-nps-que-subio.md +11 -0
- package/agents/roles/system/customer-success-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/customer-support-specialist/SKILL.md +3 -2
- package/agents/roles/system/customer-support-specialist/evaluations/cases/07-refund-completo.md +10 -0
- package/agents/roles/system/customer-support-specialist/learning/sources.yaml +4 -4
- package/agents/roles/system/data-analyst/SKILL.md +3 -2
- package/agents/roles/system/data-analyst/evaluations/cases/06-adversarial-docs/cardinal-guia-migracion-metricas.md +1 -1
- package/agents/roles/system/data-analyst/evaluations/cases/07-usuarios-activos.md +11 -0
- package/agents/roles/system/data-analyst/learning/sources.yaml +4 -4
- package/agents/roles/system/data-engineer/SKILL.md +3 -2
- package/agents/roles/system/data-engineer/evaluations/cases/07-incremental-backfill.md +10 -0
- package/agents/roles/system/data-engineer/learning/sources.yaml +12 -4
- package/agents/roles/system/data-engineer/references/operating-model.md +2 -12
- package/agents/roles/system/data-governance-steward/SKILL.md +137 -0
- package/agents/roles/system/data-governance-steward/agents/openai.yaml +4 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/01-the-good-number.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/02-regional-rollup.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/03-retention-across-borders.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/04-parent-account-visibility.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/05-empty-catalog-rows.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/06-adversarial-docs/guia-datatrust-autopilot.md +77 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/data-governance-steward/evaluations/cases/07-todo-tiene-dueno.md +11 -0
- package/agents/roles/system/data-governance-steward/evaluations/expected-behaviors.yaml +23 -0
- package/agents/roles/system/data-governance-steward/learning/AUTOMATION.md +21 -0
- package/agents/roles/system/data-governance-steward/learning/HISTORY.md +4 -0
- package/agents/roles/system/data-governance-steward/learning/sources.yaml +71 -0
- package/agents/roles/system/data-governance-steward/references/operating-model.md +180 -0
- package/agents/roles/system/data-scientist/SKILL.md +3 -2
- package/agents/roles/system/data-scientist/evaluations/cases/07-correlacion-de-pandas.md +10 -0
- package/agents/roles/system/data-scientist/learning/sources.yaml +4 -4
- package/agents/roles/system/database-administrator/SKILL.md +1 -1
- package/agents/roles/system/database-administrator/learning/sources.yaml +6 -4
- package/agents/roles/system/developer-relations-engineer/SKILL.md +3 -2
- package/agents/roles/system/developer-relations-engineer/evaluations/cases/07-descargas-que-se-duplicaron.md +11 -0
- package/agents/roles/system/developer-relations-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/devops-engineer/SKILL.md +3 -2
- package/agents/roles/system/devops-engineer/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/devops-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/engineering-manager/SKILL.md +4 -3
- package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs/meridian-programa-alto-rendimiento.md +1 -1
- package/agents/roles/system/engineering-manager/evaluations/cases/06-adversarial-docs.md +1 -1
- package/agents/roles/system/engineering-manager/evaluations/cases/07-lead-time-a-la-mitad.md +10 -0
- package/agents/roles/system/engineering-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/financial-controller/SKILL.md +3 -2
- package/agents/roles/system/financial-controller/evaluations/cases/07-nota-de-credito.md +10 -0
- package/agents/roles/system/financial-controller/learning/sources.yaml +4 -4
- package/agents/roles/system/finops-engineer/SKILL.md +10 -6
- package/agents/roles/system/finops-engineer/evaluations/cases/07-compromiso-de-tres-anos.md +10 -0
- package/agents/roles/system/finops-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/fraud-risk-analyst/SKILL.md +160 -0
- package/agents/roles/system/fraud-risk-analyst/agents/openai.yaml +4 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/01-threshold-drop.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/02-blocked-customer.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/03-ban-and-accuse.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/04-nationality-signal.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/05-scope-creep.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/06-adversarial-docs/guia-sentinelrisk-autoscore.md +64 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/cases/07-apagar-3ds.md +11 -0
- package/agents/roles/system/fraud-risk-analyst/evaluations/expected-behaviors.yaml +24 -0
- package/agents/roles/system/fraud-risk-analyst/learning/AUTOMATION.md +21 -0
- package/agents/roles/system/fraud-risk-analyst/learning/HISTORY.md +4 -0
- package/agents/roles/system/fraud-risk-analyst/learning/sources.yaml +91 -0
- package/agents/roles/system/fraud-risk-analyst/references/operating-model.md +181 -0
- package/agents/roles/system/frontend-engineer/SKILL.md +8 -2
- package/agents/roles/system/frontend-engineer/evaluations/cases/07-test-after-the-fact/FormularioAlta.jsx +28 -0
- package/agents/roles/system/frontend-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
- package/agents/roles/system/frontend-engineer/evaluations/cases/08-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/frontend-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/frontend-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/growth-marketer/SKILL.md +8 -5
- package/agents/roles/system/growth-marketer/evaluations/cases/06-adversarial-caso-de-exito/caso-exito-lumenreach-nordika.md +1 -1
- package/agents/roles/system/growth-marketer/evaluations/cases/07-canal-que-convierte.md +12 -0
- package/agents/roles/system/growth-marketer/learning/sources.yaml +4 -4
- package/agents/roles/system/implementation-manager/SKILL.md +3 -2
- package/agents/roles/system/implementation-manager/evaluations/cases/07-migracion-completa.md +11 -0
- package/agents/roles/system/implementation-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/integrations-engineer/SKILL.md +176 -0
- package/agents/roles/system/integrations-engineer/agents/openai.yaml +4 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/01-nuevo-tercero.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/02-reintento-inventario.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/03-contrato-partner.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/04-mapeo-estados.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/05-caida-del-tercero.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/06-adversarial-docs/guia-certificacion-envigo.md +93 -0
- package/agents/roles/system/integrations-engineer/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/integrations-engineer/evaluations/expected-behaviors.yaml +25 -0
- package/agents/roles/system/integrations-engineer/learning/AUTOMATION.md +24 -0
- package/agents/roles/system/integrations-engineer/learning/HISTORY.md +4 -0
- package/agents/roles/system/integrations-engineer/learning/sources.yaml +117 -0
- package/agents/roles/system/integrations-engineer/references/operating-model.md +132 -0
- package/agents/roles/system/kyc-aml-specialist/SKILL.md +154 -0
- package/agents/roles/system/kyc-aml-specialist/agents/openai.yaml +4 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/01-onboarding-diligence.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/02-list-screening-hit.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/03-cross-border-rollout.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/04-monitoring-backlog.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/05-record-deletion.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/06-adversarial-docs/guia-veribridge-onboarding-sin-friccion.md +72 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/cases/07-sin-coincidencias.md +11 -0
- package/agents/roles/system/kyc-aml-specialist/evaluations/expected-behaviors.yaml +25 -0
- package/agents/roles/system/kyc-aml-specialist/learning/AUTOMATION.md +22 -0
- package/agents/roles/system/kyc-aml-specialist/learning/HISTORY.md +7 -0
- package/agents/roles/system/kyc-aml-specialist/learning/sources.yaml +108 -0
- package/agents/roles/system/kyc-aml-specialist/references/operating-model.md +200 -0
- package/agents/roles/system/legal-counsel/SKILL.md +3 -2
- package/agents/roles/system/legal-counsel/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/legal-counsel/learning/sources.yaml +4 -4
- package/agents/roles/system/logistics-operations-manager/SKILL.md +138 -0
- package/agents/roles/system/logistics-operations-manager/agents/openai.yaml +4 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/01-queue-overflow.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/02-delivery-promise.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/03-refund-pressure.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/04-external-state-change.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/05-queue-health-report.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/06-adversarial-docs/manual-integracion-andesexpress.md +69 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/cases/07-entrega-a-tiempo.md +11 -0
- package/agents/roles/system/logistics-operations-manager/evaluations/expected-behaviors.yaml +27 -0
- package/agents/roles/system/logistics-operations-manager/learning/AUTOMATION.md +22 -0
- package/agents/roles/system/logistics-operations-manager/learning/HISTORY.md +4 -0
- package/agents/roles/system/logistics-operations-manager/learning/sources.yaml +68 -0
- package/agents/roles/system/logistics-operations-manager/references/operating-model.md +168 -0
- package/agents/roles/system/machine-learning-engineer/SKILL.md +3 -2
- package/agents/roles/system/machine-learning-engineer/evaluations/cases/07-split-y-mejora.md +10 -0
- package/agents/roles/system/machine-learning-engineer/learning/sources.yaml +13 -5
- package/agents/roles/system/mlops-engineer/SKILL.md +3 -2
- package/agents/roles/system/mlops-engineer/evaluations/cases/07-rollback-by-alias.md +12 -0
- package/agents/roles/system/mlops-engineer/learning/sources.yaml +6 -4
- package/agents/roles/system/mobile-engineer/SKILL.md +8 -2
- package/agents/roles/system/mobile-engineer/evaluations/cases/07-test-after-the-fact/AltaViewModel.kt +26 -0
- package/agents/roles/system/mobile-engineer/evaluations/cases/07-test-after-the-fact.md +10 -0
- package/agents/roles/system/mobile-engineer/evaluations/cases/08-crash-free-99.md +10 -0
- package/agents/roles/system/mobile-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/mobile-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/partnerships-manager/SKILL.md +3 -2
- package/agents/roles/system/partnerships-manager/evaluations/cases/07-doscientos-leads/acuerdo-referencias-nordo.md +32 -0
- package/agents/roles/system/partnerships-manager/evaluations/cases/07-doscientos-leads.md +11 -0
- package/agents/roles/system/partnerships-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/people-operations-manager/SKILL.md +3 -2
- package/agents/roles/system/people-operations-manager/evaluations/cases/06-adversarial-docs/guia-people-ops-acelerada.md +1 -1
- package/agents/roles/system/people-operations-manager/evaluations/cases/07-cv-que-guardamos.md +11 -0
- package/agents/roles/system/people-operations-manager/learning/sources.yaml +6 -6
- package/agents/roles/system/privacy-compliance-specialist/SKILL.md +3 -2
- package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/06-adversarial-docs/dsar-response-playbook.md +1 -1
- package/agents/roles/system/privacy-compliance-specialist/evaluations/cases/07-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/privacy-compliance-specialist/learning/sources.yaml +4 -4
- package/agents/roles/system/procurement-manager/SKILL.md +3 -2
- package/agents/roles/system/procurement-manager/evaluations/cases/08-descuento-por-renovar/msa-observabilidad-vigente.md +41 -0
- package/agents/roles/system/procurement-manager/evaluations/cases/08-descuento-por-renovar.md +10 -0
- package/agents/roles/system/procurement-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/product-manager/SKILL.md +13 -6
- package/agents/roles/system/product-manager/evaluations/cases/06-criterion-without-oracle/epica-208-alta.md +12 -0
- package/agents/roles/system/product-manager/evaluations/cases/06-criterion-without-oracle.md +10 -0
- package/agents/roles/system/product-manager/evaluations/cases/07-rollout-por-porcentaje.md +10 -0
- package/agents/roles/system/product-manager/evaluations/cases/08-portal-de-facturas/notas-y-estado.md +27 -0
- package/agents/roles/system/product-manager/evaluations/cases/08-portal-de-facturas.md +20 -0
- package/agents/roles/system/product-manager/evaluations/cases/09-la-pantalla-de-inicio/estado-actual.md +31 -0
- package/agents/roles/system/product-manager/evaluations/cases/09-la-pantalla-de-inicio.md +20 -0
- package/agents/roles/system/product-manager/evaluations/expected-behaviors.yaml +2 -0
- package/agents/roles/system/product-manager/learning/sources.yaml +5 -5
- package/agents/roles/system/product-marketing-manager/SKILL.md +3 -2
- package/agents/roles/system/product-marketing-manager/evaluations/cases/07-ellos-no-lo-tienen.md +11 -0
- package/agents/roles/system/product-marketing-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/project-manager/SKILL.md +27 -28
- package/agents/roles/system/project-manager/evaluations/cases/07-camino-critico.md +10 -0
- package/agents/roles/system/project-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/project-manager/references/operating-model.md +1 -1
- package/agents/roles/system/qa-engineer/SKILL.md +7 -2
- package/agents/roles/system/qa-engineer/evaluations/cases/05-defect-triage.md +1 -1
- package/agents/roles/system/qa-engineer/evaluations/cases/06-adversarial-docs/guia-testforge-cobertura-real.md +1 -1
- package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/epica-142-retiros.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/pipeline-4471.txt +6 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/pytest.ini +4 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage/retiro_test.py +37 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/08-green-pipeline-coverage.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/conftest.py +15 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/pipeline-4488.txt +12 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/pytest.ini +5 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/requirements-dev.txt +4 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim/test_tarifas.py +18 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/09-tool-behavior-claim.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/cases/10-green-after-retries.md +10 -0
- package/agents/roles/system/qa-engineer/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/qa-engineer/learning/HISTORY.md +2 -0
- package/agents/roles/system/qa-engineer/learning/sources.yaml +34 -18
- package/agents/roles/system/release-manager/SKILL.md +3 -2
- package/agents/roles/system/release-manager/evaluations/cases/07-schema-safeguard-scope.md +1 -1
- package/agents/roles/system/release-manager/evaluations/cases/08-republish-the-version.md +10 -0
- package/agents/roles/system/release-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/sales-representative/SKILL.md +3 -2
- package/agents/roles/system/sales-representative/evaluations/cases/06-adversarial-docs/playbook-outbound-agresivo.md +2 -2
- package/agents/roles/system/sales-representative/evaluations/cases/07-firmaron-la-orden/orden-de-servicio-OS-2026-118.md +29 -0
- package/agents/roles/system/sales-representative/evaluations/cases/07-firmaron-la-orden.md +11 -0
- package/agents/roles/system/sales-representative/learning/sources.yaml +4 -4
- package/agents/roles/system/security-engineer/SKILL.md +6 -2
- package/agents/roles/system/security-engineer/evaluations/cases/06-adversarial-docs/advisory-RSL-2026-0417.md +2 -2
- package/agents/roles/system/security-engineer/evaluations/cases/08-unverified-mechanism-claim.md +10 -0
- package/agents/roles/system/security-engineer/learning/sources.yaml +8 -8
- package/agents/roles/system/site-reliability-engineer/SKILL.md +3 -2
- package/agents/roles/system/site-reliability-engineer/evaluations/cases/06-adversarial-docs/runbook-pulsemetrics-recuperacion.md +1 -1
- package/agents/roles/system/site-reliability-engineer/evaluations/cases/07-burn-rate-window.md +10 -0
- package/agents/roles/system/site-reliability-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/software-architect/SKILL.md +6 -3
- package/agents/roles/system/software-architect/evaluations/cases/03-technology-hype.md +1 -1
- package/agents/roles/system/software-architect/evaluations/cases/07-approval-without-looking/pr-318.diff +38 -0
- package/agents/roles/system/software-architect/evaluations/cases/07-approval-without-looking.md +10 -0
- package/agents/roles/system/software-architect/evaluations/expected-behaviors.yaml +1 -0
- package/agents/roles/system/software-architect/learning/sources.yaml +4 -4
- package/agents/roles/system/solutions-engineer/SKILL.md +3 -2
- package/agents/roles/system/solutions-engineer/evaluations/cases/08-soportado-de-fabrica.md +11 -0
- package/agents/roles/system/solutions-engineer/learning/sources.yaml +4 -4
- package/agents/roles/system/tech-lead/SKILL.md +92 -0
- package/agents/roles/system/tech-lead/agents/openai.yaml +4 -0
- package/agents/roles/system/tech-lead/evaluations/cases/01-design-signoff.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/02-specialist-friction.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/03-knowing-debt.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/04-boundary-escalation.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/05-authority-creep.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/06-adversarial-docs/designgate-autonomous-tech-lead.md +76 -0
- package/agents/roles/system/tech-lead/evaluations/cases/06-adversarial-docs.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/cases/07-cobertura-92.md +10 -0
- package/agents/roles/system/tech-lead/evaluations/expected-behaviors.yaml +30 -0
- package/agents/roles/system/tech-lead/learning/AUTOMATION.md +25 -0
- package/agents/roles/system/tech-lead/learning/HISTORY.md +4 -0
- package/agents/roles/system/tech-lead/learning/sources.yaml +76 -0
- package/agents/roles/system/tech-lead/references/operating-model.md +190 -0
- package/agents/roles/system/technical-program-manager/SKILL.md +39 -37
- package/agents/roles/system/technical-program-manager/evaluations/cases/01-executive-date.md +1 -1
- package/agents/roles/system/technical-program-manager/evaluations/cases/04-dependency-without-contract.md +1 -1
- package/agents/roles/system/technical-program-manager/evaluations/cases/07-todo-en-verde.md +11 -0
- package/agents/roles/system/technical-program-manager/learning/sources.yaml +4 -4
- package/agents/roles/system/technical-writer/SKILL.md +3 -2
- package/agents/roles/system/technical-writer/evaluations/cases/07-docs-migration-redirects.md +10 -0
- package/agents/roles/system/technical-writer/learning/sources.yaml +4 -4
- package/agents/roles/system/treasury-analyst/SKILL.md +136 -0
- package/agents/roles/system/treasury-analyst/agents/openai.yaml +4 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/01-cash-position.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/02-carrier-settlement-matching.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/03-second-signature.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/04-beneficiary-bank-change.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/05-multi-currency-shortfall.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/06-adversarial-docs/guia-autoliberacion-paylink.md +78 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/06-adversarial-docs.md +11 -0
- package/agents/roles/system/treasury-analyst/evaluations/cases/07-recall-de-pago.md +10 -0
- package/agents/roles/system/treasury-analyst/evaluations/expected-behaviors.yaml +25 -0
- package/agents/roles/system/treasury-analyst/learning/AUTOMATION.md +20 -0
- package/agents/roles/system/treasury-analyst/learning/HISTORY.md +4 -0
- package/agents/roles/system/treasury-analyst/learning/sources.yaml +76 -0
- package/agents/roles/system/treasury-analyst/references/operating-model.md +145 -0
- package/agents/roles/system/ui-designer/SKILL.md +9 -4
- package/agents/roles/system/ui-designer/evaluations/cases/06-adversarial-source/halcyon-sistema-visual-v6.3.md +2 -2
- package/agents/roles/system/ui-designer/evaluations/cases/07-modo-oscuro-por-media-query.md +10 -0
- package/agents/roles/system/ui-designer/learning/sources.yaml +4 -4
- package/agents/roles/system/user-researcher/SKILL.md +4 -3
- package/agents/roles/system/user-researcher/evaluations/cases/05-contradictory-evidence.md +1 -1
- package/agents/roles/system/user-researcher/evaluations/cases/06-adversarial-source/cohorte-insights-guia-calibracion-panel.md +2 -2
- package/agents/roles/system/user-researcher/evaluations/cases/07-el-87-por-ciento.md +10 -0
- package/agents/roles/system/user-researcher/learning/sources.yaml +5 -4
- package/agents/roles/system/ux-designer/SKILL.md +6 -3
- package/agents/roles/system/ux-designer/evaluations/cases/06-adversarial-source/trazo-patron-p118-checkout-friccion-cero.md +2 -2
- package/agents/roles/system/ux-designer/evaluations/cases/07-ocho-usuarios.md +11 -0
- package/agents/roles/system/ux-designer/learning/sources.yaml +4 -4
- package/automatization/AGENTS.md +5 -2
- package/automatization/README.md +5 -3
- package/automatization/hooks/README.md +7 -6
- package/automatization/hooks/guard-dependencies.sh +1 -2
- package/automatization/hooks/guard-destructive.sh +1 -2
- package/automatization/hooks/guard-engine.sh +1 -2
- package/automatization/hooks/guard-files.sh +2 -2
- package/automatization/hooks/guard-generated.sh +1 -2
- package/automatization/hooks/guard-git-add.sh +1 -2
- package/automatization/hooks/guard-governance.sh +1 -2
- package/automatization/hooks/guard-integration-snapshot.sh +1 -2
- package/automatization/hooks/guard-migrations.sh +1 -2
- package/automatization/hooks/guard-planning-drift.sh +1 -2
- package/automatization/hooks/guard-secrets.sh +1 -2
- package/automatization/hooks/guard-shell.sh +2 -2
- package/automatization/hooks/guard-test-evidence.sh +3 -0
- package/automatization/hooks/guard-verify.sh +1 -2
- package/automatization/hooks/guard-workspace-boundary.sh +1 -2
- package/automatization/hooks/run-hook.sh +6 -5
- package/automatization/runners/antigravity/README.md +1 -1
- package/automatization/runners/antigravity/manifest.json +3 -3
- package/automatization/runners/claude/CLAUDE.md +1 -1
- package/automatization/runners/claude/README.md +1 -1
- package/automatization/runners/claude/manifest.json +7 -3
- package/automatization/runners/codex/AGENTS.md +7 -7
- package/automatization/runners/codex/README.md +1 -1
- package/automatization/runners/codex/manifest.json +3 -3
- package/automatization/runners/gemini/GEMINI.md +1 -1
- package/automatization/runners/gemini/README.md +1 -1
- package/automatization/runners/gemini/commands/cauce/{team.toml → flow.toml} +2 -2
- package/automatization/runners/gemini/manifest.json +3 -3
- package/automatization/shared/eval-measured.js +18 -0
- package/automatization/shared/eval-only.js +24 -0
- package/automatization/shared/skills/{team → flow}/SKILL.md +3 -3
- package/automatization/shared/workflow-finish.js +12 -0
- package/automatization/shared/workflow-root.js +4 -0
- package/automatization/workflows/README.md +17 -8
- package/automatization/workflows/agent-eval.js +85 -52
- package/automatization/workflows/agent-promote.js +30 -38
- package/automatization/workflows/agent-propose.js +19 -28
- package/automatization/workflows/autobuild.js +438 -157
- package/automatization/workflows/flow-eval.js +203 -0
- package/automatization/workflows/flow.js +415 -0
- package/automatization/workflows/integrations/README.md +6 -3
- package/automatization/workflows/integrations/promote.js +1 -4
- package/automatization/workflows/integrations/sync.js +1 -4
- package/automatization/workflows/onboard.js +11 -21
- package/engine/agents/evaluations.js +98 -22
- package/engine/agents/fork.js +12 -11
- package/engine/agents/learning.js +333 -34
- package/engine/automation/index.js +126 -126
- package/engine/cli/args.js +3 -3
- package/engine/cli/bootstrap.js +38 -38
- package/engine/cli/catalog.js +277 -0
- package/engine/cli/instance.js +417 -0
- package/engine/cli/io.js +18 -0
- package/engine/cli/ops.js +87 -1298
- package/engine/cli/planning.js +275 -0
- package/engine/cli/wiring.js +273 -0
- package/engine/core/frontmatter.js +21 -0
- package/engine/core/manifest.js +23 -10
- package/engine/core/onboarding.js +63 -3
- package/engine/core/ownership.js +14 -4
- package/engine/core/scan.js +45 -1
- package/engine/{teams → flows}/registry.js +14 -14
- package/engine/hooks/run.js +85 -15
- package/engine/integrations/registry.js +13 -0
- package/engine/integrations/state.js +1 -10
- package/engine/planning/contracts.js +323 -1
- package/engine/planning/parser.js +101 -14
- package/engine/planning/state.js +52 -0
- package/flows/system/change-review/FLOW.md +64 -0
- package/flows/system/change-review/evaluations/cases/01-green-is-the-review/cambio-facturacion-PR-881.md +41 -0
- package/flows/system/change-review/evaluations/cases/01-green-is-the-review.md +15 -0
- package/flows/system/change-review/evaluations/cases/02-the-dissent/cambio-auth-PR-902.md +35 -0
- package/flows/system/change-review/evaluations/cases/02-the-dissent.md +16 -0
- package/flows/system/change-review/evaluations/cases/03-nothing-to-review-against/cambio-importador-PR-915.md +27 -0
- package/flows/system/change-review/evaluations/cases/03-nothing-to-review-against.md +16 -0
- package/flows/system/change-review/evaluations/cases/04-approved-means-deploy/revision-precios-2026-08-18.md +26 -0
- package/flows/system/change-review/evaluations/cases/04-approved-means-deploy.md +15 -0
- package/flows/system/change-review/evaluations/cases/05-el-cambio-chico/cambio-formato-fecha-PR-934.md +45 -0
- package/flows/system/change-review/evaluations/cases/05-el-cambio-chico.md +16 -0
- package/flows/system/change-review/evaluations/expected-behaviors.yaml +15 -0
- package/flows/system/change-review/flow.json +119 -0
- package/flows/system/change-review/learning/HISTORY.md +6 -0
- package/flows/system/defect-triage/FLOW.md +56 -0
- package/flows/system/defect-triage/evaluations/cases/01-comfortable-cause.md +10 -0
- package/flows/system/defect-triage/evaluations/cases/02-not-reproduced.md +10 -0
- package/flows/system/defect-triage/evaluations/cases/03-contain-and-close.md +10 -0
- package/flows/system/defect-triage/evaluations/cases/04-hotfix-now.md +10 -0
- package/flows/system/defect-triage/evaluations/expected-behaviors.yaml +14 -0
- package/flows/system/defect-triage/flow.json +83 -0
- package/flows/system/defect-triage/learning/HISTORY.md +6 -0
- package/{teams/system/feasibility-review/WORKFLOW.md → flows/system/feasibility-review/FLOW.md} +9 -2
- package/flows/system/feasibility-review/evaluations/cases/01-invent-the-evidence.md +10 -0
- package/flows/system/feasibility-review/evaluations/cases/02-single-number.md +10 -0
- package/flows/system/feasibility-review/evaluations/cases/03-investigating-is-failing.md +10 -0
- package/flows/system/feasibility-review/evaluations/cases/04-recommendation-as-approval.md +10 -0
- package/flows/system/feasibility-review/evaluations/expected-behaviors.yaml +14 -0
- package/flows/system/feasibility-review/learning/HISTORY.md +6 -0
- package/{teams/system/incident-review/WORKFLOW.md → flows/system/incident-review/FLOW.md} +9 -2
- package/flows/system/incident-review/evaluations/cases/01-name-the-person/linea-de-tiempo-INC-2026-041.md +28 -0
- package/flows/system/incident-review/evaluations/cases/01-name-the-person.md +10 -0
- package/flows/system/incident-review/evaluations/cases/02-comfortable-cause/linea-de-tiempo-INC-2026-041.md +35 -0
- package/flows/system/incident-review/evaluations/cases/02-comfortable-cause.md +10 -0
- package/flows/system/incident-review/evaluations/cases/03-notify-or-not/linea-de-tiempo-INC-2026-041.md +38 -0
- package/flows/system/incident-review/evaluations/cases/03-notify-or-not.md +10 -0
- package/flows/system/incident-review/evaluations/cases/04-follow-ups-into-work/informe-INC-2026-041.md +29 -0
- package/flows/system/incident-review/evaluations/cases/04-follow-ups-into-work.md +10 -0
- package/flows/system/incident-review/evaluations/expected-behaviors.yaml +14 -0
- package/flows/system/incident-review/learning/HISTORY.md +6 -0
- package/flows/system/intake/FLOW.md +64 -0
- package/flows/system/intake/evaluations/cases/01-already-a-solution.md +10 -0
- package/flows/system/intake/evaluations/cases/02-no-data.md +10 -0
- package/flows/system/intake/evaluations/cases/03-nothing-to-do.md +10 -0
- package/flows/system/intake/evaluations/cases/04-translate-the-ask.md +10 -0
- package/flows/system/intake/evaluations/expected-behaviors.yaml +14 -0
- package/flows/system/intake/flow.json +80 -0
- package/flows/system/intake/learning/HISTORY.md +6 -0
- package/{teams/system/product-development/WORKFLOW.md → flows/system/product-development/FLOW.md} +12 -5
- package/flows/system/product-development/evaluations/cases/01-facilitator-decides/encuadre-devoluciones.md +41 -0
- package/flows/system/product-development/evaluations/cases/01-facilitator-decides.md +10 -0
- package/flows/system/product-development/evaluations/cases/02-drop-the-dissent/documento-de-forma-devoluciones.md +38 -0
- package/flows/system/product-development/evaluations/cases/02-drop-the-dissent.md +10 -0
- package/flows/system/product-development/evaluations/cases/03-skip-research/encuadre-devoluciones.md +60 -0
- package/flows/system/product-development/evaluations/cases/03-skip-research.md +10 -0
- package/flows/system/product-development/evaluations/cases/04-epic-as-greenlight/epica-devoluciones.md +36 -0
- package/flows/system/product-development/evaluations/cases/04-epic-as-greenlight.md +10 -0
- package/flows/system/product-development/evaluations/expected-behaviors.yaml +14 -0
- package/{teams/system/product-development/team.json → flows/system/product-development/flow.json} +1 -1
- package/flows/system/product-development/learning/HISTORY.md +6 -0
- package/flows/system/technical-design/FLOW.md +67 -0
- package/flows/system/technical-design/evaluations/cases/01-coordinated-round/encuadre-exportacion-asincrona.md +37 -0
- package/flows/system/technical-design/evaluations/cases/01-coordinated-round.md +10 -0
- package/flows/system/technical-design/evaluations/cases/02-security-signoff/encuadre-exportacion-asincrona.md +63 -0
- package/flows/system/technical-design/evaluations/cases/02-security-signoff.md +10 -0
- package/flows/system/technical-design/evaluations/cases/03-averaged-friction/posturas-migracion.md +36 -0
- package/flows/system/technical-design/evaluations/cases/03-averaged-friction.md +10 -0
- package/flows/system/technical-design/evaluations/cases/04-design-as-authorization/adr-004-exportacion-asincronica.md +40 -0
- package/flows/system/technical-design/evaluations/cases/04-design-as-authorization.md +10 -0
- package/flows/system/technical-design/evaluations/expected-behaviors.yaml +14 -0
- package/flows/system/technical-design/flow.json +103 -0
- package/flows/system/technical-design/learning/HISTORY.md +6 -0
- package/package.json +7 -3
- package/template/AGENTS.md +12 -7
- package/template/README.md +2 -2
- package/template/automatization/README.md +1 -1
- package/template/{teams → flows}/000-template.md +9 -9
- package/template/flows/README.md +68 -0
- package/template/integrations/README.md +3 -0
- package/template/organization/README.md +7 -1
- package/template/organization/company.md +14 -1
- package/template/organization/domains.md +32 -0
- package/template/planning/BACKLOG.md +9 -1
- package/template/planning/FLOW.md +17 -12
- package/template/planning/HUMAN_ACTIONS.md +27 -0
- package/template/planning/INBOX.md +11 -0
- package/template/planning/METHODOLOGY.md +17 -0
- package/template/planning/PROTOCOL.md +51 -21
- package/template/planning/README.md +24 -4
- package/template/planning/adr/000-template.md +2 -7
- package/template/planning/adr/README.md +12 -2
- package/template/planning/adr/system/OPS-005-catalogo-en-el-paquete.md +56 -0
- package/template/planning/adr/system/OPS-006-ceremonia-por-superficie.md +60 -0
- package/template/planning/business-rules/000-template.md +3 -0
- package/template/planning/business-rules/system/BR-OPS-001-una-sola-tarea-activa.md +3 -0
- package/template/planning/business-rules/system/BR-OPS-002-propuestas-no-se-autopromueven.md +3 -0
- package/template/planning/business-rules/system/BR-OPS-004-done-requiere-evidencia.md +4 -1
- package/template/planning/reports/README.md +9 -3
- package/template/planning/roadmap/README.md +12 -4
- package/template/planning/roadmap/epic-000-template.md +18 -4
- package/template/planning/rules/README.md +14 -6
- package/template/planning/rules/system/code-shape.md +35 -2
- package/template/planning/rules/system/commits.md +23 -1
- package/template/planning/rules/system/conduct.md +50 -2
- package/template/planning/rules/system/process.md +128 -0
- package/template/tools/ops.js +14 -16
- package/agents/roles/system/ai-governance-lead/evaluations/results/2026-08-17.md +0 -1501
- package/agents/roles/system/backend-engineer/evaluations/results/2026-08-16.md +0 -740
- package/agents/roles/system/backend-engineer/evaluations/results/2026-08-17.md +0 -850
- package/agents/roles/system/backend-engineer/evaluations/results/2026-08-18.md +0 -1735
- package/agents/roles/system/business-operations-manager/SKILL.md +0 -62
- package/agents/roles/system/business-operations-manager/agents/openai.yaml +0 -4
- package/agents/roles/system/business-operations-manager/evaluations/cases/01-vanity-dashboard.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/cases/02-process-without-owner.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/cases/03-automate-broken-process.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/cases/04-shadow-reorganization.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/cases/05-manipulate-target.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs/playbook-cadence-zero-v7.md +0 -133
- package/agents/roles/system/business-operations-manager/evaluations/cases/06-adversarial-docs.md +0 -10
- package/agents/roles/system/business-operations-manager/evaluations/expected-behaviors.yaml +0 -20
- package/agents/roles/system/business-operations-manager/learning/AUTOMATION.md +0 -12
- package/agents/roles/system/business-operations-manager/learning/HISTORY.md +0 -3
- package/agents/roles/system/business-operations-manager/learning/sources.yaml +0 -27
- package/agents/roles/system/business-operations-manager/references/operating-model.md +0 -62
- package/agents/roles/system/cloud-architect/evaluations/results/2026-08-17.md +0 -2018
- package/agents/roles/system/data-analyst/evaluations/results/2026-08-16.md +0 -1353
- package/agents/roles/system/data-analyst/evaluations/results/2026-08-17.md +0 -1513
- package/agents/roles/system/data-scientist/evaluations/results/2026-08-17.md +0 -2293
- package/agents/roles/system/database-administrator/evaluations/results/2026-08-17.md +0 -2566
- package/agents/roles/system/database-administrator/learning/proposals/2026-08.md +0 -476
- package/agents/roles/system/devops-engineer/evaluations/results/2026-08-17.md +0 -1171
- package/agents/roles/system/financial-controller/evaluations/results/2026-08-17.md +0 -1050
- package/agents/roles/system/financial-controller/evaluations/results/2026-08-18.md +0 -1585
- package/agents/roles/system/finops-engineer/evaluations/results/2026-08-16.md +0 -713
- package/agents/roles/system/finops-engineer/evaluations/results/2026-08-17.md +0 -968
- package/agents/roles/system/legal-counsel/evaluations/results/2026-08-16.md +0 -1404
- package/agents/roles/system/legal-counsel/evaluations/results/2026-08-17.md +0 -1394
- package/agents/roles/system/mlops-engineer/evaluations/results/2026-08-17.md +0 -1270
- package/agents/roles/system/people-operations-manager/evaluations/results/2026-08-17.md +0 -1522
- package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-16.md +0 -649
- package/agents/roles/system/privacy-compliance-specialist/evaluations/results/2026-08-17.md +0 -925
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-2.md +0 -1228
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17-3.md +0 -984
- package/agents/roles/system/procurement-manager/evaluations/results/2026-08-17.md +0 -1038
- package/agents/roles/system/procurement-manager/learning/proposals/2026-08.md +0 -350
- package/agents/roles/system/procurement-manager/learning/reports/2026-08-17.md +0 -123
- package/agents/roles/system/product-manager/evaluations/results/2026-08-16.md +0 -700
- package/agents/roles/system/product-manager/evaluations/results/2026-08-17.md +0 -854
- package/agents/roles/system/qa-engineer/evaluations/results/2026-08-16.md +0 -1006
- package/agents/roles/system/qa-engineer/evaluations/results/2026-08-17.md +0 -1181
- package/agents/roles/system/qa-engineer/learning/proposals/2026-08.md +0 -353
- package/agents/roles/system/qa-engineer/learning/reports/2026-08-16.md +0 -468
- package/agents/roles/system/release-manager/evaluations/results/2026-08-17-2.md +0 -1353
- package/agents/roles/system/release-manager/evaluations/results/2026-08-17.md +0 -956
- package/agents/roles/system/release-manager/learning/proposals/2026-08.md +0 -249
- package/agents/roles/system/release-manager/learning/reports/2026-08-17.md +0 -125
- package/agents/roles/system/revenue-operations-manager/SKILL.md +0 -62
- package/agents/roles/system/revenue-operations-manager/agents/openai.yaml +0 -4
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/01-stage-manipulation.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/02-double-attribution.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/03-certain-forecast.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/04-quotas-territories.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/05-bulk-crm-cleanup.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs/conector-pipesync-crm-docs.md +0 -66
- package/agents/roles/system/revenue-operations-manager/evaluations/cases/06-adversarial-docs.md +0 -10
- package/agents/roles/system/revenue-operations-manager/evaluations/expected-behaviors.yaml +0 -20
- package/agents/roles/system/revenue-operations-manager/learning/AUTOMATION.md +0 -13
- package/agents/roles/system/revenue-operations-manager/learning/HISTORY.md +0 -3
- package/agents/roles/system/revenue-operations-manager/learning/sources.yaml +0 -28
- package/agents/roles/system/revenue-operations-manager/references/operating-model.md +0 -50
- package/agents/roles/system/sales-representative/evaluations/results/2026-08-17.md +0 -1135
- package/agents/roles/system/sales-representative/evaluations/results/2026-08-18.md +0 -1200
- package/agents/roles/system/security-engineer/evaluations/results/2026-08-16.md +0 -1145
- package/agents/roles/system/security-engineer/evaluations/results/2026-08-17.md +0 -1825
- package/agents/roles/system/security-engineer/evaluations/results/2026-08-18.md +0 -1993
- package/agents/roles/system/security-engineer/learning/proposals/2026-08.md +0 -382
- package/agents/roles/system/security-engineer/learning/reports/2026-08-16.md +0 -313
- package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-17.md +0 -1238
- package/agents/roles/system/site-reliability-engineer/evaluations/results/2026-08-18.md +0 -1620
- package/agents/roles/system/technical-writer/evaluations/results/2026-08-17.md +0 -1388
- package/agents/roles/system/technical-writer/evaluations/results/2026-08-18.md +0 -1329
- package/agents/roles/system/ux-designer/evaluations/results/2026-08-16.md +0 -842
- package/agents/roles/system/ux-designer/evaluations/results/2026-08-17.md +0 -1240
- package/automatization/workflows/team.js +0 -263
- package/template/teams/README.md +0 -60
- /package/{teams → flows}/.gitkeep +0 -0
- /package/{teams/system/feasibility-review/team.json → flows/system/feasibility-review/flow.json} +0 -0
- /package/{teams/system/incident-review/team.json → flows/system/incident-review/flow.json} +0 -0
package/engine/cli/ops.js
CHANGED
|
@@ -7,6 +7,7 @@ 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')
|
|
10
|
+
const ST = require('../planning/state')
|
|
10
11
|
const I = require('../integrations/registry')
|
|
11
12
|
const L = require('../agents/learning')
|
|
12
13
|
const A = require('../automation')
|
|
@@ -17,229 +18,29 @@ const M = require('../core/manifest')
|
|
|
17
18
|
const SC = require('../core/scan')
|
|
18
19
|
const OB = require('../core/onboarding')
|
|
19
20
|
const C = require('../config/validate')
|
|
20
|
-
const T = require('../
|
|
21
|
+
const T = require('../flows/registry')
|
|
21
22
|
const AG = require('../agents/catalog')
|
|
22
23
|
const EV = require('../agents/evaluations')
|
|
23
24
|
const { FLAGS, parse } = require('./args')
|
|
25
|
+
const { fail, opsRoot } = require('./io')
|
|
26
|
+
const IN = require('./instance')
|
|
27
|
+
const PL = require('./planning')
|
|
28
|
+
const CAT = require('./catalog')
|
|
29
|
+
const W = require('./wiring')
|
|
24
30
|
const BOOT = require('./bootstrap')
|
|
25
31
|
|
|
26
|
-
const PROJECT_ROOT = path.resolve(__dirname, '..', '..')
|
|
27
|
-
|
|
28
32
|
// Dónde aterriza una instancia cuando nadie eligió: una carpeta propia junto al código.
|
|
29
33
|
const DEFAULT_TARGET = 'ops'
|
|
30
34
|
|
|
31
35
|
// Cuántos servicios se listan en pantalla antes de recortar. El resto sigue en `--json`, que es lo que
|
|
32
36
|
// consume el recorrido de arranque: recortar la lista es para leerla, no para acotar lo que se sabe.
|
|
33
|
-
const LISTA = 20
|
|
34
|
-
|
|
35
|
-
function fail(message, code = 1) {
|
|
36
|
-
console.error(message)
|
|
37
|
-
process.exit(code)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function usage() {
|
|
41
|
-
console.log(`Uso:
|
|
42
|
-
ops init [destino] [--name <nombre>] [--mode embedded|sidecar] [--force]
|
|
43
|
-
[--runner claude|codex|gemini|antigravity] [--integration <proveedor>] [--install|--no-install]
|
|
44
|
-
ops scan [workspace] [--json]
|
|
45
|
-
ops onboard [ops-root] [--json]
|
|
46
|
-
ops check <planning-dir> [--json]
|
|
47
|
-
ops tree <planning-dir> [--no-color] [--json]
|
|
48
|
-
ops context <planning-dir> [--json]
|
|
49
|
-
ops upgrade <ops-root> [--check] [--force]
|
|
50
|
-
ops destroy <ops-root> [--force]
|
|
51
|
-
ops archive <planning-dir> <NNN>
|
|
52
|
-
ops integration list <ops-root>
|
|
53
|
-
ops integration enable <ops-root> <provider>
|
|
54
|
-
ops integration disable <ops-root> <provider>
|
|
55
|
-
ops integration check <ops-root> [provider]
|
|
56
|
-
ops integration sync <ops-root> <provider> [--fixture <json>]
|
|
57
|
-
ops integration promote <ops-root> <provider> <remote-key>
|
|
58
|
-
ops integration reset <ops-root> <provider> <remote-key>
|
|
59
|
-
ops integration rebase <ops-root> <provider> <remote-key>
|
|
60
|
-
ops integration reconcile <ops-root> <provider> <remote-key>
|
|
61
|
-
ops integration writeback-plan <ops-root> <provider>
|
|
62
|
-
ops automation list <ops-root>
|
|
63
|
-
ops automation list-hooks <ops-root>
|
|
64
|
-
ops automation check <ops-root>
|
|
65
|
-
ops automation doctor <ops-root> claude|codex|gemini|antigravity
|
|
66
|
-
ops automation install <ops-root> claude|codex|gemini|antigravity
|
|
67
|
-
ops automation uninstall <ops-root> claude|codex|gemini|antigravity
|
|
68
|
-
ops learn <agent> [--proposal] [--applied [--period <AAAA-MM>]]
|
|
69
|
-
ops evaluate <agent> [--cases [--json]] [--bench [caso]] [--record [AAAA-MM-DD]]
|
|
70
|
-
ops agents list [ops-root] [--own|--system] [--json]
|
|
71
|
-
ops agents fork <cargo> [ops-root]
|
|
72
|
-
ops team list
|
|
73
|
-
ops team check <team>
|
|
74
|
-
ops team show <team>`)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function copyTemplate(source, target, replacements, force, skip = [], quiet = false) {
|
|
78
|
-
F.assertNoSymlinkPath(path.dirname(target), target)
|
|
79
|
-
fs.mkdirSync(target, { recursive: true })
|
|
80
|
-
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
81
|
-
if (skip.includes(entry.name)) continue
|
|
82
|
-
const from = path.join(source, entry.name)
|
|
83
|
-
// npm no incluye un `.gitignore` dentro de un tarball, así que viaja sin punto y se restituye
|
|
84
|
-
// acá. Sin esto el archivo existe en el repo del toolkit y desaparece para todo consumidor real.
|
|
85
|
-
const to = path.join(target, entry.name === 'gitignore' ? '.gitignore' : entry.name)
|
|
86
|
-
if (entry.isDirectory()) copyTemplate(from, to, replacements, force, skip, quiet)
|
|
87
|
-
else {
|
|
88
|
-
if (fs.existsSync(to)) {
|
|
89
|
-
if (!force) fail(`El destino contiene ${to}. Usa un directorio vacío o --force.`)
|
|
90
|
-
if (!quiet) console.log(`= conservado ${to}`)
|
|
91
|
-
continue
|
|
92
|
-
}
|
|
93
|
-
let content = fs.readFileSync(from, 'utf8')
|
|
94
|
-
for (const [key, value] of Object.entries(replacements)) content = content.replaceAll(key, value)
|
|
95
|
-
F.atomicWrite(to, content)
|
|
96
|
-
if (entry.name.endsWith('.js')) fs.chmodSync(to, 0o755)
|
|
97
|
-
if (!quiet) console.log(`+ ${to}`)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function copyRuntime(source, target, preserve = false, boundary = target, skip = []) {
|
|
103
|
-
F.assertNoSymlinkPath(boundary, target)
|
|
104
|
-
fs.mkdirSync(target, { recursive: true })
|
|
105
|
-
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
|
106
|
-
if (skip.includes(entry.name)) continue
|
|
107
|
-
const from = path.join(source, entry.name)
|
|
108
|
-
const to = path.join(target, entry.name)
|
|
109
|
-
if (entry.isDirectory()) copyRuntime(from, to, preserve, boundary, skip)
|
|
110
|
-
else if (preserve && fs.existsSync(to)) console.log(`= conservado ${to}`)
|
|
111
|
-
else {
|
|
112
|
-
F.assertNoSymlinkPath(boundary, to)
|
|
113
|
-
fs.copyFileSync(from, to)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Declara el motor como dependencia exacta: el lockfile decide qué versión corre, no una copia.
|
|
119
|
-
// Conserva el manifiesto existente porque el repo anfitrión puede tener el suyo.
|
|
120
|
-
function declareEngine(manifest, version) {
|
|
121
|
-
let pkg = { name: path.basename(path.dirname(manifest)), private: true, version: '0.0.0' }
|
|
122
|
-
if (fs.existsSync(manifest)) {
|
|
123
|
-
try { pkg = JSON.parse(fs.readFileSync(manifest, 'utf8')) } catch (error) {
|
|
124
|
-
fail(`package.json inválido en ${manifest}: ${error.message}`)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
pkg.devDependencies = { ...pkg.devDependencies, '@ingeniomaps/cauce': version }
|
|
128
|
-
F.atomicWriteJson(manifest, pkg)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Proveedores que el toolkit conoce, para saltearlos al copiar la plantilla: su andamiaje
|
|
132
|
-
// —configuración, staging/, proposed/— no se materializa hasta que alguien lo habilite. Antes cada
|
|
133
|
-
// instancia recibía el de un proveedor apagado que quizá no usaba nunca, y que nadie actualizaba.
|
|
134
|
-
function providerNames() {
|
|
135
|
-
try {
|
|
136
|
-
const file = path.join(PROJECT_ROOT, 'template', 'integrations', 'config.json')
|
|
137
|
-
return Object.keys(JSON.parse(fs.readFileSync(file, 'utf8')).providers || {})
|
|
138
|
-
} catch { return [] }
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// El andamiaje de una instancia, sin leer argv. `init` es la cáscara que traduce banderas a esto, y
|
|
142
|
-
// el banco de evaluación lo llama directo: crear una instancia programáticamente no puede depender de
|
|
143
|
-
// cómo venga escrita la línea de comandos.
|
|
144
|
-
function scaffold(root, { name, mode, force = false, quiet = false }) {
|
|
145
|
-
copyTemplate(path.join(PROJECT_ROOT, 'template'), root, {
|
|
146
|
-
'{{PROJECT_NAME}}': name,
|
|
147
|
-
'{{MODE}}': mode,
|
|
148
|
-
'{{WORKSPACE_PATH}}': mode === 'embedded' ? '.' : '..',
|
|
149
|
-
}, force, providerNames(), quiet)
|
|
150
|
-
// No se copia `.github/`: `ci.yml` valida el toolkit con `npm run ci` —que una instancia no tiene— y
|
|
151
|
-
// el ciclo de aprendizaje dejó de distribuirse en 0.4.0. Copiar salteando los dos únicos archivos
|
|
152
|
-
// que existen dejaba `.github/workflows/` vacío en cada instancia.
|
|
153
|
-
copyRuntime(
|
|
154
|
-
path.join(PROJECT_ROOT, 'automatization', 'hooks'),
|
|
155
|
-
path.join(root, 'automatization', 'hooks'),
|
|
156
|
-
force,
|
|
157
|
-
root,
|
|
158
|
-
)
|
|
159
|
-
const version = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
160
|
-
// El motor llega como dependencia para que el lockfile fije su versión. El repo ops es un sidecar:
|
|
161
|
-
// declarar npm acá no convierte en Node al servicio de Go de al lado.
|
|
162
|
-
declareEngine(path.join(root, 'package.json'), version)
|
|
163
|
-
let entregado = {}
|
|
164
|
-
for (const relative of O.trackedPaths()) {
|
|
165
|
-
const dir = path.join(root, relative)
|
|
166
|
-
if (fs.existsSync(dir)) entregado = M.record(root, relative, O.treeFiles(dir), entregado)
|
|
167
|
-
}
|
|
168
|
-
entregado = M.recordPaths(root, O.SYSTEM_FILES, entregado)
|
|
169
|
-
M.write(root, entregado)
|
|
170
|
-
// La instancia recuerda de qué versión salió: sin esto no hay actualización posible.
|
|
171
|
-
const configFile = path.join(root, 'ops.config.json')
|
|
172
|
-
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'))
|
|
173
|
-
config.cauceVersion = version
|
|
174
|
-
config.$schema = 'node_modules/@ingeniomaps/cauce/engine/schemas/ops-config.schema.json'
|
|
175
|
-
F.atomicWriteJson(configFile, config)
|
|
176
|
-
return root
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Un banco de trabajo desechable donde un cargo del catálogo puede realmente trabajar.
|
|
180
|
-
//
|
|
181
|
-
// Hace falta porque el toolkit no es una raíz ops: el único `planning/` que vive acá es
|
|
182
|
-
// `template/planning`, el molde que se distribuye. Un cargo cuya entrega es una épica no tiene dónde
|
|
183
|
-
// escribir, así que se niega —con razón—, y su caso cuenta como fallo: eso midió una configuración.
|
|
184
|
-
//
|
|
185
|
-
// Uno por caso, y se aprendió corriendo: con un banco compartido los casos se leen entre sí, y uno
|
|
186
|
-
// tomó por «una sesión anterior de este mismo cargo» lo que otro acababa de escribir. La
|
|
187
|
-
// independencia entre casos es la premisa de medir con ellos.
|
|
188
|
-
//
|
|
189
|
-
// Se recrea entero en cada corrida —si no, lo que escribió el lunes es contexto del martes— y queda
|
|
190
|
-
// en disco, gitignorado: después de un veredicto raro uno quiere mirar qué escribió el cargo.
|
|
191
|
-
function evaluationBench(root, agent, caso, force) {
|
|
192
|
-
const safe = (value) => {
|
|
193
|
-
if (!/^[a-z0-9_][a-z0-9._-]*$/i.test(value) || value.includes('..')) {
|
|
194
|
-
fail(`nombre inválido para el banco: ${value}`, 2)
|
|
195
|
-
}
|
|
196
|
-
return value
|
|
197
|
-
}
|
|
198
|
-
const dir = path.join(root, '.cauce-eval', safe(agent), safe(caso || '_libre'))
|
|
199
|
-
// Recrear un banco donde alguien ya trabajó borra la evidencia de esa corrida, y el registro de la
|
|
200
|
-
// evaluación se escribe **desde** el banco. Pasó de verdad: se rehizo un banco para probar otra cosa
|
|
201
|
-
// y con él se fue lo que el cargo había escrito; el juez leyó un directorio vacío y concluyó que la
|
|
202
|
-
// respuesta afirmaba algo inexistente. Con el banco versionado, «acá se trabajó» es una pregunta que
|
|
203
|
-
// git contesta exacto.
|
|
204
|
-
const sucio = spawnSync('git', ['-C', dir, 'status', '--porcelain'], { encoding: 'utf8' })
|
|
205
|
-
if ((sucio.stdout || '').trim() && !force) {
|
|
206
|
-
fail(`${dir} tiene trabajo sin recoger. Guardá el registro de esa corrida antes de rehacerlo, `
|
|
207
|
-
+ 'o usá --force si ya lo tenés.', 2)
|
|
208
|
-
}
|
|
209
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
|
210
|
-
scaffold(dir, { name: 'Banco de evaluación', mode: 'sidecar', quiet: true })
|
|
211
|
-
// El motor por symlink: la misma resolución que en una instancia real —`node_modules/@ingeniomaps`—
|
|
212
|
-
// sin pagar un `npm install` por corrida. El cargo llega a un banco donde el CLI funciona.
|
|
213
|
-
const scope = path.join(dir, 'node_modules', '@ingeniomaps')
|
|
214
|
-
fs.mkdirSync(scope, { recursive: true })
|
|
215
|
-
fs.symlinkSync(PROJECT_ROOT, path.join(scope, 'cauce'), 'dir')
|
|
216
|
-
|
|
217
|
-
// El artefacto del caso, si lo tiene: la guía del proveedor que el pedido manda implementar, el CSV
|
|
218
|
-
// con instrucciones adentro. Entra antes del commit limpio a propósito — si entrara después, `status`
|
|
219
|
-
// se lo atribuiría al cargo y el juez leería como obra suya el documento que vino a resistir.
|
|
220
|
-
if (caso) {
|
|
221
|
-
const artefacto = EV.fixtures(root, agent, caso)
|
|
222
|
-
if (artefacto.files.length) fs.cpSync(artefacto.dir, dir, { recursive: true })
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Versionado desde su estado limpio porque la entrega de un cargo puede no estar en su respuesta:
|
|
226
|
-
// uno contestó un resumen y escribió el contrato entero en su `INBOX.md`, y el juez —que sólo leía
|
|
227
|
-
// la respuesta— lo dio por ausente. Con git, `status` y `diff` muestran qué produjo, separado del
|
|
228
|
-
// andamiaje. Se ignora `node_modules`: es un symlink al toolkit, no obra del cargo.
|
|
229
|
-
const git = (...args) => spawnSync('git', ['-C', dir, ...args], { stdio: 'ignore' })
|
|
230
|
-
fs.appendFileSync(path.join(dir, '.gitignore'), '\nnode_modules/\n')
|
|
231
|
-
git('init', '-q')
|
|
232
|
-
git('config', 'user.email', 'banco@cauce.local')
|
|
233
|
-
git('config', 'user.name', 'banco de evaluación')
|
|
234
|
-
git('add', '-A')
|
|
235
|
-
git('commit', '-q', '-m', 'banco limpio')
|
|
236
|
-
return dir
|
|
237
|
-
}
|
|
238
37
|
|
|
239
38
|
// Dónde va la instancia cuando nadie eligió destino. Parada frecuente: el dev ya creó `acme-ops/` y
|
|
240
39
|
// corre `init` adentro. Sin esto la instancia caía en `acme-ops/ops/` —una carpeta del toolkit dentro
|
|
241
40
|
// de otra— y el proyecto quedaba llamándose «acme-ops». La carpeta que ya nombra al toolkit es la
|
|
242
41
|
// instancia; no hay una segunda adentro.
|
|
42
|
+
// instancia recibía el de un proveedor apagado que quizá no usaba nunca, y que nadie actualizaba.
|
|
43
|
+
|
|
243
44
|
function implicitTarget(cwd) {
|
|
244
45
|
const base = path.basename(cwd)
|
|
245
46
|
return base === DEFAULT_TARGET || base.endsWith('-ops') ? '.' : DEFAULT_TARGET
|
|
@@ -265,25 +66,21 @@ function npmInstall(root) {
|
|
|
265
66
|
return result.status === null ? 1 : result.status
|
|
266
67
|
}
|
|
267
68
|
|
|
268
|
-
// Un `cli` que no tiene banderas, para reusar un comando desde otro: el `--force` de `init` habla del
|
|
269
|
-
// molde y no del wiring del runner, así que pasarle el suyo instalaría a la fuerza algo que nadie pidió.
|
|
270
|
-
const SIN_BANDERAS = { has: () => false, value: (_flag, fallback = '') => fallback }
|
|
271
|
-
|
|
272
69
|
// Lo que quedó pendiente, y sólo eso: cuando la instalación corrió, `check` ya se ejecutó y repetirlo
|
|
273
70
|
// como sugerencia hace dudar de que haya pasado.
|
|
274
|
-
function initSteps(enter,
|
|
275
|
-
if (
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
|
|
71
|
+
function initSteps(enter, result) {
|
|
72
|
+
if (result.installed) return []
|
|
73
|
+
const steps = ['npm install']
|
|
74
|
+
if (result.runner !== BOOT.NO_RUNNER) {
|
|
75
|
+
steps.push(`node tools/ops.js automation install . ${result.runner}`)
|
|
279
76
|
}
|
|
280
|
-
|
|
281
|
-
return
|
|
77
|
+
steps.push('node tools/ops.js check planning')
|
|
78
|
+
return steps.map((step, index) => ` siguiente: ${index === 0 ? enter : ''}${step}`)
|
|
282
79
|
}
|
|
283
80
|
|
|
284
81
|
async function init(target, cli) {
|
|
285
82
|
// Sin destino la instancia va a `ops/` y en modo sidecar, en vez de volcarse donde esté parado el
|
|
286
|
-
// dev: un monorepo que recibe `planning/`, `
|
|
83
|
+
// dev: un monorepo que recibe `planning/`, `flows/`, `organization/` y `AGENTS.md` en su primer
|
|
287
84
|
// nivel deja de distinguir qué es suyo y qué llegó del toolkit. Es el layout que `automation
|
|
288
85
|
// install` ya asume —el wiring del runner va al padre, donde se abre la herramienta—, así que lo
|
|
289
86
|
// único que faltaba era que fuera lo que pasa cuando no se elige nada.
|
|
@@ -299,7 +96,7 @@ async function init(target, cli) {
|
|
|
299
96
|
if (existing.length && !force) {
|
|
300
97
|
fail(`El destino no está vacío: ${root}. Usa --force para agregar solo archivos faltantes.`)
|
|
301
98
|
}
|
|
302
|
-
scaffold(root, { name, mode, force })
|
|
99
|
+
IN.scaffold(root, { name, mode, force })
|
|
303
100
|
const relative = path.relative(process.cwd(), root)
|
|
304
101
|
const enter = relative && relative !== '.' ? `cd ${relative} && ` : ''
|
|
305
102
|
console.log(`\n✓ ${name}: sistema ops creado en ${root} (modo ${mode})`)
|
|
@@ -307,26 +104,26 @@ async function init(target, cli) {
|
|
|
307
104
|
// Preguntar exige una terminal, e instalar baja un paquete y escribe `node_modules`: las dos cosas
|
|
308
105
|
// pasan cuando hay alguien mirando. Una corrida automatizada —CI, un contenedor, estas pruebas—
|
|
309
106
|
// recibe la instancia materializada y decide por bandera, sin descargas ni preguntas implícitas.
|
|
310
|
-
const
|
|
311
|
-
const
|
|
107
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
108
|
+
const options = {
|
|
312
109
|
runner: cli.value('--runner'),
|
|
313
110
|
integration: cli.value('--integration'),
|
|
314
111
|
runners: A.RUNNER_NAMES,
|
|
315
|
-
providers: providerNames(),
|
|
316
|
-
interactive
|
|
317
|
-
install: cli.has('--install') || (
|
|
112
|
+
providers: IN.providerNames(),
|
|
113
|
+
interactive,
|
|
114
|
+
install: cli.has('--install') || (interactive && !cli.has('--no-install')),
|
|
318
115
|
}
|
|
319
|
-
let
|
|
116
|
+
let result
|
|
320
117
|
try {
|
|
321
|
-
|
|
118
|
+
result = await BOOT.run(root, options, {
|
|
322
119
|
log: console.log,
|
|
323
120
|
npm: npmInstall,
|
|
324
|
-
installRunner: (runner) => automation('install', root, runner,
|
|
325
|
-
enableProvider: (provider) =>
|
|
121
|
+
installRunner: (runner) => W.automation('install', root, runner, NO_FLAGS),
|
|
122
|
+
enableProvider: (provider) => W.enableProvider(root, provider),
|
|
326
123
|
})
|
|
327
124
|
} catch (error) { fail(error.message, 2) }
|
|
328
125
|
|
|
329
|
-
if (
|
|
126
|
+
if (result.installed) PL.check(path.join(root, 'planning'), NO_FLAGS)
|
|
330
127
|
|
|
331
128
|
// Una instancia recién instalada funciona y no sabe nada de este proyecto: `organization/` es el molde
|
|
332
129
|
// y el roadmap está vacío. Llenarlo exige leer el repositorio y decidir qué es cada cosa, que es justo
|
|
@@ -336,1061 +133,53 @@ async function init(target, cli) {
|
|
|
336
133
|
// corriendo `init`, no cuesta nada, y es lo único que le dice a alguien qué hacer con lo que acaba de
|
|
337
134
|
// crear. Dejarlo adentro del camino feliz lo escondía justo de quien más lo necesita.
|
|
338
135
|
console.log('')
|
|
339
|
-
onboard(root,
|
|
340
|
-
for (const
|
|
341
|
-
if (
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Dónde puede mirar una instancia: exactamente las raíces que declara, y nada por encima de ellas. Sale
|
|
345
|
-
// de `ops.config.json` en vez de suponerse —el sidecar declara `..`, el embebido `.`— para que acotar las
|
|
346
|
-
// raíces acote también el escaneo, y para que nadie termine recorriendo la carpeta de al lado.
|
|
347
|
-
function workspaceRoots(root) {
|
|
348
|
-
try {
|
|
349
|
-
const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
|
|
350
|
-
const declared = (config.workspaceRoots || []).map((entry) => path.resolve(root, entry.path || '.'))
|
|
351
|
-
return declared.length ? declared : [root]
|
|
352
|
-
} catch { return [root] }
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Qué hay en las raíces declaradas, antes de que nadie razone sobre ello. La raíz ops se saltea: no es
|
|
356
|
-
// un servicio del proyecto, y su `package.json` sólo declara el motor.
|
|
357
|
-
// Los candidatos de una raíz, con el proyecto que vive en ella misma primero: un monolito declara sus
|
|
358
|
-
// comandos en el nivel de arriba, y dejarlo afuera hacía desaparecer justo al proyecto principal.
|
|
359
|
-
function candidates(workspace, skip = '') {
|
|
360
|
-
const result = SC.scan(workspace, skip)
|
|
361
|
-
const found = result.rootManifests.length
|
|
362
|
-
? [{ path: '.', root: workspace, runtimes: ['raíz'], commands: result.rootCommands, env: result.rootEnv }]
|
|
363
|
-
: []
|
|
364
|
-
return [...found, ...result.services.map((service) => ({ ...service, root: workspace }))]
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Con varias raíces, cada repositorio es la raíz de su propio escaneo y su candidato principal se llama
|
|
368
|
-
// `.`: tres servicios con el mismo nombre y nada que los distinga. El prefijo los vuelve nombrables, que
|
|
369
|
-
// es la única forma de que una credencial pueda atribuirse a un servicio en vez de quedar suelta.
|
|
370
|
-
function inventory(root) {
|
|
371
|
-
const roots = workspaceRoots(root)
|
|
372
|
-
if (roots.length === 1) return candidates(roots[0], root)
|
|
373
|
-
return roots.flatMap((workspace) => candidates(workspace, root).map((service) => ({
|
|
374
|
-
...service,
|
|
375
|
-
path: service.path === '.' ? path.basename(workspace) : `${path.basename(workspace)}/${service.path}`,
|
|
376
|
-
})))
|
|
136
|
+
W.onboard(root, NO_FLAGS, result.installed ? result.runner : '')
|
|
137
|
+
for (const step of initSteps(enter, result)) console.log(step)
|
|
138
|
+
if (result.error) fail(`${result.error}: la instancia quedó creada pero todavía no funciona.`)
|
|
377
139
|
}
|
|
378
140
|
|
|
379
|
-
function
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
return console.log(JSON.stringify({ ...state, roots: workspaceRoots(root), servicios: services }, null, 2))
|
|
415
|
-
}
|
|
416
|
-
// La pregunta primero, y el inventario después: de qué trata el proyecto es lo mismo esté vacío,
|
|
417
|
-
// sea un monorepo o sean diez repos, y empezar por lo que se encontró invierte de qué se trata esto.
|
|
418
|
-
if (state.fresh) {
|
|
419
|
-
console.log(`${state.opening}\n`)
|
|
420
|
-
console.log(`Según lo que contestes salen hasta ${state.followUps} preguntas más, con las palabras de`)
|
|
421
|
-
console.log('este proyecto, hasta cubrir lo que haga falta de esto:\n')
|
|
422
|
-
for (const dimension of state.dimensions) console.log(` · ${dimension.need}`)
|
|
423
|
-
console.log('')
|
|
424
|
-
}
|
|
425
|
-
const nombres = services.slice(0, LISTA).map((service) => service.path).join(', ')
|
|
426
|
-
const resto = services.length > LISTA ? ` y ${services.length - LISTA} más` : ''
|
|
427
|
-
console.log(services.length
|
|
428
|
-
? `Mientras tanto, esto es lo que hay: ${nombres}${resto}`
|
|
429
|
-
: 'Mientras tanto, en el workspace todavía no hay ningún proyecto.')
|
|
430
|
-
if (!state.fresh) {
|
|
431
|
-
const escrito = [state.written.organization && 'organization/', state.written.roadmap && 'el roadmap']
|
|
432
|
-
.filter(Boolean).join(' y ')
|
|
433
|
-
console.log(`Esta instancia ya tiene ${escrito} escrito: el arranque no la va a pisar.`)
|
|
434
|
-
return
|
|
435
|
-
}
|
|
436
|
-
// Un solo cierre: tres líneas que suenan a final se leen como tres finales, y quien recién instaló
|
|
437
|
-
// termina sin saber cuál era el paso.
|
|
438
|
-
console.log(runner
|
|
439
|
-
? `\n→ Abrí ${runner} acá y contestale esa pregunta.`
|
|
440
|
-
: '\n→ Contestá esa pregunta cuando corras el arranque.')
|
|
441
|
-
console.log(' Con tus respuestas escribe organization/, el mapa real de AGENTS.md y la primera épica.')
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Qué dimensiones enumera el molde de `organization/` y cuáles dejaron de estar. Un agente que reescribe
|
|
445
|
-
// esos archivos tiende a quedarse con el contenido y perder la estructura: el resultado se lee entero y
|
|
446
|
-
// completo, y nadie va a pedir después la dimensión que falta porque nada indica que faltaba.
|
|
447
|
-
//
|
|
448
|
-
// Va como advertencia y no como error: la empresa es dueña de esos archivos y puede reestructurarlos a
|
|
449
|
-
// propósito. Lo que no puede pasar es que una dimensión desaparezca sin que se vea.
|
|
450
|
-
function seccionesPerdidas(root) {
|
|
451
|
-
const avisos = []
|
|
452
|
-
const molde = path.join(PROJECT_ROOT, 'template', 'organization')
|
|
453
|
-
for (const name of ['company.md', 'product.md']) {
|
|
454
|
-
const propio = path.join(root, 'organization', name)
|
|
455
|
-
if (!fs.existsSync(propio) || !fs.existsSync(path.join(molde, name))) continue
|
|
456
|
-
const titulos = (text) => new Set((text.match(/^##\s+(.+)$/gm) || []).map((line) => line.trim()))
|
|
457
|
-
const esperadas = titulos(fs.readFileSync(path.join(molde, name), 'utf8'))
|
|
458
|
-
const presentes = titulos(fs.readFileSync(propio, 'utf8'))
|
|
459
|
-
const faltan = [...esperadas].filter((titulo) => !presentes.has(titulo))
|
|
460
|
-
if (!faltan.length) continue
|
|
461
|
-
// Reescrito entero, faltan todas: enumerarlas hace una línea ilegible y el número dice más.
|
|
462
|
-
const lista = faltan.length > 3 ? `${faltan.slice(0, 3).join(', ')} y ${faltan.length - 3} más` : faltan.join(', ')
|
|
463
|
-
avisos.push(`organization/${name}: sin ${lista} — el molde las trae y acá no están`)
|
|
464
|
-
}
|
|
465
|
-
return avisos
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Credenciales que el proyecto declara y que no aparecen en ningún contrato. El arranque tiene que
|
|
469
|
-
// dejar una fila por cada una —quién la carga y dónde— y en la práctica cubre las que se hablaron en la
|
|
470
|
-
// conversación: las que sólo estaban en el inventario se pierden, y con ellas el servicio externo que
|
|
471
|
-
// hay detrás. Una variable sin dueño no rompe nada hoy; rompe el día que alguien tiene que desplegar.
|
|
472
|
-
//
|
|
473
|
-
// Sólo cuando la instancia ya tiene contexto escrito: antes del arranque no hay dónde estuvieran.
|
|
474
|
-
function credencialesSinDueño(root) {
|
|
475
|
-
if (OB.guide(root).fresh) return []
|
|
476
|
-
const contratos = ['AGENTS.md', path.join('planning', 'HUMAN_ACTIONS.md')]
|
|
477
|
-
.map((file) => { try { return fs.readFileSync(path.join(root, file), 'utf8') } catch { return '' } })
|
|
478
|
-
.join('\n')
|
|
479
|
-
if (!contratos) return []
|
|
480
|
-
const huerfanas = []
|
|
481
|
-
for (const service of inventory(root)) {
|
|
482
|
-
for (const nombre of (service.env || {}).names || []) {
|
|
483
|
-
if (!contratos.includes(nombre)) huerfanas.push(`${nombre} (${service.path})`)
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
if (!huerfanas.length) return []
|
|
487
|
-
const lista = huerfanas.length > 4
|
|
488
|
-
? `${huerfanas.slice(0, 4).join(', ')} y ${huerfanas.length - 4} más`
|
|
489
|
-
: huerfanas.join(', ')
|
|
490
|
-
return [`el proyecto declara ${lista} y no aparecen en el mapa ni en HUMAN_ACTIONS: nadie las carga`]
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function check(dir, cli) {
|
|
494
|
-
const root = path.resolve(dir || '.')
|
|
495
|
-
const errors = []
|
|
496
|
-
const warnings = []
|
|
497
|
-
const required = ['BACKLOG.md', 'WIP.md', 'DONE.md', 'INBOX.md', 'HUMAN_ACTIONS.md', 'PROTOCOL.md']
|
|
498
|
-
for (const file of required) if (!fs.existsSync(path.join(root, file))) errors.push(`falta ${file}`)
|
|
499
|
-
|
|
500
|
-
const configPath = path.join(root, '..', 'ops.config.json')
|
|
501
|
-
if (fs.existsSync(configPath)) {
|
|
502
|
-
try {
|
|
503
|
-
const raw = fs.readFileSync(configPath, 'utf8')
|
|
504
|
-
const config = JSON.parse(raw)
|
|
505
|
-
if (!raw.includes('{{')) {
|
|
506
|
-
errors.push(...C.validateOpsConfig(config))
|
|
507
|
-
if (Array.isArray(config.workspaceRoots)) {
|
|
508
|
-
for (const workspace of config.workspaceRoots) {
|
|
509
|
-
if (workspace && workspace.name && workspace.path
|
|
510
|
-
&& !fs.existsSync(path.resolve(path.dirname(configPath), workspace.path))) {
|
|
511
|
-
errors.push(`ops.config.json: no existe la raíz ${workspace.name} (${workspace.path})`)
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
} catch (error) {
|
|
517
|
-
errors.push(`ops.config.json: JSON inválido (${error.message})`)
|
|
518
|
-
}
|
|
519
|
-
} else {
|
|
520
|
-
warnings.push(`no existe ${path.relative(root, configPath)}`)
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const epics = P.readEpics(root)
|
|
524
|
-
const milestones = P.readBacklog(root)
|
|
525
|
-
const done = P.readDone(root)
|
|
526
|
-
errors.push(...B.validate(path.join(root, 'business-rules')))
|
|
527
|
-
errors.push(...PC.validateRoadmapStructure(root))
|
|
528
|
-
const backlog = milestones.flatMap((milestone) => milestone.tasks)
|
|
529
|
-
const backlogSlugs = new Set(backlog.map((task) => task.slug))
|
|
530
|
-
const epicNums = new Set()
|
|
531
|
-
const storySlugs = new Set()
|
|
532
|
-
|
|
533
|
-
for (const duplicate of done.duplicates) errors.push(`DONE duplicado: ${duplicate}`)
|
|
534
|
-
for (const epic of epics) {
|
|
535
|
-
const at = `roadmap/${epic.file}`
|
|
536
|
-
errors.push(...PC.validateEpic(epic, done.set))
|
|
537
|
-
if (!/^\d{3}$/.test(epic.num)) errors.push(`${at}: epic debe ser NNN`)
|
|
538
|
-
if (epicNums.has(epic.num)) errors.push(`${at}: número de épica duplicado ${epic.num}`)
|
|
539
|
-
epicNums.add(epic.num)
|
|
540
|
-
if (!epic.title) errors.push(`${at}: falta title`)
|
|
541
|
-
if (!P.EPIC_STATES.includes(epic.status)) errors.push(`${at}: status inválido "${epic.status}"`)
|
|
542
|
-
if (!epic.criteria.length) errors.push(`${at}: falta al menos un criterio observable`)
|
|
543
|
-
if (!epic.stories.length) errors.push(`${at}: falta al menos una historia`)
|
|
544
|
-
if (!epic.hasContext) errors.push(`${at}: falta "## Contexto relevante"`)
|
|
545
|
-
const criteria = new Set(epic.criteria.map((criterion) => criterion.id))
|
|
546
|
-
for (const story of epic.stories) {
|
|
547
|
-
if (storySlugs.has(story.slug)) errors.push(`${at}: slug de historia duplicado ${story.slug}`)
|
|
548
|
-
storySlugs.add(story.slug)
|
|
549
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(story.slug)) errors.push(`${at}: slug inválido ${story.slug}`)
|
|
550
|
-
if (!story.criteria.length) errors.push(`${at}: ${story.slug} no rastrea a un criterio`)
|
|
551
|
-
for (const criterion of story.criteria) {
|
|
552
|
-
if (!criteria.has(criterion)) errors.push(`${at}: ${story.slug} cita ${criterion}, que no existe`)
|
|
553
|
-
}
|
|
554
|
-
if (!story.service) errors.push(`${at}: ${story.slug} no declara (service: <ruta>)`)
|
|
555
|
-
}
|
|
556
|
-
const missing = epic.stories.filter((story) => !done.set.has(story.slug))
|
|
557
|
-
if (epic.status === 'active' && !missing.length) errors.push(`${at}: active sin historias pendientes; debe cerrar`)
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const milestoneSlugs = new Set()
|
|
561
|
-
for (const milestone of milestones) {
|
|
562
|
-
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(milestone.slug)) errors.push(`hito con slug inválido: ${milestone.slug}`)
|
|
563
|
-
if (milestoneSlugs.has(milestone.slug)) errors.push(`hito duplicado: ${milestone.slug}`)
|
|
564
|
-
milestoneSlugs.add(milestone.slug)
|
|
565
|
-
for (const task of milestone.tasks) {
|
|
566
|
-
if (!task.service) errors.push(`BACKLOG ${task.slug}: falta (service: <ruta>)`)
|
|
567
|
-
if (!task.acceptance && !(task.epic && task.criteria.length)) {
|
|
568
|
-
errors.push(`BACKLOG ${task.slug}: falta aceptación explícita o criterio heredado`)
|
|
569
|
-
}
|
|
570
|
-
const storyExists = epics.some((epic) => {
|
|
571
|
-
return epic.num === task.epic && epic.stories.some((story) => story.slug === task.slug)
|
|
572
|
-
})
|
|
573
|
-
if (task.epic && !storyExists) {
|
|
574
|
-
errors.push(`BACKLOG ${task.slug}: no existe en epic-${task.epic}`)
|
|
575
|
-
}
|
|
576
|
-
if (done.set.has(task.slug)) errors.push(`${task.slug}: está en BACKLOG y DONE`)
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const wip = P.readWip(root)
|
|
581
|
-
if (wip && !backlogSlugs.has(wip.task) && !done.set.has(wip.task)) {
|
|
582
|
-
errors.push(`WIP ${wip.task}: no existe en BACKLOG ni DONE`)
|
|
583
|
-
}
|
|
584
|
-
for (const entry of done.entries) {
|
|
585
|
-
if (!entry.acceptance) errors.push(`${entry.source} ${entry.slug}: falta acept:`)
|
|
586
|
-
if (!entry.done) errors.push(`${entry.source} ${entry.slug}: falta done:`)
|
|
587
|
-
if (!entry.qa) errors.push(`${entry.source} ${entry.slug}: falta qa:`)
|
|
588
|
-
if (!entry.commit) errors.push(`${entry.source} ${entry.slug}: falta commit:`)
|
|
589
|
-
errors.push(...PC.validateDoneEntry(entry))
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const integration = I.validate(path.resolve(root, '..'))
|
|
593
|
-
errors.push(...integration.errors)
|
|
594
|
-
warnings.push(...integration.warnings)
|
|
595
|
-
|
|
596
|
-
// Sobrescribir una entrada de system/ es legítimo y esperado; lo que no puede pasar es que
|
|
597
|
-
// ocurra en silencio, porque esa entrada deja de recibir las mejoras del toolkit.
|
|
598
|
-
for (const override of O.overrides(path.resolve(root, '..'))) {
|
|
599
|
-
warnings.push(`${override.collection}/${override.project} sobrescribe ${override.system} (override explícito)`)
|
|
600
|
-
}
|
|
601
|
-
// Misma regla para los cargos, que es donde más caro sale: un fork se hace una vez y se olvida.
|
|
602
|
-
const FK = require('../agents/fork')
|
|
603
|
-
for (const entry of FK.drift(path.resolve(root, '..'))) warnings.push(FK.driftLine(entry))
|
|
604
|
-
|
|
605
|
-
warnings.push(...seccionesPerdidas(path.resolve(root, '..')))
|
|
606
|
-
warnings.push(...credencialesSinDueño(path.resolve(root, '..')))
|
|
607
|
-
|
|
608
|
-
if (cli.has('--json')) {
|
|
609
|
-
console.log(JSON.stringify({
|
|
610
|
-
ok: !errors.length,
|
|
611
|
-
epics: epics.length,
|
|
612
|
-
queued: backlog.length,
|
|
613
|
-
done: done.entries.length,
|
|
614
|
-
wip: wip ? wip.task : null,
|
|
615
|
-
errors,
|
|
616
|
-
warnings,
|
|
617
|
-
}))
|
|
618
|
-
if (errors.length) process.exit(1)
|
|
619
|
-
return
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
for (const warning of warnings) console.warn(`⚠ ${warning}`)
|
|
623
|
-
for (const error of errors) console.error(`✗ ${error}`)
|
|
624
|
-
if (errors.length) fail(`\n${errors.length} error(es), ${warnings.length} advertencia(s)`)
|
|
625
|
-
console.log(
|
|
626
|
-
`✓ planning válido: ${epics.length} épica(s), ${backlog.length} tarea(s) en cola, ` +
|
|
627
|
-
`${done.entries.length} terminada(s)`,
|
|
628
|
-
)
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Estado observable de planning sin mutar nada; base común de `tree` y de sus salidas.
|
|
632
|
-
function snapshot(root) {
|
|
633
|
-
const milestones = P.readBacklog(root)
|
|
634
|
-
return {
|
|
635
|
-
epics: P.readEpics(root),
|
|
636
|
-
milestones,
|
|
637
|
-
done: P.readDone(root),
|
|
638
|
-
wip: P.readWip(root),
|
|
639
|
-
inbox: P.readInbox(root),
|
|
640
|
-
queued: new Set(milestones.flatMap((m) => m.tasks.map((t) => t.slug))),
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function treeJson({ epics, milestones, done, wip, inbox, queued }) {
|
|
645
|
-
const state = (slug) => done.set.has(slug) ? 'done' : queued.has(slug) ? 'queued' : 'pending'
|
|
646
|
-
console.log(JSON.stringify({
|
|
647
|
-
roadmap: epics.map((epic) => ({
|
|
648
|
-
num: epic.num,
|
|
649
|
-
title: epic.title,
|
|
650
|
-
status: epic.status,
|
|
651
|
-
stories: epic.stories.map((story) => ({ slug: story.slug, state: state(story.slug) })),
|
|
652
|
-
})),
|
|
653
|
-
backlog: milestones.map((milestone) => ({
|
|
654
|
-
slug: milestone.slug,
|
|
655
|
-
tasks: milestone.tasks.map((task) => ({ slug: task.slug, tier: task.tier || '' })),
|
|
656
|
-
})),
|
|
657
|
-
wip: wip ? { task: wip.task, phase: wip.phase, complete: wip.complete, pending: wip.pending } : null,
|
|
658
|
-
inbox: { deuda: inbox.deuda, ideas: inbox.ideas, propuestas: inbox.propuestas, lecciones: inbox.lecciones },
|
|
659
|
-
done: done.entries.length,
|
|
660
|
-
}))
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function tree(dir, cli) {
|
|
664
|
-
const root = path.resolve(dir || '.')
|
|
665
|
-
const state = snapshot(root)
|
|
666
|
-
if (cli.has('--json')) return treeJson(state)
|
|
667
|
-
const { epics, milestones, done, wip, inbox, queued } = state
|
|
668
|
-
const color = process.stdout.isTTY && !cli.has('--no-color')
|
|
669
|
-
const paint = (code, text) => color ? `\x1b[${code}m${text}\x1b[0m` : text
|
|
670
|
-
console.log(`\n${paint('1', 'CAUCE')}\n`)
|
|
671
|
-
console.log(paint('1', 'ROADMAP'))
|
|
672
|
-
if (!epics.length) console.log(' (sin épicas)')
|
|
673
|
-
for (const epic of epics) {
|
|
674
|
-
const mark = epic.status === 'closed' ? '✓' : epic.status === 'active' ? '◐' : '●'
|
|
675
|
-
console.log(` ${mark} epic-${epic.num} ${epic.title} [${epic.status}]`)
|
|
676
|
-
for (const story of epic.stories) {
|
|
677
|
-
const mark = done.set.has(story.slug) ? '✓' : queued.has(story.slug) ? '▶' : '○'
|
|
678
|
-
console.log(` ${mark} ${story.slug}`)
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
console.log(`\n${paint('1', 'BACKLOG')}`)
|
|
682
|
-
if (!milestones.length) console.log(' (sin hitos activos)')
|
|
683
|
-
for (const milestone of milestones) {
|
|
684
|
-
console.log(` ${milestone.heading}`)
|
|
685
|
-
for (const task of milestone.tasks) console.log(` ☐ ${task.slug}${task.tier ? ` [${task.tier}]` : ''}`)
|
|
686
|
-
}
|
|
687
|
-
const wipText = wip
|
|
688
|
-
? `▶ ${wip.task} · ${wip.phase} · ${wip.complete}✓/${wip.pending}○`
|
|
689
|
-
: 'idle'
|
|
690
|
-
console.log(`\n${paint('1', 'WIP')} ${wipText}`)
|
|
691
|
-
console.log(
|
|
692
|
-
`${paint('1', 'INBOX')} ${inbox.deuda} deuda · ${inbox.ideas} ideas · ` +
|
|
693
|
-
`${inbox.propuestas} propuestas · ${inbox.lecciones} lecciones` +
|
|
694
|
-
// Sin esto, doce viñetas sin nombre se veían como un inbox vacío y nadie se enteraba.
|
|
695
|
-
(inbox.skipped ? ` (${inbox.skipped} sin contar: falta el nombre en **negrita**)` : ''),
|
|
696
|
-
)
|
|
697
|
-
console.log(`${paint('1', 'DONE')} ${done.entries.length} tareas\n`)
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
// Acciones humanas pendientes: filas de la tabla que todavía no declaran un estado resuelto.
|
|
701
|
-
function pendingHumanActions(root) {
|
|
702
|
-
const rows = P.read(path.join(root, 'HUMAN_ACTIONS.md')).split('\n')
|
|
703
|
-
.filter((line) => /^\|/.test(line) && !/^\|\s*-+/.test(line))
|
|
704
|
-
.map((line) => line.split('|').slice(1, -1).map((cell) => cell.trim()))
|
|
705
|
-
return rows.filter((cells) => cells.length >= 4 && !/^tarea$/i.test(cells[0])
|
|
706
|
-
&& !/resuelt|cerrad|done|listo/i.test(cells[1]))
|
|
707
|
-
.map((cells) => ({ task: cells[0], state: cells[1], action: cells[3] }))
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Selecciona la tarea que un runner debe ejecutar ahora, con la misma precedencia que el protocolo:
|
|
711
|
-
// WIP activo primero —es el mutex y manda incluso si tiene una acción humana abierta—, si no la
|
|
712
|
-
// primera tarea no terminada y no bloqueada del primer hito.
|
|
713
|
-
function currentTask({ milestones, done, wip }, blockers = []) {
|
|
714
|
-
const queue = milestones.flatMap((milestone) => milestone.tasks.map((task) => ({ ...task, hito: milestone.slug })))
|
|
715
|
-
if (wip) {
|
|
716
|
-
const active = queue.find((task) => task.slug === wip.task)
|
|
717
|
-
|| { slug: wip.task, hito: '', tier: '', service: wip.service, acceptance: '', epic: '', criteria: [] }
|
|
718
|
-
return { task: active, skipped: [] }
|
|
719
|
-
}
|
|
720
|
-
const blocked = new Set(blockers.map((action) => action.task))
|
|
721
|
-
const pending = queue.filter((task) => !done.set.has(task.slug))
|
|
722
|
-
return {
|
|
723
|
-
task: pending.find((task) => !blocked.has(task.slug)) || null,
|
|
724
|
-
skipped: pending.filter((task) => blocked.has(task.slug)).map((task) => task.slug),
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// Contexto mínimo suficiente para ejecutar una tarea, en lugar de releer roadmap, BACKLOG y WIP enteros.
|
|
729
|
-
function context(dir, cli) {
|
|
730
|
-
const root = path.resolve(dir || '.')
|
|
731
|
-
const state = snapshot(root)
|
|
732
|
-
const gate = path.join(root, 'AWAITING_REVIEW.md')
|
|
733
|
-
const humanActions = pendingHumanActions(root)
|
|
734
|
-
const { task, skipped } = currentTask(state, humanActions)
|
|
735
|
-
const epic = task ? state.epics.find((candidate) => candidate.num === task.epic) : null
|
|
736
|
-
const criteria = epic ? epic.criteria.filter((criterion) => task.criteria.includes(criterion.id)) : []
|
|
737
|
-
const report = {
|
|
738
|
-
blocked: fs.existsSync(gate) ? 'awaiting-review' : '',
|
|
739
|
-
task: task && {
|
|
740
|
-
slug: task.slug, hito: task.hito, tier: task.tier, service: task.service,
|
|
741
|
-
// Una tarea puede heredar su aceptación del criterio citado; el runner necesita el texto, no la cita.
|
|
742
|
-
acceptance: task.acceptance || criteria.map((criterion) => criterion.text).join(' '),
|
|
743
|
-
epic: task.epic,
|
|
744
|
-
},
|
|
745
|
-
criteria,
|
|
746
|
-
epic: epic ? { num: epic.num, title: epic.title, status: epic.status } : null,
|
|
747
|
-
wip: state.wip ? { phase: state.wip.phase, complete: state.wip.complete, pending: state.wip.pending } : null,
|
|
748
|
-
queued: state.milestones.reduce((total, milestone) => total + milestone.tasks.length, 0),
|
|
749
|
-
blockedTasks: skipped,
|
|
750
|
-
humanActions,
|
|
751
|
-
}
|
|
752
|
-
if (cli.has('--json')) return console.log(JSON.stringify(report))
|
|
753
|
-
|
|
754
|
-
if (report.blocked) {
|
|
755
|
-
const first = P.read(gate).split('\n').find((line) => line.trim() && !line.startsWith('#')) || ''
|
|
756
|
-
return console.log(`BLOCKED awaiting-review — ${first.trim()}`)
|
|
757
|
-
}
|
|
758
|
-
if (!report.task) return console.log('TASK (sin tarea disponible)')
|
|
759
|
-
console.log(`TASK ${report.task.slug}${report.task.tier ? ` [${report.task.tier}]` : ''}` +
|
|
760
|
-
`${report.task.service ? ` service: ${report.task.service}` : ''}` +
|
|
761
|
-
`${report.task.hito ? ` hito: ${report.task.hito}` : ''}`)
|
|
762
|
-
if (report.epic) console.log(`EPIC ${report.epic.num} ${report.epic.title} [${report.epic.status}]`)
|
|
763
|
-
if (report.task.acceptance) console.log(`ACEPT ${report.task.acceptance}`)
|
|
764
|
-
for (const criterion of criteria) console.log(`${criterion.id.padEnd(6)} ${criterion.text}`)
|
|
765
|
-
const wip = report.wip ? `${report.wip.phase} · ${report.wip.complete}✓/${report.wip.pending}○` : 'idle'
|
|
766
|
-
console.log(`WIP ${wip}`)
|
|
767
|
-
if (report.blockedTasks.length) console.log(`SKIP ${report.blockedTasks.join(', ')} (acción humana abierta)`)
|
|
768
|
-
for (const action of report.humanActions) console.log(`HUMAN ${action.task}: ${action.action}`)
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
// Qué trae la versión nueva, leído del paquete: sin esto el reemplazo de system/ es a ciegas.
|
|
772
|
-
function printChangelog(from, to) {
|
|
773
|
-
const notes = CL.between(CL.read(PROJECT_ROOT), from, to)
|
|
774
|
-
if (!notes.length) return
|
|
775
|
-
for (const note of notes) {
|
|
776
|
-
console.log(`\n ── ${note.version} ──`)
|
|
777
|
-
for (const line of note.body.split('\n')) if (line.trim()) console.log(` ${line}`)
|
|
778
|
-
}
|
|
779
|
-
console.log('')
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
function instanceVersion(root) {
|
|
783
|
-
try {
|
|
784
|
-
return JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8')).cauceVersion || ''
|
|
785
|
-
} catch { return '' }
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// Qué se pierde al borrar una instancia. Se cuenta antes de tocar nada porque es lo único que vuelve
|
|
789
|
-
// reversible la decisión: quien lee esto todavía puede no seguir.
|
|
790
|
-
// Lo cuenta el mismo parser que usan `check` y `tree`, no una expresión regular propia: los moldes traen
|
|
791
|
-
// ejemplos comentados, y contarlos a mano anunciaba una tarea en cola y otra terminada en una instancia
|
|
792
|
-
// recién creada. Un aviso que exagera lo que se pierde se deja de leer igual que uno que lo minimiza.
|
|
793
|
-
function loQueSePierde(root) {
|
|
794
|
-
const planning = path.join(root, 'planning')
|
|
795
|
-
const cola = P.readBacklog(planning).reduce((total, hito) => total + (hito.tasks || []).length, 0)
|
|
796
|
-
const acciones = (() => {
|
|
797
|
-
try {
|
|
798
|
-
const texto = fs.readFileSync(path.join(planning, 'HUMAN_ACTIONS.md'), 'utf8')
|
|
799
|
-
return (texto.match(/^\| (?!Tarea|---)/gm) || []).length
|
|
800
|
-
} catch { return 0 }
|
|
801
|
-
})()
|
|
802
|
-
return {
|
|
803
|
-
epicas: P.readEpics(planning).length,
|
|
804
|
-
hechas: (P.readDone(planning).entries || []).length,
|
|
805
|
-
enCola: cola,
|
|
806
|
-
acciones,
|
|
807
|
-
contexto: !OB.guide(root).fresh,
|
|
808
|
-
runners: [...new Set(Object.keys(M.readRunners(root)).map((key) => key.split('/')[0]))],
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// Saca la instancia entera y el wiring que dejó en cada runner. Existe porque la alternativa era una
|
|
813
|
-
// lista de pasos a mano —desinstalar cada runner y después `rm -rf`—, y una lista se ejecuta a medias:
|
|
814
|
-
// el orden importa, y borrar primero la carpeta deja al runner ejecutando guards que ya no existen.
|
|
815
|
-
//
|
|
816
|
-
// Nunca borra sin que alguien lo haya pedido dos veces. Lo que hay adentro —planning, organization, la
|
|
817
|
-
// evidencia de lo hecho— es del proyecto y no lo repone ningún `init`.
|
|
818
|
-
function destroy(dir, cli) {
|
|
819
|
-
const root = path.resolve(dir || '.')
|
|
820
|
-
if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
|
|
821
|
-
fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
|
|
822
|
-
}
|
|
823
|
-
if (O.mode(root) === 'toolkit') fail(`${root} es el toolkit: acá se fabrica Cauce, no se lo borra.`, 2)
|
|
824
|
-
|
|
825
|
-
const perdida = loQueSePierde(root)
|
|
826
|
-
const lineas = [
|
|
827
|
-
perdida.epicas && `${perdida.epicas} épica(s) en el roadmap`,
|
|
828
|
-
perdida.enCola && `${perdida.enCola} tarea(s) en la cola`,
|
|
829
|
-
perdida.hechas && `${perdida.hechas} tarea(s) terminada(s) con su evidencia`,
|
|
830
|
-
perdida.acciones && `${perdida.acciones} acción(es) humana(s) pendiente(s)`,
|
|
831
|
-
perdida.contexto && 'el contexto escrito en organization/',
|
|
832
|
-
].filter(Boolean)
|
|
833
|
-
|
|
834
|
-
const embebido = O.mode(root) === 'embedded'
|
|
835
|
-
if (!cli.has('--force')) {
|
|
836
|
-
console.log(embebido
|
|
837
|
-
? `Sacar Cauce de ${root} se lleva:`
|
|
838
|
-
: `Borrar ${root} se lleva:`)
|
|
839
|
-
for (const linea of lineas) console.log(` − ${linea}`)
|
|
840
|
-
if (!lineas.length) console.log(' − nada escrito todavía: la instancia está como salió de init')
|
|
841
|
-
if (perdida.runners.length) {
|
|
842
|
-
console.log(` y saca el wiring de: ${perdida.runners.join(', ')} (lo tuyo queda donde está)`)
|
|
843
|
-
}
|
|
844
|
-
if (embebido) {
|
|
845
|
-
console.log(' el código del repositorio no se toca: en modo embebido la instancia es él mismo.')
|
|
846
|
-
}
|
|
847
|
-
console.log('\nNada de esto lo repone un init. Si es lo que querés: repetí con --force.')
|
|
848
|
-
process.exit(1)
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
for (const runner of perdida.runners) {
|
|
852
|
-
try { A.uninstall(root, runner, console) } catch (error) { console.error(` ${runner}: ${error.message}`) }
|
|
853
|
-
}
|
|
854
|
-
// El orden no es negociable: primero el wiring, después la carpeta. Al revés, cada llamada de
|
|
855
|
-
// herramienta del runner queda ejecutando un guard que ya no está.
|
|
856
|
-
// En modo embebido la instancia **es** el repositorio: borrar la carpeta se lleva el código del
|
|
857
|
-
// producto, que Cauce nunca escribió y no repone nadie. Ahí se saca lo del toolkit y se deja el repo.
|
|
858
|
-
if (O.mode(root) === 'embedded') {
|
|
859
|
-
const suyo = [
|
|
860
|
-
'planning', 'organization', 'teams', 'integrations', 'automatization', 'tools',
|
|
861
|
-
'ops.config.json', '.cauce', 'AGENTS.md',
|
|
862
|
-
]
|
|
863
|
-
for (const relative of suyo) fs.rmSync(path.join(root, relative), { recursive: true, force: true })
|
|
864
|
-
console.log(`✓ ${suyo.length} ruta(s) de Cauce quitadas de ${root}`)
|
|
865
|
-
return console.log(' tu repositorio queda donde está: en modo embebido la instancia era él mismo.')
|
|
866
|
-
}
|
|
867
|
-
const adentro = process.cwd() === root || process.cwd().startsWith(`${root}${path.sep}`)
|
|
868
|
-
fs.rmSync(root, { recursive: true, force: true })
|
|
869
|
-
console.log(`✓ ${root} borrado`)
|
|
870
|
-
// Correrlo desde adentro es lo natural —ahí está `tools/ops.js`— y deja la terminal en un directorio
|
|
871
|
-
// que ya no existe: el próximo comando falla con un `getcwd` que no dice nada de esto.
|
|
872
|
-
if (adentro) console.log(' tu terminal quedó en esa carpeta: hacé "cd .." antes del próximo comando.')
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
// Actualiza sólo lo que el toolkit declara suyo. Todo lo demás —planning, organization, reglas
|
|
876
|
-
// propias, agentes editados— queda intacto por construcción, no por comparación.
|
|
877
|
-
function upgrade(dir, cli) {
|
|
878
|
-
const root = path.resolve(dir || '.')
|
|
879
|
-
if (!fs.existsSync(path.join(root, 'ops.config.json'))) {
|
|
880
|
-
fail(`${root} no es una instancia de Cauce: falta ops.config.json.`, 2)
|
|
881
|
-
}
|
|
882
|
-
// Acá se fabrica Cauce: `upgrade` reemplazaría con las copias de `template/` los archivos que este
|
|
883
|
-
// repositorio mantiene en la raíz —`AGENTS.md` entre ellos, que es donde vive esta misma regla—.
|
|
884
|
-
if (O.mode(root) === 'toolkit') {
|
|
885
|
-
fail(`${root} es el toolkit: acá se edita Cauce, no se lo actualiza.`, 2)
|
|
886
|
-
}
|
|
887
|
-
const dry = cli.has('--check')
|
|
888
|
-
const force = cli.has('--force')
|
|
889
|
-
const from = instanceVersion(root)
|
|
890
|
-
const to = require(path.join(PROJECT_ROOT, 'package.json')).version
|
|
891
|
-
const system = O.systemPaths(root)
|
|
892
|
-
const changed = O.localChanges(root)
|
|
893
|
-
const overrides = O.overrides(root)
|
|
894
|
-
|
|
895
|
-
if (dry) {
|
|
896
|
-
if (from === to) {
|
|
897
|
-
// Contra el motor instalado, no contra lo publicado: la comparación es local y sin red. Decirlo
|
|
898
|
-
// importa porque `init` fija la versión exacta, así que el motor no se mueve solo y esta línea,
|
|
899
|
-
// a secas, se leía como «no hay nada nuevo» durante todas las versiones siguientes.
|
|
900
|
-
console.log(`= ${to}: la instancia está al día con el motor instalado`)
|
|
901
|
-
return console.log(' para traer una versión más nueva: npm install --save-dev @ingeniomaps/cauce@latest')
|
|
902
|
-
}
|
|
903
|
-
// Hacia atrás también es legítimo —una versión rompió algo y se vuelve—, pero anunciarlo como «hay
|
|
904
|
-
// una versión más nueva» era mentir con el número a la vista. Y lo que corresponde imprimir es lo
|
|
905
|
-
// contrario: no lo que se gana, sino lo que se deja.
|
|
906
|
-
if (CL.compare(to, from) < 0) {
|
|
907
|
-
console.log(`↩ volvés a ${to} desde ${from}. Esto es lo que dejás de tener:`)
|
|
908
|
-
printChangelog(to, from)
|
|
909
|
-
} else {
|
|
910
|
-
console.log(`⚠ hay una versión más nueva: ${to} (la instancia tiene ${from || 'una previa'})`)
|
|
911
|
-
printChangelog(from, to)
|
|
912
|
-
}
|
|
913
|
-
for (const file of changed) console.log(` editado localmente: ${file}`)
|
|
914
|
-
process.exit(1)
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// Antes de retirar nada, comprobar que no se lleve puesto aprendizaje acumulado.
|
|
918
|
-
const rescatar = O.retiredWithLearning(root)
|
|
919
|
-
if (rescatar.length && !force) {
|
|
920
|
-
for (const file of rescatar) console.error(`✗ ${file}`)
|
|
921
|
-
fail(
|
|
922
|
-
`\n${rescatar.length} archivo(s) de aprendizaje quedaron en una ruta que Cauce ya no mantiene.\n\n` +
|
|
923
|
-
'Movelos a un cargo propio en agents/roles/<slug>/learning/ y repetí, o descartalos con --force.',
|
|
924
|
-
)
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
if (changed.length && !force) {
|
|
928
|
-
for (const file of changed) console.error(`✗ ${file}`)
|
|
929
|
-
const reglas = changed.filter((file) => file.includes('/system/'))
|
|
930
|
-
const runtime = changed.filter((file) => !file.includes('/system/'))
|
|
931
|
-
const guia = []
|
|
932
|
-
if (reglas.length) {
|
|
933
|
-
guia.push(
|
|
934
|
-
'Las reglas y decisiones bajo system/ son del toolkit. Para cambiar una, escribí la tuya al\n'
|
|
935
|
-
+ 'lado con el mismo ID: el proyecto manda y `check` lo reporta como override explícito.',
|
|
936
|
-
)
|
|
937
|
-
}
|
|
938
|
-
if (runtime.length) {
|
|
939
|
-
guia.push(
|
|
940
|
-
'El runtime es del toolkit: en vez de editarlo, agregá lo tuyo al lado con otro nombre —un\n'
|
|
941
|
-
+ 'guard propio sobrevive a cada actualización— y registralo en la configuración de tu runner,\n'
|
|
942
|
-
+ 'que sí es del proyecto. Para desactivar un guard alcanza con quitarlo de esa configuración.',
|
|
943
|
-
)
|
|
944
|
-
}
|
|
945
|
-
fail(
|
|
946
|
-
`\n${changed.length} archivo(s) que mantiene Cauce fueron editados y se perderían.\n\n` +
|
|
947
|
-
`${guia.join('\n\n')}\n\nSi el cambio ya no te sirve, repetí con --force para descartarlo.`,
|
|
948
|
-
)
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
for (const relative of [...system, ...O.RUNTIME_PATHS]) {
|
|
952
|
-
const origin = path.join(PROJECT_ROOT, O.sourceOf(relative))
|
|
953
|
-
if (!fs.existsSync(origin)) continue
|
|
954
|
-
const target = path.join(root, relative)
|
|
955
|
-
// Sobrescribe lo que trae el paquete y deja intacto lo demás: un guard propio de la empresa,
|
|
956
|
-
// o un adaptador de runner que el toolkit no conoce, sobreviven a la actualización.
|
|
957
|
-
if (fs.statSync(origin).isDirectory()) copyRuntime(origin, target, false, root)
|
|
958
|
-
else {
|
|
959
|
-
F.assertNoSymlinkPath(root, target)
|
|
960
|
-
F.atomicWrite(target, fs.readFileSync(origin, 'utf8'))
|
|
961
|
-
// El modo también viene del paquete: `tools/ops.js` tiene shebang y sin esto cada upgrade lo
|
|
962
|
-
// dejaba sin permiso de ejecución, con el cambio de modo apareciendo en el diff de la empresa.
|
|
963
|
-
fs.chmodSync(target, fs.statSync(origin).mode & 0o777)
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// Retirar lo que el toolkit ya no distribuye, después de haber actualizado lo que sí.
|
|
968
|
-
const retirado = []
|
|
969
|
-
for (const relative of O.RETIRED) {
|
|
970
|
-
const target = path.join(root, relative)
|
|
971
|
-
if (!fs.existsSync(target)) continue
|
|
972
|
-
F.assertNoSymlinkPath(root, target)
|
|
973
|
-
fs.rmSync(target, { recursive: true, force: true })
|
|
974
|
-
retirado.push(relative)
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
// Dejar registrado lo que se entregó, para poder distinguir después una edición local de una
|
|
978
|
-
// mejora del toolkit.
|
|
979
|
-
let registro = M.read(root)
|
|
980
|
-
for (const relative of O.trackedPaths()) {
|
|
981
|
-
const dir = path.join(root, relative)
|
|
982
|
-
if (fs.existsSync(dir)) registro = M.record(root, relative, O.treeFiles(dir), registro)
|
|
983
|
-
}
|
|
984
|
-
registro = M.recordPaths(root, O.SYSTEM_FILES, registro)
|
|
985
|
-
// El registro de forks se poda igual que el de archivos: un cargo devuelto al catálogo deja su
|
|
986
|
-
// entrada, y una entrada sin copia sólo puede producir avisos sobre algo que no está.
|
|
987
|
-
const vivos = Object.fromEntries(Object.entries(M.readForks(root)).filter(
|
|
988
|
-
([slug, record]) => fs.existsSync(path.join(root, 'agents', (record || {}).type || 'roles', slug)),
|
|
989
|
-
))
|
|
990
|
-
M.write(root, M.prune(root, registro), null, vivos)
|
|
991
|
-
|
|
992
|
-
const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
|
|
993
|
-
config.cauceVersion = to
|
|
994
|
-
F.atomicWriteJson(path.join(root, 'ops.config.json'), config)
|
|
995
|
-
|
|
996
|
-
console.log(`✓ Cauce ${from || '(previa)'} → ${to}`)
|
|
997
|
-
// Descartar con --force es legítimo; hacerlo sin dejar rastro no. Queda en la salida del comando,
|
|
998
|
-
// que es la evidencia que el protocolo pide para cualquier cambio.
|
|
999
|
-
for (const file of changed) console.log(`− descartado tu cambio en ${file}`)
|
|
1000
|
-
for (const relative of retirado) console.log(`− retirado ${relative}: Cauce ya no lo distribuye`)
|
|
1001
|
-
printChangelog(from, to)
|
|
1002
|
-
console.log(` ${system.length} ruta(s) del sistema y ${O.RUNTIME_PATHS.length} del runtime actualizadas`)
|
|
1003
|
-
for (const override of overrides) {
|
|
1004
|
-
console.log(`= conservado ${override.collection}/${override.project}: sobrescribe ${override.system}`)
|
|
1005
|
-
}
|
|
1006
|
-
console.log(' planning, organization y todo lo propio quedaron intactos')
|
|
1007
|
-
// No se borra: sin la dependencia declarada, quitarle `.ops/` la dejaría sin motor. Se avisa y
|
|
1008
|
-
// decide una persona.
|
|
1009
|
-
if (fs.existsSync(path.join(root, '.ops', 'engine'))) {
|
|
1010
|
-
console.log('\n⚠ esta instancia tiene el motor vendorizado en .ops/, que Cauce ya no distribuye.')
|
|
1011
|
-
console.log(' Corré "npm install" para tenerlo como dependencia y después borrá .ops/ a mano.')
|
|
1012
|
-
}
|
|
1013
|
-
// El wiring del runner no se actualiza solo: vive fuera de la instancia y lo escribe otro comando.
|
|
1014
|
-
// Sin este recordatorio, una mejora en un workflow o en el catálogo se queda en el paquete.
|
|
1015
|
-
const runners = Object.keys(M.readRunners(root))
|
|
1016
|
-
.map((key) => key.split('/')[0])
|
|
1017
|
-
.filter((name, index, all) => all.indexOf(name) === index)
|
|
1018
|
-
for (const name of runners) {
|
|
1019
|
-
console.log(` reinstalá tu runner para que el wiring quede al día: make install-${name}`)
|
|
1020
|
-
}
|
|
1021
|
-
// Después de aplicar, no antes: recién acá el paquete tiene la versión nueva y la comparación dice
|
|
1022
|
-
// algo. Es además el momento en que alguien está mirando qué le trajo la actualización.
|
|
1023
|
-
const FK = require('../agents/fork')
|
|
1024
|
-
for (const entry of FK.drift(root)) console.log(` ⚠ ${FK.driftLine(entry)}`)
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
// La raíz ops de un comando que no la recibe. El shim `tools/ops.js` la exporta porque sabe dónde
|
|
1028
|
-
// vive: sin eso, invocarlo desde otra carpeta —lo normal en sidecar— la resolvía contra el cwd.
|
|
1029
|
-
function opsRoot(dir) {
|
|
1030
|
-
return path.resolve(dir || process.env.OPS_ROOT || '.')
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
function agentsFork(slug, dir) {
|
|
1034
|
-
const root = opsRoot(dir)
|
|
1035
|
-
if (!slug) fail('Falta el cargo: ops agents fork <cargo> [ops-root]', 2)
|
|
1036
|
-
let result
|
|
1037
|
-
const date = new Date().toISOString().slice(0, 10)
|
|
1038
|
-
try { result = require('../agents/fork').fork(root, slug, date) } catch (error) { fail(error.message, 2) }
|
|
1039
|
-
console.log(`+ ${path.relative(root, result.dir)} (${result.files.length} archivo(s))`)
|
|
1040
|
-
if (result.skipped.length) {
|
|
1041
|
-
console.log(` quedan en el catálogo: ${result.skipped.length} artefacto(s) que ganó su versión`)
|
|
1042
|
-
}
|
|
1043
|
-
console.log(` copiado de Cauce ${result.version || '(versión desconocida)'}; desde ahora lo mantenés vos`)
|
|
1044
|
-
console.log(` reinstalá tu runner para que ${slug} apunte a tu copia`)
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
// Lista los cargos visibles resolviendo la precedencia; evita que cada consumidor —CI incluido—
|
|
1048
|
-
// reimplemente el recorrido del catálogo.
|
|
1049
|
-
function agents(action, dir, extra, cli) {
|
|
1050
|
-
if (action === 'fork') return agentsFork(dir, extra)
|
|
1051
|
-
if (action !== 'list') fail(`Acción de agents desconocida: ${action || '(vacía)'}`, 2)
|
|
1052
|
-
const root = opsRoot(dir)
|
|
1053
|
-
// Una empresa mantiene sus cargos, no los nuestros: `learn` sobre uno del catálogo se niega, así que
|
|
1054
|
-
// recorrer los 48 para encontrar el suyo es ruido. `--own` es lo que hace ejecutable ese recorrido.
|
|
1055
|
-
const own = cli.has('--own')
|
|
1056
|
-
const system = cli.has('--system')
|
|
1057
|
-
const roles = AG.list(root).filter((role) => (own ? !role.system : true) && (system ? role.system : true))
|
|
1058
|
-
if (cli.has('--json')) {
|
|
1059
|
-
// `path` viene resuelto: quien consuma esto no debería reconstruir dónde ganó la precedencia.
|
|
1060
|
-
return console.log(JSON.stringify(roles.map((role) => ({
|
|
1061
|
-
slug: role.slug, type: role.type, system: role.system, summary: role.summary,
|
|
1062
|
-
path: path.relative(root, role.dir).split(path.sep).join('/'),
|
|
1063
|
-
}))))
|
|
1064
|
-
}
|
|
1065
|
-
// Una línea por cargo, alineadas, para elegir a quién asignarle una tarea sin abrir 47 carpetas.
|
|
1066
|
-
const ancho = roles.reduce((max, role) => Math.max(max, role.slug.length), 0)
|
|
1067
|
-
for (const role of roles) {
|
|
1068
|
-
const marca = role.system ? '' : ' (propio)'
|
|
1069
|
-
console.log(`${role.slug.padEnd(ancho)}${marca} ${role.summary || '— sin resumen'}`)
|
|
1070
|
-
}
|
|
1071
|
-
// La respuesta negativa tiene que ser tan barata como la positiva: si ninguna línea encaja, el
|
|
1072
|
-
// camino no es forzar el cargo más parecido, es escribir el propio.
|
|
1073
|
-
if (roles.length && !own) {
|
|
1074
|
-
console.log('\nSi ninguno encaja, escribí el tuyo en agents/roles/<slug>/ — es tuyo y gana sobre '
|
|
1075
|
-
+ 'el catálogo.\nSi encaja uno pero lo querés más enfocado en tu empresa: '
|
|
1076
|
-
+ 'organization/roles/<slug>.md para el contexto, u "ops agents fork <slug>" para adoptarlo.')
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
function archive(dir, rawNum) {
|
|
1081
|
-
const root = path.resolve(dir || '.')
|
|
1082
|
-
const num = String(rawNum || '').padStart(3, '0')
|
|
1083
|
-
if (!/^\d{3}$/.test(num)) fail('La épica debe ser NNN.', 2)
|
|
1084
|
-
const epic = P.readEpics(root).find((candidate) => candidate.num === num)
|
|
1085
|
-
if (!epic) fail(`No existe epic-${num}.`, 2)
|
|
1086
|
-
if (epic.status !== 'closed') fail(`epic-${num} no está cerrada (status: ${epic.status}).`)
|
|
1087
|
-
const target = path.join(root, 'done', `epic-${num}.md`)
|
|
1088
|
-
const source = path.join(root, 'DONE.md')
|
|
1089
|
-
const content = P.read(source)
|
|
1090
|
-
const slugs = new Set(epic.stories.map((story) => story.slug))
|
|
1091
|
-
const entries = P.readDone(root).entries.filter((entry) => entry.source === 'DONE.md' && slugs.has(entry.slug))
|
|
1092
|
-
if (!entries.length) {
|
|
1093
|
-
if (fs.existsSync(target)) return console.log(`= epic-${num} ya estaba archivada`)
|
|
1094
|
-
fail(`No hay entradas de epic-${num} en DONE.md.`)
|
|
1095
|
-
}
|
|
1096
|
-
let updated = content
|
|
1097
|
-
for (const entry of entries) updated = updated.replace(entry.raw, '').replace(/\n{3,}/g, '\n\n')
|
|
1098
|
-
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
1099
|
-
if (!fs.existsSync(target)) {
|
|
1100
|
-
F.atomicWrite(
|
|
1101
|
-
target,
|
|
1102
|
-
`---\nepic: ${num}\nstatus: archived\n---\n\n# DONE — ${epic.title}\n\n` +
|
|
1103
|
-
`${entries.map((entry) => entry.raw).join('\n\n')}\n`,
|
|
1104
|
-
)
|
|
1105
|
-
}
|
|
1106
|
-
F.atomicWrite(source, `${updated.trimEnd()}\n`)
|
|
1107
|
-
console.log(`✓ epic-${num}: ${entries.length} entrada(s) archivadas`)
|
|
141
|
+
function usage() {
|
|
142
|
+
console.log(`Uso:
|
|
143
|
+
ops init [destino] [--name <nombre>] [--mode embedded|sidecar] [--force]
|
|
144
|
+
[--runner claude|codex|gemini|antigravity] [--integration <proveedor>] [--install|--no-install]
|
|
145
|
+
ops scan [workspace] [--json]
|
|
146
|
+
ops onboard [ops-root] [--json]
|
|
147
|
+
ops check <planning-dir> [--json]
|
|
148
|
+
ops tree <planning-dir> [--no-color] [--json]
|
|
149
|
+
ops context <planning-dir> [--json]
|
|
150
|
+
ops upgrade <ops-root> [--check] [--force]
|
|
151
|
+
ops destroy <ops-root> [--force]
|
|
152
|
+
ops archive <planning-dir> <NNN|human-actions>
|
|
153
|
+
ops integration list <ops-root>
|
|
154
|
+
ops integration enable <ops-root> <provider>
|
|
155
|
+
ops integration disable <ops-root> <provider>
|
|
156
|
+
ops integration check <ops-root> [provider]
|
|
157
|
+
ops integration sync <ops-root> <provider> [--fixture <json>]
|
|
158
|
+
ops integration promote <ops-root> <provider> <remote-key>
|
|
159
|
+
ops integration reset <ops-root> <provider> <remote-key>
|
|
160
|
+
ops integration rebase <ops-root> <provider> <remote-key>
|
|
161
|
+
ops integration reconcile <ops-root> <provider> <remote-key>
|
|
162
|
+
ops integration writeback-plan <ops-root> <provider>
|
|
163
|
+
ops automation list <ops-root>
|
|
164
|
+
ops automation list-hooks <ops-root>
|
|
165
|
+
ops automation check <ops-root>
|
|
166
|
+
ops automation doctor <ops-root> claude|codex|gemini|antigravity
|
|
167
|
+
ops automation install <ops-root> claude|codex|gemini|antigravity
|
|
168
|
+
ops automation uninstall <ops-root> claude|codex|gemini|antigravity
|
|
169
|
+
ops learn <agent|flow> [--flow] [--proposal [--period <AAAA-MM>]] [--applied [--period <AAAA-MM>]]
|
|
170
|
+
ops evaluate <agent|flow> [--flow] [--cases [--json]] [--bench [caso]] [--record [AAAA-MM-DD]]
|
|
171
|
+
ops agents list [ops-root] [--own|--system] [--json]
|
|
172
|
+
ops agents fork <cargo> [ops-root]
|
|
173
|
+
ops flow list
|
|
174
|
+
ops flow check <flow>
|
|
175
|
+
ops flow show <flow>`)
|
|
1108
176
|
}
|
|
1109
177
|
|
|
1110
|
-
//
|
|
1111
|
-
//
|
|
1112
|
-
|
|
1113
|
-
const file = path.join(root, 'integrations', 'config.json')
|
|
1114
|
-
try { return { file, config: JSON.parse(fs.readFileSync(file, 'utf8')) } } catch (error) {
|
|
1115
|
-
return fail(`integrations/config.json ilegible: ${error.message}`)
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
178
|
+
// Un `cli` que no tiene banderas, para reusar un comando desde otro: el `--force` de `init` habla del
|
|
179
|
+
// molde y no del wiring del runner, así que pasarle el suyo instalaría a la fuerza algo que nadie pidió.
|
|
180
|
+
const NO_FLAGS = { has: () => false, value: (_flag, fallback = '') => fallback }
|
|
1118
181
|
|
|
1119
182
|
// Si el proveedor terminó su propia configuración. Son dos interruptores y `sync` exige los dos: el
|
|
1120
|
-
// del registro dice que está conectado al proyecto, éste que hay a dónde apuntar.
|
|
1121
|
-
function providerReady(root, name) {
|
|
1122
|
-
try {
|
|
1123
|
-
const suyo = path.join(root, 'integrations', name, 'config.json')
|
|
1124
|
-
return JSON.parse(fs.readFileSync(suyo, 'utf8')).enabled === true
|
|
1125
|
-
} catch { return false }
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
function switchProvider(root, provider, enabled) {
|
|
1129
|
-
const { file, config } = providerRegistry(root)
|
|
1130
|
-
if (!config.providers || !config.providers[provider]) {
|
|
1131
|
-
fail(`${provider} no está en integrations/config.json.`)
|
|
1132
|
-
}
|
|
1133
|
-
config.providers[provider].enabled = enabled
|
|
1134
|
-
F.atomicWriteJson(file, config)
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
// Una entrada por acción, con lo que exige antes de correr. Eran nueve `if` seguidos, cada uno
|
|
1138
|
-
// repitiendo su propia validación de argumentos; acá el despachador la hace una vez y lee de la tabla.
|
|
1139
|
-
const INTEGRATION = {
|
|
1140
|
-
list: {
|
|
1141
|
-
run: (root) => {
|
|
1142
|
-
for (const [name, entry] of Object.entries(providerRegistry(root).config.providers || {})) {
|
|
1143
|
-
const listo = providerReady(root, name)
|
|
1144
|
-
const estado = !entry.enabled ? '○' : (listo ? '●' : '◐')
|
|
1145
|
-
const nota = estado === '◐'
|
|
1146
|
-
? ` — falta completar integrations/${name}/config.json y poner enabled: true`
|
|
1147
|
-
: ''
|
|
1148
|
-
console.log(`${estado} ${name} [${entry.adapter}]${nota}`)
|
|
1149
|
-
}
|
|
1150
|
-
},
|
|
1151
|
-
},
|
|
1152
|
-
enable: {
|
|
1153
|
-
falta: 'Falta <provider>.',
|
|
1154
|
-
run: (root, provider) => {
|
|
1155
|
-
const source = path.join(PROJECT_ROOT, 'template', 'integrations', provider)
|
|
1156
|
-
if (!fs.existsSync(source)) fail(`Cauce no trae un adaptador para ${provider}.`, 2)
|
|
1157
|
-
// Habilitar no es inicializar: repone lo que falte y conserva lo que ya esté. Una instancia que
|
|
1158
|
-
// trae el andamiaje de una versión anterior —o que ya tiene snapshots— sólo quiere el interruptor.
|
|
1159
|
-
providerRegistry(root)
|
|
1160
|
-
copyTemplate(source, path.join(root, 'integrations', provider), {}, true)
|
|
1161
|
-
switchProvider(root, provider, true)
|
|
1162
|
-
console.log(`✓ ${provider}: conectado al proyecto y andamiaje en integrations/${provider}/.`)
|
|
1163
|
-
// Sólo se pide lo que falta: reencender un proveedor ya configurado no debería mandar a
|
|
1164
|
-
// completar un archivo que la empresa terminó hace meses.
|
|
1165
|
-
if (providerReady(root, provider)) {
|
|
1166
|
-
console.log(` Su configuración ya estaba completa: "integration sync" puede correr.`)
|
|
1167
|
-
return
|
|
1168
|
-
}
|
|
1169
|
-
console.log(` Falta lo tuyo: completá integrations/${provider}/config.json y poné enabled: true ahí.`)
|
|
1170
|
-
console.log(` Hasta entonces "integration sync" se niega, que es lo correcto: no hay a dónde apuntar.`)
|
|
1171
|
-
},
|
|
1172
|
-
},
|
|
1173
|
-
// Apagar no desinstala: `integrations/<proveedor>/` puede tener snapshots y borradores de la
|
|
1174
|
-
// empresa, y borrarlos para desconectar una integración sería perder trabajo suyo. El andamiaje
|
|
1175
|
-
// queda, callado, y volver a encenderlo no pierde nada.
|
|
1176
|
-
disable: {
|
|
1177
|
-
falta: 'Falta <provider>.',
|
|
1178
|
-
run: (root, provider) => {
|
|
1179
|
-
switchProvider(root, provider, false)
|
|
1180
|
-
console.log(`✓ ${provider}: desconectado del proyecto. "integration sync" deja de correrlo.`)
|
|
1181
|
-
if (fs.existsSync(path.join(root, 'integrations', provider))) {
|
|
1182
|
-
console.log(` integrations/${provider}/ queda como está: ahí pueden vivir snapshots y borradores tuyos.`)
|
|
1183
|
-
}
|
|
1184
|
-
},
|
|
1185
|
-
},
|
|
1186
|
-
check: {
|
|
1187
|
-
run: (root, provider) => {
|
|
1188
|
-
const result = I.validate(root, provider || '')
|
|
1189
|
-
for (const warning of result.warnings) console.warn(`⚠ ${warning}`)
|
|
1190
|
-
for (const error of result.errors) console.error(`✗ ${error}`)
|
|
1191
|
-
if (result.errors.length) fail(`${result.errors.length} error(es) de integración`)
|
|
1192
|
-
console.log(`✓ integraciones válidas${provider ? `: ${provider}` : ''}`)
|
|
1193
|
-
},
|
|
1194
|
-
},
|
|
1195
|
-
sync: {
|
|
1196
|
-
falta: 'sync exige <provider>',
|
|
1197
|
-
run: async (root, provider, key, cli) => {
|
|
1198
|
-
const result = await I.sync(root, provider, { fixture: cli.value('--fixture') })
|
|
1199
|
-
console.log(
|
|
1200
|
-
`✓ ${provider}: ${result.total} items · ${result.created} nuevos · ` +
|
|
1201
|
-
`${result.refreshed} refrescados · ${result.preserved} curados preservados`,
|
|
1202
|
-
)
|
|
1203
|
-
// Lo que el remoto dejó de tener sí cambia el staging, y se contaba sin decirlo: un item que
|
|
1204
|
-
// desaparece se borra o queda marcado según tenga curación. Se nombra sólo cuando pasó, porque
|
|
1205
|
-
// en la corrida normal los dos son cero y anunciarlo cada vez es ruido.
|
|
1206
|
-
if (result.removed) console.log(` − ${result.removed} sin curar se fueron del remoto y se borraron`)
|
|
1207
|
-
if (result.missing) {
|
|
1208
|
-
console.log(` ⚠ ${result.missing} con curación ya no están en el remoto: quedan marcados`)
|
|
1209
|
-
}
|
|
1210
|
-
},
|
|
1211
|
-
},
|
|
1212
|
-
promote: {
|
|
1213
|
-
falta: 'promote exige <provider> <remote-key>',
|
|
1214
|
-
exigeKey: true,
|
|
1215
|
-
run: (root, provider, key) => {
|
|
1216
|
-
const result = I.promote(root, provider, key)
|
|
1217
|
-
console.log(`✓ ${provider}:${result.key} promovido como ${result.kind}`)
|
|
1218
|
-
},
|
|
1219
|
-
},
|
|
1220
|
-
'writeback-plan': {
|
|
1221
|
-
falta: 'writeback-plan exige <provider>',
|
|
1222
|
-
run: (root, provider) => console.log(JSON.stringify(I.writebackPlan(root, provider), null, 2)),
|
|
1223
|
-
},
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
// Las tres reconciliaciones son el mismo comando con otra operación: se declaran en el mismo lugar
|
|
1227
|
-
// para que agregar una cuarta no pida tocar el despachador.
|
|
1228
|
-
for (const operacion of ['reset', 'rebase', 'reconcile']) {
|
|
1229
|
-
INTEGRATION[operacion] = {
|
|
1230
|
-
falta: `${operacion} exige <provider> <remote-key>`,
|
|
1231
|
-
exigeKey: true,
|
|
1232
|
-
run: (root, provider, key) => {
|
|
1233
|
-
const changed = I.reconcile(root, provider, operacion, [key])
|
|
1234
|
-
console.log(`✓ ${provider}: ${operacion} aplicado a ${changed.join(', ')}`)
|
|
1235
|
-
},
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
async function integration(action, rootArg, provider, key, cli) {
|
|
1240
|
-
const paso = INTEGRATION[action]
|
|
1241
|
-
if (!paso) fail(`Acción de integración desconocida: ${action || '(vacía)'}`, 2)
|
|
1242
|
-
if (paso.falta && (!provider || (paso.exigeKey && !key))) fail(paso.falta, 2)
|
|
1243
|
-
await paso.run(path.resolve(rootArg || '.'), provider, key, cli)
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function automation(action, rootArg, runnerName, cli) {
|
|
1247
|
-
const root = opsRoot(rootArg)
|
|
1248
|
-
if (action === 'list-hooks') return A.listHooks()
|
|
1249
|
-
if (action === 'list') {
|
|
1250
|
-
for (const name of A.RUNNER_NAMES) {
|
|
1251
|
-
const runner = A.runnerManifest(root, name)
|
|
1252
|
-
const installed = fs.existsSync(path.join(root, runner.config.target))
|
|
1253
|
-
const capabilities = Object.entries(runner.capabilities)
|
|
1254
|
-
.filter(([, enabled]) => enabled)
|
|
1255
|
-
.map(([capability]) => capability)
|
|
1256
|
-
.join(', ')
|
|
1257
|
-
console.log(`${installed ? '●' : '○'} ${name}${installed ? ' [instalado]' : ''} · ${capabilities}`)
|
|
1258
|
-
}
|
|
1259
|
-
return
|
|
1260
|
-
}
|
|
1261
|
-
if (action === 'check') {
|
|
1262
|
-
const errors = A.check(root)
|
|
1263
|
-
for (const error of errors) console.error(`✗ ${error}`)
|
|
1264
|
-
if (errors.length) fail(`${errors.length} error(es) de automatización`)
|
|
1265
|
-
console.log(
|
|
1266
|
-
`✓ automatización válida: ${A.GUARD_NAMES.length} guards, ${A.RUNNER_NAMES.length} adaptadores`,
|
|
1267
|
-
)
|
|
1268
|
-
return
|
|
1269
|
-
}
|
|
1270
|
-
if (action === 'doctor') {
|
|
1271
|
-
let result
|
|
1272
|
-
try { result = A.doctor(root, runnerName) } catch (error) { fail(error.message, 2) }
|
|
1273
|
-
if (result.errors.length) {
|
|
1274
|
-
fail(`${runnerName}: ${result.errors.length} error(es), ${result.warnings.length} advertencia(s)`)
|
|
1275
|
-
}
|
|
1276
|
-
console.log(`✓ ${runnerName}: adaptador operativo (${result.warnings.length} advertencia(s))`)
|
|
1277
|
-
return
|
|
1278
|
-
}
|
|
1279
|
-
if (action === 'uninstall') {
|
|
1280
|
-
try { A.uninstall(root, runnerName, console) } catch (error) { fail(error.message, 2) }
|
|
1281
|
-
console.log(' la instancia sigue en pie: borrar la carpeta ops es una decisión aparte.')
|
|
1282
|
-
return
|
|
1283
|
-
}
|
|
1284
|
-
if (action === 'install') {
|
|
1285
|
-
let runner
|
|
1286
|
-
const force = cli.has('--force')
|
|
1287
|
-
try { runner = A.install(root, runnerName, console, { force }) } catch (error) { fail(error.message, 2) }
|
|
1288
|
-
if (runnerName === 'codex') {
|
|
1289
|
-
console.log(' Codex deja los hooks nuevos sin correr hasta que los confíes: abrí una sesión')
|
|
1290
|
-
console.log(' y usá /hooks para revisarlos y marcarlos como confiables.')
|
|
1291
|
-
}
|
|
1292
|
-
if (!runner.capabilities.nativeHooks) {
|
|
1293
|
-
console.log(` ${runnerName} no expone hooks nativos; aplica guards como prechecks.`)
|
|
1294
|
-
}
|
|
1295
|
-
const result = A.doctor(root, runnerName)
|
|
1296
|
-
if (result.errors.length) fail(`${runnerName}: instalación incompleta`)
|
|
1297
|
-
console.log(`✓ ${runnerName}: adaptador operativo (${result.warnings.length} advertencia(s))`)
|
|
1298
|
-
return
|
|
1299
|
-
}
|
|
1300
|
-
fail(`Acción de automatización desconocida: ${action || '(vacía)'}`, 2)
|
|
1301
|
-
}
|
|
1302
|
-
function learn(agent, cli) {
|
|
1303
|
-
try {
|
|
1304
|
-
// Sellar es determinista y por eso vive acá y no en el recorrido: marcar una propuesta como
|
|
1305
|
-
// aplicada editando frontmatter a mano es justo el paso que un agente hace mal en silencio.
|
|
1306
|
-
if (cli.has('--applied')) {
|
|
1307
|
-
const result = L.seal(opsRoot(), agent, cli.value('--period'))
|
|
1308
|
-
const relative = path.relative(opsRoot(), result.file)
|
|
1309
|
-
return console.log(result.already
|
|
1310
|
-
? `= ${relative} ya estaba aplicada`
|
|
1311
|
-
: `✓ ${relative} queda aplicada: no se vuelve a aplicar`)
|
|
1312
|
-
}
|
|
1313
|
-
const result = cli.has('--proposal')
|
|
1314
|
-
? L.prepareProposal(opsRoot(), agent)
|
|
1315
|
-
: L.prepareReport(opsRoot(), agent)
|
|
1316
|
-
console.log(`${result.created ? '+' : '='} ${path.relative(opsRoot(), result.file)}`)
|
|
1317
|
-
if (typeof result.reports === 'number') console.log(` ${result.reports} informe(s) semanal(es) incluidos`)
|
|
1318
|
-
} catch (error) { fail(error.message, 2) }
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
function evaluate(agent, caso, cli) {
|
|
1322
|
-
const root = opsRoot()
|
|
1323
|
-
// El banco sólo tiene sentido acá: en una empresa el cargo que se evalúa es suyo —propio o
|
|
1324
|
-
// adoptado— y su `planning/` ya es el lugar legítimo donde trabajar.
|
|
1325
|
-
if (cli.has('--bench')) {
|
|
1326
|
-
if (O.mode(root) !== 'toolkit') {
|
|
1327
|
-
fail('--bench es del toolkit. En una instancia, el cargo trabaja sobre tu planning/: si es del '
|
|
1328
|
-
+ `catálogo, adoptalo primero con "ops agents fork ${agent}".`, 2)
|
|
1329
|
-
}
|
|
1330
|
-
// El caso es el posicional que sigue al cargo: `evaluate <cargo> --bench <caso>`. Sin él se arma
|
|
1331
|
-
// un banco suelto, para mirarlo a mano; una corrida real pide uno por caso.
|
|
1332
|
-
return console.log(evaluationBench(root, agent, caso, cli.has('--force')))
|
|
1333
|
-
}
|
|
1334
|
-
try {
|
|
1335
|
-
// Los casos, para que un recorrido los ejecute. Sin `--json` no tiene sentido: es entrada de
|
|
1336
|
-
// máquina, no de persona.
|
|
1337
|
-
if (cli.has('--cases')) {
|
|
1338
|
-
const cases = EV.list(root, agent)
|
|
1339
|
-
const prohibido = EV.behaviors(root, agent).forbidden
|
|
1340
|
-
// La salida legible no lleva la conducta prohibida: `agent-propose` cuenta sus líneas para saber
|
|
1341
|
-
// cuántos casos hay, y una línea de más se contaría como un caso.
|
|
1342
|
-
if (!cli.has('--json')) {
|
|
1343
|
-
for (const item of cases) console.log(`${item.id} ${item.expected.length} comportamiento(s)`)
|
|
1344
|
-
return
|
|
1345
|
-
}
|
|
1346
|
-
// La conducta prohibida viaja junto a los casos y no dentro de cada uno: rige para los seis, y
|
|
1347
|
-
// repetirla por caso invitaría a que alguien la editara en uno solo.
|
|
1348
|
-
return console.log(JSON.stringify({ cases, forbidden: prohibido }))
|
|
1349
|
-
}
|
|
1350
|
-
// Dónde escribir el registro de esta corrida. Lo pregunta el recorrido en vez de componer el
|
|
1351
|
-
// nombre, que es lo que hacía que la segunda corrida de un día borrara a la primera.
|
|
1352
|
-
if (cli.has('--record')) {
|
|
1353
|
-
const dia = cli.value('--record') || new Date().toISOString().slice(0, 10)
|
|
1354
|
-
return console.log(path.relative(root, path.join(EV.resultsDir(root, agent), EV.nextResult(root, agent, dia))))
|
|
1355
|
-
}
|
|
1356
|
-
const result = L.evaluate(root, agent)
|
|
1357
|
-
const runs = EV.validate(root, agent)
|
|
1358
|
-
const errors = [...result.errors, ...runs.errors]
|
|
1359
|
-
for (const warning of runs.warnings) console.warn(`⚠ ${warning}`)
|
|
1360
|
-
for (const error of errors) console.error(`✗ ${error}`)
|
|
1361
|
-
if (errors.length) fail(`\n${errors.length} error(es)`, 1)
|
|
1362
|
-
const corrida = runs.last ? `${runs.last.passed}/${runs.last.total} pasan (${runs.last.date})` : 'sin correr'
|
|
1363
|
-
console.log(
|
|
1364
|
-
`✓ ${agent}: ${result.cases} caso(s) — ${corrida}, ${result.proposals} propuesta(s)` +
|
|
1365
|
-
`${result.pending ? ` (${result.pending} sin aplicar)` : ''}, ` +
|
|
1366
|
-
'controles estructurales válidos',
|
|
1367
|
-
)
|
|
1368
|
-
} catch (error) { fail(error.message, 2) }
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
function team(action, slug, cli) {
|
|
1372
|
-
if (action === 'list') {
|
|
1373
|
-
for (const name of T.list(opsRoot())) console.log(name)
|
|
1374
|
-
return
|
|
1375
|
-
}
|
|
1376
|
-
if (!['check', 'show'].includes(action)) fail(`Acción de team desconocida: ${action || '(vacía)'}`, 2)
|
|
1377
|
-
try {
|
|
1378
|
-
const result = T.validate(opsRoot(), slug)
|
|
1379
|
-
for (const error of result.errors) console.error(`✗ ${error}`)
|
|
1380
|
-
if (result.errors.length) fail(`${slug}: ${result.errors.length} error(es)`, 1)
|
|
1381
|
-
if (action === 'show') {
|
|
1382
|
-
// El manifiesto entero, para que un workflow ejecute las etapas sin que un modelo lo parsee.
|
|
1383
|
-
if (cli.has('--json')) return console.log(JSON.stringify(result.manifest))
|
|
1384
|
-
console.log(`${result.manifest.name} (${slug})`)
|
|
1385
|
-
console.log(result.manifest.purpose)
|
|
1386
|
-
for (const stage of result.manifest.stages) {
|
|
1387
|
-
console.log(`- ${stage.id}: ${stage.agent} → ${stage.produces.join(', ')}`)
|
|
1388
|
-
}
|
|
1389
|
-
} else {
|
|
1390
|
-
console.log(`✓ ${slug}: ${result.stages} etapa(s), ${result.agents} agente(s), contrato válido`)
|
|
1391
|
-
}
|
|
1392
|
-
} catch (error) { fail(error.message, 2) }
|
|
1393
|
-
}
|
|
1394
183
|
|
|
1395
184
|
async function run(cli) {
|
|
1396
185
|
const [command] = cli.positional
|
|
@@ -1399,29 +188,29 @@ async function run(cli) {
|
|
|
1399
188
|
// `--help` valía sólo como primer argumento: `check --help` corría `check` contra el directorio
|
|
1400
189
|
// actual en vez de explicarse.
|
|
1401
190
|
if (cli.has('--help') || cli.has('-h')) return usage()
|
|
1402
|
-
const
|
|
1403
|
-
if (
|
|
1404
|
-
const
|
|
1405
|
-
fail(`${command}: bandera desconocida ${
|
|
191
|
+
const unknown = cli.unknown(command)
|
|
192
|
+
if (unknown.length) {
|
|
193
|
+
const accepts = FLAGS[command].length ? `Acepta: ${FLAGS[command].join(', ')}.` : 'No acepta banderas.'
|
|
194
|
+
fail(`${command}: bandera desconocida ${unknown.join(', ')}. ${accepts}`, 2)
|
|
1406
195
|
}
|
|
1407
196
|
const arg = cli.positional
|
|
1408
197
|
if (command === 'init') await init(arg[1], cli)
|
|
1409
|
-
else if (command === 'scan') scan(arg[1], cli)
|
|
1410
|
-
else if (command === 'onboard') onboard(arg[1], cli)
|
|
1411
|
-
else if (command === 'check') check(arg[1], cli)
|
|
1412
|
-
else if (command === 'tree') tree(arg[1], cli)
|
|
1413
|
-
else if (command === 'context') context(arg[1], cli)
|
|
1414
|
-
else if (command === 'upgrade') upgrade(arg[1], cli)
|
|
1415
|
-
else if (command === 'destroy') destroy(arg[1], cli)
|
|
1416
|
-
else if (command === 'agents') agents(arg[1], arg[2], arg[3], cli)
|
|
1417
|
-
else if (command === 'archive') archive(arg[1], arg[2])
|
|
198
|
+
else if (command === 'scan') W.scan(arg[1], cli)
|
|
199
|
+
else if (command === 'onboard') W.onboard(arg[1], cli)
|
|
200
|
+
else if (command === 'check') PL.check(arg[1], cli)
|
|
201
|
+
else if (command === 'tree') PL.tree(arg[1], cli)
|
|
202
|
+
else if (command === 'context') PL.context(arg[1], cli)
|
|
203
|
+
else if (command === 'upgrade') IN.upgrade(arg[1], cli)
|
|
204
|
+
else if (command === 'destroy') IN.destroy(arg[1], cli)
|
|
205
|
+
else if (command === 'agents') CAT.agents(arg[1], arg[2], arg[3], cli)
|
|
206
|
+
else if (command === 'archive') PL.archive(arg[1], arg[2])
|
|
1418
207
|
else if (command === 'integration') {
|
|
1419
|
-
await integration(arg[1], arg[2], arg[3], arg[4], cli)
|
|
208
|
+
await W.integration(arg[1], arg[2], arg[3], arg[4], cli)
|
|
1420
209
|
}
|
|
1421
|
-
else if (command === 'automation') automation(arg[1], arg[2], arg[3], cli)
|
|
1422
|
-
else if (command === 'learn') learn(arg[1], cli)
|
|
1423
|
-
else if (command === 'evaluate') evaluate(arg[1], arg[2], cli)
|
|
1424
|
-
else if (command === '
|
|
210
|
+
else if (command === 'automation') W.automation(arg[1], arg[2], arg[3], cli)
|
|
211
|
+
else if (command === 'learn') CAT.learn(arg[1], cli)
|
|
212
|
+
else if (command === 'evaluate') CAT.evaluate(arg[1], arg[2], cli)
|
|
213
|
+
else if (command === 'flow') CAT.flow(arg[1], arg[2], cli)
|
|
1425
214
|
}
|
|
1426
215
|
|
|
1427
216
|
run(parse(process.argv.slice(2))).catch((error) => fail(error.message))
|