@roomi-fields/grillhouse 0.0.0-stage → 0.23.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.
Files changed (166) hide show
  1. package/.claude-plugin/plugin.json +9 -0
  2. package/METACADRE.md +41 -0
  3. package/agents/developpeur.md +10 -0
  4. package/agents/explorateur.md +9 -0
  5. package/agents/questionneur.md +11 -0
  6. package/agents/referent-technique.md +9 -0
  7. package/agents/regulateur.md +11 -0
  8. package/agents/relecteur.md +9 -0
  9. package/agents/testwriter.md +10 -0
  10. package/bin/grillhouse.mjs +125 -0
  11. package/cadre/templates/projet/.claude/settings.json +30 -0
  12. package/cadre/templates/projet/.editorconfig +47 -0
  13. package/cadre/templates/projet/.env.example +57 -0
  14. package/cadre/templates/projet/.eslintrc.cjs +56 -0
  15. package/cadre/templates/projet/.github/workflows/ci.yml +66 -0
  16. package/cadre/templates/projet/.mcp.json +13 -0
  17. package/cadre/templates/projet/.prettierrc +11 -0
  18. package/cadre/templates/projet/CHANGELOG.md +61 -0
  19. package/cadre/templates/projet/CLAUDE.md +46 -0
  20. package/cadre/templates/projet/CONTRIBUTING.md +243 -0
  21. package/cadre/templates/projet/CREDITS.md +41 -0
  22. package/cadre/templates/projet/README.md +152 -0
  23. package/cadre/templates/projet/_gitignore +91 -0
  24. package/cadre/templates/projet/_npmignore +46 -0
  25. package/cadre/templates/projet/deployment/README.md +98 -0
  26. package/cadre/templates/projet/deployment/docs/01-INSTALL.md +145 -0
  27. package/cadre/templates/projet/deployment/docs/02-CONFIGURATION.md +117 -0
  28. package/cadre/templates/projet/deployment/docs/03-API.md +264 -0
  29. package/cadre/templates/projet/deployment/docs/04-TROUBLESHOOTING.md +262 -0
  30. package/cadre/templates/projet/deployment/scripts/install.ps1 +100 -0
  31. package/cadre/templates/projet/deployment/scripts/start.ps1 +61 -0
  32. package/cadre/templates/projet/deployment/scripts/stop.ps1 +37 -0
  33. package/cadre/templates/projet/deployment/scripts/test.ps1 +118 -0
  34. package/cadre/templates/projet/docs/agents/issue-tracker.md +44 -0
  35. package/cadre/templates/projet/ecosystem.config.cjs +73 -0
  36. package/cadre/templates/projet/package.json +90 -0
  37. package/cadre/templates/projet/pitmaster/SUIVI.md +22 -0
  38. package/cadre/templates/projet/src/config.ts +68 -0
  39. package/cadre/templates/projet/src/errors.ts +102 -0
  40. package/cadre/templates/projet/src/index.ts +46 -0
  41. package/cadre/templates/projet/src/services/.gitkeep +8 -0
  42. package/cadre/templates/projet/src/types/index.ts +24 -0
  43. package/cadre/templates/projet/src/utils/logger.ts +106 -0
  44. package/cadre/templates/projet/tests/integration/.gitkeep +7 -0
  45. package/cadre/templates/projet/tests/unit/config.spec.ts +68 -0
  46. package/cadre/templates/projet/tests/unit/errors.spec.ts +132 -0
  47. package/cadre/templates/projet/tests/unit/types.spec.ts +73 -0
  48. package/cadre/templates/projet/tsconfig.eslint.json +9 -0
  49. package/cadre/templates/projet/tsconfig.json +41 -0
  50. package/cadre/templates/projet/tsdoc.json +6 -0
  51. package/cadre/templates/projet/vitest.config.ts +44 -0
  52. package/cadre/templates/turbo/paquets.json +29 -0
  53. package/cadre/templates/turbo/sans-paquets.json +28 -0
  54. package/charte.md +107 -0
  55. package/docs/ARCHITECTURE.md +90 -0
  56. package/hooks/crochet.mjs +33 -0
  57. package/hooks/hooks.json +58 -0
  58. package/hooks/register.tsx +558 -0
  59. package/package.json +75 -4
  60. package/scripts/avancement.mjs +244 -0
  61. package/scripts/bilan.mjs +523 -0
  62. package/scripts/branche-de-nuit.mjs +59 -0
  63. package/scripts/cadre.mjs +109 -0
  64. package/scripts/circuit.mjs +4375 -0
  65. package/scripts/code-mort.mjs +371 -0
  66. package/scripts/composants-affectes.mjs +598 -0
  67. package/scripts/consommateurs-arbitres.mjs +192 -0
  68. package/scripts/consommateurs.mjs +310 -0
  69. package/scripts/crochet.mjs +24 -0
  70. package/scripts/crochets.mjs +135 -0
  71. package/scripts/dernier-vert.mjs +208 -0
  72. package/scripts/dette.mjs +181 -0
  73. package/scripts/droits.mjs +354 -0
  74. package/scripts/enveloppe/codes.mjs +17 -0
  75. package/scripts/enveloppe/composant.mjs +11 -0
  76. package/scripts/enveloppe/enveloppe.mjs +628 -0
  77. package/scripts/enveloppe/lancer.sh +311 -0
  78. package/scripts/etapes.mjs +323 -0
  79. package/scripts/etat-du-circuit.mjs +104 -0
  80. package/scripts/frontieres.mjs +456 -0
  81. package/scripts/gardes-du-cadre.mjs +72 -0
  82. package/scripts/greffons.mjs +338 -0
  83. package/scripts/grillhouse-maj.mjs +514 -0
  84. package/scripts/index-interfaces.mjs +293 -0
  85. package/scripts/init.mjs +117 -0
  86. package/scripts/integration/file-attente.mjs +61 -0
  87. package/scripts/integration/file.mjs +494 -0
  88. package/scripts/integration/integrer.mjs +381 -0
  89. package/scripts/integration/rejeu.mjs +139 -0
  90. package/scripts/integration/verdict.mjs +13 -0
  91. package/scripts/interfaces-contre-code.mjs +547 -0
  92. package/scripts/ligne-etat/defaut.sh +110 -0
  93. package/scripts/ligne-etat/ligne-etat.mjs +93 -0
  94. package/scripts/migrations/0.23.0-charte-importee.mjs +47 -0
  95. package/scripts/migrations/0.23.0-config-vitest.mjs +105 -0
  96. package/scripts/migrations/0.23.0-crochets-de-seance.mjs +55 -0
  97. package/scripts/migrations/0.23.0-marche-du-projet.mjs +82 -0
  98. package/scripts/migrations.mjs +43 -0
  99. package/scripts/nuit.sh +269 -0
  100. package/scripts/paquets.mjs +79 -0
  101. package/scripts/poussee-cles.mjs +46 -0
  102. package/scripts/preparation.mjs +84 -0
  103. package/scripts/references-par-les-types.mjs +420 -0
  104. package/scripts/registre.mjs +336 -0
  105. package/scripts/resultats.mjs +580 -0
  106. package/scripts/resumes.mjs +57 -0
  107. package/scripts/retirer-copie.sh +127 -0
  108. package/scripts/session-start.sh +203 -0
  109. package/scripts/sortie.mjs +155 -0
  110. package/scripts/sous-modules.sh +316 -0
  111. package/scripts/structure-guard.sh +27 -0
  112. package/scripts/superviseur.mjs +102 -0
  113. package/scripts/tableau.mjs +1886 -0
  114. package/scripts/tests.mjs +236 -0
  115. package/scripts/ticket.sh +23 -0
  116. package/scripts/turbo-des-paquets.mjs +899 -0
  117. package/scripts/un-commit-par-ticket.mjs +212 -0
  118. package/scripts/verifier.mjs +469 -0
  119. package/scripts/verrou-de-fichier.mjs +130 -0
  120. package/scripts/verrou-de-machine.mjs +211 -0
  121. package/scripts/verrous/commande.mjs +180 -0
  122. package/scripts/verrous/ticket-du-sous-agent.mjs +39 -0
  123. package/scripts/verrous/verrou.mjs +165 -0
  124. package/scripts/vitest-config/avancement.mjs +39 -0
  125. package/scripts/vitest-config/avancement.reporter.mjs +56 -0
  126. package/scripts/vitest-config/base-de-passe.mjs +128 -0
  127. package/scripts/vitest-config/base.d.ts +6 -0
  128. package/scripts/vitest-config/base.mjs +87 -0
  129. package/scripts/vitest-config/changements.mjs +173 -0
  130. package/scripts/vitest-config/cibles.mjs +207 -0
  131. package/scripts/vitest-config/duree.reporter.mjs +98 -0
  132. package/scripts/vitest-config/machine.setup.mjs +14 -0
  133. package/scripts/vitest-config/mesure.d.ts +6 -0
  134. package/scripts/vitest-config/mesure.mjs +13 -0
  135. package/scripts/vitest-config/mesure.setup.mjs +12 -0
  136. package/scripts/vitest-config/pression.mjs +90 -0
  137. package/scripts/vitest-config/resultats.mjs +23 -0
  138. package/scripts/vitest-config/resultats.setup.mjs +19 -0
  139. package/scripts/vitest.nuit.config.mjs +26 -0
  140. package/skills/developpeur/SKILL.md +175 -0
  141. package/skills/explorateur/SKILL.md +84 -0
  142. package/skills/grill/SKILL.md +238 -0
  143. package/skills/grill/references/releve-structurel.md +38 -0
  144. package/skills/mesure/SKILL.md +168 -0
  145. package/skills/pitmaster/SKILL.md +382 -0
  146. package/skills/pitmaster/references/cadre.md +119 -0
  147. package/skills/pitmaster/references/consigne-agent.md +308 -0
  148. package/skills/pitmaster/references/erreurs.md +25 -0
  149. package/skills/pitmaster/references/tour.md +124 -0
  150. package/skills/questionneur/SKILL.md +64 -0
  151. package/skills/redacteur/SKILL.md +84 -0
  152. package/skills/redacteur/references/documents-d-un-composant.md +105 -0
  153. package/skills/referent-technique/SKILL.md +145 -0
  154. package/skills/regulateur/SKILL.md +220 -0
  155. package/skills/release/SKILL.md +134 -0
  156. package/skills/release/scripts/update-version.cjs +92 -0
  157. package/skills/relecteur/SKILL.md +152 -0
  158. package/skills/relecteur/references/LICENSE-MIT +30 -0
  159. package/skills/relecteur/references/SOURCE.md +8 -0
  160. package/skills/relecteur/references/claims-check.md +14 -0
  161. package/skills/relecteur/references/deletion-check.md +14 -0
  162. package/skills/relecteur/references/edge-case-hunter.md +80 -0
  163. package/skills/relecteur/references/verification-gap.md +113 -0
  164. package/skills/testwriter/SKILL.md +187 -0
  165. package/types/index.d.ts +56 -0
  166. package/README.md +0 -3
