@ogygie/cli 0.11.2 → 0.13.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 +326 -153
- package/package.json +2 -2
- package/templates/project/AGENTS.md +10 -0
package/dist/cli.js
CHANGED
|
@@ -14555,12 +14555,90 @@ function throwOgygieError(input, httpStatus = 500) {
|
|
|
14555
14555
|
throw new OgygieFailure(ogygieError(input), httpStatus);
|
|
14556
14556
|
}
|
|
14557
14557
|
|
|
14558
|
-
// src/commands/
|
|
14559
|
-
import { existsSync as
|
|
14560
|
-
import { join as
|
|
14558
|
+
// src/commands/adopt.ts
|
|
14559
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
14560
|
+
import { dirname, join as join3 } from "node:path";
|
|
14561
14561
|
|
|
14562
|
-
// src/
|
|
14563
|
-
|
|
14562
|
+
// src/diff.ts
|
|
14563
|
+
function diffLines(oldText, newText) {
|
|
14564
|
+
if (oldText === newText) return "";
|
|
14565
|
+
const oldLines = oldText.split("\n");
|
|
14566
|
+
const newLines = newText.split("\n");
|
|
14567
|
+
const lcs = Array.from(
|
|
14568
|
+
{ length: oldLines.length + 1 },
|
|
14569
|
+
() => new Array(newLines.length + 1).fill(0)
|
|
14570
|
+
);
|
|
14571
|
+
for (let i2 = oldLines.length - 1; i2 >= 0; i2 -= 1) {
|
|
14572
|
+
for (let j2 = newLines.length - 1; j2 >= 0; j2 -= 1) {
|
|
14573
|
+
lcs[i2][j2] = oldLines[i2] === newLines[j2] ? (lcs[i2 + 1][j2 + 1] ?? 0) + 1 : Math.max(lcs[i2 + 1][j2] ?? 0, lcs[i2][j2 + 1] ?? 0);
|
|
14574
|
+
}
|
|
14575
|
+
}
|
|
14576
|
+
const output = [];
|
|
14577
|
+
let previousWasChange = false;
|
|
14578
|
+
let i = 0;
|
|
14579
|
+
let j = 0;
|
|
14580
|
+
while (i < oldLines.length || j < newLines.length) {
|
|
14581
|
+
if (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {
|
|
14582
|
+
previousWasChange = false;
|
|
14583
|
+
i += 1;
|
|
14584
|
+
j += 1;
|
|
14585
|
+
} else {
|
|
14586
|
+
if (!previousWasChange) output.push(`@@ line ${j + 1}`);
|
|
14587
|
+
previousWasChange = true;
|
|
14588
|
+
if (j >= newLines.length || i < oldLines.length && (lcs[i + 1]?.[j] ?? 0) >= (lcs[i]?.[j + 1] ?? 0)) {
|
|
14589
|
+
output.push(`- ${oldLines[i]}`);
|
|
14590
|
+
i += 1;
|
|
14591
|
+
} else {
|
|
14592
|
+
output.push(`+ ${newLines[j]}`);
|
|
14593
|
+
j += 1;
|
|
14594
|
+
}
|
|
14595
|
+
}
|
|
14596
|
+
}
|
|
14597
|
+
return output.join("\n");
|
|
14598
|
+
}
|
|
14599
|
+
|
|
14600
|
+
// src/gates-report.ts
|
|
14601
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14602
|
+
import { join } from "node:path";
|
|
14603
|
+
|
|
14604
|
+
// src/framework-gates.ts
|
|
14605
|
+
var frameworkGates = [
|
|
14606
|
+
{
|
|
14607
|
+
scriptName: "ogygie:verify-vocabulary",
|
|
14608
|
+
command: "ogygie verify-vocabulary",
|
|
14609
|
+
intent: "Le vocabulaire d\xE9clar\xE9 au manifeste : alias interdits dans les identifiants, puret\xE9 des fichiers gouvern\xE9s."
|
|
14610
|
+
}
|
|
14611
|
+
];
|
|
14612
|
+
function missingFrameworkGates(scripts) {
|
|
14613
|
+
const commands = Object.values(scripts ?? {}).join(" ; ");
|
|
14614
|
+
return frameworkGates.filter((gate) => !commands.includes(gate.command));
|
|
14615
|
+
}
|
|
14616
|
+
|
|
14617
|
+
// src/gates-report.ts
|
|
14618
|
+
function reportMissingGates(projectRoot, log) {
|
|
14619
|
+
const packageJsonPath = join(projectRoot, "package.json");
|
|
14620
|
+
if (!existsSync(packageJsonPath)) return;
|
|
14621
|
+
let scripts;
|
|
14622
|
+
try {
|
|
14623
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
14624
|
+
const scriptsValue = typeof parsed === "object" && parsed !== null ? parsed.scripts : void 0;
|
|
14625
|
+
if (typeof scriptsValue === "object" && scriptsValue !== null) {
|
|
14626
|
+
scripts = scriptsValue;
|
|
14627
|
+
}
|
|
14628
|
+
} catch {
|
|
14629
|
+
return;
|
|
14630
|
+
}
|
|
14631
|
+
const missing = missingFrameworkGates(scripts);
|
|
14632
|
+
if (missing.length === 0) return;
|
|
14633
|
+
log("");
|
|
14634
|
+
log(
|
|
14635
|
+
`\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) :`
|
|
14636
|
+
);
|
|
14637
|
+
for (const gate of missing) {
|
|
14638
|
+
log(` "${gate.scriptName}": "${gate.command}" # ${gate.intent}`);
|
|
14639
|
+
}
|
|
14640
|
+
log(" puis appelle ces scripts depuis ton script `verify`.");
|
|
14641
|
+
}
|
|
14564
14642
|
|
|
14565
14643
|
// src/governed-files.ts
|
|
14566
14644
|
var GovernedFileSchema = external_exports.object({
|
|
@@ -14627,14 +14705,9 @@ var governedFiles = [
|
|
|
14627
14705
|
}
|
|
14628
14706
|
];
|
|
14629
14707
|
|
|
14630
|
-
// src/checksum.ts
|
|
14631
|
-
function sha256Hex(content) {
|
|
14632
|
-
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
14633
|
-
}
|
|
14634
|
-
|
|
14635
14708
|
// src/project-manifest.ts
|
|
14636
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
14637
|
-
import { join } from "node:path";
|
|
14709
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
14710
|
+
import { join as join2 } from "node:path";
|
|
14638
14711
|
|
|
14639
14712
|
// ../ogygie-core/src/manifest.schema.ts
|
|
14640
14713
|
var FeatureNameSchema = external_exports.string().regex(/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/, "kebab-case required");
|
|
@@ -14918,9 +14991,9 @@ var OgygieManifestSchema = external_exports.object({
|
|
|
14918
14991
|
// src/project-manifest.ts
|
|
14919
14992
|
var MANIFEST_FILE_NAME = "ogygie.manifest.json";
|
|
14920
14993
|
var PinnedVersionSchema = external_exports.object({ ogygieVersion: OgygieVersionSchema.optional() });
|
|
14921
|
-
function
|
|
14922
|
-
const manifestPath =
|
|
14923
|
-
if (!
|
|
14994
|
+
function requireManifestPath(projectRoot) {
|
|
14995
|
+
const manifestPath = join2(projectRoot, MANIFEST_FILE_NAME);
|
|
14996
|
+
if (!existsSync2(manifestPath)) {
|
|
14924
14997
|
throwOgygieError({
|
|
14925
14998
|
code: "OGYGIE_CLI_USAGE",
|
|
14926
14999
|
message: `No ${MANIFEST_FILE_NAME} here \u2014 this is not an Ogygie project root.`,
|
|
@@ -14929,8 +15002,19 @@ function readPinnedOgygieVersion(projectRoot) {
|
|
|
14929
15002
|
suggestedFix: "Run the command from the project root, or scaffold a project first with `pnpm dlx @ogygie/cli init <product-name>`."
|
|
14930
15003
|
});
|
|
14931
15004
|
}
|
|
15005
|
+
return manifestPath;
|
|
15006
|
+
}
|
|
15007
|
+
function readOptionalPinnedOgygieVersion(projectRoot) {
|
|
15008
|
+
const manifestPath = requireManifestPath(projectRoot);
|
|
14932
15009
|
const parsed = PinnedVersionSchema.safeParse(
|
|
14933
|
-
JSON.parse(
|
|
15010
|
+
JSON.parse(readFileSync2(manifestPath, "utf8"))
|
|
15011
|
+
);
|
|
15012
|
+
return parsed.success ? parsed.data.ogygieVersion ?? null : null;
|
|
15013
|
+
}
|
|
15014
|
+
function readPinnedOgygieVersion(projectRoot) {
|
|
15015
|
+
const manifestPath = requireManifestPath(projectRoot);
|
|
15016
|
+
const parsed = PinnedVersionSchema.safeParse(
|
|
15017
|
+
JSON.parse(readFileSync2(manifestPath, "utf8"))
|
|
14934
15018
|
);
|
|
14935
15019
|
const pinned = parsed.success ? parsed.data.ogygieVersion : void 0;
|
|
14936
15020
|
if (pinned === void 0) {
|
|
@@ -14945,28 +15029,126 @@ function readPinnedOgygieVersion(projectRoot) {
|
|
|
14945
15029
|
return pinned;
|
|
14946
15030
|
}
|
|
14947
15031
|
function writePinnedOgygieVersion(projectRoot, version2) {
|
|
14948
|
-
const manifestPath =
|
|
14949
|
-
const content =
|
|
15032
|
+
const manifestPath = join2(projectRoot, MANIFEST_FILE_NAME);
|
|
15033
|
+
const content = readFileSync2(manifestPath, "utf8");
|
|
14950
15034
|
const bumped = content.replace(
|
|
14951
15035
|
/"ogygieVersion"\s*:\s*"[^"]*"/,
|
|
14952
15036
|
`"ogygieVersion": "${version2}"`
|
|
14953
15037
|
);
|
|
14954
15038
|
writeFileSync(manifestPath, bumped);
|
|
14955
15039
|
}
|
|
15040
|
+
function addPinnedOgygieVersion(projectRoot, version2) {
|
|
15041
|
+
const manifestPath = requireManifestPath(projectRoot);
|
|
15042
|
+
const content = readFileSync2(manifestPath, "utf8");
|
|
15043
|
+
const openingBrace = content.indexOf("{");
|
|
15044
|
+
if (openingBrace === -1) {
|
|
15045
|
+
throwOgygieError({
|
|
15046
|
+
code: "OGYGIE_CLI_USAGE",
|
|
15047
|
+
message: `${MANIFEST_FILE_NAME} is not a JSON object \u2014 the pin cannot be added.`,
|
|
15048
|
+
file: MANIFEST_FILE_NAME,
|
|
15049
|
+
cause: "no opening brace found in the manifest",
|
|
15050
|
+
suggestedFix: "Fix the manifest so it is a valid JSON object, then retry."
|
|
15051
|
+
});
|
|
15052
|
+
}
|
|
15053
|
+
const indentation = /\n(\s+)"/.exec(content.slice(openingBrace))?.[1] ?? " ";
|
|
15054
|
+
const pinned = `${content.slice(0, openingBrace + 1)}
|
|
15055
|
+
${indentation}"ogygieVersion": "${version2}",${content.slice(openingBrace + 1)}`;
|
|
15056
|
+
writeFileSync(manifestPath, pinned);
|
|
15057
|
+
}
|
|
15058
|
+
|
|
15059
|
+
// src/commands/adopt.ts
|
|
15060
|
+
async function adoptProject(deps) {
|
|
15061
|
+
const alreadyPinned = readOptionalPinnedOgygieVersion(deps.projectRoot);
|
|
15062
|
+
if (alreadyPinned !== null) {
|
|
15063
|
+
throwOgygieError({
|
|
15064
|
+
code: "OGYGIE_ALREADY_ADOPTED",
|
|
15065
|
+
message: `This project is already pinned to Ogygie ${alreadyPinned} \u2014 adoption is a one-way passage, it does not replay.`,
|
|
15066
|
+
file: MANIFEST_FILE_NAME,
|
|
15067
|
+
cause: `ogygieVersion ${alreadyPinned} already present in the manifest`,
|
|
15068
|
+
suggestedFix: "Use `ogygie upgrade` to move a governed project to a newer version; `adopt` only serves a repo that has never been governed."
|
|
15069
|
+
});
|
|
15070
|
+
}
|
|
15071
|
+
const planned = [];
|
|
15072
|
+
for (const governed of governedFiles) {
|
|
15073
|
+
const content = readFileSync3(
|
|
15074
|
+
join3(deps.templatesDir, governed.templatePath),
|
|
15075
|
+
"utf8"
|
|
15076
|
+
);
|
|
15077
|
+
const absolutePath = join3(deps.projectRoot, governed.targetPath);
|
|
15078
|
+
const occupied = existsSync3(absolutePath);
|
|
15079
|
+
const current = occupied ? readFileSync3(absolutePath, "utf8") : "";
|
|
15080
|
+
const diff = diffLines(current, content);
|
|
15081
|
+
if (diff === "") continue;
|
|
15082
|
+
planned.push({ targetPath: governed.targetPath, content, occupied, diff });
|
|
15083
|
+
}
|
|
15084
|
+
const occupiedWrites = planned.filter((write) => write.occupied);
|
|
15085
|
+
const freshWrites = planned.filter((write) => !write.occupied);
|
|
15086
|
+
if (occupiedWrites.length > 0) {
|
|
15087
|
+
deps.log(
|
|
15088
|
+
`\u26A0 ${occupiedWrites.length} chemin(s) gouvern\xE9(s) portent d\xE9j\xE0 un fichier de ce projet. L'adoption les \xC9CRASE \u2014 leur contenu actuel n'existera plus que dans l'historique git :`
|
|
15089
|
+
);
|
|
15090
|
+
for (const write of occupiedWrites) deps.log(` ${write.targetPath}`);
|
|
15091
|
+
deps.log(
|
|
15092
|
+
" Le partage produit/g\xE9n\xE9rique de ces fichiers est un travail humain : il se fait AVANT cette commande, pas par elle (B7)."
|
|
15093
|
+
);
|
|
15094
|
+
deps.log("");
|
|
15095
|
+
}
|
|
15096
|
+
const fullDiff = [...occupiedWrites, ...freshWrites].map(
|
|
15097
|
+
(write) => `=== ${write.targetPath}${write.occupied ? " (\xE9crase un fichier du projet)" : " (nouveau)"}
|
|
15098
|
+
${write.diff}`
|
|
15099
|
+
).join("\n\n");
|
|
15100
|
+
const confirmed = await deps.confirm(fullDiff);
|
|
15101
|
+
if (!confirmed) {
|
|
15102
|
+
deps.log("ogygie adopt \u2014 abandonn\xE9, aucun fichier modifi\xE9, projet non \xE9pingl\xE9.");
|
|
15103
|
+
return {
|
|
15104
|
+
adoptedVersion: deps.cliVersion,
|
|
15105
|
+
writtenFiles: [],
|
|
15106
|
+
overwrittenProductFiles: []
|
|
15107
|
+
};
|
|
15108
|
+
}
|
|
15109
|
+
for (const write of planned) {
|
|
15110
|
+
const absolutePath = join3(deps.projectRoot, write.targetPath);
|
|
15111
|
+
mkdirSync(dirname(absolutePath), { recursive: true });
|
|
15112
|
+
writeFileSync2(absolutePath, write.content);
|
|
15113
|
+
}
|
|
15114
|
+
addPinnedOgygieVersion(deps.projectRoot, deps.cliVersion);
|
|
15115
|
+
deps.log(
|
|
15116
|
+
`ogygie adopt \u2014 projet adopt\xE9 en ${deps.cliVersion}, ${planned.length} fichier(s) gouvern\xE9(s) pos\xE9(s) dont ${occupiedWrites.length} \xE9cras\xE9(s).`
|
|
15117
|
+
);
|
|
15118
|
+
deps.log(
|
|
15119
|
+
`Rappel lockstep (D2) : installer @ogygie/core ET @ogygie/cli en ${deps.cliVersion} \xE0 l'EXACT (\`pnpm add -D @ogygie/cli@${deps.cliVersion}\`, \`pnpm add @ogygie/core@${deps.cliVersion}\`), puis committer l'adoption SEULE \u2014 jamais m\xEAl\xE9e \xE0 une feature.`
|
|
15120
|
+
);
|
|
15121
|
+
reportMissingGates(deps.projectRoot, deps.log);
|
|
15122
|
+
return {
|
|
15123
|
+
adoptedVersion: deps.cliVersion,
|
|
15124
|
+
writtenFiles: planned.map((write) => write.targetPath),
|
|
15125
|
+
overwrittenProductFiles: occupiedWrites.map((write) => write.targetPath)
|
|
15126
|
+
};
|
|
15127
|
+
}
|
|
15128
|
+
|
|
15129
|
+
// src/commands/check.ts
|
|
15130
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
15131
|
+
import { join as join4 } from "node:path";
|
|
15132
|
+
|
|
15133
|
+
// src/checksum.ts
|
|
15134
|
+
import { createHash } from "node:crypto";
|
|
15135
|
+
function sha256Hex(content) {
|
|
15136
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
15137
|
+
}
|
|
14956
15138
|
|
|
14957
15139
|
// src/commands/check.ts
|
|
14958
15140
|
var PackageVersionSchema = external_exports.object({ version: external_exports.string() });
|
|
14959
15141
|
function installedCoreVersion(projectRoot) {
|
|
14960
|
-
const packagePath =
|
|
15142
|
+
const packagePath = join4(
|
|
14961
15143
|
projectRoot,
|
|
14962
15144
|
"node_modules",
|
|
14963
15145
|
"@ogygie",
|
|
14964
15146
|
"core",
|
|
14965
15147
|
"package.json"
|
|
14966
15148
|
);
|
|
14967
|
-
if (!
|
|
15149
|
+
if (!existsSync4(packagePath)) return null;
|
|
14968
15150
|
const parsed = PackageVersionSchema.safeParse(
|
|
14969
|
-
JSON.parse(
|
|
15151
|
+
JSON.parse(readFileSync4(packagePath, "utf8"))
|
|
14970
15152
|
);
|
|
14971
15153
|
return parsed.success ? parsed.data.version : null;
|
|
14972
15154
|
}
|
|
@@ -15001,8 +15183,8 @@ function checkProject(deps) {
|
|
|
15001
15183
|
for (const governed of governedFiles) {
|
|
15002
15184
|
const expected = expectedChecksums[governed.targetPath];
|
|
15003
15185
|
if (expected === void 0) continue;
|
|
15004
|
-
const absolutePath =
|
|
15005
|
-
const actual =
|
|
15186
|
+
const absolutePath = join4(deps.projectRoot, governed.targetPath);
|
|
15187
|
+
const actual = existsSync4(absolutePath) ? sha256Hex(readFileSync4(absolutePath, "utf8")) : "<absent>";
|
|
15006
15188
|
if (actual !== expected) {
|
|
15007
15189
|
problems.push(
|
|
15008
15190
|
ogygieError({
|
|
@@ -15020,23 +15202,23 @@ function checkProject(deps) {
|
|
|
15020
15202
|
|
|
15021
15203
|
// src/commands/init.ts
|
|
15022
15204
|
import {
|
|
15023
|
-
existsSync as
|
|
15024
|
-
mkdirSync,
|
|
15205
|
+
existsSync as existsSync5,
|
|
15206
|
+
mkdirSync as mkdirSync2,
|
|
15025
15207
|
readdirSync,
|
|
15026
|
-
readFileSync as
|
|
15208
|
+
readFileSync as readFileSync5,
|
|
15027
15209
|
statSync,
|
|
15028
|
-
writeFileSync as
|
|
15210
|
+
writeFileSync as writeFileSync3
|
|
15029
15211
|
} from "node:fs";
|
|
15030
|
-
import { dirname, join as
|
|
15212
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
15031
15213
|
var PRODUCT_NAME_PATTERN = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
15032
15214
|
var RENAMED_TEMPLATE_FILES = {
|
|
15033
15215
|
gitignore: ".gitignore"
|
|
15034
15216
|
};
|
|
15035
15217
|
function listTemplateFiles(root, folder = "") {
|
|
15036
15218
|
const files = [];
|
|
15037
|
-
for (const entry of readdirSync(
|
|
15219
|
+
for (const entry of readdirSync(join5(root, folder)).sort()) {
|
|
15038
15220
|
const relativePath = folder === "" ? entry : `${folder}/${entry}`;
|
|
15039
|
-
if (statSync(
|
|
15221
|
+
if (statSync(join5(root, relativePath)).isDirectory()) {
|
|
15040
15222
|
files.push(...listTemplateFiles(root, relativePath));
|
|
15041
15223
|
} else {
|
|
15042
15224
|
files.push(relativePath);
|
|
@@ -15061,16 +15243,16 @@ function initProject(deps, productName2) {
|
|
|
15061
15243
|
suggestedFix: "Pick a kebab-case name, e.g. `ogygie init order-desk`."
|
|
15062
15244
|
});
|
|
15063
15245
|
}
|
|
15064
|
-
if (
|
|
15246
|
+
if (existsSync5(join5(deps.targetDir, MANIFEST_FILE_NAME))) {
|
|
15065
15247
|
throwOgygieError({
|
|
15066
15248
|
code: "OGYGIE_INIT_TARGET_NOT_EMPTY",
|
|
15067
15249
|
message: `The target directory already hosts an Ogygie project (${MANIFEST_FILE_NAME} found).`,
|
|
15068
15250
|
file: MANIFEST_FILE_NAME,
|
|
15069
|
-
cause: `${
|
|
15251
|
+
cause: `${join5(deps.targetDir, MANIFEST_FILE_NAME)} exists`,
|
|
15070
15252
|
suggestedFix: "Run `pnpm ogygie upgrade` in that project instead of init."
|
|
15071
15253
|
});
|
|
15072
15254
|
}
|
|
15073
|
-
const existingEntries =
|
|
15255
|
+
const existingEntries = existsSync5(deps.targetDir) ? readdirSync(deps.targetDir).filter((entry) => entry !== ".git") : [];
|
|
15074
15256
|
if (existingEntries.length > 0) {
|
|
15075
15257
|
throwOgygieError({
|
|
15076
15258
|
code: "OGYGIE_INIT_TARGET_NOT_EMPTY",
|
|
@@ -15080,14 +15262,14 @@ function initProject(deps, productName2) {
|
|
|
15080
15262
|
suggestedFix: "Start from an empty directory (only a .git folder is tolerated)."
|
|
15081
15263
|
});
|
|
15082
15264
|
}
|
|
15083
|
-
const projectTemplates =
|
|
15265
|
+
const projectTemplates = join5(deps.templatesDir, "project");
|
|
15084
15266
|
const writtenFiles = [];
|
|
15085
15267
|
for (const templateRelativePath of listTemplateFiles(projectTemplates)) {
|
|
15086
|
-
const content =
|
|
15268
|
+
const content = readFileSync5(join5(projectTemplates, templateRelativePath), "utf8").replaceAll("__PRODUCT_NAME__", productName2).replaceAll("__OGYGIE_VERSION__", deps.cliVersion);
|
|
15087
15269
|
const targetRelativePath = targetPathOf(templateRelativePath);
|
|
15088
|
-
const absoluteTarget =
|
|
15089
|
-
|
|
15090
|
-
|
|
15270
|
+
const absoluteTarget = join5(deps.targetDir, targetRelativePath);
|
|
15271
|
+
mkdirSync2(dirname2(absoluteTarget), { recursive: true });
|
|
15272
|
+
writeFileSync3(absoluteTarget, content);
|
|
15091
15273
|
writtenFiles.push(targetRelativePath);
|
|
15092
15274
|
}
|
|
15093
15275
|
deps.log(`ogygie init \u2014 ${writtenFiles.length} fichiers \xE9crits (ogygieVersion ${deps.cliVersion}).`);
|
|
@@ -15103,11 +15285,11 @@ function initProject(deps, productName2) {
|
|
|
15103
15285
|
|
|
15104
15286
|
// src/commands/studio.ts
|
|
15105
15287
|
import { createRequire } from "node:module";
|
|
15106
|
-
import { join as
|
|
15288
|
+
import { join as join6 } from "node:path";
|
|
15107
15289
|
import { pathToFileURL } from "node:url";
|
|
15108
15290
|
var PUBLISHED_STUDIO_SERVER = "@ogygie/studio/server";
|
|
15109
15291
|
async function loadStudioFrom(projectRoot) {
|
|
15110
|
-
const resolveFromProject = createRequire(
|
|
15292
|
+
const resolveFromProject = createRequire(join6(projectRoot, "package.json"));
|
|
15111
15293
|
const entryPoint = resolveFromProject.resolve(PUBLISHED_STUDIO_SERVER);
|
|
15112
15294
|
return await import(pathToFileURL(entryPoint).href);
|
|
15113
15295
|
}
|
|
@@ -15146,13 +15328,13 @@ async function runStudio(deps) {
|
|
|
15146
15328
|
}
|
|
15147
15329
|
|
|
15148
15330
|
// src/commands/mcp.ts
|
|
15149
|
-
import { readFileSync as
|
|
15331
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
15150
15332
|
import { createRequire as createRequire2 } from "node:module";
|
|
15151
|
-
import { join as
|
|
15333
|
+
import { join as join7 } from "node:path";
|
|
15152
15334
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
15153
15335
|
var PUBLISHED_MCP_SERVER = "@ogygie/mcp/server";
|
|
15154
15336
|
async function loadMcpFrom(projectRoot) {
|
|
15155
|
-
const resolveFromProject = createRequire2(
|
|
15337
|
+
const resolveFromProject = createRequire2(join7(projectRoot, "package.json"));
|
|
15156
15338
|
const entryPoint = resolveFromProject.resolve(PUBLISHED_MCP_SERVER);
|
|
15157
15339
|
return await import(pathToFileURL2(entryPoint).href);
|
|
15158
15340
|
}
|
|
@@ -15189,64 +15371,13 @@ async function runMcp(deps) {
|
|
|
15189
15371
|
cwd: deps.projectRoot,
|
|
15190
15372
|
version: deps.cliVersion,
|
|
15191
15373
|
token: process.env["OGYGIE_MCP_TOKEN"],
|
|
15192
|
-
readConfigFile: (absolutePath) =>
|
|
15374
|
+
readConfigFile: (absolutePath) => readFileSync6(absolutePath, "utf8")
|
|
15193
15375
|
});
|
|
15194
15376
|
}
|
|
15195
15377
|
|
|
15196
15378
|
// src/commands/upgrade.ts
|
|
15197
|
-
import { existsSync as
|
|
15198
|
-
import { dirname as
|
|
15199
|
-
|
|
15200
|
-
// src/diff.ts
|
|
15201
|
-
function diffLines(oldText, newText) {
|
|
15202
|
-
if (oldText === newText) return "";
|
|
15203
|
-
const oldLines = oldText.split("\n");
|
|
15204
|
-
const newLines = newText.split("\n");
|
|
15205
|
-
const lcs = Array.from(
|
|
15206
|
-
{ length: oldLines.length + 1 },
|
|
15207
|
-
() => new Array(newLines.length + 1).fill(0)
|
|
15208
|
-
);
|
|
15209
|
-
for (let i2 = oldLines.length - 1; i2 >= 0; i2 -= 1) {
|
|
15210
|
-
for (let j2 = newLines.length - 1; j2 >= 0; j2 -= 1) {
|
|
15211
|
-
lcs[i2][j2] = oldLines[i2] === newLines[j2] ? (lcs[i2 + 1][j2 + 1] ?? 0) + 1 : Math.max(lcs[i2 + 1][j2] ?? 0, lcs[i2][j2 + 1] ?? 0);
|
|
15212
|
-
}
|
|
15213
|
-
}
|
|
15214
|
-
const output = [];
|
|
15215
|
-
let previousWasChange = false;
|
|
15216
|
-
let i = 0;
|
|
15217
|
-
let j = 0;
|
|
15218
|
-
while (i < oldLines.length || j < newLines.length) {
|
|
15219
|
-
if (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {
|
|
15220
|
-
previousWasChange = false;
|
|
15221
|
-
i += 1;
|
|
15222
|
-
j += 1;
|
|
15223
|
-
} else {
|
|
15224
|
-
if (!previousWasChange) output.push(`@@ line ${j + 1}`);
|
|
15225
|
-
previousWasChange = true;
|
|
15226
|
-
if (j >= newLines.length || i < oldLines.length && (lcs[i + 1]?.[j] ?? 0) >= (lcs[i]?.[j + 1] ?? 0)) {
|
|
15227
|
-
output.push(`- ${oldLines[i]}`);
|
|
15228
|
-
i += 1;
|
|
15229
|
-
} else {
|
|
15230
|
-
output.push(`+ ${newLines[j]}`);
|
|
15231
|
-
j += 1;
|
|
15232
|
-
}
|
|
15233
|
-
}
|
|
15234
|
-
}
|
|
15235
|
-
return output.join("\n");
|
|
15236
|
-
}
|
|
15237
|
-
|
|
15238
|
-
// src/framework-gates.ts
|
|
15239
|
-
var frameworkGates = [
|
|
15240
|
-
{
|
|
15241
|
-
scriptName: "ogygie:verify-vocabulary",
|
|
15242
|
-
command: "ogygie verify-vocabulary",
|
|
15243
|
-
intent: "Le vocabulaire d\xE9clar\xE9 au manifeste : alias interdits dans les identifiants, puret\xE9 des fichiers gouvern\xE9s."
|
|
15244
|
-
}
|
|
15245
|
-
];
|
|
15246
|
-
function missingFrameworkGates(scripts) {
|
|
15247
|
-
const commands = Object.values(scripts ?? {}).join(" ; ");
|
|
15248
|
-
return frameworkGates.filter((gate) => !commands.includes(gate.command));
|
|
15249
|
-
}
|
|
15379
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
15380
|
+
import { dirname as dirname3, join as join8 } from "node:path";
|
|
15250
15381
|
|
|
15251
15382
|
// src/version.ts
|
|
15252
15383
|
function compareVersions(a, b) {
|
|
@@ -15262,30 +15393,6 @@ function compareVersions(a, b) {
|
|
|
15262
15393
|
}
|
|
15263
15394
|
|
|
15264
15395
|
// src/commands/upgrade.ts
|
|
15265
|
-
function reportMissingGates(deps) {
|
|
15266
|
-
const packageJsonPath = join6(deps.projectRoot, "package.json");
|
|
15267
|
-
if (!existsSync4(packageJsonPath)) return;
|
|
15268
|
-
let scripts;
|
|
15269
|
-
try {
|
|
15270
|
-
const parsed = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
|
|
15271
|
-
const scriptsValue = typeof parsed === "object" && parsed !== null ? parsed.scripts : void 0;
|
|
15272
|
-
if (typeof scriptsValue === "object" && scriptsValue !== null) {
|
|
15273
|
-
scripts = scriptsValue;
|
|
15274
|
-
}
|
|
15275
|
-
} catch {
|
|
15276
|
-
return;
|
|
15277
|
-
}
|
|
15278
|
-
const missing = missingFrameworkGates(scripts);
|
|
15279
|
-
if (missing.length === 0) return;
|
|
15280
|
-
deps.log("");
|
|
15281
|
-
deps.log(
|
|
15282
|
-
`\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) :`
|
|
15283
|
-
);
|
|
15284
|
-
for (const gate of missing) {
|
|
15285
|
-
deps.log(` "${gate.scriptName}": "${gate.command}" # ${gate.intent}`);
|
|
15286
|
-
}
|
|
15287
|
-
deps.log(" puis appelle ces scripts depuis ton script `verify`.");
|
|
15288
|
-
}
|
|
15289
15396
|
async function upgradeProject(deps) {
|
|
15290
15397
|
const installedVersion = readPinnedOgygieVersion(deps.projectRoot);
|
|
15291
15398
|
if (compareVersions(deps.cliVersion, installedVersion) < 0) {
|
|
@@ -15318,8 +15425,8 @@ async function upgradeProject(deps) {
|
|
|
15318
15425
|
for (const governed of governedFiles) {
|
|
15319
15426
|
const expected = installedChecksums[governed.targetPath];
|
|
15320
15427
|
if (expected === void 0) continue;
|
|
15321
|
-
const absolutePath =
|
|
15322
|
-
const actual =
|
|
15428
|
+
const absolutePath = join8(deps.projectRoot, governed.targetPath);
|
|
15429
|
+
const actual = existsSync6(absolutePath) ? sha256Hex(readFileSync7(absolutePath, "utf8")) : "<absent>";
|
|
15323
15430
|
if (actual !== expected) {
|
|
15324
15431
|
throwOgygieError({
|
|
15325
15432
|
code: "OGYGIE_GOVERNED_FILE_MODIFIED",
|
|
@@ -15333,12 +15440,12 @@ async function upgradeProject(deps) {
|
|
|
15333
15440
|
const plannedWrites = [];
|
|
15334
15441
|
const diffBlocks = [];
|
|
15335
15442
|
for (const governed of governedFiles) {
|
|
15336
|
-
const content =
|
|
15337
|
-
|
|
15443
|
+
const content = readFileSync7(
|
|
15444
|
+
join8(deps.templatesDir, governed.templatePath),
|
|
15338
15445
|
"utf8"
|
|
15339
15446
|
);
|
|
15340
|
-
const absolutePath =
|
|
15341
|
-
const current =
|
|
15447
|
+
const absolutePath = join8(deps.projectRoot, governed.targetPath);
|
|
15448
|
+
const current = existsSync6(absolutePath) ? readFileSync7(absolutePath, "utf8") : "";
|
|
15342
15449
|
const diff = diffLines(current, content);
|
|
15343
15450
|
if (diff !== "") {
|
|
15344
15451
|
plannedWrites.push({ targetPath: governed.targetPath, content });
|
|
@@ -15358,9 +15465,9 @@ ${diff}`);
|
|
|
15358
15465
|
};
|
|
15359
15466
|
}
|
|
15360
15467
|
for (const write of plannedWrites) {
|
|
15361
|
-
const absolutePath =
|
|
15362
|
-
|
|
15363
|
-
|
|
15468
|
+
const absolutePath = join8(deps.projectRoot, write.targetPath);
|
|
15469
|
+
mkdirSync3(dirname3(absolutePath), { recursive: true });
|
|
15470
|
+
writeFileSync4(absolutePath, write.content);
|
|
15364
15471
|
}
|
|
15365
15472
|
}
|
|
15366
15473
|
writePinnedOgygieVersion(deps.projectRoot, deps.cliVersion);
|
|
@@ -15370,7 +15477,7 @@ ${diff}`);
|
|
|
15370
15477
|
deps.log(
|
|
15371
15478
|
`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.`
|
|
15372
15479
|
);
|
|
15373
|
-
reportMissingGates(deps);
|
|
15480
|
+
reportMissingGates(deps.projectRoot, deps.log);
|
|
15374
15481
|
return {
|
|
15375
15482
|
fromVersion: installedVersion,
|
|
15376
15483
|
toVersion: deps.cliVersion,
|
|
@@ -15379,8 +15486,8 @@ ${diff}`);
|
|
|
15379
15486
|
}
|
|
15380
15487
|
|
|
15381
15488
|
// src/commands/verify-vocabulary.ts
|
|
15382
|
-
import { existsSync as
|
|
15383
|
-
import { join as
|
|
15489
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
|
|
15490
|
+
import { join as join9, relative, sep } from "node:path";
|
|
15384
15491
|
|
|
15385
15492
|
// ../ogygie-core/src/vocabulary.ts
|
|
15386
15493
|
var TOKEN_PATTERN = new RegExp("\\p{L}[\\p{L}\\p{N}]*(?:[-_$][\\p{L}\\p{N}]+)*", "gu");
|
|
@@ -15406,6 +15513,22 @@ function tokenCarriesRoot(tokenSegments, rootSegments) {
|
|
|
15406
15513
|
}
|
|
15407
15514
|
return false;
|
|
15408
15515
|
}
|
|
15516
|
+
function tokenCarriesExactName(token, name) {
|
|
15517
|
+
const tokenSegments = segmentsOfIdentifier(token);
|
|
15518
|
+
const nameSegments = segmentsOfIdentifier(name);
|
|
15519
|
+
if (nameSegments.length === 0) return false;
|
|
15520
|
+
for (let start = 0; start + nameSegments.length <= tokenSegments.length; start += 1) {
|
|
15521
|
+
let allMatch = true;
|
|
15522
|
+
for (let offset = 0; offset < nameSegments.length; offset += 1) {
|
|
15523
|
+
if (tokenSegments[start + offset] !== nameSegments[offset]) {
|
|
15524
|
+
allMatch = false;
|
|
15525
|
+
break;
|
|
15526
|
+
}
|
|
15527
|
+
}
|
|
15528
|
+
if (allMatch) return true;
|
|
15529
|
+
}
|
|
15530
|
+
return false;
|
|
15531
|
+
}
|
|
15409
15532
|
function findProductVocabulary(sourceText, vocabularyRoots) {
|
|
15410
15533
|
const roots = vocabularyRoots.map((root) => ({
|
|
15411
15534
|
root,
|
|
@@ -15452,6 +15575,13 @@ function canonicalByAlias(vocabulary) {
|
|
|
15452
15575
|
}
|
|
15453
15576
|
return byAlias;
|
|
15454
15577
|
}
|
|
15578
|
+
function derogatedFeatureIdsOf(vocabulary, featureIds) {
|
|
15579
|
+
if (vocabulary === void 0 || vocabulary.entries.length === 0) return [];
|
|
15580
|
+
const forbiddenAliases = [...canonicalByAlias(vocabulary).keys()];
|
|
15581
|
+
return featureIds.filter(
|
|
15582
|
+
(featureId) => findProductVocabulary(featureId, forbiddenAliases).length > 0
|
|
15583
|
+
);
|
|
15584
|
+
}
|
|
15455
15585
|
function auditProductVocabulary(input) {
|
|
15456
15586
|
const { vocabulary } = input;
|
|
15457
15587
|
if (vocabulary === void 0 || vocabulary.entries.length === 0) return [];
|
|
@@ -15459,9 +15589,12 @@ function auditProductVocabulary(input) {
|
|
|
15459
15589
|
const forbiddenAliases = [...canonicalOfAlias.keys()];
|
|
15460
15590
|
const canonicalTerms = vocabulary.entries.map((entry) => entry.canonical);
|
|
15461
15591
|
const violations = [];
|
|
15592
|
+
const featureIds = input.featureIds ?? [];
|
|
15593
|
+
const derivesFromAFeatureId = (identifier) => featureIds.some((featureId) => tokenCarriesExactName(identifier, featureId));
|
|
15462
15594
|
for (const file2 of input.sourceFiles) {
|
|
15463
15595
|
const judged = stripProseStrings(stripComments(file2.content));
|
|
15464
15596
|
for (const match of findProductVocabulary(judged, forbiddenAliases)) {
|
|
15597
|
+
if (derivesFromAFeatureId(match.identifier)) continue;
|
|
15465
15598
|
violations.push({
|
|
15466
15599
|
kind: "FORBIDDEN_ALIAS",
|
|
15467
15600
|
filePath: file2.path,
|
|
@@ -15487,8 +15620,8 @@ function auditProductVocabulary(input) {
|
|
|
15487
15620
|
var CODE_EXTENSIONS = [".ts", ".tsx", ".mts", ".mjs", ".js", ".jsx"];
|
|
15488
15621
|
var IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", ".git", "coverage"]);
|
|
15489
15622
|
function readManifest(projectRoot) {
|
|
15490
|
-
const manifestPath =
|
|
15491
|
-
if (!
|
|
15623
|
+
const manifestPath = join9(projectRoot, MANIFEST_FILE_NAME);
|
|
15624
|
+
if (!existsSync7(manifestPath)) {
|
|
15492
15625
|
throwOgygieError({
|
|
15493
15626
|
code: "OGYGIE_CLI_USAGE",
|
|
15494
15627
|
message: `No ${MANIFEST_FILE_NAME} here \u2014 this is not an Ogygie project root.`,
|
|
@@ -15498,7 +15631,7 @@ function readManifest(projectRoot) {
|
|
|
15498
15631
|
});
|
|
15499
15632
|
}
|
|
15500
15633
|
const parsed = OgygieManifestSchema.safeParse(
|
|
15501
|
-
JSON.parse(
|
|
15634
|
+
JSON.parse(readFileSync8(manifestPath, "utf8"))
|
|
15502
15635
|
);
|
|
15503
15636
|
if (!parsed.success) {
|
|
15504
15637
|
throwOgygieError({
|
|
@@ -15512,13 +15645,13 @@ function readManifest(projectRoot) {
|
|
|
15512
15645
|
return parsed.data;
|
|
15513
15646
|
}
|
|
15514
15647
|
function collectCodeFiles(projectRoot, folder) {
|
|
15515
|
-
const absoluteFolder =
|
|
15516
|
-
if (!
|
|
15648
|
+
const absoluteFolder = join9(projectRoot, folder);
|
|
15649
|
+
if (!existsSync7(absoluteFolder)) return [];
|
|
15517
15650
|
const files = [];
|
|
15518
15651
|
const walk = (directory) => {
|
|
15519
15652
|
for (const entry of readdirSync2(directory)) {
|
|
15520
15653
|
if (IGNORED_DIRS.has(entry)) continue;
|
|
15521
|
-
const entryPath =
|
|
15654
|
+
const entryPath = join9(directory, entry);
|
|
15522
15655
|
if (statSync2(entryPath).isDirectory()) {
|
|
15523
15656
|
walk(entryPath);
|
|
15524
15657
|
continue;
|
|
@@ -15526,7 +15659,7 @@ function collectCodeFiles(projectRoot, folder) {
|
|
|
15526
15659
|
if (!CODE_EXTENSIONS.some((extension) => entry.endsWith(extension))) continue;
|
|
15527
15660
|
files.push({
|
|
15528
15661
|
path: relative(projectRoot, entryPath).split(sep).join("/"),
|
|
15529
|
-
content:
|
|
15662
|
+
content: readFileSync8(entryPath, "utf8")
|
|
15530
15663
|
});
|
|
15531
15664
|
}
|
|
15532
15665
|
};
|
|
@@ -15551,17 +15684,25 @@ function verifyVocabulary(deps) {
|
|
|
15551
15684
|
`${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(", ")}`
|
|
15552
15685
|
);
|
|
15553
15686
|
}
|
|
15687
|
+
const featureIds = manifest.features.map((feature) => feature.name);
|
|
15688
|
+
const derogated = derogatedFeatureIdsOf(manifest.vocabulary, featureIds);
|
|
15689
|
+
if (derogated.length > 0 && deps.log !== void 0) {
|
|
15690
|
+
deps.log(
|
|
15691
|
+
`${derogated.length} id(s) de feature hors du scan des alias, et les identifiants qui en D\xC9RIVENT (le manifeste les nomme, le code les suit) : ${derogated.join(", ")}`
|
|
15692
|
+
);
|
|
15693
|
+
}
|
|
15554
15694
|
const sourceFiles = productCodeFoldersOf(manifest).flatMap(
|
|
15555
15695
|
(folder) => collectCodeFiles(deps.projectRoot, folder)
|
|
15556
15696
|
);
|
|
15557
|
-
const governed = governedFiles.filter((file2) =>
|
|
15697
|
+
const governed = governedFiles.filter((file2) => existsSync7(join9(deps.projectRoot, file2.targetPath))).map((file2) => ({
|
|
15558
15698
|
path: file2.targetPath,
|
|
15559
|
-
content:
|
|
15699
|
+
content: readFileSync8(join9(deps.projectRoot, file2.targetPath), "utf8")
|
|
15560
15700
|
}));
|
|
15561
15701
|
return auditProductVocabulary({
|
|
15562
15702
|
vocabulary: manifest.vocabulary,
|
|
15563
15703
|
sourceFiles,
|
|
15564
|
-
governedFiles: governed
|
|
15704
|
+
governedFiles: governed,
|
|
15705
|
+
featureIds
|
|
15565
15706
|
}).map(
|
|
15566
15707
|
(violation) => violation.kind === "FORBIDDEN_ALIAS" ? ogygieError({
|
|
15567
15708
|
code: "OGYGIE_VOCABULARY_FORBIDDEN_ALIAS",
|
|
@@ -15671,6 +15812,30 @@ var governedChecksumsByVersion = {
|
|
|
15671
15812
|
"scripts/verify-specs.ts": "04289dbeeb029703779757a2d8b58852c6b16414ab7503bf3223cfe4e9a4d45e",
|
|
15672
15813
|
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15673
15814
|
},
|
|
15815
|
+
"0.12.0": {
|
|
15816
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15817
|
+
"AGENTS.md": "74725a71e531502d4d4898518bad68e610a370bd410058b58e11220532dc327f",
|
|
15818
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15819
|
+
"scripts/lint-interdits.ts": "20c32a6f3af4aa68b24fe37b25a2989f58e77fd96808c3853dc6fd98f4c9af84",
|
|
15820
|
+
"scripts/ogygie-design-brief.ts": "aba1585717b30808dd0bc8930a37a1ad2d83ee6869f969dbd06ca595e37a4a89",
|
|
15821
|
+
"scripts/ogygie-impact.ts": "f06cc8b4a46ddfbcfb32ba747c2454548ca49918123d04ab13d210b4a89c1fb9",
|
|
15822
|
+
"scripts/verify-contracts.ts": "9cd64c88dcc3b54b65bf1e1b7a68eb00353ee16f2a9ce8bca3c49ba063c2a08b",
|
|
15823
|
+
"scripts/verify-manifest.ts": "1adca9edee11e54ccaf0511012411516c52c68a32f686ca320c7e437afb22c7c",
|
|
15824
|
+
"scripts/verify-specs.ts": "04289dbeeb029703779757a2d8b58852c6b16414ab7503bf3223cfe4e9a4d45e",
|
|
15825
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15826
|
+
},
|
|
15827
|
+
"0.13.0": {
|
|
15828
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15829
|
+
"AGENTS.md": "74725a71e531502d4d4898518bad68e610a370bd410058b58e11220532dc327f",
|
|
15830
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15831
|
+
"scripts/lint-interdits.ts": "20c32a6f3af4aa68b24fe37b25a2989f58e77fd96808c3853dc6fd98f4c9af84",
|
|
15832
|
+
"scripts/ogygie-design-brief.ts": "aba1585717b30808dd0bc8930a37a1ad2d83ee6869f969dbd06ca595e37a4a89",
|
|
15833
|
+
"scripts/ogygie-impact.ts": "f06cc8b4a46ddfbcfb32ba747c2454548ca49918123d04ab13d210b4a89c1fb9",
|
|
15834
|
+
"scripts/verify-contracts.ts": "9cd64c88dcc3b54b65bf1e1b7a68eb00353ee16f2a9ce8bca3c49ba063c2a08b",
|
|
15835
|
+
"scripts/verify-manifest.ts": "1adca9edee11e54ccaf0511012411516c52c68a32f686ca320c7e437afb22c7c",
|
|
15836
|
+
"scripts/verify-specs.ts": "04289dbeeb029703779757a2d8b58852c6b16414ab7503bf3223cfe4e9a4d45e",
|
|
15837
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15838
|
+
},
|
|
15674
15839
|
"0.2.0": {
|
|
15675
15840
|
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15676
15841
|
"AGENTS.md": "ca73ab619d8c55fac11858d815bf5fbab6537ecebe7ae4123cbdeb3e3e36bf1a",
|
|
@@ -15764,23 +15929,23 @@ var governedChecksumsByVersion = {
|
|
|
15764
15929
|
};
|
|
15765
15930
|
|
|
15766
15931
|
// src/paths.ts
|
|
15767
|
-
import { existsSync as
|
|
15768
|
-
import { dirname as
|
|
15932
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "node:fs";
|
|
15933
|
+
import { dirname as dirname4, join as join10 } from "node:path";
|
|
15769
15934
|
import { fileURLToPath } from "node:url";
|
|
15770
|
-
var PACKAGE_ROOT =
|
|
15935
|
+
var PACKAGE_ROOT = join10(dirname4(fileURLToPath(import.meta.url)), "..");
|
|
15771
15936
|
var PackageVersionSchema2 = external_exports.object({ version: external_exports.string() });
|
|
15772
15937
|
function cliVersion() {
|
|
15773
15938
|
return PackageVersionSchema2.parse(
|
|
15774
|
-
JSON.parse(
|
|
15939
|
+
JSON.parse(readFileSync9(join10(PACKAGE_ROOT, "package.json"), "utf8"))
|
|
15775
15940
|
).version;
|
|
15776
15941
|
}
|
|
15777
15942
|
function templatesDir() {
|
|
15778
|
-
const embedded =
|
|
15779
|
-
if (
|
|
15943
|
+
const embedded = join10(PACKAGE_ROOT, "templates");
|
|
15944
|
+
if (existsSync8(embedded)) return embedded;
|
|
15780
15945
|
return repoTemplatesDir();
|
|
15781
15946
|
}
|
|
15782
15947
|
function repoTemplatesDir() {
|
|
15783
|
-
return
|
|
15948
|
+
return join10(PACKAGE_ROOT, "..", "..", "templates");
|
|
15784
15949
|
}
|
|
15785
15950
|
|
|
15786
15951
|
// src/cli.ts
|
|
@@ -15800,7 +15965,7 @@ function printError(error51) {
|
|
|
15800
15965
|
console.error(` fix : ${error51.suggestedFix}`);
|
|
15801
15966
|
}
|
|
15802
15967
|
async function confirmOverwrite(fullDiff) {
|
|
15803
|
-
console.log("Diff des fichiers gouvern\xE9s (
|
|
15968
|
+
console.log("Diff des fichiers gouvern\xE9s (\xE9tat actuel \u2192 version cible) :\n");
|
|
15804
15969
|
console.log(fullDiff === "" ? "(aucune diff\xE9rence de contenu)" : fullDiff);
|
|
15805
15970
|
if (skipConfirmation) return true;
|
|
15806
15971
|
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -15821,6 +15986,14 @@ try {
|
|
|
15821
15986
|
},
|
|
15822
15987
|
productName ?? ""
|
|
15823
15988
|
);
|
|
15989
|
+
} else if (command === "adopt") {
|
|
15990
|
+
await adoptProject({
|
|
15991
|
+
projectRoot: process.cwd(),
|
|
15992
|
+
templatesDir: templatesDir(),
|
|
15993
|
+
cliVersion: cliVersion(),
|
|
15994
|
+
confirm: confirmOverwrite,
|
|
15995
|
+
log: (line) => console.log(line)
|
|
15996
|
+
});
|
|
15824
15997
|
} else if (command === "upgrade") {
|
|
15825
15998
|
await upgradeProject({
|
|
15826
15999
|
projectRoot: process.cwd(),
|
|
@@ -15871,8 +16044,8 @@ ogygie verify-vocabulary \u2014 ${problems.length} probl\xE8me(s).`);
|
|
|
15871
16044
|
code: "OGYGIE_CLI_USAGE",
|
|
15872
16045
|
message: `Unknown command ${JSON.stringify(command ?? "")}.`,
|
|
15873
16046
|
file: null,
|
|
15874
|
-
cause: `expected one of: init <product-name>, upgrade, check, verify-vocabulary, studio, mcp (flags: --json, --yes)`,
|
|
15875
|
-
suggestedFix: "Run `ogygie init <product-name>`, `ogygie upgrade`, `ogygie check`, `ogygie verify-vocabulary`, `ogygie studio` or `ogygie mcp`."
|
|
16047
|
+
cause: `expected one of: init <product-name>, adopt, upgrade, check, verify-vocabulary, studio, mcp (flags: --json, --yes)`,
|
|
16048
|
+
suggestedFix: "Run `ogygie init <product-name>`, `ogygie adopt`, `ogygie upgrade`, `ogygie check`, `ogygie verify-vocabulary`, `ogygie studio` or `ogygie mcp`."
|
|
15876
16049
|
})
|
|
15877
16050
|
);
|
|
15878
16051
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogygie/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@ogygie/core": "0.
|
|
20
|
+
"@ogygie/core": "0.13.0",
|
|
21
21
|
"@types/node": "^24.0.0",
|
|
22
22
|
"esbuild": "^0.28.0",
|
|
23
23
|
"tsx": "^4.20.0",
|
|
@@ -267,6 +267,16 @@ il devient actif le jour où le produit déclare, jamais avant. Renommer un
|
|
|
267
267
|
concept est donc une opération à un seul endroit — on change le canonique, le
|
|
268
268
|
gate liste tous les identifiants à corriger.
|
|
269
269
|
|
|
270
|
+
**Les identifiants DÉRIVÉS d'un id de feature sortent du scan des alias.** Un
|
|
271
|
+
id de feature nomme un document et suit la langue du projet ; le code en dérive
|
|
272
|
+
mécaniquement son dossier `features/<id>/`, ses chemins d'import et ses
|
|
273
|
+
`register<Id>Routes`. Un token qui porte l'id de feature comme suite EXACTE de
|
|
274
|
+
segments n'est donc pas jugé, et le gate ANNONCE les ids concernés à chaque
|
|
275
|
+
exécution. Un identifiant qui ne fait que COMMENCER par la racine sans reprendre
|
|
276
|
+
l'id entier reste jugé : la dérogation couvre la dérivation, jamais le choix
|
|
277
|
+
d'un nom. Renommer le concept reste une opération à un seul endroit — on renomme
|
|
278
|
+
la feature, l'exclusion suit.
|
|
279
|
+
|
|
270
280
|
Choisir les racines est un acte de conception, pas une formalité : l'appariement
|
|
271
281
|
se fait PAR RACINE, donc une racine trop courte attrape des mots génériques
|
|
272
282
|
(déclarer `memo` interdit fait tomber `memory`, `createInMemoryDb`…). Une racine
|