@bleedingdev/modern-js-create 3.2.0-ultramodern.103 → 3.2.0-ultramodern.105

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.
@@ -0,0 +1,28 @@
1
+ export declare const WORKSPACE_PACKAGE_VERSION = "workspace:*";
2
+ export declare const BLEEDINGDEV_CREATE_PACKAGE = "@bleedingdev/modern-js-create";
3
+ export declare const BLEEDINGDEV_PACKAGE_SCOPE = "bleedingdev";
4
+ export declare const BLEEDINGDEV_PACKAGE_NAME_PREFIX = "modern-js-";
5
+ export declare const BLEEDINGDEV_FRAMEWORK_VERSION_ENV = "MODERN_CREATE_ULTRAMODERN_FRAMEWORK_VERSION";
6
+ export declare const ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES: readonly ['@modern-js/runtime', '@modern-js/app-tools', '@modern-js/tsconfig', '@modern-js/plugin-i18n', '@modern-js/plugin-tanstack', '@modern-js/plugin-bff', '@modern-js/adapter-rstest'];
7
+ export declare const ULTRAMODERN_WORKSPACE_MODERN_PACKAGES: readonly ['@modern-js/app-tools', '@modern-js/plugin-bff', '@modern-js/plugin-i18n', '@modern-js/plugin-tanstack', '@modern-js/runtime'];
8
+ export type UltramodernPackageSourceStrategy = 'workspace' | 'install';
9
+ export type ResolvedUltramodernPackageSource = {
10
+ strategy: UltramodernPackageSourceStrategy;
11
+ modernPackageVersion: string;
12
+ registry?: string;
13
+ aliasScope?: string;
14
+ aliasPackageNamePrefix?: string;
15
+ };
16
+ export type UltramodernModernPackagesMetadata = {
17
+ packages: string[];
18
+ specifier: string;
19
+ registry?: string;
20
+ aliases?: Record<string, string>;
21
+ };
22
+ export declare function modernPackageVersion(packageSource: ResolvedUltramodernPackageSource): string;
23
+ export declare function modernAliasPackageName(packageName: string, packageSource: ResolvedUltramodernPackageSource): string;
24
+ export declare function modernPackageSpecifier(packageName: string, packageSource: ResolvedUltramodernPackageSource): string;
25
+ export declare function modernPackageAliases(packageNames: readonly string[], packageSource: ResolvedUltramodernPackageSource): Record<string, string> | undefined;
26
+ export declare function createModernPackagesMetadata(packageNames: readonly string[], packageSource: ResolvedUltramodernPackageSource, options?: {
27
+ includeAliases?: boolean;
28
+ }): UltramodernModernPackagesMetadata;
@@ -1,4 +1,4 @@
1
- type UltramodernPackageSourceStrategy = 'workspace' | 'install';
1
+ import { type UltramodernPackageSourceStrategy } from './ultramodern-package-source';
2
2
  export type UltramodernWorkspaceOptions = {
3
3
  targetDir: string;
4
4
  packageName: string;
@@ -28,4 +28,3 @@ export declare const ultramodernWorkspaceVersions: {
28
28
  tailwind: string;
29
29
  tailwindPostcss: string;
30
30
  };
31
- export {};
package/package.json CHANGED
@@ -21,7 +21,7 @@
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
24
- "version": "3.2.0-ultramodern.103",
24
+ "version": "3.2.0-ultramodern.105",
25
25
  "types": "./dist/types/index.d.ts",
26
26
  "main": "./dist/index.js",
27
27
  "bin": {
@@ -39,9 +39,9 @@
39
39
  "devDependencies": {
40
40
  "@rslib/core": "0.21.5",
41
41
  "@types/node": "^25.9.1",
42
- "@typescript/native-preview": "7.0.0-dev.20260527.2",
42
+ "@typescript/native-preview": "7.0.0-dev.20260606.1",
43
43
  "tsx": "^4.22.3",
44
- "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.103"
44
+ "@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.105"
45
45
  },
46
46
  "publishConfig": {
47
47
  "registry": "https://registry.npmjs.org/",
@@ -54,6 +54,6 @@
54
54
  "start": "node ./dist/index.js"
55
55
  },
56
56
  "ultramodern": {
57
- "frameworkVersion": "3.2.0-ultramodern.103"
57
+ "frameworkVersion": "3.2.0-ultramodern.105"
58
58
  }
59
59
  }
@@ -39,7 +39,13 @@ export default defineConfig(
39
39
  {{/if}} runtimeFramework: '{{bffRuntime}}',
40
40
  },
41
41
 
42
- {{/if}} plugins: [
42
+ {{/if}} html: {
43
+ meta: {
44
+ viewport: 'width=device-width, initial-scale=1.0, viewport-fit=cover',
45
+ },
46
+ title: 'UltraModern.js Starter',
47
+ },
48
+ plugins: [
43
49
  appTools(),
44
50
  i18nPlugin({
45
51
  localeDetection: {
@@ -53,12 +59,6 @@ export default defineConfig(
53
59
  {{/if}}{{#if enableBff}}
54
60
  bffPlugin(),
55
61
  {{/if}} ],
56
- html: {
57
- title: 'UltraModern.js Starter',
58
- meta: {
59
- viewport: 'width=device-width, initial-scale=1.0, viewport-fit=cover',
60
- },
61
- },
62
62
  source: {
63
63
  globalVars: {
64
64
  ULTRAMODERN_SITE_URL: siteUrl,
@@ -70,8 +70,8 @@ export default defineConfig(
70
70
  enableBffRequestId,
71
71
  enableModuleFederationSSR,
72
72
  enableTelemetryExporters,
73
- telemetryFailLoudStartup,
74
73
  ...(typeof otlpEndpoint === 'string' ? { otlpEndpoint } : {}),
74
+ telemetryFailLoudStartup,
75
75
  ...(typeof victoriaMetricsEndpoint === 'string' ? { victoriaMetricsEndpoint } : {}),
76
76
  },
77
77
  ),
@@ -30,34 +30,34 @@
30
30
  "@modern-js/runtime": "{{runtimeVersion}}",
31
31
  {{#if isTanstackRouter}} "@tanstack/react-router": "{{tanstackRouterVersion}}",
32
32
  {{/if}}
33
- "i18next": "26.2.0",
34
- "react": "^19.2.6",
35
- "react-dom": "^19.2.6",
36
- "react-i18next": "17.0.8"
33
+ "i18next": "{{i18nextVersion}}",
34
+ "react": "{{reactVersion}}",
35
+ "react-dom": "{{reactDomVersion}}",
36
+ "react-i18next": "{{reactI18nextVersion}}"
37
37
  },
38
38
  "devDependencies": {
39
- "@effect/tsgo": "0.13.0",
39
+ "@effect/tsgo": "{{effectTsgoVersion}}",
40
40
  "@modern-js/adapter-rstest": "{{adapterRstestVersion}}",
41
41
  "@modern-js/app-tools": "{{appToolsVersion}}",
42
42
  {{#if enableBff}} "@modern-js/plugin-bff": "{{pluginBffVersion}}",
43
43
  {{/if}} "@modern-js/tsconfig": "{{tsconfigVersion}}",
44
- "@rstest/core": "0.10.3",
44
+ "@rstest/core": "{{rstestCoreVersion}}",
45
45
  {{#if enableTailwind}}
46
46
  "@tailwindcss/postcss": "^{{tailwindPostcssVersion}}",
47
47
  {{/if}}
48
48
  "@types/node": "^20",
49
- "@types/react": "^19.1.8",
50
- "@types/react-dom": "^19.1.6",
51
- "@typescript/native-preview": "7.0.0-dev.20260527.2",
52
- "happy-dom": "^20.9.0",
49
+ "@types/react": "{{typesReactVersion}}",
50
+ "@types/react-dom": "{{typesReactDomVersion}}",
51
+ "@typescript/native-preview": "{{typescriptNativePreviewVersion}}",
52
+ "happy-dom": "{{happyDomVersion}}",
53
53
  {{#unless isSubproject}}
54
54
  "lefthook": "^2.1.9",
55
- "oxfmt": "0.51.0",
56
- "oxlint": "1.66.0",
57
- {{/unless}}{{#if enableTailwind}} "postcss": "^8.5.6",
55
+ "oxfmt": "{{oxfmtVersion}}",
56
+ "oxlint": "{{oxlintVersion}}",
57
+ {{/unless}}{{#if enableTailwind}} "postcss": "{{postcssVersion}}",
58
58
  {{/if}} "rimraf": "^6.1.3"{{#if enableTailwind}},
59
59
  "tailwindcss": "^{{tailwindVersion}}"{{/if}}{{#unless isSubproject}},
60
- "ultracite": "7.7.0"{{/unless}}
60
+ "ultracite": "{{ultraciteVersion}}"{{/unless}}
61
61
  },
62
62
  "engines": {
63
63
  "node": ">=20",
@@ -3,6 +3,11 @@ minimumReleaseAgeStrict: true
3
3
  minimumReleaseAgeIgnoreMissingTime: false
4
4
  minimumReleaseAgeExclude:
5
5
  - '@bleedingdev/modern-js-*'
6
+ - '@tanstack/react-router'
7
+ - '@tanstack/router-core'
8
+ - '@typescript/native-preview'
9
+ - '@typescript/native-preview-*'
10
+ - '@types/react'
6
11
  trustPolicy: no-downgrade
7
12
  trustPolicyIgnoreAfter: 1440
8
13
  blockExoticSubdeps: true
@@ -7,6 +7,97 @@ const ignoredDirectories = new Set(['.modern', '.modernjs', 'dist', 'node_module
7
7
  const visibleAttributePattern =
8
8
  /\s(?:aria-label|alt|placeholder|title)=["']([^"']*[A-Za-z][^"']*)["']/gu;
9
9
  const jsxTextPattern = />([^<>{}]*[A-Za-z][^<>{}]*)</gu;
10
+ const jsxIntrinsicTags = new Set([
11
+ 'a',
12
+ 'abbr',
13
+ 'address',
14
+ 'area',
15
+ 'article',
16
+ 'aside',
17
+ 'audio',
18
+ 'b',
19
+ 'blockquote',
20
+ 'body',
21
+ 'br',
22
+ 'button',
23
+ 'canvas',
24
+ 'caption',
25
+ 'cite',
26
+ 'code',
27
+ 'col',
28
+ 'colgroup',
29
+ 'data',
30
+ 'datalist',
31
+ 'dd',
32
+ 'del',
33
+ 'details',
34
+ 'dfn',
35
+ 'dialog',
36
+ 'div',
37
+ 'dl',
38
+ 'dt',
39
+ 'em',
40
+ 'fieldset',
41
+ 'figcaption',
42
+ 'figure',
43
+ 'footer',
44
+ 'form',
45
+ 'h1',
46
+ 'h2',
47
+ 'h3',
48
+ 'h4',
49
+ 'h5',
50
+ 'h6',
51
+ 'head',
52
+ 'header',
53
+ 'hr',
54
+ 'html',
55
+ 'i',
56
+ 'iframe',
57
+ 'img',
58
+ 'input',
59
+ 'label',
60
+ 'legend',
61
+ 'li',
62
+ 'link',
63
+ 'main',
64
+ 'mark',
65
+ 'menu',
66
+ 'meta',
67
+ 'meter',
68
+ 'nav',
69
+ 'ol',
70
+ 'option',
71
+ 'p',
72
+ 'picture',
73
+ 'pre',
74
+ 'progress',
75
+ 'q',
76
+ 'script',
77
+ 'section',
78
+ 'select',
79
+ 'small',
80
+ 'source',
81
+ 'span',
82
+ 'strong',
83
+ 'style',
84
+ 'summary',
85
+ 'svg',
86
+ 'table',
87
+ 'tbody',
88
+ 'td',
89
+ 'template',
90
+ 'textarea',
91
+ 'tfoot',
92
+ 'th',
93
+ 'thead',
94
+ 'time',
95
+ 'title',
96
+ 'tr',
97
+ 'u',
98
+ 'ul',
99
+ 'video',
100
+ ]);
10
101
 
11
102
  const collectFiles = (directory) => {
12
103
  if (!fs.existsSync(directory)) {
@@ -37,6 +128,24 @@ const isCodeElementText = (content, index) => {
37
128
  }
38
129
  return /^<code(?:\s|>)/u.test(content.slice(tagStart, index));
39
130
  };
131
+ const isJsxTagEnd = (content, index) => {
132
+ const tagStart = content.lastIndexOf('<', index);
133
+ if (tagStart === -1 || content.slice(tagStart + 1, index).includes('<')) {
134
+ return false;
135
+ }
136
+ const match = content
137
+ .slice(tagStart, index + 1)
138
+ .match(/^<\/?\s*([A-Za-z][\w:.-]*)\b[^<>]*>$/u);
139
+ if (!match) {
140
+ return false;
141
+ }
142
+ const [, tagName] = match;
143
+ return (
144
+ /^[A-Z]/u.test(tagName) ||
145
+ tagName.includes('-') ||
146
+ jsxIntrinsicTags.has(tagName)
147
+ );
148
+ };
40
149
  const isIgnoredLine = (content, index) => {
41
150
  const lineStart = content.lastIndexOf('\n', index) + 1;
42
151
  const lineEnd = content.indexOf('\n', index);
@@ -54,20 +163,23 @@ const violations = [];
54
163
  for (const filePath of scanRoots.flatMap(collectFiles)) {
55
164
  const content = fs.readFileSync(filePath, 'utf-8');
56
165
  for (const match of content.matchAll(visibleAttributePattern)) {
166
+ const [, visibleText] = match;
57
167
  if (!isIgnoredLine(content, match.index ?? 0)) {
58
168
  violations.push({
59
169
  filePath,
60
170
  line: lineNumberForIndex(content, match.index ?? 0),
61
- text: match[1].trim(),
171
+ text: visibleText.trim(),
62
172
  });
63
173
  }
64
174
  }
65
175
 
66
176
  for (const match of content.matchAll(jsxTextPattern)) {
67
- const text = match[1].replaceAll(/\s+/gu, ' ').trim();
177
+ const [, jsxText] = match;
178
+ const text = jsxText.replaceAll(/\s+/gu, ' ').trim();
68
179
  if (
69
180
  text &&
70
181
  !isIgnoredLine(content, match.index ?? 0) &&
182
+ isJsxTagEnd(content, match.index ?? 0) &&
71
183
  !isCodeElementText(content, match.index ?? 0)
72
184
  ) {
73
185
  violations.push({
@@ -422,11 +422,19 @@ if (readPnpmConfig('minimumReleaseAgeIgnoreMissingTime') !== false) {
422
422
  console.error('pnpm minimumReleaseAgeIgnoreMissingTime must be false');
423
423
  process.exit(1);
424
424
  }
425
+ const expectedMinimumReleaseAgeExcludes = [
426
+ '@bleedingdev/modern-js-*',
427
+ '@tanstack/react-router',
428
+ '@tanstack/router-core',
429
+ '@typescript/native-preview',
430
+ '@typescript/native-preview-*',
431
+ '@types/react',
432
+ ];
425
433
  if (
426
434
  JSON.stringify(readPnpmConfig('minimumReleaseAgeExclude')) !==
427
- JSON.stringify(['@bleedingdev/modern-js-*'])
435
+ JSON.stringify(expectedMinimumReleaseAgeExcludes)
428
436
  ) {
429
- console.error('pnpm minimumReleaseAgeExclude must allow just-published BleedingDev Modern cohorts only');
437
+ console.error('pnpm minimumReleaseAgeExclude must allow only approved latest-lane package cohorts');
430
438
  process.exit(1);
431
439
  }
432
440
  if (readPnpmConfig('trustPolicy') !== 'no-downgrade') {
@@ -481,6 +489,11 @@ if (
481
489
  process.exit(1);
482
490
  }
483
491
 
492
+ if (packageJson.modernjs?.packageSource?.strategy !== packageSource.strategy) {
493
+ console.error('package.json package source strategy must match package source metadata');
494
+ process.exit(1);
495
+ }
496
+
484
497
  if (packageSource.schemaVersion !== 1) {
485
498
  console.error('Package source metadata must use schemaVersion 1');
486
499
  process.exit(1);
@@ -496,21 +509,33 @@ if (packageSource.strategy !== 'workspace' && packageSource.strategy !== 'instal
496
509
  process.exit(1);
497
510
  }
498
511
 
499
- const expectedModernPackages = [
500
- '@modern-js/runtime',
501
- '@modern-js/app-tools',
502
- '@modern-js/tsconfig',
503
- '@modern-js/plugin-i18n',
504
- '@modern-js/plugin-tanstack',
505
- '@modern-js/plugin-bff',
506
- '@modern-js/adapter-rstest',
507
- ];
512
+ const declaredModernPackages = packageSource.modernPackages?.packages;
513
+ if (!Array.isArray(declaredModernPackages) || declaredModernPackages.length === 0) {
514
+ console.error('Package source metadata must declare the generated Modern package cohort');
515
+ process.exit(1);
516
+ }
508
517
 
509
- for (const packageName of expectedModernPackages) {
510
- if (!packageSource.modernPackages?.packages?.includes(packageName)) {
511
- console.error(`Package source metadata must include ${packageName}`);
512
- process.exit(1);
513
- }
518
+ const invalidModernPackages = declaredModernPackages.filter(
519
+ (packageName) => typeof packageName !== 'string' || !packageName.startsWith('@modern-js/'),
520
+ );
521
+ if (invalidModernPackages.length > 0) {
522
+ console.error(`Package source metadata contains invalid Modern packages: ${invalidModernPackages.join(', ')}`);
523
+ process.exit(1);
524
+ }
525
+
526
+ const packageJsonModernDependencies = [
527
+ ...new Set(
528
+ ['dependencies', 'devDependencies']
529
+ .flatMap((section) => Object.keys(packageJson[section] ?? {}))
530
+ .filter((packageName) => packageName.startsWith('@modern-js/')),
531
+ ),
532
+ ];
533
+ const missingMetadataPackages = packageJsonModernDependencies.filter(
534
+ (packageName) => !declaredModernPackages.includes(packageName),
535
+ );
536
+ if (missingMetadataPackages.length > 0) {
537
+ console.error(`Package source metadata must include package.json Modern dependencies: ${missingMetadataPackages.join(', ')}`);
538
+ process.exit(1);
514
539
  }
515
540
 
516
541
  const expectedModernSpecifier = packageSource.modernPackages?.specifier;
@@ -535,32 +560,15 @@ const expectedModernDependency = (packageName) => {
535
560
  : expectedModernSpecifier;
536
561
  };
537
562
 
538
- for (const packageName of [
539
- '@modern-js/runtime',
540
- '@modern-js/plugin-i18n',
541
- '@modern-js/plugin-tanstack',
542
- ]) {
543
- if (
544
- packageJson.dependencies?.[packageName] &&
545
- packageJson.dependencies[packageName] !== expectedModernDependency(packageName)
546
- ) {
547
- console.error(`Dependency ${packageName} must match package source metadata`);
548
- process.exit(1);
549
- }
550
- }
551
-
552
- for (const packageName of [
553
- '@modern-js/app-tools',
554
- '@modern-js/adapter-rstest',
555
- '@modern-js/tsconfig',
556
- '@modern-js/plugin-bff',
557
- ]) {
558
- if (
559
- packageJson.devDependencies?.[packageName] &&
560
- packageJson.devDependencies[packageName] !== expectedModernDependency(packageName)
561
- ) {
562
- console.error(`Dev dependency ${packageName} must match package source metadata`);
563
- process.exit(1);
563
+ for (const section of ['dependencies', 'devDependencies']) {
564
+ for (const packageName of declaredModernPackages) {
565
+ if (
566
+ packageJson[section]?.[packageName] &&
567
+ packageJson[section][packageName] !== expectedModernDependency(packageName)
568
+ ) {
569
+ console.error(`${section}.${packageName} must match package source metadata`);
570
+ process.exit(1);
571
+ }
564
572
  }
565
573
  }
566
574
 
@@ -89,6 +89,7 @@ describe('generated UltraModern contract', () => {
89
89
  modernjs?: {
90
90
  packageSource?: {
91
91
  config?: string;
92
+ strategy?: string;
92
93
  };
93
94
  preset?: string;
94
95
  };
@@ -105,15 +106,20 @@ describe('generated UltraModern contract', () => {
105
106
  expect(packageJson.modernjs?.packageSource?.config).toBe(
106
107
  './.modernjs/ultramodern-package-source.json',
107
108
  );
108
- expect(packageSource.strategy).toMatch(/^(workspace|install)$/u);
109
- expect(packageSource.modernPackages?.packages).toContain(
110
- '@modern-js/runtime',
111
- );
112
- expect(packageSource.modernPackages?.packages).toContain(
113
- '@modern-js/app-tools',
109
+ expect(packageJson.modernjs?.packageSource?.strategy).toBe(
110
+ packageSource.strategy,
114
111
  );
115
- expect(packageSource.modernPackages?.packages).toContain(
116
- '@modern-js/adapter-rstest',
112
+ expect(packageSource.strategy).toMatch(/^(workspace|install)$/u);
113
+ const generatedModernDependencies = [
114
+ ...new Set(
115
+ (['dependencies', 'devDependencies'] as const).flatMap(section =>
116
+ Object.keys(packageJson[section] ?? {}),
117
+ ),
118
+ ),
119
+ ].filter(packageName => packageName.startsWith('@modern-js/'));
120
+ expect(packageSource.modernPackages?.packages?.length).toBeGreaterThan(0);
121
+ expect(packageSource.modernPackages?.packages).toEqual(
122
+ expect.arrayContaining(generatedModernDependencies),
117
123
  );
118
124
  expect(packageSource.modernPackages?.specifier).toBeTruthy();
119
125
  expect(
@@ -19,9 +19,18 @@ pnpm dlx @bleedingdev/modern-js-create payments --vertical
19
19
  ```
20
20
 
21
21
  Each added vertical owns its UI/routes, browser-safe Module Federation exposes,
22
- localized route metadata, CSS prefix, Effect BFF handlers, local API contract,
23
- and typed client surface. Server handlers and Effect client/contract modules
24
- stay out of browser exposes.
22
+ private-first route metadata, localized URLs, public-route opt-ins, CSS prefix,
23
+ Effect BFF handlers, local API contract, and typed client surface. Server
24
+ handlers and Effect client/contract modules stay out of browser exposes.
25
+
26
+ ## Private-First Public Surfaces
27
+
28
+ Generated app routes are private and non-indexable by default. Private app,
29
+ auth, tenant, dashboard, and internal routes publish no discovery output unless
30
+ route metadata explicitly marks them `public && indexable`. The default scaffold
31
+ therefore emits only a disallowing `robots.txt`; sitemap, web manifest,
32
+ `llms.txt`, API catalog, security.txt, and JSON-LD output stay omitted until a
33
+ safe public route or public docs/help/product surface exists.
25
34
 
26
35
  Run the scaffold validator before adding business code and after every
27
36
  `--vertical` mutation:
@@ -8,6 +8,11 @@ minimumReleaseAgeStrict: true
8
8
  minimumReleaseAgeIgnoreMissingTime: false
9
9
  minimumReleaseAgeExclude:
10
10
  - '@bleedingdev/modern-js-*'
11
+ - '@tanstack/react-router'
12
+ - '@tanstack/router-core'
13
+ - '@typescript/native-preview'
14
+ - '@typescript/native-preview-*'
15
+ - '@types/react'
11
16
  trustPolicy: no-downgrade
12
17
  trustPolicyIgnoreAfter: 1440
13
18
  blockExoticSubdeps: true
@@ -22,7 +27,7 @@ peerDependencyRules:
22
27
  typescript: '>=6.0.0'
23
28
 
24
29
  overrides:
25
- '@tanstack/react-router': 1.170.11
30
+ '@tanstack/react-router': 1.170.15
26
31
  node-fetch: '^3.3.2'
27
32
 
28
33
  allowBuilds:
@@ -122,6 +122,12 @@ function porcelainStatus() {
122
122
  return run('git', ['status', '--porcelain'], { timeout: 30000 });
123
123
  }
124
124
 
125
+ function commitInstallerChanges(message) {
126
+ run('git', ['commit', '--no-verify', '-m', message], {
127
+ timeout: 120000,
128
+ });
129
+ }
130
+
125
131
  function ensureGitRepository() {
126
132
  if (!isGitWorkTree()) {
127
133
  if (checkOnly) {
@@ -139,9 +145,7 @@ function ensureGitRepository() {
139
145
  }
140
146
  log('creating initial workspace commit before adding reference subtrees');
141
147
  run('git', ['add', '-A'], { timeout: 30000 });
142
- run('git', ['commit', '-m', 'Initialize UltraModern workspace'], {
143
- timeout: 120000,
144
- });
148
+ commitInstallerChanges('Initialize UltraModern workspace');
145
149
  return true;
146
150
  }
147
151
 
@@ -311,9 +315,7 @@ function commitManifestIfChanged() {
311
315
  return;
312
316
  }
313
317
  run('git', ['add', manifestPath], { timeout: 30000 });
314
- run('git', ['commit', '-m', 'Record agent reference repo manifest'], {
315
- timeout: 120000,
316
- });
318
+ commitInstallerChanges('Record agent reference repo manifest');
317
319
  }
318
320
 
319
321
  function main() {