@@ -0,0 +1,244 @@
1
+ // The progress of an automatic pass, in figures, as Bazel's counter of actions « [1 234 / 2 000] »
2
+ // and the steps of a CI: the files of tests done over planned, and the instant of the last sign,
3
+ // which the board shows (scripts/tableau.mjs), so that a pass that freezes is seen.
4
+ //
5
+ // - A pass has its progress directory, which the variable GRILLHOUSE_AVANCEMENT (AVANCEMENT of
6
+ // scripts/vitest-config/avancement.mjs) names to its commands: the circuit opens one for each
7
+ // automatic pass and names it in its state (scripts/circuit.mjs), `npm run verifier` opens its own when it
8
+ // receives none (scripts/verifier.mjs). The directory holds `passe.json` (`{ genre, debut,
9
+ // precedent }`: its kind, its start, the total of the previous pass of that kind), the records
10
+ // of its tasks (`taches/`, written by the reporter of progress of the base configuration, one
11
+ // per run of Vitest, and by the queue for the steps of its CI, scripts/integration/file.mjs),
12
+ // and the plans of its runs of Turborepo (`plan/`).
13
+ // - The reading adds the records: the files done, and the files planned of the tasks started;
14
+ // while every task the plans name has not started, the total of the previous pass of the same
15
+ // kind stands when it is greater (a pass without a plan never knows all its tasks started).
16
+ // The last sign is the latest writing of a record, or the start of the pass without one.
17
+ // - A plan: before a run of Turborepo that plays tests, its plan (`turbo run … --dry=json`) names
18
+ // the tasks of tests and those Turborepo replays from its cache; a task replayed counts its files
19
+ // as done at once, its number known by its hash from the pass that played it, as a cache of
20
+ // Bazel keeps the outcome of an action by its key.
21
+ // - The totals live in `<git common dir>/grillhouse/avancement.json`, one per repository, its
22
+ // copies included, beside the last green (scripts/dernier-vert.mjs): `{ passes, taches }`, the
23
+ // total of the last pass of each kind and the files planned of each task by its hash (the
24
+ // latest TACHES_GARDEES); each pass keeps its own at its end (`garderLeTotal`).
25
+ // - A measure out of a ticket (`npm run verifier` of a supervisor or of an integration) writes
26
+ // beside the lock of the machine (scripts/verrou-de-machine.mjs; the file of the lock with
27
+ // `.avancement.json` added, never the lock itself) its repository, its command, its progress
28
+ // directory and its process, removed at its end; the board reads it while that process lives
29
+ // and the lock is held (`lireLaMesure`).
30
+ import { spawnSync } from 'node:child_process';
31
+ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
32
+ import { tmpdir } from 'node:os';
33
+ import path from 'node:path';
34
+ import { ecrireAtomique, TACHES } from './vitest-config/avancement.mjs';
35
+ import { verrouDeMachine } from './verrou-de-machine.mjs';
36
+
37
+ const PASSE = 'passe.json';
38
+ const PLAN = 'plan';
39
+ const TACHES_GARDEES = 2_000;
40
+ const TESTS = 'test:vitest';
41
+
42
+ const lireJson = f => {
43
+ try {
44
+ return JSON.parse(readFileSync(f, 'utf8'));
45
+ } catch {
46
+ return null;
47
+ }
48
+ };
49
+ const lister = d => {
50
+ try {
51
+ return readdirSync(d).filter(f => f.endsWith('.json'));
52
+ } catch {
53
+ return [];
54
+ }
55
+ };
56
+
57
+ // The file of the totals of the repository at <racine>, null out of git.
58
+ function fichierDesTotaux(racine) {
59
+ const r = spawnSync(
60
+ 'git',
61
+ ['-C', racine, 'rev-parse', '--path-format=absolute', '--git-common-dir'],
62
+ {
63
+ encoding: 'utf8',
64
+ }
65
+ );
66
+ return r.status === 0 ? path.join(r.stdout.trim(), 'grillhouse', 'avancement.json') : null;
67
+ }
68
+ const lireLesTotaux = racine => {
69
+ const f = fichierDesTotaux(racine);
70
+ const t = f ? lireJson(f) : null;
71
+ return { fichier: f, passes: t?.passes ?? {}, taches: t?.taches ?? {} };
72
+ };
73
+
74
+ /**
75
+ * Opens the progress directory of a pass of kind <genre> (null: a pass whose total is not kept)
76
+ * in the repository at <racine>, made under <dans>, its start <debut>; its path.
77
+ * @param {{ racine: string, genre?: string | null, dans?: string, debut?: number }} o
78
+ */
79
+ export function ouvrirAvancement({ racine, genre = null, dans = tmpdir(), debut = Date.now() }) {
80
+ const dossier = mkdtempSync(path.join(dans, 'avancement-'));
81
+ const precedent = genre ? (lireLesTotaux(racine).passes[genre] ?? null) : null;
82
+ ecrireAtomique(path.join(dossier, PASSE), { genre, debut, precedent });
83
+ return dossier;
84
+ }
85
+
86
+ /**
87
+ * Before a run of Turborepo of <args> in <racine> under <env>, while <dossier> is a progress
88
+ * directory: the plan of the run, its tasks of tests and those replayed from the cache, each of
89
+ * those counted done at once. A plan that does not read changes nothing.
90
+ * @param {string | undefined} dossier
91
+ * @param {{ racine: string, args: string[], env?: Record<string, string | undefined> }} o
92
+ */
93
+ export function planifier(dossier, { racine, args, env = process.env }) {
94
+ if (!dossier) return;
95
+ const r = spawnSync('turbo', ['run', ...args.filter(a => a !== '--summarize'), '--dry=json'], {
96
+ cwd: racine,
97
+ env,
98
+ encoding: 'utf8',
99
+ maxBuffer: 256 * 1024 * 1024,
100
+ });
101
+ let plan;
102
+ try {
103
+ plan = JSON.parse(r.stdout);
104
+ } catch {
105
+ return;
106
+ }
107
+ const taches = (plan?.tasks ?? []).filter(t => t.task === TESTS && t.hash);
108
+ if (!taches.length) return;
109
+ const { taches: connues } = lireLesTotaux(racine);
110
+ try {
111
+ for (const t of taches.filter(x => x.cache?.status === 'HIT')) {
112
+ const n = connues[t.hash] ?? 0;
113
+ ecrireAtomique(path.join(dossier, TACHES, `cache-${t.hash}.json`), {
114
+ hash: t.hash,
115
+ faits: n,
116
+ prevus: n,
117
+ a: Date.now(),
118
+ cache: true,
119
+ });
120
+ }
121
+ ecrireAtomique(path.join(dossier, PLAN, `${process.pid}-${Date.now()}.json`), {
122
+ hashes: taches.map(t => t.hash),
123
+ });
124
+ } catch {
125
+ // A directory that does not write leaves the pass going, without its progress.
126
+ }
127
+ }
128
+
129
+ // The records of <dossier>, one per task: a task Turborepo replayed and played anyway counts by
130
+ // the record of its run, a task played again by its latest.
131
+ function enregistrements(dossier) {
132
+ const parHash = new Map();
133
+ const sans = [];
134
+ for (const f of lister(path.join(dossier, TACHES))) {
135
+ const e = lireJson(path.join(dossier, TACHES, f));
136
+ if (!e || !Number.isFinite(e.faits) || !Number.isFinite(e.prevus)) continue;
137
+ if (!e.hash) sans.push(e);
138
+ else {
139
+ const avant = parHash.get(e.hash);
140
+ if (!avant || (!e.cache && (avant.cache || e.a > avant.a))) parHash.set(e.hash, e);
141
+ }
142
+ }
143
+ return [...parHash.values(), ...sans];
144
+ }
145
+
146
+ /**
147
+ * The progress of the pass whose directory is <dossier>: its files done and planned, and the
148
+ * instant of its last sign (ms); null when the directory does not read.
149
+ * @param {string} dossier
150
+ * @returns {{ faits: number, prevus: number, dernier: number } | null}
151
+ */
152
+ export function lireAvancement(dossier) {
153
+ const passe = lireJson(path.join(dossier, PASSE));
154
+ if (!passe) return null;
155
+ const liste = enregistrements(dossier);
156
+ const faits = liste.reduce((s, e) => s + e.faits, 0);
157
+ const demarres = liste.reduce((s, e) => s + e.prevus, 0);
158
+ const planifies = new Set(
159
+ lister(path.join(dossier, PLAN)).flatMap(
160
+ f => lireJson(path.join(dossier, PLAN, f))?.hashes ?? []
161
+ )
162
+ );
163
+ const vus = new Set(liste.map(e => e.hash).filter(Boolean));
164
+ const tous = planifies.size > 0 && [...planifies].every(h => vus.has(h));
165
+ const prevus = tous ? demarres : Math.max(demarres, passe.precedent ?? 0);
166
+ const debut = Number(passe.debut) || 0;
167
+ const dernier = Math.max(debut, ...liste.map(e => Number(e.a) || 0));
168
+ return { faits, prevus, debut, dernier };
169
+ }
170
+
171
+ /**
172
+ * Keeps, at the end of the pass whose directory is <dossier>, its total under its kind and the
173
+ * files planned of each task it played, in the totals of the repository at <racine>.
174
+ * @param {string} dossier
175
+ * @param {string} racine
176
+ */
177
+ export function garderLeTotal(dossier, racine) {
178
+ const passe = lireJson(path.join(dossier, PASSE));
179
+ if (!passe) return;
180
+ const t = lireLesTotaux(racine);
181
+ if (!t.fichier) return;
182
+ const liste = enregistrements(dossier);
183
+ if (passe.genre && liste.length) t.passes[passe.genre] = liste.reduce((s, e) => s + e.prevus, 0);
184
+ for (const e of liste.filter(x => x.hash && !x.cache)) {
185
+ delete t.taches[e.hash];
186
+ t.taches[e.hash] = e.prevus;
187
+ }
188
+ const cles = Object.keys(t.taches);
189
+ for (const c of cles.slice(0, Math.max(0, cles.length - TACHES_GARDEES))) delete t.taches[c];
190
+ try {
191
+ ecrireAtomique(t.fichier, { passes: t.passes, taches: t.taches });
192
+ } catch {
193
+ // A place that does not write keeps no total: the next pass counts without it.
194
+ }
195
+ }
196
+
197
+ // The file of the measure out of a ticket under <env>: beside the lock of the machine.
198
+ function fichierDeLaMesure(env = process.env) {
199
+ const verrou = verrouDeMachine(env);
200
+ return { verrou, fichier: `${verrou}.avancement.json` };
201
+ }
202
+
203
+ /**
204
+ * Names beside the lock of the machine the measure out of a ticket that runs: the repository
205
+ * <depot>, the command <commande>, its progress directory <dossier>, this process; a function that
206
+ * removes it.
207
+ * @param {{ env?: Record<string, string | undefined>, depot: string, commande: string, dossier: string }} o
208
+ * @returns {() => void}
209
+ */
210
+ export function ecrireLaMesure({ env = process.env, depot, commande, dossier }) {
211
+ const { fichier } = fichierDeLaMesure(env);
212
+ try {
213
+ mkdirSync(path.dirname(fichier), { recursive: true });
214
+ ecrireAtomique(fichier, { depot, commande, dossier, pid: process.pid });
215
+ } catch {
216
+ return () => {};
217
+ }
218
+ return () => {
219
+ if (lireJson(fichier)?.pid === process.pid) rmSync(fichier, { force: true });
220
+ };
221
+ }
222
+
223
+ const vivant = pid => {
224
+ try {
225
+ process.kill(pid, 0);
226
+ return true;
227
+ } catch (e) {
228
+ return e.code === 'EPERM';
229
+ }
230
+ };
231
+
232
+ /**
233
+ * The measure out of a ticket that holds the lock of the machine under <env>: its repository, its
234
+ * command and its progress; null without one, or when its process is gone or the lock is not held.
235
+ * @param {Record<string, string | undefined>} [env]
236
+ */
237
+ export function lireLaMesure(env = process.env) {
238
+ const { verrou, fichier } = fichierDeLaMesure(env);
239
+ const m = lireJson(fichier);
240
+ if (!m || !Number.isInteger(m.pid) || !vivant(m.pid) || !existsSync(verrou)) return null;
241
+ const a = typeof m.dossier === 'string' ? lireAvancement(m.dossier) : null;
242
+ if (!a) return null;
243
+ return { depot: String(m.depot ?? ''), commande: String(m.commande ?? ''), ...a };
244
+ }