@bensandee/tooling 0.93.1 → 0.95.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bensandee/tooling",
3
- "version": "0.93.1",
3
+ "version": "0.95.0",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -38,9 +38,9 @@
38
38
  "json5": "^2.2.3",
39
39
  "jsonc-parser": "^3.3.1",
40
40
  "picomatch": "^4.0.7",
41
- "smol-toml": "^1.9.0",
41
+ "smol-toml": "^1.9.1",
42
42
  "yaml": "^2.9.1",
43
- "zod": "^4.6.5"
43
+ "zod": "^4.6.7"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@bensandee/claude-hooks": "0.12.0",
@@ -49,14 +49,14 @@
49
49
  "@changesets/cli": "3.0.3",
50
50
  "@release-it/bumper": "8.0.1",
51
51
  "@types/cross-spawn": "6.0.6",
52
- "@types/node": "24.19.1",
52
+ "@types/node": "24.19.2",
53
53
  "@types/picomatch": "4.0.3",
54
- "commit-and-tag-version": "13.2.1",
55
- "modern-web-guidance": "0.0.193",
56
- "oxfmt": "0.72.0",
57
- "prettier": "3.9.9",
54
+ "commit-and-tag-version": "13.3.0",
55
+ "modern-web-guidance": "0.0.194",
56
+ "oxfmt": "0.73.0",
57
+ "prettier": "3.9.10",
58
58
  "release-it": "21.1.1",
59
- "tsdown": "0.23.0"
59
+ "tsdown": "0.23.1"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsdown",
@@ -370,7 +370,7 @@
370
370
  "maximum": 9007199254740991
371
371
  },
372
372
  "crossPackage": {
373
- "description": "Count dependency edges that cross a workspace package boundary (default true). On by default because the alternative measures the wrong thing: with it off, moving a module into its own package removes its importers from every metric, so extracting a package hides a hub rather than fixing it. A monorepo adopting the default sees fan-in and fan-out rise once, and adopts that rise as a ceiling-only pull request -- a branch whose diff changes nothing but health-baseline.json and .tooling.json, which a person merges; a single-package repo has no workspace siblings to resolve, so no new edge enters its graph. Set it to false only where the package boundary is a curated API surface an edge across genuinely costs less, or to hold a baseline on the old measurement basis.",
373
+ "description": "Count dependency edges that cross a workspace package boundary (default true). On by default because the alternative measures the wrong thing: with it off, moving a module into its own package removes its importers from every metric, so extracting a package hides a hub rather than fixing it. A monorepo adopting the default sees fan-in and fan-out rise once, and adopts that rise with `bst health:raise` in the pull request that adopts the setting, which a person merges; a single-package repo has no workspace siblings to resolve, so no new edge enters its graph. Set it to false only where the package boundary is a curated API surface an edge across genuinely costs less, or to hold a baseline on the old measurement basis.",
374
374
  "type": "boolean"
375
375
  },
376
376
  "history": {
@@ -388,8 +388,69 @@
388
388
  "minimum": 0,
389
389
  "maximum": 9007199254740991
390
390
  },
