@rtorcato/repo-tooling 4.1.0 → 4.3.0
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/AGENTS.md +1 -1
- package/README.md +12 -8
- package/dist/base/checks.js +5 -0
- package/dist/base/fixers.js +5 -0
- package/dist/base/github-settings.js +3 -3
- package/dist/cli/commands/fix-targets.js +4 -0
- package/dist/cli/commands/setup-presets.js +4 -0
- package/dist/cli/commands/setup.js +9 -0
- package/dist/cli/generators/docs-site.js +106 -148
- package/dist/cli/generators/index.js +6 -0
- package/dist/cli/generators/security.js +45 -0
- package/dist/cli/index.js +6 -34
- package/dist/cli/self-repo.js +47 -0
- package/dist/cli/utils/lockfile.js +4 -4
- package/dist/languages/js/checks.js +2 -2
- package/dist/languages/js/fixers.js +3 -0
- package/package.json +2 -1
package/AGENTS.md
CHANGED
|
@@ -8,7 +8,7 @@ A one-package JavaScript / TypeScript tooling distribution. Ships every preset (
|
|
|
8
8
|
|
|
9
9
|
**Swift** repos (detected via `Package.swift`) are covered end to end: `setup --preset swift-library` scaffolds a SwiftPM package, and `doctor`/`fix` run the language-agnostic checks plus SwiftLint / Periphery / `.gitignore` / `Package.swift`. **Python** repos (detected via `pyproject.toml` / `setup.py`) get `doctor`/`fix` — Ruff / mypy / pytest / `.gitignore` / CI / git hooks — but no `setup` preset yet. **Perl** distributions (detected via `cpanfile` / `Makefile.PL` / `dist.ini`) get the same deal: Perl::Critic / perltidy / `.gitignore` / CI / git hooks, no `setup` preset. See `src/languages/` — one directory per language module, `src/base/` for what's shared.
|
|
10
10
|
|
|
11
|
-
The **ai-issue-loop** pipeline (the `loop` commands, its skills, and the label / agent-user / skills checks)
|
|
11
|
+
repo-tooling stands alone. The **ai-issue-loop** pipeline (the `loop` commands, its skills, and the label / agent-user / skills checks) is an optional companion package, [`@rtorcato/repo-ai`](https://rtorcato.github.io/repo-ai/), since #658. Nothing here requires it or checks for it; `repo-tooling loop` only prints a pointer there. Its settings sit in this package's `.repo-tooling.json` under `rules.aiLoop` and `rules.requiredSkills`, which repo-tooling carries forward without reading. See `apps/docs/docs/guides/ai-issue-loop.md` for how the two fit together.
|
|
12
12
|
|
|
13
13
|
## CLI surface (agent-friendly)
|
|
14
14
|
|
package/README.md
CHANGED
|
@@ -137,6 +137,7 @@ full field reference. The keys `doctor` acts on:
|
|
|
137
137
|
- `securityAutomation` — boolean (Dependabot + CodeQL)
|
|
138
138
|
- `bundler` — `tsup` \| `esbuild` \| `vite` \| `none`
|
|
139
139
|
- `aiSetup` — boolean (AGENTS.md, CLAUDE.md, Cursor/Copilot rules)
|
|
140
|
+
- `brand` — boolean (`brand/` banner + social-card SVG sources and `render.sh`)
|
|
140
141
|
|
|
141
142
|
**How opt-outs actually work.** When the lockfile records that you declined an
|
|
142
143
|
*optional* tool (e.g. `securityAutomation: false`), `doctor` demotes that check
|
|
@@ -190,19 +191,22 @@ throwaway fixtures) — in any session:
|
|
|
190
191
|
/plugin install repo-tooling@repo-tooling
|
|
191
192
|
```
|
|
192
193
|
|
|
193
|
-
###
|
|
194
|
+
### Optional: agents on your issue queue (repo-ai)
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
repo-tooling works entirely on its own. If you also want agents working your
|
|
197
|
+
GitHub issues, the optional companion
|
|
198
|
+
[`@rtorcato/repo-ai`](https://rtorcato.github.io/repo-ai/) runs a label-driven
|
|
199
|
+
loop: an `ai-ready` issue becomes a worktree, a PR and two agent reviews, then
|
|
200
|
+
waits for you to merge. It builds on the repo standard repo-tooling sets up
|
|
201
|
+
(branch protection, the release gate, worktree config) and keeps its settings in
|
|
202
|
+
the same `.repo-tooling.json`:
|
|
200
203
|
|
|
201
204
|
```bash
|
|
202
|
-
npx @rtorcato/repo-ai
|
|
205
|
+
npx @rtorcato/repo-ai setup
|
|
203
206
|
```
|
|
204
207
|
|
|
205
|
-
|
|
208
|
+
See [Using with repo-ai](https://rtorcato.github.io/repo-tooling/guides/ai-issue-loop/)
|
|
209
|
+
for how the two fit together. The loop shipped inside repo-tooling until 4.0.0.
|
|
206
210
|
|
|
207
211
|
### Use with other AI tools (Cursor / Copilot / Codex)
|
|
208
212
|
|
package/dist/base/checks.js
CHANGED
|
@@ -266,6 +266,11 @@ export async function checkDependabot(dir) {
|
|
|
266
266
|
if (!automerge.includes("package-ecosystem == 'github-actions'")) {
|
|
267
267
|
deltas.push('auto-merge workflow leaves CI action bumps to a human (#452)');
|
|
268
268
|
}
|
|
269
|
+
// #694: a declined PR used to get only a log notice — no assignee, no
|
|
270
|
+
// comment — and could sit green and ownerless indefinitely.
|
|
271
|
+
if (!automerge.includes("steps.merge.outcome == 'skipped'")) {
|
|
272
|
+
deltas.push('auto-merge workflow leaves declined PRs with no owner (#694)');
|
|
273
|
+
}
|
|
269
274
|
}
|
|
270
275
|
else {
|
|
271
276
|
deltas.push('missing dependabot-automerge workflow');
|
package/dist/base/fixers.js
CHANGED
|
@@ -75,6 +75,7 @@ export const BASE_FIXERS = [
|
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
target: 'editorconfig',
|
|
78
|
+
selfSafe: true,
|
|
78
79
|
description: 'Scaffold .editorconfig (UTF-8, LF, tab indent)',
|
|
79
80
|
appliesTo: ['EditorConfig'],
|
|
80
81
|
outputs: ['.editorconfig'],
|
|
@@ -145,6 +146,7 @@ export const BASE_FIXERS = [
|
|
|
145
146
|
},
|
|
146
147
|
{
|
|
147
148
|
target: 'codeql',
|
|
149
|
+
selfSafe: true,
|
|
148
150
|
description: 'Scaffold .github/workflows/codeql.yml (security scanning)',
|
|
149
151
|
appliesTo: ['CodeQL'],
|
|
150
152
|
outputs: ['.github/workflows/codeql.yml'],
|
|
@@ -214,6 +216,7 @@ export const BASE_FIXERS = [
|
|
|
214
216
|
},
|
|
215
217
|
{
|
|
216
218
|
target: 'codeowners',
|
|
219
|
+
selfSafe: true,
|
|
217
220
|
description: 'Scaffold .github/CODEOWNERS with commented examples',
|
|
218
221
|
appliesTo: ['CODEOWNERS'],
|
|
219
222
|
outputs: ['.github/CODEOWNERS'],
|
|
@@ -226,6 +229,7 @@ export const BASE_FIXERS = [
|
|
|
226
229
|
},
|
|
227
230
|
{
|
|
228
231
|
target: 'community-health',
|
|
232
|
+
selfSafe: true,
|
|
229
233
|
description: 'Scaffold CONTRIBUTING.md, SECURITY.md, PR + issue templates',
|
|
230
234
|
appliesTo: ['Community health'],
|
|
231
235
|
outputs: [
|
|
@@ -244,6 +248,7 @@ export const BASE_FIXERS = [
|
|
|
244
248
|
},
|
|
245
249
|
{
|
|
246
250
|
target: 'brand',
|
|
251
|
+
selfSafe: true,
|
|
247
252
|
description: 'Scaffold brand/ — banner, mobile-banner and social-card SVG sources + render.sh, and repoint a README still on root-level banner paths',
|
|
248
253
|
appliesTo: ['Brand assets'],
|
|
249
254
|
outputs: [
|
|
@@ -217,7 +217,7 @@ async function checkBranchProtection(exec, nwo, branch) {
|
|
|
217
217
|
* enabled only makes them available on the merge button, and one mis-click puts
|
|
218
218
|
* every intermediate branch commit on the default branch: semantic-release then
|
|
219
219
|
* reads subjects nobody reviewed (a stray `fix:` inside a docs PR cuts a
|
|
220
|
-
* release), and `ai-
|
|
220
|
+
* release), and repo-ai's `ai-loop` cleanup stops finding the `(#N)` squash subject
|
|
221
221
|
* it uses to confirm work landed, so worktrees leak. Observed on `js-common`
|
|
222
222
|
* #204, whose CONTRIBUTING already said squash-only — the rule existed, nothing
|
|
223
223
|
* enforced it.
|
|
@@ -706,8 +706,8 @@ const PROTECTION_BODY = JSON.stringify({
|
|
|
706
706
|
// any required_pull_request_reviews as drift, so `fix github-settings` PUTs it
|
|
707
707
|
// back to null. A repo that deliberately requires approvals will have that
|
|
708
708
|
// silently reverted by the next unrelated fix run, with nothing in the output
|
|
709
|
-
// naming the rule that was removed. Known downstream case:
|
|
710
|
-
// pipeline works around it with approval labels precisely because of this.
|
|
709
|
+
// naming the rule that was removed. Known downstream case: @rtorcato/repo-ai's
|
|
710
|
+
// ai-loop pipeline works around it with approval labels precisely because of this.
|
|
711
711
|
// Loosen the standard here first if a repo ever genuinely needs review gating.
|
|
712
712
|
required_pull_request_reviews: null,
|
|
713
713
|
restrictions: null,
|
|
@@ -173,6 +173,8 @@ export function declinedInLock(lock, checkName) {
|
|
|
173
173
|
return c.turborepo === false;
|
|
174
174
|
case 'Tailwind':
|
|
175
175
|
return c.tailwind === false;
|
|
176
|
+
case 'Brand assets':
|
|
177
|
+
return c.brand === false;
|
|
176
178
|
default:
|
|
177
179
|
return false;
|
|
178
180
|
}
|
|
@@ -246,6 +248,8 @@ export function lockfilePatchForTarget(target, lock) {
|
|
|
246
248
|
return c.docsSite ? null : { docsSite: true };
|
|
247
249
|
case 'bun':
|
|
248
250
|
return c.bun ? null : { bun: true };
|
|
251
|
+
case 'brand':
|
|
252
|
+
return c.brand ? null : { brand: true };
|
|
249
253
|
default:
|
|
250
254
|
return null;
|
|
251
255
|
}
|
|
@@ -59,6 +59,7 @@ export function buildPresetConfig(name, projectName) {
|
|
|
59
59
|
semanticRelease: true,
|
|
60
60
|
bundler: 'tsup',
|
|
61
61
|
publint: true,
|
|
62
|
+
brand: true,
|
|
62
63
|
};
|
|
63
64
|
case 'web-app':
|
|
64
65
|
return {
|
|
@@ -210,6 +211,7 @@ export const CONFIG_SCHEMA = {
|
|
|
210
211
|
nx: { type: 'boolean' },
|
|
211
212
|
tailwind: { type: 'boolean' },
|
|
212
213
|
docsSite: { type: 'boolean' },
|
|
214
|
+
brand: { type: 'boolean' },
|
|
213
215
|
bun: { type: 'boolean' },
|
|
214
216
|
},
|
|
215
217
|
};
|
|
@@ -315,6 +317,8 @@ export function computeFileList(config) {
|
|
|
315
317
|
files.push('postcss.config.mjs', 'src/styles/globals.css');
|
|
316
318
|
if (config.bun)
|
|
317
319
|
files.push('bunfig.toml');
|
|
320
|
+
if (config.brand)
|
|
321
|
+
files.push('brand/banner.svg', 'brand/banner-mobile.svg', 'brand/social-card.svg', 'brand/render.sh');
|
|
318
322
|
files.push('README.md');
|
|
319
323
|
return files;
|
|
320
324
|
}
|
|
@@ -22,6 +22,7 @@ const REVIEWABLE_FEATURES = [
|
|
|
22
22
|
{ key: 'securityAutomation', label: 'Security automation (Dependabot + CodeQL)' },
|
|
23
23
|
{ key: 'badges', label: 'README status badges' },
|
|
24
24
|
{ key: 'aiSetup', label: 'AI agent rules (AGENTS.md, CLAUDE.md, Cursor, Copilot)' },
|
|
25
|
+
{ key: 'brand', label: 'Brand assets (brand/ banner + social-card SVGs, render.sh)' },
|
|
25
26
|
];
|
|
26
27
|
/**
|
|
27
28
|
* A preset run may only ask questions when a human is on both ends of the pipe.
|
|
@@ -457,6 +458,13 @@ async function promptForConfig(targetDir, seed) {
|
|
|
457
458
|
message: '🤖 Add AI agent rules (AGENTS.md, CLAUDE.md, Cursor, Copilot, Claude skill)?',
|
|
458
459
|
default: seed?.aiSetup ?? true,
|
|
459
460
|
},
|
|
461
|
+
{
|
|
462
|
+
type: 'confirm',
|
|
463
|
+
name: 'brand',
|
|
464
|
+
message: '🎨 Add brand assets (brand/ banner + social-card SVG sources, render.sh)?',
|
|
465
|
+
// On by default for libraries — the ones that publish a README to npm.
|
|
466
|
+
default: (answers) => seed?.brand ?? answers.projectType === 'library',
|
|
467
|
+
},
|
|
460
468
|
{
|
|
461
469
|
type: 'select',
|
|
462
470
|
name: 'orchestrator',
|
|
@@ -546,6 +554,7 @@ async function promptForConfig(targetDir, seed) {
|
|
|
546
554
|
publint: answers.publint ?? false,
|
|
547
555
|
badges: answers.badges ?? false,
|
|
548
556
|
aiSetup: answers.aiSetup ?? false,
|
|
557
|
+
brand: answers.brand ?? false,
|
|
549
558
|
turborepo: answers.orchestrator === 'turbo',
|
|
550
559
|
nx: answers.orchestrator === 'nx',
|
|
551
560
|
tailwind: answers.tailwind ?? false,
|
|
@@ -86,9 +86,16 @@ async function ensureWorkspace(targetDir) {
|
|
|
86
86
|
await fs.writeFile(file, next);
|
|
87
87
|
return rel;
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
/**
|
|
90
|
+
* A JS string literal in the Biome preset's quote style: single quotes, unless
|
|
91
|
+
* the value holds more single than double quotes — the same pick Biome makes.
|
|
92
|
+
*/
|
|
93
|
+
function jsString(value) {
|
|
94
|
+
const singles = value.split("'").length - 1;
|
|
95
|
+
const doubles = value.split('"').length - 1;
|
|
96
|
+
if (singles > doubles)
|
|
97
|
+
return JSON.stringify(value);
|
|
98
|
+
return `'${JSON.stringify(value).slice(1, -1).replaceAll('\\"', '"').replaceAll("'", "\\'")}'`;
|
|
92
99
|
}
|
|
93
100
|
function docusaurusConfig(meta, typedocModules) {
|
|
94
101
|
const owner = meta.owner ?? 'your-org';
|
|
@@ -100,111 +107,111 @@ function docusaurusConfig(meta, typedocModules) {
|
|
|
100
107
|
? "import { getTypedocPlugins } from '@rtorcato/repo-tooling/docusaurus'\n"
|
|
101
108
|
: '';
|
|
102
109
|
const typedocPlugins = typedocModules.length
|
|
103
|
-
?
|
|
110
|
+
? `\t\t...getTypedocPlugins([${typedocModules.map(jsString).join(', ')}]),\n`
|
|
104
111
|
: '';
|
|
105
112
|
return `import type * as Preset from '@docusaurus/preset-classic'
|
|
106
113
|
import type { Config } from '@docusaurus/types'
|
|
107
114
|
import { themes as prismThemes } from 'prism-react-renderer'
|
|
108
115
|
${typedocImport}
|
|
109
116
|
const config: Config = {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
\ttitle: '${meta.title}',
|
|
118
|
+
\ttagline: ${jsString(meta.tagline)},
|
|
119
|
+
\tfavicon: 'img/favicon.ico',
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
\turl: 'https://${owner}.github.io',
|
|
122
|
+
\tbaseUrl: '/${repo}/',
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
\torganizationName: '${owner}',
|
|
125
|
+
\tprojectName: '${repo}',
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
\tonBrokenLinks: 'warn',
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
\tmarkdown: {
|
|
130
|
+
\t\tformat: 'detect',
|
|
131
|
+
\t\thooks: {
|
|
132
|
+
\t\t\tonBrokenMarkdownLinks: 'warn',
|
|
133
|
+
\t\t},
|
|
134
|
+
\t},
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
136
|
+
\ti18n: {
|
|
137
|
+
\t\tdefaultLocale: 'en',
|
|
138
|
+
\t\tlocales: ['en'],
|
|
139
|
+
\t},
|
|
133
140
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
141
|
+
\tpresets: [
|
|
142
|
+
\t\t[
|
|
143
|
+
\t\t\t'classic',
|
|
144
|
+
\t\t\t{
|
|
145
|
+
\t\t\t\tdocs: {
|
|
146
|
+
\t\t\t\t\tsidebarPath: './sidebars.ts',
|
|
147
|
+
\t\t\t\t\trouteBasePath: '/docs',
|
|
148
|
+
\t\t\t\t\teditUrl: '${ghUrl}/edit/main/apps/docs/',
|
|
149
|
+
\t\t\t\t},
|
|
150
|
+
\t\t\t\tblog: false,
|
|
151
|
+
\t\t\t\ttheme: {
|
|
152
|
+
\t\t\t\t\tcustomCss: './src/css/custom.css',
|
|
153
|
+
\t\t\t\t},
|
|
154
|
+
\t\t\t} satisfies Preset.Options,
|
|
155
|
+
\t\t],
|
|
156
|
+
\t],
|
|
150
157
|
|
|
151
|
-
|
|
152
|
-
${typedocPlugins}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
158
|
+
\tplugins: [
|
|
159
|
+
${typedocPlugins}\t\t[
|
|
160
|
+
\t\t\t'@easyops-cn/docusaurus-search-local',
|
|
161
|
+
\t\t\t{
|
|
162
|
+
\t\t\t\thashed: true,
|
|
163
|
+
\t\t\t\tindexDocs: true,
|
|
164
|
+
\t\t\t\tindexBlog: false,
|
|
165
|
+
\t\t\t\tdocsRouteBasePath: '/docs',
|
|
166
|
+
\t\t\t\thighlightSearchTermsOnTargetPage: true,
|
|
167
|
+
\t\t\t\tsearchBarShortcutHint: false,
|
|
168
|
+
\t\t\t},
|
|
169
|
+
\t\t],
|
|
170
|
+
\t],
|
|
164
171
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
172
|
+
\tthemeConfig: {
|
|
173
|
+
\t\tcolorMode: {
|
|
174
|
+
\t\t\tdefaultMode: 'dark',
|
|
175
|
+
\t\t\trespectPrefersColorScheme: true,
|
|
176
|
+
\t\t},
|
|
177
|
+
\t\tnavbar: {
|
|
178
|
+
\t\t\ttitle: '${meta.title}',
|
|
179
|
+
\t\t\titems: [
|
|
180
|
+
\t\t\t\t{ to: '/docs', position: 'left', label: 'Docs' },
|
|
181
|
+
\t\t\t\t{
|
|
182
|
+
\t\t\t\t\thref: '${ghUrl}',
|
|
183
|
+
\t\t\t\t\tlabel: 'GitHub',
|
|
184
|
+
\t\t\t\t\tposition: 'right',
|
|
185
|
+
\t\t\t\t},
|
|
186
|
+
\t\t\t],
|
|
187
|
+
\t\t},
|
|
188
|
+
\t\tfooter: {
|
|
189
|
+
\t\t\tstyle: 'dark',
|
|
190
|
+
\t\t\tlinks: [
|
|
191
|
+
\t\t\t\t{
|
|
192
|
+
\t\t\t\t\ttitle: 'Docs',
|
|
193
|
+
\t\t\t\t\titems: [{ label: 'Getting Started', to: '/docs' }],
|
|
194
|
+
\t\t\t\t},
|
|
195
|
+
\t\t\t\t{
|
|
196
|
+
\t\t\t\t\ttitle: 'More',
|
|
197
|
+
\t\t\t\t\titems: [
|
|
198
|
+
\t\t\t\t\t\t{ label: 'GitHub', href: '${ghUrl}' },
|
|
199
|
+
\t\t\t\t\t\t{ label: 'Issues', href: '${ghUrl}/issues' },
|
|
200
|
+
\t\t\t\t\t],
|
|
201
|
+
\t\t\t\t},
|
|
202
|
+
\t\t\t],
|
|
203
|
+
\t\t\tcopyright: \`Copyright © \${new Date().getFullYear()} ${meta.title}. Built with Docusaurus.\`,
|
|
204
|
+
\t\t},
|
|
205
|
+
\t\t// \`theme\` is the LIGHT-mode Prism theme and \`darkTheme\` the dark one. Both
|
|
206
|
+
\t\t// were vsDark here, which is why the shared stylesheet had to pin fenced
|
|
207
|
+
\t\t// blocks dark in light mode too (#324). Keep this pairing and the CSS in
|
|
208
|
+
\t\t// step — vsDark tokens on a light surface are unreadable.
|
|
209
|
+
\t\tprism: {
|
|
210
|
+
\t\t\ttheme: prismThemes.vsLight,
|
|
211
|
+
\t\t\tdarkTheme: prismThemes.vsDark,
|
|
212
|
+
\t\t\tadditionalLanguages: ['bash', 'json', 'typescript'],
|
|
213
|
+
\t\t},
|
|
214
|
+
\t} satisfies Preset.ThemeConfig,
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
export default config
|
|
@@ -215,7 +222,7 @@ const SIDEBARS = `import type { SidebarsConfig } from '@docusaurus/plugin-conten
|
|
|
215
222
|
// Autogenerated from the docs/ folder structure — add markdown files and they
|
|
216
223
|
// appear here. Swap for an explicit list when you want to control ordering.
|
|
217
224
|
const sidebars: SidebarsConfig = {
|
|
218
|
-
|
|
225
|
+
\tdocs: [{ type: 'autogenerated', dirName: '.' }],
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
export default sidebars
|
|
@@ -246,13 +253,13 @@ function customCss(accent) {
|
|
|
246
253
|
@import "./_jt-tokens.css";
|
|
247
254
|
|
|
248
255
|
:root {
|
|
249
|
-
|
|
250
|
-
|
|
256
|
+
\t--ifm-color-primary: ${accent.light};
|
|
257
|
+
\t--jt-accent: ${accent.light};
|
|
251
258
|
}
|
|
252
259
|
|
|
253
260
|
[data-theme="dark"] {
|
|
254
|
-
|
|
255
|
-
|
|
261
|
+
\t--ifm-color-primary: ${accent.dark};
|
|
262
|
+
\t--jt-accent: ${accent.dark};
|
|
256
263
|
}
|
|
257
264
|
`;
|
|
258
265
|
}
|
|
@@ -279,9 +286,6 @@ function docsPackageJson(meta, typedoc) {
|
|
|
279
286
|
serve: 'docusaurus serve',
|
|
280
287
|
clear: 'docusaurus clear',
|
|
281
288
|
typecheck: 'tsc --noEmit',
|
|
282
|
-
// Opt-in smoke test — builds, serves, and checks the site renders. Heavy
|
|
283
|
-
// browser install, so it's a manual/CI-gated run, not part of `build`.
|
|
284
|
-
'test:e2e': 'playwright test',
|
|
285
289
|
},
|
|
286
290
|
dependencies: {
|
|
287
291
|
'@docusaurus/core': '^3.10.2',
|
|
@@ -297,7 +301,6 @@ function docsPackageJson(meta, typedoc) {
|
|
|
297
301
|
'@docusaurus/module-type-aliases': '^3.10.2',
|
|
298
302
|
'@docusaurus/tsconfig': '^3.8.1',
|
|
299
303
|
'@docusaurus/types': '^3.10.2',
|
|
300
|
-
'@playwright/test': '^1.49.0',
|
|
301
304
|
'@rtorcato/repo-tooling': SELF_RANGE,
|
|
302
305
|
'@types/react': '^19.0.0',
|
|
303
306
|
typescript: '~5.6.3',
|
|
@@ -354,34 +357,6 @@ jobs:
|
|
|
354
357
|
build-filter: '${meta.docsPkgName}'
|
|
355
358
|
`;
|
|
356
359
|
}
|
|
357
|
-
/**
|
|
358
|
-
* Playwright config for the docs smoke test. Reuses the shipped preset, then
|
|
359
|
-
* builds + serves the site on :3000 and points the base URL at the site's
|
|
360
|
-
* GitHub Pages base path so routes resolve exactly as in production. One
|
|
361
|
-
* browser keeps the CI browser install light.
|
|
362
|
-
*/
|
|
363
|
-
function playwrightConfig(meta) {
|
|
364
|
-
const url = `http://localhost:3000${siteBaseUrl(meta)}`;
|
|
365
|
-
return `import { defineConfig, devices } from '@playwright/test'
|
|
366
|
-
import base from '@rtorcato/repo-tooling/playwright'
|
|
367
|
-
|
|
368
|
-
export default defineConfig({
|
|
369
|
-
...base,
|
|
370
|
-
testDir: './tests',
|
|
371
|
-
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
|
|
372
|
-
use: {
|
|
373
|
-
...base.use,
|
|
374
|
-
baseURL: process.env.PLAYWRIGHT_BASE_URL ?? '${url}',
|
|
375
|
-
},
|
|
376
|
-
webServer: {
|
|
377
|
-
command: 'pnpm run build && pnpm exec docusaurus serve --port 3000',
|
|
378
|
-
url: process.env.PLAYWRIGHT_BASE_URL ?? '${url}',
|
|
379
|
-
reuseExistingServer: !process.env.CI,
|
|
380
|
-
timeout: 180_000,
|
|
381
|
-
},
|
|
382
|
-
})
|
|
383
|
-
`;
|
|
384
|
-
}
|
|
385
360
|
// routeBasePath is '/docs', so the site root has no page of its own and the
|
|
386
361
|
// navbar logo links to a 404 on every page (#664). Redirect it to the docs.
|
|
387
362
|
// Tabs/no semicolons to match the Biome preset the consuming repo is linted with.
|
|
@@ -392,21 +367,6 @@ export default function Home() {
|
|
|
392
367
|
\treturn <Redirect to={useBaseUrl('/docs')} />
|
|
393
368
|
}
|
|
394
369
|
`;
|
|
395
|
-
const SMOKE_SPEC = `import { expect, test } from '@playwright/test'
|
|
396
|
-
|
|
397
|
-
// Smoke test: assert the built site serves and its core UI renders. Deliberately
|
|
398
|
-
// content-agnostic — it validates "the site builds and boots", not copy.
|
|
399
|
-
test('homepage responds and renders the shell', async ({ page }) => {
|
|
400
|
-
const res = await page.goto('./')
|
|
401
|
-
expect(res?.ok()).toBeTruthy()
|
|
402
|
-
await expect(page.locator('.navbar')).toBeVisible()
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
test('the starter doc renders a heading', async ({ page }) => {
|
|
406
|
-
await page.goto('./')
|
|
407
|
-
await expect(page.locator('h1')).toBeVisible()
|
|
408
|
-
})
|
|
409
|
-
`;
|
|
410
370
|
/**
|
|
411
371
|
* Scaffold the Docusaurus docs site. Writes each file only when missing and
|
|
412
372
|
* returns the relative paths actually written, so `fix docs-site` is safe to
|
|
@@ -440,8 +400,6 @@ export async function generateDocsSite(pkg, targetDir, options = {}) {
|
|
|
440
400
|
[`${DOCS_APP}/src/css/custom.css`, customCss(accent)],
|
|
441
401
|
[`${DOCS_APP}/src/pages/index.tsx`, HOME_PAGE],
|
|
442
402
|
[`${DOCS_APP}/docs/intro.md`, introDoc(meta, badges)],
|
|
443
|
-
[`${DOCS_APP}/playwright.config.ts`, playwrightConfig(meta)],
|
|
444
|
-
[`${DOCS_APP}/tests/smoke.spec.ts`, SMOKE_SPEC],
|
|
445
403
|
['.github/workflows/docs.yml', docsWorkflow(meta)],
|
|
446
404
|
];
|
|
447
405
|
// TypeDoc emits docs/api/<id> on build — keep the generated tree out of git.
|
|
@@ -19,6 +19,7 @@ import { generateTailwind } from './tailwind.js';
|
|
|
19
19
|
import { generateTurborepo } from './turborepo.js';
|
|
20
20
|
import { generateNx } from './nx.js';
|
|
21
21
|
import { generateBun } from './bun.js';
|
|
22
|
+
import { generateBrand } from './brand.js';
|
|
22
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
24
|
const __dirname = path.dirname(__filename);
|
|
24
25
|
export async function generateConfigs(config, targetDir) {
|
|
@@ -122,6 +123,11 @@ export async function generateConfigs(config, targetDir) {
|
|
|
122
123
|
}
|
|
123
124
|
// Generate README
|
|
124
125
|
await generateReadme(config, targetDir);
|
|
126
|
+
// brand/ (#677) — the same generator as `fix brand`. After the README so the
|
|
127
|
+
// banner-path repoint sees it; no tagline yet, as the lockfile isn't written.
|
|
128
|
+
if (config.brand) {
|
|
129
|
+
await generateBrand(await fs.readJson(path.join(targetDir, 'package.json')), targetDir);
|
|
130
|
+
}
|
|
125
131
|
// Copy ts-reset if TypeScript is enabled
|
|
126
132
|
if (config.typescript.enabled) {
|
|
127
133
|
await copyTSReset(targetDir);
|
|
@@ -163,6 +163,8 @@ export function dependabotIgnoreRules(content) {
|
|
|
163
163
|
* Everything else falls through to a human: production bumps ship to consumers
|
|
164
164
|
* of a published package (#423), majors are breaking by definition, and an
|
|
165
165
|
* ungrouped npm PR reports an empty \`dependency-group\`, so the gate fails closed.
|
|
166
|
+
* A PR that falls through is assigned to the repo owner with one upserted comment
|
|
167
|
+
* saying why (#694), so it never sits green and ownerless.
|
|
166
168
|
*
|
|
167
169
|
* CI action bumps are the one ungrouped case that is allowed through, matched on
|
|
168
170
|
* \`package-ecosystem\` (#452): they reach no consumer of the published package
|
|
@@ -226,11 +228,13 @@ jobs:
|
|
|
226
228
|
# No names reported means we cannot verify anything — fail closed.
|
|
227
229
|
if [ -z "\${NAMES//[, ]/}" ]; then
|
|
228
230
|
echo "::notice::no dependency names reported — leaving this PR for a human"
|
|
231
|
+
echo "reason=Dependabot reported no dependency names, so nothing could be verified" >> "$GITHUB_OUTPUT"
|
|
229
232
|
safe=false
|
|
230
233
|
fi
|
|
231
234
|
for name in \${NAMES//,/ }; do
|
|
232
235
|
if printf '%s\\n' "$ships" | grep -qxF -- "$name"; then
|
|
233
236
|
echo "::notice::$name ships to consumers — leaving this PR for a human"
|
|
237
|
+
echo "reason=$name ships to consumers of this package" >> "$GITHUB_OUTPUT"
|
|
234
238
|
safe=false
|
|
235
239
|
break
|
|
236
240
|
fi
|
|
@@ -247,6 +251,7 @@ jobs:
|
|
|
247
251
|
# "ci(deps): …", which cuts no release. Majors still fall through to a
|
|
248
252
|
# human — the update-type clause below applies to them too.
|
|
249
253
|
- name: Auto-merge dev-dependency and CI action patch and minor updates
|
|
254
|
+
id: merge
|
|
250
255
|
if: |
|
|
251
256
|
steps.gate.outputs.safe == 'true' &&
|
|
252
257
|
(steps.metadata.outputs.dependency-group == 'dev-minor' ||
|
|
@@ -257,6 +262,46 @@ jobs:
|
|
|
257
262
|
env:
|
|
258
263
|
PR_URL: \${{ github.event.pull_request.html_url }}
|
|
259
264
|
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
265
|
+
|
|
266
|
+
# Everything the step above declines needs a human, and a notice in the
|
|
267
|
+
# Actions log is not one (#694). Assign the repo owner and say why in a
|
|
268
|
+
# single comment, edited in place on every later run rather than repeated.
|
|
269
|
+
- name: Hand the PR to a human
|
|
270
|
+
if: steps.merge.outcome == 'skipped'
|
|
271
|
+
env:
|
|
272
|
+
PR_URL: \${{ github.event.pull_request.html_url }}
|
|
273
|
+
GH_TOKEN: \${{ secrets.GITHUB_TOKEN }}
|
|
274
|
+
REPO: \${{ github.repository }}
|
|
275
|
+
PR_NUMBER: \${{ github.event.pull_request.number }}
|
|
276
|
+
OWNER: \${{ github.repository_owner }}
|
|
277
|
+
REASON: \${{ steps.gate.outputs.reason }}
|
|
278
|
+
UPDATE_TYPE: \${{ steps.metadata.outputs.update-type }}
|
|
279
|
+
run: |
|
|
280
|
+
set -euo pipefail
|
|
281
|
+
|
|
282
|
+
if [ -z "$REASON" ]; then
|
|
283
|
+
case "$UPDATE_TYPE" in
|
|
284
|
+
version-update:semver-major) REASON="this is a major version bump" ;;
|
|
285
|
+
version-update:semver-minor | version-update:semver-patch)
|
|
286
|
+
REASON="this is neither a dev-minor group bump nor a CI action bump, so it may ship to consumers" ;;
|
|
287
|
+
*) REASON="Dependabot reported no update type, so nothing could be verified" ;;
|
|
288
|
+
esac
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
marker='<!-- dependabot-automerge:manual-review -->'
|
|
292
|
+
body="$marker
|
|
293
|
+
Not auto-merged: $REASON. Leaving this for @$OWNER to review and merge by hand."
|
|
294
|
+
|
|
295
|
+
ids=$(gh api --paginate "repos/$REPO/issues/$PR_NUMBER/comments" \\
|
|
296
|
+
--jq ".[] | select(.body | startswith(\\"$marker\\")) | .id")
|
|
297
|
+
if [ -n "$ids" ]; then
|
|
298
|
+
gh api -X PATCH "repos/$REPO/issues/comments/\${ids%%$'\\n'*}" -f body="$body" > /dev/null
|
|
299
|
+
else
|
|
300
|
+
gh pr comment "$PR_URL" --body "$body"
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
# An organisation login cannot be assigned; the comment still stands.
|
|
304
|
+
gh pr edit "$PR_URL" --add-assignee "$OWNER" || echo "::warning::could not assign $OWNER"
|
|
260
305
|
`;
|
|
261
306
|
/** Relative paths this generator owns, in a stable order for \`filesWritten\`. */
|
|
262
307
|
export const DEPENDABOT_FILES = [
|
package/dist/cli/index.js
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path from 'node:path';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import { Command } from 'commander';
|
|
5
|
-
import fs from 'fs-extra';
|
|
6
4
|
import { doctorCommand } from './commands/doctor.js';
|
|
7
5
|
import { fixCommand } from './commands/fix.js';
|
|
8
6
|
import { setupProject } from './commands/setup.js';
|
|
7
|
+
import { selfRepoRefusal } from './self-repo.js';
|
|
9
8
|
import { copyPreset, PRESETS } from './utils/copy-preset.js';
|
|
10
9
|
import { getToolVersion } from './utils/version.js';
|
|
11
|
-
async function isSelfRepo(dir) {
|
|
12
|
-
try {
|
|
13
|
-
const pkg = await fs.readJson(path.join(dir, 'package.json'));
|
|
14
|
-
return pkg.name === '@rtorcato/repo-tooling';
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
10
|
const program = new Command();
|
|
21
11
|
program
|
|
22
12
|
.name('@rtorcato/repo-tooling')
|
|
@@ -351,29 +341,11 @@ program
|
|
|
351
341
|
process.exitCode = 1;
|
|
352
342
|
});
|
|
353
343
|
program.hook('preAction', async (_, actionCommand) => {
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// Dogfood escape hatch (#273): allow read-only `doctor` against this repo
|
|
360
|
-
// so CI can audit our own config the same way it does consumers'. Scoped
|
|
361
|
-
// to doctor — the mutating setup/fix stay blocked even with the flag set.
|
|
362
|
-
if (name === 'doctor' && process.env.REPO_TOOLING_ALLOW_SELF === '1')
|
|
363
|
-
return;
|
|
364
|
-
// One mutating exception (#531): `fix lockfile` writes only
|
|
365
|
-
// .repo-tooling.json — no scaffolding — so our own lockfile can be
|
|
366
|
-
// migrated by the fixer we ship instead of by hand.
|
|
367
|
-
if (name === 'fix' &&
|
|
368
|
-
actionCommand.args[0] === 'lockfile' &&
|
|
369
|
-
process.env.REPO_TOOLING_ALLOW_SELF === '1')
|
|
370
|
-
return;
|
|
371
|
-
const dir = actionCommand.opts().directory ?? process.cwd();
|
|
372
|
-
if (await isSelfRepo(dir)) {
|
|
373
|
-
console.log(chalk.yellow('\n⚠️ This command cannot be run inside the @rtorcato/repo-tooling repo itself.\n'));
|
|
374
|
-
console.log(chalk.gray(' setup and doctor are for consumer projects, not for the tooling repo.\n'));
|
|
375
|
-
process.exit(0);
|
|
376
|
-
}
|
|
344
|
+
const refusal = await selfRepoRefusal(actionCommand.name(), actionCommand.args[0], actionCommand.opts());
|
|
345
|
+
if (refusal) {
|
|
346
|
+
console.log(chalk.yellow('\n⚠️ This command cannot be run inside the @rtorcato/repo-tooling repo itself.\n'));
|
|
347
|
+
console.log(chalk.gray(` ${refusal}\n`));
|
|
348
|
+
process.exit(0);
|
|
377
349
|
}
|
|
378
350
|
});
|
|
379
351
|
// Handle unknown commands
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { getFixers } from './commands/fix.js';
|
|
4
|
+
export async function isSelfRepo(dir) {
|
|
5
|
+
try {
|
|
6
|
+
const pkg = await fs.readJson(path.join(dir, 'package.json'));
|
|
7
|
+
return pkg.name === '@rtorcato/repo-tooling';
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Why `setup` / `doctor` / `fix` must not run in `dir`, or null when it may.
|
|
15
|
+
* Most fixers write configs that import `@rtorcato/repo-tooling/...`, which
|
|
16
|
+
* this repo can't depend on, so inside it everything is refused unless
|
|
17
|
+
* `REPO_TOOLING_ALLOW_SELF=1` is set. With the flag, read-only `doctor` (#273)
|
|
18
|
+
* and `fix <target>` for a `selfSafe` target (#673) get through. A bare `fix`
|
|
19
|
+
* stays refused even then: it walks every fixer, self-safe or not.
|
|
20
|
+
*/
|
|
21
|
+
export async function selfRepoRefusal(command, target, opts, env = process.env) {
|
|
22
|
+
if (command !== 'setup' && command !== 'doctor' && command !== 'fix')
|
|
23
|
+
return null;
|
|
24
|
+
// `fix --list` is read-only and safe to run anywhere, including this repo.
|
|
25
|
+
if (command === 'fix' && opts.list)
|
|
26
|
+
return null;
|
|
27
|
+
if (!(await isSelfRepo(opts.directory ?? process.cwd())))
|
|
28
|
+
return null;
|
|
29
|
+
const allowSelf = env.REPO_TOOLING_ALLOW_SELF === '1';
|
|
30
|
+
if (allowSelf && command === 'doctor')
|
|
31
|
+
return null;
|
|
32
|
+
if (allowSelf && command === 'fix') {
|
|
33
|
+
if (!target) {
|
|
34
|
+
const safe = getFixers()
|
|
35
|
+
.filter((f) => f.selfSafe)
|
|
36
|
+
.map((f) => f.target);
|
|
37
|
+
return `a bare \`fix\` runs every fixer, including ones whose output imports @rtorcato/repo-tooling — name a self-safe target instead: ${safe.join(', ')}.`;
|
|
38
|
+
}
|
|
39
|
+
const fixer = getFixers().find((f) => f.target.toLowerCase() === target.toLowerCase());
|
|
40
|
+
if (fixer?.selfSafe)
|
|
41
|
+
return null;
|
|
42
|
+
return fixer
|
|
43
|
+
? `\`fix ${target}\` is not self-safe: its output imports or depends on @rtorcato/repo-tooling, which this repo cannot depend on.`
|
|
44
|
+
: `\`fix ${target}\` is not a known self-safe target.`;
|
|
45
|
+
}
|
|
46
|
+
return 'setup and doctor are for consumer projects, not for the tooling repo.';
|
|
47
|
+
}
|
|
@@ -36,8 +36,6 @@ export const MCP_IMPORTANCE = ['nice-to-have', 'important', 'critical'];
|
|
|
36
36
|
* be this tool asserting a rule on a repo whose humans have not stated any.
|
|
37
37
|
*/
|
|
38
38
|
export const DEFAULT_RULES = {
|
|
39
|
-
aiLoop: {},
|
|
40
|
-
requiredSkills: [],
|
|
41
39
|
mcp: { recommended: [] },
|
|
42
40
|
exceptions: {},
|
|
43
41
|
};
|
|
@@ -111,7 +109,8 @@ export function lockfileSchema() {
|
|
|
111
109
|
aiLoop: {
|
|
112
110
|
type: 'object',
|
|
113
111
|
additionalProperties: false,
|
|
114
|
-
|
|
112
|
+
deprecated: true,
|
|
113
|
+
description: 'Deprecated: moved to `.repo-ai.json`, owned by @rtorcato/repo-ai. Still accepted so existing files validate; removed in the next major. `npx @rtorcato/repo-ai fix config` moves it across.',
|
|
115
114
|
properties: {
|
|
116
115
|
agentUser: {
|
|
117
116
|
type: 'string',
|
|
@@ -122,7 +121,8 @@ export function lockfileSchema() {
|
|
|
122
121
|
requiredSkills: {
|
|
123
122
|
type: 'array',
|
|
124
123
|
items: { type: 'string' },
|
|
125
|
-
|
|
124
|
+
deprecated: true,
|
|
125
|
+
description: 'Deprecated: moved to `.repo-ai.json`, owned by @rtorcato/repo-ai. Still accepted so existing files validate; removed in the next major. `npx @rtorcato/repo-ai fix config` moves it across.',
|
|
126
126
|
},
|
|
127
127
|
mcp: {
|
|
128
128
|
type: 'object',
|
|
@@ -1188,8 +1188,8 @@ export async function checkTreeshakeSetup(dir, pkg) {
|
|
|
1188
1188
|
* checkout (#396). JS-only: the other language modules have nothing to symlink.
|
|
1189
1189
|
*
|
|
1190
1190
|
* Two consumers, one list (#527). Claude Code honours the setting only for
|
|
1191
|
-
* worktrees it creates itself (`EnterWorktree`);
|
|
1192
|
-
*
|
|
1191
|
+
* worktrees it creates itself (`EnterWorktree`); @rtorcato/repo-ai's `ai-loop`
|
|
1192
|
+
* creates its own with `git worktree add`, so it reads this same list and
|
|
1193
1193
|
* makes the symlinks itself. That is why the check is worth passing on a repo
|
|
1194
1194
|
* running the loop, where the setting alone would govern nothing.
|
|
1195
1195
|
*/
|
|
@@ -546,6 +546,7 @@ export const FIXERS = [
|
|
|
546
546
|
},
|
|
547
547
|
{
|
|
548
548
|
target: 'vscode-extensions',
|
|
549
|
+
selfSafe: true,
|
|
549
550
|
description: 'Recommend the VS Code extensions matching the enabled tools (.vscode/extensions.json)',
|
|
550
551
|
appliesTo: ['VS Code extensions'],
|
|
551
552
|
outputs: ['.vscode/extensions.json'],
|
|
@@ -559,6 +560,7 @@ export const FIXERS = [
|
|
|
559
560
|
},
|
|
560
561
|
{
|
|
561
562
|
target: 'nvmrc',
|
|
563
|
+
selfSafe: true,
|
|
562
564
|
description: 'Scaffold .nvmrc pinned to Node 22',
|
|
563
565
|
appliesTo: ['Node version pin'],
|
|
564
566
|
outputs: ['.nvmrc'],
|
|
@@ -892,6 +894,7 @@ export const FIXERS = [
|
|
|
892
894
|
},
|
|
893
895
|
{
|
|
894
896
|
target: 'lockfile',
|
|
897
|
+
selfSafe: true,
|
|
895
898
|
description: `Scaffold ${LOCKFILE_NAME} recording current tool choices`,
|
|
896
899
|
appliesTo: ['lockfile'],
|
|
897
900
|
outputs: [LOCKFILE_NAME],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtorcato/repo-tooling",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -248,6 +248,7 @@
|
|
|
248
248
|
"@next/eslint-plugin-next": "^16.3.2",
|
|
249
249
|
"@playwright/test": "^1.62.1",
|
|
250
250
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
251
|
+
"@rtorcato/repo-ai": "^3.1.1",
|
|
251
252
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
252
253
|
"@semantic-release/exec": "^7.1.0",
|
|
253
254
|
"@semantic-release/github": "^12.0.9",
|