@ogygie/cli 0.18.0 → 0.20.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/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@ogygie/cli",
3
- "version": "0.18.0",
3
+ "version": "0.20.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
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/julienTeam/Ogygie.git",
9
+ "directory": "packages/ogygie-cli"
10
+ },
6
11
  "type": "module",
7
12
  "bin": {
8
13
  "ogygie": "dist/cli.js"
@@ -17,7 +22,7 @@
17
22
  "access": "public"
18
23
  },
19
24
  "devDependencies": {
20
- "@ogygie/core": "0.18.0",
25
+ "@ogygie/core": "0.20.0",
21
26
  "@types/node": "^24.0.0",
22
27
  "esbuild": "^0.28.0",
23
28
  "tsx": "^4.20.0",
@@ -4,11 +4,12 @@
4
4
  name: ogygie-gates
5
5
  on:
6
6
  push:
7
- branches: [main]
7
+ branches: [main, "spec/**"]
8
8
  pull_request:
9
9
 
10
10
  # Least-privilege token: gitleaks-action reads the PR's commit list on
11
11
  # pull_request events; PR comments stay disabled so no write scope is needed.
12
+ # The one job that writes (speculate) raises its own permission, alone.
12
13
  permissions:
13
14
  contents: read
14
15
  pull-requests: read
@@ -41,3 +42,126 @@ jobs:
41
42
  - uses: semgrep/semgrep-action@v1 # dangerous patterns (SAST)
42
43
  with: { config: "p/typescript p/owasp-top-ten" }
43
44
  - run: pnpm audit --audit-level=high # dependency CVEs
45
+
46
+ # Hosted mode (spec ogygie-hosted-mode B5): a pull request whose branch is
47
+ # spec/<id> is mergeable only once its spec is SIGNED (status validated).
48
+ # Require this check in the branch protection of the default branch: GitHub
49
+ # then holds the rule, never a UI. Any other branch passes and says so.
50
+ spec-signed:
51
+ if: "${{ github.event_name == 'pull_request' }}"
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: pnpm/action-setup@v4
56
+ - uses: actions/setup-node@v4
57
+ with: { node-version: 22, cache: pnpm }
58
+ - run: pnpm install --frozen-lockfile
59
+ - run: pnpm exec ogygie verify-spec-signed # reads GITHUB_HEAD_REF
60
+
61
+ # Hosted mode (spec ogygie-hosted-mode B4): on a push to spec/<id>, the
62
+ # generator derives on the branch what the spec declares, deterministically,
63
+ # and commits it there — never on the default branch — with the preview pages
64
+ # of a screen. The first schemas.ts of a NEW feature is drawn by the model and
65
+ # judged by the gates, written once; refused twice, the pull request gets the
66
+ # needs-operator label and nothing is generated. The holes are filled — library, draw, escalation — with the client's model key
67
+ # (a repo secret), the test of a candidate running without it. A repo
68
+ # without the generator installed is told so, and the branch is left as is.
69
+ # With OGYGIE_CI_TOKEN (a token allowed to push to spec/* branches), the
70
+ # commit it pushes gets its own checks; with the default token, it does not.
71
+ speculate:
72
+ if: "${{ github.event_name == 'push' && startsWith(github.ref_name, 'spec/') }}"
73
+ runs-on: ubuntu-latest
74
+ permissions:
75
+ contents: write
76
+ # The needs-operator label on the branch's pull request (labels go through the issues API).
77
+ pull-requests: write
78
+ issues: write
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+ with:
82
+ fetch-depth: 0
83
+ token: "${{ secrets.OGYGIE_CI_TOKEN || github.token }}"
84
+ - uses: pnpm/action-setup@v4
85
+ - uses: actions/setup-node@v4
86
+ with: { node-version: 22, cache: pnpm }
87
+ - run: pnpm install --frozen-lockfile
88
+ - id: generator
89
+ run: |
90
+ if node -e "require.resolve('@ogygie/generate/cli')" 2>/dev/null; then
91
+ echo "installed=true" >> "$GITHUB_OUTPUT"
92
+ else
93
+ echo "installed=false" >> "$GITHUB_OUTPUT"
94
+ echo "ogygie-gates — @ogygie/generate n'est pas installé : la branche n'est pas spéculée."
95
+ fi
96
+ - if: "${{ steps.generator.outputs.installed == 'true' }}"
97
+ env:
98
+ OGYGIE_MODEL_API_KEY: "${{ secrets.OGYGIE_MODEL_API_KEY }}"
99
+ GH_TOKEN: "${{ github.token }}"
100
+ run: |
101
+ SPEC_ID="${GITHUB_REF_NAME#spec/}"
102
+ SPEC_FILE="specs/${SPEC_ID}.spec.md"
103
+ if [ ! -f "$SPEC_FILE" ]; then
104
+ echo "ogygie-gates — $SPEC_FILE absent de la branche : rien à spéculer."
105
+ exit 0
106
+ fi
107
+ if ! grep -q '^# Génération' "$SPEC_FILE"; then
108
+ echo "ogygie-gates — $SPEC_FILE sans section # Génération : rien à générer."
109
+ exit 0
110
+ fi
111
+ # A NEW feature has no schemas.ts yet: the model writes the first one, the
112
+ # gates judge it, two attempts (exit 2 = the operator). The pull request of
113
+ # the branch carries the verdict as a label, so the studio can say it.
114
+ pnpm exec ogygie generate schemas "$SPEC_ID" || SCHEMAS_STATUS=$?
115
+ PR_NUMBER="$(gh pr list --head "$GITHUB_REF_NAME" --state open --json number --jq '.[0].number' 2>/dev/null || true)"
116
+ if [ "${SCHEMAS_STATUS:-0}" = "2" ]; then
117
+ if [ -n "$PR_NUMBER" ]; then
118
+ gh label create needs-operator --force --color D9A35A --description "Le premier schemas.ts de la feature attend l'opérateur : les gates l'ont refusé deux fois." >/dev/null
119
+ gh pr edit "$PR_NUMBER" --add-label needs-operator
120
+ fi
121
+ echo "ogygie-gates — le schemas.ts de $SPEC_ID attend l'opérateur (needs-operator) : rien n'est généré sur la branche."
122
+ exit 1
123
+ elif [ "${SCHEMAS_STATUS:-0}" != "0" ]; then
124
+ exit "$SCHEMAS_STATUS"
125
+ fi
126
+ if [ -n "$PR_NUMBER" ]; then
127
+ gh pr edit "$PR_NUMBER" --remove-label needs-operator 2>/dev/null || true
128
+ fi
129
+ pnpm exec ogygie generate check "$SPEC_ID"
130
+ pnpm exec ogygie generate generate "$SPEC_ID"
131
+ # The holes: the library first, then the client's model workspace key —
132
+ # a repo secret, present in THIS step alone; the test of a candidate
133
+ # runs in a process that holds no secret. Absent, the library alone.
134
+ pnpm exec ogygie generate fill "$SPEC_ID" || FILL_STATUS=$?
135
+ pnpm exec ogygie generate verify
136
+ if [ "${FILL_STATUS:-0}" != "0" ]; then
137
+ echo "ogygie-gates — des trous restent à l'humain, ou le plafond du modèle est atteint : voir le journal ci-dessus."
138
+ fi
139
+ # The previews of a screen (spec ogygie-hosted-mode B10): a page per
140
+ # state under previews/<id>/, committed with the derived code; not a
141
+ # screen, or a jsdom front: nothing to render, said, exit 0.
142
+ pnpm exec ogygie generate preview "$SPEC_ID"
143
+ - if: "${{ steps.generator.outputs.installed == 'true' }}"
144
+ run: |
145
+ # A commit that is no longer the head of the branch — a composition the
146
+ # model proposed before its preferred one — keeps its previews alone,
147
+ # under the composition's folder; the derived code is the head's.
148
+ HEAD_SHA="$(git ls-remote origin "refs/heads/${GITHUB_REF_NAME}" | cut -f1)"
149
+ if [ -n "$HEAD_SHA" ] && [ "$HEAD_SHA" != "$GITHUB_SHA" ]; then
150
+ git add previews 2>/dev/null || true
151
+ MESSAGE="speculate: ${GITHUB_REF_NAME#spec/} — aperçus d'une composition proposée (${GITHUB_SHA:0:7})"
152
+ else
153
+ git add -A
154
+ MESSAGE="speculate: ${GITHUB_REF_NAME#spec/} — dérivé par ogygie generate sur la branche"
155
+ fi
156
+ if git diff --cached --quiet; then
157
+ echo "ogygie-gates — rien de nouveau à committer sur la branche."
158
+ exit 0
159
+ fi
160
+ git config user.name "ogygie-speculate"
161
+ git config user.email "ogygie-speculate@users.noreply.github.com"
162
+ git commit -m "$MESSAGE"
163
+ # The branch may have moved since this commit was checked out: replay on its head.
164
+ git reset --hard HEAD --quiet
165
+ git clean -fdq
166
+ git pull --rebase origin "$GITHUB_REF_NAME"
167
+ git push origin "HEAD:${GITHUB_REF_NAME}"
@@ -1,32 +1,112 @@
1
1
  {
2
- "$intent": "Placeholder design tokens of the starter. A product REWRITES these values when it opens (AGENTS.product.md); the structure stays. The design tool composes ONLY from these tokens — a missing color or spacing is an amendment of this file (explicit decision), never a hardcoded value (AGENTS.md §Front).",
2
+ "$intent": "Design tokens of the starter: the Ogygie charter, two themes (dark first, the identity; light, a complete working theme). A product REWRITES these values when it opens (AGENTS.product.md); the structure stays. A colour or a shadow is either ONE value for every theme or one value per theme id of `themes`, the first theme being the default. The design tool composes ONLY from these tokens: a missing colour, spacing or motion value is an amendment of this file (explicit decision), never a hardcoded value (AGENTS.md §Front). The socle `@ogygie/ui` only names tokens (surface, border, text, accent, danger, focus...): every name it uses must stay declared here.",
3
+ "themes": [
4
+ "dark",
5
+ "light"
6
+ ],
3
7
  "color": {
4
- "surface": "#f6f6f4",
5
- "surface-raised": "#ffffff",
6
- "border": "#e2e1dc",
7
- "text": "#22241f",
8
- "text-muted": "#5f6259",
9
- "accent": "#3b5b92",
10
- "accent-contrast": "#ffffff",
11
- "positive": "#2f6b4f",
12
- "warning": "#a96a1f",
13
- "danger": "#b3423a",
14
- "focus": "#7aa2ff"
8
+ "surface": {
9
+ "dark": "#0b1322",
10
+ "light": "#f6f3ec"
11
+ },
12
+ "surface-raised": {
13
+ "dark": "#131d30",
14
+ "light": "#ffffff"
15
+ },
16
+ "surface-sunken": {
17
+ "dark": "#060b15",
18
+ "light": "#ece8de"
19
+ },
20
+ "border": {
21
+ "dark": "#223049",
22
+ "light": "#ddd7ca"
23
+ },
24
+ "border-strong": {
25
+ "dark": "#5a6d93",
26
+ "light": "#8b8571"
27
+ },
28
+ "text": {
29
+ "dark": "#ebe6da",
30
+ "light": "#121a2a"
31
+ },
32
+ "text-muted": {
33
+ "dark": "#a2abba",
34
+ "light": "#5a6475"
35
+ },
36
+ "text-faint": {
37
+ "dark": "#7b8694",
38
+ "light": "#667080"
39
+ },
40
+ "accent": {
41
+ "dark": "#5cd6cc",
42
+ "light": "#0f7580"
43
+ },
44
+ "accent-strong": {
45
+ "dark": "#86e6de",
46
+ "light": "#0a5c66"
47
+ },
48
+ "accent-soft": {
49
+ "dark": "#0e2b31",
50
+ "light": "#dbf2f3"
51
+ },
52
+ "accent-contrast": {
53
+ "dark": "#0b1322",
54
+ "light": "#f3fbfb"
55
+ },
56
+ "accent-glow": {
57
+ "dark": "#5cd6cc29",
58
+ "light": "#0f758024"
59
+ },
60
+ "orichalcum": {
61
+ "dark": "#d9a35a",
62
+ "light": "#9e5420"
63
+ },
64
+ "positive": {
65
+ "dark": "#8fd67a",
66
+ "light": "#2e7a1f"
67
+ },
68
+ "positive-soft": {
69
+ "dark": "#152a16",
70
+ "light": "#e2f3dc"
71
+ },
72
+ "warning": {
73
+ "dark": "#f2d06b",
74
+ "light": "#8a6400"
75
+ },
76
+ "warning-soft": {
77
+ "dark": "#2e2410",
78
+ "light": "#fbeccc"
79
+ },
80
+ "danger": {
81
+ "dark": "#ff8a7a",
82
+ "light": "#b42318"
83
+ },
84
+ "danger-soft": {
85
+ "dark": "#3a1612",
86
+ "light": "#fbe3df"
87
+ },
88
+ "focus": {
89
+ "dark": "#5cd6cc",
90
+ "light": "#0f7580"
91
+ }
15
92
  },
16
93
  "typography": {
17
- "font-sans": "system-ui, -apple-system, 'Segoe UI', sans-serif",
18
- "font-mono": "ui-monospace, 'SF Mono', 'Cascadia Mono', monospace",
94
+ "font-display": "'Cormorant Garamond', 'Iowan Old Style', Georgia, serif",
95
+ "font-sans": "'Hanken Grotesk', 'Helvetica Neue', Arial, sans-serif",
96
+ "font-mono": "'Fragment Mono', ui-monospace, 'SF Mono', Menlo, monospace",
19
97
  "size": {
20
98
  "xs": "0.75rem",
21
- "sm": "0.875rem",
22
- "base": "1rem",
99
+ "sm": "0.8125rem",
100
+ "base": "0.9375rem",
23
101
  "lg": "1.125rem",
24
- "xl": "1.375rem",
25
- "2xl": "1.75rem"
102
+ "xl": "1.25rem",
103
+ "2xl": "1.75rem",
104
+ "3xl": "3rem",
105
+ "4xl": "4.5rem"
26
106
  },
27
107
  "leading": {
28
- "tight": "1.25",
29
- "normal": "1.5"
108
+ "tight": "1.2",
109
+ "normal": "1.6"
30
110
  }
31
111
  },
32
112
  "spacing": {
@@ -36,15 +116,42 @@
36
116
  "4": "1rem",
37
117
  "6": "1.5rem",
38
118
  "8": "2rem",
39
- "12": "3rem"
119
+ "12": "3rem",
120
+ "16": "4rem",
121
+ "24": "6rem"
40
122
  },
41
123
  "radius": {
124
+ "xs": "0.125rem",
42
125
  "sm": "0.25rem",
43
126
  "md": "0.5rem",
44
127
  "lg": "0.75rem",
128
+ "xl": "1.25rem",
45
129
  "full": "9999px"
46
130
  },
47
131
  "shadow": {
48
- "raised": "0 1px 2px rgb(0 0 0 / 0.06), 0 2px 8px rgb(0 0 0 / 0.04)"
132
+ "raised": {
133
+ "dark": "inset 0 1px 0 rgb(255 255 255 / 0.05), 0 8px 24px rgb(0 0 0 / 0.4)",
134
+ "light": "0 1px 2px rgb(18 26 42 / 0.08), 0 8px 24px rgb(18 26 42 / 0.08)"
135
+ },
136
+ "overlay": {
137
+ "dark": "inset 0 1px 0 rgb(255 255 255 / 0.08), 0 24px 64px rgb(0 0 0 / 0.65)",
138
+ "light": "0 2px 4px rgb(18 26 42 / 0.08), 0 24px 64px rgb(18 26 42 / 0.16)"
139
+ },
140
+ "glow": {
141
+ "dark": "0 0 0 1px rgb(92 214 204 / 0.4), 0 0 48px rgb(92 214 204 / 0.2)",
142
+ "light": "0 0 0 1px rgb(15 117 128 / 0.4), 0 0 48px rgb(15 117 128 / 0.14)"
143
+ }
144
+ },
145
+ "motion": {
146
+ "ease": {
147
+ "swell": "cubic-bezier(0.2, 0.7, 0.2, 1)",
148
+ "retreat": "cubic-bezier(0.4, 0, 0.2, 1)"
149
+ },
150
+ "duration": {
151
+ "fast": "160ms",
152
+ "base": "320ms",
153
+ "slow": "640ms",
154
+ "count": "800ms"
155
+ }
49
156
  }
50
157
  }
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "@ogygie/cli": "__OGYGIE_VERSION__",
26
+ "@ogygie/generate": "__OGYGIE_VERSION__",
26
27
  "@types/node": "^24.0.0",
27
28
  "tsx": "^4.20.0",
28
29
  "typescript": "^5.8.0",