@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
@@ -0,0 +1,246 @@
1
+ // The elements of a lot: each function, class, type, interface, enum, constant at the top of a
2
+ // module and export the lot adds, changes or removes, with its package, its file and line, and
3
+ // whether its package's entry exports it. As API Extractor's report and its diff, read line by
4
+ // line, and a review that asks a word on every changed file, the circuit gives this list to the
5
+ // reviewer (`relire` of scripts/circuit.mjs), whose sheet holds one line of conformity per element
6
+ // (scripts/resultats.mjs, `conformiteDuLot`).
7
+ //
8
+ // - The lot is the difference between two commits of the copy (<avant>, its HEAD, and <apres>,
9
+ // the lot the circuit keeps): its changed files by `git diff --name-status -z`, the text of a
10
+ // file before by `git show <avant>:<fichier>`.
11
+ // - The declarations are read by TypeScript's language service over the components'
12
+ // `tsconfig.json` (`leService` of scripts/references-par-les-types.mjs): the production
13
+ // sources it reads, the tests and the declaration files left out; the file before, by
14
+ // TypeScript's parser. A declaration of the lot is new when its name is not in the file before,
15
+ // removed when it is not in the file after, and changed when its text differs (its TSDoc
16
+ // comment included), as the declarations of the reports are compared.
17
+ // - An element is exported when the entry of its package exports it (the service's symbols), or
18
+ // when the API report of its package names it (`docs/INTERFACE.api.md`, which
19
+ // scripts/interfaces-contre-code.mjs writes): after the lot for a new or changed one, before
20
+ // it for a removed one. A declaration of a report that the lot adds, changes or removes, and
21
+ // that no element of its package's sources already names (a type that changed through another),
22
+ // is an element of the report itself, exported.
23
+ // - A changed source that the service does not read (a package without `tsconfig.json`) is named
24
+ // in `horsDuService`: the reviewer surveys its elements himself.
25
+ import { spawnSync } from 'node:child_process';
26
+ import { existsSync } from 'node:fs';
27
+ import { createRequire } from 'node:module';
28
+ import path from 'node:path';
29
+ import { composants } from './consommateurs.mjs';
30
+ import { estTest, leService } from './references-par-les-types.mjs';
31
+
32
+ const requis = createRequire(import.meta.url);
33
+ const SOURCE = /\.[cm]?[jt]sx?$/;
34
+ const RAPPORT = path.join('docs', 'INTERFACE.api.md');
35
+ const CLOTURE = '```';
36
+
37
+ const git = (copie, ...a) =>
38
+ spawnSync('git', ['-C', copie, ...a], { encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
39
+ const enPosix = f => f.split(path.sep).join('/');
40
+
41
+ // The text of <fichier> (from the copy) at the commit <rev>, or null when it does not hold it.
42
+ function aLa(copie, rev, fichier) {
43
+ const r = git(copie, 'show', `${rev}:${enPosix(fichier)}`);
44
+ return r.status === 0 ? r.stdout : null;
45
+ }
46
+
47
+ // The declarations at the top of the source file <sf>: Map nom → `{ nom, genre, ligne, noeud,
48
+ // texte }`, `noeud` its name, `texte` its text with its TSDoc comment (an overloaded function, a
49
+ // merged interface: the texts of all its declarations).
50
+ function declarationsDe(ts, sf) {
51
+ const out = new Map();
52
+ const ajouter = (nom, genre, noeud, porteur) => {
53
+ const debut = porteur.getStart(sf, true);
54
+ const texte = sf.text.slice(debut, porteur.getEnd());
55
+ const d = out.get(nom);
56
+ if (d) {
57
+ d.texte += `\n${texte}`;
58
+ } else {
59
+ const ligne = sf.getLineAndCharacterOfPosition(debut).line + 1;
60
+ out.set(nom, { nom, genre, ligne, noeud, texte });
61
+ }
62
+ };
63
+ const noms = n =>
64
+ ts.isIdentifier(n)
65
+ ? [n]
66
+ : n.elements.flatMap(e => (ts.isOmittedExpression(e) ? [] : noms(e.name)));
67
+ const GENRES = [
68
+ [ts.isFunctionDeclaration, 'fonction'],
69
+ [ts.isClassDeclaration, 'classe'],
70
+ [ts.isInterfaceDeclaration, 'interface'],
71
+ [ts.isTypeAliasDeclaration, 'type'],
72
+ [ts.isEnumDeclaration, 'enum'],
73
+ [ts.isModuleDeclaration, 'espace'],
74
+ ];
75
+ for (const st of sf.statements) {
76
+ const genre = GENRES.find(([est]) => est(st))?.[1];
77
+ if (genre) {
78
+ ajouter(st.name ? st.name.text : 'default', genre, st.name ?? null, st);
79
+ } else if (ts.isVariableStatement(st)) {
80
+ const decls = st.declarationList.declarations;
81
+ for (const d of decls) {
82
+ for (const n of noms(d.name)) ajouter(n.text, 'constante', n, decls.length > 1 ? d : st);
83
+ }
84
+ } else if (ts.isExportDeclaration(st)) {
85
+ const clause = st.exportClause;
86
+ if (clause && ts.isNamedExports(clause)) {
87
+ for (const s of clause.elements) ajouter(s.name.text, 'export', s.name, st);
88
+ } else if (clause) {
89
+ ajouter(clause.name.text, 'export', clause.name, st);
90
+ } else {
91
+ ajouter(`* from ${st.moduleSpecifier.text}`, 'export', null, st);
92
+ }
93
+ } else if (ts.isExportAssignment(st)) {
94
+ ajouter('default', 'export', null, st);
95
+ }
96
+ }
97
+ return out;
98
+ }
99
+
100
+ // The declarations of an API report's text <texte>, as declarationsDe gives them, each line that
101
+ // of the report file; an empty Map without a report.
102
+ function declarationsDuRapport(ts, texte) {
103
+ if (texte === null) return new Map();
104
+ const lignes = texte.split('\n');
105
+ const debut = lignes.indexOf(`${CLOTURE}ts`);
106
+ if (debut < 0) return new Map();
107
+ const fin = lignes.indexOf(CLOTURE, debut + 1);
108
+ const code = lignes.slice(debut + 1, fin < 0 ? undefined : fin).join('\n');
109
+ const sf = ts.createSourceFile('rapport.d.ts', code, ts.ScriptTarget.Latest, true);
110
+ const decls = declarationsDe(ts, sf);
111
+ for (const d of decls.values()) {
112
+ d.ligne += debut + 1;
113
+ }
114
+ return decls;
115
+ }
116
+
117
+ // The elements the lot of the copy <copie> adds, changes or removes between the commits <avant>
118
+ // and <apres>: `{ etat: 'fait', raison: null, elements, horsDuService, service }`, each element
119
+ // `{ id, nom, genre, statut, paquet, fichier, ligne, exporte }` (`id` `<fichier>#<nom>`, `statut`
120
+ // neuf, changé or supprimé, `fichier` from the copy, `ligne` in the file after, or before for a
121
+ // removed one), sorted by file, line and name, `horsDuService` the changed sources the language
122
+ // service does not read, `service` null when it ran, else why it could not (no `tsconfig.json`
123
+ // in the components); or `{ etat: 'impossible', raison, elements: [], horsDuService: [] }`.
124
+ export function elementsDuLot(copie, { avant, apres }) {
125
+ try {
126
+ return calculer(copie, avant, apres);
127
+ } catch (e) {
128
+ return impossible(`erreur : ${e.message.split('\n')[0]}`);
129
+ }
130
+ }
131
+
132
+ const impossible = raison => ({ etat: 'impossible', raison, elements: [], horsDuService: [] });
133
+
134
+ function calculer(copie, avant, apres) {
135
+ const r = git(copie, 'diff', '--name-status', '-z', '--no-renames', avant, apres);
136
+ if (r.status !== 0) {
137
+ return impossible(`git diff ${avant} ${apres} : ${r.stderr.trim().split('\n')[0]}`);
138
+ }
139
+ const champs = r.stdout.split('\0').filter(Boolean);
140
+ const changes = [];
141
+ for (let i = 0; i + 1 < champs.length; i += 2) {
142
+ changes.push({ fichier: champs[i + 1], supprime: champs[i] === 'D' });
143
+ }
144
+ const ts = requis('typescript');
145
+ const tous = composants(copie);
146
+ const dirs = [...tous].sort((a, b) => b.dir.length - a.dir.length);
147
+ const composantDe = f => dirs.find(c => path.resolve(copie, f).startsWith(c.dir + path.sep));
148
+ const s = changes.length ? leService(copie, tous) : {};
149
+ const lus = new Map((s.sources ?? []).map(x => [path.resolve(x.sf.fileName), x]));
150
+ const nomDe = c => c.paquet ?? enPosix(path.relative(copie, c.dir));
151
+
152
+ // The reports of a component, before and after the lot, read once.
153
+ const rapports = new Map();
154
+ const rapportsDe = c => {
155
+ if (!rapports.has(c)) {
156
+ const f = path.relative(copie, path.join(c.dir, RAPPORT));
157
+ rapports.set(c, {
158
+ fichier: enPosix(f),
159
+ avant: declarationsDuRapport(ts, aLa(copie, avant, f)),
160
+ apres: declarationsDuRapport(ts, aLa(copie, apres, f)),
161
+ });
162
+ }
163
+ return rapports.get(c);
164
+ };
165
+
166
+ const elements = [];
167
+ const horsDuService = [];
168
+ for (const { fichier, supprime } of changes) {
169
+ const c = composantDe(fichier);
170
+ const abs = path.resolve(copie, fichier);
171
+ if (!c || !SOURCE.test(fichier) || estTest(c, abs)) continue;
172
+ const lu = supprime ? null : lus.get(abs);
173
+ const lisible = supprime
174
+ ? !s.raison && existsSync(path.join(c.dir, 'tsconfig.json'))
175
+ : Boolean(lu);
176
+ if (!lisible) {
177
+ horsDuService.push(enPosix(fichier));
178
+ continue;
179
+ }
180
+ const texteAvant = aLa(copie, avant, fichier);
181
+ const decAvant =
182
+ texteAvant === null
183
+ ? new Map()
184
+ : declarationsDe(ts, ts.createSourceFile(abs, texteAvant, ts.ScriptTarget.Latest, true));
185
+ const decApres = lu ? declarationsDe(ts, lu.sf) : new Map();
186
+ const { avant: rapportAvant, apres: rapportApres } = rapportsDe(c);
187
+ const element = (d, statut, exporte) =>
188
+ elements.push({
189
+ id: `${enPosix(fichier)}#${d.nom}`,
190
+ nom: d.nom,
191
+ genre: d.genre,
192
+ statut,
193
+ paquet: nomDe(c),
194
+ fichier: enPosix(fichier),
195
+ ligne: d.ligne,
196
+ exporte,
197
+ });
198
+ const exporteApres = d =>
199
+ rapportApres.has(d.nom) || (d.noeud ? s.publie(c, d.noeud) : abs === s.entree(c));
200
+ for (const d of decApres.values()) {
201
+ const avantLui = decAvant.get(d.nom);
202
+ if (!avantLui) element(d, 'neuf', exporteApres(d));
203
+ else if (avantLui.texte !== d.texte) element(d, 'changé', exporteApres(d));
204
+ }
205
+ for (const d of decAvant.values()) {
206
+ if (!decApres.has(d.nom)) element(d, 'supprimé', rapportAvant.has(d.nom));
207
+ }
208
+ }
209
+
210
+ // The declarations of the reports the lot changes that no element of their sources names.
211
+ const changesEnSet = new Set(changes.map(x => enPosix(x.fichier)));
212
+ for (const c of tous) {
213
+ const { fichier, avant: a, apres: p } = rapportsDe(c);
214
+ if (!changesEnSet.has(fichier)) continue;
215
+ const nommes = new Set(elements.filter(e => e.paquet === nomDe(c)).map(e => e.nom));
216
+ const ajouter = (d, statut) =>
217
+ nommes.has(d.nom) ||
218
+ elements.push({
219
+ id: `${fichier}#${d.nom}`,
220
+ nom: d.nom,
221
+ genre: d.genre,
222
+ statut,
223
+ paquet: nomDe(c),
224
+ fichier,
225
+ ligne: d.ligne,
226
+ exporte: true,
227
+ });
228
+ for (const d of p.values()) {
229
+ const avantLui = a.get(d.nom);
230
+ if (!avantLui) ajouter(d, 'neuf');
231
+ else if (avantLui.texte !== d.texte) ajouter(d, 'changé');
232
+ }
233
+ for (const d of a.values()) if (!p.has(d.nom)) ajouter(d, 'supprimé');
234
+ }
235
+
236
+ elements.sort(
237
+ (x, y) => x.fichier.localeCompare(y.fichier) || x.ligne - y.ligne || x.nom.localeCompare(y.nom)
238
+ );
239
+ return {
240
+ etat: 'fait',
241
+ raison: null,
242
+ elements,
243
+ horsDuService: horsDuService.sort(),
244
+ service: s.raison ?? null,
245
+ };
246
+ }