@bensandee/tooling 0.94.0 → 0.96.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.94.0",
3
+ "version": "0.96.0",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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
@@ -637,15 +698,19 @@
637
698
  "additionalProperties": false
638
699
  },
639
700
  "agent": {
640
- "description": "agent:start behaviour overrides. Opt-in: see agent.autonomy for the interior-autonomy gate.",
701
+ "description": "agent:start behaviour overrides. Opt-in: agent.enabled is the consent gate -- every other key in the block does nothing without it.",
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": {
648
- "description": "Opt into interior autonomy (default false). When a phase's latest rationale reports readiness \"ready\" with an empty openQuestions[] and the review phase is clean, the agent applies the existing plan -> implement -> review -> park-at-mergeable advance itself instead of parking for a human. Never applies to the design/spec gate or the final merge from the mergeable terminal -- those always wait on a human. Also arms the trunk-red detector: when this repo's default branch combined status settles red, the agent files a tracking issue naming the failing checks and labels it agent/spec, starting the fix through the ordinary spec/plan/implement/review pipeline. It also closes that issue, with a comment, if the default branch recovers on its own before a human acts on it. Also arms the world-changed retrigger sweep: a pull request labelled agent-flags/autopilot that is parked at a -stuck or -failed terminal is re-queued when the fact its park recorded stops being true (the default branch went green, or moved past a conflicted base).",
709
+ "description": "Retired and ignored -- it armed neither the advance rung nor the sweeps it once did. Advance is now the unit's own `agent-flags/advance` or `agent-flags/autopilot` label, and the trunk-red detector and world-changed retrigger sweep read `agent.trunkRed`. The key is still accepted so an existing config keeps syncing; the agent warns once per launch for a repo that still sets it.",
710
+ "type": "boolean"
711
+ },
712
+ "trunkRed": {
713
+ "description": "Whether this repo's trunk-red detector and world-changed retrigger sweep run (default: on). The detector files a tracking issue when the default branch's combined status settles red and labels it agent/spec, and closes that issue with a comment if the branch recovers on its own before a human acts on it. The retrigger sweep re-queues a pull request labelled agent-flags/autopilot parked at a -stuck or -failed terminal when the fact its park recorded stops being true (the default branch went green, or moved past a conflicted base). A missing key means on; false disarms both.",
649
714
  "type": "boolean"
650
715
  },
651
716
  "preRunRebase": {
@@ -779,6 +844,14 @@
779
844
  }
780
845
  },
781
846
  "additionalProperties": false
847
+ },
848
+ "chatIssueRepos": {
849
+ "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.",
850
+ "type": "array",
851
+ "items": {
852
+ "type": "string",
853
+ "pattern": "^(?!\\.\\.?\\/)[\\w.-]+\\/(?!\\.\\.?$)[\\w.-]+$"
854
+ }
782
855
  }
783
856
  },
784
857
  "additionalProperties": false