@ogygie/studio 0.29.1 → 0.30.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/server.js
CHANGED
|
@@ -1397,12 +1397,22 @@ async function readParsedSpec(deps, specId) {
|
|
|
1397
1397
|
}
|
|
1398
1398
|
return parsed.spec;
|
|
1399
1399
|
}
|
|
1400
|
+
async function generatedProductOf(deps) {
|
|
1401
|
+
if (!await deps.storage.exists("ogygie.manifest.json")) return false;
|
|
1402
|
+
try {
|
|
1403
|
+
const raw = JSON.parse(await deps.storage.readFile("ogygie.manifest.json"));
|
|
1404
|
+
return typeof raw === "object" && raw !== null && "generation" in raw && raw.generation !== null;
|
|
1405
|
+
} catch {
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1400
1409
|
async function getSpec(deps, specId) {
|
|
1401
1410
|
const spec = await readParsedSpec(deps, specId);
|
|
1402
1411
|
return {
|
|
1403
1412
|
frontmatter: spec.frontmatter,
|
|
1404
1413
|
body: spec.body,
|
|
1405
|
-
presentSections: presentSectionsOf(spec.body)
|
|
1414
|
+
presentSections: presentSectionsOf(spec.body),
|
|
1415
|
+
generatedProduct: await generatedProductOf(deps)
|
|
1406
1416
|
};
|
|
1407
1417
|
}
|
|
1408
1418
|
async function saveSpec(deps, specId, input) {
|
|
@@ -1931,7 +1941,9 @@ var GetSpecOutputSchema = z7.object({
|
|
|
1931
1941
|
frontmatter: SpecFrontmatterSchema,
|
|
1932
1942
|
/** Markdown body after the frontmatter block, byte-exact. */
|
|
1933
1943
|
body: z7.string(),
|
|
1934
|
-
presentSections: z7.array(z7.string())
|
|
1944
|
+
presentSections: z7.array(z7.string()),
|
|
1945
|
+
/** D58: the product's map declares a `generation` block — a spec without `# Génération` is then hand-written, nothing derives from it. */
|
|
1946
|
+
generatedProduct: z7.boolean().default(false)
|
|
1935
1947
|
});
|
|
1936
1948
|
var UpdateSpecInputSchema = z7.object({
|
|
1937
1949
|
frontmatter: SpecFrontmatterSchema,
|
|
@@ -2715,6 +2727,11 @@ var BRIEF_IN_MESSAGE_MAX = 2e3;
|
|
|
2715
2727
|
var MAX_QUESTION_ROUNDS = 3;
|
|
2716
2728
|
var QUESTIONS_MARKER = /^\s*QUESTIONS\s*$/mu;
|
|
2717
2729
|
var NO_MORE_QUESTIONS = "no more questions: write the draft now, and put what you do not know under # Hypoth\xE8ses";
|
|
2730
|
+
var UNGENERATED_ASKED = /sans\s+g[ée]n[ée]ration/iu;
|
|
2731
|
+
var BLOCK_REQUIRED = "this product is generated (D58): write the `# G\xE9n\xE9ration` block in the closed grammar of the card \u2014 a spec without it would be implemented by nobody; only a brief saying \xAB sans g\xE9n\xE9ration \xBB may omit it";
|
|
2732
|
+
function asksUngenerated(brief) {
|
|
2733
|
+
return UNGENERATED_ASKED.test(brief);
|
|
2734
|
+
}
|
|
2718
2735
|
async function readIfPresent(storage, path) {
|
|
2719
2736
|
return await storage.exists(path) ? storage.readFile(path) : null;
|
|
2720
2737
|
}
|
|
@@ -2814,7 +2831,9 @@ function buildBriefPrompt(context) {
|
|
|
2814
2831
|
"- Tu ne cites une d\xE9cision `D<n>` QUE si elle figure dans la liste des d\xE9cisions connues ci-dessous ; sinon n'en cite aucune.",
|
|
2815
2832
|
"- Tu n'inventes ni table, ni route, ni feature existante : tu t'appuies sur la carte du produit ci-dessous ; une rubrique nouvelle nomme ses propres contrats.",
|
|
2816
2833
|
"- Tu n'\xE9cris jamais de code ni de design (couleurs, composants) : la spec dit QUOI, jamais COMMENT. Le bloc YAML de `# G\xE9n\xE9ration` n'est pas du code : c'est la d\xE9claration que le g\xE9n\xE9rateur lit.",
|
|
2817
|
-
...context.grammarCard === null || context.grammarCard === void 0 ? [] : [
|
|
2834
|
+
...context.grammarCard === null || context.grammarCard === void 0 ? [] : [
|
|
2835
|
+
context.ungenerated === true ? "- Ce produit est g\xE9n\xE9r\xE9, mais le brief demande une rubrique SANS g\xE9n\xE9ration : n'\xE9cris pas la section `# G\xE9n\xE9ration`, cette spec sera impl\xE9ment\xE9e \xE0 la main." : "- Ce produit est G\xC9N\xC9R\xC9 (D58) : \xE9cris TOUJOURS la section `# G\xE9n\xE9ration` apr\xE8s `# @intent`, et ses `# Comportements` dans la grammaire ferm\xE9e (carte ci-dessous) ; le brouillon est jug\xE9 par le v\xE9rificateur du g\xE9n\xE9rateur avant d'\xEAtre montr\xE9, et un brouillon sans cette section est refus\xE9. Routes, base, tests et \xE9crans se d\xE9rivent du bloc ; ce qui se pense devient un trou typ\xE9."
|
|
2836
|
+
],
|
|
2818
2837
|
"- Tu ne proposes jamais `status: validated` ni `implemented` : valider est le geste de l'humain.",
|
|
2819
2838
|
"- HYPOTH\xC8SES : quand tu tranches un point que le brief ne dit pas (un nombre, un ordre, un p\xE9rim\xE8tre, un mot), ajoute apr\xE8s `# Budget` (et avant `# Design` si elle existe) une section `# Hypoth\xE8ses`, une puce par choix, en fran\xE7ais, courte ; sans hypoth\xE8se, pas de section.",
|
|
2820
2839
|
'- QUESTIONS : quand la demande ci-dessous t\'y autorise et qu\'une r\xE9ponse du client CHANGERAIT la spec, tu peux, AU LIEU du fichier, r\xE9pondre par une ligne `QUESTIONS` seule puis un objet JSON `{"questions":[{"id":"kebab-case","text":"la question, en fran\xE7ais","options":["r\xE9ponse","r\xE9ponse"]}]}` \u2014 trois \xE0 cinq questions ferm\xE9es, deux \xE0 quatre options chacune, rien d\'autre avant ni apr\xE8s. Une question dont la r\xE9ponse ne changerait pas la spec ne se pose pas : elle devient une hypoth\xE8se.',
|
|
@@ -2966,6 +2985,8 @@ async function runBrief(deps, input) {
|
|
|
2966
2985
|
const existingSpec = input.specId !== void 0 ? await readIfPresent(deps.storage, `specs/${input.specId}.spec.md`) : null;
|
|
2967
2986
|
const isAmendment = existingSpec !== null;
|
|
2968
2987
|
const round = Math.min(Math.max(input.round ?? 1, 1), MAX_QUESTION_ROUNDS + 1);
|
|
2988
|
+
const ungeneratedAsked = asksUngenerated(input.brief);
|
|
2989
|
+
const blockRequired = manifest.generation !== null && !ungeneratedAsked;
|
|
2969
2990
|
const questionsAllowed = input.skipQuestions !== true && round <= MAX_QUESTION_ROUNDS;
|
|
2970
2991
|
const prompt = buildBriefPrompt({
|
|
2971
2992
|
template,
|
|
@@ -2979,7 +3000,8 @@ async function runBrief(deps, input) {
|
|
|
2979
3000
|
brief: input.brief,
|
|
2980
3001
|
attachments: input.attachments,
|
|
2981
3002
|
answers: input.answers,
|
|
2982
|
-
questions: { allowed: questionsAllowed, round }
|
|
3003
|
+
questions: { allowed: questionsAllowed, round },
|
|
3004
|
+
ungenerated: ungeneratedAsked
|
|
2983
3005
|
});
|
|
2984
3006
|
const usage = [];
|
|
2985
3007
|
let refusedQuestions = false;
|
|
@@ -3021,7 +3043,8 @@ async function runBrief(deps, input) {
|
|
|
3021
3043
|
});
|
|
3022
3044
|
const generationJudgement = judged.problems.length === 0 && judged.specId !== null ? await judgeGeneration({ content, specPath: `specs/${judged.specId}.spec.md`, generation: manifest.generation, storage: deps.storage, features: manifest.features }) : { problems: [], tally: null };
|
|
3023
3045
|
judged.problems.push(...generationJudgement.problems);
|
|
3024
|
-
if (generationJudgement.tally
|
|
3046
|
+
if (blockRequired && judged.problems.length === 0 && generationJudgement.tally === null) judged.problems.push(BLOCK_REQUIRED);
|
|
3047
|
+
if (generationJudgement.tally !== null || blockRequired) maxAttempts = MAX_ATTEMPTS_GENERATED;
|
|
3025
3048
|
deps.log(JSON.stringify({ action: "brief", attempt, specId: judged.specId, problems: judged.problems.length, generated: generationJudgement.tally !== null, ...answer.usage }));
|
|
3026
3049
|
if (judged.problems.length === 0 && judged.specId !== null) {
|
|
3027
3050
|
const specId = judged.specId;
|