@bensandee/tooling 0.89.1 → 0.90.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/dist/bin.mjs +332 -220
- package/package.json +4 -4
- package/tooling.schema.json +5 -1
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { accessSync, appendFileSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
3
|
-
import * as path$1 from "node:path";
|
|
4
|
-
import path from "node:path";
|
|
3
|
+
import path, * as path$1 from "node:path";
|
|
5
4
|
import { parseArgs, parseEnv } from "node:util";
|
|
6
5
|
import { FatalError, TransientError, UnexpectedError, describeError, describeError as describeError$1, redact, redact as redact$1 } from "@bensandee/common";
|
|
7
6
|
import { defineCommand, renderUsage, runCommand, runMain } from "citty";
|
|
@@ -176,7 +175,7 @@ function loadEnvFileForBin(rawArgs, bin, io = processIo) {
|
|
|
176
175
|
//#region src/env-file-preload.ts
|
|
177
176
|
const envFileLoad = loadEnvFileForBin(process.argv.slice(2), {
|
|
178
177
|
name: "bst",
|
|
179
|
-
version: "0.
|
|
178
|
+
version: "0.90.0"
|
|
180
179
|
});
|
|
181
180
|
//#endregion
|
|
182
181
|
//#region ../cli-kit/src/logger.ts
|
|
@@ -828,7 +827,7 @@ const DockerImageOverrideSchema = z.strictObject({
|
|
|
828
827
|
buildArgs: z.record(z.string(), z.string()).optional().meta({ description: "Build args passed to `docker build` for this package. Values support ${VAR} and ${VAR:-default} expansion against process.env." }),
|
|
829
828
|
imageName: z.string().min(1).optional().meta({ description: "Image name used for docker:build tags, docker:publish refs, and docker:check cross-referencing. Falls back to the derived name: the package.json name for the root package (npm scope stripped), or {root-name}-{package-name} for a monorepo package. A value that already carries a registry host (e.g. 'ghcr.io/me/app') is used as written by docker:publish, with no registryNamespace prefix added, and must name the registry this run logs in to -- one naming any other host refuses before login." }),
|
|
830
829
|
version: z.string().optional().meta({ description: "Version used for docker:publish tags; overrides the version resolved from the enclosing package." }),
|
|
831
|
-
publish: z.boolean().optional().meta({ description: "The publish decision either way: true keeps an image published -- including one
|
|
830
|
+
publish: z.boolean().optional().meta({ description: "The publish decision either way: true keeps an image published -- including one whose compose service is declared only in the check overlay, which is not published by default -- and false excludes it while publishDocker stays true. Unset means the discovery default, which publishes everything except a check-only image." })
|
|
832
831
|
});
|
|
833
832
|
const DockerMapSchema = z.record(z.string(), DockerImageOverrideSchema);
|
|
834
833
|
const TolerantDockerMapSchema = z.record(z.string(), tolerantBlock(DockerImageOverrideSchema));
|
|
@@ -1701,6 +1700,7 @@ const LintSweepConfigSchema = z.strictObject({
|
|
|
1701
1700
|
});
|
|
1702
1701
|
const AgentEgressConfigSchema = z.strictObject({ browseHosts: z.array(z.string().min(1)).optional().meta({ description: "Host patterns this repo's agent containers may fetch from, on top of the hosts every run needs: an exact host (`fastdl.mongodb.org`) or a wildcard over its leftmost label (`*.example.com`). No ports. These join the browse lane, which serves GET and HEAD only." }) });
|
|
1703
1702
|
const AgentConfigSchema = z.strictObject({
|
|
1703
|
+
enabled: z.boolean().optional().meta({ 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`. Provisioning the agent's labels is not consent, and is never gated by this key." }),
|
|
1704
1704
|
autonomy: z.boolean().optional().meta({ 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)." }),
|
|
1705
1705
|
preRunRebase: z.boolean().optional().meta({ 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." }),
|
|
1706
1706
|
backlog: z.boolean().optional().meta({ description: "Whether the agent may self-trigger backlog runs consuming this repo's .agent/findings/ ledger (default: on wherever the ledger exists, which only happens in a repo whose own review loop wrote it). A backlog run opens a pull request that fixes or refutes the pending findings; false disarms it, leaving the ledger to accumulate for a human. A run starts only once at least three findings are pending or one is older than seven days." }),
|
|
@@ -3375,7 +3375,7 @@ function localTarget(call, entry) {
|
|
|
3375
3375
|
}
|
|
3376
3376
|
//#endregion
|
|
3377
3377
|
//#region src/release/docker-check-only-phase.ts
|
|
3378
|
-
const DOCKER_CHECK_ONLY_PHASE = "
|
|
3378
|
+
const DOCKER_CHECK_ONLY_PHASE = "enforce";
|
|
3379
3379
|
//#endregion
|
|
3380
3380
|
//#region src/release/docker.ts
|
|
3381
3381
|
function defaultPublish(checkOnlyIn, phase) {
|
|
@@ -3816,7 +3816,7 @@ function runDockerBuild(executor, config) {
|
|
|
3816
3816
|
log$1.info(`Built ${pkg.imageName}:latest`);
|
|
3817
3817
|
return { packages: [pkg] };
|
|
3818
3818
|
}
|
|
3819
|
-
const packages = detectDockerPackages(executor, config.cwd, repoName, config.checkOnlyPhase ?? "
|
|
3819
|
+
const packages = detectDockerPackages(executor, config.cwd, repoName, config.checkOnlyPhase ?? "enforce");
|
|
3820
3820
|
if (packages.length === 0) {
|
|
3821
3821
|
log$1.info("No packages with docker config found");
|
|
3822
3822
|
return { packages: [] };
|
|
@@ -3976,7 +3976,7 @@ function reportCheckOnlyImages(packages, phase) {
|
|
|
3976
3976
|
}
|
|
3977
3977
|
}
|
|
3978
3978
|
function runDockerPublish(executor, config) {
|
|
3979
|
-
const checkOnlyPhase = config.checkOnlyPhase ?? "
|
|
3979
|
+
const checkOnlyPhase = config.checkOnlyPhase ?? "enforce";
|
|
3980
3980
|
const { packages } = runDockerBuild(executor, {
|
|
3981
3981
|
cwd: config.cwd,
|
|
3982
3982
|
packageDir: void 0,
|
|
@@ -4439,8 +4439,8 @@ function buildWorkflowYaml(options) {
|
|
|
4439
4439
|
}
|
|
4440
4440
|
//#endregion
|
|
4441
4441
|
//#region src/generators/mise-pin.ts
|
|
4442
|
-
const MISE_VERSION = "2026.10.
|
|
4443
|
-
const MISE_LINUX_X64_SHA256 = "
|
|
4442
|
+
const MISE_VERSION = "2026.10.8";
|
|
4443
|
+
const MISE_LINUX_X64_SHA256 = "c72477ae6c3a8e7d817a322f43da70e4643e75ee5c21265f1c19c894273b7428";
|
|
4444
4444
|
const SETUP_MISE_ACTION_URL = "https://code.orangebikelabs.com/bensandee/common/actions/setup-mise";
|
|
4445
4445
|
const SETUP_MISE_ACTION_TAG = "setup-mise@latest";
|
|
4446
4446
|
const SETUP_MISE_ACTION_SHA = "ebe622ef0d24c7efec4a5f6093ddb18500900e24";
|
|
@@ -4858,6 +4858,39 @@ function diagnoseCheckScriptForwardingAt(targetDir) {
|
|
|
4858
4858
|
return diagnoseCheckScriptForwarding(scripts);
|
|
4859
4859
|
}
|
|
4860
4860
|
//#endregion
|
|
4861
|
+
//#region src/generators/agent-cli-dep.ts
|
|
4862
|
+
const AGENT_CLI_PACKAGE = "@bensandee/agent-cli";
|
|
4863
|
+
const AgentEnabledSliceSchema = z.looseObject({ agent: z.looseObject({ enabled: z.boolean().optional() }).optional() });
|
|
4864
|
+
function agentEnabled(ctx) {
|
|
4865
|
+
return parseToolingConfigSlice(ctx.read(".tooling.json"), AgentEnabledSliceSchema, { block: "agent" })?.agent?.enabled === true;
|
|
4866
|
+
}
|
|
4867
|
+
function wantsAgentCli(ctx) {
|
|
4868
|
+
if (!agentEnabled(ctx)) return false;
|
|
4869
|
+
if (isWorkspacePackage(ctx, "@bensandee/agent-cli")) return false;
|
|
4870
|
+
return !readUnmanagedDevDepNames(ctx).has(AGENT_CLI_PACKAGE);
|
|
4871
|
+
}
|
|
4872
|
+
function isWorkspacePackage(ctx, packageName) {
|
|
4873
|
+
if (ctx.config.structure !== "monorepo") return false;
|
|
4874
|
+
const packagesDir = path.join(ctx.targetDir, "packages");
|
|
4875
|
+
if (!existsSync(packagesDir)) return false;
|
|
4876
|
+
try {
|
|
4877
|
+
for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
|
|
4878
|
+
if (!entry.isDirectory()) continue;
|
|
4879
|
+
const pkgJsonPath = path.join(packagesDir, entry.name, "package.json");
|
|
4880
|
+
if (!existsSync(pkgJsonPath)) continue;
|
|
4881
|
+
try {
|
|
4882
|
+
if (parsePackageJson(readFileSync(pkgJsonPath, "utf-8"))?.name === packageName) return true;
|
|
4883
|
+
} catch (_error) {}
|
|
4884
|
+
}
|
|
4885
|
+
} catch (_error) {}
|
|
4886
|
+
return false;
|
|
4887
|
+
}
|
|
4888
|
+
const UnmanagedSliceSchema = z.object({ unmanagedDevDependencies: UnmanagedDevDependenciesSchema.optional() });
|
|
4889
|
+
function readUnmanagedDevDepNames(ctx) {
|
|
4890
|
+
const slice = parseToolingConfigSlice(ctx.read(".tooling.json"), UnmanagedSliceSchema, { block: "unmanagedDevDependencies" });
|
|
4891
|
+
return new Set(slice?.unmanagedDevDependencies ?? []);
|
|
4892
|
+
}
|
|
4893
|
+
//#endregion
|
|
4861
4894
|
//#region src/generators/package-json.ts
|
|
4862
4895
|
const NODE_ENGINES_FLOOR = "24.13.0";
|
|
4863
4896
|
const ENGINES_NODE = `>=${NODE_ENGINES_FLOOR}`;
|
|
@@ -4966,18 +4999,18 @@ function perPackageDevDeps(config) {
|
|
|
4966
4999
|
"@types/node": "24.19.1",
|
|
4967
5000
|
"@vitest/coverage-v8": "5.0.3",
|
|
4968
5001
|
"commit-and-tag-version": "13.2.1",
|
|
4969
|
-
"knip": "6.
|
|
4970
|
-
"lefthook": "2.1
|
|
4971
|
-
"modern-web-guidance": "0.0.
|
|
4972
|
-
"oxfmt": "0.
|
|
4973
|
-
"oxlint": "1.
|
|
5002
|
+
"knip": "6.41.0",
|
|
5003
|
+
"lefthook": "2.2.1",
|
|
5004
|
+
"modern-web-guidance": "0.0.193",
|
|
5005
|
+
"oxfmt": "0.72.0",
|
|
5006
|
+
"oxlint": "1.87.0",
|
|
4974
5007
|
"oxlint-tsgolint": "7.0.2003",
|
|
4975
5008
|
"prettier": "3.9.9",
|
|
4976
|
-
"release-it": "21.1.
|
|
5009
|
+
"release-it": "21.1.1",
|
|
4977
5010
|
"tsdown": "0.23.0",
|
|
4978
5011
|
"typescript": "6.0.3",
|
|
4979
5012
|
"vitest": "5.0.3",
|
|
4980
|
-
"pnpm": "11.28.
|
|
5013
|
+
"pnpm": "11.28.5"
|
|
4981
5014
|
}["typescript"];
|
|
4982
5015
|
deps["@types/node"] = {
|
|
4983
5016
|
"@changesets/cli": "3.0.3",
|
|
@@ -4985,18 +5018,18 @@ function perPackageDevDeps(config) {
|
|
|
4985
5018
|
"@types/node": "24.19.1",
|
|
4986
5019
|
"@vitest/coverage-v8": "5.0.3",
|
|
4987
5020
|
"commit-and-tag-version": "13.2.1",
|
|
4988
|
-
"knip": "6.
|
|
4989
|
-
"lefthook": "2.1
|
|
4990
|
-
"modern-web-guidance": "0.0.
|
|
4991
|
-
"oxfmt": "0.
|
|
4992
|
-
"oxlint": "1.
|
|
5021
|
+
"knip": "6.41.0",
|
|
5022
|
+
"lefthook": "2.2.1",
|
|
5023
|
+
"modern-web-guidance": "0.0.193",
|
|
5024
|
+
"oxfmt": "0.72.0",
|
|
5025
|
+
"oxlint": "1.87.0",
|
|
4993
5026
|
"oxlint-tsgolint": "7.0.2003",
|
|
4994
5027
|
"prettier": "3.9.9",
|
|
4995
|
-
"release-it": "21.1.
|
|
5028
|
+
"release-it": "21.1.1",
|
|
4996
5029
|
"tsdown": "0.23.0",
|
|
4997
5030
|
"typescript": "6.0.3",
|
|
4998
5031
|
"vitest": "5.0.3",
|
|
4999
|
-
"pnpm": "11.28.
|
|
5032
|
+
"pnpm": "11.28.5"
|
|
5000
5033
|
}["@types/node"];
|
|
5001
5034
|
}
|
|
5002
5035
|
if (config.builder === "tsdown") deps["tsdown"] = {
|
|
@@ -5005,18 +5038,18 @@ function perPackageDevDeps(config) {
|
|
|
5005
5038
|
"@types/node": "24.19.1",
|
|
5006
5039
|
"@vitest/coverage-v8": "5.0.3",
|
|
5007
5040
|
"commit-and-tag-version": "13.2.1",
|
|
5008
|
-
"knip": "6.
|
|
5009
|
-
"lefthook": "2.1
|
|
5010
|
-
"modern-web-guidance": "0.0.
|
|
5011
|
-
"oxfmt": "0.
|
|
5012
|
-
"oxlint": "1.
|
|
5041
|
+
"knip": "6.41.0",
|
|
5042
|
+
"lefthook": "2.2.1",
|
|
5043
|
+
"modern-web-guidance": "0.0.193",
|
|
5044
|
+
"oxfmt": "0.72.0",
|
|
5045
|
+
"oxlint": "1.87.0",
|
|
5013
5046
|
"oxlint-tsgolint": "7.0.2003",
|
|
5014
5047
|
"prettier": "3.9.9",
|
|
5015
|
-
"release-it": "21.1.
|
|
5048
|
+
"release-it": "21.1.1",
|
|
5016
5049
|
"tsdown": "0.23.0",
|
|
5017
5050
|
"typescript": "6.0.3",
|
|
5018
5051
|
"vitest": "5.0.3",
|
|
5019
|
-
"pnpm": "11.28.
|
|
5052
|
+
"pnpm": "11.28.5"
|
|
5020
5053
|
}["tsdown"];
|
|
5021
5054
|
return deps;
|
|
5022
5055
|
}
|
|
@@ -5028,18 +5061,18 @@ function vitestDevDeps() {
|
|
|
5028
5061
|
"@types/node": "24.19.1",
|
|
5029
5062
|
"@vitest/coverage-v8": "5.0.3",
|
|
5030
5063
|
"commit-and-tag-version": "13.2.1",
|
|
5031
|
-
"knip": "6.
|
|
5032
|
-
"lefthook": "2.1
|
|
5033
|
-
"modern-web-guidance": "0.0.
|
|
5034
|
-
"oxfmt": "0.
|
|
5035
|
-
"oxlint": "1.
|
|
5064
|
+
"knip": "6.41.0",
|
|
5065
|
+
"lefthook": "2.2.1",
|
|
5066
|
+
"modern-web-guidance": "0.0.193",
|
|
5067
|
+
"oxfmt": "0.72.0",
|
|
5068
|
+
"oxlint": "1.87.0",
|
|
5036
5069
|
"oxlint-tsgolint": "7.0.2003",
|
|
5037
5070
|
"prettier": "3.9.9",
|
|
5038
|
-
"release-it": "21.1.
|
|
5071
|
+
"release-it": "21.1.1",
|
|
5039
5072
|
"tsdown": "0.23.0",
|
|
5040
5073
|
"typescript": "6.0.3",
|
|
5041
5074
|
"vitest": "5.0.3",
|
|
5042
|
-
"pnpm": "11.28.
|
|
5075
|
+
"pnpm": "11.28.5"
|
|
5043
5076
|
}["vitest"],
|
|
5044
5077
|
"@vitest/coverage-v8": {
|
|
5045
5078
|
"@changesets/cli": "3.0.3",
|
|
@@ -5047,18 +5080,18 @@ function vitestDevDeps() {
|
|
|
5047
5080
|
"@types/node": "24.19.1",
|
|
5048
5081
|
"@vitest/coverage-v8": "5.0.3",
|
|
5049
5082
|
"commit-and-tag-version": "13.2.1",
|
|
5050
|
-
"knip": "6.
|
|
5051
|
-
"lefthook": "2.1
|
|
5052
|
-
"modern-web-guidance": "0.0.
|
|
5053
|
-
"oxfmt": "0.
|
|
5054
|
-
"oxlint": "1.
|
|
5083
|
+
"knip": "6.41.0",
|
|
5084
|
+
"lefthook": "2.2.1",
|
|
5085
|
+
"modern-web-guidance": "0.0.193",
|
|
5086
|
+
"oxfmt": "0.72.0",
|
|
5087
|
+
"oxlint": "1.87.0",
|
|
5055
5088
|
"oxlint-tsgolint": "7.0.2003",
|
|
5056
5089
|
"prettier": "3.9.9",
|
|
5057
|
-
"release-it": "21.1.
|
|
5090
|
+
"release-it": "21.1.1",
|
|
5058
5091
|
"tsdown": "0.23.0",
|
|
5059
5092
|
"typescript": "6.0.3",
|
|
5060
5093
|
"vitest": "5.0.3",
|
|
5061
|
-
"pnpm": "11.28.
|
|
5094
|
+
"pnpm": "11.28.5"
|
|
5062
5095
|
}["@vitest/coverage-v8"]
|
|
5063
5096
|
};
|
|
5064
5097
|
}
|
|
@@ -5069,18 +5102,18 @@ function rootDevDeps(config) {
|
|
|
5069
5102
|
"@types/node": "24.19.1",
|
|
5070
5103
|
"@vitest/coverage-v8": "5.0.3",
|
|
5071
5104
|
"commit-and-tag-version": "13.2.1",
|
|
5072
|
-
"knip": "6.
|
|
5073
|
-
"lefthook": "2.1
|
|
5074
|
-
"modern-web-guidance": "0.0.
|
|
5075
|
-
"oxfmt": "0.
|
|
5076
|
-
"oxlint": "1.
|
|
5105
|
+
"knip": "6.41.0",
|
|
5106
|
+
"lefthook": "2.2.1",
|
|
5107
|
+
"modern-web-guidance": "0.0.193",
|
|
5108
|
+
"oxfmt": "0.72.0",
|
|
5109
|
+
"oxlint": "1.87.0",
|
|
5077
5110
|
"oxlint-tsgolint": "7.0.2003",
|
|
5078
5111
|
"prettier": "3.9.9",
|
|
5079
|
-
"release-it": "21.1.
|
|
5112
|
+
"release-it": "21.1.1",
|
|
5080
5113
|
"tsdown": "0.23.0",
|
|
5081
5114
|
"typescript": "6.0.3",
|
|
5082
5115
|
"vitest": "5.0.3",
|
|
5083
|
-
"pnpm": "11.28.
|
|
5116
|
+
"pnpm": "11.28.5"
|
|
5084
5117
|
}["lefthook"] };
|
|
5085
5118
|
if (config.stack !== "none") {
|
|
5086
5119
|
deps["knip"] = {
|
|
@@ -5089,18 +5122,18 @@ function rootDevDeps(config) {
|
|
|
5089
5122
|
"@types/node": "24.19.1",
|
|
5090
5123
|
"@vitest/coverage-v8": "5.0.3",
|
|
5091
5124
|
"commit-and-tag-version": "13.2.1",
|
|
5092
|
-
"knip": "6.
|
|
5093
|
-
"lefthook": "2.1
|
|
5094
|
-
"modern-web-guidance": "0.0.
|
|
5095
|
-
"oxfmt": "0.
|
|
5096
|
-
"oxlint": "1.
|
|
5125
|
+
"knip": "6.41.0",
|
|
5126
|
+
"lefthook": "2.2.1",
|
|
5127
|
+
"modern-web-guidance": "0.0.193",
|
|
5128
|
+
"oxfmt": "0.72.0",
|
|
5129
|
+
"oxlint": "1.87.0",
|
|
5097
5130
|
"oxlint-tsgolint": "7.0.2003",
|
|
5098
5131
|
"prettier": "3.9.9",
|
|
5099
|
-
"release-it": "21.1.
|
|
5132
|
+
"release-it": "21.1.1",
|
|
5100
5133
|
"tsdown": "0.23.0",
|
|
5101
5134
|
"typescript": "6.0.3",
|
|
5102
5135
|
"vitest": "5.0.3",
|
|
5103
|
-
"pnpm": "11.28.
|
|
5136
|
+
"pnpm": "11.28.5"
|
|
5104
5137
|
}["knip"];
|
|
5105
5138
|
deps["oxlint"] = {
|
|
5106
5139
|
"@changesets/cli": "3.0.3",
|
|
@@ -5108,18 +5141,18 @@ function rootDevDeps(config) {
|
|
|
5108
5141
|
"@types/node": "24.19.1",
|
|
5109
5142
|
"@vitest/coverage-v8": "5.0.3",
|
|
5110
5143
|
"commit-and-tag-version": "13.2.1",
|
|
5111
|
-
"knip": "6.
|
|
5112
|
-
"lefthook": "2.1
|
|
5113
|
-
"modern-web-guidance": "0.0.
|
|
5114
|
-
"oxfmt": "0.
|
|
5115
|
-
"oxlint": "1.
|
|
5144
|
+
"knip": "6.41.0",
|
|
5145
|
+
"lefthook": "2.2.1",
|
|
5146
|
+
"modern-web-guidance": "0.0.193",
|
|
5147
|
+
"oxfmt": "0.72.0",
|
|
5148
|
+
"oxlint": "1.87.0",
|
|
5116
5149
|
"oxlint-tsgolint": "7.0.2003",
|
|
5117
5150
|
"prettier": "3.9.9",
|
|
5118
|
-
"release-it": "21.1.
|
|
5151
|
+
"release-it": "21.1.1",
|
|
5119
5152
|
"tsdown": "0.23.0",
|
|
5120
5153
|
"typescript": "6.0.3",
|
|
5121
5154
|
"vitest": "5.0.3",
|
|
5122
|
-
"pnpm": "11.28.
|
|
5155
|
+
"pnpm": "11.28.5"
|
|
5123
5156
|
}["oxlint"];
|
|
5124
5157
|
Object.assign(deps, vitestDevDeps());
|
|
5125
5158
|
}
|
|
@@ -5129,37 +5162,21 @@ function rootDevDeps(config) {
|
|
|
5129
5162
|
"@types/node": "24.19.1",
|
|
5130
5163
|
"@vitest/coverage-v8": "5.0.3",
|
|
5131
5164
|
"commit-and-tag-version": "13.2.1",
|
|
5132
|
-
"knip": "6.
|
|
5133
|
-
"lefthook": "2.1
|
|
5134
|
-
"modern-web-guidance": "0.0.
|
|
5135
|
-
"oxfmt": "0.
|
|
5136
|
-
"oxlint": "1.
|
|
5165
|
+
"knip": "6.41.0",
|
|
5166
|
+
"lefthook": "2.2.1",
|
|
5167
|
+
"modern-web-guidance": "0.0.193",
|
|
5168
|
+
"oxfmt": "0.72.0",
|
|
5169
|
+
"oxlint": "1.87.0",
|
|
5137
5170
|
"oxlint-tsgolint": "7.0.2003",
|
|
5138
5171
|
"prettier": "3.9.9",
|
|
5139
|
-
"release-it": "21.1.
|
|
5172
|
+
"release-it": "21.1.1",
|
|
5140
5173
|
"tsdown": "0.23.0",
|
|
5141
5174
|
"typescript": "6.0.3",
|
|
5142
5175
|
"vitest": "5.0.3",
|
|
5143
|
-
"pnpm": "11.28.
|
|
5176
|
+
"pnpm": "11.28.5"
|
|
5144
5177
|
}["oxlint-tsgolint"];
|
|
5145
5178
|
return deps;
|
|
5146
5179
|
}
|
|
5147
|
-
function isWorkspacePackage(ctx, packageName) {
|
|
5148
|
-
if (ctx.config.structure !== "monorepo") return false;
|
|
5149
|
-
const packagesDir = path.join(ctx.targetDir, "packages");
|
|
5150
|
-
if (!existsSync(packagesDir)) return false;
|
|
5151
|
-
try {
|
|
5152
|
-
for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
|
|
5153
|
-
if (!entry.isDirectory()) continue;
|
|
5154
|
-
const pkgJsonPath = path.join(packagesDir, entry.name, "package.json");
|
|
5155
|
-
if (!existsSync(pkgJsonPath)) continue;
|
|
5156
|
-
try {
|
|
5157
|
-
if (parsePackageJson(readFileSync(pkgJsonPath, "utf-8"))?.name === packageName) return true;
|
|
5158
|
-
} catch (_error) {}
|
|
5159
|
-
}
|
|
5160
|
-
} catch (_error) {}
|
|
5161
|
-
return false;
|
|
5162
|
-
}
|
|
5163
5180
|
function addReleaseDeps(deps, config) {
|
|
5164
5181
|
switch (config.releaseStrategy) {
|
|
5165
5182
|
case "release-it":
|
|
@@ -5169,18 +5186,18 @@ function addReleaseDeps(deps, config) {
|
|
|
5169
5186
|
"@types/node": "24.19.1",
|
|
5170
5187
|
"@vitest/coverage-v8": "5.0.3",
|
|
5171
5188
|
"commit-and-tag-version": "13.2.1",
|
|
5172
|
-
"knip": "6.
|
|
5173
|
-
"lefthook": "2.1
|
|
5174
|
-
"modern-web-guidance": "0.0.
|
|
5175
|
-
"oxfmt": "0.
|
|
5176
|
-
"oxlint": "1.
|
|
5189
|
+
"knip": "6.41.0",
|
|
5190
|
+
"lefthook": "2.2.1",
|
|
5191
|
+
"modern-web-guidance": "0.0.193",
|
|
5192
|
+
"oxfmt": "0.72.0",
|
|
5193
|
+
"oxlint": "1.87.0",
|
|
5177
5194
|
"oxlint-tsgolint": "7.0.2003",
|
|
5178
5195
|
"prettier": "3.9.9",
|
|
5179
|
-
"release-it": "21.1.
|
|
5196
|
+
"release-it": "21.1.1",
|
|
5180
5197
|
"tsdown": "0.23.0",
|
|
5181
5198
|
"typescript": "6.0.3",
|
|
5182
5199
|
"vitest": "5.0.3",
|
|
5183
|
-
"pnpm": "11.28.
|
|
5200
|
+
"pnpm": "11.28.5"
|
|
5184
5201
|
}["release-it"];
|
|
5185
5202
|
if (config.structure === "monorepo") deps["@release-it/bumper"] = {
|
|
5186
5203
|
"@changesets/cli": "3.0.3",
|
|
@@ -5188,18 +5205,18 @@ function addReleaseDeps(deps, config) {
|
|
|
5188
5205
|
"@types/node": "24.19.1",
|
|
5189
5206
|
"@vitest/coverage-v8": "5.0.3",
|
|
5190
5207
|
"commit-and-tag-version": "13.2.1",
|
|
5191
|
-
"knip": "6.
|
|
5192
|
-
"lefthook": "2.1
|
|
5193
|
-
"modern-web-guidance": "0.0.
|
|
5194
|
-
"oxfmt": "0.
|
|
5195
|
-
"oxlint": "1.
|
|
5208
|
+
"knip": "6.41.0",
|
|
5209
|
+
"lefthook": "2.2.1",
|
|
5210
|
+
"modern-web-guidance": "0.0.193",
|
|
5211
|
+
"oxfmt": "0.72.0",
|
|
5212
|
+
"oxlint": "1.87.0",
|
|
5196
5213
|
"oxlint-tsgolint": "7.0.2003",
|
|
5197
5214
|
"prettier": "3.9.9",
|
|
5198
|
-
"release-it": "21.1.
|
|
5215
|
+
"release-it": "21.1.1",
|
|
5199
5216
|
"tsdown": "0.23.0",
|
|
5200
5217
|
"typescript": "6.0.3",
|
|
5201
5218
|
"vitest": "5.0.3",
|
|
5202
|
-
"pnpm": "11.28.
|
|
5219
|
+
"pnpm": "11.28.5"
|
|
5203
5220
|
}["@release-it/bumper"];
|
|
5204
5221
|
break;
|
|
5205
5222
|
case "simple":
|
|
@@ -5209,18 +5226,18 @@ function addReleaseDeps(deps, config) {
|
|
|
5209
5226
|
"@types/node": "24.19.1",
|
|
5210
5227
|
"@vitest/coverage-v8": "5.0.3",
|
|
5211
5228
|
"commit-and-tag-version": "13.2.1",
|
|
5212
|
-
"knip": "6.
|
|
5213
|
-
"lefthook": "2.1
|
|
5214
|
-
"modern-web-guidance": "0.0.
|
|
5215
|
-
"oxfmt": "0.
|
|
5216
|
-
"oxlint": "1.
|
|
5229
|
+
"knip": "6.41.0",
|
|
5230
|
+
"lefthook": "2.2.1",
|
|
5231
|
+
"modern-web-guidance": "0.0.193",
|
|
5232
|
+
"oxfmt": "0.72.0",
|
|
5233
|
+
"oxlint": "1.87.0",
|
|
5217
5234
|
"oxlint-tsgolint": "7.0.2003",
|
|
5218
5235
|
"prettier": "3.9.9",
|
|
5219
|
-
"release-it": "21.1.
|
|
5236
|
+
"release-it": "21.1.1",
|
|
5220
5237
|
"tsdown": "0.23.0",
|
|
5221
5238
|
"typescript": "6.0.3",
|
|
5222
5239
|
"vitest": "5.0.3",
|
|
5223
|
-
"pnpm": "11.28.
|
|
5240
|
+
"pnpm": "11.28.5"
|
|
5224
5241
|
}["commit-and-tag-version"];
|
|
5225
5242
|
break;
|
|
5226
5243
|
case "changesets": deps["@changesets/cli"] = {
|
|
@@ -5229,18 +5246,18 @@ function addReleaseDeps(deps, config) {
|
|
|
5229
5246
|
"@types/node": "24.19.1",
|
|
5230
5247
|
"@vitest/coverage-v8": "5.0.3",
|
|
5231
5248
|
"commit-and-tag-version": "13.2.1",
|
|
5232
|
-
"knip": "6.
|
|
5233
|
-
"lefthook": "2.1
|
|
5234
|
-
"modern-web-guidance": "0.0.
|
|
5235
|
-
"oxfmt": "0.
|
|
5236
|
-
"oxlint": "1.
|
|
5249
|
+
"knip": "6.41.0",
|
|
5250
|
+
"lefthook": "2.2.1",
|
|
5251
|
+
"modern-web-guidance": "0.0.193",
|
|
5252
|
+
"oxfmt": "0.72.0",
|
|
5253
|
+
"oxlint": "1.87.0",
|
|
5237
5254
|
"oxlint-tsgolint": "7.0.2003",
|
|
5238
5255
|
"prettier": "3.9.9",
|
|
5239
|
-
"release-it": "21.1.
|
|
5256
|
+
"release-it": "21.1.1",
|
|
5240
5257
|
"tsdown": "0.23.0",
|
|
5241
5258
|
"typescript": "6.0.3",
|
|
5242
5259
|
"vitest": "5.0.3",
|
|
5243
|
-
"pnpm": "11.28.
|
|
5260
|
+
"pnpm": "11.28.5"
|
|
5244
5261
|
}["@changesets/cli"];
|
|
5245
5262
|
}
|
|
5246
5263
|
}
|
|
@@ -5307,10 +5324,6 @@ function reclaimManagedDevDeps(ctx, pkg, managedDevDeps, unmanaged, changes) {
|
|
|
5307
5324
|
}
|
|
5308
5325
|
pkg.devDependencies = existingDevDeps;
|
|
5309
5326
|
}
|
|
5310
|
-
function readUnmanagedDevDeps(ctx) {
|
|
5311
|
-
const slice = parseToolingConfigSlice(ctx.read(".tooling.json"), z.object({ unmanagedDevDependencies: UnmanagedDevDependenciesSchema.optional() }), { block: "unmanagedDevDependencies" });
|
|
5312
|
-
return new Set(slice?.unmanagedDevDependencies ?? []);
|
|
5313
|
-
}
|
|
5314
5327
|
function isAsciiCheckEnabled(ctx) {
|
|
5315
5328
|
return parseToolingConfigSlice(ctx.read(".tooling.json"), z.object({ ascii: AsciiConfigSchema.optional() }), { block: "ascii" })?.ascii?.enabled === true;
|
|
5316
5329
|
}
|
|
@@ -5345,18 +5358,18 @@ function applyMetadataDefaults(pkg, ctx, changes) {
|
|
|
5345
5358
|
"@types/node": "24.19.1",
|
|
5346
5359
|
"@vitest/coverage-v8": "5.0.3",
|
|
5347
5360
|
"commit-and-tag-version": "13.2.1",
|
|
5348
|
-
"knip": "6.
|
|
5349
|
-
"lefthook": "2.1
|
|
5350
|
-
"modern-web-guidance": "0.0.
|
|
5351
|
-
"oxfmt": "0.
|
|
5352
|
-
"oxlint": "1.
|
|
5361
|
+
"knip": "6.41.0",
|
|
5362
|
+
"lefthook": "2.2.1",
|
|
5363
|
+
"modern-web-guidance": "0.0.193",
|
|
5364
|
+
"oxfmt": "0.72.0",
|
|
5365
|
+
"oxlint": "1.87.0",
|
|
5353
5366
|
"oxlint-tsgolint": "7.0.2003",
|
|
5354
5367
|
"prettier": "3.9.9",
|
|
5355
|
-
"release-it": "21.1.
|
|
5368
|
+
"release-it": "21.1.1",
|
|
5356
5369
|
"tsdown": "0.23.0",
|
|
5357
5370
|
"typescript": "6.0.3",
|
|
5358
5371
|
"vitest": "5.0.3",
|
|
5359
|
-
"pnpm": "11.28.
|
|
5372
|
+
"pnpm": "11.28.5"
|
|
5360
5373
|
}["pnpm"];
|
|
5361
5374
|
const desiredPm = pinnedPnpm ? `pnpm@${pinnedPnpm}` : void 0;
|
|
5362
5375
|
if (desiredPm) {
|
|
@@ -5395,26 +5408,27 @@ function buildManagedDevDeps(ctx) {
|
|
|
5395
5408
|
const devDeps = { ...rootDevDeps(ctx.config) };
|
|
5396
5409
|
if (!isMonorepo) Object.assign(devDeps, perPackageDevDeps(ctx.config));
|
|
5397
5410
|
if (ctx.config.stack !== "none" || ctx.config.ci === "forgejo") devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.15.0";
|
|
5398
|
-
if (!isWorkspacePackage(ctx, "@bensandee/tooling")) devDeps["@bensandee/tooling"] = "0.
|
|
5411
|
+
if (!isWorkspacePackage(ctx, "@bensandee/tooling")) devDeps["@bensandee/tooling"] = "0.90.0";
|
|
5399
5412
|
if (ctx.config.useEslintPlugin && ctx.config.stack !== "none") devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.17.0";
|
|
5413
|
+
if (wantsAgentCli(ctx)) devDeps[AGENT_CLI_PACKAGE] = "0.1.1";
|
|
5400
5414
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
|
|
5401
5415
|
"@changesets/cli": "3.0.3",
|
|
5402
5416
|
"@release-it/bumper": "8.0.1",
|
|
5403
5417
|
"@types/node": "24.19.1",
|
|
5404
5418
|
"@vitest/coverage-v8": "5.0.3",
|
|
5405
5419
|
"commit-and-tag-version": "13.2.1",
|
|
5406
|
-
"knip": "6.
|
|
5407
|
-
"lefthook": "2.1
|
|
5408
|
-
"modern-web-guidance": "0.0.
|
|
5409
|
-
"oxfmt": "0.
|
|
5410
|
-
"oxlint": "1.
|
|
5420
|
+
"knip": "6.41.0",
|
|
5421
|
+
"lefthook": "2.2.1",
|
|
5422
|
+
"modern-web-guidance": "0.0.193",
|
|
5423
|
+
"oxfmt": "0.72.0",
|
|
5424
|
+
"oxlint": "1.87.0",
|
|
5411
5425
|
"oxlint-tsgolint": "7.0.2003",
|
|
5412
5426
|
"prettier": "3.9.9",
|
|
5413
|
-
"release-it": "21.1.
|
|
5427
|
+
"release-it": "21.1.1",
|
|
5414
5428
|
"tsdown": "0.23.0",
|
|
5415
5429
|
"typescript": "6.0.3",
|
|
5416
5430
|
"vitest": "5.0.3",
|
|
5417
|
-
"pnpm": "11.28.
|
|
5431
|
+
"pnpm": "11.28.5"
|
|
5418
5432
|
}["oxfmt"];
|
|
5419
5433
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = {
|
|
5420
5434
|
"@changesets/cli": "3.0.3",
|
|
@@ -5422,18 +5436,18 @@ function buildManagedDevDeps(ctx) {
|
|
|
5422
5436
|
"@types/node": "24.19.1",
|
|
5423
5437
|
"@vitest/coverage-v8": "5.0.3",
|
|
5424
5438
|
"commit-and-tag-version": "13.2.1",
|
|
5425
|
-
"knip": "6.
|
|
5426
|
-
"lefthook": "2.1
|
|
5427
|
-
"modern-web-guidance": "0.0.
|
|
5428
|
-
"oxfmt": "0.
|
|
5429
|
-
"oxlint": "1.
|
|
5439
|
+
"knip": "6.41.0",
|
|
5440
|
+
"lefthook": "2.2.1",
|
|
5441
|
+
"modern-web-guidance": "0.0.193",
|
|
5442
|
+
"oxfmt": "0.72.0",
|
|
5443
|
+
"oxlint": "1.87.0",
|
|
5430
5444
|
"oxlint-tsgolint": "7.0.2003",
|
|
5431
5445
|
"prettier": "3.9.9",
|
|
5432
|
-
"release-it": "21.1.
|
|
5446
|
+
"release-it": "21.1.1",
|
|
5433
5447
|
"tsdown": "0.23.0",
|
|
5434
5448
|
"typescript": "6.0.3",
|
|
5435
5449
|
"vitest": "5.0.3",
|
|
5436
|
-
"pnpm": "11.28.
|
|
5450
|
+
"pnpm": "11.28.5"
|
|
5437
5451
|
}["prettier"];
|
|
5438
5452
|
addReleaseDeps(devDeps, ctx.config);
|
|
5439
5453
|
return devDeps;
|
|
@@ -5442,7 +5456,7 @@ async function generatePackageJson(ctx) {
|
|
|
5442
5456
|
const filePath = "package.json";
|
|
5443
5457
|
const existing = ctx.read(filePath);
|
|
5444
5458
|
const allScripts = buildManagedScripts(ctx);
|
|
5445
|
-
const unmanaged =
|
|
5459
|
+
const unmanaged = readUnmanagedDevDepNames(ctx);
|
|
5446
5460
|
const devDeps = buildManagedDevDeps(ctx);
|
|
5447
5461
|
if (existing) {
|
|
5448
5462
|
const pkg = parsePackageJson(existing);
|
|
@@ -5492,18 +5506,18 @@ async function generatePackageJson(ctx) {
|
|
|
5492
5506
|
"@types/node": "24.19.1",
|
|
5493
5507
|
"@vitest/coverage-v8": "5.0.3",
|
|
5494
5508
|
"commit-and-tag-version": "13.2.1",
|
|
5495
|
-
"knip": "6.
|
|
5496
|
-
"lefthook": "2.1
|
|
5497
|
-
"modern-web-guidance": "0.0.
|
|
5498
|
-
"oxfmt": "0.
|
|
5499
|
-
"oxlint": "1.
|
|
5509
|
+
"knip": "6.41.0",
|
|
5510
|
+
"lefthook": "2.2.1",
|
|
5511
|
+
"modern-web-guidance": "0.0.193",
|
|
5512
|
+
"oxfmt": "0.72.0",
|
|
5513
|
+
"oxlint": "1.87.0",
|
|
5500
5514
|
"oxlint-tsgolint": "7.0.2003",
|
|
5501
5515
|
"prettier": "3.9.9",
|
|
5502
|
-
"release-it": "21.1.
|
|
5516
|
+
"release-it": "21.1.1",
|
|
5503
5517
|
"tsdown": "0.23.0",
|
|
5504
5518
|
"typescript": "6.0.3",
|
|
5505
5519
|
"vitest": "5.0.3",
|
|
5506
|
-
"pnpm": "11.28.
|
|
5520
|
+
"pnpm": "11.28.5"
|
|
5507
5521
|
}["pnpm"]}`
|
|
5508
5522
|
};
|
|
5509
5523
|
ctx.write(filePath, JSON.stringify(pkg, null, 2) + "\n");
|
|
@@ -7828,19 +7842,25 @@ function foreignOwnedBinaries(ctx) {
|
|
|
7828
7842
|
function ignoreBinariesField(binaries) {
|
|
7829
7843
|
return binaries.length > 0 ? `\n ignoreBinaries: ${JSON.stringify(binaries)},` : "";
|
|
7830
7844
|
}
|
|
7831
|
-
function
|
|
7845
|
+
function ignorableDependencies(ctx) {
|
|
7846
|
+
return wantsAgentCli(ctx) ? [AGENT_CLI_PACKAGE] : [];
|
|
7847
|
+
}
|
|
7848
|
+
function ignoreDependenciesField(dependencies) {
|
|
7849
|
+
return dependencies.length > 0 ? `\n ignoreDependencies: ${JSON.stringify(dependencies)},` : "";
|
|
7850
|
+
}
|
|
7851
|
+
function buildKnipConfigSingle(ignoreBinaries, ignoreDependencies) {
|
|
7832
7852
|
return `import type { KnipConfig } from "knip";
|
|
7833
7853
|
|
|
7834
7854
|
export default {
|
|
7835
7855
|
entry: ["src/index.ts"],
|
|
7836
|
-
project: ["src/**/*.ts"],${ignoreBinariesField(ignoreBinaries)}
|
|
7856
|
+
project: ["src/**/*.ts"],${ignoreBinariesField(ignoreBinaries)}${ignoreDependenciesField(ignoreDependencies)}
|
|
7837
7857
|
} satisfies KnipConfig;
|
|
7838
7858
|
`;
|
|
7839
7859
|
}
|
|
7840
|
-
function buildKnipConfigMonorepo(ignoreBinaries) {
|
|
7860
|
+
function buildKnipConfigMonorepo(ignoreBinaries, ignoreDependencies) {
|
|
7841
7861
|
return `import type { KnipConfig } from "knip";
|
|
7842
7862
|
|
|
7843
|
-
export default {${ignoreBinariesField(ignoreBinaries)}
|
|
7863
|
+
export default {${ignoreBinariesField(ignoreBinaries)}${ignoreDependenciesField(ignoreDependencies)}
|
|
7844
7864
|
workspaces: {
|
|
7845
7865
|
".": {
|
|
7846
7866
|
entry: [],
|
|
@@ -7901,16 +7921,17 @@ function hookIsInProjectScope(content, path) {
|
|
|
7901
7921
|
if (patterns === void 0) return true;
|
|
7902
7922
|
return patterns.some((pattern) => picomatch(pattern, { dot: true })(HOOK_FILE));
|
|
7903
7923
|
}
|
|
7904
|
-
function ensureIgnorePattern(content, path, ignoreBinaries) {
|
|
7924
|
+
function ensureIgnorePattern(content, path, ignoreBinaries, ignoreDependencies) {
|
|
7905
7925
|
const needsHooksIgnore = hookIsInProjectScope(content, path);
|
|
7906
|
-
if (isJsonConfig(path)) return ensureIgnoreInJson(content, ignoreBinaries, needsHooksIgnore);
|
|
7907
|
-
return ensureIgnoreInTs(content, ignoreBinaries, needsHooksIgnore);
|
|
7926
|
+
if (isJsonConfig(path)) return ensureIgnoreInJson(content, ignoreBinaries, ignoreDependencies, needsHooksIgnore);
|
|
7927
|
+
return ensureIgnoreInTs(content, ignoreBinaries, ignoreDependencies, needsHooksIgnore);
|
|
7908
7928
|
}
|
|
7909
7929
|
const KnipJsonSchema = z.object({
|
|
7910
7930
|
ignore: z.array(z.string()).optional(),
|
|
7911
|
-
ignoreBinaries: z.array(z.string()).optional()
|
|
7931
|
+
ignoreBinaries: z.array(z.string()).optional(),
|
|
7932
|
+
ignoreDependencies: z.array(z.string()).optional()
|
|
7912
7933
|
}).passthrough();
|
|
7913
|
-
function ensureIgnoreInJson(content, ignoreBinaries, needsHooksIgnore) {
|
|
7934
|
+
function ensureIgnoreInJson(content, ignoreBinaries, ignoreDependencies, needsHooksIgnore) {
|
|
7914
7935
|
let parsedJson;
|
|
7915
7936
|
try {
|
|
7916
7937
|
parsedJson = JSON.parse(content);
|
|
@@ -7931,6 +7952,12 @@ function ensureIgnoreInJson(content, ignoreBinaries, needsHooksIgnore) {
|
|
|
7931
7952
|
parsed.ignoreBinaries = [...parsed.ignoreBinaries ?? [], ...missingBinaries];
|
|
7932
7953
|
changed = true;
|
|
7933
7954
|
}
|
|
7955
|
+
const existingDependencies = new Set(parsed.ignoreDependencies ?? []);
|
|
7956
|
+
const missingDependencies = ignoreDependencies.filter((dependency) => !existingDependencies.has(dependency));
|
|
7957
|
+
if (missingDependencies.length > 0) {
|
|
7958
|
+
parsed.ignoreDependencies = [...parsed.ignoreDependencies ?? [], ...missingDependencies];
|
|
7959
|
+
changed = true;
|
|
7960
|
+
}
|
|
7934
7961
|
if (!changed) return void 0;
|
|
7935
7962
|
return JSON.stringify(parsed, null, 2) + "\n";
|
|
7936
7963
|
}
|
|
@@ -7940,7 +7967,30 @@ function missingIgnoreBinariesInTs(content, binaries) {
|
|
|
7940
7967
|
const arrayContent = match[1] ?? "";
|
|
7941
7968
|
return binaries.filter((binary) => !arrayContent.includes(`"${binary}"`));
|
|
7942
7969
|
}
|
|
7943
|
-
function
|
|
7970
|
+
function missingIgnoreDependenciesInTs(content, dependencies) {
|
|
7971
|
+
const match = /ignoreDependencies:\s*\[([^\]]*)\]/.exec(content);
|
|
7972
|
+
if (!match) return dependencies;
|
|
7973
|
+
const arrayContent = match[1] ?? "";
|
|
7974
|
+
return dependencies.filter((dependency) => !arrayContent.includes(`"${dependency}"`));
|
|
7975
|
+
}
|
|
7976
|
+
function insertIntoTsArray(content, field, entries) {
|
|
7977
|
+
const quoted = entries.map((entry) => `"${entry}"`).join(", ");
|
|
7978
|
+
const arrayPattern = new RegExp("(" + field + ":\\s*\\[)");
|
|
7979
|
+
if (arrayPattern.test(content)) return {
|
|
7980
|
+
content: content.replace(arrayPattern, `$1${quoted}, `),
|
|
7981
|
+
changed: true
|
|
7982
|
+
};
|
|
7983
|
+
const exportPattern = /(export default\s*\{)\n/;
|
|
7984
|
+
if (exportPattern.test(content)) return {
|
|
7985
|
+
content: content.replace(exportPattern, `$1\n ${field}: [${quoted}],\n`),
|
|
7986
|
+
changed: true
|
|
7987
|
+
};
|
|
7988
|
+
return {
|
|
7989
|
+
content,
|
|
7990
|
+
changed: false
|
|
7991
|
+
};
|
|
7992
|
+
}
|
|
7993
|
+
function ensureIgnoreInTs(content, ignoreBinaries, ignoreDependencies, needsHooksIgnore) {
|
|
7944
7994
|
let result = content;
|
|
7945
7995
|
let changed = false;
|
|
7946
7996
|
if (needsHooksIgnore && !result.includes(".claude/hooks")) {
|
|
@@ -7958,18 +8008,15 @@ function ensureIgnoreInTs(content, ignoreBinaries, needsHooksIgnore) {
|
|
|
7958
8008
|
}
|
|
7959
8009
|
const missingBinaries = missingIgnoreBinariesInTs(result, ignoreBinaries);
|
|
7960
8010
|
if (missingBinaries.length > 0) {
|
|
7961
|
-
const
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
changed = true;
|
|
7971
|
-
}
|
|
7972
|
-
}
|
|
8011
|
+
const inserted = insertIntoTsArray(result, "ignoreBinaries", missingBinaries);
|
|
8012
|
+
result = inserted.content;
|
|
8013
|
+
changed = changed || inserted.changed;
|
|
8014
|
+
}
|
|
8015
|
+
const missingDependencies = missingIgnoreDependenciesInTs(result, ignoreDependencies);
|
|
8016
|
+
if (missingDependencies.length > 0) {
|
|
8017
|
+
const inserted = insertIntoTsArray(result, "ignoreDependencies", missingDependencies);
|
|
8018
|
+
result = inserted.content;
|
|
8019
|
+
changed = changed || inserted.changed;
|
|
7973
8020
|
}
|
|
7974
8021
|
return changed ? result : void 0;
|
|
7975
8022
|
}
|
|
@@ -7985,6 +8032,7 @@ async function generateKnip(ctx) {
|
|
|
7985
8032
|
};
|
|
7986
8033
|
const isMonorepo = ctx.config.structure === "monorepo";
|
|
7987
8034
|
const ignoreBinaries = foreignOwnedBinaries(ctx);
|
|
8035
|
+
const ignoreDependencies = ignorableDependencies(ctx);
|
|
7988
8036
|
const buildConfig = isMonorepo ? buildKnipConfigMonorepo : buildKnipConfigSingle;
|
|
7989
8037
|
const existingPath = KNIP_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
7990
8038
|
if (existingPath) {
|
|
@@ -7995,7 +8043,7 @@ async function generateKnip(ctx) {
|
|
|
7995
8043
|
description: "Could not read existing knip config"
|
|
7996
8044
|
};
|
|
7997
8045
|
if (content.trim() === "") {
|
|
7998
|
-
const blank = isJsonConfig(existingPath) ? ensureIgnorePattern("{}", existingPath, ignoreBinaries) ?? "{}\n" : formattedTemplate(ctx, existingPath, buildConfig(ignoreBinaries));
|
|
8046
|
+
const blank = isJsonConfig(existingPath) ? ensureIgnorePattern("{}", existingPath, ignoreBinaries, ignoreDependencies) ?? "{}\n" : formattedTemplate(ctx, existingPath, buildConfig(ignoreBinaries, ignoreDependencies));
|
|
7999
8047
|
ctx.write(existingPath, blank);
|
|
8000
8048
|
return {
|
|
8001
8049
|
filePath: existingPath,
|
|
@@ -8003,13 +8051,13 @@ async function generateKnip(ctx) {
|
|
|
8003
8051
|
description: createdDescription(existingPath)
|
|
8004
8052
|
};
|
|
8005
8053
|
}
|
|
8006
|
-
const updated = ensureIgnorePattern(content, existingPath, ignoreBinaries);
|
|
8054
|
+
const updated = ensureIgnorePattern(content, existingPath, ignoreBinaries, ignoreDependencies);
|
|
8007
8055
|
if (updated) {
|
|
8008
8056
|
ctx.write(existingPath, updated);
|
|
8009
8057
|
return {
|
|
8010
8058
|
filePath: existingPath,
|
|
8011
8059
|
action: "updated",
|
|
8012
|
-
description: "Added \".claude/hooks/**\" and/or
|
|
8060
|
+
description: "Added \".claude/hooks/**\", foreign-owned ignoreBinaries, and/or ignored dependencies"
|
|
8013
8061
|
};
|
|
8014
8062
|
}
|
|
8015
8063
|
return {
|
|
@@ -8018,7 +8066,7 @@ async function generateKnip(ctx) {
|
|
|
8018
8066
|
description: existingPath === filePath ? "Already configured" : `Existing config found at ${existingPath}`
|
|
8019
8067
|
};
|
|
8020
8068
|
}
|
|
8021
|
-
ctx.write(filePath, formattedTemplate(ctx, filePath, buildConfig(ignoreBinaries)));
|
|
8069
|
+
ctx.write(filePath, formattedTemplate(ctx, filePath, buildConfig(ignoreBinaries, ignoreDependencies)));
|
|
8022
8070
|
return {
|
|
8023
8071
|
filePath,
|
|
8024
8072
|
action: "created",
|
|
@@ -9136,7 +9184,7 @@ const COMMENTS_GUIDE_BODY = [
|
|
|
9136
9184
|
"",
|
|
9137
9185
|
"- Default to none. A comment earns its place by carrying what the code cannot -- a rejected alternative, the bug that forced this shape, an invariant enforced elsewhere -- and earns it one decision at a time, in a sentence or two each: what was rejected, and what it cost. Five decisions is five sentences, not five paragraphs. Recoverable from the code? Leave it unwritten.",
|
|
9138
9186
|
"- Present tense, about what is. No `now`, `previously`, `changed to`: diff narration is stale on the next edit, and the commit message carries it. No running ledgers or changelogs: when adjusting a threshold, constant, or baseline, state the active invariant and prune past deltas.",
|
|
9139
|
-
"- Never cite a lifecycle-managed doc, PR, or issue from code: no PR or issue numbers (`PR #123`, `closes #456`), no path under `docs/superpowers/` or `docs/design/`, none carrying a `retired/` or `completed/` segment, no `docs/plan-*.md`, and no bare decision or task ID (`D3`, `T2`). They move, retire, and renumber, nothing checks the reference, and the reader has no way to tell a dead one from a live one. State the constraint itself, or cite a maintained reference doc instead. A `docs/` path that is a glob or an elided example rather than a reference (`docs/*.md`) is vocabulary, not a citation, and is fine.",
|
|
9187
|
+
"- Never cite a lifecycle-managed doc, PR, or issue from code: no PR or issue numbers (`PR #123`, `closes #456`), no path under `docs/specs/`, `docs/plans/`, `docs/superpowers/` or `docs/design/`, none carrying a `retired/` or `completed/` segment, no `docs/plan-*.md`, and no bare decision or task ID (`D3`, `T2`). They move, retire, and renumber, nothing checks the reference, and the reader has no way to tell a dead one from a live one. State the constraint itself, or cite a maintained reference doc instead. A `docs/` path that is a glob or an elided example rather than a reference (`docs/*.md`) is vocabulary, not a citation, and is fine.",
|
|
9140
9188
|
"- Code that exists only to accept an older shape -- a file, field, flag, environment variable, label or comment format -- carries `// @compat <id> until <expiry>: <reason>` on the line above it. The expiry is a date (`YYYY-MM-DD`) when the old shape lives with consumers who upgrade on their own schedule; a version of the code's own package (`v1.4.0`) when its consumer upgrades with each release; or `forge-clean` for old data on the forge, which has neither. Remove the code and every test that carries the same id together.",
|
|
9141
9189
|
"- **The code is the fact.** When a comment disagrees with it, fix it in one line or delete it -- never grow one to argue the code is right."
|
|
9142
9190
|
].join("\n");
|
|
@@ -9198,7 +9246,7 @@ const PLAN_LIFECYCLE_GUIDE_BODY = [
|
|
|
9198
9246
|
"",
|
|
9199
9247
|
"A plan's **directory** is the source of truth for whether it shipped -- not its `Status:` line, not its checkboxes. Read the source it names.",
|
|
9200
9248
|
"",
|
|
9201
|
-
"`.superpowers/` (no `docs/` prefix) is scratch state, not a plan's home -- a plan doc
|
|
9249
|
+
"`.superpowers/` (no `docs/` prefix) is scratch state, not a plan's home -- a plan doc lives under `docs/plans/` (older ones under `docs/superpowers/plans/`), even one drafted while running a plan through subagents.",
|
|
9202
9250
|
"",
|
|
9203
9251
|
"Retiring the plan is the last task of the plan, done in the PR that delivers the work: \"in the tree\" means committed on this branch, not merged to a default branch. Run `bst docs:retire <plan.md> --status <verb> --shipped \"<text>\"` -- it moves the doc, relinks what the move breaks, de-links paths pointing outside `docs/`, and writes the dated terminal `Status:` line, the historical banner, and the \"Shipped:\" paragraph. You supply the judgement: the honest verb -- `completed` / `superseded` / `abandoned` / `rejected` -- and a truthful `--shipped` naming the delivering PR; never fabricate it for uncommitted code or claim a merge that has not happened. A plan that lands one phase of a larger spec retires the plan and leaves the spec open.",
|
|
9204
9252
|
"",
|
|
@@ -9215,11 +9263,11 @@ const PLANNING_GUIDE_BODY = [
|
|
|
9215
9263
|
"",
|
|
9216
9264
|
"## The spec",
|
|
9217
9265
|
"",
|
|
9218
|
-
"Say why and what, not how: the problem, who it is for, the constraints, what done looks like, and the alternatives you rejected, each with its reason. Work with several independent parts gets one spec per part. Save it as `docs/
|
|
9266
|
+
"Say why and what, not how: the problem, who it is for, the constraints, what done looks like, and the alternatives you rejected, each with its reason. Work with several independent parts gets one spec per part. Save it as `docs/specs/<YYYY-MM-DD>-<topic>-design.md`.",
|
|
9219
9267
|
"",
|
|
9220
9268
|
"## The plan",
|
|
9221
9269
|
"",
|
|
9222
|
-
"Write for an executor who has not seen this repo or the spec. They write idiomatic code once they know the exact interface and the exact test, and make a reasonable choice wherever the plan leaves one open; what they cannot know is what you decided. A plan records those decisions -- it is not a transcript of the code. Save it under `docs/
|
|
9270
|
+
"Write for an executor who has not seen this repo or the spec. They write idiomatic code once they know the exact interface and the exact test, and make a reasonable choice wherever the plan leaves one open; what they cannot know is what you decided. A plan records those decisions -- it is not a transcript of the code. Save it under `docs/plans/`.",
|
|
9223
9271
|
"",
|
|
9224
9272
|
"- Check the plan's premises against the tree before building on them. \"Today X is broken\" is a claim: read the code that handles X, not just the line that throws.",
|
|
9225
9273
|
"- Label each task `Task <n>:` at the start of its heading or checkbox, numbered from 1, so `bst plan:brief` can find it. A checkbox's first sentence is the task's name on the control panel: end it with a period inside one short line, and put the files, commands, and checks in the sentences after it.",
|
|
@@ -9288,18 +9336,18 @@ function webUiGuideBody() {
|
|
|
9288
9336
|
"@types/node": "24.19.1",
|
|
9289
9337
|
"@vitest/coverage-v8": "5.0.3",
|
|
9290
9338
|
"commit-and-tag-version": "13.2.1",
|
|
9291
|
-
"knip": "6.
|
|
9292
|
-
"lefthook": "2.1
|
|
9293
|
-
"modern-web-guidance": "0.0.
|
|
9294
|
-
"oxfmt": "0.
|
|
9295
|
-
"oxlint": "1.
|
|
9339
|
+
"knip": "6.41.0",
|
|
9340
|
+
"lefthook": "2.2.1",
|
|
9341
|
+
"modern-web-guidance": "0.0.193",
|
|
9342
|
+
"oxfmt": "0.72.0",
|
|
9343
|
+
"oxlint": "1.87.0",
|
|
9296
9344
|
"oxlint-tsgolint": "7.0.2003",
|
|
9297
9345
|
"prettier": "3.9.9",
|
|
9298
|
-
"release-it": "21.1.
|
|
9346
|
+
"release-it": "21.1.1",
|
|
9299
9347
|
"tsdown": "0.23.0",
|
|
9300
9348
|
"typescript": "6.0.3",
|
|
9301
9349
|
"vitest": "5.0.3",
|
|
9302
|
-
"pnpm": "11.28.
|
|
9350
|
+
"pnpm": "11.28.5"
|
|
9303
9351
|
}["modern-web-guidance"]}`;
|
|
9304
9352
|
return [
|
|
9305
9353
|
"# Web UI",
|
|
@@ -9859,7 +9907,7 @@ function reportFailures(failed, command) {
|
|
|
9859
9907
|
function generateMigratePrompt(results, config, detected) {
|
|
9860
9908
|
const sections = [];
|
|
9861
9909
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9862
|
-
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.
|
|
9910
|
+
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.90.0 repo:sync\` on ${timestamp}_\n`);
|
|
9863
9911
|
const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
|
|
9864
9912
|
if (changed.length > 0) {
|
|
9865
9913
|
const items = changed.map((r) => {
|
|
@@ -10880,7 +10928,7 @@ function originHeadBranch(git, cwd) {
|
|
|
10880
10928
|
return name === "" ? void 0 : name;
|
|
10881
10929
|
}
|
|
10882
10930
|
function unconfirmedDefaultWarning(branch, namedByTree) {
|
|
10883
|
-
return `health: could not confirm the default branch, so the reference was read against origin/${branch}, while .tooling.json#defaultBranch names ${namedByTree}.
|
|
10931
|
+
return `health: could not confirm the default branch, so the reference was read against origin/${branch}, while .tooling.json#defaultBranch names ${namedByTree}. If ${namedByTree} is the repository's default branch, record it in this clone with \`git remote set-head origin --auto\`, or run in GitHub or Forgejo Actions, where the event payload names it.`;
|
|
10884
10932
|
}
|
|
10885
10933
|
function gitRunner(executor) {
|
|
10886
10934
|
return (args, cwd) => {
|
|
@@ -11962,10 +12010,10 @@ function runComposeLintGate(targetDir, config, memo) {
|
|
|
11962
12010
|
for (const warning of findDockerRebuildRisks(targetDir, config, memo)) log$1.warn(warning);
|
|
11963
12011
|
return issues.length;
|
|
11964
12012
|
}
|
|
11965
|
-
function runCheckOnlyImageGate(config, targetDir, memo) {
|
|
12013
|
+
function runCheckOnlyImageGate(config, targetDir, memo, phase) {
|
|
11966
12014
|
if (!config.publishDocker) return [];
|
|
11967
12015
|
const images = [];
|
|
11968
|
-
for (const risk of findCheckOnlyPublishRisks(targetDir,
|
|
12016
|
+
for (const risk of findCheckOnlyPublishRisks(targetDir, phase, memo)) {
|
|
11969
12017
|
log$1.warn(`Image "${risk.imageName}" is declared only in ${risk.declaredIn}, the check overlay, so a later tooling release stops publishing it with docker:publish by default. Keep it: set publish to true for "${risk.imageName}" under .tooling.json#docker. Settle it: set publish to false there, or move the service into a base compose file.`);
|
|
11970
12018
|
images.push(risk.imageName);
|
|
11971
12019
|
}
|
|
@@ -11979,6 +12027,18 @@ function runWorkflowAptGate(targetDir) {
|
|
|
11979
12027
|
}
|
|
11980
12028
|
return steps;
|
|
11981
12029
|
}
|
|
12030
|
+
function findInertAgentKeys(saved) {
|
|
12031
|
+
const agent = saved?.agent;
|
|
12032
|
+
if (!agent) return [];
|
|
12033
|
+
if (agent.enabled === true) return [];
|
|
12034
|
+
return Object.keys(agent).filter((key) => key !== "enabled").toSorted();
|
|
12035
|
+
}
|
|
12036
|
+
function runInertAgentSettingsGate(saved) {
|
|
12037
|
+
const keys = findInertAgentKeys(saved);
|
|
12038
|
+
if (keys.length === 0) return [];
|
|
12039
|
+
log$1.warn(`.tooling.json sets ${keys.map((key) => `agent.${key}`).join(", ")}, but agent.enabled is not true, so the agent ignores them. Set "agent.enabled": true, or remove those keys.`);
|
|
12040
|
+
return keys;
|
|
12041
|
+
}
|
|
11982
12042
|
function runPublishConfigGate(config, targetDir) {
|
|
11983
12043
|
if (!config.publishNpm) return 0;
|
|
11984
12044
|
const issues = lintPublishConfig(targetDir, config.structure);
|
|
@@ -12204,7 +12264,7 @@ const syncCommand = defineCommand({
|
|
|
12204
12264
|
},
|
|
12205
12265
|
json: {
|
|
12206
12266
|
type: "boolean",
|
|
12207
|
-
description: "Emit one JSON document: status (up-to-date | drift | workflow-drift | failed), actionable, failed, toolchain, checkOnlyImages, aptSteps, warnCount, exitCode. --check only: the write mode prompts and inherits child streams."
|
|
12267
|
+
description: "Emit one JSON document: status (up-to-date | drift | workflow-drift | failed), actionable, failed, toolchain, checkOnlyImages, aptSteps, inertAgentKeys, warnCount, exitCode. --check only: the write mode prompts and inherits child streams."
|
|
12208
12268
|
}
|
|
12209
12269
|
},
|
|
12210
12270
|
async run({ args }) {
|
|
@@ -12252,6 +12312,7 @@ async function runUpdate(targetDir, flags = {}) {
|
|
|
12252
12312
|
}
|
|
12253
12313
|
warnStaleChecksAdd(saved);
|
|
12254
12314
|
warnCoveredChecksAdd(saved, targetDir);
|
|
12315
|
+
runInertAgentSettingsGate(saved);
|
|
12255
12316
|
if (runComposeLintGate(targetDir, saved, dockerDiscovery) > 0) {
|
|
12256
12317
|
process.exitCode = 1;
|
|
12257
12318
|
return [];
|
|
@@ -12277,12 +12338,17 @@ function gateFailure(toolchainWarned = false) {
|
|
|
12277
12338
|
toolchain: { warned: toolchainWarned },
|
|
12278
12339
|
checkOnlyImages: [],
|
|
12279
12340
|
aptSteps: [],
|
|
12341
|
+
inertAgentKeys: [],
|
|
12280
12342
|
warnCount: 0,
|
|
12281
12343
|
exitCode: 1
|
|
12282
12344
|
};
|
|
12283
12345
|
}
|
|
12284
|
-
function
|
|
12285
|
-
|
|
12346
|
+
function advisoryWarnCount(advisories) {
|
|
12347
|
+
return (advisories.toolchainWarned ? 1 : 0) + advisories.checkOnlyImages.length + advisories.aptSteps.length + (advisories.inertAgentKeys.length > 0 ? 1 : 0);
|
|
12348
|
+
}
|
|
12349
|
+
function upToDateReport(advisories) {
|
|
12350
|
+
const toolchain = { warned: advisories.toolchainWarned };
|
|
12351
|
+
const warnCount = advisoryWarnCount(advisories);
|
|
12286
12352
|
if (warnCount > 0) {
|
|
12287
12353
|
if (!isJsonMode()) rawLog$1(formatWarnMarker(warnCount));
|
|
12288
12354
|
} else log$1.success("Repository is up to date.");
|
|
@@ -12291,8 +12357,9 @@ function upToDateReport(toolchain, checkOnlyImages, aptSteps) {
|
|
|
12291
12357
|
actionable: [],
|
|
12292
12358
|
failed: [],
|
|
12293
12359
|
toolchain,
|
|
12294
|
-
checkOnlyImages,
|
|
12295
|
-
aptSteps,
|
|
12360
|
+
checkOnlyImages: advisories.checkOnlyImages,
|
|
12361
|
+
aptSteps: advisories.aptSteps,
|
|
12362
|
+
inertAgentKeys: advisories.inertAgentKeys,
|
|
12296
12363
|
warnCount,
|
|
12297
12364
|
exitCode: 0
|
|
12298
12365
|
};
|
|
@@ -12315,17 +12382,21 @@ async function runCheckReport(targetDir) {
|
|
|
12315
12382
|
...gateFailure(),
|
|
12316
12383
|
toolchain: { warned: toolchain.warned }
|
|
12317
12384
|
};
|
|
12318
|
-
const
|
|
12319
|
-
|
|
12385
|
+
const advisories = {
|
|
12386
|
+
toolchainWarned: toolchain.warned,
|
|
12387
|
+
checkOnlyImages: runCheckOnlyImageGate(config, targetDir, dockerDiscovery),
|
|
12388
|
+
aptSteps: runWorkflowAptGate(targetDir),
|
|
12389
|
+
inertAgentKeys: runInertAgentSettingsGate(saved)
|
|
12390
|
+
};
|
|
12320
12391
|
const { ctx, pendingWrites } = createDryRunContext(config, dockerDiscovery);
|
|
12321
12392
|
logDetectionSummary(ctx);
|
|
12322
12393
|
const results = await runGenerators(ctx);
|
|
12323
12394
|
const failed = failedResults(results);
|
|
12324
12395
|
reportFailures(failed, "repo:sync");
|
|
12325
12396
|
const actionable = actionableResults(results, pendingWrites, targetDir);
|
|
12326
|
-
const
|
|
12327
|
-
const toolchainState = { warned:
|
|
12328
|
-
if (actionable.length === 0 && failed.length === 0) return upToDateReport(
|
|
12397
|
+
const advisoryCount = advisoryWarnCount(advisories);
|
|
12398
|
+
const toolchainState = { warned: advisories.toolchainWarned };
|
|
12399
|
+
if (actionable.length === 0 && failed.length === 0) return upToDateReport(advisories);
|
|
12329
12400
|
if (actionable.length === 0) return {
|
|
12330
12401
|
status: "failed",
|
|
12331
12402
|
actionable: [],
|
|
@@ -12333,21 +12404,23 @@ async function runCheckReport(targetDir) {
|
|
|
12333
12404
|
toolchain: toolchainState,
|
|
12334
12405
|
checkOnlyImages: [],
|
|
12335
12406
|
aptSteps: [],
|
|
12407
|
+
inertAgentKeys: [],
|
|
12336
12408
|
warnCount: 0,
|
|
12337
12409
|
exitCode: 1
|
|
12338
12410
|
};
|
|
12339
12411
|
log$1.warn(`${actionable.length} file(s) would be changed by repo:sync`);
|
|
12340
12412
|
for (const r of actionable) logActionableChange(r, targetDir, pendingWrites);
|
|
12341
12413
|
if (failed.length === 0 && actionable.every((r) => isWorkflowDirPath(r.filePath))) {
|
|
12342
|
-
const warnCount = actionable.length +
|
|
12414
|
+
const warnCount = actionable.length + advisoryCount;
|
|
12343
12415
|
if (!isJsonMode()) rawLog$1(formatWarnMarker(warnCount));
|
|
12344
12416
|
return {
|
|
12345
12417
|
status: "workflow-drift",
|
|
12346
12418
|
actionable,
|
|
12347
12419
|
failed: [],
|
|
12348
12420
|
toolchain: toolchainState,
|
|
12349
|
-
checkOnlyImages,
|
|
12350
|
-
aptSteps,
|
|
12421
|
+
checkOnlyImages: advisories.checkOnlyImages,
|
|
12422
|
+
aptSteps: advisories.aptSteps,
|
|
12423
|
+
inertAgentKeys: advisories.inertAgentKeys,
|
|
12351
12424
|
warnCount,
|
|
12352
12425
|
exitCode: 0
|
|
12353
12426
|
};
|
|
@@ -12359,6 +12432,7 @@ async function runCheckReport(targetDir) {
|
|
|
12359
12432
|
toolchain: toolchainState,
|
|
12360
12433
|
checkOnlyImages: [],
|
|
12361
12434
|
aptSteps: [],
|
|
12435
|
+
inertAgentKeys: [],
|
|
12362
12436
|
warnCount: 0,
|
|
12363
12437
|
exitCode: 1
|
|
12364
12438
|
};
|
|
@@ -15251,7 +15325,7 @@ function retireDestination(src, verb) {
|
|
|
15251
15325
|
if (dir === DOCS_ROOT && LEGACY_ACTIVE_PATTERN.test(base)) return `${DOCS_ROOT}/${RETIRED_DIR}/${verb}-${base.slice(5)}`;
|
|
15252
15326
|
return `${dir}/${RETIRED_DIR}/${base}`;
|
|
15253
15327
|
}
|
|
15254
|
-
function
|
|
15328
|
+
function candidatePairs(exec, config) {
|
|
15255
15329
|
const candidates = [];
|
|
15256
15330
|
for (const active of [...conventionalActiveDirs(exec), ...config.extraDirs]) candidates.push({
|
|
15257
15331
|
active,
|
|
@@ -15266,8 +15340,16 @@ function discoverPairs(exec, config) {
|
|
|
15266
15340
|
activePattern: LEGACY_ACTIVE_PATTERN,
|
|
15267
15341
|
enforced: isEnforced(DOCS_ROOT, config.enforced)
|
|
15268
15342
|
});
|
|
15343
|
+
return candidates;
|
|
15344
|
+
}
|
|
15345
|
+
function matchableActiveDirs(exec, config) {
|
|
15269
15346
|
const seen = /* @__PURE__ */ new Set();
|
|
15270
|
-
|
|
15347
|
+
for (const pair of candidatePairs(exec, config)) if (exec.readDir(pair.active) !== void 0) seen.add(pair.active);
|
|
15348
|
+
return [...seen].toSorted();
|
|
15349
|
+
}
|
|
15350
|
+
function discoverPairs(exec, config) {
|
|
15351
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15352
|
+
return candidatePairs(exec, config).filter((pair) => {
|
|
15271
15353
|
if (seen.has(pair.active)) return false;
|
|
15272
15354
|
if (!(listDocs(exec, pair.active, pair.activePattern).length > 0 || pair.retiredDirs.some((dir) => listDocs(exec, dir).length > 0))) return false;
|
|
15273
15355
|
seen.add(pair.active);
|
|
@@ -20115,7 +20197,7 @@ function composePs(executor, config) {
|
|
|
20115
20197
|
//#region src/docker-check/health.ts
|
|
20116
20198
|
const DOCKER_RUN_START_FAILURE_EXIT_CODE = 125;
|
|
20117
20199
|
const PROBE_TIMEOUT_SECONDS = "5";
|
|
20118
|
-
const PROBE_IMAGE = "curlimages/curl:8.
|
|
20200
|
+
const PROBE_IMAGE = "curlimages/curl:8.22.0@sha256:58adaa4e8dca9c988bae2aba4ab3434a0bb2da16bbe3f92dec39ec7785166777";
|
|
20119
20201
|
function getContainerHealth(containers, serviceName) {
|
|
20120
20202
|
return containers.find((c) => c.Service === serviceName)?.Health ?? "unknown";
|
|
20121
20203
|
}
|
|
@@ -24093,7 +24175,7 @@ function collectViolations(exec, config, opts) {
|
|
|
24093
24175
|
retiredCount += result.retired;
|
|
24094
24176
|
}
|
|
24095
24177
|
violations.push(...collectSpecRetirementViolations(exec, pairs, config, opts));
|
|
24096
|
-
const activeDirs =
|
|
24178
|
+
const activeDirs = matchableActiveDirs(exec, config);
|
|
24097
24179
|
const unmatchedEnforced = Array.isArray(config.enforced) ? config.enforced.filter((entry) => !activeDirs.includes(entry)) : [];
|
|
24098
24180
|
return {
|
|
24099
24181
|
violations,
|
|
@@ -24236,7 +24318,7 @@ const docsCheckCommand = defineCommand({
|
|
|
24236
24318
|
},
|
|
24237
24319
|
json: {
|
|
24238
24320
|
type: "boolean",
|
|
24239
|
-
description: "Emit one JSON document: violations ([{file, rule, message, fix, severity (fatal | advisory)}]), activeCount, retiredCount, pairCount, pass, activeDirs, unmatchedEnforced. When docs are disabled in .tooling.json the document is only enabled (false)."
|
|
24321
|
+
description: "Emit one JSON document: violations ([{file, rule, message, fix, severity (fatal | advisory)}]), activeCount, retiredCount, pairCount, pass, activeDirs, unmatchedEnforced. activeDirs lists every existing doc directory an enforced entry can match, holding docs or not; unmatchedEnforced the .tooling.json#docs.enforced entries that match none of them. When docs are disabled in .tooling.json the document is only enabled (false)."
|
|
24240
24322
|
},
|
|
24241
24323
|
verbose: {
|
|
24242
24324
|
type: "boolean",
|
|
@@ -25871,6 +25953,9 @@ var EmptyCommentError = class extends FatalError {
|
|
|
25871
25953
|
super("The comment resolved to empty text.");
|
|
25872
25954
|
}
|
|
25873
25955
|
};
|
|
25956
|
+
function rejectEmptyComment(comment) {
|
|
25957
|
+
if (comment !== void 0 && comment.trim() === "") throw new EmptyCommentError();
|
|
25958
|
+
}
|
|
25874
25959
|
var PullRequestStateError = class extends FatalError {
|
|
25875
25960
|
number;
|
|
25876
25961
|
action;
|
|
@@ -25880,20 +25965,35 @@ var PullRequestStateError = class extends FatalError {
|
|
|
25880
25965
|
this.action = action;
|
|
25881
25966
|
}
|
|
25882
25967
|
};
|
|
25968
|
+
var NotAPullRequestError = class extends FatalError {
|
|
25969
|
+
number;
|
|
25970
|
+
constructor(number) {
|
|
25971
|
+
super(`#${String(number)} is an issue, not a pull request.`);
|
|
25972
|
+
this.number = number;
|
|
25973
|
+
}
|
|
25974
|
+
};
|
|
25975
|
+
var StateNotTakenError = class extends FatalError {
|
|
25976
|
+
number;
|
|
25977
|
+
expected;
|
|
25978
|
+
actual;
|
|
25979
|
+
constructor(number, expected, actual) {
|
|
25980
|
+
super(`The forge did not change #${String(number)}: it is still ${actual}, not ${expected}.`);
|
|
25981
|
+
this.number = number;
|
|
25982
|
+
this.expected = expected;
|
|
25983
|
+
this.actual = actual;
|
|
25984
|
+
}
|
|
25985
|
+
};
|
|
25883
25986
|
const TARGET_STATE = {
|
|
25884
25987
|
close: "closed",
|
|
25885
25988
|
reopen: "open"
|
|
25886
25989
|
};
|
|
25887
|
-
async function
|
|
25888
|
-
const ref = parseIssueRef(request.ref);
|
|
25889
|
-
const number = requireIssueNumber(ref);
|
|
25890
|
-
if (request.comment !== void 0 && request.comment.trim() === "") throw new EmptyCommentError();
|
|
25891
|
-
const commentGiven = request.comment !== void 0;
|
|
25892
|
-
const { api } = resolve(resolveIssueTarget(ref.target, request.repo));
|
|
25990
|
+
async function changeState(api, number, request, subject) {
|
|
25893
25991
|
const before = await api.get(number);
|
|
25894
|
-
if (before.isPullRequest) throw new PullRequestStateError(number, request.action);
|
|
25992
|
+
if (subject === "issue" && before.isPullRequest) throw new PullRequestStateError(number, request.action);
|
|
25993
|
+
if (subject === "pull-request" && !before.isPullRequest) throw new NotAPullRequestError(number);
|
|
25994
|
+
const commentGiven = request.comment !== void 0;
|
|
25895
25995
|
const targetState = TARGET_STATE[request.action];
|
|
25896
|
-
const buildResult = (url, current, changed, posted) => ({
|
|
25996
|
+
const buildResult = (url, current, changed, posted, merged) => ({
|
|
25897
25997
|
number,
|
|
25898
25998
|
url,
|
|
25899
25999
|
action: request.action,
|
|
@@ -25902,20 +26002,32 @@ async function setIssueState(resolve, request) {
|
|
|
25902
26002
|
current
|
|
25903
26003
|
},
|
|
25904
26004
|
changed,
|
|
25905
|
-
...commentGiven ? { comment: { posted } } : {}
|
|
26005
|
+
...commentGiven ? { comment: { posted } } : {},
|
|
26006
|
+
...merged === void 0 ? {} : { merged }
|
|
25906
26007
|
});
|
|
25907
|
-
if (before.state === targetState)
|
|
26008
|
+
if (before.state === targetState) {
|
|
26009
|
+
const merged = subject === "pull-request" ? (await api.pullRequest(number)).merged : void 0;
|
|
26010
|
+
return buildResult(before.url, before.state, false, false, merged);
|
|
26011
|
+
}
|
|
25908
26012
|
if (request.dryRun) return buildResult(before.url, targetState, true, true);
|
|
25909
26013
|
if (commentGiven) await api.addComment(number, request.comment ?? "");
|
|
25910
26014
|
let after;
|
|
25911
26015
|
try {
|
|
25912
26016
|
after = await api.setState(number, targetState);
|
|
26017
|
+
if (after.state !== targetState) throw new StateNotTakenError(number, targetState, after.state);
|
|
25913
26018
|
} catch (error) {
|
|
25914
26019
|
if (commentGiven && error instanceof Error) error.message = `The comment on #${String(number)} was already posted; a retry will duplicate it. ` + error.message;
|
|
25915
26020
|
throw error;
|
|
25916
26021
|
}
|
|
25917
26022
|
return buildResult(after.url, after.state, true, true);
|
|
25918
26023
|
}
|
|
26024
|
+
async function setIssueState(resolve, request) {
|
|
26025
|
+
const ref = parseIssueRef(request.ref);
|
|
26026
|
+
const number = requireIssueNumber(ref);
|
|
26027
|
+
rejectEmptyComment(request.comment);
|
|
26028
|
+
const { api } = resolve(resolveIssueTarget(ref.target, request.repo));
|
|
26029
|
+
return changeState(api, number, request, "issue");
|
|
26030
|
+
}
|
|
25919
26031
|
const PAST_TENSE = {
|
|
25920
26032
|
close: "Closed",
|
|
25921
26033
|
reopen: "Reopened"
|
|
@@ -26808,7 +26920,7 @@ async function runCli(config) {
|
|
|
26808
26920
|
//#region src/cli-main.ts
|
|
26809
26921
|
setLogLevel(resolveLogLevel({}));
|
|
26810
26922
|
setForgejoLogger(log$1);
|
|
26811
|
-
const displayVersion = "0.
|
|
26923
|
+
const displayVersion = "0.90.0";
|
|
26812
26924
|
const bstSubCommands = {
|
|
26813
26925
|
"repo:sync": syncCommand,
|
|
26814
26926
|
"checks:run": runChecksCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"@types/node": "24.19.1",
|
|
53
53
|
"@types/picomatch": "4.0.3",
|
|
54
54
|
"commit-and-tag-version": "13.2.1",
|
|
55
|
-
"modern-web-guidance": "0.0.
|
|
56
|
-
"oxfmt": "0.
|
|
55
|
+
"modern-web-guidance": "0.0.193",
|
|
56
|
+
"oxfmt": "0.72.0",
|
|
57
57
|
"prettier": "3.9.9",
|
|
58
|
-
"release-it": "21.1.
|
|
58
|
+
"release-it": "21.1.1",
|
|
59
59
|
"tsdown": "0.23.0"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
package/tooling.schema.json
CHANGED
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"type": "string"
|
|
95
95
|
},
|
|
96
96
|
"publish": {
|
|
97
|
-
"description": "The publish decision either way: true keeps an image published -- including one
|
|
97
|
+
"description": "The publish decision either way: true keeps an image published -- including one whose compose service is declared only in the check overlay, which is not published by default -- and false excludes it while publishDocker stays true. Unset means the discovery default, which publishes everything except a check-only image.",
|
|
98
98
|
"type": "boolean"
|
|
99
99
|
}
|
|
100
100
|
},
|
|
@@ -600,6 +600,10 @@
|
|
|
600
600
|
"description": "agent:start behaviour overrides. Opt-in: see agent.autonomy for the interior-autonomy gate.",
|
|
601
601
|
"type": "object",
|
|
602
602
|
"properties": {
|
|
603
|
+
"enabled": {
|
|
604
|
+
"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`. Provisioning the agent's labels is not consent, and is never gated by this key.",
|
|
605
|
+
"type": "boolean"
|
|
606
|
+
},
|
|
603
607
|
"autonomy": {
|
|
604
608
|
"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).",
|
|
605
609
|
"type": "boolean"
|