@ogygie/cli 0.2.0 → 0.4.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.
- package/dist/cli.js +81 -9
- package/package.json +2 -2
- package/templates/project/AGENTS.md +8 -0
package/dist/cli.js
CHANGED
|
@@ -14667,7 +14667,20 @@ var OgygieManifestSchema = external_exports.object({
|
|
|
14667
14667
|
name: external_exports.string(),
|
|
14668
14668
|
path: external_exports.string(),
|
|
14669
14669
|
type: external_exports.enum(["api", "front", "gateway"]),
|
|
14670
|
-
entryPoint: external_exports.string()
|
|
14670
|
+
entryPoint: external_exports.string(),
|
|
14671
|
+
/**
|
|
14672
|
+
* Code the product HOSTS without maintaining it — imported as-is from
|
|
14673
|
+
* elsewhere, the framework's own dev tooling being the typical case.
|
|
14674
|
+
* A vendored app is skipped by the vocabulary gate: a product's business
|
|
14675
|
+
* vocabulary has no claim over code it will never rename.
|
|
14676
|
+
*
|
|
14677
|
+
* A literal `true`, like `shared` on a table: an app is either vendored
|
|
14678
|
+
* (and says so) or the product's own (and stays silent) — no third
|
|
14679
|
+
* state. The derogation is declared here, machine-readably, and the gate
|
|
14680
|
+
* ANNOUNCES it on every run — one that stops being visible is one that
|
|
14681
|
+
* gets forgotten.
|
|
14682
|
+
*/
|
|
14683
|
+
vendored: external_exports.literal(true).optional()
|
|
14671
14684
|
})
|
|
14672
14685
|
),
|
|
14673
14686
|
features: external_exports.array(FeatureManifestSchema),
|
|
@@ -14991,6 +15004,19 @@ function diffLines(oldText, newText) {
|
|
|
14991
15004
|
return output.join("\n");
|
|
14992
15005
|
}
|
|
14993
15006
|
|
|
15007
|
+
// src/framework-gates.ts
|
|
15008
|
+
var frameworkGates = [
|
|
15009
|
+
{
|
|
15010
|
+
scriptName: "ogygie:verify-vocabulary",
|
|
15011
|
+
command: "ogygie verify-vocabulary",
|
|
15012
|
+
intent: "Le vocabulaire d\xE9clar\xE9 au manifeste : alias interdits dans les identifiants, puret\xE9 des fichiers gouvern\xE9s."
|
|
15013
|
+
}
|
|
15014
|
+
];
|
|
15015
|
+
function missingFrameworkGates(scripts) {
|
|
15016
|
+
const commands = Object.values(scripts ?? {}).join(" ; ");
|
|
15017
|
+
return frameworkGates.filter((gate) => !commands.includes(gate.command));
|
|
15018
|
+
}
|
|
15019
|
+
|
|
14994
15020
|
// src/version.ts
|
|
14995
15021
|
function compareVersions(a, b) {
|
|
14996
15022
|
const partsA = a.split(".").map(Number);
|
|
@@ -15005,6 +15031,30 @@ function compareVersions(a, b) {
|
|
|
15005
15031
|
}
|
|
15006
15032
|
|
|
15007
15033
|
// src/commands/upgrade.ts
|
|
15034
|
+
function reportMissingGates(deps) {
|
|
15035
|
+
const packageJsonPath = join4(deps.projectRoot, "package.json");
|
|
15036
|
+
if (!existsSync4(packageJsonPath)) return;
|
|
15037
|
+
let scripts;
|
|
15038
|
+
try {
|
|
15039
|
+
const parsed = JSON.parse(readFileSync4(packageJsonPath, "utf8"));
|
|
15040
|
+
const scriptsValue = typeof parsed === "object" && parsed !== null ? parsed.scripts : void 0;
|
|
15041
|
+
if (typeof scriptsValue === "object" && scriptsValue !== null) {
|
|
15042
|
+
scripts = scriptsValue;
|
|
15043
|
+
}
|
|
15044
|
+
} catch {
|
|
15045
|
+
return;
|
|
15046
|
+
}
|
|
15047
|
+
const missing = missingFrameworkGates(scripts);
|
|
15048
|
+
if (missing.length === 0) return;
|
|
15049
|
+
deps.log("");
|
|
15050
|
+
deps.log(
|
|
15051
|
+
`\u26A0 ${missing.length} gate(s) du framework ne tournent pas dans ta boucle locale (package.json n'est pas gouvern\xE9 \u2014 \xE0 toi de l'ajouter, la CI gouvern\xE9e les appelle d\xE9j\xE0) :`
|
|
15052
|
+
);
|
|
15053
|
+
for (const gate of missing) {
|
|
15054
|
+
deps.log(` "${gate.scriptName}": "${gate.command}" # ${gate.intent}`);
|
|
15055
|
+
}
|
|
15056
|
+
deps.log(" puis appelle ces scripts depuis ton script `verify`.");
|
|
15057
|
+
}
|
|
15008
15058
|
async function upgradeProject(deps) {
|
|
15009
15059
|
const installedVersion = readPinnedOgygieVersion(deps.projectRoot);
|
|
15010
15060
|
if (compareVersions(deps.cliVersion, installedVersion) < 0) {
|
|
@@ -15013,7 +15063,7 @@ async function upgradeProject(deps) {
|
|
|
15013
15063
|
message: `The running CLI (${deps.cliVersion}) is older than the project's ogygieVersion (${installedVersion}) \u2014 a downgrade does not exist.`,
|
|
15014
15064
|
file: "ogygie.manifest.json",
|
|
15015
15065
|
cause: `cli ${deps.cliVersion} < ogygieVersion ${installedVersion}`,
|
|
15016
|
-
suggestedFix:
|
|
15066
|
+
suggestedFix: `Install a CLI at least as recent as the project, WITH an explicit version (init pins exact ranges, so a bare \`pnpm update\` moves nothing): pnpm update @ogygie/core@${installedVersion} @ogygie/cli@${installedVersion} \u2014 then retry the upgrade.`
|
|
15017
15067
|
});
|
|
15018
15068
|
}
|
|
15019
15069
|
if (compareVersions(deps.cliVersion, installedVersion) === 0) {
|
|
@@ -15089,6 +15139,7 @@ ${diff}`);
|
|
|
15089
15139
|
deps.log(
|
|
15090
15140
|
`Rappel lockstep (D2) : mettre @ogygie/core ET @ogygie/cli \xE0 ${deps.cliVersion} (\`pnpm update @ogygie/core@${deps.cliVersion} @ogygie/cli@${deps.cliVersion}\`), puis committer l'upgrade SEUL \u2014 jamais m\xEAl\xE9 \xE0 une feature.`
|
|
15091
15141
|
);
|
|
15142
|
+
reportMissingGates(deps);
|
|
15092
15143
|
return {
|
|
15093
15144
|
fromVersion: installedVersion,
|
|
15094
15145
|
toVersion: deps.cliVersion,
|
|
@@ -15152,9 +15203,6 @@ function stripComments(sourceText) {
|
|
|
15152
15203
|
}
|
|
15153
15204
|
|
|
15154
15205
|
// ../ogygie-core/src/vocabulary-audit.ts
|
|
15155
|
-
function vocabularyRootsOf(vocabulary) {
|
|
15156
|
-
return vocabulary.entries.flatMap((entry) => [entry.canonical, ...entry.forbiddenAliases]);
|
|
15157
|
-
}
|
|
15158
15206
|
function canonicalByAlias(vocabulary) {
|
|
15159
15207
|
const byAlias = /* @__PURE__ */ new Map();
|
|
15160
15208
|
for (const entry of vocabulary.entries) {
|
|
@@ -15167,7 +15215,7 @@ function auditProductVocabulary(input) {
|
|
|
15167
15215
|
if (vocabulary === void 0 || vocabulary.entries.length === 0) return [];
|
|
15168
15216
|
const canonicalOfAlias = canonicalByAlias(vocabulary);
|
|
15169
15217
|
const forbiddenAliases = [...canonicalOfAlias.keys()];
|
|
15170
|
-
const
|
|
15218
|
+
const canonicalTerms = vocabulary.entries.map((entry) => entry.canonical);
|
|
15171
15219
|
const violations = [];
|
|
15172
15220
|
for (const file2 of input.sourceFiles) {
|
|
15173
15221
|
for (const match of findProductVocabulary(stripComments(file2.content), forbiddenAliases)) {
|
|
@@ -15180,7 +15228,7 @@ function auditProductVocabulary(input) {
|
|
|
15180
15228
|
}
|
|
15181
15229
|
}
|
|
15182
15230
|
for (const file2 of input.governedFiles) {
|
|
15183
|
-
for (const match of findProductVocabulary(file2.content,
|
|
15231
|
+
for (const match of findProductVocabulary(file2.content, canonicalTerms)) {
|
|
15184
15232
|
violations.push({
|
|
15185
15233
|
kind: "GOVERNED_FILE",
|
|
15186
15234
|
filePath: file2.path,
|
|
@@ -15244,13 +15292,22 @@ function collectCodeFiles(projectRoot, folder) {
|
|
|
15244
15292
|
}
|
|
15245
15293
|
function productCodeFoldersOf(manifest) {
|
|
15246
15294
|
return [
|
|
15247
|
-
...manifest.apps.map((app) => app.path),
|
|
15295
|
+
...manifest.apps.filter((app) => app.vendored !== true).map((app) => app.path),
|
|
15248
15296
|
...(manifest.framework?.packages ?? []).map((packageEntry) => packageEntry.path)
|
|
15249
15297
|
];
|
|
15250
15298
|
}
|
|
15299
|
+
function vendoredAppsOf(manifest) {
|
|
15300
|
+
return manifest.apps.filter((app) => app.vendored === true).map((app) => ({ name: app.name, path: app.path }));
|
|
15301
|
+
}
|
|
15251
15302
|
function verifyVocabulary(deps) {
|
|
15252
15303
|
const manifest = readManifest(deps.projectRoot);
|
|
15253
15304
|
if (manifest.vocabulary === void 0) return [];
|
|
15305
|
+
const vendored = vendoredAppsOf(manifest);
|
|
15306
|
+
if (vendored.length > 0 && deps.log !== void 0) {
|
|
15307
|
+
deps.log(
|
|
15308
|
+
`${vendored.length} app(s) hors du scan (vendored \u2014 code h\xE9berg\xE9, non maintenu par le produit) : ${vendored.map((app) => `${app.name} (${app.path})`).join(", ")}`
|
|
15309
|
+
);
|
|
15310
|
+
}
|
|
15254
15311
|
const sourceFiles = productCodeFoldersOf(manifest).flatMap(
|
|
15255
15312
|
(folder) => collectCodeFiles(deps.projectRoot, folder)
|
|
15256
15313
|
);
|
|
@@ -15292,6 +15349,18 @@ var governedChecksumsByVersion = {
|
|
|
15292
15349
|
"AGENTS.md": "ca73ab619d8c55fac11858d815bf5fbab6537ecebe7ae4123cbdeb3e3e36bf1a",
|
|
15293
15350
|
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15294
15351
|
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15352
|
+
},
|
|
15353
|
+
"0.3.0": {
|
|
15354
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15355
|
+
"AGENTS.md": "ca73ab619d8c55fac11858d815bf5fbab6537ecebe7ae4123cbdeb3e3e36bf1a",
|
|
15356
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15357
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15358
|
+
},
|
|
15359
|
+
"0.4.0": {
|
|
15360
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15361
|
+
"AGENTS.md": "c7d51a0cd97b67343b486bdb7c54914da68d96409eaa985959d2a525c65549fd",
|
|
15362
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15363
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15295
15364
|
}
|
|
15296
15365
|
};
|
|
15297
15366
|
|
|
@@ -15378,7 +15447,10 @@ ogygie check \u2014 ${problems.length} probl\xE8me(s).`);
|
|
|
15378
15447
|
`ogygie check \u2014 OK, lockstep ${cliVersion()} et fichiers gouvern\xE9s intacts.`
|
|
15379
15448
|
);
|
|
15380
15449
|
} else if (command === "verify-vocabulary") {
|
|
15381
|
-
const problems = verifyVocabulary({
|
|
15450
|
+
const problems = verifyVocabulary({
|
|
15451
|
+
projectRoot: process.cwd(),
|
|
15452
|
+
log: (line) => console.log(line)
|
|
15453
|
+
});
|
|
15382
15454
|
if (problems.length > 0) {
|
|
15383
15455
|
for (const problem of problems) printError(problem);
|
|
15384
15456
|
console.error(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogygie/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Ogygie framework CLI: deterministic scaffolding (init), governed-file upgrades (upgrade) and lockstep verification (check) for Ogygie products.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"tsx": "^4.20.0",
|
|
23
23
|
"typescript": "^5.8.0",
|
|
24
24
|
"zod": "^4.0.0",
|
|
25
|
-
"@ogygie/core": "0.
|
|
25
|
+
"@ogygie/core": "0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"typecheck": "tsc -p tsconfig.json",
|
|
@@ -272,6 +272,14 @@ se fait PAR RACINE, donc une racine trop courte attrape des mots génériques
|
|
|
272
272
|
(déclarer `memo` interdit fait tomber `memory`, `createInMemoryDb`…). Une racine
|
|
273
273
|
se déclare assez longue pour ne nommer que le concept.
|
|
274
274
|
|
|
275
|
+
**Le code que le produit héberge sans le maintenir** — l'outil de dev du
|
|
276
|
+
framework propagé dans le dépôt en est le cas type — se déclare
|
|
277
|
+
`"vendored": true` sur son entrée d'`apps[]`, et sort du scan : un vocabulaire
|
|
278
|
+
métier n'a pas de prise sur du code que le produit ne renommera jamais. La
|
|
279
|
+
dérogation se déclare au manifeste, comme celle d'une table partagée, et le
|
|
280
|
+
gate l'ANNONCE à chaque exécution — une dérogation qu'on ne voit plus est une
|
|
281
|
+
dérogation qui s'oublie. Elle ne couvre JAMAIS le code du produit lui-même.
|
|
282
|
+
|
|
275
283
|
## Front (apps/web) — règles et templates canoniques
|
|
276
284
|
|
|
277
285
|
### Stack imposée (toute autre lib = amendement DECISIONS.md d'abord)
|