@ingeniomaps/cauce 0.39.0 → 0.41.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 +25 -0
- package/engine/automation/index.js +69 -0
- package/engine/cli/ops.js +18 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,31 @@ 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.41.0] - 2026-08-19
|
|
18
|
+
|
|
19
|
+
### Corregido
|
|
20
|
+
|
|
21
|
+
- **`destroy` ya no borra el repositorio en modo embebido.** Ahí la instancia **es** el repositorio, así
|
|
22
|
+
que borrar la carpeta se llevaba el código del producto: pasó de verdad sobre un caso de prueba, y el
|
|
23
|
+
aviso previo enumeraba épicas y acciones humanas sin nombrar lo único irrecuperable. En embebido saca
|
|
24
|
+
lo que Cauce escribió —`planning/`, `organization/`, `teams/`, `integrations/`, `automatization/`,
|
|
25
|
+
`tools/`, la configuración y el manifiesto— y deja el repositorio. En sidecar sigue borrando la
|
|
26
|
+
carpeta, que ahí no contiene nada más.
|
|
27
|
+
|
|
28
|
+
## [0.40.0] - 2026-08-19
|
|
29
|
+
|
|
30
|
+
### Corregido
|
|
31
|
+
|
|
32
|
+
- **En modo embebido, Codex se quedaba sin instrucciones.** `AGENTS.md` es el único nombre que el runner
|
|
33
|
+
y la instancia comparten, y ahí `install` conservaba el archivo entero —lo correcto para algo del
|
|
34
|
+
proyecto—, así que el recorrido que ese runner tenía que seguir nunca llegaba a estar escrito. Ahora su
|
|
35
|
+
contenido se fusiona adentro, entre marcas: reinstalar reemplaza el bloque en vez de duplicarlo,
|
|
36
|
+
desinstalar lo saca sin llevarse el archivo, y todo lo que la empresa escribió alrededor queda intacto.
|
|
37
|
+
|
|
38
|
+
`doctor` lo tenía al revés en ese archivo: comparando el hash completo avisaba cuando el bloque estaba
|
|
39
|
+
bien —el texto de la empresa alrededor difiere siempre— y decía «operativo» cuando alguien lo había
|
|
40
|
+
borrado, que es la falla que importa.
|
|
41
|
+
|
|
17
42
|
## [0.39.0] - 2026-08-19
|
|
18
43
|
|
|
19
44
|
### 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') {
|
package/engine/cli/ops.js
CHANGED
|
@@ -831,13 +831,19 @@ function destroy(dir, cli) {
|
|
|
831
831
|
perdida.contexto && 'el contexto escrito en organization/',
|
|
832
832
|
].filter(Boolean)
|
|
833
833
|
|
|
834
|
+
const embebido = O.mode(root) === 'embedded'
|
|
834
835
|
if (!cli.has('--force')) {
|
|
835
|
-
console.log(
|
|
836
|
+
console.log(embebido
|
|
837
|
+
? `Sacar Cauce de ${root} se lleva:`
|
|
838
|
+
: `Borrar ${root} se lleva:`)
|
|
836
839
|
for (const linea of lineas) console.log(` − ${linea}`)
|
|
837
840
|
if (!lineas.length) console.log(' − nada escrito todavía: la instancia está como salió de init')
|
|
838
841
|
if (perdida.runners.length) {
|
|
839
842
|
console.log(` y saca el wiring de: ${perdida.runners.join(', ')} (lo tuyo queda donde está)`)
|
|
840
843
|
}
|
|
844
|
+
if (embebido) {
|
|
845
|
+
console.log(' el código del repositorio no se toca: en modo embebido la instancia es él mismo.')
|
|
846
|
+
}
|
|
841
847
|
console.log('\nNada de esto lo repone un init. Si es lo que querés: repetí con --force.')
|
|
842
848
|
process.exit(1)
|
|
843
849
|
}
|
|
@@ -847,6 +853,17 @@ function destroy(dir, cli) {
|
|
|
847
853
|
}
|
|
848
854
|
// El orden no es negociable: primero el wiring, después la carpeta. Al revés, cada llamada de
|
|
849
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
|
+
}
|
|
850
867
|
const adentro = process.cwd() === root || process.cwd().startsWith(`${root}${path.sep}`)
|
|
851
868
|
fs.rmSync(root, { recursive: true, force: true })
|
|
852
869
|
console.log(`✓ ${root} borrado`)
|