@bleedingdev/modern-js-create 3.2.0-ultramodern.7 → 3.2.0-ultramodern.8
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/dist/index.js +202 -34
- package/package.json +3 -3
- package/template/.agents/skills-lock.json +34 -0
- package/template/AGENTS.md +20 -0
- package/template/api/effect/index.ts.handlebars +7 -39
- package/template/modern.config.ts.handlebars +8 -7
- package/template/oxfmt.config.ts +7 -0
- package/template/oxlint.config.ts +12 -0
- package/template/package.json.handlebars +21 -11
- package/template/scripts/bootstrap-agent-skills.mjs +103 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +84 -0
- package/template/tsconfig.json +106 -2
- package/template-workspace/.agents/skills-lock.json +39 -0
- package/template-workspace/AGENTS.md +18 -1
- package/template-workspace/oxfmt.config.ts +7 -0
- package/template-workspace/oxlint.config.ts +12 -0
- package/template-workspace/pnpm-workspace.yaml +0 -2
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +106 -0
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +60 -0
- package/template/biome.json +0 -41
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
"dev": "modern dev",
|
|
7
7
|
"build": "modern build",
|
|
8
8
|
"serve": "modern serve",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"typecheck": "node -e \"const fs = require('node:fs'); const { execFileSync, spawnSync } = require('node:child_process'); const bin = execFileSync('effect-tsgo', ['get-exe-path'], { encoding: 'utf8' }).trim(); if (process.platform !== 'win32') fs.chmodSync(bin, 0o755); const result = spawnSync(bin, ['--noEmit', '-p', 'tsconfig.json'], { stdio: 'inherit' }); process.exit(result.status ?? 1);\"",
|
|
10
|
+
"skills:install": "node ./scripts/bootstrap-agent-skills.mjs",
|
|
11
|
+
"skills:check": "node ./scripts/bootstrap-agent-skills.mjs --check",
|
|
12
|
+
"ultramodern:check": "{{#unless isSubproject}}pnpm format:check && pnpm lint && {{/unless}}pnpm typecheck{{#unless isSubproject}} && pnpm skills:check{{/unless}} && node ./scripts/validate-ultramodern.mjs"{{#unless isSubproject}},
|
|
13
|
+
"format": "oxfmt .",
|
|
14
|
+
"format:check": "oxfmt --check .",
|
|
15
|
+
"lint": "oxlint .",
|
|
16
|
+
"lint:fix": "oxlint . --fix",
|
|
11
17
|
"prepare": "simple-git-hooks"{{/unless}}
|
|
12
18
|
},
|
|
13
19
|
"engines": {
|
|
@@ -15,31 +21,35 @@
|
|
|
15
21
|
}{{#unless isSubproject}},
|
|
16
22
|
"lint-staged": {
|
|
17
23
|
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
|
|
18
|
-
"
|
|
24
|
+
"oxfmt --write",
|
|
25
|
+
"oxlint --fix"
|
|
19
26
|
]
|
|
20
27
|
},
|
|
21
28
|
"simple-git-hooks": {
|
|
22
29
|
"pre-commit": "npx lint-staged"
|
|
23
30
|
}{{/unless}},
|
|
24
31
|
"dependencies": {
|
|
25
|
-
"@modern-js/runtime": "{{
|
|
26
|
-
"@modern-js/plugin-tanstack": "{{
|
|
32
|
+
"@modern-js/runtime": "{{runtimeVersion}}"{{#if isTanstackRouter}},
|
|
33
|
+
"@modern-js/plugin-tanstack": "{{pluginTanstackVersion}}",
|
|
27
34
|
"@tanstack/react-router": "1.170.1"{{/if}},
|
|
28
35
|
"react": "^19.2.3",
|
|
29
36
|
"react-dom": "^19.2.0"
|
|
30
37
|
},
|
|
31
38
|
"devDependencies": {
|
|
32
|
-
"@modern-js/app-tools": "{{
|
|
33
|
-
"@modern-js/tsconfig": "{{
|
|
34
|
-
"@modern-js/plugin-bff": "{{
|
|
39
|
+
"@modern-js/app-tools": "{{appToolsVersion}}",
|
|
40
|
+
"@modern-js/tsconfig": "{{tsconfigVersion}}"{{#if enableBff}},
|
|
41
|
+
"@modern-js/plugin-bff": "{{pluginBffVersion}}"{{/if}}{{#if enableTailwind}},
|
|
35
42
|
"@tailwindcss/postcss": "^4.1.18",
|
|
36
43
|
"postcss": "^8.5.6",
|
|
37
|
-
"tailwindcss": "^4.1.18"{{/if}}
|
|
38
|
-
"@
|
|
39
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
44
|
+
"tailwindcss": "^4.1.18"{{/if}},
|
|
45
|
+
"@effect/tsgo": "0.7.3",
|
|
46
|
+
"@typescript/native-preview": "7.0.0-dev.20260518.1",
|
|
40
47
|
"@types/node": "^20",
|
|
41
48
|
"@types/react": "^19.1.8",
|
|
42
49
|
"@types/react-dom": "^19.1.6"{{#unless isSubproject}},
|
|
50
|
+
"oxlint": "1.65.0",
|
|
51
|
+
"oxfmt": "0.50.0",
|
|
52
|
+
"ultracite": "7.7.0",
|
|
43
53
|
"lint-staged": "~15.4.0",
|
|
44
54
|
"simple-git-hooks": "^2.11.1"{{/unless}},
|
|
45
55
|
"rimraf": "^6.0.1"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
8
|
+
const checkOnly = process.argv.includes('--check');
|
|
9
|
+
const force = process.argv.includes('--force');
|
|
10
|
+
|
|
11
|
+
function readJson(filePath) {
|
|
12
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function run(command, args, options = {}) {
|
|
16
|
+
return execFileSync(command, args, {
|
|
17
|
+
cwd: options.cwd ?? root,
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cloneSource(source, targetDir) {
|
|
24
|
+
const repo = source.repository.replace(/^https:\/\/github.com\//, '');
|
|
25
|
+
try {
|
|
26
|
+
run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
|
|
27
|
+
stdio: 'inherit',
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
} catch {
|
|
31
|
+
run('git', ['clone', '--depth', '1', source.repository, targetDir], {
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveSkillDir(sourceRoot, skillName) {
|
|
38
|
+
const candidates = [
|
|
39
|
+
path.join(sourceRoot, skillName),
|
|
40
|
+
path.join(sourceRoot, 'skills', skillName),
|
|
41
|
+
path.join(sourceRoot, 'skills', 'engineering', skillName),
|
|
42
|
+
path.join(sourceRoot, 'skills', 'productivity', skillName),
|
|
43
|
+
];
|
|
44
|
+
return candidates.find(candidate =>
|
|
45
|
+
fs.existsSync(path.join(candidate, 'SKILL.md')),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(lockPath)) {
|
|
50
|
+
console.error('Missing .agents/skills-lock.json');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const lock = readJson(lockPath);
|
|
55
|
+
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
56
|
+
const privateSources = (lock.sources ?? []).filter(
|
|
57
|
+
source => source.install === 'clone-if-authorized',
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (checkOnly) {
|
|
61
|
+
const missing = privateSources.flatMap(source =>
|
|
62
|
+
(source.baseline ?? [])
|
|
63
|
+
.map(skill => skill.name)
|
|
64
|
+
.filter(skillName => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
|
|
65
|
+
);
|
|
66
|
+
if (missing.length > 0) {
|
|
67
|
+
console.warn(
|
|
68
|
+
`Private skills not installed: ${missing.join(', ')}. Run pnpm skills:install if you have access.`,
|
|
69
|
+
);
|
|
70
|
+
} else {
|
|
71
|
+
console.log('Agent skills are installed.');
|
|
72
|
+
}
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
77
|
+
|
|
78
|
+
for (const source of privateSources) {
|
|
79
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
|
|
80
|
+
try {
|
|
81
|
+
cloneSource(source, tempDir);
|
|
82
|
+
for (const skill of source.baseline ?? []) {
|
|
83
|
+
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
84
|
+
if (!sourceSkillDir) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Skill ${skill.name} not found in ${source.repository}`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const targetSkillDir = path.join(installDir, skill.name);
|
|
90
|
+
if (fs.existsSync(targetSkillDir)) {
|
|
91
|
+
if (!force) {
|
|
92
|
+
console.log(`Skipping existing ${skill.name}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
fs.rmSync(targetSkillDir, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
|
|
98
|
+
console.log(`Installed ${skill.name}`);
|
|
99
|
+
}
|
|
100
|
+
} finally {
|
|
101
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -52,8 +52,22 @@ const requiredPostMaterialization = [
|
|
|
52
52
|
'dependency-install-with-lifecycle-deny',
|
|
53
53
|
'template-manifest-retained',
|
|
54
54
|
];
|
|
55
|
+
const requiredPaths = [
|
|
56
|
+
'AGENTS.md',
|
|
57
|
+
'.agents/skills-lock.json',
|
|
58
|
+
'oxlint.config.ts',
|
|
59
|
+
'oxfmt.config.ts',
|
|
60
|
+
'scripts/bootstrap-agent-skills.mjs',
|
|
61
|
+
];
|
|
55
62
|
const manifestErrors = [];
|
|
56
63
|
|
|
64
|
+
for (const requiredPath of requiredPaths) {
|
|
65
|
+
if (!fs.existsSync(path.resolve(process.cwd(), requiredPath))) {
|
|
66
|
+
console.error(`${requiredPath} not found`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
if (templateManifest.schemaVersion !== 1) {
|
|
58
72
|
manifestErrors.push('schemaVersion');
|
|
59
73
|
}
|
|
@@ -99,4 +113,74 @@ if (manifestErrors.length > 0) {
|
|
|
99
113
|
process.exit(1);
|
|
100
114
|
}
|
|
101
115
|
|
|
116
|
+
const packageJson = JSON.parse(
|
|
117
|
+
fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf8'),
|
|
118
|
+
);
|
|
119
|
+
const unresolvedTemplateMarker = '{' + '{';
|
|
120
|
+
if (JSON.stringify(packageJson).includes(unresolvedTemplateMarker)) {
|
|
121
|
+
console.error('package.json contains unresolved template markers');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const skillsLock = JSON.parse(
|
|
125
|
+
fs.readFileSync(
|
|
126
|
+
path.resolve(process.cwd(), '.agents/skills-lock.json'),
|
|
127
|
+
'utf8',
|
|
128
|
+
),
|
|
129
|
+
);
|
|
130
|
+
const requiredScripts = {
|
|
131
|
+
format: 'oxfmt .',
|
|
132
|
+
'format:check': 'oxfmt --check .',
|
|
133
|
+
lint: 'oxlint .',
|
|
134
|
+
'lint:fix': 'oxlint . --fix',
|
|
135
|
+
'skills:install': 'node ./scripts/bootstrap-agent-skills.mjs',
|
|
136
|
+
'skills:check': 'node ./scripts/bootstrap-agent-skills.mjs --check',
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
for (const [scriptName, scriptCommand] of Object.entries(requiredScripts)) {
|
|
140
|
+
if (packageJson.scripts?.[scriptName] !== scriptCommand) {
|
|
141
|
+
console.error(`Missing or invalid package script: ${scriptName}`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (
|
|
147
|
+
!packageJson.scripts?.typecheck?.includes('effect-tsgo') ||
|
|
148
|
+
!packageJson.scripts.typecheck.includes('get-exe-path')
|
|
149
|
+
) {
|
|
150
|
+
console.error('typecheck must use effect-tsgo as the TypeScript checker');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
for (const dependency of [
|
|
155
|
+
'@effect/tsgo',
|
|
156
|
+
'@typescript/native-preview',
|
|
157
|
+
'oxlint',
|
|
158
|
+
'oxfmt',
|
|
159
|
+
'ultracite',
|
|
160
|
+
]) {
|
|
161
|
+
if (!packageJson.devDependencies?.[dependency]) {
|
|
162
|
+
console.error(`Missing devDependency: ${dependency}`);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const privateSource = skillsLock.sources?.find(
|
|
168
|
+
source => source.repository === 'https://github.com/TechsioCZ/skills',
|
|
169
|
+
);
|
|
170
|
+
const privateSkills = new Set(
|
|
171
|
+
privateSource?.baseline?.map(skill => skill.name) ?? [],
|
|
172
|
+
);
|
|
173
|
+
for (const skillName of [
|
|
174
|
+
'plan-graph',
|
|
175
|
+
'dag',
|
|
176
|
+
'subagent-graph',
|
|
177
|
+
'helm',
|
|
178
|
+
'debugger-mode',
|
|
179
|
+
]) {
|
|
180
|
+
if (!privateSkills.has(skillName)) {
|
|
181
|
+
console.error(`Missing private skill allowlist entry: ${skillName}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
102
186
|
console.log('Ultramodern contract check passed.');
|
package/template/tsconfig.json
CHANGED
|
@@ -3,13 +3,117 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"declaration": false,
|
|
5
5
|
"jsx": "preserve",
|
|
6
|
-
"
|
|
6
|
+
"target": "ESNext",
|
|
7
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
8
|
+
"module": "preserve",
|
|
9
|
+
"moduleResolution": "Bundler",
|
|
10
|
+
"moduleDetection": "force",
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"allowJs": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"esModuleInterop": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUncheckedIndexedAccess": true,
|
|
20
|
+
"exactOptionalPropertyTypes": true,
|
|
21
|
+
"noImplicitOverride": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
24
|
+
"noImplicitReturns": true,
|
|
7
25
|
"paths": {
|
|
8
26
|
"@/*": ["./src/*"],
|
|
9
27
|
"@api/*": ["./api/*"],
|
|
10
28
|
"@shared/*": ["./shared/*"]
|
|
11
29
|
},
|
|
12
|
-
"rootDir": "
|
|
30
|
+
"rootDir": ".",
|
|
31
|
+
"plugins": [
|
|
32
|
+
{
|
|
33
|
+
"name": "@effect/language-service",
|
|
34
|
+
"diagnostics": true,
|
|
35
|
+
"includeSuggestionsInTsc": true,
|
|
36
|
+
"ignoreEffectSuggestionsInTscExitCode": false,
|
|
37
|
+
"ignoreEffectWarningsInTscExitCode": false,
|
|
38
|
+
"ignoreEffectErrorsInTscExitCode": false,
|
|
39
|
+
"skipDisabledOptimization": true,
|
|
40
|
+
"diagnosticSeverity": {
|
|
41
|
+
"anyUnknownInErrorContext": "error",
|
|
42
|
+
"classSelfMismatch": "error",
|
|
43
|
+
"duplicatePackage": "error",
|
|
44
|
+
"effectFnImplicitAny": "error",
|
|
45
|
+
"floatingEffect": "error",
|
|
46
|
+
"genericEffectServices": "error",
|
|
47
|
+
"missingEffectContext": "error",
|
|
48
|
+
"missingEffectError": "error",
|
|
49
|
+
"missingLayerContext": "error",
|
|
50
|
+
"missingReturnYieldStar": "error",
|
|
51
|
+
"missingStarInYieldEffectGen": "error",
|
|
52
|
+
"nonObjectEffectServiceType": "error",
|
|
53
|
+
"outdatedApi": "error",
|
|
54
|
+
"overriddenSchemaConstructor": "error",
|
|
55
|
+
"catchUnfailableEffect": "error",
|
|
56
|
+
"effectFnIife": "error",
|
|
57
|
+
"effectGenUsesAdapter": "error",
|
|
58
|
+
"effectInFailure": "error",
|
|
59
|
+
"effectInVoidSuccess": "error",
|
|
60
|
+
"globalErrorInEffectCatch": "error",
|
|
61
|
+
"globalErrorInEffectFailure": "error",
|
|
62
|
+
"layerMergeAllWithDependencies": "error",
|
|
63
|
+
"lazyPromiseInEffectSync": "error",
|
|
64
|
+
"leakingRequirements": "error",
|
|
65
|
+
"multipleEffectProvide": "error",
|
|
66
|
+
"returnEffectInGen": "error",
|
|
67
|
+
"runEffectInsideEffect": "error",
|
|
68
|
+
"schemaSyncInEffect": "error",
|
|
69
|
+
"scopeInLayerEffect": "error",
|
|
70
|
+
"strictEffectProvide": "error",
|
|
71
|
+
"tryCatchInEffectGen": "error",
|
|
72
|
+
"unknownInEffectCatch": "error",
|
|
73
|
+
"asyncFunction": "error",
|
|
74
|
+
"cryptoRandomUUID": "error",
|
|
75
|
+
"cryptoRandomUUIDInEffect": "error",
|
|
76
|
+
"extendsNativeError": "error",
|
|
77
|
+
"globalConsole": "error",
|
|
78
|
+
"globalConsoleInEffect": "error",
|
|
79
|
+
"globalDate": "error",
|
|
80
|
+
"globalDateInEffect": "error",
|
|
81
|
+
"globalFetch": "error",
|
|
82
|
+
"globalFetchInEffect": "error",
|
|
83
|
+
"globalRandom": "error",
|
|
84
|
+
"globalRandomInEffect": "error",
|
|
85
|
+
"globalTimers": "error",
|
|
86
|
+
"globalTimersInEffect": "error",
|
|
87
|
+
"instanceOfSchema": "error",
|
|
88
|
+
"newPromise": "error",
|
|
89
|
+
"nodeBuiltinImport": "error",
|
|
90
|
+
"preferSchemaOverJson": "error",
|
|
91
|
+
"processEnv": "error",
|
|
92
|
+
"processEnvInEffect": "error",
|
|
93
|
+
"unsafeEffectTypeAssertion": "error",
|
|
94
|
+
"catchAllToMapError": "error",
|
|
95
|
+
"deterministicKeys": "error",
|
|
96
|
+
"effectDoNotation": "error",
|
|
97
|
+
"effectFnOpportunity": "error",
|
|
98
|
+
"effectMapFlatten": "error",
|
|
99
|
+
"effectMapVoid": "error",
|
|
100
|
+
"effectSucceedWithVoid": "error",
|
|
101
|
+
"missedPipeableOpportunity": "error",
|
|
102
|
+
"missingEffectServiceDependency": "error",
|
|
103
|
+
"nestedEffectGenYield": "error",
|
|
104
|
+
"redundantSchemaTagIdentifier": "error",
|
|
105
|
+
"schemaStructWithTag": "error",
|
|
106
|
+
"schemaUnionOfLiterals": "error",
|
|
107
|
+
"serviceNotAsClass": "error",
|
|
108
|
+
"strictBooleanExpressions": "error",
|
|
109
|
+
"unnecessaryArrowBlock": "error",
|
|
110
|
+
"unnecessaryEffectGen": "error",
|
|
111
|
+
"unnecessaryFailYieldableError": "error",
|
|
112
|
+
"unnecessaryPipe": "error",
|
|
113
|
+
"unnecessaryPipeChain": "error"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
]
|
|
13
117
|
},
|
|
14
118
|
"include": ["src", "api", "shared", "config", "modern.config.ts"],
|
|
15
119
|
"exclude": ["**/node_modules"]
|
|
@@ -7,6 +7,45 @@
|
|
|
7
7
|
"licensePath": ".agents/rstackjs-agent-skills-LICENSE"
|
|
8
8
|
},
|
|
9
9
|
"installDir": ".agents/skills",
|
|
10
|
+
"sources": [
|
|
11
|
+
{
|
|
12
|
+
"id": "rstack-agent-skills",
|
|
13
|
+
"visibility": "public",
|
|
14
|
+
"repository": "https://github.com/rstackjs/agent-skills",
|
|
15
|
+
"commit": "61c948b42512e223bad44b83af4080eba48b2677",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"licensePath": ".agents/rstackjs-agent-skills-LICENSE",
|
|
18
|
+
"install": "vendored"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "techsiocz-private",
|
|
22
|
+
"visibility": "private",
|
|
23
|
+
"repository": "https://github.com/TechsioCZ/skills",
|
|
24
|
+
"install": "clone-if-authorized",
|
|
25
|
+
"baseline": [
|
|
26
|
+
{
|
|
27
|
+
"name": "plan-graph",
|
|
28
|
+
"reason": "Build and validate DAGs from .plan.md files"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "dag",
|
|
32
|
+
"reason": "Inspect current plan frontiers and blocked lanes"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "subagent-graph",
|
|
36
|
+
"reason": "Design dependency-aware multi-agent launch graphs"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "helm",
|
|
40
|
+
"reason": "Steer already-running multi-agent work"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"name": "debugger-mode",
|
|
44
|
+
"reason": "Run hypothesis-driven debugging with runtime evidence"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
],
|
|
10
49
|
"baseline": [
|
|
11
50
|
{
|
|
12
51
|
"name": "rsbuild-best-practices",
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This workspace is generated as an agent-ready UltraModern.js SuperApp. Agents should treat the files under `.agents/skills` as local project instructions, not optional reading.
|
|
4
4
|
|
|
5
|
+
## Quality Gates
|
|
6
|
+
|
|
7
|
+
- `pnpm lint` runs Oxlint with the Ultracite preset.
|
|
8
|
+
- `pnpm format` runs oxfmt.
|
|
9
|
+
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
10
|
+
- `pnpm check` runs formatting, linting, effect-tsgo, private-skill availability checks, and the generated workspace contract.
|
|
11
|
+
|
|
5
12
|
## Required Skill Baseline
|
|
6
13
|
|
|
7
14
|
Use these skills when the task touches the matching subsystem:
|
|
@@ -14,6 +21,16 @@ Use these skills when the task touches the matching subsystem:
|
|
|
14
21
|
- `rslib-modern-package`: Package contracts for shared libraries, exports, side effects, dependency placement, README, and release readiness.
|
|
15
22
|
- `rstest-best-practices`: Rstest configuration, test writing, mocking, snapshots, coverage, and CI test behavior.
|
|
16
23
|
|
|
24
|
+
## Private Skills
|
|
25
|
+
|
|
26
|
+
ScriptedAlchemy/TechsioCZ skills are private and are cloned only when the current developer is authorized for `TechsioCZ/skills`.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm skills:install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The installer copies only the allowlisted private skills from `.agents/skills-lock.json`: `plan-graph`, `dag`, `subagent-graph`, `helm`, and `debugger-mode`.
|
|
33
|
+
|
|
17
34
|
## Project Priorities
|
|
18
35
|
|
|
19
36
|
- Keep `presetUltramodern` as the single preset.
|
|
@@ -25,4 +42,4 @@ Use these skills when the task touches the matching subsystem:
|
|
|
25
42
|
|
|
26
43
|
## Skill Provenance
|
|
27
44
|
|
|
28
|
-
The vendored Rstack skills are pinned in `.agents/skills-lock.json`. Do not update, remove, or replace them casually. If a skill needs updating, update the lock file and run `pnpm
|
|
45
|
+
The vendored Rstack skills and private TechsioCZ skill allowlist are pinned in `.agents/skills-lock.json`. Do not update, remove, or replace them casually. If a skill needs updating, update the lock file and run `pnpm check`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "oxlint";
|
|
2
|
+
import core from "ultracite/oxlint/core";
|
|
3
|
+
import react from "ultracite/oxlint/react";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
env: {
|
|
7
|
+
browser: true,
|
|
8
|
+
node: true,
|
|
9
|
+
},
|
|
10
|
+
extends: [core, react],
|
|
11
|
+
ignorePatterns: ["dist", "node_modules", ".modern", ".modernjs", "**/routeTree.gen.ts"],
|
|
12
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
8
|
+
const checkOnly = process.argv.includes('--check');
|
|
9
|
+
const force = process.argv.includes('--force');
|
|
10
|
+
|
|
11
|
+
function readJson(filePath) {
|
|
12
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function run(command, args, options = {}) {
|
|
16
|
+
return execFileSync(command, args, {
|
|
17
|
+
cwd: options.cwd ?? root,
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cloneSource(source, targetDir) {
|
|
24
|
+
const repo = source.repository.replace(/^https:\/\/github.com\//, '');
|
|
25
|
+
try {
|
|
26
|
+
run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
|
|
27
|
+
stdio: 'inherit',
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
} catch {
|
|
31
|
+
run('git', ['clone', '--depth', '1', source.repository, targetDir], {
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveSkillDir(sourceRoot, skillName) {
|
|
38
|
+
const candidates = [
|
|
39
|
+
path.join(sourceRoot, skillName),
|
|
40
|
+
path.join(sourceRoot, 'skills', skillName),
|
|
41
|
+
path.join(sourceRoot, 'skills', 'engineering', skillName),
|
|
42
|
+
path.join(sourceRoot, 'skills', 'productivity', skillName),
|
|
43
|
+
];
|
|
44
|
+
return candidates.find(candidate =>
|
|
45
|
+
fs.existsSync(path.join(candidate, 'SKILL.md')),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(lockPath)) {
|
|
50
|
+
console.error('Missing .agents/skills-lock.json');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const lock = readJson(lockPath);
|
|
55
|
+
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
56
|
+
const privateSources = (lock.sources ?? []).filter(
|
|
57
|
+
source => source.install === 'clone-if-authorized',
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (checkOnly) {
|
|
61
|
+
const missing = privateSources.flatMap(source =>
|
|
62
|
+
(source.baseline ?? [])
|
|
63
|
+
.map(skill => skill.name)
|
|
64
|
+
.filter(
|
|
65
|
+
skillName =>
|
|
66
|
+
!fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
if (missing.length > 0) {
|
|
70
|
+
console.warn(
|
|
71
|
+
`Private skills not installed: ${missing.join(', ')}. Run pnpm skills:install if you have access.`,
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
console.log('Agent skills are installed.');
|
|
75
|
+
}
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
fs.mkdirSync(installDir, { recursive: true });
|
|
80
|
+
|
|
81
|
+
for (const source of privateSources) {
|
|
82
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
|
|
83
|
+
try {
|
|
84
|
+
cloneSource(source, tempDir);
|
|
85
|
+
for (const skill of source.baseline ?? []) {
|
|
86
|
+
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
87
|
+
if (!sourceSkillDir) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Skill ${skill.name} not found in ${source.repository}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const targetSkillDir = path.join(installDir, skill.name);
|
|
93
|
+
if (fs.existsSync(targetSkillDir)) {
|
|
94
|
+
if (!force) {
|
|
95
|
+
console.log(`Skipping existing ${skill.name}`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
fs.rmSync(targetSkillDir, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
|
|
101
|
+
console.log(`Installed ${skill.name}`);
|
|
102
|
+
}
|
|
103
|
+
} finally {
|
|
104
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
}
|