@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,336 @@
1
+ #!/usr/bin/env node
2
+ // The register of finished agents: one JSON line per agent that ends, written by a hook of the
3
+ // frame's plugin (`grillhouse registre`: SubagentStop for a sub-agent, SessionEnd for a session, a
4
+ // role agent launched in a shell included). An agent resumed by a message has one line per stop; the board
5
+ // reads an agent's latest line, and the transcripts of running ones. The circuit of a ticket
6
+ // (scripts/circuit.mjs) writes a line at the end of each step it leads itself, with no agent
7
+ // (`ligneDEtape`: its role `circuit`, its step and component, one turn, no tokens).
8
+ //
9
+ // - The line: the project (the main tree of the directory the session opened in, which the hook
10
+ // reads in CLAUDE_PROJECT_DIR, else in its input's `cwd`), the agent, its ticket (« TON TICKET : »; a sub-agent
11
+ // whose prompt names none takes that of the regulator that launched it), its role,
12
+ // its start and end, its working time and the turns it sums (start and end in milliseconds, the
13
+ // waits for a message left out), its tokens, its cache reads and its cost when the session wrote
14
+ // one. All of it is read from the agent's transcript; the agent declares nothing.
15
+ // - The register is `~/.claude/grillhouse/registre.jsonl` (GRILLHOUSE_REGISTRE replaces it): the
16
+ // envelope of a role agent leaves it writable.
17
+ // - A hook never stops the session, and never waits for a transcript: it hands the fields of its
18
+ // input that the line reads to a detached process (`--ecrire`) and ends at once, so a session of several hundred MB closes
19
+ // before Claude Code's hook time limit. The detached process reads the transcript in chunks
20
+ // (`lireFichier` in tableau.mjs), never whole; a transcript it cannot read writes nothing.
21
+ // - One stop has one line: the write holds the register's lock (`prendreVerrou`, judged by the
22
+ // lock's age, since a role agent's envelope and the outside do not share process numbers),
23
+ // and skips a line whose agent and end the register already holds. The hook and
24
+ // `--rattraper` write the same way.
25
+ // - `npx grillhouse registre --copie <copie>` writes the lines of the sessions of an agent's copy
26
+ // the register does not hold yet, whatever their age: the launcher (scripts/enveloppe/lancer.sh)
27
+ // runs it once the envelope has ended, outside it, so a role agent's line no longer rests on its
28
+ // end hook alone.
29
+ // - `npx grillhouse registre --rattraper` writes the lines of the project's finished agents the
30
+ // register does not hold yet: those that ended before the hook existed, read from their
31
+ // transcripts (the project's sessions and their sub-agents, the agents' copies). A transcript
32
+ // that the board shows running (`tourne` in tableau.mjs: moved in the last 2 minutes, or, for a
33
+ // sub-agent, its last tool call awaiting its answer within 30 minutes) waits for its hook.
34
+ import { execFileSync, spawn } from 'node:child_process';
35
+ import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync } from 'node:fs';
36
+ import os from 'node:os';
37
+ import path from 'node:path';
38
+ import { pathToFileURL } from 'node:url';
39
+ import {
40
+ lireFichier,
41
+ ROLE_DU_CIRCUIT,
42
+ roleDeLAgent,
43
+ roleDuMeta,
44
+ ticketDuRegulateur,
45
+ tourne,
46
+ } from './tableau.mjs';
47
+ import { lacherVerrou, prendreVerrou } from './verrou-de-fichier.mjs';
48
+
49
+ const REGISTRE =
50
+ process.env.GRILLHOUSE_REGISTRE ??
51
+ path.join(os.homedir(), '.claude', 'grillhouse', 'registre.jsonl');
52
+
53
+ // The transcript and the role the hook's input names: a sub-agent's sits next to the session's.
54
+ export function source(input) {
55
+ if (input.hook_event_name === 'SubagentStop') {
56
+ const transcription =
57
+ input.agent_transcript_path ??
58
+ (input.agent_id && input.transcript_path
59
+ ? path.join(
60
+ input.transcript_path.replace(/\.jsonl$/, ''),
61
+ 'subagents',
62
+ `agent-${input.agent_id}.jsonl`
63
+ )
64
+ : null);
65
+ // The meta file names the type the sub-agent was spawned as; the input's `agent_type` is a
66
+ // named agent's name.
67
+ let role = null;
68
+ if (transcription) {
69
+ try {
70
+ role = roleDuMeta(
71
+ JSON.parse(readFileSync(transcription.replace(/\.jsonl$/, '.meta.json'), 'utf8'))
72
+ );
73
+ } catch {
74
+ // A sub-agent without its meta file takes the input's type.
75
+ }
76
+ }
77
+ role ??= roleDeLAgent(input.agent_type ?? null);
78
+ return { transcription, role, agent: input.agent_id ?? null };
79
+ }
80
+ return {
81
+ transcription: input.transcript_path ?? null,
82
+ role: null,
83
+ agent: input.session_id ?? null,
84
+ };
85
+ }
86
+
87
+ // The register's line for this hook input and the reading `t` of its transcript (`lireFichier` or
88
+ // `lireTranscription`), or null. `projet` is the main tree of the session.
89
+ export function ligne(input, t, projet) {
90
+ const { transcription, role, agent } = source(input);
91
+ if (t.premier === null) {
92
+ return null;
93
+ }
94
+ return {
95
+ projet,
96
+ agent,
97
+ ticket: t.ticket ?? (transcription ? ticketDuRegulateur(transcription) : null),
98
+ role: role ?? t.role,
99
+ debut: new Date(t.premier).toISOString(),
100
+ fin: new Date(t.dernier).toISOString(),
101
+ travail: t.travail,
102
+ tours: t.tours,
103
+ jetons: t.jetons,
104
+ cache: t.cache,
105
+ cout: t.cout,
106
+ };
107
+ }
108
+
109
+ // The register's line of a step the circuit of <ticket> leads itself (scripts/circuit.mjs), from
110
+ // <debut> to <fin> (ISO dates): one agent per step, its role `circuit`, one turn, no tokens.
111
+ export function ligneDEtape({ projet, ticket, etape, composant, debut, fin }) {
112
+ const [d, f] = [Date.parse(debut), Date.parse(fin)];
113
+ return {
114
+ projet,
115
+ agent: `${ROLE_DU_CIRCUIT}:${ticket}:${debut}`,
116
+ ticket,
117
+ role: ROLE_DU_CIRCUIT,
118
+ etape,
119
+ composant,
120
+ debut,
121
+ fin,
122
+ travail: f - d,
123
+ tours: [[d, f]],
124
+ jetons: 0,
125
+ cache: 0,
126
+ cout: null,
127
+ };
128
+ }
129
+
130
+ // The main tree of a directory: an agent's copy belongs to the repository it was made from.
131
+ function arbrePrincipal(dir) {
132
+ try {
133
+ const commun = execFileSync(
134
+ 'git',
135
+ ['-C', dir, 'rev-parse', '--path-format=absolute', '--git-common-dir'],
136
+ {
137
+ encoding: 'utf8',
138
+ stdio: ['ignore', 'pipe', 'ignore'],
139
+ }
140
+ ).trim();
141
+ return path.dirname(commun);
142
+ } catch {
143
+ return dir;
144
+ }
145
+ }
146
+
147
+ const enTirets = p => p.replace(/[^A-Za-z0-9]/g, '-');
148
+
149
+ // The hook inputs of the project's finished agents that `connus` (agent ids) does not hold.
150
+ export function aRattraper(racine, home, connus, maintenant = Date.now()) {
151
+ const projets = path.join(home, '.claude', 'projects');
152
+ if (!existsSync(projets)) {
153
+ return [];
154
+ }
155
+ const out = [];
156
+ const fini = (f, sousAgent) => !tourne(f, maintenant, sousAgent);
157
+ const jsonl = d => (existsSync(d) ? readdirSync(d).filter(n => n.endsWith('.jsonl')) : []);
158
+ const propre = path.join(projets, enTirets(racine));
159
+ const copies = `${enTirets(path.join(racine, '.claude', 'worktrees'))}-`;
160
+ for (const d of readdirSync(projets)) {
161
+ const dir = path.join(projets, d);
162
+ if (d !== enTirets(racine) && !d.startsWith(copies)) {
163
+ continue;
164
+ }
165
+ for (const n of jsonl(dir)) {
166
+ const f = path.join(dir, n);
167
+ const session = n.replace(/\.jsonl$/, '');
168
+ if (!connus.has(session) && fini(f, false)) {
169
+ out.push({
170
+ hook_event_name: 'SessionEnd',
171
+ transcript_path: f,
172
+ session_id: session,
173
+ cwd: racine,
174
+ });
175
+ }
176
+ if (dir !== propre) {
177
+ continue;
178
+ }
179
+ const sous = path.join(dir, session, 'subagents');
180
+ for (const m of jsonl(sous)) {
181
+ const agent = m.replace(/^agent-/, '').replace(/\.jsonl$/, '');
182
+ const g = path.join(sous, m);
183
+ if (!connus.has(agent) && fini(g, true)) {
184
+ out.push({
185
+ hook_event_name: 'SubagentStop',
186
+ transcript_path: f,
187
+ agent_id: agent,
188
+ agent_transcript_path: g,
189
+ cwd: racine,
190
+ });
191
+ }
192
+ }
193
+ }
194
+ }
195
+ return out;
196
+ }
197
+
198
+ // How long a register lock may stand before it counts as abandoned: a write holds it for the
199
+ // time of one read and one append.
200
+ const PERIME = 30_000;
201
+
202
+ // Appends `l` to the register unless it already holds the line of this agent's end. Returns whether
203
+ // it wrote. The check and the append happen under the register's lock.
204
+ export function ecrire(l) {
205
+ mkdirSync(path.dirname(REGISTRE), { recursive: true });
206
+ const verrou = `${REGISTRE}.verrou`;
207
+ prendreVerrou(verrou, { attendre: true, perime: PERIME });
208
+ try {
209
+ const deja = existsSync(REGISTRE) ? readFileSync(REGISTRE, 'utf8') : '';
210
+ for (const x of deja.split('\n')) {
211
+ try {
212
+ const e = JSON.parse(x);
213
+ if (e.agent === l.agent && e.fin === l.fin) {
214
+ return false;
215
+ }
216
+ } catch {
217
+ // A torn line holds nothing.
218
+ }
219
+ }
220
+ appendFileSync(REGISTRE, `${JSON.stringify(l)}\n`);
221
+ return true;
222
+ } finally {
223
+ lacherVerrou(verrou);
224
+ }
225
+ }
226
+
227
+ // Reads the transcript of `input` in chunks and writes its line; false when nothing was written.
228
+ function ecrireLaLigne(input, projet) {
229
+ const { transcription } = source(input);
230
+ if (!transcription || !existsSync(transcription)) {
231
+ return false;
232
+ }
233
+ const l = ligne(input, lireFichier(transcription), projet);
234
+ return l ? ecrire(l) : false;
235
+ }
236
+
237
+ // The directory where Claude Code keeps the transcripts of the sessions of an agent's copy
238
+ // (`copie`), in the home <home>: its path in dashes under `~/.claude/projects`. The circuit
239
+ // (scripts/circuit.mjs) watches it.
240
+ export const transcriptionsDeLaCopie = (copie, home) =>
241
+ path.join(home, '.claude', 'projects', enTirets(copie));
242
+
243
+ // The sessions of an agent's copy (`copie`) the register does not hold yet (`connus`), as the end
244
+ // hook would hand them: the launcher calls it once the envelope has ended, so every session of
245
+ // the copy is finished, however recently its transcript moved.
246
+ export function deLaCopie(copie, home, connus) {
247
+ const dir = transcriptionsDeLaCopie(copie, home);
248
+ if (!existsSync(dir)) {
249
+ return [];
250
+ }
251
+ return readdirSync(dir)
252
+ .filter(n => n.endsWith('.jsonl'))
253
+ .map(n => n.replace(/\.jsonl$/, ''))
254
+ .filter(session => !connus.has(session))
255
+ .map(session => ({
256
+ hook_event_name: 'SessionEnd',
257
+ session_id: session,
258
+ transcript_path: path.join(dir, `${session}.jsonl`),
259
+ cwd: copie,
260
+ }));
261
+ }
262
+
263
+ // The agents the register holds.
264
+ function connusDuRegistre() {
265
+ const deja = existsSync(REGISTRE) ? readFileSync(REGISTRE, 'utf8') : '';
266
+ return new Set(
267
+ deja
268
+ .split('\n')
269
+ .map(l => {
270
+ try {
271
+ return JSON.parse(l).agent;
272
+ } catch {
273
+ return null;
274
+ }
275
+ })
276
+ .filter(Boolean)
277
+ );
278
+ }
279
+
280
+ // The fields of the hook input that `source` and `ligne` read: the detached process receives these
281
+ // only, since an input also carries the agent's last message and an argument is bound to 128 KiB.
282
+ const CHAMPS = [
283
+ 'hook_event_name',
284
+ 'transcript_path',
285
+ 'session_id',
286
+ 'agent_id',
287
+ 'agent_type',
288
+ 'agent_transcript_path',
289
+ 'cwd',
290
+ ];
291
+
292
+ const direct = import.meta.url === pathToFileURL(process.argv[1] ?? '').href;
293
+ if (direct && process.argv.includes('--rattraper')) {
294
+ const racine = arbrePrincipal(process.cwd());
295
+ let n = 0;
296
+ for (const input of aRattraper(racine, os.homedir(), connusDuRegistre())) {
297
+ try {
298
+ if (ecrireLaLigne(input, racine)) {
299
+ n++;
300
+ }
301
+ } catch {
302
+ // A transcript it cannot read waits for the next catch-up.
303
+ }
304
+ }
305
+ process.stdout.write(`${n} agent(s) fini(s) ajouté(s) au registre.\n`);
306
+ } else if (direct && process.argv[2] === '--copie') {
307
+ const copie = path.resolve(process.argv[3] ?? '.');
308
+ for (const input of deLaCopie(copie, os.homedir(), connusDuRegistre())) {
309
+ try {
310
+ ecrireLaLigne(input, arbrePrincipal(copie));
311
+ } catch {
312
+ // A transcript it cannot read writes nothing.
313
+ }
314
+ }
315
+ } else if (direct && process.argv[2] === '--ecrire') {
316
+ try {
317
+ const input = JSON.parse(process.argv[3]);
318
+ ecrireLaLigne(input, arbrePrincipal(input.projet_dir ?? input.cwd ?? process.cwd()));
319
+ } catch {
320
+ // A reading that fails writes nothing.
321
+ }
322
+ } else if (direct) {
323
+ try {
324
+ const input = JSON.parse(readFileSync(0, 'utf8'));
325
+ const retenu = Object.fromEntries(CHAMPS.filter(c => input[c] != null).map(c => [c, input[c]]));
326
+ // The directory the session opened in: the last directory of its shell (`cwd`) may lie where
327
+ // git finds no repository, a mount of the envelope, where git stops at the boundary.
328
+ if (process.env.CLAUDE_PROJECT_DIR) retenu.projet_dir = process.env.CLAUDE_PROJECT_DIR;
329
+ spawn(process.execPath, [process.argv[1], '--ecrire', JSON.stringify(retenu)], {
330
+ detached: true,
331
+ stdio: 'ignore',
332
+ }).unref();
333
+ } catch {
334
+ // A hook never stops the session.
335
+ }
336
+ }