@bleedingdev/modern-js-create 3.2.0-ultramodern.10 → 3.2.0-ultramodern.100
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 +152 -35
- package/bin/run.js +0 -0
- package/dist/index.js +4745 -604
- package/dist/types/locale/en.d.ts +3 -0
- package/dist/types/locale/zh.d.ts +3 -0
- package/dist/types/ultramodern-workspace.d.ts +11 -0
- package/package.json +6 -6
- package/template/.codex/hooks.json +16 -0
- package/template/.github/renovate.json +53 -0
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +34 -10
- package/template/.mise.toml.handlebars +2 -0
- package/template/AGENTS.md +9 -6
- package/template/README.md +60 -34
- package/template/api/effect/index.ts.handlebars +20 -9
- package/template/api/lambda/hello.ts.handlebars +5 -5
- package/template/config/public/locales/cs/translation.json +39 -0
- package/template/config/public/locales/en/translation.json +39 -0
- package/template/lefthook.yml +10 -0
- package/template/modern.config.ts.handlebars +27 -1
- package/template/oxfmt.config.ts +8 -1
- package/template/oxlint.config.ts +8 -1
- package/template/package.json.handlebars +30 -26
- package/template/pnpm-workspace.yaml +29 -0
- package/template/rstest.config.mts +5 -0
- package/template/scripts/bootstrap-agent-skills.mjs +148 -15
- package/template/scripts/check-i18n-strings.mjs +94 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +374 -2
- package/template/src/modern-app-env.d.ts +2 -0
- package/template/src/modern.runtime.ts.handlebars +17 -1
- package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
- package/template/src/routes/layout.tsx.handlebars +2 -1
- package/template/tailwind.config.ts.handlebars +1 -1
- package/template/tests/tsconfig.json +7 -0
- package/template/tests/ultramodern.contract.test.ts.handlebars +78 -0
- package/template/tsconfig.json +1 -0
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/skills-lock.json +19 -0
- package/template-workspace/.codex/hooks.json +16 -0
- package/template-workspace/.github/renovate.json +29 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +54 -0
- package/template-workspace/.gitignore.handlebars +5 -0
- package/template-workspace/.mise.toml.handlebars +2 -0
- package/template-workspace/AGENTS.md +36 -5
- package/template-workspace/README.md.handlebars +61 -11
- package/template-workspace/lefthook.yml +10 -0
- package/template-workspace/oxfmt.config.ts +13 -3
- package/template-workspace/oxlint.config.ts +12 -4
- package/template-workspace/pnpm-workspace.yaml +26 -8
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +184 -26
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +368 -0
- package/template/src/routes/page.tsx.handlebars +0 -136
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -403
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import ultracite from
|
|
1
|
+
import { defineConfig } from 'oxfmt';
|
|
2
|
+
import ultracite from 'ultracite/oxfmt';
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
extends: [ultracite],
|
|
6
|
-
ignorePatterns: [
|
|
6
|
+
ignorePatterns: [
|
|
7
|
+
'.agents',
|
|
8
|
+
'**/*.json',
|
|
9
|
+
'dist',
|
|
10
|
+
'node_modules',
|
|
11
|
+
'repos/**',
|
|
12
|
+
'.modern',
|
|
13
|
+
'.modernjs',
|
|
14
|
+
'**/routeTree.gen.ts',
|
|
15
|
+
],
|
|
16
|
+
singleQuote: true,
|
|
7
17
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineConfig } from
|
|
2
|
-
import core from
|
|
3
|
-
import react from
|
|
1
|
+
import { defineConfig } from 'oxlint';
|
|
2
|
+
import core from 'ultracite/oxlint/core';
|
|
3
|
+
import react from 'ultracite/oxlint/react';
|
|
4
4
|
|
|
5
5
|
export default defineConfig({
|
|
6
6
|
env: {
|
|
@@ -8,5 +8,13 @@ export default defineConfig({
|
|
|
8
8
|
node: true,
|
|
9
9
|
},
|
|
10
10
|
extends: [core, react],
|
|
11
|
-
ignorePatterns: [
|
|
11
|
+
ignorePatterns: [
|
|
12
|
+
'.agents',
|
|
13
|
+
'dist',
|
|
14
|
+
'node_modules',
|
|
15
|
+
'repos/**',
|
|
16
|
+
'.modern',
|
|
17
|
+
'.modernjs',
|
|
18
|
+
'**/routeTree.gen.ts',
|
|
19
|
+
],
|
|
12
20
|
});
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
packages:
|
|
2
2
|
- apps/*
|
|
3
|
-
-
|
|
4
|
-
- services/*
|
|
3
|
+
- verticals/*
|
|
5
4
|
- packages/*
|
|
6
5
|
|
|
6
|
+
minimumReleaseAge: 1440
|
|
7
|
+
minimumReleaseAgeStrict: true
|
|
8
|
+
minimumReleaseAgeIgnoreMissingTime: false
|
|
9
|
+
minimumReleaseAgeExclude:
|
|
10
|
+
- '@bleedingdev/modern-js-*'
|
|
11
|
+
trustPolicy: no-downgrade
|
|
12
|
+
trustPolicyIgnoreAfter: 1440
|
|
13
|
+
blockExoticSubdeps: true
|
|
14
|
+
engineStrict: true
|
|
15
|
+
pmOnFail: error
|
|
16
|
+
verifyDepsBeforeRun: error
|
|
17
|
+
strictDepBuilds: true
|
|
18
|
+
|
|
19
|
+
peerDependencyRules:
|
|
20
|
+
allowedVersions:
|
|
21
|
+
react: '>=19.0.0'
|
|
22
|
+
typescript: '>=6.0.0'
|
|
23
|
+
|
|
24
|
+
overrides:
|
|
25
|
+
'@tanstack/react-router': 1.170.11
|
|
26
|
+
node-fetch: '^3.3.2'
|
|
27
|
+
|
|
7
28
|
allowBuilds:
|
|
8
29
|
'@swc/core': true
|
|
9
30
|
core-js: true
|
|
10
31
|
esbuild: true
|
|
32
|
+
lefthook: true
|
|
11
33
|
msgpackr-extract: true
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- '@swc/core'
|
|
15
|
-
- core-js
|
|
16
|
-
- esbuild
|
|
17
|
-
- msgpackr-extract
|
|
34
|
+
sharp: true
|
|
35
|
+
workerd: true
|
|
@@ -8,33 +8,151 @@ const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
|
8
8
|
const checkOnly = process.argv.includes('--check');
|
|
9
9
|
const force = process.argv.includes('--force');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
|
-
}
|
|
11
|
+
const readJson = filePath => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const run = (command, args, options = {}) =>
|
|
14
|
+
execFileSync(command, args, {
|
|
17
15
|
cwd: options.cwd ?? root,
|
|
18
|
-
encoding: '
|
|
16
|
+
encoding: 'utf-8',
|
|
19
17
|
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
20
18
|
});
|
|
21
|
-
}
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
const repo = source.repository.replace(/^https:\/\/github.com\//, '');
|
|
20
|
+
const commandExists = command => {
|
|
25
21
|
try {
|
|
26
|
-
run(
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
run(command, ['--version'], { stdio: 'ignore' });
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const runShell = script =>
|
|
30
|
+
run('sh', ['-lc', script], {
|
|
31
|
+
stdio: 'inherit',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const installGit = () => {
|
|
35
|
+
if (commandExists('git')) {
|
|
29
36
|
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (commandExists('brew')) {
|
|
40
|
+
run('brew', ['install', 'git'], { stdio: 'inherit' });
|
|
41
|
+
} else if (process.platform === 'linux' && commandExists('apt-get')) {
|
|
42
|
+
const sudo =
|
|
43
|
+
typeof process.getuid === 'function' && process.getuid() === 0
|
|
44
|
+
? ''
|
|
45
|
+
: 'sudo ';
|
|
46
|
+
runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
|
|
47
|
+
} else if (process.platform === 'linux' && commandExists('dnf')) {
|
|
48
|
+
const sudo =
|
|
49
|
+
typeof process.getuid === 'function' && process.getuid() === 0
|
|
50
|
+
? ''
|
|
51
|
+
: 'sudo ';
|
|
52
|
+
runShell(`${sudo}dnf install -y git`);
|
|
53
|
+
} else if (process.platform === 'linux' && commandExists('yum')) {
|
|
54
|
+
const sudo =
|
|
55
|
+
typeof process.getuid === 'function' && process.getuid() === 0
|
|
56
|
+
? ''
|
|
57
|
+
: 'sudo ';
|
|
58
|
+
runShell(`${sudo}yum install -y git`);
|
|
59
|
+
} else if (process.platform === 'linux' && commandExists('apk')) {
|
|
60
|
+
runShell('apk add --no-cache git');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!commandExists('git')) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'Git is required for UltraModern setup. Install git and run pnpm skills:install again.',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const isInsideGitWorkTree = () => {
|
|
71
|
+
try {
|
|
72
|
+
return run('git', ['rev-parse', '--is-inside-work-tree']).trim() === 'true';
|
|
73
|
+
} catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const initializeGitRepository = () => {
|
|
79
|
+
if (isInsideGitWorkTree()) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
run('git', ['init', '-b', 'main'], { stdio: 'inherit' });
|
|
30
85
|
} catch {
|
|
31
|
-
run('git', ['
|
|
32
|
-
|
|
86
|
+
run('git', ['init'], { stdio: 'inherit' });
|
|
87
|
+
run('git', ['branch', '-M', 'main'], { stdio: 'inherit' });
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const installLefthook = () => {
|
|
92
|
+
try {
|
|
93
|
+
run('lefthook', ['install'], { stdio: 'inherit' });
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn(`Unable to install lefthook hooks: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const removeTree = dir =>
|
|
100
|
+
fs.rmSync(dir, {
|
|
101
|
+
force: true,
|
|
102
|
+
maxRetries: 5,
|
|
103
|
+
recursive: true,
|
|
104
|
+
retryDelay: 100,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const cloneSource = (source, targetDir) => {
|
|
108
|
+
if (source.commit) {
|
|
109
|
+
run('git', ['init', targetDir]);
|
|
110
|
+
run('git', ['remote', 'add', 'origin', source.repository], {
|
|
111
|
+
cwd: targetDir,
|
|
112
|
+
});
|
|
113
|
+
run('git', ['fetch', '--depth', '1', '--quiet', 'origin', source.commit], {
|
|
114
|
+
cwd: targetDir,
|
|
33
115
|
});
|
|
116
|
+
run(
|
|
117
|
+
'git',
|
|
118
|
+
[
|
|
119
|
+
'-c',
|
|
120
|
+
'advice.detachedHead=false',
|
|
121
|
+
'checkout',
|
|
122
|
+
'--detach',
|
|
123
|
+
'--quiet',
|
|
124
|
+
'FETCH_HEAD',
|
|
125
|
+
],
|
|
126
|
+
{ cwd: targetDir },
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
34
129
|
}
|
|
35
|
-
}
|
|
36
130
|
|
|
37
|
-
|
|
131
|
+
const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
|
|
132
|
+
try {
|
|
133
|
+
run('gh', [
|
|
134
|
+
'repo',
|
|
135
|
+
'clone',
|
|
136
|
+
repo,
|
|
137
|
+
targetDir,
|
|
138
|
+
'--',
|
|
139
|
+
'--depth',
|
|
140
|
+
'1',
|
|
141
|
+
'--quiet',
|
|
142
|
+
]);
|
|
143
|
+
} catch {
|
|
144
|
+
run('git', [
|
|
145
|
+
'clone',
|
|
146
|
+
'--depth',
|
|
147
|
+
'1',
|
|
148
|
+
'--quiet',
|
|
149
|
+
source.repository,
|
|
150
|
+
targetDir,
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const resolveSkillDir = (sourceRoot, skillName) => {
|
|
38
156
|
const candidates = [
|
|
39
157
|
path.join(sourceRoot, skillName),
|
|
40
158
|
path.join(sourceRoot, 'skills', skillName),
|
|
@@ -44,7 +162,7 @@ function resolveSkillDir(sourceRoot, skillName) {
|
|
|
44
162
|
return candidates.find(candidate =>
|
|
45
163
|
fs.existsSync(path.join(candidate, 'SKILL.md')),
|
|
46
164
|
);
|
|
47
|
-
}
|
|
165
|
+
};
|
|
48
166
|
|
|
49
167
|
if (!fs.existsSync(lockPath)) {
|
|
50
168
|
console.error('Missing .agents/skills-lock.json');
|
|
@@ -53,12 +171,28 @@ if (!fs.existsSync(lockPath)) {
|
|
|
53
171
|
|
|
54
172
|
const lock = readJson(lockPath);
|
|
55
173
|
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
56
|
-
const
|
|
174
|
+
const sources = lock.sources ?? [];
|
|
175
|
+
const requiredCloneSources = sources.filter(
|
|
176
|
+
source => source.install === 'clone',
|
|
177
|
+
);
|
|
178
|
+
const optionalCloneSources = sources.filter(
|
|
57
179
|
source => source.install === 'clone-if-authorized',
|
|
58
180
|
);
|
|
181
|
+
const requiredSkills = [
|
|
182
|
+
...(lock.baseline ?? []),
|
|
183
|
+
...requiredCloneSources.flatMap(source => source.baseline ?? []),
|
|
184
|
+
].filter(
|
|
185
|
+
(skill, index, skills) =>
|
|
186
|
+
skills.findIndex(candidate => candidate.name === skill.name) === index,
|
|
187
|
+
);
|
|
59
188
|
|
|
60
189
|
if (checkOnly) {
|
|
61
|
-
const
|
|
190
|
+
const missingRequired = requiredSkills
|
|
191
|
+
.map(skill => skill.name)
|
|
192
|
+
.filter(
|
|
193
|
+
skillName => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
194
|
+
);
|
|
195
|
+
const missingOptional = optionalCloneSources.flatMap(source =>
|
|
62
196
|
(source.baseline ?? [])
|
|
63
197
|
.map(skill => skill.name)
|
|
64
198
|
.filter(
|
|
@@ -66,22 +200,44 @@ if (checkOnly) {
|
|
|
66
200
|
!fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
67
201
|
),
|
|
68
202
|
);
|
|
69
|
-
|
|
203
|
+
|
|
204
|
+
if (missingRequired.length > 0) {
|
|
205
|
+
console.error(
|
|
206
|
+
`Required agent skills not installed: ${missingRequired.join(', ')}. Run pnpm skills:install.`,
|
|
207
|
+
);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (missingOptional.length > 0) {
|
|
70
212
|
console.warn(
|
|
71
|
-
`Private skills not installed: ${
|
|
213
|
+
`Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
|
|
72
214
|
);
|
|
73
215
|
} else {
|
|
74
|
-
console.log('
|
|
216
|
+
console.log('Required and private agent skills are installed.');
|
|
217
|
+
process.exit(0);
|
|
75
218
|
}
|
|
219
|
+
console.log('Required agent skills are installed.');
|
|
76
220
|
process.exit(0);
|
|
77
221
|
}
|
|
78
222
|
|
|
79
223
|
fs.mkdirSync(installDir, { recursive: true });
|
|
224
|
+
installGit();
|
|
225
|
+
initializeGitRepository();
|
|
80
226
|
|
|
81
|
-
for (const source of
|
|
227
|
+
for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
|
|
82
228
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
|
|
83
229
|
try {
|
|
84
|
-
|
|
230
|
+
try {
|
|
231
|
+
cloneSource(source, tempDir);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (source.install === 'clone-if-authorized') {
|
|
234
|
+
console.warn(
|
|
235
|
+
`Skipping ${source.repository}; current developer may not have access.`,
|
|
236
|
+
);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
85
241
|
for (const skill of source.baseline ?? []) {
|
|
86
242
|
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
87
243
|
if (!sourceSkillDir) {
|
|
@@ -95,12 +251,14 @@ for (const source of privateSources) {
|
|
|
95
251
|
console.log(`Skipping existing ${skill.name}`);
|
|
96
252
|
continue;
|
|
97
253
|
}
|
|
98
|
-
|
|
254
|
+
removeTree(targetSkillDir);
|
|
99
255
|
}
|
|
100
256
|
fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
|
|
101
257
|
console.log(`Installed ${skill.name}`);
|
|
102
258
|
}
|
|
103
259
|
} finally {
|
|
104
|
-
|
|
260
|
+
removeTree(tempDir);
|
|
105
261
|
}
|
|
106
262
|
}
|
|
263
|
+
|
|
264
|
+
installLefthook();
|