391
+ "sizeBudgets": {
392
+ "description": "Artifact size budgets: id -> what to measure. Each id records one ceiling in health-baseline.json's sizeBudgets, and `health:check` fails when the measured number exceeds it. Absent means no size metric at all. A budget with no recorded value fails as unrecorded until `--update-baseline` records it; a file or glob that measures nothing fails rather than reading as zero. Measured at the working tree only, never at the merge base, so a crossing always names this branch. Raising a recorded value is a raise: write it with `bst health:raise`, in the pull request whose change crossed the budget.",
393
+ "type": "object",
394
+ "propertyNames": {
395
+ "type": "string",
396
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
397
+ },
398
+ "additionalProperties": {
399
+ "anyOf": [
400
+ {
401
+ "type": "object",
402
+ "properties": {
403
+ "file": {
404
+ "type": "string",
405
+ "minLength": 1,
406
+ "description": "Repo-relative path of the one file whose byte size is measured."
407
+ }
408
+ },
409
+ "required": ["file"],
410
+ "description": "A size budget measuring the bytes of a single file."
411
+ },
412
+ {
413
+ "type": "object",
414
+ "properties": {
415
+ "glob": {
416
+ "type": "string",
417
+ "minLength": 1,
418
+ "description": "Glob matched against repo-relative paths under the target."
419
+ },
420
+ "aggregate": {
421
+ "type": "string",
422
+ "enum": ["total", "largest"],
423
+ "description": "How the matched files become one number: the sum of their bytes, or the biggest single file."
424
+ }
425
+ },
426
+ "required": ["glob", "aggregate"],
427
+ "description": "A size budget measuring the bytes of every file a glob matches."
428
+ },
429
+ {
430
+ "type": "object",
431
+ "properties": {
432
+ "script": {
433
+ "type": "string",
434
+ "minLength": 1,
435
+ "description": "Package script run as `pnpm run --silent <script>`; it reports one number through the reserved measure marker."
436
+ },
437
+ "files": {
438
+ "type": "array",
439
+ "items": {
440
+ "type": "string",
441
+ "minLength": 1
442
+ },
443
+ "description": "Globs naming the code that computes the number: a match in the branch's diff marks the measurement as changed by this branch."
444
+ }
445
+ },
446
+ "required": ["script", "files"],
447
+ "description": "A size budget measured by running a package script that prints one number."
448
+ }
449
+ ]
450
+ }
451
+ },
391
452
  "exempt": {
392
- "description": "Per-metric, per-module exemption, keyed first by metric name (fanInOutliers, fanOutOutliers or crowdedDirectories) then by the repo-relative path exactly as `health:summary --explain` prints it: a module path for the two fan metrics, a directory path for crowdedDirectories. Each path maps to a required, non-empty justification string. An exempt module or directory is listed in the summary with its count and reason, carries no ceiling in health-baseline.json, and is counted in no metric -- it is out of the gate, not suppressed inside it. Adding one is a raise: it travels alone in a ceiling-only pull request, never beside code. An exempt module stays fully in the graph -- its edges still count toward every other module's fan-in/fan-out.",
453
+ "description": "Per-metric, per-module exemption, keyed first by metric name (fanInOutliers, fanOutOutliers or crowdedDirectories) then by the repo-relative path exactly as `health:summary --explain` prints it: a module path for the two fan metrics, a directory path for crowdedDirectories. A key may instead be a picomatch glob (e.g. `packages/contract/src/core/**`), matched against the path itself and never its ancestors; an exact key wins over a glob that also matches, and otherwise the first matching glob's reason applies. A glob also covers modules added later, which still show in the summary with their counts. Each path maps to a required, non-empty justification string. An exempt module or directory is listed in the summary with its count and reason, carries no ceiling in health-baseline.json, and is counted in no metric -- it is out of the gate, not suppressed inside it. Adding one is a raise: write it with `bst health:raise --exempt`, in the pull request whose code made the module a hub. An exempt module stays fully in the graph -- its edges still count toward every other module's fan-in/fan-out.",
393
454
  "type": "object",
394
455
  "propertyNames": {
395
456
  "type": "string",
@@ -535,7 +596,7 @@
535
596
  }
536
597
  },
537
598
  "staleDays": {
538
- "description": "Days before an untouched active doc is flagged (default 30; 0 disables the rule).",
599
+ "description": "Days before an unchanged active doc is flagged (default 30; 0 disables the rule).",
539
600
  "type": "integer",
540
601
  "minimum": 0,
541
602
  "maximum": 9007199254740991
@@ -579,9 +640,9 @@
579
640
  "const": false
580
641
  },
581
642
  "defaultBranch": {
582
- "description": "Repository default branch name, B. Drives the generated CI `on.push` trigger and every default-branch gate in `ci.yml` and `release.yml`, the release path (`release:changesets`, the version PR, `release:trigger`) and `checks:run`'s coverage gate. Generated output uses 'main' when unset; `checks:run` still falls back to `origin/HEAD`. Must be a plain branch name: ASCII letters, digits, '.', '_', '-' and '/'.",
643
+ "description": "Repository default branch name. Drives the generated CI `on.push` trigger and every default-branch gate in `ci.yml` and `release.yml`, the release path (`release:changesets`, the version PR, `release:trigger`) and `checks:run`'s coverage gate. Generated output uses 'main' when unset; `checks:run` still falls back to `origin/HEAD`. Must be a plain branch name: ASCII letters, digits, '.', '_', '-' and '/', with no leading 'refs/'.",
583
644
  "type": "string",
584
- "pattern": "^(?!.*\\/\\/)(?!.*\\.\\.)(?!.*\\/$)(?!.*\\.lock$)[A-Za-z0-9][A-Za-z0-9._/-]*$"
645
+ "pattern": "^(?!refs\\/)(?!.*\\/\\/)(?!.*\\.\\.)(?!.*\\/$)(?!.*\\.lock$)[A-Za-z0-9][A-Za-z0-9._/-]*$"
585
646
  },
586
647
  "unmanagedDevDependencies": {
587
648
  "description": "devDependency names repo:sync must never add or re-pin. Escape hatch for holding a tooling-managed dep (oxlint, vitest, knip, ...) at a deliberately off-canonical version; without it, repo:sync raises any managed devDep whose version has drifted below the canonical pin.",
@@ -641,7 +702,7 @@
641
702
  "type": "object",
642
703
  "properties": {
643
704
  "enabled": {
644
- "description": "The repo's explicit consent to the agent (default false). Only `true`, read from the repo's **default branch**, lets the agent work the repo: claim units, run sweeps, recover orphans, merge, act on a chat proposal or start a new spec. A missing key, `false`, a missing `.tooling.json` or a read that fails all mean not enabled, and the answer is re-read within about a minute. Every other `agent.*` key below has no effect without this one; `repo:sync` warns when the block sets one of them and this key is not `true`. Where this is `true`, `repo:sync` also installs `@bensandee/agent-cli`. `bst setup:agent` sets it to true and runs the sync. Provisioning the agent's labels is not consent, and is never gated by this key.",
705
+ "description": "The repo's explicit consent to the agent (default false). Only `true`, read from the repo's **default branch**, lets the agent work the repo: claim units, run sweeps, recover orphans, merge, act on a chat proposal or start a new spec. A missing key, `false`, a missing `.tooling.json` or a read that fails all mean not enabled, and the answer is re-read within about a minute. Every other `agent.*` key below has no effect without this one; `repo:sync` warns when the block sets one of them and this key is not `true`. Where this is `true`, `repo:sync` also installs `@bensandee/agent-cli`. `pnpm exec bst setup:agent` sets it to true and runs the sync. Provisioning the agent's labels is not consent, and is never gated by this key.",
645
706
  "type": "boolean"
646
707
  },
647
708
  "autonomy": {
@@ -649,7 +710,7 @@
649
710
  "type": "boolean"
650
711
  },
651
712
  "preRunRebase": {
652
- "description": "Merge the current default branch into the work branch before the implement (execute) phase's container starts (default true). A clean merge or an already-up-to-date branch runs the container as is; a conflicting merge is aborted, recorded on the run's status comment, and the run proceeds on the un-synced branch -- it never fails the run. Two consecutive conflicting attempts hand the unit to agent/repair before any container runs; a timeout or a failed merge never counts toward that pair. Set false to skip the attempt entirely for this repo: no fetch, no merge, no abort, and the branch is worked exactly as cloned.",
713
+ "description": "Merge the current default branch into the work branch before the container starts (default true) -- for an implement (execute) run, and for a spec or plan rerun on an existing branch. A clean merge or an already-up-to-date branch runs the container as is; a conflicting merge is aborted, recorded on the run's status comment, and the run proceeds on the un-synced branch -- a conflict, timeout or failed merge never fails the run, for any phase. Only the execute phase keeps the two-strike rule: two consecutive conflicting attempts hand the unit to agent/repair before any container runs, while a docs run just tries again on its next slice. Set false to skip the attempt entirely for this repo, in every phase: no fetch, no merge, no abort, and the branch is worked exactly as cloned.",
653
714
  "type": "boolean"
654
715
  },
655
716
  "backlog": {
@@ -779,6 +840,14 @@
779
840
  }
780
841
  },
781
842
  "additionalProperties": false
843
+ },
844
+ "chatIssueRepos": {
845
+ "description": "Repositories with a DIFFERENT owner that a control-panel chat from this repository may file an issue in, each one a full `owner/name`, never a wildcard. A repository with the same owner needs no entry, and an entry grants nothing on its own: the target must have enabled the agent too. Read from the repo's DEFAULT BRANCH at the moment a card is applied, so a pull request cannot widen the reach of its own chat. Like every other `agent.*` key, this has no effect unless `agent.enabled` is true.",
846
+ "type": "array",
847
+ "items": {
848
+ "type": "string",
849
+ "pattern": "^(?!\\.\\.?\\/)[\\w.-]+\\/(?!\\.\\.?$)[\\w.-]+$"
850
+ }
782
851
  }
783
852
  },
784
853
  "additionalProperties": false