@ingeniomaps/cauce 0.90.0 → 0.91.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 +51 -0
- package/engine/cli/args.js +1 -0
- package/engine/cli/contract.js +127 -0
- package/engine/cli/ops.js +3 -0
- package/engine/hooks/files.js +24 -5
- package/engine/hooks/shell.js +12 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,57 @@ 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.91.0] - 2026-09-15
|
|
18
|
+
|
|
19
|
+
### Agregado
|
|
20
|
+
|
|
21
|
+
- **`ops contract <ops-root> [--json]`: el contrato de tu proyecto sin pasarlo por un modelo.** Devuelve lo
|
|
22
|
+
que un recorrido necesita antes de la primera fase —cómo se llama el proyecto, dónde puede escribir, con
|
|
23
|
+
qué se verifica, qué límites rigen y qué formatos exige `planning/`—, derivado de `ops.config.json`,
|
|
24
|
+
`AGENTS.md`, `organization/workspace.md` y `planning/PROTOCOL.md`.
|
|
25
|
+
|
|
26
|
+
Los diez campos salen de parsear, así que el comando no inventa nada y cuesta cero tokens. `autobuild` los
|
|
27
|
+
derivaba con un agente que leía esos cuatro archivos y los transcribía; el comando existe para que deje de
|
|
28
|
+
hacerlo, aunque el recorrido todavía no lo use.
|
|
29
|
+
|
|
30
|
+
**Falla en vez de contestar a medias.** Si falta uno de los cuatro archivos, o si `AGENTS.md` o
|
|
31
|
+
`PROTOCOL.md` perdieron la sección de la que sale el contrato, se niega nombrando cuál y manda a correr
|
|
32
|
+
`ops upgrade`, que es quien los repone: entregar límites vacíos es peor que parar, porque un límite que no
|
|
33
|
+
llega se lee igual que uno que no existe. Que `organization/workspace.md` no declare excepciones **no** es
|
|
34
|
+
un error: es tuyo y puede no tenerlas.
|
|
35
|
+
|
|
36
|
+
### Corregido
|
|
37
|
+
|
|
38
|
+
- **La protección que evita que un gate te borre el `node_modules` no estaba puesta.** `verify` corre los
|
|
39
|
+
gates sobre una copia del índice con el `node_modules` enlazado a tu proyecto, y le pedía a pnpm que no
|
|
40
|
+
sincronizara dependencias antes de correr el script. Se lo pedía con un nombre que pnpm ignora: sus
|
|
41
|
+
ajustes se leen del entorno con el prefijo `pnpm_config_`, no con el de npm. La petición viajaba y se
|
|
42
|
+
descartaba en silencio desde 0.75.0, que es cuando entró.
|
|
43
|
+
|
|
44
|
+
Con pnpm 11, donde esa comprobación viene encendida, se nota al commitear un cambio de `pnpm-lock.yaml`:
|
|
45
|
+
pnpm decide reinstalar, la reinstalación **empieza borrando** el directorio de módulos —el tuyo, por el
|
|
46
|
+
enlace— y sin TTY aborta con `ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY`. Los gates vuelven en poco más
|
|
47
|
+
de un segundo, así que se leen como una suite en rojo que no lo es.
|
|
48
|
+
|
|
49
|
+
Si te está pasando, alcanza con actualizar. Lo que **no** corresponde, aunque el bloqueo lo sugiera, es
|
|
50
|
+
aprobar las rutas o `OPS_SKIP_VERIFY=1` —las dos commitean sin haber corrido el gate— ni `CI=true`, que
|
|
51
|
+
desarma justamente la confirmación con la que pnpm frena antes de purgar.
|
|
52
|
+
|
|
53
|
+
- **`plan-first` decía que no tenías plan cuando lo que pasaba es que no veía el tuyo.** El guard busca el
|
|
54
|
+
WIP de tu id, y ese id sale del árbol donde corre el proceso mientras no exportes `CAUCE_RUNNER`. Con la
|
|
55
|
+
instancia al lado de dos repositorios, el mismo cambio se frenaba o pasaba según el directorio desde el
|
|
56
|
+
que saliera la llamada — y el bloqueo decía «WIP está en IDLE» con el plan escrito y a la vista, así que
|
|
57
|
+
mandaba a escribir de nuevo algo que ya existía.
|
|
58
|
+
|
|
59
|
+
Ahora distingue las dos causas: si hay un plan bajo otro id, lo nombra con su tarea y te manda a volver a
|
|
60
|
+
ese id —`ops runners <planning>` los lista— o a montar el tuyo con `ops worktree <planning> <tarea>`. Y
|
|
61
|
+
**deja de ofrecerte aprobar la ruta**, porque con un plan a la vista eso escribe por vos que el cambio no
|
|
62
|
+
es trabajo de ninguna tarea, que es falso y queda en el registro.
|
|
63
|
+
|
|
64
|
+
Lo que no cambia: el plan de otro runner sigue sin autorizarte a escribir. Si van a trabajar en paralelo,
|
|
65
|
+
cada agente necesita su árbol —`git worktree`, que es lo que `ops worktree` prepara—, y ahí el problema no
|
|
66
|
+
existe porque no comparten archivos.
|
|
67
|
+
|
|
17
68
|
## [0.90.0] - 2026-09-15
|
|
18
69
|
|
|
19
70
|
### Corregido
|
package/engine/cli/args.js
CHANGED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// El contrato del proyecto, derivado del disco. Es lo que un recorrido necesita saber antes de la primera
|
|
4
|
+
// fase —cómo se llama el proyecto, dónde puede escribir, con qué se verifica, qué límites rigen y qué
|
|
5
|
+
// formatos exige planning— y hasta 0.91.0 lo producía un agente que leía cuatro archivos y los transcribía.
|
|
6
|
+
//
|
|
7
|
+
// Transcribir no es decidir: los diez campos salen de parsear. Seis son valores de `ops.config.json`, uno
|
|
8
|
+
// es copiar una sección de `PROTOCOL.md` y dos son secciones de `AGENTS.md` y `organization/workspace.md`.
|
|
9
|
+
// Pagarle a un modelo por eso cuesta una llamada por corrida y, sobre todo, hace que lo que viaja al
|
|
10
|
+
// preámbulo de cada subagente sea **lo que alguien transcribió** en vez de lo que el archivo dice (caso 154).
|
|
11
|
+
//
|
|
12
|
+
// El porqué de resolverlo con un comando y no con un agente ya está escrito donde se decidió la primera
|
|
13
|
+
// vez: el comentario de `readContext`, en el recorrido. Acá sólo se repite la elección, no su razón.
|
|
14
|
+
|
|
15
|
+
const fs = require('node:fs')
|
|
16
|
+
const path = require('node:path')
|
|
17
|
+
const P = require('../planning/parser')
|
|
18
|
+
const { fail, opsRoot } = require('./io')
|
|
19
|
+
|
|
20
|
+
// Los cuatro que componen el contrato, con la ruta relativa a la raíz de la instancia. El orden es el que
|
|
21
|
+
// usa el mensaje de error: se nombra el primero que falte y no los cuatro, porque arreglar uno suele
|
|
22
|
+
// arreglar la causa de los demás —una raíz mal apuntada los pierde todos a la vez—.
|
|
23
|
+
const SOURCES = ['AGENTS.md', path.join('organization', 'workspace.md'), 'ops.config.json',
|
|
24
|
+
path.join('planning', 'PROTOCOL.md')]
|
|
25
|
+
|
|
26
|
+
// Qué sección sostiene cada campo de texto, y de quién es el archivo que la trae. La diferencia decide qué
|
|
27
|
+
// pasa cuando falta, y no es una preferencia: `AGENTS.md` y `PROTOCOL.md` están en `TEMPLATE_FILES`, así que
|
|
28
|
+
// `upgrade` los reemplaza enteros y su sección no puede faltar en una instancia viva —si falta, algo se
|
|
29
|
+
// rompió y seguir entregaría límites vacíos a cada subagente—. `organization/workspace.md` es del proyecto:
|
|
30
|
+
// que no declare excepciones es un estado legítimo y el molde lo dice.
|
|
31
|
+
const REQUIRED_SECTIONS = [
|
|
32
|
+
{ file: 'AGENTS.md', heading: /Autonom/, name: '## Autonomía' },
|
|
33
|
+
{ file: path.join('planning', 'PROTOCOL.md'), heading: /Contratos/, name: '## Contratos' },
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
const readIfAny = (file) => {
|
|
37
|
+
try { return fs.readFileSync(file, 'utf8') } catch { return '' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Con qué arranca un párrafo que **enuncia** un límite, frente a uno que lo explica. Es vocabulario
|
|
41
|
+
// cerrado, igual que `lane` o `blocked`, y por la misma razón: lo que sigue es una lista de la que un
|
|
42
|
+
// agente tiene que poder obedecer cada entrada, y una heurística abierta admite cualquier cosa.
|
|
43
|
+
const ENUNCIA = /^(?:El runner\b|Debe\b|Nunca\b)/
|
|
44
|
+
// Un límite por entrada y no la sección cruda. `SCOPE()` las une con `; ` en una sola frase del preámbulo
|
|
45
|
+
// que se reenvía a **cada** subagente, así que el tamaño no se paga una vez: volcar ahí los dos mil bytes
|
|
46
|
+
// de la sección entera metía encabezados, conectores —«Eso rige sin que nadie escriba nada.»— y los
|
|
47
|
+
// párrafos que razonan sobre `BR-OPS-002` y `runner.allowPush`, que son prosa para una persona.
|
|
48
|
+
//
|
|
49
|
+
// Se corta por párrafo y por su sujeto, no por posición ni por oración. Partir por oración fue el primer
|
|
50
|
+
// intento y devolvía diecisiete entradas de las que cuatro eran límites; por posición habría funcionado
|
|
51
|
+
// sobre el molde de hoy y se habría roto con el primero que agregue un párrafo.
|
|
52
|
+
//
|
|
53
|
+
// Lo que esto no resuelve, y por eso existe el caso 157: que sea una deducción gramatical y no una marca.
|
|
54
|
+
// Un límite que el proyecto escriba con otra forma no entra, y eso no se ve — la lista sale más corta y se
|
|
55
|
+
// lee igual de completa. Sobre `AGENTS.md` casi no puede pasar porque `upgrade` lo reemplaza entero; sobre
|
|
56
|
+
// `organization/workspace.md`, que lo escribe una persona, pasa siempre que no imite esta gramática.
|
|
57
|
+
function limits(text) {
|
|
58
|
+
return text.split(/\n\s*\n/)
|
|
59
|
+
.map((block) => block.split('\n')
|
|
60
|
+
.map((line) => line.replace(/^[-*]\s+/, '').trim())
|
|
61
|
+
.filter((line) => line && !line.startsWith('#') && !line.startsWith('|'))
|
|
62
|
+
.join(' ')
|
|
63
|
+
.trim())
|
|
64
|
+
.filter((block) => ENUNCIA.test(block))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function contract(dir, cli) {
|
|
68
|
+
const root = opsRoot(dir)
|
|
69
|
+
const missing = SOURCES.find((name) => !fs.existsSync(path.join(root, name)))
|
|
70
|
+
// Sin los cuatro no hay contrato que derivar, y contestar uno a medias es peor que no contestar: lo que
|
|
71
|
+
// se pierde no se ve en la salida, se ve tres fases después en lo que un subagente creyó que podía tocar.
|
|
72
|
+
if (missing) {
|
|
73
|
+
return fail(`${root} no tiene ${missing}, así que no hay contrato que derivar. Es la raíz que escribió `
|
|
74
|
+
+ '`automation install`: comprobá que exista y, si moviste el proyecto de carpeta, reinstalá el adaptador.', 2)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let config
|
|
78
|
+
try {
|
|
79
|
+
config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return fail(`ops.config.json no se pudo leer como JSON: ${error.message}`, 2)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sections = {}
|
|
85
|
+
for (const { file, heading, name } of REQUIRED_SECTIONS) {
|
|
86
|
+
const found = P.section(readIfAny(path.join(root, file)), heading)
|
|
87
|
+
// Nombrar la sección y el archivo es lo que separa este error de «algo salió mal»: quien lo lee tiene
|
|
88
|
+
// que poder abrir el archivo y ver qué encabezado falta, y el arreglo es restaurarlo con `upgrade`.
|
|
89
|
+
if (!found.trim()) {
|
|
90
|
+
return fail(`${file} no tiene la sección ${name}, y de ahí sale el contrato que reciben los agentes. `
|
|
91
|
+
+ 'Ese archivo lo reemplaza `ops upgrade` entero: corrélo para restaurarlo.', 2)
|
|
92
|
+
}
|
|
93
|
+
sections[file] = found
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const roots = Array.isArray(config.workspaceRoots) ? config.workspaceRoots : []
|
|
97
|
+
const runner = config.runner || {}
|
|
98
|
+
const excepciones = P.section(readIfAny(path.join(root, 'organization', 'workspace.md')),
|
|
99
|
+
/Excepciones de autonom/)
|
|
100
|
+
const report = {
|
|
101
|
+
rootOk: true,
|
|
102
|
+
project: String(config.project || ''),
|
|
103
|
+
// «nombre → ruta», que es la forma con la que el preámbulo las enumera como límite de escritura.
|
|
104
|
+
workspaceRoots: roots.filter((one) => one && one.name && one.path).map((one) => `${one.name} → ${one.path}`),
|
|
105
|
+
// Una entrada por raíz que declare `verify`, y ninguna por las que no: la lista vacía significa que el
|
|
106
|
+
// proyecto no dice con qué se verifica, que es distinto de no haberlo mirado.
|
|
107
|
+
gates: roots.filter((one) => one && one.path && one.verify).map((one) => `${one.path} → ${one.verify}`),
|
|
108
|
+
maxTaskHours: Number(runner.maxTaskHours || 0),
|
|
109
|
+
commitPerTask: Boolean(runner.commitPerTask),
|
|
110
|
+
humanCheckpoint: Boolean(runner.humanCheckpointBetweenMilestones),
|
|
111
|
+
// Textual y sin reformular: es el formato contra el que se escribe roadmap, BACKLOG, WIP y DONE, y un
|
|
112
|
+
// resumen de un formato no sirve para cumplirlo.
|
|
113
|
+
contracts: sections[path.join('planning', 'PROTOCOL.md')].trim(),
|
|
114
|
+
// Los del toolkit primero y los del proyecto después, que es el orden en que se leen: lo segundo amplía
|
|
115
|
+
// o restringe lo primero, y al revés se leería como si el proyecto fijara la base.
|
|
116
|
+
boundaries: [...limits(sections['AGENTS.md']), ...limits(excepciones)],
|
|
117
|
+
}
|
|
118
|
+
if (cli.has('--json')) return console.log(JSON.stringify(report))
|
|
119
|
+
console.log(`${report.project} (${report.workspaceRoots.join('; ') || 'sin raíces declaradas'})`)
|
|
120
|
+
console.log(`gates ${report.gates.join('; ') || 'ninguno declarado'}`)
|
|
121
|
+
console.log(`runner ${report.maxTaskHours} h por tarea · `
|
|
122
|
+
+ `commit ${report.commitPerTask ? 'por tarea' : 'libre'} · `
|
|
123
|
+
+ `checkpoint ${report.humanCheckpoint ? 'entre hitos' : 'no'}`)
|
|
124
|
+
console.log(`límites ${report.boundaries.length} · contratos ${report.contracts.length} caracteres`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { contract }
|
package/engine/cli/ops.js
CHANGED
|
@@ -9,6 +9,7 @@ const { FLAGS, parse } = require('./args')
|
|
|
9
9
|
const { fail } = require('./io')
|
|
10
10
|
const IN = require('./instance')
|
|
11
11
|
const PL = require('./planning')
|
|
12
|
+
const CT = require('./contract')
|
|
12
13
|
const VA = require('./validate')
|
|
13
14
|
const AR = require('./archive')
|
|
14
15
|
const CLM = require('./claims')
|
|
@@ -149,6 +150,7 @@ function usage() {
|
|
|
149
150
|
ops check <planning-dir> [--json]
|
|
150
151
|
ops tree <planning-dir> [--no-color] [--json]
|
|
151
152
|
ops context <planning-dir> [--hito <slug>] [--json]
|
|
153
|
+
ops contract <ops-root> [--json]
|
|
152
154
|
ops recurring <planning-dir> [--promote <qué>] [--json]
|
|
153
155
|
ops runners <planning-dir> [--json]
|
|
154
156
|
ops claim <planning-dir> <tarea>
|
|
@@ -208,6 +210,7 @@ async function run(cli) {
|
|
|
208
210
|
else if (command === 'check') VA.check(arg[1], cli)
|
|
209
211
|
else if (command === 'tree') PL.tree(arg[1], cli)
|
|
210
212
|
else if (command === 'context') PL.context(arg[1], cli)
|
|
213
|
+
else if (command === 'contract') CT.contract(arg[1], cli)
|
|
211
214
|
else if (command === 'recurring') PL.recurring(arg[1], cli)
|
|
212
215
|
else if (command === 'runners') CLM.runners(arg[1], cli)
|
|
213
216
|
else if (command === 'claim') CLM.claim(arg[1], arg[2], cli)
|
package/engine/hooks/files.js
CHANGED
|
@@ -14,7 +14,7 @@ const {
|
|
|
14
14
|
const AP = require('./approval')
|
|
15
15
|
const CHAT = require('./chat')
|
|
16
16
|
const { selfApproval } = require('./self-approval')
|
|
17
|
-
const { readWip } = require('../planning/parser')
|
|
17
|
+
const { readWip, readWips } = require('../planning/parser')
|
|
18
18
|
const { runner } = require('../planning/claims')
|
|
19
19
|
const { hasTasks } = require('../planning/state')
|
|
20
20
|
const { TEMPLATE_PREFIXES } = require('../core/ownership')
|
|
@@ -235,14 +235,33 @@ function planFirst(input) {
|
|
|
235
235
|
// Que el guard quede inerte lo dice `automation check`, porque una condición invisible es peor que
|
|
236
236
|
// no tenerla.
|
|
237
237
|
if (!hasTasks(planning)) return
|
|
238
|
+
// No tener plan y no ver el propio piden cosas opuestas: escribirlo, o volver al id desde el que ya se
|
|
239
|
+
// escribió. `runner()` sale del árbol donde corre el proceso, así que con una instancia al lado de dos
|
|
240
|
+
// repositorios el mismo cambio cae de un lado o del otro según el directorio, y el mensaje mandaba a
|
|
241
|
+
// escribir un plan que estaba a la vista (caso 152).
|
|
242
|
+
//
|
|
243
|
+
// Se lista recién acá, después de las dos salidas de arriba: quien tiene su plan sale por la primera y
|
|
244
|
+
// no paga esta lectura, que es la misma razón por la que `hasTasks` se pregunta donde se pregunta.
|
|
245
|
+
const ajenos = readWips(planning).filter((one) => one.complete + one.pending > 0)
|
|
238
246
|
const estado = wip ? `WIP tiene la tarea ${wip.task} y ningún paso` : 'WIP está en IDLE'
|
|
239
|
-
const why =
|
|
240
|
-
|
|
241
|
-
|
|
247
|
+
const why = ajenos.length
|
|
248
|
+
? `hay plan escrito, pero bajo otro id: ${ajenos.map((one) => `${one.runner} (${one.task})`).join(', ')}.\n`
|
|
249
|
+
+ 'Si ese plan es tuyo, volvé a su id con `export CAUCE_RUNNER=<id>` —`ops runners <planning>` los lista '
|
|
250
|
+
+ 'con su tarea y su avance— y repetí el cambio. Si vas a trabajar en paralelo, montá tu propio árbol '
|
|
251
|
+
+ 'con `ops worktree <planning> <tarea>`, que te devuelve el id hecho.\n'
|
|
252
|
+
: `${estado}, así que el plan todavía no está escrito.\n`
|
|
253
|
+
+ 'Escribí en tu planning/wip/<runner>.md la tarea y su plan aprobado —pasos numerados, cada uno con '
|
|
254
|
+
+ 'un estado verificable— y volvé al cambio. Si esto no es trabajo de una tarea, aprobá la ruta.\n'
|
|
242
255
|
for (const raw of filesOf(input)) {
|
|
243
256
|
if (!isProduct(root, path.resolve(cwdOf(input), raw))) continue
|
|
244
257
|
if (approved(input, raw)) continue
|
|
245
|
-
|
|
258
|
+
// Con un plan a la vista no se ofrece ninguna de las dos salidas: aprobar la ruta escribe «esto no es
|
|
259
|
+
// trabajo de una tarea», que ahí es falso, y anunciar la variable ofrece el permiso más ancho cuando
|
|
260
|
+
// la acción correcta es angosta y concreta —el mismo criterio con que `HOW` decide no nombrarla—.
|
|
261
|
+
const how = ajenos.length
|
|
262
|
+
? AP.HOW(null, [], input, [])
|
|
263
|
+
: AP.HOW('OPS_PLAN_FIRST_OVERRIDE', [raw], input)
|
|
264
|
+
block(`${raw} cambia el producto sin plan. ${why}${how}`)
|
|
246
265
|
}
|
|
247
266
|
}
|
|
248
267
|
|
package/engine/hooks/shell.js
CHANGED
|
@@ -488,11 +488,17 @@ function commitTree(dir) {
|
|
|
488
488
|
// el lockfile de la copia y reinstala, lo que **empieza borrando** el `node_modules` del proyecto.
|
|
489
489
|
//
|
|
490
490
|
// Lo que se apaga es esa comprobación previa, que es el motivo por el que quiere tocar nada.
|
|
491
|
-
// `verify-deps-before-run` la gobierna y tiene
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
// código.
|
|
491
|
+
// `verify-deps-before-run` la gobierna y tiene cinco valores —`install`, `warn`, `prompt`, `error` y
|
|
492
|
+
// `false`—. El que hace el daño es `install`, que reinstala solo y es el default desde pnpm 11. `error`
|
|
493
|
+
// tampoco serviría: frena el gate cuando el lockfile de la copia difiere de lo instalado, que es justo
|
|
494
|
+
// lo que pasa al commitear un cambio de lockfile por partes. La copia no tiene que sincronizar nada:
|
|
495
|
+
// tiene que medir el código.
|
|
496
|
+
//
|
|
497
|
+
// El prefijo es `pnpm_config_` y no `npm_config_`, y esa sola palabra es la diferencia entre apagar la
|
|
498
|
+
// comprobación y no apagar nada: pnpm lee sus ajustes del entorno con su propio prefijo, así que con el
|
|
499
|
+
// de npm la variable llega igual y se ignora en silencio. Acá estuvo `npm_config_` desde el arreglo del
|
|
500
|
+
// 070 y no surtió efecto nunca (caso 151), con lo cual la protección que ese arreglo creyó poner no
|
|
501
|
+
// estuvo puesta. Medido sobre pnpm 10.30.2 y 11.20.0, iguales las dos.
|
|
496
502
|
//
|
|
497
503
|
// Acá estuvo `CI: 'true'` y fue una regresión (caso 070). Resolvía el síntoma del 068 —pnpm dejaba de
|
|
498
504
|
// preguntar antes de purgar— desarmando la confirmación en vez de quitarle el motivo, y esa
|
|
@@ -502,7 +508,7 @@ function commitTree(dir) {
|
|
|
502
508
|
//
|
|
503
509
|
// La regla que queda: no se desarma la confirmación de una herramienta, se le quita el motivo de
|
|
504
510
|
// preguntar. Una confirmación que estorba casi siempre está cuidando algo.
|
|
505
|
-
return { root: temp, temp, env: {
|
|
511
|
+
return { root: temp, temp, env: { pnpm_config_verify_deps_before_run: 'false' } }
|
|
506
512
|
}
|
|
507
513
|
|
|
508
514
|
function verify(input) {
|