@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.
@@ -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
- * `.matrix` is this user's LAN — both produced real commits in the #327 set.
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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: 'list',
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.1",
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": "^5.6.2",
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": "^1.23.1",
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": "^29.1.1",
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`. It exists because a pass label currently means both "clean" and
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
- └─ round 3 ─> ai-blocked
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. (`/_loop-status` is the
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-changes` if **either** holds:
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. Dev-only
587
- > minor/patch bumps with green CI get the pass label. **If you cannot determine a
588
- > package's type, treat it as production and block**; failing closed is correct here.
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
- > Be sparing with `ai-notes` here specifically: it suppresses auto-merge, so a
599
- > reflexive note on every dependency bump wedges the one path that runs
600
- > unattended. A truncated body you could not fully read **is** worth a note; 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.** There is no linked issue to mark `ai-blocked` and no worktree to enter, and
613
- an agent has no business rewriting a bot's lockfile. It simply waits for a human,
614
- and Pass 5 counts it as `⚠<n>held`. Everything below applies only to PRs this loop
615
- opened from an `ai-ready` issue.
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: