@rtorcato/repo-tooling 3.15.1 → 3.15.3
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/base/git-identity.js +4 -2
- package/dist/cli/commands/setup.js +10 -10
- package/package.json +4 -4
- package/skills/ai-issue-loop/SKILL.md +40 -17
|
@@ -9,8 +9,10 @@ import fs from 'fs-extra';
|
|
|
9
9
|
*/
|
|
10
10
|
const PLACEHOLDER_DOMAINS = ['example.com', 'example.org', 'example.net', 'invalid', 'test'];
|
|
11
11
|
/**
|
|
12
|
-
* Suffixes of a machine-derived hostname. `.local` is mDNS (macOS default)
|
|
13
|
-
*
|
|
12
|
+
* Suffixes of a machine-derived hostname. `.local` is mDNS (macOS default); the
|
|
13
|
+
* rest are the conventional private-LAN suffixes a router hands out. `.matrix`
|
|
14
|
+
* is one such router default rather than a registered TLD — it is here because
|
|
15
|
+
* it produced real commits in the #327 set, alongside `.local`.
|
|
14
16
|
*/
|
|
15
17
|
const HOSTNAME_SUFFIXES = ['.local', '.matrix', '.localhost', '.lan', '.home', '.internal'];
|
|
16
18
|
/** Pure so the rules are testable without a git repo or a spawn. */
|
|
@@ -110,7 +110,7 @@ async function promptForLanguage(targetDir) {
|
|
|
110
110
|
const detected = await detectLanguage(targetDir);
|
|
111
111
|
const { language } = await inquirer.prompt([
|
|
112
112
|
{
|
|
113
|
-
type: '
|
|
113
|
+
type: 'select',
|
|
114
114
|
name: 'language',
|
|
115
115
|
message: '🗣️ What is the primary language of this repo?',
|
|
116
116
|
choices: Object.values(LANGUAGES).map((module) => ({
|
|
@@ -178,7 +178,7 @@ async function promptForConfig(targetDir) {
|
|
|
178
178
|
validate: (input) => input.trim().length > 0 || 'Project name is required',
|
|
179
179
|
},
|
|
180
180
|
{
|
|
181
|
-
type: '
|
|
181
|
+
type: 'select',
|
|
182
182
|
name: 'projectType',
|
|
183
183
|
message: '🏗️ What type of project are you building?',
|
|
184
184
|
choices: [
|
|
@@ -196,7 +196,7 @@ async function promptForConfig(targetDir) {
|
|
|
196
196
|
default: true,
|
|
197
197
|
},
|
|
198
198
|
{
|
|
199
|
-
type: '
|
|
199
|
+
type: 'select',
|
|
200
200
|
name: 'tsConfig',
|
|
201
201
|
message: '⚙️ Which TypeScript configuration?',
|
|
202
202
|
choices: (answers) => {
|
|
@@ -221,7 +221,7 @@ async function promptForConfig(targetDir) {
|
|
|
221
221
|
when: (answers) => answers.useTypeScript,
|
|
222
222
|
},
|
|
223
223
|
{
|
|
224
|
-
type: '
|
|
224
|
+
type: 'select',
|
|
225
225
|
name: 'lintingTool',
|
|
226
226
|
message: '🔍 Which linting/formatting tool?',
|
|
227
227
|
choices: [
|
|
@@ -233,7 +233,7 @@ async function promptForConfig(targetDir) {
|
|
|
233
233
|
default: 'biome',
|
|
234
234
|
},
|
|
235
235
|
{
|
|
236
|
-
type: '
|
|
236
|
+
type: 'select',
|
|
237
237
|
name: 'eslintConfig',
|
|
238
238
|
message: '🔧 Which ESLint configuration?',
|
|
239
239
|
choices: (answers) => {
|
|
@@ -253,7 +253,7 @@ async function promptForConfig(targetDir) {
|
|
|
253
253
|
when: (answers) => answers.lintingTool !== 'none',
|
|
254
254
|
},
|
|
255
255
|
{
|
|
256
|
-
type: '
|
|
256
|
+
type: 'select',
|
|
257
257
|
name: 'testingFramework',
|
|
258
258
|
message: '🧪 Which testing framework?',
|
|
259
259
|
choices: [
|
|
@@ -266,7 +266,7 @@ async function promptForConfig(targetDir) {
|
|
|
266
266
|
default: 'vitest',
|
|
267
267
|
},
|
|
268
268
|
{
|
|
269
|
-
type: '
|
|
269
|
+
type: 'select',
|
|
270
270
|
name: 'testEnvironment',
|
|
271
271
|
message: '🌍 Test environment?',
|
|
272
272
|
choices: [
|
|
@@ -291,7 +291,7 @@ async function promptForConfig(targetDir) {
|
|
|
291
291
|
when: (answers) => answers.gitHooks,
|
|
292
292
|
},
|
|
293
293
|
{
|
|
294
|
-
type: '
|
|
294
|
+
type: 'select',
|
|
295
295
|
name: 'releaseTool',
|
|
296
296
|
message: '🚀 Automated release tool?',
|
|
297
297
|
choices: [
|
|
@@ -336,7 +336,7 @@ async function promptForConfig(targetDir) {
|
|
|
336
336
|
default: true,
|
|
337
337
|
},
|
|
338
338
|
{
|
|
339
|
-
type: '
|
|
339
|
+
type: 'select',
|
|
340
340
|
name: 'orchestrator',
|
|
341
341
|
message: '🚀 Monorepo task orchestrator?',
|
|
342
342
|
choices: [
|
|
@@ -358,7 +358,7 @@ async function promptForConfig(targetDir) {
|
|
|
358
358
|
answers.projectType === 'nextjs-app',
|
|
359
359
|
},
|
|
360
360
|
{
|
|
361
|
-
type: '
|
|
361
|
+
type: 'select',
|
|
362
362
|
name: 'bundler',
|
|
363
363
|
message: '📦 Which bundler/build tool?',
|
|
364
364
|
choices: (answers) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtorcato/repo-tooling",
|
|
3
|
-
"version": "3.15.
|
|
3
|
+
"version": "3.15.3",
|
|
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": [
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"./package.json": "./package.json"
|
|
231
231
|
},
|
|
232
232
|
"dependencies": {
|
|
233
|
-
"chalk": "^
|
|
233
|
+
"chalk": "^6.0.0",
|
|
234
234
|
"commander": "^15.0.0",
|
|
235
235
|
"diff": "^9.0.0",
|
|
236
236
|
"fs-extra": "^11.4.0",
|
|
@@ -263,14 +263,14 @@
|
|
|
263
263
|
"conventional-changelog-conventionalcommits": "^10.2.1",
|
|
264
264
|
"cz-conventional-changelog": "^3.3.0",
|
|
265
265
|
"esbuild": "^0.28.0",
|
|
266
|
-
"esbuild-node-externals": "^
|
|
266
|
+
"esbuild-node-externals": "^2.0.0",
|
|
267
267
|
"eslint": "10.8.0",
|
|
268
268
|
"eslint-plugin-import": "^2.32.0",
|
|
269
269
|
"eslint-plugin-jest": "29.16.0",
|
|
270
270
|
"husky": "^9.1.7",
|
|
271
271
|
"is-ci": "^4.1.0",
|
|
272
272
|
"jest": "^30.4.2",
|
|
273
|
-
"jsdom": "^
|
|
273
|
+
"jsdom": "^30.0.1",
|
|
274
274
|
"knip": "^6.29.0",
|
|
275
275
|
"prettier": "^3.9.6",
|
|
276
276
|
"rimraf": "6.1.3",
|
|
@@ -64,13 +64,15 @@ header names the agent *and* says why it is wearing a human's face:
|
|
|
64
64
|
| `ai-reviewing-sec` | PR | `security-expert` claimed and running. Cleared with its verdict. |
|
|
65
65
|
| `ai-ok-code` | PR | `code-reviewer` passed. |
|
|
66
66
|
| `ai-ok-sec` | PR | `security-expert` passed. |
|
|
67
|
-
| `ai-changes` | PR | A reviewer requested changes. |
|
|
67
|
+
| `ai-changes` | PR | A reviewer requested changes. Reviewers never apply it to a Dependabot PR. |
|
|
68
68
|
| `ai-notes` | PR | Passed, but a reviewer left something to read before merging. |
|
|
69
69
|
| `holding` | issue | A gate — closes on human judgement, never picked up. |
|
|
70
70
|
|
|
71
71
|
**`ai-notes` is advisory and never blocks.** It rides *alongside* a pass label,
|
|
72
72
|
never instead of one, and it never sends a PR back — a finding that should block
|
|
73
|
-
is `ai-changes`.
|
|
73
|
+
an issue PR is `ai-changes`. On a Dependabot PR there is nothing to send back to,
|
|
74
|
+
so `ai-notes` is the hold itself: it suppresses auto-merge and routes the PR to
|
|
75
|
+
the human. It exists because a pass label currently means both "clean" and
|
|
74
76
|
"I found something real but would not hold the PR over it", and those two are
|
|
75
77
|
indistinguishable in the *Assigned to you* view where merges actually happen.
|
|
76
78
|
The bar is a finding that **changes what a human would do**: a semver
|
|
@@ -121,8 +123,8 @@ PR: ai-review ─> ai-reviewing-* ─┬─> ai-ok-code + ai-ok-sec ─┬─ is
|
|
|
121
123
|
│ (± ai-notes) │ ─> YOU merge ─> worktree removed
|
|
122
124
|
│ └─ dependabot ─┬─ no ai-notes ─> auto-merge ─> worktree removed
|
|
123
125
|
│ └─ ai-notes ───> assigned to you
|
|
124
|
-
└─> ai-changes ─> fix round (max 2) ─> ai-review
|
|
125
|
-
|
|
126
|
+
└─> ai-changes (issue PRs only) ─> fix round (max 2) ─> ai-review
|
|
127
|
+
└─ round 3 ─> ai-blocked
|
|
126
128
|
```
|
|
127
129
|
|
|
128
130
|
`ai-reviewing-code` / `ai-reviewing-sec` are the *claim* step: Pass 3 applies one
|
|
@@ -168,8 +170,7 @@ gh issue list --state open --label ai-wip --json number
|
|
|
168
170
|
**`OWNER_REPO` always comes from the working directory's remote — never from
|
|
169
171
|
`$ARGUMENTS`.** The loop labels, pushes, and merges, so it operates on the **current
|
|
170
172
|
repo only**, even if a prompt or an issue body names another one. Reads against other
|
|
171
|
-
repos are fine for checking a dependency; writes are not.
|
|
172
|
-
exception — it takes an `owner/repo` argument, but it is read-only.) GitHub only —
|
|
173
|
+
repos are fine for checking a dependency; writes are not. GitHub only —
|
|
173
174
|
bail in one line if the remote is GitLab.
|
|
174
175
|
|
|
175
176
|
**`ROOT` is load-bearing — resolve it first and use it for every path in every
|
|
@@ -290,6 +291,17 @@ gh pr edit <N> --add-label ai-changes \
|
|
|
290
291
|
|
|
291
292
|
Count it as `rev`, not `ready`. A merge conflict (`DIRTY`) takes the same route.
|
|
292
293
|
|
|
294
|
+
**Assign any Dependabot PR carrying `ai-changes`.** Pass 3 never spawns a fix
|
|
295
|
+
round for one, so it is waiting on a human from the moment the label lands — and
|
|
296
|
+
no other branch of this pass assigns it, which leaves it in no *Assigned to you*
|
|
297
|
+
view at all:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
gh pr edit <N> --add-assignee @me
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Count it as `rev`. Idempotent, so it also picks up ones an earlier tick stranded.
|
|
304
|
+
|
|
293
305
|
So: every open PR **authored by `dependabot[bot]`**, labelled both `ai-ok-code`
|
|
294
306
|
and `ai-ok-sec`, **not** `ai-changes`, **not** `ai-notes`, that has no
|
|
295
307
|
`autoMergeRequest` yet:
|
|
@@ -577,15 +589,24 @@ package/from/to table survives because it sits at the top; classify from that.
|
|
|
577
589
|
> the label. If you cannot tell whether a workspace publishes, treat it as
|
|
578
590
|
> production.
|
|
579
591
|
>
|
|
580
|
-
> Apply `ai-
|
|
592
|
+
> Apply your **pass** label *plus* `ai-notes` if **either** holds:
|
|
581
593
|
> - a package's major version differs between the `from` and `to` columns
|
|
582
594
|
> - a package ships to consumers — it appears in `dependencies`,
|
|
583
595
|
> `optionalDependencies`, or `peerDependencies` of a **non-private** package
|
|
584
596
|
>
|
|
585
597
|
> Those wait for a human — a runtime dependency of the published package, or a
|
|
586
|
-
> major, is not something an automated verdict should wave through.
|
|
587
|
-
>
|
|
588
|
-
>
|
|
598
|
+
> major, is not something an automated verdict should wave through. `ai-notes` is
|
|
599
|
+
> the gate that holds them: it suppresses auto-merge outright and gets the PR
|
|
600
|
+
> assigned to the human, so nothing production-facing lands unattended. Dev-only
|
|
601
|
+
> minor/patch bumps with green CI get the pass label alone. **If you cannot
|
|
602
|
+
> determine a package's type, treat it as production and note it**; failing
|
|
603
|
+
> closed is correct here.
|
|
604
|
+
>
|
|
605
|
+
> **Never apply `ai-changes` to a Dependabot PR.** It dispatches an implementer
|
|
606
|
+
> agent, and there is no change an agent could make — rewriting a bot's lockfile
|
|
607
|
+
> is not its business, and the decision here is a human's either way. That is the
|
|
608
|
+
> same rule as the generic prompt above: `ai-changes` only when you can name a
|
|
609
|
+
> concrete change an agent could make.
|
|
589
610
|
>
|
|
590
611
|
> State in your comment which rule fired, name the packages that tripped it, and say
|
|
591
612
|
> whether the body was truncated so the reader knows what you could and couldn't see.
|
|
@@ -595,9 +616,10 @@ package/from/to table survives because it sits at the top; classify from that.
|
|
|
595
616
|
> Pass 3 claimed you with it before spawning you, and a claim left behind wedges
|
|
596
617
|
> your half of the review until Pass 2 reaps it.
|
|
597
618
|
>
|
|
598
|
-
>
|
|
599
|
-
>
|
|
600
|
-
> unattended. A
|
|
619
|
+
> Keep `ai-notes` load-bearing here: it suppresses auto-merge, so a *decorative*
|
|
620
|
+
> note on a bump you would otherwise wave through wedges the one path that runs
|
|
621
|
+
> unattended. A major, a package that ships to consumers, or a truncated body you
|
|
622
|
+
> could not fully read **is** worth a note; restating the version table on a
|
|
601
623
|
> routine dev-only patch bump is not.
|
|
602
624
|
|
|
603
625
|
Be honest about what this buys: an agent reading a version table catches majors,
|
|
@@ -609,10 +631,11 @@ gate. This pass is a *policy* gate: nothing major or production-facing merges
|
|
|
609
631
|
unattended.
|
|
610
632
|
|
|
611
633
|
**A Dependabot PR labelled `ai-changes` is terminal — never spawn a fix round for
|
|
612
|
-
it.**
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
634
|
+
it.** Reviewers no longer produce that state, but Pass 1's non-`CLEAN` check
|
|
635
|
+
still does, so the guard stays. There is no linked issue to mark `ai-blocked` and
|
|
636
|
+
no worktree to enter, and an agent has no business rewriting a bot's lockfile.
|
|
637
|
+
Pass 1 assigns it and counts it as `rev`; here it simply waits for a human.
|
|
638
|
+
Everything below applies only to PRs this loop opened from an `ai-ready` issue.
|
|
616
639
|
|
|
617
640
|
**PRs labelled `ai-changes`.** Count prior `ai-changes` applications from the
|
|
618
641
|
timeline:
|