@roomi-fields/grillhouse 0.23.1 → 0.24.1

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.
Files changed (65) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/METACADRE.md +1 -1
  3. package/agents/explorateur.md +4 -2
  4. package/agents/questionneur.md +1 -1
  5. package/bin/grillhouse.mjs +3 -58
  6. package/cadre/templates/projet/CLAUDE.md +5 -1
  7. package/cadre/templates/projet/docs/agents/issue-tracker.md +4 -4
  8. package/charte.md +23 -6
  9. package/docs/ARCHITECTURE.md +130 -1
  10. package/hooks/register.tsx +3 -13
  11. package/package.json +13 -1
  12. package/scripts/bilan.mjs +134 -88
  13. package/scripts/circuit.mjs +1547 -2248
  14. package/scripts/composants-affectes.mjs +27 -108
  15. package/scripts/consommateurs-arbitres.mjs +15 -21
  16. package/scripts/consommateurs.d.ts +8 -0
  17. package/scripts/consommateurs.mjs +5 -1
  18. package/scripts/dette.d.ts +23 -0
  19. package/scripts/dette.mjs +76 -13
  20. package/scripts/droits.mjs +38 -44
  21. package/scripts/elements-du-lot.mjs +246 -0
  22. package/scripts/enveloppe/enveloppe.mjs +105 -232
  23. package/scripts/enveloppe/lancer.sh +79 -49
  24. package/scripts/enveloppe/paquets-du-plan.mjs +49 -0
  25. package/scripts/etapes.mjs +148 -213
  26. package/scripts/frontieres.mjs +4 -2
  27. package/scripts/gardes-du-cadre.mjs +8 -6
  28. package/scripts/grillhouse-maj.mjs +113 -19
  29. package/scripts/index-interfaces.mjs +21 -1
  30. package/scripts/integration/integrer.mjs +2 -2
  31. package/scripts/interfaces-contre-code.mjs +25 -17
  32. package/scripts/migrations/0.23.0-config-vitest.mjs +26 -23
  33. package/scripts/migrations/0.23.2-copies-du-cadre.mjs +201 -0
  34. package/scripts/migrations/0.24.0-circuit-simplifie.mjs +274 -0
  35. package/scripts/migrations.mjs +41 -6
  36. package/scripts/paquets.d.ts +5 -0
  37. package/scripts/paquets.mjs +3 -0
  38. package/scripts/references-par-les-types.mjs +55 -36
  39. package/scripts/registre.mjs +7 -15
  40. package/scripts/resultats.mjs +283 -142
  41. package/scripts/resumes.mjs +1 -1
  42. package/scripts/session-start.sh +1 -2
  43. package/scripts/sources.mjs +28 -0
  44. package/scripts/sous-commandes.mjs +62 -0
  45. package/scripts/tableau.mjs +85 -191
  46. package/scripts/tests.mjs +19 -17
  47. package/scripts/verifier.mjs +5 -4
  48. package/scripts/verrou-de-machine.mjs +131 -24
  49. package/scripts/verrous/verrou.mjs +66 -5
  50. package/skills/developpeur/SKILL.md +60 -50
  51. package/skills/explorateur/SKILL.md +90 -64
  52. package/skills/grill/SKILL.md +1 -1
  53. package/skills/mesure/SKILL.md +2 -1
  54. package/skills/pitmaster/SKILL.md +132 -118
  55. package/skills/pitmaster/references/consigne-agent.md +54 -148
  56. package/skills/questionneur/SKILL.md +18 -18
  57. package/skills/relecteur/SKILL.md +69 -23
  58. package/skills/testwriter/SKILL.md +46 -35
  59. package/types/index.d.ts +1 -3
  60. package/agents/referent-technique.md +0 -9
  61. package/agents/regulateur.md +0 -11
  62. package/scripts/enveloppe/composant.mjs +0 -11
  63. package/scripts/etat-du-circuit.mjs +0 -104
  64. package/skills/referent-technique/SKILL.md +0 -145
  65. package/skills/regulateur/SKILL.md +0 -220
@@ -13,7 +13,14 @@
13
13
  // - The migrations first (scripts/migrations.mjs): each migration of the package newer than the
14
14
  // version the project recorded (`grillhouse.version` of its package.json; none recorded, every
15
15
  // migration), in the order of their versions; each is replayable without effect, and the report
16
- // names each one that changed something, with what it changed.
16
+ // names each one that changed something, with what it changed. What a migration leaves to the
17
+ // project (a copy of the frame the project modified, scripts/migrations/0.23.2-copies-du-cadre.mjs)
18
+ // is written on the error stream, with the command that does it, and the exit code is non-zero;
19
+ // the project keeps the version it recorded, so that the next update plays that migration again.
20
+ // Last, after the update and the regeneration, what stays to the project (`restes` of a
21
+ // migration played, scripts/migrations.mjs: in 0.24.0, a file that still calls a former copy of
22
+ // the frame) is reported on the standard output, « reste au projet », with no effect on the exit
23
+ // code nor on the version recorded.
17
24
  // - The frame copies no file into a project: its scripts are the package's, its skills, agents,
18
25
  // hooks of Claude Code, common charter and meta-frame the plugin's, which the package carries
19
26
  // (scripts/cadre.mjs): the frame arrives in one piece, at the version npm installs.
@@ -31,7 +38,8 @@
31
38
  // the frame's status line (`ligne-etat`, whatever its former form); a status line of the
32
39
  // project's own stays. The settings keep their other keys, in their order.
33
40
  // - `package.json`, when there is one: the version of the frame it is brought to
34
- // (`grillhouse.version`, the package's version), the frame's npm scripts (`tableau`,
41
+ // (`grillhouse.version`, the package's version once no migration leaves anything to do), the
42
+ // frame's npm scripts (`tableau`,
35
43
  // `grillhouse:maj`), its `prepare` beginning with `grillhouse hooks` (the frame's git hooks at
36
44
  // each `npm install`, as Husky's `prepare`; a `prepare` of the project's own follows it,
37
45
  // `grillhouse hooks && …`), and `grillhouse:gardes` (scripts/gardes-du-cadre.mjs, the frame's controls), `grillhouse:dette:inscrire` and
@@ -89,6 +97,13 @@
89
97
  // `^<version>` of the package), a declaration it holds kept; a package out of the frame is not
90
98
  // touched. The report names the packages that received it, `npm install`, which links it, and the
91
99
  // import; the update writes no Vitest configuration.
100
+ // - A package of the frame whose code imports the frame's package (a file of its `src/` out of the
101
+ // tests, the files whose development dependencies the guard of the boundaries refuses,
102
+ // scripts/frontieres.mjs; its imports read by TypeScript's `preProcessFile`) declares it in its
103
+ // `dependencies`, as npm puts there what the published code imports: a declaration in its
104
+ // `devDependencies` moves there with its range, else the range the root declares (else
105
+ // `^<version>`); this comes first, so that the declaration of the Vitest base above keeps it.
106
+ // The report names the packages and `npm install`.
92
107
  // - The git hooks: both of the frame (commit-msg « a closed ticket refuses a second commit, a reopened ticket takes its corrections », pre-push night branch
93
108
  // guard) are placed by scripts/crochets.mjs, the mechanism `grillhouse hooks` (the project's
94
109
  // `prepare`) also runs, in the hooks folder git reads for the project (`core.hooksPath`, else the
@@ -114,14 +129,17 @@
114
129
  // - The report names the version of the frame, « Cadre 0.23.0. », the package's.
115
130
  import { spawnSync } from 'node:child_process';
116
131
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
132
+ import { createRequire } from 'node:module';
117
133
  import path from 'node:path';
118
134
  import { pathToFileURL } from 'node:url';
119
135
  import semver from 'semver';
120
136
  import { CADRE, VERSION } from './cadre.mjs';
121
137
  import { poserCrochets } from './crochets.mjs';
122
138
  import { installerGreffons } from './greffons.mjs';
123
- import { migrer } from './migrations.mjs';
139
+ import { migrer, restesAuProjet } from './migrations.mjs';
140
+ import { CODE, TESTS } from './frontieres.mjs';
124
141
  import { paquets } from './paquets.mjs';
142
+ import { sources } from './sources.mjs';
125
143
 
126
144
  const SCRIPTS = {
127
145
  tableau: 'grillhouse tableau --texte',
@@ -211,6 +229,64 @@ function poserTurbo(projet, source) {
211
229
  return gabarit;
212
230
  }
213
231
 
232
+ // <deps> written sorted by name, as npm writes them.
233
+ const trie = deps =>
234
+ Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a.localeCompare(b, 'en')));
235
+ // The range at which the root package.json <p> declares the frame's package, or undefined.
236
+ const plageDeLaRacine = p =>
237
+ CHAMPS_INSTALLES.map(c => p[c]?.[PAQUET_DU_CADRE]).find(v => v !== undefined);
238
+
239
+ // Whether the code of the package at <dossier> of <projet> imports the frame's package (an entry of
240
+ // it): a file of its `src/` out of the tests (CODE and TESTS of scripts/frontieres.mjs, the files
241
+ // whose development dependencies its guard refuses), its imports read by TypeScript
242
+ // (`preProcessFile`), a comment or a string importing nothing.
243
+ function codeImporteLeCadre(projet, dossier) {
244
+ const ts = createRequire(import.meta.url)('typescript');
245
+ return sources(path.join(projet, dossier))
246
+ .map(f => `${dossier}/${f}`)
247
+ .filter(f => new RegExp(CODE).test(f) && !new RegExp(TESTS).test(f))
248
+ .some(f =>
249
+ ts
250
+ .preProcessFile(readFileSync(path.join(projet, f), 'utf8'), true, true)
251
+ .importedFiles.some(
252
+ ({ fileName: m }) => m === PAQUET_DU_CADRE || m.startsWith(`${PAQUET_DU_CADRE}/`)
253
+ )
254
+ );
255
+ }
256
+
257
+ // Declares the frame's package in the dependencies of each package of the frame of the project of
258
+ // packages at <projet> (root package.json <p>) whose code imports it (codeImporteLeCadre), as npm
259
+ // puts what the published code imports: a declaration in its devDependencies moves there with its
260
+ // range, else the range the root declares (else `^<version>` of the package); one already in its
261
+ // dependencies or optionalDependencies stays. Returns { nom, paquets } (paquets: the packages
262
+ // whose dependencies received it), or null when nothing changes.
263
+ function declarerDependanceDuCadre(projet, p) {
264
+ if (p.workspaces === undefined) {
265
+ return null;
266
+ }
267
+ const racine = plageDeLaRacine(p);
268
+ const declarants = [];
269
+ for (const { dossier, pkg, hors } of paquets(projet)) {
270
+ const declare = ['dependencies', 'optionalDependencies'].some(
271
+ c => pkg[c]?.[PAQUET_DU_CADRE] !== undefined
272
+ );
273
+ if (hors || declare || !codeImporteLeCadre(projet, dossier)) {
274
+ continue;
275
+ }
276
+ const { [PAQUET_DU_CADRE]: dev, ...autres } = pkg.devDependencies ?? {};
277
+ pkg.dependencies = trie({
278
+ ...(pkg.dependencies ?? {}),
279
+ [PAQUET_DU_CADRE]: dev ?? racine ?? `^${VERSION}`,
280
+ });
281
+ if (dev !== undefined) {
282
+ pkg.devDependencies = autres;
283
+ }
284
+ writeFileSync(path.join(projet, dossier, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`);
285
+ declarants.push(dossier);
286
+ }
287
+ return declarants.length > 0 ? { nom: PAQUET_DU_CADRE, paquets: declarants } : null;
288
+ }
289
+
214
290
  // Declares the frame's package, whose base configuration of Vitest each Vitest configuration
215
291
  // imports, in the devDependencies of each package of the frame of the project of packages at
216
292
  // <projet> (root package.json <p>) whose scripts hold `test:vitest`, at the range the root declares
@@ -222,16 +298,14 @@ function poserConfigVitest(projet, p) {
222
298
  const testes = paquets(projet).filter(
223
299
  ({ pkg, hors }) => !hors && typeof pkg.scripts?.['test:vitest'] === 'string'
224
300
  );
225
- const racine = CHAMPS_INSTALLES.map(c => p[c]?.[PAQUET_DU_CADRE]).find(v => v !== undefined);
301
+ const racine = plageDeLaRacine(p);
226
302
  const declarants = [];
227
303
  for (const { dossier, pkg } of testes) {
228
304
  if (CHAMPS_INSTALLES.some(c => pkg[c]?.[PAQUET_DU_CADRE] !== undefined)) continue;
229
- pkg.devDependencies = Object.fromEntries(
230
- Object.entries({
231
- ...(pkg.devDependencies ?? {}),
232
- [PAQUET_DU_CADRE]: racine ?? `^${VERSION}`,
233
- }).sort(([a], [b]) => a.localeCompare(b, 'en'))
234
- );
305
+ pkg.devDependencies = trie({
306
+ ...(pkg.devDependencies ?? {}),
307
+ [PAQUET_DU_CADRE]: racine ?? `^${VERSION}`,
308
+ });
235
309
  writeFileSync(path.join(projet, dossier, 'package.json'), `${JSON.stringify(pkg, null, 2)}\n`);
236
310
  declarants.push(dossier);
237
311
  }
@@ -285,15 +359,17 @@ export function versionDuProjet(projet) {
285
359
 
286
360
  /**
287
361
  * Brings the project at `projet` to the frame's files at `source` (CADRE, scripts/cadre.mjs; a
288
- * trial's own in the tests). Returns what changed.
362
+ * trial's own in the tests); <inscrite> is the version it records (the package's; null records
363
+ * none, the project keeping its own). Returns what changed.
289
364
  */
290
- export function mettreAJour(projet, source) {
365
+ export function mettreAJour(projet, source, { inscrite = VERSION } = {}) {
291
366
  const bilan = {
292
367
  dependances: [],
293
368
  test: null,
294
369
  vitest: null,
295
370
  turbo: null,
296
371
  configVitest: null,
372
+ dependanceDuCadre: null,
297
373
  gardes: null,
298
374
  ligneEtat: false,
299
375
  greffon: false,
@@ -301,7 +377,7 @@ export function mettreAJour(projet, source) {
301
377
  const pkg = path.join(projet, 'package.json');
302
378
  if (existsSync(pkg)) {
303
379
  const p = lireJson(pkg, {});
304
- p.grillhouse = { ...(p.grillhouse ?? {}), version: VERSION };
380
+ if (inscrite !== null) p.grillhouse = { ...(p.grillhouse ?? {}), version: inscrite };
305
381
  const pkgSource = lireJson(path.join(source, GABARIT), {});
306
382
  const gardes = pkgSource.scripts?.['grillhouse:gardes'];
307
383
  p.scripts = { ...(p.scripts ?? {}), ...SCRIPTS };
@@ -353,9 +429,7 @@ export function mettreAJour(projet, source) {
353
429
  }
354
430
  }
355
431
  if (p.devDependencies) {
356
- p.devDependencies = Object.fromEntries(
357
- Object.entries(p.devDependencies).sort(([a], [b]) => a.localeCompare(b, 'en'))
358
- );
432
+ p.devDependencies = trie(p.devDependencies);
359
433
  }
360
434
  if (typeof pkgSource.packageManager === 'string' && typeof p.packageManager !== 'string') {
361
435
  p.packageManager = pkgSource.packageManager;
@@ -394,6 +468,7 @@ export function mettreAJour(projet, source) {
394
468
  bilan.gardes = { deplace: false };
395
469
  }
396
470
  }
471
+ bilan.dependanceDuCadre = declarerDependanceDuCadre(projet, p);
397
472
  bilan.configVitest = poserConfigVitest(projet, p);
398
473
  writeFileSync(pkg, `${JSON.stringify(p, null, 2)}\n`);
399
474
  }
@@ -453,6 +528,12 @@ export function rapport(b) {
453
528
  `Configuration de base de Vitest : ${nom} déclaré dans les devDependencies de ${declarants.join(', ')}. \`npm install\` relie le paquet. La configuration Vitest de chaque paquet l'importe par son nom : \`import base from '${CONFIG_VITEST}'\`.`
454
529
  );
455
530
  }
531
+ if (b.dependanceDuCadre) {
532
+ const { nom, paquets: declarants } = b.dependanceDuCadre;
533
+ lignes.push(
534
+ `Dépendance d'exécution : ${nom} déclaré dans les dependencies de ${declarants.join(', ')}, dont le code hors des tests importe une entrée du paquet. \`npm install\` relie le paquet.`
535
+ );
536
+ }
456
537
  if (b.turbo) {
457
538
  lignes.push(
458
539
  `turbo.json écrit depuis le gabarit du cadre ${b.turbo} ; il est au projet, la mise à jour n'y touche plus.`
@@ -497,18 +578,31 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
497
578
  process.exitCode = 2;
498
579
  } else {
499
580
  const projet = process.cwd();
500
- const migrations = migrer(projet, versionDuProjet(projet));
501
- const b = mettreAJour(projet, CADRE);
581
+ const depuis = versionDuProjet(projet);
582
+ const migrations = migrer(projet, depuis);
583
+ const resteAFaire = migrations.some(m => m.aFaire.length > 0);
584
+ const b = mettreAJour(projet, CADRE, resteAFaire ? { inscrite: null } : {});
502
585
  process.stdout.write(`Cadre ${VERSION}.\n`);
503
- for (const { version, nom, lignes } of migrations) {
586
+ for (const { version, nom, lignes, aFaire } of migrations) {
504
587
  process.stdout.write(
505
588
  `Migration ${version} (${nom}) :\n${lignes.map(l => `- ${l}\n`).join('')}`
506
589
  );
590
+ if (aFaire.length > 0) {
591
+ process.stderr.write(
592
+ `Migration ${version} (${nom}), à faire :\n${aFaire.map(l => `- ${l}\n`).join('')}`
593
+ );
594
+ process.exitCode = 1;
595
+ }
507
596
  }
508
597
  process.stdout.write(rapport(b));
509
598
  for (const l of [...regenerer(projet), ...installerGreffons(projet)]) {
510
599
  process.stderr.write(`${l}\n`);
511
600
  process.exitCode = 1;
512
601
  }
602
+ for (const { version, nom, lignes } of restesAuProjet(projet, depuis)) {
603
+ process.stdout.write(
604
+ `Migration ${version} (${nom}), reste au projet :\n${lignes.map(l => `- ${l}\n`).join('')}`
605
+ );
606
+ }
513
607
  }
514
608
  }
@@ -36,6 +36,7 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
36
36
  import path from 'node:path';
37
37
  import { pathToFileURL } from 'node:url';
38
38
  import { composants, declares } from './consommateurs.mjs';
39
+ import { lireLaListe } from './enveloppe/paquets-du-plan.mjs';
39
40
  import { DEBUT, FIN, marques } from './interfaces-contre-code.mjs';
40
41
  import { ecrireSarif } from './resultats.mjs';
41
42
 
@@ -179,7 +180,10 @@ function rend(racine, rel) {
179
180
  // What the components before <composant> publish, as the section the launcher writes after the
180
181
  // instruction of a role agent (scripts/enveloppe/lancer.sh). <composant> is the component's path
181
182
  // in the repository at <racine> (`packages/a`, `src/parser`), or `.` for a copy of the whole
182
- // repository, which the section points to the index of the interfaces. Its providers are the
183
+ // repository, which the section points to the index of the interfaces; or the plan of a session,
184
+ // several paths in the form of the envelope's list (`packages/a:packages/b`, `lireLaListe`), one
185
+ // part of the section per package of the plan, its providers under it, as one session works the
186
+ // whole plan of a ticket (grillhouse-3uv.95.6). Its providers are the
183
187
  // components whose `## Consommateurs` section names it; for each, its line in the index of the
184
188
  // interfaces (its heading, its first sentence, its interface), the names of the elements its
185
189
  // `## Consommateurs` line declares for the component, never their sentence, which the index of the
@@ -187,7 +191,23 @@ function rend(racine, rel) {
187
191
  // or one line saying the frame or the rubric is missing. A declaration of `*`, an import of the
188
192
  // whole component, names no element: its line says the elements are in the index. Each interface
189
193
  // and each frame is read once.
194
+ /** @param {string} racine @param {string} composant @returns {string} */
190
195
  export function publie(racine, composant) {
196
+ const plan = lireLaListe(composant);
197
+ if (plan.length > 1) {
198
+ const parties = plan.map(c =>
199
+ publie(racine, c)
200
+ .split('\n')
201
+ .slice(2)
202
+ .map(l => (l.startsWith('### ') ? `#${l}` : l))
203
+ );
204
+ return [
205
+ TITRE_PUBLIE,
206
+ '',
207
+ 'Ton plan porte plusieurs paquets : pour chacun, ce que publient les composants dont il dépend.',
208
+ ...plan.flatMap((c, i) => ['', `### Pour \`${path.basename(c)}\``, '', ...parties[i]]),
209
+ ].join('\n');
210
+ }
191
211
  if (composant === '.') {
192
212
  return [
193
213
  TITRE_PUBLIE,
@@ -14,8 +14,8 @@
14
14
  // --ticket is a usage error, named. In order, each step refusing stops the run:
15
15
  // 1. the verdict: the ticket has `ACCEPTÉ` as its last reviewer verdict (`ACCEPTÉ` or
16
16
  // `RENDU` at the start of a comment of the author `relecteur`; another author's is none) and, when labelled `arbitrage`, each arbitrage
17
- // (`estArbitrage`: a « ## Arbitrage » comment, or the regulator's the circuit writes) is settled
18
- // (`arbitrageTranche`: the regulator's, `### Verdict — tranché`, or a « ## Réponse du
17
+ // (`estArbitrage`: a « ## Arbitrage » comment, the supervisor's) is settled
18
+ // (`arbitrageTranche`: `### Verdict — tranché`, or a « ## Réponse du
19
19
  // responsable » comment after it);
20
20
  // 2. the lots: the tests lot touches only what the testwriter writes (`isTesterFile`: test files
21
21
  // and the shared test material), the code lot none of it;
@@ -45,10 +45,10 @@
45
45
  // `faute-mal-formee`, `faute-hors-catalogue`, `faute-non-nommee`, `faute-non-produite`,
46
46
  // `faute-non-listee`), or by the declaration file and TSDoc's message id for a TSDoc message.
47
47
  //
48
- // In the envelope of a component (`GRILLHOUSE_ENVELOPPE`, scripts/enveloppe/composant.mjs), the
49
- // check and `npm run api` read and write that component alone: its neighbours' source is hidden,
50
- // their published parts read-only, and the integration checks every component on the whole
51
- // repository. The component's build comes first, as outside (`npm test -- <its files>` builds it).
48
+ // In the envelope of an agent (`GRILLHOUSE_ENVELOPPE`, scripts/enveloppe/paquets-du-plan.mjs), the
49
+ // check and `npm run api` judge and write the components of its plan alone: the others are
50
+ // read-only there, and the integration checks every component on the whole repository. The
51
+ // component's build comes first, as outside (`npm test -- <its files>` builds it).
52
52
  //
53
53
  // A component without `docs/INTERFACE.md` is reported and skipped. A project that prefers a
54
54
  // bench per component calls `verifierComposant(racine, composant)` from its test.
@@ -58,7 +58,7 @@ import { tmpdir } from 'node:os';
58
58
  import path from 'node:path';
59
59
  import { pathToFileURL } from 'node:url';
60
60
  import { composants, lireSiPresent, parcourir } from './consommateurs.mjs';
61
- import { composantDeLEnveloppe } from './enveloppe/composant.mjs';
61
+ import { paquetsDeLEnveloppe } from './enveloppe/paquets-du-plan.mjs';
62
62
  import { trancher } from './dette.mjs';
63
63
 
64
64
  const require = createRequire(import.meta.url);
@@ -428,7 +428,10 @@ function avecPartie(m, texte, partie) {
428
428
  // the debt (`dette.mjs`) judges, keyed by the component's interface (by the declaration file for a
429
429
  // TSDoc message, its rule TSDoc's message id); a refusal that a command or a build repairs (a
430
430
  // generated part, an API report, a built entry, the marks, the TSDoc configuration) has no key and
431
- // is never inscribed.
431
+ // is never inscribed. The refusals `npm run api` lifts carry their rule without a file, never
432
+ // inscribed either: `rapport-api` (the API report absent or stale) and `partie-generee` (the
433
+ // generated part of the interface), by which the circuit regenerates them at the final pass
434
+ // (scripts/circuit.mjs, the references of the frame).
432
435
  export function verifierComposant(racine, c, { ecrire = false } = {}) {
433
436
  const refus = [];
434
437
  const signaux = [];
@@ -442,6 +445,8 @@ export function verifierComposant(racine, c, { ecrire = false } = {}) {
442
445
  ? { fichier, ...(ligne ? { ligne } : {}), regle, message: `${rel} : ${texte}` }
443
446
  : { message: `${rel} : ${texte}` }
444
447
  );
448
+ // A refusal `npm run api` lifts: its rule, no file.
449
+ const aRegenerer = (texte, regle) => refus.push({ regle, message: `${rel} : ${texte}` });
445
450
  if (!existsSync(iface)) {
446
451
  signaux.push(`${rel} n'a pas d'interface (docs/INTERFACE.md) : rien n'est vérifié.`);
447
452
  return { refus, signaux };
@@ -474,12 +479,14 @@ export function verifierComposant(racine, c, { ecrire = false } = {}) {
474
479
  }
475
480
  const manque = !ecrire && !existsSync(path.join(c.dir, 'docs', 'INTERFACE.api.md'));
476
481
  if (manque) {
477
- refuser(
478
- "son rapport docs/INTERFACE.api.md manque ; npm run api l'écrit, après la construction."
482
+ aRegenerer(
483
+ "son rapport docs/INTERFACE.api.md manque ; npm run api l'écrit, après la construction.",
484
+ 'rapport-api'
479
485
  );
480
486
  } else if (!api.ajour && !ecrire) {
481
- refuser(
482
- "l'API a changé sans son rapport docs/INTERFACE.api.md ; npm run api le régénère, après la construction."
487
+ aRegenerer(
488
+ "l'API a changé sans son rapport docs/INTERFACE.api.md ; npm run api le régénère, après la construction.",
489
+ 'rapport-api'
483
490
  );
484
491
  }
485
492
  const nommees = new Set();
@@ -497,8 +504,9 @@ export function verifierComposant(racine, c, { ecrire = false } = {}) {
497
504
  if (ecrire) {
498
505
  if (attendu !== texte) writeFileSync(iface, attendu);
499
506
  } else if (attendu !== texte) {
500
- refuser(
501
- `la part générée de docs/INTERFACE.md (entre ${DEBUT} et ${FIN}) n'est pas celle du code ; npm run api la refait.`
507
+ aRegenerer(
508
+ `la part générée de docs/INTERFACE.md (entre ${DEBUT} et ${FIN}) n'est pas celle du code ; npm run api la refait.`,
509
+ 'partie-generee'
502
510
  );
503
511
  }
504
512
  }
@@ -522,13 +530,13 @@ function codesContreCatalogue(c, racine, fautes, refuser) {
522
530
  }
523
531
  }
524
532
 
525
- // The components a run reads: every one of <racine>; in the envelope of a component, that one
526
- // alone, its neighbours read by their published form and never written.
533
+ // The components a run reads: every one of <racine>; in an envelope, those its plan names, the
534
+ // others never written; every one when the plan names none of them (a folder of the root, `.`).
527
535
  export function aVerifier(racine, env = process.env) {
528
536
  const tous = composants(racine);
529
- const propre = composantDeLEnveloppe(env);
530
- const sien = propre && tous.filter(c => path.resolve(c.dir) === path.resolve(racine, propre));
531
- return sien?.length ? sien : tous;
537
+ const plan = (paquetsDeLEnveloppe(env) ?? []).map(p => path.resolve(racine, p));
538
+ const siens = tous.filter(c => plan.includes(path.resolve(c.dir)));
539
+ return siens.length ? siens : tous;
532
540
  }
533
541
 
534
542
  if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
@@ -2,22 +2,24 @@
2
2
  // (and `/mesure`), in place of the workspace package `@grillhouse/vitest-config` that the frame's
3
3
  // copied files held.
4
4
  //
5
- // - Each source file of the project (`.ts`, `.mts`, `.cts`, `.js`, `.mjs`, `.cjs`; node_modules,
6
- // .git, dist, coverage, .turbo, .claude, .beads, .codegraph and .rtfm left out) that names
5
+ // - Each source file of the project (scripts/sources.mjs) that names
7
6
  // `@grillhouse/vitest-config` or `@grillhouse/vitest-config/mesure` in quotes names the package's
8
7
  // instead; a root configuration that imports the base by its former path
9
8
  // (`./scripts/vitest-config/base.mjs`, `./packages/grillhouse/scripts/vitest-config/base.mjs`)
10
- // imports it by its name.
9
+ // imports it by its name. `formesAnterieures` gives back what a text so rewritten held before,
10
+ // by which the migration of the copies (0.23.2-copies-du-cadre.mjs) still knows a former copy of
11
+ // the frame by its fingerprint.
11
12
  // - Each package.json of the project (the root's and each workspace's) that declares
12
13
  // `@grillhouse/vitest-config` declares `@roomi-fields/grillhouse` in the same field in its place,
13
14
  // at the range the root declares (else `^<version>` of the package), unless it already declares
14
15
  // it; the root's workspaces lose the former directory of the package (`scripts/vitest-config`,
15
16
  // `packages/grillhouse/scripts/vitest-config`).
16
17
  // - A project already in the new form is not written.
17
- import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
18
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
18
19
  import path from 'node:path';
19
20
  import { VERSION } from '../cadre.mjs';
20
21
  import { paquets } from '../paquets.mjs';
22
+ import { sources } from '../sources.mjs';
21
23
 
22
24
  export const version = '0.23.0';
23
25
  export const nom = 'configuration de base de Vitest';
@@ -27,33 +29,34 @@ const PAQUET = '@roomi-fields/grillhouse';
27
29
  const NOUVEAU = `${PAQUET}/vitest-config`;
28
30
  const ANCIENS_ESPACES = ['scripts/vitest-config', 'packages/grillhouse/scripts/vitest-config'];
29
31
  const CHAMPS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
30
- const EXTENSIONS = /\.(?:[mc]?ts|[mc]?js)$/;
31
- const HORS = new Set([
32
- 'node_modules',
33
- '.git',
34
- 'dist',
35
- 'coverage',
36
- '.turbo',
37
- '.claude',
38
- '.beads',
39
- '.codegraph',
40
- '.rtfm',
41
- ]);
42
32
  // A quoted name of the former package, or a former path of the base at the root.
43
33
  const NOMME =
44
34
  /(['"])(?:@grillhouse\/vitest-config(\/mesure)?|\.\/(?:packages\/grillhouse\/)?scripts\/vitest-config\/base\.mjs)\1/g;
45
35
 
46
- function sources(racine, rel = '') {
47
- return readdirSync(path.join(racine, rel), { withFileTypes: true }).flatMap(e => {
48
- const r = rel === '' ? e.name : `${rel}/${e.name}`;
49
- if (e.isDirectory()) return HORS.has(e.name) ? [] : sources(racine, r);
50
- return e.isFile() && EXTENSIONS.test(e.name) ? [r] : [];
51
- });
52
- }
36
+ // A quoted name of the package's base, as this migration writes it.
37
+ const ECRIT = /(['"])@roomi-fields\/grillhouse\/vitest-config(\/mesure)?\1/g;
38
+ const ANCIENS_CHEMINS = [
39
+ './scripts/vitest-config/base.mjs',
40
+ './packages/grillhouse/scripts/vitest-config/base.mjs',
41
+ ];
53
42
 
54
43
  const lireJson = f => JSON.parse(readFileSync(f, 'utf8'));
55
44
  const ecrireJson = (f, v) => writeFileSync(f, `${JSON.stringify(v, null, 2)}\n`);
56
45
 
46
+ /**
47
+ * The texts <texte> may have held before this migration rewrote it: each name it wrote back to the
48
+ * former package's, or each name of the base back to one former path; none when it wrote nothing.
49
+ */
50
+ export function formesAnterieures(texte) {
51
+ if (texte.search(ECRIT) < 0) return [];
52
+ return [
53
+ texte.replace(ECRIT, (_, q, mesure) => `${q}${ANCIEN}${mesure ?? ''}${q}`),
54
+ ...ANCIENS_CHEMINS.map(c =>
55
+ texte.replace(ECRIT, (tout, q, mesure) => (mesure ? tout : `${q}${c}${q}`))
56
+ ),
57
+ ];
58
+ }
59
+
57
60
  /** Brings the project to the package's base configuration of Vitest; returns what it did. */
58
61
  export function migrer(projet) {
59
62
  const lignes = [];