@roomi-fields/grillhouse 0.0.0-stage → 0.23.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.
- package/.claude-plugin/plugin.json +9 -0
- package/METACADRE.md +41 -0
- package/agents/developpeur.md +10 -0
- package/agents/explorateur.md +9 -0
- package/agents/questionneur.md +11 -0
- package/agents/referent-technique.md +9 -0
- package/agents/regulateur.md +11 -0
- package/agents/relecteur.md +9 -0
- package/agents/testwriter.md +10 -0
- package/bin/grillhouse.mjs +125 -0
- package/cadre/templates/projet/.claude/settings.json +30 -0
- package/cadre/templates/projet/.editorconfig +47 -0
- package/cadre/templates/projet/.env.example +57 -0
- package/cadre/templates/projet/.eslintrc.cjs +56 -0
- package/cadre/templates/projet/.github/workflows/ci.yml +66 -0
- package/cadre/templates/projet/.mcp.json +13 -0
- package/cadre/templates/projet/.prettierrc +11 -0
- package/cadre/templates/projet/CHANGELOG.md +61 -0
- package/cadre/templates/projet/CLAUDE.md +46 -0
- package/cadre/templates/projet/CONTRIBUTING.md +243 -0
- package/cadre/templates/projet/CREDITS.md +41 -0
- package/cadre/templates/projet/README.md +152 -0
- package/cadre/templates/projet/_gitignore +91 -0
- package/cadre/templates/projet/_npmignore +46 -0
- package/cadre/templates/projet/deployment/README.md +98 -0
- package/cadre/templates/projet/deployment/docs/01-INSTALL.md +145 -0
- package/cadre/templates/projet/deployment/docs/02-CONFIGURATION.md +117 -0
- package/cadre/templates/projet/deployment/docs/03-API.md +264 -0
- package/cadre/templates/projet/deployment/docs/04-TROUBLESHOOTING.md +262 -0
- package/cadre/templates/projet/deployment/scripts/install.ps1 +100 -0
- package/cadre/templates/projet/deployment/scripts/start.ps1 +61 -0
- package/cadre/templates/projet/deployment/scripts/stop.ps1 +37 -0
- package/cadre/templates/projet/deployment/scripts/test.ps1 +118 -0
- package/cadre/templates/projet/docs/agents/issue-tracker.md +44 -0
- package/cadre/templates/projet/ecosystem.config.cjs +73 -0
- package/cadre/templates/projet/package.json +90 -0
- package/cadre/templates/projet/pitmaster/SUIVI.md +22 -0
- package/cadre/templates/projet/src/config.ts +68 -0
- package/cadre/templates/projet/src/errors.ts +102 -0
- package/cadre/templates/projet/src/index.ts +46 -0
- package/cadre/templates/projet/src/services/.gitkeep +8 -0
- package/cadre/templates/projet/src/types/index.ts +24 -0
- package/cadre/templates/projet/src/utils/logger.ts +106 -0
- package/cadre/templates/projet/tests/integration/.gitkeep +7 -0
- package/cadre/templates/projet/tests/unit/config.spec.ts +68 -0
- package/cadre/templates/projet/tests/unit/errors.spec.ts +132 -0
- package/cadre/templates/projet/tests/unit/types.spec.ts +73 -0
- package/cadre/templates/projet/tsconfig.eslint.json +9 -0
- package/cadre/templates/projet/tsconfig.json +41 -0
- package/cadre/templates/projet/tsdoc.json +6 -0
- package/cadre/templates/projet/vitest.config.ts +44 -0
- package/cadre/templates/turbo/paquets.json +29 -0
- package/cadre/templates/turbo/sans-paquets.json +28 -0
- package/charte.md +107 -0
- package/docs/ARCHITECTURE.md +90 -0
- package/hooks/crochet.mjs +33 -0
- package/hooks/hooks.json +58 -0
- package/hooks/register.tsx +558 -0
- package/package.json +75 -4
- package/scripts/avancement.mjs +244 -0
- package/scripts/bilan.mjs +523 -0
- package/scripts/branche-de-nuit.mjs +59 -0
- package/scripts/cadre.mjs +109 -0
- package/scripts/circuit.mjs +4375 -0
- package/scripts/code-mort.mjs +371 -0
- package/scripts/composants-affectes.mjs +598 -0
- package/scripts/consommateurs-arbitres.mjs +192 -0
- package/scripts/consommateurs.mjs +310 -0
- package/scripts/crochet.mjs +24 -0
- package/scripts/crochets.mjs +135 -0
- package/scripts/dernier-vert.mjs +208 -0
- package/scripts/dette.mjs +181 -0
- package/scripts/droits.mjs +354 -0
- package/scripts/enveloppe/codes.mjs +17 -0
- package/scripts/enveloppe/composant.mjs +11 -0
- package/scripts/enveloppe/enveloppe.mjs +628 -0
- package/scripts/enveloppe/lancer.sh +311 -0
- package/scripts/etapes.mjs +323 -0
- package/scripts/etat-du-circuit.mjs +104 -0
- package/scripts/frontieres.mjs +456 -0
- package/scripts/gardes-du-cadre.mjs +72 -0
- package/scripts/greffons.mjs +338 -0
- package/scripts/grillhouse-maj.mjs +514 -0
- package/scripts/index-interfaces.mjs +293 -0
- package/scripts/init.mjs +117 -0
- package/scripts/integration/file-attente.mjs +61 -0
- package/scripts/integration/file.mjs +494 -0
- package/scripts/integration/integrer.mjs +381 -0
- package/scripts/integration/rejeu.mjs +139 -0
- package/scripts/integration/verdict.mjs +13 -0
- package/scripts/interfaces-contre-code.mjs +547 -0
- package/scripts/ligne-etat/defaut.sh +110 -0
- package/scripts/ligne-etat/ligne-etat.mjs +93 -0
- package/scripts/migrations/0.23.0-charte-importee.mjs +47 -0
- package/scripts/migrations/0.23.0-config-vitest.mjs +105 -0
- package/scripts/migrations/0.23.0-crochets-de-seance.mjs +55 -0
- package/scripts/migrations/0.23.0-marche-du-projet.mjs +82 -0
- package/scripts/migrations.mjs +43 -0
- package/scripts/nuit.sh +269 -0
- package/scripts/paquets.mjs +79 -0
- package/scripts/poussee-cles.mjs +46 -0
- package/scripts/preparation.mjs +84 -0
- package/scripts/references-par-les-types.mjs +420 -0
- package/scripts/registre.mjs +336 -0
- package/scripts/resultats.mjs +580 -0
- package/scripts/resumes.mjs +57 -0
- package/scripts/retirer-copie.sh +127 -0
- package/scripts/session-start.sh +203 -0
- package/scripts/sortie.mjs +155 -0
- package/scripts/sous-modules.sh +316 -0
- package/scripts/structure-guard.sh +27 -0
- package/scripts/superviseur.mjs +102 -0
- package/scripts/tableau.mjs +1886 -0
- package/scripts/tests.mjs +236 -0
- package/scripts/ticket.sh +23 -0
- package/scripts/turbo-des-paquets.mjs +899 -0
- package/scripts/un-commit-par-ticket.mjs +212 -0
- package/scripts/verifier.mjs +469 -0
- package/scripts/verrou-de-fichier.mjs +130 -0
- package/scripts/verrou-de-machine.mjs +211 -0
- package/scripts/verrous/commande.mjs +180 -0
- package/scripts/verrous/ticket-du-sous-agent.mjs +39 -0
- package/scripts/verrous/verrou.mjs +165 -0
- package/scripts/vitest-config/avancement.mjs +39 -0
- package/scripts/vitest-config/avancement.reporter.mjs +56 -0
- package/scripts/vitest-config/base-de-passe.mjs +128 -0
- package/scripts/vitest-config/base.d.ts +6 -0
- package/scripts/vitest-config/base.mjs +87 -0
- package/scripts/vitest-config/changements.mjs +173 -0
- package/scripts/vitest-config/cibles.mjs +207 -0
- package/scripts/vitest-config/duree.reporter.mjs +98 -0
- package/scripts/vitest-config/machine.setup.mjs +14 -0
- package/scripts/vitest-config/mesure.d.ts +6 -0
- package/scripts/vitest-config/mesure.mjs +13 -0
- package/scripts/vitest-config/mesure.setup.mjs +12 -0
- package/scripts/vitest-config/pression.mjs +90 -0
- package/scripts/vitest-config/resultats.mjs +23 -0
- package/scripts/vitest-config/resultats.setup.mjs +19 -0
- package/scripts/vitest.nuit.config.mjs +26 -0
- package/skills/developpeur/SKILL.md +175 -0
- package/skills/explorateur/SKILL.md +84 -0
- package/skills/grill/SKILL.md +238 -0
- package/skills/grill/references/releve-structurel.md +38 -0
- package/skills/mesure/SKILL.md +168 -0
- package/skills/pitmaster/SKILL.md +382 -0
- package/skills/pitmaster/references/cadre.md +119 -0
- package/skills/pitmaster/references/consigne-agent.md +308 -0
- package/skills/pitmaster/references/erreurs.md +25 -0
- package/skills/pitmaster/references/tour.md +124 -0
- package/skills/questionneur/SKILL.md +64 -0
- package/skills/redacteur/SKILL.md +84 -0
- package/skills/redacteur/references/documents-d-un-composant.md +105 -0
- package/skills/referent-technique/SKILL.md +145 -0
- package/skills/regulateur/SKILL.md +220 -0
- package/skills/release/SKILL.md +134 -0
- package/skills/release/scripts/update-version.cjs +92 -0
- package/skills/relecteur/SKILL.md +152 -0
- package/skills/relecteur/references/LICENSE-MIT +30 -0
- package/skills/relecteur/references/SOURCE.md +8 -0
- package/skills/relecteur/references/claims-check.md +14 -0
- package/skills/relecteur/references/deletion-check.md +14 -0
- package/skills/relecteur/references/edge-case-hunter.md +80 -0
- package/skills/relecteur/references/verification-gap.md +113 -0
- package/skills/testwriter/SKILL.md +187 -0
- package/types/index.d.ts +56 -0
- package/README.md +0 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The status line of a Grillhouse project: the person's own status line on the left, the chantier
|
|
3
|
+
// in progress on the right. The project's settings name it as their `statusLine` command.
|
|
4
|
+
//
|
|
5
|
+
// npx grillhouse ligne-etat reads Claude Code's status line input on stdin
|
|
6
|
+
//
|
|
7
|
+
// - The left part: the status line command of the person's own settings (~/.claude/settings.json),
|
|
8
|
+
// run on the same input; without one, `defaut.sh` beside this file.
|
|
9
|
+
// - The right part: `grillhouse-etat.txt` in the project's common git directory, which each
|
|
10
|
+
// measure of the board writes (`scripts/tableau.mjs`), dim, aligned on the terminal's width
|
|
11
|
+
// (COLUMNS), on the first line.
|
|
12
|
+
import { execFileSync } from 'node:child_process';
|
|
13
|
+
import { readFileSync } from 'node:fs';
|
|
14
|
+
import os from 'node:os';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const ICI = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
// The columns Claude Code keeps around the status line: two before it, two after.
|
|
20
|
+
const MARGE = 4;
|
|
21
|
+
|
|
22
|
+
// A colour sequence of the terminal (ESC [ … m).
|
|
23
|
+
const COULEUR = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g');
|
|
24
|
+
|
|
25
|
+
/** A text without its colour sequences. @param {string} texte */
|
|
26
|
+
export const sansCouleur = texte => texte.replace(COULEUR, '');
|
|
27
|
+
|
|
28
|
+
/** The cells a text takes on screen. @param {string} texte */
|
|
29
|
+
export const largeurVisible = texte => Array.from(sansCouleur(texte)).length;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The left part with the right one aligned on `colonnes`, on its first line; at least two spaces
|
|
33
|
+
* between them.
|
|
34
|
+
* @param {string} gauche
|
|
35
|
+
* @param {string} droite
|
|
36
|
+
* @param {number} colonnes
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
export function composer(gauche, droite, colonnes) {
|
|
40
|
+
if (!droite) {
|
|
41
|
+
return gauche;
|
|
42
|
+
}
|
|
43
|
+
const [premiere, ...suite] = gauche.split('\n');
|
|
44
|
+
const vide = Math.max(2, colonnes - largeurVisible(premiere) - largeurVisible(droite) - MARGE);
|
|
45
|
+
return [`${premiere}${' '.repeat(vide)}\x1b[0;90m${droite}\x1b[0m`, ...suite].join('\n');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The status line command of the person's own settings, or null: none, or this one.
|
|
50
|
+
* @param {string} home
|
|
51
|
+
* @returns {string | null}
|
|
52
|
+
*/
|
|
53
|
+
export function commandePersonnelle(home) {
|
|
54
|
+
try {
|
|
55
|
+
const s = JSON.parse(readFileSync(path.join(home, '.claude', 'settings.json'), 'utf8'));
|
|
56
|
+
const c = s.statusLine?.type === 'command' ? s.statusLine.command : null;
|
|
57
|
+
return c && !c.includes('ligne-etat/ligne-etat.mjs') ? c : null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// What a command prints on `entree`, its output kept when it fails.
|
|
64
|
+
function sortie(commande, entree, cwd) {
|
|
65
|
+
try {
|
|
66
|
+
return execFileSync('/bin/sh', ['-c', commande], { input: entree, cwd, encoding: 'utf8' });
|
|
67
|
+
} catch (e) {
|
|
68
|
+
return typeof e.stdout === 'string' ? e.stdout : '';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The chantier in progress the board last wrote for the project at `projet`, or ''.
|
|
73
|
+
function etat(projet) {
|
|
74
|
+
try {
|
|
75
|
+
const commun = execFileSync(
|
|
76
|
+
'git',
|
|
77
|
+
['-C', projet, 'rev-parse', '--path-format=absolute', '--git-common-dir'],
|
|
78
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
|
|
79
|
+
).trim();
|
|
80
|
+
return readFileSync(path.join(commun, 'grillhouse-etat.txt'), 'utf8').split('\n')[0].trim();
|
|
81
|
+
} catch {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
|
|
87
|
+
const entree = readFileSync(0, 'utf8');
|
|
88
|
+
const projet = process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
|
|
89
|
+
const commande = commandePersonnelle(os.homedir()) ?? `bash "${path.join(ICI, 'defaut.sh')}"`;
|
|
90
|
+
const gauche = sortie(commande, entree, projet).replace(/\n+$/, '');
|
|
91
|
+
const colonnes = Number(process.env.COLUMNS) || 80;
|
|
92
|
+
process.stdout.write(`${composer(gauche, etat(projet), colonnes)}\n`);
|
|
93
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The common charter arrives whole, imported by the project's CLAUDE.md from the package installed
|
|
2
|
+
// (`@node_modules/@roomi-fields/grillhouse/charte.md`), as Claude Code loads a CLAUDE.md and its
|
|
3
|
+
// imports whole; no hook writes it any more, since Claude Code caps a hook's output.
|
|
4
|
+
//
|
|
5
|
+
// - A CLAUDE.md that imports a charter of the frame (`@…grillhouse/charte.md`) other than the
|
|
6
|
+
// former copy is left: it already has one (Grillhouse imports its source,
|
|
7
|
+
// `@mods/grillhouse/charte.md`).
|
|
8
|
+
// - The import of the former copy (`@.claude/grillhouse/charte.md`) becomes the import of the
|
|
9
|
+
// package installed.
|
|
10
|
+
// - The paragraph of the template that gave the charter by the plugin (« Les règles du cadre : la
|
|
11
|
+
// charte commune, que le greffon du cadre … donne à chaque séance. ») becomes
|
|
12
|
+
// « Les règles du cadre : @node_modules/@roomi-fields/grillhouse/charte.md ».
|
|
13
|
+
// - A CLAUDE.md that names neither receives that line at its end, after a blank line.
|
|
14
|
+
// - A project without CLAUDE.md is not written.
|
|
15
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
|
|
18
|
+
export const version = '0.23.0';
|
|
19
|
+
export const nom = 'charte importée';
|
|
20
|
+
|
|
21
|
+
const IMPORT = '@node_modules/@roomi-fields/grillhouse/charte.md';
|
|
22
|
+
const LIGNE = `Les règles du cadre : ${IMPORT}`;
|
|
23
|
+
const ANCIENNE_COPIE = '@.claude/grillhouse/charte.md';
|
|
24
|
+
// An import of a charter of the frame: `@<chemin>grillhouse/charte.md`, at a word's start.
|
|
25
|
+
const IMPORT_D_UNE_CHARTE = /(?:^|\s)@(\S*grillhouse\/charte\.md)/g;
|
|
26
|
+
// The paragraph of 0.23.0's template that gave the charter by the plugin, up to its blank line.
|
|
27
|
+
const PARAGRAPHE_DU_GREFFON =
|
|
28
|
+
/^Les règles du cadre : la charte commune, que le greffon[^]*?donne à chaque séance\.$/m;
|
|
29
|
+
|
|
30
|
+
/** Brings the import of the common charter to the project's CLAUDE.md; returns what it did. */
|
|
31
|
+
export function migrer(projet) {
|
|
32
|
+
const fichier = path.join(projet, 'CLAUDE.md');
|
|
33
|
+
if (!existsSync(fichier)) return [];
|
|
34
|
+
const texte = readFileSync(fichier, 'utf8');
|
|
35
|
+
const imports = [...texte.matchAll(IMPORT_D_UNE_CHARTE)].map(m => `@${m[1]}`);
|
|
36
|
+
if (imports.some(i => i !== ANCIENNE_COPIE)) return [];
|
|
37
|
+
let neuf;
|
|
38
|
+
if (imports.length > 0) {
|
|
39
|
+
neuf = texte.replaceAll(ANCIENNE_COPIE, IMPORT);
|
|
40
|
+
} else if (PARAGRAPHE_DU_GREFFON.test(texte)) {
|
|
41
|
+
neuf = texte.replace(PARAGRAPHE_DU_GREFFON, LIGNE);
|
|
42
|
+
} else {
|
|
43
|
+
neuf = `${texte.replace(/\n*$/, '\n')}\n${LIGNE}\n`;
|
|
44
|
+
}
|
|
45
|
+
writeFileSync(fichier, neuf);
|
|
46
|
+
return [`CLAUDE.md : la charte commune importée du paquet installé (${IMPORT})`];
|
|
47
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The frame's base configuration of Vitest is the package's: `@roomi-fields/grillhouse/vitest-config`
|
|
2
|
+
// (and `/mesure`), in place of the workspace package `@grillhouse/vitest-config` that the frame's
|
|
3
|
+
// copied files held.
|
|
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
|
|
7
|
+
// `@grillhouse/vitest-config` or `@grillhouse/vitest-config/mesure` in quotes names the package's
|
|
8
|
+
// instead; a root configuration that imports the base by its former path
|
|
9
|
+
// (`./scripts/vitest-config/base.mjs`, `./packages/grillhouse/scripts/vitest-config/base.mjs`)
|
|
10
|
+
// imports it by its name.
|
|
11
|
+
// - Each package.json of the project (the root's and each workspace's) that declares
|
|
12
|
+
// `@grillhouse/vitest-config` declares `@roomi-fields/grillhouse` in the same field in its place,
|
|
13
|
+
// at the range the root declares (else `^<version>` of the package), unless it already declares
|
|
14
|
+
// it; the root's workspaces lose the former directory of the package (`scripts/vitest-config`,
|
|
15
|
+
// `packages/grillhouse/scripts/vitest-config`).
|
|
16
|
+
// - A project already in the new form is not written.
|
|
17
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { VERSION } from '../cadre.mjs';
|
|
20
|
+
import { paquets } from '../paquets.mjs';
|
|
21
|
+
|
|
22
|
+
export const version = '0.23.0';
|
|
23
|
+
export const nom = 'configuration de base de Vitest';
|
|
24
|
+
|
|
25
|
+
const ANCIEN = '@grillhouse/vitest-config';
|
|
26
|
+
const PAQUET = '@roomi-fields/grillhouse';
|
|
27
|
+
const NOUVEAU = `${PAQUET}/vitest-config`;
|
|
28
|
+
const ANCIENS_ESPACES = ['scripts/vitest-config', 'packages/grillhouse/scripts/vitest-config'];
|
|
29
|
+
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
|
+
// A quoted name of the former package, or a former path of the base at the root.
|
|
43
|
+
const NOMME =
|
|
44
|
+
/(['"])(?:@grillhouse\/vitest-config(\/mesure)?|\.\/(?:packages\/grillhouse\/)?scripts\/vitest-config\/base\.mjs)\1/g;
|
|
45
|
+
|
|
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
|
+
}
|
|
53
|
+
|
|
54
|
+
const lireJson = f => JSON.parse(readFileSync(f, 'utf8'));
|
|
55
|
+
const ecrireJson = (f, v) => writeFileSync(f, `${JSON.stringify(v, null, 2)}\n`);
|
|
56
|
+
|
|
57
|
+
/** Brings the project to the package's base configuration of Vitest; returns what it did. */
|
|
58
|
+
export function migrer(projet) {
|
|
59
|
+
const lignes = [];
|
|
60
|
+
for (const f of sources(projet)) {
|
|
61
|
+
const texte = readFileSync(path.join(projet, f), 'utf8');
|
|
62
|
+
const neuf = texte.replace(NOMME, (_, q, mesure) => `${q}${NOUVEAU}${mesure ?? ''}${q}`);
|
|
63
|
+
if (neuf !== texte) {
|
|
64
|
+
writeFileSync(path.join(projet, f), neuf);
|
|
65
|
+
lignes.push(`${f} : importe ${NOUVEAU}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const fracine = path.join(projet, 'package.json');
|
|
69
|
+
if (!existsSync(fracine)) return lignes;
|
|
70
|
+
const racine = lireJson(fracine);
|
|
71
|
+
const plage = CHAMPS.map(c => racine[c]?.[PAQUET]).find(v => v !== undefined) ?? `^${VERSION}`;
|
|
72
|
+
const declarer = pkg => {
|
|
73
|
+
let change = false;
|
|
74
|
+
for (const c of CHAMPS) {
|
|
75
|
+
if (pkg[c]?.[ANCIEN] === undefined) continue;
|
|
76
|
+
const { [ANCIEN]: _ancien, ...reste } = pkg[c];
|
|
77
|
+
const deja = CHAMPS.some(d => pkg[d]?.[PAQUET] !== undefined);
|
|
78
|
+
pkg[c] = Object.fromEntries(
|
|
79
|
+
Object.entries(deja ? reste : { ...reste, [PAQUET]: plage }).sort(([a], [b]) =>
|
|
80
|
+
a.localeCompare(b, 'en')
|
|
81
|
+
)
|
|
82
|
+
);
|
|
83
|
+
change = true;
|
|
84
|
+
}
|
|
85
|
+
return change;
|
|
86
|
+
};
|
|
87
|
+
for (const { dossier, pkg } of paquets(projet)) {
|
|
88
|
+
if (ANCIENS_ESPACES.includes(dossier) || !declarer(pkg)) continue;
|
|
89
|
+
ecrireJson(path.join(projet, dossier, 'package.json'), pkg);
|
|
90
|
+
lignes.push(`${dossier}/package.json : déclare ${PAQUET} à la place de ${ANCIEN}`);
|
|
91
|
+
}
|
|
92
|
+
const declare = declarer(racine);
|
|
93
|
+
if (declare) lignes.push(`package.json : déclare ${PAQUET} à la place de ${ANCIEN}`);
|
|
94
|
+
const liste = Array.isArray(racine.workspaces) ? racine.workspaces : racine.workspaces?.packages;
|
|
95
|
+
const restants = Array.isArray(liste) ? liste.filter(e => !ANCIENS_ESPACES.includes(e)) : liste;
|
|
96
|
+
const espaces = Array.isArray(liste) && restants.length !== liste.length;
|
|
97
|
+
if (espaces) {
|
|
98
|
+
racine.workspaces = Array.isArray(racine.workspaces)
|
|
99
|
+
? restants
|
|
100
|
+
: { ...racine.workspaces, packages: restants };
|
|
101
|
+
lignes.push(`package.json : l'ancien dossier de ${ANCIEN} quitte les workspaces`);
|
|
102
|
+
}
|
|
103
|
+
if (declare || espaces) ecrireJson(fracine, racine);
|
|
104
|
+
return lignes;
|
|
105
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// The hooks of Claude Code come from the frame's plugin (mods/grillhouse/hooks/hooks.json): the
|
|
2
|
+
// project's settings no longer run the frame's session scripts by their path.
|
|
3
|
+
//
|
|
4
|
+
// - In the project's `.claude/settings.json`, each hook whose command runs one of the frame's former
|
|
5
|
+
// session scripts under `$CLAUDE_PROJECT_DIR/scripts/` (session-start.sh, structure-guard.sh,
|
|
6
|
+
// verrous/ticket-du-sous-agent.mjs, registre.mjs) goes; a group left without a hook goes, an
|
|
7
|
+
// event left without a group goes, and `hooks` left empty goes. A hook of the project's own stays,
|
|
8
|
+
// and so do the other keys, in their order.
|
|
9
|
+
// - A project without settings, or whose settings hold none of these hooks, is not written.
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
|
|
13
|
+
export const version = '0.23.0';
|
|
14
|
+
export const nom = 'crochets de séance';
|
|
15
|
+
|
|
16
|
+
// The frame's former session scripts, as the project's settings ran them.
|
|
17
|
+
const ANCIENS = [
|
|
18
|
+
'scripts/session-start.sh',
|
|
19
|
+
'scripts/structure-guard.sh',
|
|
20
|
+
'scripts/verrous/ticket-du-sous-agent.mjs',
|
|
21
|
+
'scripts/registre.mjs',
|
|
22
|
+
];
|
|
23
|
+
const duCadre = crochet =>
|
|
24
|
+
typeof crochet?.command === 'string' &&
|
|
25
|
+
ANCIENS.some(s => crochet.command.includes(`$CLAUDE_PROJECT_DIR/${s}`));
|
|
26
|
+
|
|
27
|
+
/** Removes the frame's former session hooks from the project's settings; returns what it did. */
|
|
28
|
+
export function migrer(projet) {
|
|
29
|
+
const f = path.join(projet, '.claude', 'settings.json');
|
|
30
|
+
if (!existsSync(f)) return [];
|
|
31
|
+
const reglages = JSON.parse(readFileSync(f, 'utf8'));
|
|
32
|
+
if (reglages.hooks === null || typeof reglages.hooks !== 'object') return [];
|
|
33
|
+
const retires = [];
|
|
34
|
+
const hooks = {};
|
|
35
|
+
for (const [evenement, groupes] of Object.entries(reglages.hooks)) {
|
|
36
|
+
if (!Array.isArray(groupes)) {
|
|
37
|
+
hooks[evenement] = groupes;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const restants = groupes
|
|
41
|
+
.map(g => {
|
|
42
|
+
if (!Array.isArray(g?.hooks)) return g;
|
|
43
|
+
const gardes = g.hooks.filter(c => !duCadre(c));
|
|
44
|
+
for (const c of g.hooks) if (duCadre(c)) retires.push(`${evenement} : ${c.command}`);
|
|
45
|
+
return gardes.length > 0 ? { ...g, hooks: gardes } : null;
|
|
46
|
+
})
|
|
47
|
+
.filter(g => g !== null);
|
|
48
|
+
if (restants.length > 0) hooks[evenement] = restants;
|
|
49
|
+
}
|
|
50
|
+
if (retires.length === 0) return [];
|
|
51
|
+
if (Object.keys(hooks).length > 0) reglages.hooks = hooks;
|
|
52
|
+
else delete reglages.hooks;
|
|
53
|
+
writeFileSync(f, `${JSON.stringify(reglages, null, 2)}\n`);
|
|
54
|
+
return retires.map(r => `.claude/settings.json : crochet retiré, que le greffon porte (${r})`);
|
|
55
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The frame's plugin comes from the marketplace roomi-fields, which names the package npm, and
|
|
2
|
+
// nothing of a project is published: the marketplace of the project's own, which an earlier update
|
|
3
|
+
// wrote, goes (`grillhouse maj` then declares roomi-fields and enables `grillhouse@roomi-fields`).
|
|
4
|
+
//
|
|
5
|
+
// - The project's `.claude-plugin/marketplace.json` goes when it is the frame's: its name begins
|
|
6
|
+
// with `grillhouse-` and each plugin it lists is `grillhouse`, from the project's package
|
|
7
|
+
// (`./node_modules/@roomi-fields/grillhouse/…`); `.claude-plugin/` left empty goes.
|
|
8
|
+
// - In `.claude/settings.json` and `.claude/settings.local.json` (where the add of a marketplace of
|
|
9
|
+
// a directory wrote it), each marketplace whose name begins with `grillhouse-` and that is the
|
|
10
|
+
// project's root (a directory, `./` or `.`) leaves `extraKnownMarketplaces`, and its plugin,
|
|
11
|
+
// `grillhouse@<name>`, leaves `enabledPlugins`. A key left empty goes; the other keys stay, in
|
|
12
|
+
// their order.
|
|
13
|
+
// - A project without them is not written.
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
export const version = '0.23.0';
|
|
18
|
+
export const nom = 'place de marché du projet';
|
|
19
|
+
|
|
20
|
+
const PREFIXE = 'grillhouse-';
|
|
21
|
+
const SOURCE_DU_PAQUET = './node_modules/@roomi-fields/grillhouse';
|
|
22
|
+
const REGLAGES = ['.claude/settings.json', '.claude/settings.local.json'];
|
|
23
|
+
|
|
24
|
+
const lireJson = f => {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(f, 'utf8'));
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Whether the manifest <m> is the frame's marketplace of a project.
|
|
33
|
+
const duCadre = m =>
|
|
34
|
+
typeof m?.name === 'string' &&
|
|
35
|
+
m.name.startsWith(PREFIXE) &&
|
|
36
|
+
Array.isArray(m.plugins) &&
|
|
37
|
+
m.plugins.length > 0 &&
|
|
38
|
+
m.plugins.every(
|
|
39
|
+
p =>
|
|
40
|
+
p?.name === 'grillhouse' &&
|
|
41
|
+
typeof p.source === 'string' &&
|
|
42
|
+
p.source.startsWith(`${SOURCE_DU_PAQUET}/`)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Whether the entry <e> of `extraKnownMarketplaces`, named <n>, is the project's own marketplace.
|
|
46
|
+
const declareDuCadre = (n, e) =>
|
|
47
|
+
n.startsWith(PREFIXE) && e?.source?.source === 'directory' && ['./', '.'].includes(e.source.path);
|
|
48
|
+
|
|
49
|
+
// Takes the project's marketplace off the settings file <rel> of the project at <projet>.
|
|
50
|
+
function retirerDesReglages(projet, rel) {
|
|
51
|
+
const f = path.join(projet, rel);
|
|
52
|
+
if (!existsSync(f)) return [];
|
|
53
|
+
const reglages = JSON.parse(readFileSync(f, 'utf8'));
|
|
54
|
+
const marches = reglages.extraKnownMarketplaces;
|
|
55
|
+
if (!marches || typeof marches !== 'object') return [];
|
|
56
|
+
const noms = Object.keys(marches).filter(n => declareDuCadre(n, marches[n]));
|
|
57
|
+
if (noms.length === 0) return [];
|
|
58
|
+
for (const n of noms) {
|
|
59
|
+
delete marches[n];
|
|
60
|
+
delete reglages.enabledPlugins?.[`grillhouse@${n}`];
|
|
61
|
+
}
|
|
62
|
+
if (Object.keys(marches).length === 0) delete reglages.extraKnownMarketplaces;
|
|
63
|
+
if (reglages.enabledPlugins && Object.keys(reglages.enabledPlugins).length === 0) {
|
|
64
|
+
delete reglages.enabledPlugins;
|
|
65
|
+
}
|
|
66
|
+
writeFileSync(f, `${JSON.stringify(reglages, null, 2)}\n`);
|
|
67
|
+
return noms.map(n => `${rel} : place de marché du projet retirée (${n}, grillhouse@${n})`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Takes the marketplace of the project's own off the project; returns what it did. */
|
|
71
|
+
export function migrer(projet) {
|
|
72
|
+
const lignes = [];
|
|
73
|
+
const dossier = path.join(projet, '.claude-plugin');
|
|
74
|
+
const manifeste = path.join(dossier, 'marketplace.json');
|
|
75
|
+
if (existsSync(manifeste) && duCadre(lireJson(manifeste))) {
|
|
76
|
+
rmSync(manifeste);
|
|
77
|
+
if (readdirSync(dossier).length === 0) rmSync(dossier, { recursive: true });
|
|
78
|
+
lignes.push('.claude-plugin/marketplace.json : place de marché du projet retirée');
|
|
79
|
+
}
|
|
80
|
+
for (const rel of REGLAGES) lignes.push(...retirerDesReglages(projet, rel));
|
|
81
|
+
return lignes;
|
|
82
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// The migrations of a project from one version of the frame to the next, as the transforms of
|
|
2
|
+
// `npx @turbo/codemod migrate`: one module per change of form, written once, in
|
|
3
|
+
// scripts/migrations/<version>-<nom>.mjs, which `grillhouse maj` plays (scripts/grillhouse-maj.mjs).
|
|
4
|
+
//
|
|
5
|
+
// - A migration exports `version` (the version of the package that brings the change of form),
|
|
6
|
+
// `nom` and `migrer(projet)`, which brings the project at <projet> to the new form and returns
|
|
7
|
+
// the lines of what it changed: none when the project already has it. It is replayable without
|
|
8
|
+
// effect: a second pass changes no byte and returns no line.
|
|
9
|
+
// - MIGRATIONS lists them, each imported by its module; the update plays those newer than the
|
|
10
|
+
// version the project recorded (`grillhouse.version` of its package.json), in the order of their
|
|
11
|
+
// versions; a project that recorded none plays them all. The package carries no migration newer
|
|
12
|
+
// than itself once released, so the range ends with the package.
|
|
13
|
+
// - Each migration has its test, tests/unit/migration.<nom>.spec.ts.
|
|
14
|
+
import semver from 'semver';
|
|
15
|
+
import * as charteImportee from './migrations/0.23.0-charte-importee.mjs';
|
|
16
|
+
import * as configVitest from './migrations/0.23.0-config-vitest.mjs';
|
|
17
|
+
import * as crochetsDeSeance from './migrations/0.23.0-crochets-de-seance.mjs';
|
|
18
|
+
import * as marcheDuProjet from './migrations/0.23.0-marche-du-projet.mjs';
|
|
19
|
+
|
|
20
|
+
/** The migrations of the package. */
|
|
21
|
+
export const MIGRATIONS = [crochetsDeSeance, configVitest, marcheDuProjet, charteImportee];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The migrations of <liste> newer than <depuis> (all of them when it is null), in the order of
|
|
25
|
+
* their versions; two of one version keep their order in the list.
|
|
26
|
+
*/
|
|
27
|
+
export function migrationsAJouer(depuis, liste = MIGRATIONS) {
|
|
28
|
+
return liste
|
|
29
|
+
.map((m, rang) => ({ m, rang }))
|
|
30
|
+
.filter(({ m }) => depuis === null || semver.gt(m.version, depuis))
|
|
31
|
+
.sort((a, b) => semver.compare(a.m.version, b.m.version) || a.rang - b.rang)
|
|
32
|
+
.map(({ m }) => m);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Plays on the project at <projet> each migration newer than <depuis>; returns, for each one that
|
|
37
|
+
* changed something, `{ version, nom, lignes }`.
|
|
38
|
+
*/
|
|
39
|
+
export function migrer(projet, depuis, liste = MIGRATIONS) {
|
|
40
|
+
return migrationsAJouer(depuis, liste)
|
|
41
|
+
.map(m => ({ version: m.version, nom: m.nom, lignes: m.migrer(projet) }))
|
|
42
|
+
.filter(r => r.lignes.length > 0);
|
|
43
|
+
}
|