@ingeniomaps/cauce 0.39.0 → 0.40.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 +14 -0
- package/engine/automation/index.js +69 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,20 @@ desde este repositorio no va, porque el que lee no puede actuar sobre eso. Cuand
|
|
|
14
14
|
unas pocas líneas casi siempre es porque cuenta cómo se descubrió el problema o por qué se eligió el
|
|
15
15
|
diseño — eso vive en el commit y en el código.
|
|
16
16
|
|
|
17
|
+
## [0.40.0] - 2026-08-19
|
|
18
|
+
|
|
19
|
+
### Corregido
|
|
20
|
+
|
|
21
|
+
- **En modo embebido, Codex se quedaba sin instrucciones.** `AGENTS.md` es el único nombre que el runner
|
|
22
|
+
y la instancia comparten, y ahí `install` conservaba el archivo entero —lo correcto para algo del
|
|
23
|
+
proyecto—, así que el recorrido que ese runner tenía que seguir nunca llegaba a estar escrito. Ahora su
|
|
24
|
+
contenido se fusiona adentro, entre marcas: reinstalar reemplaza el bloque en vez de duplicarlo,
|
|
25
|
+
desinstalar lo saca sin llevarse el archivo, y todo lo que la empresa escribió alrededor queda intacto.
|
|
26
|
+
|
|
27
|
+
`doctor` lo tenía al revés en ese archivo: comparando el hash completo avisaba cuando el bloque estaba
|
|
28
|
+
bien —el texto de la empresa alrededor difiere siempre— y decía «operativo» cuando alguien lo había
|
|
29
|
+
borrado, que es la falla que importa.
|
|
30
|
+
|
|
17
31
|
## [0.39.0] - 2026-08-19
|
|
18
32
|
|
|
19
33
|
### Corregido
|
|
@@ -438,6 +438,19 @@ function doctor(root, name, output = console) {
|
|
|
438
438
|
}
|
|
439
439
|
for (const item of runner.instructions || []) {
|
|
440
440
|
const resolved = { item, ...resolveItem(paths, root, name, item) }
|
|
441
|
+
// El archivo compartido no se compara entero: alrededor del bloque vive el texto de la empresa, así
|
|
442
|
+
// que su hash difiere siempre. Comparado como archivo, `doctor` avisaba cuando el bloque estaba bien
|
|
443
|
+
// y callaba cuando alguien lo había borrado, que es exactamente al revés.
|
|
444
|
+
if (esArchivoCompartido(root, resolved.target)) {
|
|
445
|
+
const contenido = render(resolved.source, opsPrefix(root))
|
|
446
|
+
if (!fs.existsSync(resolved.target)) errors.push(`falta ${item.target}`)
|
|
447
|
+
else if (!fs.readFileSync(resolved.target, 'utf8').includes(bloqueInicio(name))) {
|
|
448
|
+
errors.push(`${item.target}: no tiene las instrucciones de Cauce; reinstalá el adaptador`)
|
|
449
|
+
} else if (!bloqueAlDia(resolved.target, name, contenido)) {
|
|
450
|
+
warnings.push(`${item.target}: su bloque de Cauce quedó viejo; reinstalá el adaptador`)
|
|
451
|
+
}
|
|
452
|
+
continue
|
|
453
|
+
}
|
|
441
454
|
// `AGENTS.md` es un nombre compartido entre herramientas y la instancia ya tiene el suyo: en
|
|
442
455
|
// modo embedded el archivo del runner y el de la empresa son el mismo, y pedirle que se
|
|
443
456
|
// referencie a sí mismo no significa nada.
|
|
@@ -585,6 +598,16 @@ function uninstall(root, name, output = console) {
|
|
|
585
598
|
const resolved = { item, ...resolveItem(paths, root, name, item) }
|
|
586
599
|
const key = deliveryKey(name, item.target)
|
|
587
600
|
if (!fs.existsSync(resolved.target)) { delete entregado[key]; continue }
|
|
601
|
+
if (esArchivoCompartido(root, resolved.target)) {
|
|
602
|
+
const texto = fs.readFileSync(resolved.target, 'utf8')
|
|
603
|
+
const limpio = sinBloque(texto, name)
|
|
604
|
+
if (limpio !== texto.trimEnd()) {
|
|
605
|
+
F.atomicWrite(resolved.target, `${limpio}\n`)
|
|
606
|
+
quitados += 1
|
|
607
|
+
}
|
|
608
|
+
delete entregado[key]
|
|
609
|
+
continue
|
|
610
|
+
}
|
|
588
611
|
const situacion = deliveryState(recorded, name, resolved, prefix)
|
|
589
612
|
if (situacion === 'ajeno') { conservados.push(item.target); continue }
|
|
590
613
|
removeFile(resolved.target, paths.install)
|
|
@@ -623,6 +646,41 @@ function uninstall(root, name, output = console) {
|
|
|
623
646
|
return { removed: quitados, kept: conservados }
|
|
624
647
|
}
|
|
625
648
|
|
|
649
|
+
// `AGENTS.md` es el único nombre que el runner y la instancia comparten: en modo embebido el archivo de
|
|
650
|
+
// instrucciones de Codex es el mismo que el de la empresa. Conservarlo entero —lo correcto para un
|
|
651
|
+
// archivo del proyecto— dejaba a ese runner sin una sola línea de Cauce, así que su contenido se
|
|
652
|
+
// fusiona adentro, entre marcas, y todo lo demás del archivo queda intacto.
|
|
653
|
+
const bloqueInicio = (name) => `<!-- cauce:${name} inicio — lo reescribe "automation install", no editar -->`
|
|
654
|
+
const bloqueFin = (name) => `<!-- cauce:${name} fin -->`
|
|
655
|
+
|
|
656
|
+
function esArchivoCompartido(root, target) {
|
|
657
|
+
return path.resolve(target) === path.resolve(root, 'AGENTS.md')
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function sinBloque(text, name) {
|
|
661
|
+
const desde = text.indexOf(bloqueInicio(name))
|
|
662
|
+
if (desde === -1) return text
|
|
663
|
+
const hasta = text.indexOf(bloqueFin(name), desde)
|
|
664
|
+
if (hasta === -1) return text
|
|
665
|
+
return `${text.slice(0, desde)}${text.slice(hasta + bloqueFin(name).length)}`.trimEnd()
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function fusionarInstruccion(file, name, contenido) {
|
|
669
|
+
const actual = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : ''
|
|
670
|
+
const cuerpo = sinBloque(actual, name).trimEnd()
|
|
671
|
+
const bloque = `${bloqueInicio(name)}\n\n${contenido.trim()}\n\n${bloqueFin(name)}\n`
|
|
672
|
+
F.atomicWrite(file, cuerpo ? `${cuerpo}\n\n${bloque}` : bloque)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function bloqueAlDia(file, name, contenido) {
|
|
676
|
+
if (!fs.existsSync(file)) return false
|
|
677
|
+
const texto = fs.readFileSync(file, 'utf8')
|
|
678
|
+
const desde = texto.indexOf(bloqueInicio(name))
|
|
679
|
+
const hasta = texto.indexOf(bloqueFin(name))
|
|
680
|
+
if (desde === -1 || hasta === -1) return false
|
|
681
|
+
return texto.slice(desde + bloqueInicio(name).length, hasta).trim() === contenido.trim()
|
|
682
|
+
}
|
|
683
|
+
|
|
626
684
|
function install(root, name, output = console, options = {}) {
|
|
627
685
|
// `install` arma la superficie de consumo de una empresa: punteros a cada cargo, una copia de los
|
|
628
686
|
// workflows y los guards. Acá los cargos y los workflows son el producto —la copia divergiría— y
|
|
@@ -677,6 +735,17 @@ function install(root, name, output = console, options = {}) {
|
|
|
677
735
|
for (const resolved of resolvedItems) {
|
|
678
736
|
const situacion = state.get(resolved)
|
|
679
737
|
const propio = runner.instructions.includes(resolved.item)
|
|
738
|
+
if (propio && esArchivoCompartido(root, resolved.target)) {
|
|
739
|
+
const contenido = render(resolved.source, opsPrefix(root))
|
|
740
|
+
if (bloqueAlDia(resolved.target, name, contenido)) {
|
|
741
|
+
output.log(`= ${name}: ${resolved.item.target} ya trae sus instrucciones`)
|
|
742
|
+
} else {
|
|
743
|
+
fusionarInstruccion(resolved.target, name, contenido)
|
|
744
|
+
output.log(`✓ ${name}: sus instrucciones quedaron dentro de ${resolved.item.target}`)
|
|
745
|
+
}
|
|
746
|
+
entregado[deliveryKey(name, resolved.item.target)] = M.digest(resolved.target)
|
|
747
|
+
continue
|
|
748
|
+
}
|
|
680
749
|
if (situacion === 'ajeno' && propio) {
|
|
681
750
|
output.log(`= ${name}: conservado ${resolved.item.target} (tiene cambios tuyos)`)
|
|
682
751
|
} else if (situacion === 'al día') {
|