@ogygie/cli 0.16.0 → 0.18.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
CHANGED
|
@@ -15940,6 +15940,30 @@ var governedChecksumsByVersion = {
|
|
|
15940
15940
|
"scripts/verify-specs.ts": "03d58baa11822ad7ae67080f990f7a82f4f94d223b12777728b8b9ddd7ebd843",
|
|
15941
15941
|
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15942
15942
|
},
|
|
15943
|
+
"0.17.0": {
|
|
15944
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15945
|
+
"AGENTS.md": "e058a54ff4750abf19f7c4b026e8be661d8ce25419596d22c20de2c387469370",
|
|
15946
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15947
|
+
"scripts/lint-interdits.ts": "4644e1aaabc1d19ed42a0bdf8e2e55b9451f789d87d59754bce3432de7c357e7",
|
|
15948
|
+
"scripts/ogygie-design-brief.ts": "aba1585717b30808dd0bc8930a37a1ad2d83ee6869f969dbd06ca595e37a4a89",
|
|
15949
|
+
"scripts/ogygie-impact.ts": "f06cc8b4a46ddfbcfb32ba747c2454548ca49918123d04ab13d210b4a89c1fb9",
|
|
15950
|
+
"scripts/verify-contracts.ts": "9cd64c88dcc3b54b65bf1e1b7a68eb00353ee16f2a9ce8bca3c49ba063c2a08b",
|
|
15951
|
+
"scripts/verify-manifest.ts": "6c448fa3a19b87666f686ca57c6ca834d9e169b1380e4fe6e4c19b995bf94f3c",
|
|
15952
|
+
"scripts/verify-specs.ts": "03d58baa11822ad7ae67080f990f7a82f4f94d223b12777728b8b9ddd7ebd843",
|
|
15953
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15954
|
+
},
|
|
15955
|
+
"0.18.0": {
|
|
15956
|
+
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15957
|
+
"AGENTS.md": "8356696ef27c53cad164ab0086163454348b18852b121eca9fb050fb73e625f0",
|
|
15958
|
+
"CLAUDE.md": "785ee8d90e368806d8cf0b4d84a15f29d1dc961168bda86cf1a6c60a923c8c9b",
|
|
15959
|
+
"scripts/lint-interdits.ts": "4644e1aaabc1d19ed42a0bdf8e2e55b9451f789d87d59754bce3432de7c357e7",
|
|
15960
|
+
"scripts/ogygie-design-brief.ts": "aba1585717b30808dd0bc8930a37a1ad2d83ee6869f969dbd06ca595e37a4a89",
|
|
15961
|
+
"scripts/ogygie-impact.ts": "f06cc8b4a46ddfbcfb32ba747c2454548ca49918123d04ab13d210b4a89c1fb9",
|
|
15962
|
+
"scripts/verify-contracts.ts": "9cd64c88dcc3b54b65bf1e1b7a68eb00353ee16f2a9ce8bca3c49ba063c2a08b",
|
|
15963
|
+
"scripts/verify-manifest.ts": "6c448fa3a19b87666f686ca57c6ca834d9e169b1380e4fe6e4c19b995bf94f3c",
|
|
15964
|
+
"scripts/verify-specs.ts": "92d54aa5111fe0753aa9d6eb3b4ac89a7f24fa06578ea3369ccb0dc9d4c3dd88",
|
|
15965
|
+
"tsconfig.base.json": "0f8919e9ed8bbc88e0308fa2fa91471dabb18d1df721bd78c04076597eba89a2"
|
|
15966
|
+
},
|
|
15943
15967
|
"0.2.0": {
|
|
15944
15968
|
".github/workflows/ogygie-gates.yml": "48c40efe9fe66dd0f9c3b94287026d80eae9afd6782b5833fdf8846d8662babf",
|
|
15945
15969
|
"AGENTS.md": "ca73ab619d8c55fac11858d815bf5fbab6537ecebe7ae4123cbdeb3e3e36bf1a",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogygie/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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.18.0",
|
|
21
21
|
"@types/node": "^24.0.0",
|
|
22
22
|
"esbuild": "^0.28.0",
|
|
23
23
|
"tsx": "^4.20.0",
|
|
@@ -295,23 +295,37 @@ dérogation qui s'oublie. Elle ne couvre JAMAIS le code du produit lui-même.
|
|
|
295
295
|
### Stack imposée (toute autre lib = amendement DECISIONS.md d'abord)
|
|
296
296
|
Vite + React 19 + TanStack Router (routing EN CODE) + TanStack Query (état serveur) ;
|
|
297
297
|
PAS de lib d'état client (useState suffit) ; Tailwind v4 thémé depuis `design/tokens.json`
|
|
298
|
-
(valeurs arbitraires `p-[13px]` interdites par lint) ;
|
|
299
|
-
`
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
(valeurs arbitraires `p-[13px]` interdites par lint) ; le socle de patterns vient du
|
|
299
|
+
paquet `@ogygie/ui` — `ScreenState`, `EmptyState`, `ErrorBanner`, `EditableText`,
|
|
300
|
+
`fetchContract`, et derrière leur propre point d'entrée `DataTable`
|
|
301
|
+
(`@ogygie/ui/data-table`), `ContractForm` (`@ogygie/ui/contract-form`),
|
|
302
|
+
`VarianceChart` (`@ogygie/ui/variance-chart`) — IMPORTÉ, jamais recopié ; les
|
|
303
|
+
patterns PRODUIT vivent dans `apps/web/src/patterns/` (jamais consommés directement
|
|
304
|
+
depuis node_modules par une feature) ; react-hook-form UNIQUEMENT via `ContractForm` ;
|
|
305
|
+
TanStack Table UNIQUEMENT via `DataTable` ; Recharts UNIQUEMENT via `VarianceChart` ;
|
|
302
306
|
date-fns via les helpers `@ogygie/core` (jamais de `new Date()` nu) ; i18n Lingui
|
|
303
|
-
(ICU, extraction vérifiée en CI)
|
|
307
|
+
(ICU, extraction vérifiée en CI) — `@ogygie/ui` ne porte AUCUNE chaîne : tout texte
|
|
308
|
+
lui arrive en prop, traduit par la feature. Noms de patterns en anglais. La feuille
|
|
309
|
+
Tailwind du produit déclare le paquet comme source
|
|
310
|
+
(`@source "../node_modules/@ogygie/ui/src";`), sinon ses classes ne sont pas générées.
|
|
304
311
|
|
|
305
312
|
### Structure d'une feature front (miroir de l'API)
|
|
306
313
|
```
|
|
307
|
-
apps/web/features/orders/
|
|
308
|
-
├── index.ts
|
|
309
|
-
├── routes.tsx
|
|
310
|
-
├── queries.ts
|
|
311
|
-
├──
|
|
312
|
-
├──
|
|
313
|
-
|
|
314
|
+
apps/web/src/features/orders/
|
|
315
|
+
├── index.ts # seul export public
|
|
316
|
+
├── routes.tsx # fabrique createRoute de la feature, que le routeur de l'app ajoute à ses enfants
|
|
317
|
+
├── queries.ts # hooks TanStack Query de LECTURE (clés de cache dérivées du manifeste)
|
|
318
|
+
├── writes.ts # hooks de MUTATION (invalidations dérivées du manifeste)
|
|
319
|
+
├── OrdersScreen.container.tsx # le CONTENEUR : lit les hooks, déroule les états par ScreenState, passe au corps des données servies
|
|
320
|
+
├── OrdersScreen.tsx # le CORPS : reçoit data, QueryLike et MutationLike — jamais un hook ; compose les vues
|
|
321
|
+
├── views/OrdersList.tsx # une vue = un pattern (DataTable, ContractForm, VarianceChart…) sur UNE opération
|
|
322
|
+
├── views/OrderForm.tsx
|
|
323
|
+
└── OrdersScreen.test.tsx # un test par état UI de la spec : le conteneur sous un fetch simulé, ou le corps sur des QueryLike
|
|
314
324
|
```
|
|
325
|
+
Une feature générée porte les mêmes fichiers à l'infixe `.gen` près
|
|
326
|
+
(`queries.gen.ts`, `writes.gen.ts`, `OrdersScreen.container.gen.tsx`,
|
|
327
|
+
`views/OrdersList.gen.tsx`, `routes.gen.tsx`) ; son corps `OrdersScreen.tsx`
|
|
328
|
+
et ses vues `custom` restent écrits.
|
|
315
329
|
|
|
316
330
|
### Template canonique — hook de requête
|
|
317
331
|
```ts
|
|
@@ -319,7 +333,7 @@ apps/web/features/orders/
|
|
|
319
333
|
/** @intent Data access of the orders feature. Re-validates contracts in dev. */
|
|
320
334
|
import { useQuery } from "@tanstack/react-query";
|
|
321
335
|
import { ListOrdersOutputSchema } from "@product/api/features/orders/schemas";
|
|
322
|
-
import { fetchContract } from "
|
|
336
|
+
import { fetchContract } from "@ogygie/ui"; // Zod parse of the answer; an OgygieFailure on a refused call or a broken contract
|
|
323
337
|
|
|
324
338
|
export function useOrders() {
|
|
325
339
|
return useQuery({
|
|
@@ -334,18 +348,25 @@ export function useOrders() {
|
|
|
334
348
|
// apps/web/features/orders/OrdersList.tsx
|
|
335
349
|
/** @intent Orders list screen: composition of patterns, zero business logic. */
|
|
336
350
|
import { Trans } from "@lingui/react/macro";
|
|
337
|
-
import {
|
|
338
|
-
import {
|
|
339
|
-
import { ErrorBanner } from "../../patterns/ErrorBanner";
|
|
351
|
+
import { ScreenState } from "@ogygie/ui";
|
|
352
|
+
import { DataTable } from "@ogygie/ui/data-table";
|
|
340
353
|
import { useOrders } from "./queries";
|
|
341
354
|
|
|
342
355
|
export function OrdersList() {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
356
|
+
return (
|
|
357
|
+
<ScreenState
|
|
358
|
+
query={useOrders()}
|
|
359
|
+
errorTitle={<Trans>Cette vue n'a pas pu être chargée</Trans>}
|
|
360
|
+
loading={<DataTable.Skeleton />}
|
|
361
|
+
isEmpty={(data) => data.orders.length === 0}
|
|
362
|
+
emptyState={{
|
|
363
|
+
title: <Trans>Ajoutez votre première commande</Trans>,
|
|
364
|
+
explanation: <Trans>Les commandes enregistrées apparaissent ici.</Trans>,
|
|
365
|
+
}}
|
|
366
|
+
>
|
|
367
|
+
{(data) => <DataTable rows={data.orders} columns={orderColumns} rowKey={(order) => order.id} />}
|
|
368
|
+
</ScreenState>
|
|
369
|
+
);
|
|
349
370
|
}
|
|
350
371
|
```
|
|
351
372
|
|
|
@@ -353,6 +374,11 @@ export function OrdersList() {
|
|
|
353
374
|
- Un écran est une COMPOSITION de patterns : jamais de `<table>`, `<form>` ou fetch nus
|
|
354
375
|
dans une feature. Si un pattern manque, on le crée dans `patterns/` (et on met à jour
|
|
355
376
|
`design/patterns.md`), on ne bricole pas dans la feature.
|
|
377
|
+
- Un écran est un CONTENEUR et un CORPS. Le conteneur lit les hooks (route, lectures,
|
|
378
|
+
écritures) et déroule les états — accès refusé, erreur, chargement — par
|
|
379
|
+
`ScreenState`, jamais par une cascade de `if` ; le corps reçoit la lecture
|
|
380
|
+
principale SERVIE, les autres lectures en `QueryLike` et les écritures en
|
|
381
|
+
`MutationLike`, jamais un hook : il se teste sans client de requêtes ni réseau.
|
|
356
382
|
- Chaque état UI listé dans la spec (vide/chargement/nominal/erreur) a son test.
|
|
357
383
|
- Toute chaîne visible passe par Lingui (`<Trans>`/`t`) — la CI casse sinon.
|
|
358
384
|
- Chiffres financiers : `tabular-nums`, alignés à droite, unité explicite.
|
|
@@ -2,27 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
> Les patterns sont les SEULES briques d'écran autorisées (AGENTS.md §Front) :
|
|
4
4
|
> un écran est une COMPOSITION de patterns, jamais de `<table>`, `<form>` ni
|
|
5
|
-
> fetch nus.
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
>
|
|
5
|
+
> fetch nus. Les patterns GÉNÉRIQUES ci-dessous sont LIVRÉS par le paquet
|
|
6
|
+
> `@ogygie/ui` : un produit les importe, il ne les recopie pas, et leur fiche
|
|
7
|
+
> ci-dessous est leur contrat. Un pattern PRODUIT — propre au domaine — naît
|
|
8
|
+
> dans `apps/web/src/patterns/` avec sa fiche ICI d'abord, jamais en one-off
|
|
9
|
+
> dans un écran. Tout texte lu par un humain arrive à un pattern générique en
|
|
10
|
+
> prop, traduit par la feature (Lingui) : le paquet ne porte aucune chaîne.
|
|
9
11
|
>
|
|
10
12
|
> Ce catalogue est une source de vérité du brief de design
|
|
11
13
|
> (`pnpm ogygie:design-brief <feature>`) : chaque fiche `## <Name>` devient
|
|
12
14
|
> une entrée du vocabulaire de composition. Noms en anglais (machine-lisible),
|
|
13
15
|
> fiches en français (document humain).
|
|
14
16
|
|
|
17
|
+
## ScreenState
|
|
18
|
+
Le composant d'état UNIQUE d'un écran (`@ogygie/ui`). Reçoit la lecture (`QueryLike`, sous-ensemble d'un résultat TanStack Query) et le corps en fonction de rendu, et décide dans cet ordre : accès refusé (`denied` de l'appelant, ou une réponse 401/403 quand une copie `accessDenied` est donnée), erreur (`ErrorBanner`, titré par `errorTitle`), chargement (`loading`, à défaut `Skeleton`), vide (`isEmpty` + `emptyState`, un `EmptyState` avec son action), puis le corps avec des données servies. Un corps ne branche jamais sur `undefined`.
|
|
19
|
+
|
|
15
20
|
## DataTable
|
|
16
|
-
Tableau de données (TanStack Table
|
|
21
|
+
Tableau de données (`@ogygie/ui/data-table`, TanStack Table — seul usage autorisé). Colonnes typées `{ id, header, cell, sortValue?, align? }` ; tri par clic d'en-tête (`aria-sort`), chiffres `align: "end"` en `tabular-nums` alignés à droite, unité explicite dans la cellule. États : squelette de chargement (`DataTable.Skeleton`), vide DÉLÉGUÉ (`emptyState` : un `EmptyState` qui dit pourquoi), nominal trié ; l'erreur appartient à `ScreenState`. La liste de colonnes reste stable (module ou `useMemo`).
|
|
17
22
|
|
|
18
23
|
## EmptyState
|
|
19
|
-
État vide intentionnel : un titre, une explication
|
|
24
|
+
État vide intentionnel (`@ogygie/ui`) : un titre qui nomme la CAUSE, une explication qui dit ce qui le remplirait, UNE action de création au plus. Jamais de tableau ou de liste vides nus — l'écran vide est le premier écran que voit chaque nouvel utilisateur. Deux causes, deux formulations : rien d'enregistré, ou un filtre qui ne trouve rien.
|
|
20
25
|
|
|
21
26
|
## ErrorBanner
|
|
22
|
-
Bandeau d'erreur
|
|
27
|
+
Bandeau d'erreur (`@ogygie/ui`) : affiche une `OgygieFailure` en entier — le titre du produit, le message, le code stable avec le statut et la cause, la correction suggérée — et dégrade toute autre erreur à son message. `kind: "save"` marque une écriture refusée (le produit titre autrement) ; `retry` ajoute le bouton que le produit libelle. Tout état d'erreur d'écran passe par lui — jamais de message improvisé.
|
|
23
28
|
|
|
24
29
|
## ContractForm
|
|
25
|
-
Formulaire DÉRIVÉ d'un contrat Zod (react-hook-form
|
|
30
|
+
Formulaire DÉRIVÉ d'un contrat Zod (`@ogygie/ui/contract-form`, react-hook-form — seul usage autorisé) : les champs viennent de la forme du schéma, leurs contrôles de leur genre (texte, nombre, date, sélection d'enum, case à cocher), leurs contraintes des bornes du schéma — rien n'est redéclaré à la main — et le schéma valide chaque soumission (`safeParse`). Libellés, aides, libellés d'options, `submitLabel` et `cancel` sont des props traduites ; `renderIssue` traduit la phrase d'un refus. États : saisie, soumission en cours (bouton inactif, `pending`), erreur de champ (une phrase sous le champ), erreur de formulaire (un `refine`), erreur serveur (`error` : un `ErrorBanner` save au-dessus des actions). `onSubmit` reçoit la SORTIE parsée du contrat.
|
|
26
31
|
|
|
27
32
|
## VarianceChart
|
|
28
|
-
Graphique d'écart (Recharts
|
|
33
|
+
Graphique d'écart (`@ogygie/ui/variance-chart`, Recharts — seul usage autorisé) : une valeur constatée (`measured`) contre une référence (`reference`), par libellé, en deux barres groupées ; l'écart signé se lit dans l'infobulle ; une ligne zéro quand les valeurs la traversent. Couleurs tirées des tokens du produit par variables CSS, noms de séries et unité en props traduites. États : vide DÉLÉGUÉ (`emptyState`), squelette (`VarianceChart.Skeleton`) ; chargement et erreur appartiennent à `ScreenState`. Montants en `tabular-nums`, axe et unité toujours étiquetés.
|
|
34
|
+
|
|
35
|
+
## EditableText
|
|
36
|
+
Texte qui s'écrit là où il se lit (`@ogygie/ui`) : un clic le transforme en champ, quitter le champ ou Entrée enregistre, Échap abandonne. Un lecteur sans droit voit le texte nu, jamais un contrôle inactif. Un brouillon blanc est refusé sans écriture (sauf `allowEmpty`) ; une borne (`maxLength`) arrête le champ et affiche le compteur ; `pending` rend le contrôle inactif le temps d'une écriture. `placeholder` et `ariaLabel` sont traduits par la feature.
|
|
@@ -131,8 +131,8 @@ for (const fileName of specFiles) {
|
|
|
131
131
|
|
|
132
132
|
if (frontmatter.status === "draft") {
|
|
133
133
|
// What counts as an implementation is the core's rule, not this script's:
|
|
134
|
-
//
|
|
135
|
-
//
|
|
134
|
+
// the STATUS the map declares, never a folder (see hasImplementation) — a
|
|
135
|
+
// generated folder waits on its branch while the spec awaits its signature.
|
|
136
136
|
const implemented =
|
|
137
137
|
owner !== undefined && hasImplementation(owner, ownerDir !== null && existsSync(ownerDir));
|
|
138
138
|
if (implemented) {
|
|
@@ -140,9 +140,9 @@ for (const fileName of specFiles) {
|
|
|
140
140
|
code: "SPEC_DRAFT_ALREADY_IMPLEMENTED",
|
|
141
141
|
message: `Spec ${fileName} is draft but an implementation exists.`,
|
|
142
142
|
file: specPath,
|
|
143
|
-
cause: "status=draft
|
|
143
|
+
cause: "status=draft while the manifest entry is in-progress or delivered",
|
|
144
144
|
suggestedFix:
|
|
145
|
-
"Move the spec to validated/implemented, or
|
|
145
|
+
"Move the spec to validated/implemented, or declare the entry skeleton while the spec awaits its validation — a folder on a branch is a proposal, not a delivery.",
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
148
|
}
|