@bleedingdev/modern-js-create 3.2.0-ultramodern.10 → 3.2.0-ultramodern.101

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.
Files changed (52) hide show
  1. package/README.md +152 -35
  2. package/bin/run.js +0 -0
  3. package/dist/index.js +4745 -604
  4. package/dist/types/locale/en.d.ts +3 -0
  5. package/dist/types/locale/zh.d.ts +3 -0
  6. package/dist/types/ultramodern-workspace.d.ts +11 -0
  7. package/package.json +6 -6
  8. package/template/.codex/hooks.json +16 -0
  9. package/template/.github/renovate.json +53 -0
  10. package/template/.github/workflows/ultramodern-gates.yml.handlebars +34 -10
  11. package/template/.mise.toml.handlebars +2 -0
  12. package/template/AGENTS.md +9 -6
  13. package/template/README.md +60 -34
  14. package/template/api/effect/index.ts.handlebars +20 -9
  15. package/template/api/lambda/hello.ts.handlebars +5 -5
  16. package/template/config/public/locales/cs/translation.json +39 -0
  17. package/template/config/public/locales/en/translation.json +39 -0
  18. package/template/lefthook.yml +10 -0
  19. package/template/modern.config.ts.handlebars +27 -1
  20. package/template/oxfmt.config.ts +8 -1
  21. package/template/oxlint.config.ts +8 -1
  22. package/template/package.json.handlebars +30 -26
  23. package/template/pnpm-workspace.yaml +29 -0
  24. package/template/rstest.config.mts +5 -0
  25. package/template/scripts/bootstrap-agent-skills.mjs +148 -15
  26. package/template/scripts/check-i18n-strings.mjs +94 -0
  27. package/template/scripts/validate-ultramodern.mjs.handlebars +374 -2
  28. package/template/src/modern-app-env.d.ts +2 -0
  29. package/template/src/modern.runtime.ts.handlebars +17 -1
  30. package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
  31. package/template/src/routes/layout.tsx.handlebars +2 -1
  32. package/template/tailwind.config.ts.handlebars +1 -1
  33. package/template/tests/tsconfig.json +7 -0
  34. package/template/tests/ultramodern.contract.test.ts.handlebars +78 -0
  35. package/template/tsconfig.json +1 -0
  36. package/template-workspace/.agents/agent-reference-repos.json +24 -0
  37. package/template-workspace/.agents/skills-lock.json +19 -0
  38. package/template-workspace/.codex/hooks.json +16 -0
  39. package/template-workspace/.github/renovate.json +29 -0
  40. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +54 -0
  41. package/template-workspace/.gitignore.handlebars +5 -0
  42. package/template-workspace/.mise.toml.handlebars +2 -0
  43. package/template-workspace/AGENTS.md +36 -5
  44. package/template-workspace/README.md.handlebars +61 -11
  45. package/template-workspace/lefthook.yml +10 -0
  46. package/template-workspace/oxfmt.config.ts +13 -3
  47. package/template-workspace/oxlint.config.ts +12 -4
  48. package/template-workspace/pnpm-workspace.yaml +26 -8
  49. package/template-workspace/scripts/bootstrap-agent-skills.mjs +184 -26
  50. package/template-workspace/scripts/setup-agent-reference-repos.mjs +368 -0
  51. package/template/src/routes/page.tsx.handlebars +0 -136
  52. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -403
@@ -0,0 +1,10 @@
1
+ pre-commit:
2
+ commands:
3
+ fix-and-check:
4
+ run: pnpm format && pnpm lint:fix && pnpm check
5
+ stage_fixed: true
6
+
7
+ pre-push:
8
+ commands:
9
+ check:
10
+ run: pnpm check
@@ -1,7 +1,17 @@
1
- import { defineConfig } from "oxfmt";
2
- import ultracite from "ultracite/oxfmt";
1
+ import { defineConfig } from 'oxfmt';
2
+ import ultracite from 'ultracite/oxfmt';
3
3
 
4
4
  export default defineConfig({
5
5
  extends: [ultracite],
6
- ignorePatterns: ["dist", "node_modules", ".modern", ".modernjs", "**/routeTree.gen.ts"],
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 "oxlint";
2
- import core from "ultracite/oxlint/core";
3
- import react from "ultracite/oxlint/react";
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: ["dist", "node_modules", ".modern", ".modernjs", "**/routeTree.gen.ts"],
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
- - apps/remotes/*
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
- onlyBuiltDependencies:
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
- function readJson(filePath) {
12
- return JSON.parse(fs.readFileSync(filePath, 'utf8'));
13
- }
11
+ const readJson = filePath => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
14
12
 
15
- function run(command, args, options = {}) {
16
- return execFileSync(command, args, {
13
+ const run = (command, args, options = {}) =>
14
+ execFileSync(command, args, {
17
15
  cwd: options.cwd ?? root,
18
- encoding: 'utf8',
16
+ encoding: 'utf-8',
19
17
  stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
20
18
  });
21
- }
22
19
 
23
- function cloneSource(source, targetDir) {
24
- const repo = source.repository.replace(/^https:\/\/github.com\//, '');
20
+ const commandExists = command => {
25
21
  try {
26
- run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
27
- stdio: 'inherit',
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', ['clone', '--depth', '1', source.repository, targetDir], {
32
- stdio: 'inherit',
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
- function resolveSkillDir(sourceRoot, skillName) {
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 privateSources = (lock.sources ?? []).filter(
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 missing = privateSources.flatMap(source =>
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
- if (missing.length > 0) {
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: ${missing.join(', ')}. Run pnpm skills:install if you have access.`,
213
+ `Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
72
214
  );
73
215
  } else {
74
- console.log('Agent skills are installed.');
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 privateSources) {
227
+ for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
82
228
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
83
229
  try {
84
- cloneSource(source, tempDir);
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
- fs.rmSync(targetSkillDir, { recursive: true, force: true });
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
- fs.rmSync(tempDir, { recursive: true, force: true });
260
+ removeTree(tempDir);
105
261
  }
106
262
  }
263
+
264
+ installLefthook();