@bleedingdev/modern-js-create 3.2.0-ultramodern.58 → 3.2.0-ultramodern.59
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/README.md +30 -39
- package/dist/index.js +670 -702
- package/dist/types/locale/en.d.ts +1 -1
- package/dist/types/locale/zh.d.ts +1 -1
- package/dist/types/ultramodern-workspace.d.ts +2 -4
- package/package.json +1 -1
- package/template/.codex/hooks.json +16 -0
- package/template/AGENTS.md +4 -8
- package/template/README.md +40 -43
- package/template/lefthook.yml +15 -0
- package/template/package.json.handlebars +5 -15
- package/template/pnpm-workspace.yaml +1 -1
- package/template/scripts/bootstrap-agent-skills.mjs +39 -7
- package/template/scripts/validate-ultramodern.mjs.handlebars +18 -8
- package/template/src/routes/[lang]/page.tsx.handlebars +1 -1
- package/template-workspace/.codex/hooks.json +16 -0
- package/template-workspace/AGENTS.md +4 -2
- package/template-workspace/README.md.handlebars +10 -11
- package/template-workspace/lefthook.yml +15 -0
- package/template-workspace/pnpm-workspace.yaml +2 -3
- package/template-workspace/scripts/assert-mf-types.mjs +3 -3
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +6 -2
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +106 -103
|
@@ -35,7 +35,7 @@ export declare const EN_LOCALE: {
|
|
|
35
35
|
optionUltramodernPackageSource: string;
|
|
36
36
|
optionUltramodernPackageScope: string;
|
|
37
37
|
optionUltramodernPackageNamePrefix: string;
|
|
38
|
-
|
|
38
|
+
optionVertical: string;
|
|
39
39
|
optionSub: string;
|
|
40
40
|
examples: string;
|
|
41
41
|
example1: string;
|
|
@@ -35,7 +35,7 @@ export declare const ZH_LOCALE: {
|
|
|
35
35
|
optionUltramodernPackageSource: string;
|
|
36
36
|
optionUltramodernPackageScope: string;
|
|
37
37
|
optionUltramodernPackageNamePrefix: string;
|
|
38
|
-
|
|
38
|
+
optionVertical: string;
|
|
39
39
|
optionSub: string;
|
|
40
40
|
examples: string;
|
|
41
41
|
example1: string;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export type MicroVerticalKind = 'remote' | 'horizontal-remote' | 'service' | 'shared';
|
|
2
1
|
type UltramodernPackageSourceStrategy = 'workspace' | 'install';
|
|
3
2
|
export type UltramodernWorkspaceOptions = {
|
|
4
3
|
targetDir: string;
|
|
@@ -13,16 +12,15 @@ export type UltramodernWorkspaceOptions = {
|
|
|
13
12
|
aliasPackageNamePrefix?: string;
|
|
14
13
|
};
|
|
15
14
|
};
|
|
16
|
-
export type
|
|
15
|
+
export type AddUltramodernVerticalOptions = {
|
|
17
16
|
workspaceRoot: string;
|
|
18
17
|
name: string;
|
|
19
|
-
kind: MicroVerticalKind;
|
|
20
18
|
modernVersion: string;
|
|
21
19
|
enableTailwind?: boolean;
|
|
22
20
|
packageSource?: UltramodernWorkspaceOptions['packageSource'];
|
|
23
21
|
};
|
|
24
22
|
export declare const ULTRAMODERN_WORKSPACE_FLAG = "--ultramodern-workspace";
|
|
25
|
-
export declare function
|
|
23
|
+
export declare function addUltramodernVertical(options: AddUltramodernVerticalOptions): void;
|
|
26
24
|
export declare function generateUltramodernWorkspace(options: UltramodernWorkspaceOptions): void;
|
|
27
25
|
export declare const ultramodernWorkspaceVersions: {
|
|
28
26
|
tanstackRouter: string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Stop": [
|
|
3
|
+
{
|
|
4
|
+
"command": "pnpm format && pnpm lint:fix && pnpm ultramodern:check",
|
|
5
|
+
"timeout": 600000,
|
|
6
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"SubagentStop": [
|
|
10
|
+
{
|
|
11
|
+
"command": "pnpm format && pnpm lint:fix && pnpm ultramodern:check",
|
|
12
|
+
"timeout": 600000,
|
|
13
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
package/template/AGENTS.md
CHANGED
|
@@ -8,7 +8,9 @@ This project is generated for Codex-first UltraModern.js work.
|
|
|
8
8
|
- `pnpm format` runs oxfmt.
|
|
9
9
|
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
10
10
|
- `pnpm i18n:check` rejects hardcoded user-visible JSX text.
|
|
11
|
-
- `
|
|
11
|
+
- `pnpm ultramodern:check` verifies the generated contract.
|
|
12
|
+
- Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm ultramodern:check`.
|
|
13
|
+
- `postinstall` installs `lefthook` when the app is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format`, `pnpm lint:fix`, and `pnpm ultramodern:check` on pre-commit; pre-push runs `pnpm ultramodern:check`.
|
|
12
14
|
|
|
13
15
|
## Internationalization
|
|
14
16
|
|
|
@@ -18,10 +20,4 @@ Routes are locale-prefixed by default through `localePathRedirect: true`. Keep l
|
|
|
18
20
|
|
|
19
21
|
## Private Skills
|
|
20
22
|
|
|
21
|
-
Private orchestration skills are
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
pnpm skills:install
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
The installer clones that private repository and copies only the allowlisted skills from `.agents/skills-lock.json`.
|
|
23
|
+
Private orchestration skills are installed automatically during `pnpm install` when the current developer is authorized for `TechsioCZ/skills`. The installer clones that private repository and copies only the allowlisted skills from `.agents/skills-lock.json`; unauthorized developers get a warning and can continue with the public contract.
|
package/template/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# UltraModern.js 3.0 Starter
|
|
2
2
|
|
|
3
3
|
This generated workspace is the Tractor reference SuperApp. It is built around
|
|
4
|
-
one shell and three full-stack
|
|
4
|
+
one shell and three full-stack Verticals:
|
|
5
5
|
|
|
6
6
|
| Package | Boundary |
|
|
7
7
|
| --- | --- |
|
|
8
8
|
| `apps/shell-super-app` | Module Federation host, route assembly, topology selection, shell fallback policy |
|
|
9
|
-
| `
|
|
10
|
-
| `
|
|
11
|
-
| `
|
|
9
|
+
| `verticals/explore` | Explore routes, MF exposes, locale JSON, CSS layer, and `/explore-api/effect/explore/*` |
|
|
10
|
+
| `verticals/decide` | Decide routes, MF exposes, locale JSON, CSS layer, and `/decide-api/effect/decide/*` |
|
|
11
|
+
| `verticals/checkout` | Checkout routes, MF exposes, locale JSON, CSS layer, and `/checkout-api/effect/checkout/*` |
|
|
12
12
|
|
|
13
13
|
Each vertical is a one-package ownership boundary. Its UI, Effect BFF contract,
|
|
14
14
|
generated client, `localisedUrls`, dynamic locale JSON, CSS, MF manifest, and
|
|
@@ -20,7 +20,7 @@ Install the dependencies:
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
mise install
|
|
23
|
-
|
|
23
|
+
pnpm install
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Get Started
|
|
@@ -28,27 +28,27 @@ mise exec -- pnpm install
|
|
|
28
28
|
Start the dev server:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
|
|
31
|
+
pnpm dev
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Build the app for production:
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
|
|
37
|
+
pnpm build
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Validate the generated Ultramodern preset contract locally:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
pnpm run ultramodern:check
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Run the normal local gates before treating the scaffold as adoption-ready:
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
pnpm run check
|
|
50
|
+
pnpm run build
|
|
51
|
+
pnpm run cloudflare:build
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
The generated preset defaults are opt-out. Disable specific contracts via env vars:
|
|
@@ -61,31 +61,29 @@ MODERN_BASELINE_ENABLE_TELEMETRY_EXPORTERS=false
|
|
|
61
61
|
|
|
62
62
|
The generated starter also includes `.github/workflows/ultramodern-gates.yml`
|
|
63
63
|
and `.github/renovate.json`. The workflow runs
|
|
64
|
-
`
|
|
64
|
+
`pnpm run ultramodern:check` and `pnpm run build` on
|
|
65
65
|
every push and pull request with read-only permissions, commit-pinned actions,
|
|
66
66
|
frozen installs, and StepSecurity audit-mode runner hardening. Renovate is
|
|
67
67
|
configured for dependency dashboard review, one-day release age, grouped
|
|
68
68
|
updates, action digest pinning, and manual approval for major upgrades.
|
|
69
69
|
|
|
70
|
-
##
|
|
70
|
+
## Vertical Workspaces
|
|
71
71
|
|
|
72
|
-
Inside
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
Inside an UltraModern workspace, add a full-stack vertical from the workspace
|
|
73
|
+
root. The add flow derives paths, package names, ports, Module Federation names,
|
|
74
|
+
topology entries, overlays, ownership, Effect BFF contracts, and root dev
|
|
75
|
+
scripts:
|
|
75
76
|
|
|
76
77
|
```bash
|
|
77
|
-
|
|
78
|
-
mise exec -- pnpm dlx @modern-js/create design-system --microvertical horizontal-remote
|
|
79
|
-
mise exec -- pnpm dlx @modern-js/create catalog-api --microvertical service
|
|
80
|
-
mise exec -- pnpm dlx @modern-js/create catalog-contracts --microvertical shared
|
|
78
|
+
pnpm dlx @modern-js/create catalog --vertical
|
|
81
79
|
```
|
|
82
80
|
|
|
83
81
|
The canonical topology is documented in
|
|
84
82
|
`docs/super-app-rfc-adr/WORKSPACE-0001-micro-vertical-workspace-scaffolding.md`.
|
|
85
|
-
Shell packages own route assembly and topology selection
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
clients, or domain-neutral utilities.
|
|
83
|
+
Shell packages own route assembly and topology selection. Verticals own route
|
|
84
|
+
subtrees, degraded UI, Effect BFF contracts, generated clients, and deployment
|
|
85
|
+
evidence. Ordinary workspace packages are limited to tokens, primitives,
|
|
86
|
+
generated clients, or domain-neutral utilities.
|
|
89
87
|
|
|
90
88
|
Use a new vertical only when the feature needs its own route subtree owner,
|
|
91
89
|
independent rollout, rollback, Cloudflare/Zephyr evidence, or incident routing.
|
|
@@ -93,23 +91,22 @@ Keep code inside an existing vertical when it shares the same product owner,
|
|
|
93
91
|
fallback behavior, release train, and Effect contract. Do not use a shared
|
|
94
92
|
package for feature composites or workflow state.
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
Do not add separate service or shared "vertical types". A vertical is the
|
|
95
|
+
full-stack ownership unit. Supporting packages are implementation details and
|
|
96
|
+
should stay ordinary workspace packages unless a real vertical owns the product
|
|
97
|
+
behavior.
|
|
100
98
|
|
|
101
99
|
The public opinionated entrypoint is `presetUltramodern(...)`. It is the default
|
|
102
|
-
UltraModern.js SuperApp surface for Effect, TanStack, SSR, BFF, and
|
|
103
|
-
Verticals.
|
|
100
|
+
UltraModern.js SuperApp surface for Effect, TanStack, SSR, BFF, and Verticals.
|
|
104
101
|
|
|
105
102
|
## Module Federation And Effect Ownership
|
|
106
103
|
|
|
107
104
|
The shell consumes vertical UI through MF manifests and consumes vertical data
|
|
108
|
-
through generated Effect clients exported by the
|
|
105
|
+
through generated Effect clients exported by the vertical packages:
|
|
109
106
|
|
|
110
107
|
- Explore exposes header, footer, recommendations, store picker, and route UI.
|
|
111
108
|
- Decide exposes product page and route UI, and may consume Explore or Checkout
|
|
112
|
-
|
|
109
|
+
verticals through declared `verticalRefs`.
|
|
113
110
|
- Checkout exposes cart, checkout, thanks, mini-cart, add-to-cart, and route UI.
|
|
114
111
|
|
|
115
112
|
The shell owns orchestration and fallback policy. A vertical owns its route-local
|
|
@@ -123,14 +120,14 @@ Route localization is owned by the package that owns the route. Each app emits
|
|
|
123
120
|
`src/routes/ultramodern-route-metadata` and passes
|
|
124
121
|
`ultramodernLocalisedUrls` to `@modern-js/plugin-i18n`. Dynamic JSON resources
|
|
125
122
|
are served from `/locales/{{lng}}/{{ns}}.json`; shell rewrites must not replace
|
|
126
|
-
|
|
123
|
+
vertical-owned localized URL maps.
|
|
127
124
|
|
|
128
125
|
CSS federation is recorded in `.modernjs/ultramodern-generated-contract.json`:
|
|
129
126
|
|
|
130
127
|
- `packages/shared-design-tokens` owns `ultramodern-shared-tokens` and exports
|
|
131
128
|
`./tokens.css`.
|
|
132
129
|
- The shell owns shell base and overlay CSS under its app root marker.
|
|
133
|
-
- Each
|
|
130
|
+
- Each vertical owns one vertical CSS layer and one vertical app root marker.
|
|
134
131
|
- Tailwind CSS v4 is app-local through `@tailwindcss/postcss`.
|
|
135
132
|
- Duplicate base styles are forbidden. SSR first paint requires shared tokens
|
|
136
133
|
and Modern/Rspack-emitted app CSS.
|
|
@@ -140,28 +137,28 @@ CSS federation is recorded in `.modernjs/ultramodern-generated-contract.json`:
|
|
|
140
137
|
Build all Cloudflare Worker artifacts:
|
|
141
138
|
|
|
142
139
|
```bash
|
|
143
|
-
MODERN_PUBLIC_SITE_URL=https://shell-super-app.example.test
|
|
140
|
+
MODERN_PUBLIC_SITE_URL=https://shell-super-app.example.test pnpm run cloudflare:build
|
|
144
141
|
```
|
|
145
142
|
|
|
146
143
|
Validate local or deployed Cloudflare output with the repo harness:
|
|
147
144
|
|
|
148
145
|
```bash
|
|
149
146
|
node scripts/ultramodern-cloudflare-ssr-validation/validate-cloudflare-ssr.js \
|
|
150
|
-
--root-dir
|
|
147
|
+
--root-dir verticals/checkout \
|
|
151
148
|
--bff /checkout-api/effect/checkout/readiness \
|
|
152
|
-
--expect-en "Checkout
|
|
149
|
+
--expect-en "Checkout Vertical" \
|
|
153
150
|
--match-build-marker \
|
|
154
|
-
--out .codex/reports/cloudflare-ssr/
|
|
151
|
+
--out .codex/reports/cloudflare-ssr/checkout-local.json
|
|
155
152
|
```
|
|
156
153
|
|
|
157
154
|
After deploying public Workers, run the generated public URL proof:
|
|
158
155
|
|
|
159
156
|
```bash
|
|
160
157
|
ULTRAMODERN_PUBLIC_URL_SHELL_SUPER_APP=https://shell-super-app.example.workers.dev \
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
ULTRAMODERN_PUBLIC_URL_EXPLORE=https://explore.example.workers.dev \
|
|
159
|
+
ULTRAMODERN_PUBLIC_URL_DECIDE=https://decide.example.workers.dev \
|
|
160
|
+
ULTRAMODERN_PUBLIC_URL_CHECKOUT=https://checkout.example.workers.dev \
|
|
161
|
+
pnpm run cloudflare:proof -- --require-public-urls
|
|
165
162
|
```
|
|
166
163
|
|
|
167
164
|
Zephyr upload and live switching proof are opt-in evidence workflows. The
|
|
@@ -172,7 +169,7 @@ credentials. Without those values, only dry-run Zephyr evidence can be claimed.
|
|
|
172
169
|
Preview the production build locally:
|
|
173
170
|
|
|
174
171
|
```bash
|
|
175
|
-
|
|
172
|
+
pnpm serve
|
|
176
173
|
```
|
|
177
174
|
|
|
178
175
|
For more information, see the
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
{{#unless isSubproject}}
|
|
16
16
|
"skills:install": "node ./scripts/bootstrap-agent-skills.mjs",
|
|
17
17
|
"skills:check": "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
18
|
+
"postinstall": "node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)",
|
|
18
19
|
{{/unless}}
|
|
19
20
|
"ultramodern:check": "{{#unless isSubproject}}pnpm format:check && pnpm lint && {{/unless}}pnpm typecheck && pnpm i18n:check && pnpm test{{#unless isSubproject}} && pnpm skills:check{{/unless}} && node ./scripts/validate-ultramodern.mjs"{{#unless isSubproject}},
|
|
20
21
|
"format": "oxfmt .",
|
|
21
22
|
"format:check": "oxfmt --check .",
|
|
22
23
|
"lint": "oxlint .",
|
|
23
|
-
"lint:fix": "oxlint . --fix"
|
|
24
|
-
"prepare": "simple-git-hooks"{{/unless}}
|
|
24
|
+
"lint:fix": "oxlint . --fix"{{/unless}}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modern-js/plugin-i18n": "{{pluginI18nVersion}}",
|
|
@@ -51,26 +51,16 @@
|
|
|
51
51
|
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
52
52
|
"happy-dom": "^20.9.0",
|
|
53
53
|
{{#unless isSubproject}}
|
|
54
|
-
"
|
|
54
|
+
"lefthook": "^2.1.9",
|
|
55
55
|
"oxfmt": "0.51.0",
|
|
56
56
|
"oxlint": "1.66.0",
|
|
57
57
|
{{/unless}}{{#if enableTailwind}} "postcss": "^8.5.6",
|
|
58
|
-
{{/if}} "rimraf": "^6.1.3"{{#
|
|
59
|
-
"simple-git-hooks": "^2.13.1"{{/unless}}{{#if enableTailwind}},
|
|
58
|
+
{{/if}} "rimraf": "^6.1.3"{{#if enableTailwind}},
|
|
60
59
|
"tailwindcss": "^{{tailwindVersion}}"{{/if}}{{#unless isSubproject}},
|
|
61
60
|
"ultracite": "7.7.0"{{/unless}}
|
|
62
|
-
}{{#unless isSubproject}},
|
|
63
|
-
"simple-git-hooks": {
|
|
64
|
-
"pre-commit": "npx lint-staged"
|
|
65
61
|
},
|
|
66
|
-
"lint-staged": {
|
|
67
|
-
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
|
68
|
-
"oxfmt --write",
|
|
69
|
-
"oxlint --fix"
|
|
70
|
-
]
|
|
71
|
-
}{{/unless}},
|
|
72
62
|
"engines": {
|
|
73
63
|
"node": ">=20",
|
|
74
|
-
"pnpm": ">={{pnpmVersion}} <11.
|
|
64
|
+
"pnpm": ">={{pnpmVersion}} <11.6.0"
|
|
75
65
|
}
|
|
76
66
|
}
|
|
@@ -55,32 +55,64 @@ if (!fs.existsSync(lockPath)) {
|
|
|
55
55
|
|
|
56
56
|
const lock = readJson(lockPath);
|
|
57
57
|
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
58
|
-
const
|
|
58
|
+
const sources = lock.sources ?? [];
|
|
59
|
+
const requiredCloneSources = sources.filter((source) => source.install === 'clone');
|
|
60
|
+
const optionalCloneSources = sources.filter(
|
|
59
61
|
(source) => source.install === 'clone-if-authorized',
|
|
60
62
|
);
|
|
63
|
+
const requiredSkills = [
|
|
64
|
+
...(lock.baseline ?? []),
|
|
65
|
+
...requiredCloneSources.flatMap((source) => source.baseline ?? []),
|
|
66
|
+
].filter(
|
|
67
|
+
(skill, index, skills) =>
|
|
68
|
+
skills.findIndex((candidate) => candidate.name === skill.name) === index,
|
|
69
|
+
);
|
|
61
70
|
|
|
62
71
|
if (checkOnly) {
|
|
63
|
-
const
|
|
72
|
+
const missingRequired = requiredSkills
|
|
73
|
+
.map((skill) => skill.name)
|
|
74
|
+
.filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')));
|
|
75
|
+
const missingOptional = optionalCloneSources.flatMap((source) =>
|
|
64
76
|
(source.baseline ?? [])
|
|
65
77
|
.map((skill) => skill.name)
|
|
66
78
|
.filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
|
|
67
79
|
);
|
|
68
|
-
|
|
80
|
+
|
|
81
|
+
if (missingRequired.length > 0) {
|
|
82
|
+
console.error(
|
|
83
|
+
`Required agent skills not installed: ${missingRequired.join(', ')}. Run pnpm skills:install.`,
|
|
84
|
+
);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (missingOptional.length > 0) {
|
|
69
89
|
console.warn(
|
|
70
|
-
`Private skills not installed: ${
|
|
90
|
+
`Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
|
|
71
91
|
);
|
|
72
92
|
} else {
|
|
73
|
-
console.log('
|
|
93
|
+
console.log('Required and private agent skills are installed.');
|
|
94
|
+
process.exit(0);
|
|
74
95
|
}
|
|
96
|
+
console.log('Required agent skills are installed.');
|
|
75
97
|
process.exit(0);
|
|
76
98
|
}
|
|
77
99
|
|
|
78
100
|
fs.mkdirSync(installDir, { recursive: true });
|
|
79
101
|
|
|
80
|
-
for (const source of
|
|
102
|
+
for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
|
|
81
103
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
|
|
82
104
|
try {
|
|
83
|
-
|
|
105
|
+
try {
|
|
106
|
+
cloneSource(source, tempDir);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (source.install === 'clone-if-authorized') {
|
|
109
|
+
console.warn(
|
|
110
|
+
`Skipping ${source.repository}; current developer may not have access.`,
|
|
111
|
+
);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
84
116
|
for (const skill of source.baseline ?? []) {
|
|
85
117
|
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
86
118
|
if (!sourceSkillDir) {
|
|
@@ -34,7 +34,7 @@ const activePnpmVersion = execFileSync('pnpm', ['--version'], {
|
|
|
34
34
|
|
|
35
35
|
if (activePnpmVersion !== expectedPnpmVersion) {
|
|
36
36
|
console.error(
|
|
37
|
-
`Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun
|
|
37
|
+
`Generated app requires pnpm ${expectedPnpmVersion}; active pnpm is ${activePnpmVersion}. Run mise install, then rerun pnpm from the activated shell`,
|
|
38
38
|
);
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
@@ -81,7 +81,7 @@ const requiredDeniedPaths = [
|
|
|
81
81
|
];
|
|
82
82
|
const requiredPostMaterialization = [
|
|
83
83
|
'ultramodern-contract-check',
|
|
84
|
-
'
|
|
84
|
+
'agent-skill-postinstall-allowed',
|
|
85
85
|
'github-workflow-security-enforced',
|
|
86
86
|
'package-source-retained',
|
|
87
87
|
'pnpm-11-policy-enforced',
|
|
@@ -92,8 +92,10 @@ const requiredPaths = [
|
|
|
92
92
|
{{#unless isSubproject}}
|
|
93
93
|
'AGENTS.md',
|
|
94
94
|
'.agents/skills-lock.json',
|
|
95
|
+
'.codex/hooks.json',
|
|
95
96
|
'.github/renovate.json',
|
|
96
97
|
'.github/workflows/ultramodern-gates.yml',
|
|
98
|
+
'lefthook.yml',
|
|
97
99
|
'oxlint.config.ts',
|
|
98
100
|
'oxfmt.config.ts',
|
|
99
101
|
'scripts/bootstrap-agent-skills.mjs',
|
|
@@ -152,9 +154,9 @@ for (const requiredSnippet of [
|
|
|
152
154
|
'pull_request:',
|
|
153
155
|
'persist-credentials: false',
|
|
154
156
|
'jdx/mise-action',
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
157
|
+
'pnpm install --frozen-lockfile',
|
|
158
|
+
'pnpm run ultramodern:check',
|
|
159
|
+
'pnpm run build',
|
|
158
160
|
'MODERN_PUBLIC_SITE_URL: http://localhost:8080',
|
|
159
161
|
'timeout-minutes:',
|
|
160
162
|
'egress-policy: audit',
|
|
@@ -218,6 +220,12 @@ for (const deniedPath of requiredDeniedPaths) {
|
|
|
218
220
|
if (templateManifest.lifecyclePolicy?.denyByDefault !== true) {
|
|
219
221
|
manifestErrors.push('lifecyclePolicy.denyByDefault');
|
|
220
222
|
}
|
|
223
|
+
if (
|
|
224
|
+
JSON.stringify(templateManifest.lifecyclePolicy?.allowedScripts) !==
|
|
225
|
+
JSON.stringify(['postinstall'])
|
|
226
|
+
) {
|
|
227
|
+
manifestErrors.push('lifecyclePolicy.allowedScripts');
|
|
228
|
+
}
|
|
221
229
|
|
|
222
230
|
for (const token of requiredPostMaterialization) {
|
|
223
231
|
if (!templateManifest.validation?.postMaterializationValidation?.includes(token)) {
|
|
@@ -260,6 +268,7 @@ const requiredScripts = {
|
|
|
260
268
|
'lint:fix': 'oxlint . --fix',
|
|
261
269
|
'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
|
|
262
270
|
'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
|
|
271
|
+
postinstall: 'node ./scripts/bootstrap-agent-skills.mjs && (git rev-parse --is-inside-work-tree >/dev/null 2>&1 && lefthook install || true)',
|
|
263
272
|
{{/unless}}
|
|
264
273
|
};
|
|
265
274
|
|
|
@@ -299,8 +308,8 @@ if (packageJson.packageManager !== `pnpm@${expectedPnpmVersion}`) {
|
|
|
299
308
|
process.exit(1);
|
|
300
309
|
}
|
|
301
310
|
|
|
302
|
-
if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.
|
|
303
|
-
console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.
|
|
311
|
+
if (packageJson.engines?.pnpm !== `>=${expectedPnpmVersion} <11.6.0`) {
|
|
312
|
+
console.error(`Generated app package must require pnpm >=${expectedPnpmVersion} <11.6.0`);
|
|
304
313
|
process.exit(1);
|
|
305
314
|
}
|
|
306
315
|
|
|
@@ -348,9 +357,9 @@ if (
|
|
|
348
357
|
'@swc/core': true,
|
|
349
358
|
'core-js': true,
|
|
350
359
|
esbuild: true,
|
|
360
|
+
lefthook: true,
|
|
351
361
|
'msgpackr-extract': true,
|
|
352
362
|
sharp: true,
|
|
353
|
-
'simple-git-hooks': true,
|
|
354
363
|
workerd: true,
|
|
355
364
|
})
|
|
356
365
|
) {
|
|
@@ -467,6 +476,7 @@ for (const dependency of [
|
|
|
467
476
|
'tailwindcss',
|
|
468
477
|
{{/if}}
|
|
469
478
|
{{#unless isSubproject}}
|
|
479
|
+
'lefthook',
|
|
470
480
|
'oxlint',
|
|
471
481
|
'oxfmt',
|
|
472
482
|
'ultracite',
|
|
@@ -127,7 +127,7 @@ const Index = () => {
|
|
|
127
127
|
<code className="code">modern.config.ts</code>
|
|
128
128
|
{/* i18n-ignore technical token */}
|
|
129
129
|
{t('home.description.afterConfig')}
|
|
130
|
-
<code className="code">
|
|
130
|
+
<code className="code">pnpm run ultramodern:check</code>
|
|
131
131
|
{/* i18n-ignore technical token */}
|
|
132
132
|
{t('home.description.end')}
|
|
133
133
|
</p>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Stop": [
|
|
3
|
+
{
|
|
4
|
+
"command": "pnpm format && pnpm lint:fix && pnpm check",
|
|
5
|
+
"timeout": 600000,
|
|
6
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"SubagentStop": [
|
|
10
|
+
{
|
|
11
|
+
"command": "pnpm format && pnpm lint:fix && pnpm check",
|
|
12
|
+
"timeout": 600000,
|
|
13
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -8,6 +8,8 @@ This workspace is generated as an agent-ready UltraModern.js SuperApp. Agents sh
|
|
|
8
8
|
- `pnpm format` runs oxfmt.
|
|
9
9
|
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
10
10
|
- `pnpm check` runs formatting, linting, effect-tsgo, private-skill availability checks, and the generated workspace contract.
|
|
11
|
+
- Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm check`.
|
|
12
|
+
- `postinstall` installs `lefthook` when the workspace is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format`, `pnpm lint:fix`, and `pnpm check` on pre-commit; pre-push runs `pnpm check`.
|
|
11
13
|
|
|
12
14
|
## Localized Routes
|
|
13
15
|
|
|
@@ -26,7 +28,7 @@ Use these skills when the task touches the matching subsystem:
|
|
|
26
28
|
- `rstest-best-practices`: Rstest configuration, test writing, mocking, snapshots, coverage, and CI test behavior.
|
|
27
29
|
- `mf`: Module Federation docs, Modern.js integration, DTS/type checks, shared dependency checks, runtime errors, and observability troubleshooting.
|
|
28
30
|
|
|
29
|
-
The public `module-federation/agent-skills` repository is installed during `
|
|
31
|
+
The public `module-federation/agent-skills` repository is installed during `pnpm install` and `pnpm skills:install`. `pnpm skills:check` fails when the required public `mf` skill is missing.
|
|
30
32
|
|
|
31
33
|
## Private Skills
|
|
32
34
|
|
|
@@ -40,7 +42,7 @@ The installer copies only the pinned private skills from `.agents/skills-lock.js
|
|
|
40
42
|
|
|
41
43
|
## Agent Reference Repositories
|
|
42
44
|
|
|
43
|
-
The workspace installs read-only source references under `repos/` by default during `
|
|
45
|
+
The workspace installs read-only source references under `repos/` by default during `pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
|
|
44
46
|
|
|
45
47
|
- `repos/effect` from `Effect-TS/effect`.
|
|
46
48
|
- `repos/ultramodern.js` from `BleedingDev/ultramodern.js`.
|
|
@@ -6,34 +6,33 @@ This workspace keeps `presetUltramodern(...)` as the single public
|
|
|
6
6
|
UltraModern.js 3.0 SuperApp surface and scaffolds the canonical Micro Vertical
|
|
7
7
|
starter topology:
|
|
8
8
|
|
|
9
|
-
- `apps/shell-super-app` owns shell route assembly and
|
|
10
|
-
- `
|
|
9
|
+
- `apps/shell-super-app` owns shell route assembly and federated manifest wiring.
|
|
10
|
+
- `verticals/commerce` owns the commerce vertical, its
|
|
11
11
|
browser-safe Module Federation exposes, and its Effect BFF/API surface.
|
|
12
|
-
- `
|
|
12
|
+
- `verticals/identity` owns the identity vertical, its
|
|
13
13
|
browser-safe Module Federation exposes, and its Effect BFF/API surface.
|
|
14
|
-
- `
|
|
14
|
+
- `verticals/design-system` owns the design-system vertical.
|
|
15
15
|
- `packages/shared-*` provide placeholders for cross-workspace contracts.
|
|
16
16
|
|
|
17
|
-
Default vertical APIs are owned by the vertical package
|
|
17
|
+
Default vertical APIs are owned by the vertical package.
|
|
18
18
|
Each full-stack vertical keeps its `api/effect/index.ts` handlers next to its
|
|
19
19
|
`shared/effect/api.ts` contract and `src/effect/*-client.ts` typed client
|
|
20
20
|
surface. The Module Federation config exposes only browser-safe Route and
|
|
21
21
|
Widget modules; server handlers and Effect client/contract modules stay out of
|
|
22
|
-
browser
|
|
23
|
-
packages.
|
|
22
|
+
browser verticals.
|
|
24
23
|
|
|
25
24
|
Run the scaffold validator before adding business code:
|
|
26
25
|
|
|
27
26
|
```bash
|
|
28
27
|
mise install
|
|
29
|
-
|
|
28
|
+
pnpm ultramodern:check
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
By default, `
|
|
31
|
+
By default, `pnpm install` also prepares read-only agent reference repositories
|
|
33
32
|
under `repos/` for Effect and UltraModern.js source lookup using squashed git
|
|
34
33
|
subtrees. Disable this setup with
|
|
35
|
-
`ULTRAMODERN_SKIP_AGENT_REPOS=1
|
|
36
|
-
explicitly with `
|
|
34
|
+
`ULTRAMODERN_SKIP_AGENT_REPOS=1 pnpm install`, or rerun it
|
|
35
|
+
explicitly with `pnpm agents:refs:install`.
|
|
37
36
|
|
|
38
37
|
The topology and ownership metadata are generated under `topology/`. The
|
|
39
38
|
workspace also ships `.github/workflows/ultramodern-workspace-gates.yml` and
|