@bensandee/tooling 0.41.0 → 0.42.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/README.md CHANGED
@@ -78,6 +78,15 @@ Custom blocks are anchored to the nearest preceding non-blank line. When `repo:s
78
78
 
79
79
  To skip a workflow file entirely, add `# @bensandee/tooling:ignore` as the first line — `repo:sync` will leave the file untouched.
80
80
 
81
+ ### Repository setup
82
+
83
+ | Command | Description |
84
+ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
85
+ | `tooling setup:secrets` | Configure CI secrets (Forgejo or GitHub). Auto-detects platform from `package.json` repository URL. Flags: `--dry-run`, `--no-docker`. |
86
+ | `tooling forgejo:secrets` | Manage Forgejo Actions secrets directly. Subcommands: `set`, `list`, `delete`. |
87
+
88
+ `setup:secrets` detects the hosting platform from the `repository` field in `package.json`. For Forgejo repositories, it prompts for username and password, creates an access token with appropriate scopes, then sets `RELEASE_TOKEN` (and Docker secrets if Docker packages are detected). For GitHub repositories, it prompts for an existing Personal Access Token and uses `gh secret set` to configure secrets.
89
+
81
90
  ### Release management
82
91
 
83
92
  | Command | Description |
package/dist/bin.mjs CHANGED
@@ -625,7 +625,8 @@ const ToolingConfigSchema = z.strictObject({
625
625
  })).optional().meta({ description: "Docker package overrides (package name → config)" }),
626
626
  dockerCheck: z.union([z.literal(false), DockerCheckConfigSchema]).optional().meta({ description: "Docker health check configuration or false to disable" }),
627
627
  registryHost: z.string().optional().meta({ description: "Docker registry hostname (e.g. ghcr.io, code.example.com)" }),
628
- registryNamespace: z.string().optional().meta({ description: "Docker registry namespace (e.g. ghcr.io/myorg, code.example.com/myorg)" })
628
+ registryNamespace: z.string().optional().meta({ description: "Docker registry namespace (e.g. ghcr.io/myorg, code.example.com/myorg)" }),
629
+ postReleaseHook: z.string().optional().meta({ description: "Override the post-release hook command (default: curl POST_RELEASE_HOOK_URL secret if set)" })
629
630
  });
630
631
  /** Load saved tooling config from the target directory. Returns undefined if missing, throws on invalid. */
631
632
  function loadToolingConfig(targetDir) {
@@ -1205,6 +1206,15 @@ function publishSteps(nodeVersionYaml, registryDerivable) {
1205
1206
  ];
1206
1207
  }
1207
1208
  const DockerMapSchema = z.object({ docker: z.record(z.string(), z.unknown()).optional() });
1209
+ const PostReleaseHookSchema = z.object({ postReleaseHook: z.string().optional() });
1210
+ /** Read the postReleaseHook command override from .tooling.json, if configured. */
1211
+ function getPostReleaseHookCommand(ctx) {
1212
+ const raw = ctx.read(".tooling.json");
1213
+ if (!raw) return void 0;
1214
+ const result = PostReleaseHookSchema.safeParse(JSON.parse(raw));
1215
+ if (!result.success) return void 0;
1216
+ return result.data.postReleaseHook;
1217
+ }
1208
1218
  /** Get names of packages that have Docker builds (by convention or .tooling.json config). */
1209
1219
  function getDockerPackageNames(ctx) {
1210
1220
  const names = [];
@@ -1330,9 +1340,9 @@ const PER_PACKAGE_DEV_DEPS = {
1330
1340
  "oxlint": "1.57.0",
1331
1341
  "prettier": "3.8.1",
1332
1342
  "release-it": "19.2.4",
1333
- "tsdown": "0.21.4",
1343
+ "tsdown": "0.21.6",
1334
1344
  "typescript": "6.0.2",
1335
- "vitest": "^4.1.1",
1345
+ "vitest": "4.1.2",
1336
1346
  "pnpm": "10.33.0"
1337
1347
  }["@types/node"],
1338
1348
  tsdown: {
@@ -1346,9 +1356,9 @@ const PER_PACKAGE_DEV_DEPS = {
1346
1356
  "oxlint": "1.57.0",
1347
1357
  "prettier": "3.8.1",
1348
1358
  "release-it": "19.2.4",
1349
- "tsdown": "0.21.4",
1359
+ "tsdown": "0.21.6",
1350
1360
  "typescript": "6.0.2",
1351
- "vitest": "^4.1.1",
1361
+ "vitest": "4.1.2",
1352
1362
  "pnpm": "10.33.0"
1353
1363
  }["tsdown"],
1354
1364
  typescript: {
@@ -1362,9 +1372,9 @@ const PER_PACKAGE_DEV_DEPS = {
1362
1372
  "oxlint": "1.57.0",
1363
1373
  "prettier": "3.8.1",
1364
1374
  "release-it": "19.2.4",
1365
- "tsdown": "0.21.4",
1375
+ "tsdown": "0.21.6",
1366
1376
  "typescript": "6.0.2",
1367
- "vitest": "^4.1.1",
1377
+ "vitest": "4.1.2",
1368
1378
  "pnpm": "10.33.0"
1369
1379
  }["typescript"],
1370
1380
  vitest: {
@@ -1378,9 +1388,9 @@ const PER_PACKAGE_DEV_DEPS = {
1378
1388
  "oxlint": "1.57.0",
1379
1389
  "prettier": "3.8.1",
1380
1390
  "release-it": "19.2.4",
1381
- "tsdown": "0.21.4",
1391
+ "tsdown": "0.21.6",
1382
1392
  "typescript": "6.0.2",
1383
- "vitest": "^4.1.1",
1393
+ "vitest": "4.1.2",
1384
1394
  "pnpm": "10.33.0"
1385
1395
  }["vitest"]
1386
1396
  };
@@ -1397,9 +1407,9 @@ const ROOT_DEV_DEPS = {
1397
1407
  "oxlint": "1.57.0",
1398
1408
  "prettier": "3.8.1",
1399
1409
  "release-it": "19.2.4",
1400
- "tsdown": "0.21.4",
1410
+ "tsdown": "0.21.6",
1401
1411
  "typescript": "6.0.2",
1402
- "vitest": "^4.1.1",
1412
+ "vitest": "4.1.2",
1403
1413
  "pnpm": "10.33.0"
1404
1414
  }["knip"],
1405
1415
  lefthook: {
@@ -1413,9 +1423,9 @@ const ROOT_DEV_DEPS = {
1413
1423
  "oxlint": "1.57.0",
1414
1424
  "prettier": "3.8.1",
1415
1425
  "release-it": "19.2.4",
1416
- "tsdown": "0.21.4",
1426
+ "tsdown": "0.21.6",
1417
1427
  "typescript": "6.0.2",
1418
- "vitest": "^4.1.1",
1428
+ "vitest": "4.1.2",
1419
1429
  "pnpm": "10.33.0"
1420
1430
  }["lefthook"],
1421
1431
  oxlint: {
@@ -1429,9 +1439,9 @@ const ROOT_DEV_DEPS = {
1429
1439
  "oxlint": "1.57.0",
1430
1440
  "prettier": "3.8.1",
1431
1441
  "release-it": "19.2.4",
1432
- "tsdown": "0.21.4",
1442
+ "tsdown": "0.21.6",
1433
1443
  "typescript": "6.0.2",
1434
- "vitest": "^4.1.1",
1444
+ "vitest": "4.1.2",
1435
1445
  "pnpm": "10.33.0"
1436
1446
  }["oxlint"]
1437
1447
  };
@@ -1470,9 +1480,9 @@ function addReleaseDeps(deps, config) {
1470
1480
  "oxlint": "1.57.0",
1471
1481
  "prettier": "3.8.1",
1472
1482
  "release-it": "19.2.4",
1473
- "tsdown": "0.21.4",
1483
+ "tsdown": "0.21.6",
1474
1484
  "typescript": "6.0.2",
1475
- "vitest": "^4.1.1",
1485
+ "vitest": "4.1.2",
1476
1486
  "pnpm": "10.33.0"
1477
1487
  }["release-it"];
1478
1488
  if (config.structure === "monorepo") deps["@release-it/bumper"] = {
@@ -1486,9 +1496,9 @@ function addReleaseDeps(deps, config) {
1486
1496
  "oxlint": "1.57.0",
1487
1497
  "prettier": "3.8.1",
1488
1498
  "release-it": "19.2.4",
1489
- "tsdown": "0.21.4",
1499
+ "tsdown": "0.21.6",
1490
1500
  "typescript": "6.0.2",
1491
- "vitest": "^4.1.1",
1501
+ "vitest": "4.1.2",
1492
1502
  "pnpm": "10.33.0"
1493
1503
  }["@release-it/bumper"];
1494
1504
  break;
@@ -1504,9 +1514,9 @@ function addReleaseDeps(deps, config) {
1504
1514
  "oxlint": "1.57.0",
1505
1515
  "prettier": "3.8.1",
1506
1516
  "release-it": "19.2.4",
1507
- "tsdown": "0.21.4",
1517
+ "tsdown": "0.21.6",
1508
1518
  "typescript": "6.0.2",
1509
- "vitest": "^4.1.1",
1519
+ "vitest": "4.1.2",
1510
1520
  "pnpm": "10.33.0"
1511
1521
  }["commit-and-tag-version"];
1512
1522
  break;
@@ -1522,9 +1532,9 @@ function addReleaseDeps(deps, config) {
1522
1532
  "oxlint": "1.57.0",
1523
1533
  "prettier": "3.8.1",
1524
1534
  "release-it": "19.2.4",
1525
- "tsdown": "0.21.4",
1535
+ "tsdown": "0.21.6",
1526
1536
  "typescript": "6.0.2",
1527
- "vitest": "^4.1.1",
1537
+ "vitest": "4.1.2",
1528
1538
  "pnpm": "10.33.0"
1529
1539
  }["@changesets/cli"];
1530
1540
  break;
@@ -1548,8 +1558,8 @@ async function generatePackageJson(ctx) {
1548
1558
  }
1549
1559
  const devDeps = { ...ROOT_DEV_DEPS };
1550
1560
  if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
1551
- devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.10.0";
1552
- devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.41.0";
1561
+ devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.10.1";
1562
+ devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.42.0";
1553
1563
  if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.12.0";
1554
1564
  if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
1555
1565
  "@changesets/cli": "2.30.0",
@@ -1562,9 +1572,9 @@ async function generatePackageJson(ctx) {
1562
1572
  "oxlint": "1.57.0",
1563
1573
  "prettier": "3.8.1",
1564
1574
  "release-it": "19.2.4",
1565
- "tsdown": "0.21.4",
1575
+ "tsdown": "0.21.6",
1566
1576
  "typescript": "6.0.2",
1567
- "vitest": "^4.1.1",
1577
+ "vitest": "4.1.2",
1568
1578
  "pnpm": "10.33.0"
1569
1579
  }["oxfmt"];
1570
1580
  if (ctx.config.formatter === "prettier") devDeps["prettier"] = {
@@ -1578,9 +1588,9 @@ async function generatePackageJson(ctx) {
1578
1588
  "oxlint": "1.57.0",
1579
1589
  "prettier": "3.8.1",
1580
1590
  "release-it": "19.2.4",
1581
- "tsdown": "0.21.4",
1591
+ "tsdown": "0.21.6",
1582
1592
  "typescript": "6.0.2",
1583
- "vitest": "^4.1.1",
1593
+ "vitest": "4.1.2",
1584
1594
  "pnpm": "10.33.0"
1585
1595
  }["prettier"];
1586
1596
  addReleaseDeps(devDeps, ctx.config);
@@ -1662,9 +1672,9 @@ async function generatePackageJson(ctx) {
1662
1672
  "oxlint": "1.57.0",
1663
1673
  "prettier": "3.8.1",
1664
1674
  "release-it": "19.2.4",
1665
- "tsdown": "0.21.4",
1675
+ "tsdown": "0.21.6",
1666
1676
  "typescript": "6.0.2",
1667
- "vitest": "^4.1.1",
1677
+ "vitest": "4.1.2",
1668
1678
  "pnpm": "10.33.0"
1669
1679
  }["pnpm"]}`
1670
1680
  };
@@ -2156,6 +2166,185 @@ async function generateGitignore(ctx) {
2156
2166
  };
2157
2167
  }
2158
2168
  //#endregion
2169
+ //#region src/generators/release-ci.ts
2170
+ const DEFAULT_POST_RELEASE_COMMAND = "if [ -n \"$POST_RELEASE_HOOK_URL\" ]; then curl -fsS \"$POST_RELEASE_HOOK_URL\"; fi";
2171
+ /** Step that runs after release, gated on the POST_RELEASE_HOOK_URL secret being set. */
2172
+ function postReleaseHookStep(command) {
2173
+ return { step: {
2174
+ name: "Post-release hook",
2175
+ env: { POST_RELEASE_HOOK_URL: actionsExpr("secrets.POST_RELEASE_HOOK_URL") },
2176
+ run: command ?? DEFAULT_POST_RELEASE_COMMAND
2177
+ } };
2178
+ }
2179
+ /** Common setup steps shared by release-it and simple strategies. */
2180
+ function commonSteps(nodeVersionYaml, publishesNpm) {
2181
+ const isNodeVersionFile = nodeVersionYaml.startsWith("node-version-file");
2182
+ return [
2183
+ { step: {
2184
+ uses: "actions/checkout@v6",
2185
+ with: { "fetch-depth": 0 }
2186
+ } },
2187
+ { step: { uses: "pnpm/action-setup@v5" } },
2188
+ { step: {
2189
+ uses: "actions/setup-node@v6",
2190
+ with: {
2191
+ ...isNodeVersionFile ? { "node-version-file": "package.json" } : { "node-version": "24" },
2192
+ cache: "pnpm",
2193
+ ...publishesNpm && { "registry-url": actionsExpr("vars.NPM_REGISTRY_URL || 'https://registry.npmjs.org'") }
2194
+ }
2195
+ } },
2196
+ { step: { run: "pnpm install --frozen-lockfile" } }
2197
+ ];
2198
+ }
2199
+ function releaseItSteps(ci, nodeVersionYaml, publishesNpm) {
2200
+ const tokenEnv = ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : { RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN") };
2201
+ const npmEnv = publishesNpm ? { NODE_AUTH_TOKEN: actionsExpr("secrets.NPM_TOKEN") } : {};
2202
+ return [...commonSteps(nodeVersionYaml, publishesNpm), { step: {
2203
+ run: "pnpm release-it --ci",
2204
+ env: {
2205
+ ...tokenEnv,
2206
+ ...npmEnv,
2207
+ RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2208
+ }
2209
+ } }];
2210
+ }
2211
+ /** Build the workflow_dispatch trigger with optional inputs for the simple strategy. */
2212
+ function simpleWorkflowDispatchTrigger() {
2213
+ return { workflow_dispatch: { inputs: {
2214
+ bump: {
2215
+ description: "Version bump type (default: conventional-commits auto-detect)",
2216
+ required: false,
2217
+ type: "choice",
2218
+ default: "auto",
2219
+ options: [
2220
+ "auto",
2221
+ "major",
2222
+ "minor",
2223
+ "patch",
2224
+ "first-release"
2225
+ ]
2226
+ },
2227
+ prerelease: {
2228
+ description: "Create a prerelease with the given tag (e.g., beta, alpha)",
2229
+ required: false,
2230
+ type: "string"
2231
+ }
2232
+ } } };
2233
+ }
2234
+ /** Build the release:simple run command with conditional flags from workflow inputs. */
2235
+ function simpleReleaseCommand() {
2236
+ return [
2237
+ "FLAGS=",
2238
+ `case "${actionsExpr("inputs.bump")}" in`,
2239
+ " major|minor|patch) FLAGS=\"$FLAGS --release-as " + actionsExpr("inputs.bump") + "\" ;;",
2240
+ " first-release) FLAGS=\"$FLAGS --first-release\" ;;",
2241
+ "esac",
2242
+ `if [ -n "${actionsExpr("inputs.prerelease")}" ]; then FLAGS="$FLAGS --prerelease ${actionsExpr("inputs.prerelease")}"; fi`,
2243
+ "pnpm exec bst release:simple $FLAGS"
2244
+ ].join("\n");
2245
+ }
2246
+ /** Build the docker publish env block, omitting registry vars when derivable at runtime. */
2247
+ function dockerPublishEnv(registryDerivable) {
2248
+ return {
2249
+ ...registryDerivable ? {} : {
2250
+ DOCKER_REGISTRY_HOST: actionsExpr("vars.DOCKER_REGISTRY_HOST"),
2251
+ DOCKER_REGISTRY_NAMESPACE: actionsExpr("vars.DOCKER_REGISTRY_NAMESPACE")
2252
+ },
2253
+ DOCKER_USERNAME: actionsExpr("secrets.DOCKER_USERNAME"),
2254
+ DOCKER_PASSWORD: actionsExpr("secrets.DOCKER_PASSWORD"),
2255
+ RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2256
+ };
2257
+ }
2258
+ function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable) {
2259
+ const releaseStep = { step: {
2260
+ name: "Release",
2261
+ env: {
2262
+ ...ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : {
2263
+ FORGEJO_SERVER_URL: actionsExpr("github.server_url"),
2264
+ FORGEJO_REPOSITORY: actionsExpr("github.repository"),
2265
+ RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN")
2266
+ },
2267
+ RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2268
+ },
2269
+ run: simpleReleaseCommand()
2270
+ } };
2271
+ const dockerStep = { step: {
2272
+ name: "Publish Docker images",
2273
+ if: "success()",
2274
+ env: dockerPublishEnv(registryDerivable),
2275
+ run: "pnpm exec bst docker:publish"
2276
+ } };
2277
+ return [
2278
+ ...commonSteps(nodeVersionYaml, publishesNpm),
2279
+ releaseStep,
2280
+ ...hasDocker ? [dockerStep] : []
2281
+ ];
2282
+ }
2283
+ function buildSteps(strategy, ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable) {
2284
+ switch (strategy) {
2285
+ case "release-it": return releaseItSteps(ci, nodeVersionYaml, publishesNpm);
2286
+ case "simple": return simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable);
2287
+ default: return null;
2288
+ }
2289
+ }
2290
+ async function generateReleaseCi(ctx) {
2291
+ const filePath = "release-ci";
2292
+ if (ctx.config.releaseStrategy === "none" || ctx.config.ci === "none") return {
2293
+ filePath,
2294
+ action: "skipped",
2295
+ description: "Release CI workflow not applicable"
2296
+ };
2297
+ const publishesNpm = ctx.config.publishNpm === true;
2298
+ if (ctx.config.releaseStrategy === "changesets") return {
2299
+ filePath,
2300
+ action: "skipped",
2301
+ description: "Release step included in CI workflow"
2302
+ };
2303
+ const isGitHub = ctx.config.ci === "github";
2304
+ const workflowPath = isGitHub ? ".github/workflows/release.yml" : ".forgejo/workflows/release.yml";
2305
+ const nodeVersionYaml = computeNodeVersionYaml(ctx);
2306
+ const hasDocker = hasDockerPackages(ctx);
2307
+ const registryDerivable = hasRepositoryUrl(ctx);
2308
+ const steps = buildSteps(ctx.config.releaseStrategy, ctx.config.ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable);
2309
+ if (!steps) return {
2310
+ filePath,
2311
+ action: "skipped",
2312
+ description: "Release CI workflow not applicable"
2313
+ };
2314
+ steps.push(postReleaseHookStep(getPostReleaseHookCommand(ctx)));
2315
+ const on = ctx.config.releaseStrategy === "simple" ? simpleWorkflowDispatchTrigger() : { workflow_dispatch: null };
2316
+ const content = buildWorkflowYaml({
2317
+ ci: ctx.config.ci,
2318
+ name: "Release",
2319
+ on,
2320
+ ...isGitHub && { permissions: { contents: "write" } },
2321
+ jobName: "release",
2322
+ steps
2323
+ });
2324
+ const alreadyExists = ctx.exists(workflowPath);
2325
+ const existing = alreadyExists ? ctx.read(workflowPath) : void 0;
2326
+ if (existing) {
2327
+ if (isToolingIgnored(existing)) return {
2328
+ filePath: workflowPath,
2329
+ action: "skipped",
2330
+ description: "Release workflow has ignore comment"
2331
+ };
2332
+ if (normalizeWorkflow(existing) === normalizeWorkflow(content)) return {
2333
+ filePath: workflowPath,
2334
+ action: "skipped",
2335
+ description: "Release workflow already up to date"
2336
+ };
2337
+ }
2338
+ const withDevPath = preserveDevBinaryPath(content, existing);
2339
+ const final = existing ? insertCustomBlocks(withDevPath, extractCustomBlocks(existing)) : withDevPath;
2340
+ ctx.write(workflowPath, final);
2341
+ return {
2342
+ filePath: workflowPath,
2343
+ action: alreadyExists ? "updated" : "created",
2344
+ description: `Generated ${isGitHub ? "GitHub" : "Forgejo"} Actions release workflow`
2345
+ };
2346
+ }
2347
+ //#endregion
2159
2348
  //#region src/generators/ci.ts
2160
2349
  /** Build the release step for the check job (changesets strategy). */
2161
2350
  function changesetsReleaseStep(ci, publishesNpm) {
@@ -2226,7 +2415,12 @@ async function generateCi(ctx) {
2226
2415
  const isChangesets = ctx.config.releaseStrategy === "changesets";
2227
2416
  const nodeVersionYaml = computeNodeVersionYaml(ctx);
2228
2417
  const publishesNpm = ctx.config.publishNpm === true;
2229
- const steps = [...checkSteps(nodeVersionYaml, isChangesets && publishesNpm), ...isChangesets ? [changesetsReleaseStep(ctx.config.ci, publishesNpm)] : []];
2418
+ const hookCommand = getPostReleaseHookCommand(ctx);
2419
+ const steps = [
2420
+ ...checkSteps(nodeVersionYaml, isChangesets && publishesNpm),
2421
+ ...isChangesets ? [changesetsReleaseStep(ctx.config.ci, publishesNpm)] : [],
2422
+ ...isChangesets ? [postReleaseHookStep(hookCommand)] : []
2423
+ ];
2230
2424
  const content = buildWorkflowYaml({
2231
2425
  ci: ctx.config.ci,
2232
2426
  name: "CI",
@@ -2473,7 +2667,7 @@ function writeIfChanged(ctx, filePath, content, label) {
2473
2667
  };
2474
2668
  }
2475
2669
  async function generateClaudeHooks(ctx) {
2476
- return [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.5.0\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync — do not edit manually.\n */\nimport{a as e,i as t,n,o as r,r as i,t as a}from\"./file-rules-cP7-5vh6.mjs\";import*as o from\"node:fs\";import{existsSync as s,readFileSync as c,readdirSync as l,statSync as u}from\"node:fs\";import*as d from\"node:path\";import{dirname as ee,join as f,resolve as p}from\"node:path\";import{fileURLToPath as te}from\"node:url\";Object.freeze({status:`aborted`});function m(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,`_zod`,{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;e<a.length;e++){let t=a[e];t in n||(n[t]=i[t].bind(n))}}let i=n?.Parent??Object;class a extends i{}Object.defineProperty(a,`name`,{value:e});function o(e){var t;let i=n?.Parent?new a:this;r(i,e),(t=i._zod).deferred??(t.deferred=[]);for(let e of i._zod.deferred)e();return i}return Object.defineProperty(o,`init`,{value:r}),Object.defineProperty(o,Symbol.hasInstance,{value:t=>n?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var h=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const g={};function _(e){return e&&Object.assign(g,e),g}function ne(e,t){return typeof t==`bigint`?t.toString():t}function re(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function ie(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const v=Symbol(`evaluating`);function y(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==v)return r===void 0&&(r=v,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const b=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function x(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ae(e){if(x(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(x(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function oe(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function S(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error(\"Cannot specify both `message` and `error` params\");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function se(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function C(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n<e.issues.length;n++)if(e.issues[n]?.continue!==!0)return!0;return!1}function w(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function T(e){return typeof e==`string`?e:e?.message}function E(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=T(e.inst?._zod.def?.error?.(e))??T(t?.error?.(e))??T(n.customError?.(e))??T(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const D=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,ne,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},ce=m(`$ZodError`,D),O=m(`$ZodError`,D,{Parent:Error}),le=(e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new h;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>E(e,a,_())));throw b(t,i?.callee),t}return o.value})(O),ue=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>E(e,a,_())));throw b(t,i?.callee),t}return o.value})(O),k=(e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new h;return a.issues.length?{success:!1,error:new(e??ce)(a.issues.map(e=>E(e,i,_())))}:{success:!0,data:a.value}})(O),A=(e=>async(t,n,r)=>{let i=r?Object.assign(r,{async:!0}):{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>E(e,i,_())))}:{success:!0,data:a.value}})(O),de=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},fe=/^-?\\d+(?:\\.\\d+)?$/,pe={major:4,minor:3,patch:6},j=m(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=pe;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=C(e),i;for(let a of t){if(a._zod.def.when){if(!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new h;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=C(e,t))});else{if(e.issues.length===t)continue;r||=C(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(C(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new h;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new h;return o.then(e=>t(e,r,a))}return t(o,r,a)}}y(e,`~standard`,()=>({validate:t=>{try{let n=k(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return A(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),me=m(`$ZodString`,(e,t)=>{j.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??de(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),he=m(`$ZodUnknown`,(e,t)=>{j.init(e,t),e._zod.parse=e=>e});function M(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...w(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function ge(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key \"${n}\": expected a Zod schema`);let n=se(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function _e(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optout===`optional`;for(let i in t){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>M(e,n,i,t,u))):M(a,n,i,t,u)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}const ve=m(`$ZodObject`,(e,t)=>{if(j.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=re(()=>ge(t));y(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=x,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optout===`optional`,i=n._zod.run({value:s[e],issues:[]},o);i instanceof Promise?c.push(i.then(n=>M(n,t,e,s,r))):M(i,t,e,s,r)}return i?_e(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),ye=m(`$ZodRecord`,(e,t)=>{j.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!ae(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let e of o)if(typeof e==`string`||typeof e==`number`||typeof e==`symbol`){s.add(typeof e==`number`?e.toString():e);let o=t.valueType._zod.run({value:i[e],issues:[]},r);o instanceof Promise?a.push(o.then(t=>{t.issues.length&&n.issues.push(...w(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...w(e,o.issues)),n.value[e]=o.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&fe.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>E(e,r,_())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...w(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...w(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function N(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const be=m(`$ZodOptional`,(e,t)=>{j.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,y(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),y(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${ie(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(t=>N(t,e.value)):N(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var P,xe=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function Se(){return new xe}(P=globalThis).__zod_globalRegistry??(P.__zod_globalRegistry=Se()),globalThis.__zod_globalRegistry;function Ce(e,t){return new e({type:`string`,...S(t)})}function we(e){return new e({type:`unknown`})}const F=m(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);j.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>le(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>k(e,t,n),e.parseAsync=async(t,n)=>ue(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>A(e,t,n),e.check=(...n)=>e.clone({...t,checks:[...t.checks??[],...n.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]},{parent:!0}),e.with=e.check,e.clone=(t,n)=>oe(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),Te=m(`ZodMiniString`,(e,t)=>{me.init(e,t),F.init(e,t)});function I(e){return Ce(Te,e)}const Ee=m(`ZodMiniUnknown`,(e,t)=>{he.init(e,t),F.init(e,t)});function L(){return we(Ee)}const De=m(`ZodMiniObject`,(e,t)=>{ve.init(e,t),F.init(e,t),y(e,`shape`,()=>t.shape)});function R(e,t){return new De({type:`object`,shape:e??{},...S(t)})}const Oe=m(`ZodMiniRecord`,(e,t)=>{ye.init(e,t),F.init(e,t)});function z(e,t,n){return new Oe({type:`record`,keyType:e,valueType:t,...S(n)})}const ke=m(`ZodMiniOptional`,(e,t)=>{be.init(e,t),F.init(e,t)});function B(e){return new ke({type:`optional`,innerType:e})}const Ae=R({session_id:I(),transcript_path:I(),cwd:I(),permission_mode:I(),hook_event_name:I(),tool_name:I(),tool_input:z(I(),L())});async function je(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t=JSON.parse(e.join(``)),n=Ae.safeParse(t);return n.success||(process.stderr.write(`[bst-hook] invalid hook input: ${n.error.message}\\n`),process.exit(0)),n.data}const Me=R({command:I(),description:B(I())}),Ne=R({file_path:I()}),Pe=R({pattern:I(),path:B(I())});function Fe(e){let t=Me.safeParse(e);return t.success?t.data:null}function Ie(e){let t=Ne.safeParse(e);return t.success?t.data:null}function Le(e){let t=Pe.safeParse(e);return t.success?t.data:null}function V(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function Re(){let e=d.dirname(te(import.meta.url));return d.join(e,`bst-hook.audit.local.jsonl`)}function ze(e){try{if(o.statSync(e).size>1048576){let t=`${e}.1`;o.renameSync(e,t)}}catch{}}let Be=!1;function Ve(e){try{let t=Re();Be||=(ze(t),!0);let n={timestamp:new Date().toISOString(),...e};o.appendFileSync(t,JSON.stringify(n)+`\n`)}catch(e){process.stderr.write(`[bst-hook] audit write failed: ${e instanceof Error?e.message:String(e)}\\n`)}}function H(e){let t=[],n=``,r=0,i=!1;for(;r<e.length;){let a=e[r];if(a===`'`){for(i=!0,r++;r<e.length&&e[r]!==`'`;)n+=e[r],r++;if(r>=e.length)return{ok:!1,error:`unterminated single quote`};r++}else if(a===`\"`){for(i=!0,r++;r<e.length&&e[r]!==`\"`;){if(e[r]===`\\\\`&&r+1<e.length){let t=e[r+1];if(t===`\"`||t===`\\\\`||t===`$`||t===\"`\"){n+=t,r+=2;continue}}n+=e[r],r++}if(r>=e.length)return{ok:!1,error:`unterminated double quote`};r++}else a===`\\\\`&&r+1<e.length?(i=!0,n+=e[r+1],r+=2):a===` `||a===` `?(i&&=(t.push(n),n=``,!1),r++):(i=!0,n+=a,r++)}return i&&t.push(n),{ok:!0,tokens:t}}function He(e){if(!e||!e.trim())return[];let t=[],n=``,r=0,i=!1,a=!1;for(;r<e.length;){let o=e[r];if(o===`'`&&!a){i=!i,n+=o,r++;continue}if(o===`\"`&&!i){a=!a,n+=o,r++;continue}if(o===`\\\\`&&!i&&r+1<e.length){n+=o+e[r+1],r+=2;continue}if(!i&&!a){if(o===`&`&&r+1<e.length&&e[r+1]===`&`){U(t,n,`&&`),n=``,r+=2;continue}if(o===`&`&&!(r+1<e.length&&e[r+1]===`>`)&&!(r>0&&(e[r-1]===`>`||e[r-1]===`<`))){U(t,n,`&`),n=``,r++;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){U(t,n,`||`),n=``,r+=2;continue}if(o===`|`){U(t,n,`|`),n=``,r++;continue}if(o===`;`){U(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function U(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const W=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function Ue(e,t){if(/[\\n\\r]/.test(t))return{safe:!1,reason:`unquoted newline in command`};for(let t of e){if(t===\"`\")return{safe:!1,reason:`backtick command substitution`};if(t.includes(`$(`))return{safe:!1,reason:`dollar command substitution`};if(t.includes(\"`\"))return{safe:!1,reason:`backtick command substitution`};if(t.includes(`<(`)||t.includes(`>(`)&&!W.has(t))return{safe:!1,reason:`process substitution`}}for(let t=0;t<e.length;t++){let n=e[t];if(!W.has(n)){if(n.includes(`<`)&&!n.includes(`<(`))return{safe:!1,reason:`input redirect`};if(n.includes(`>`)){if(n.endsWith(`>/dev/null`)||n.match(/^(&>>|&>|>>|>|1>|2>)$/)&&e[t+1]===`/dev/null`)continue;return{safe:!1,reason:`output redirect to file`}}}}return{safe:!0}}const We=R({scripts:z(I(),L())});function G(e,t){let n=Qe(t);if(!n)return null;let r=n[e];if(typeof r!=`string`)return null;let i=n[`pre${e}`],a=n[`post${e}`];return{pre:typeof i==`string`?i:null,main:r,post:typeof a==`string`?a:null}}function Ge(e,t,n){let r=Je(t,n);if(!r)return null;let i=G(e,r);return i?{...i,pkgDir:r}:null}function Ke(e,t){let n=K(t);if(!n)return null;let r=[];for(let t of n){let n=G(e,t);n&&r.push({...n,pkgDir:t})}return r}function qe(e){let t=p(e);for(;;){if(s(f(t,`pnpm-workspace.yaml`)))return t;let e=ee(t);if(e===t)return null;t=e}}function K(e){let t=Ye(e);if(!t)return null;let n=[];for(let r of t){if(!r.endsWith(`/*`))continue;let t=p(e,r.slice(0,-2)),i;try{i=l(t)}catch{continue}for(let e of i){let r=f(t,e);try{if(!u(r).isDirectory())continue}catch{continue}n.push(r)}}return n.length>0?n:null}function Je(e,t){let n=K(t);if(!n)return null;for(let t of n){let n=Ze(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function Ye(e){try{let t=c(f(e,`pnpm-workspace.yaml`),`utf-8`).split(`\n`),n=t.findIndex(e=>/^packages\\s*:/.test(e));if(n===-1)return null;let r=[];for(let e=n+1;e<t.length;e++){let n=t[e],i=/^\\s+-\\s+(.+)$/.exec(n);if(!i)break;r.push(i[1].trim().replace(/['\"`]/g,``))}return r.length>0?r:null}catch{return null}}const Xe=R({name:I()});function Ze(e){try{let t=c(f(e,`package.json`),`utf-8`),n=Xe.safeParse(JSON.parse(t));return n.success?n.data.name:null}catch{return null}}function Qe(e){try{let t=c(f(e,`package.json`),`utf-8`),n=We.safeParse(JSON.parse(t));return n.success?n.data.scripts:null}catch{return null}}const $e=new Set([`LD_PRELOAD`,`LD_LIBRARY_PATH`,`DYLD_INSERT_LIBRARIES`,`NODE_OPTIONS`,`PYTHONPATH`,`PYTHONSTARTUP`,`PERL5OPT`,`PERL5LIB`,`RUBYOPT`,`RUBYLIB`,`BASH_ENV`,`ENV`,`npm_config_prefix`,`npm_config_registry`,`EDITOR`,`VISUAL`]),et=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function q(e){return $e.has(e)?!0:et.some(t=>e.startsWith(t))}function tt(e,t){let n=H(e);if(!n.ok||n.tokens.length!==2)return null;let r=n.tokens;if(r[0]!==`cd`)return null;let i=r[1];return d.isAbsolute(i)?i:d.resolve(t.cwd,i)}function J(e,t){if(t.depth>=5)return{action:`passthrough`,rule:null,reason:`max evaluation depth exceeded`};let n=e.trim();if(!n)return{action:`passthrough`,rule:null,reason:null};if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=He(n);if(r.length===0)return{action:`passthrough`,rule:null,reason:null};let i=null,a=null,o=t;for(let e of r){if(e.operator===`&&`){let t=tt(e.command,o);t&&(o={...o,cwd:t})}let t=nt(e.command,o);if(t.action===`deny`)return t;t.action===`passthrough`&&(i||=t),t.action===`allow`&&(a=t)}return i||(r.length===1&&a?a:{action:`allow`,rule:`all-segments`,reason:null})}function Y(e,t){if(e.pre){let n=J(e.pre,t);if(n.action!==`allow`)return n}let n=J(e.main,t);if(n.action!==`allow`)return n;if(e.post){let n=J(e.post,t);if(n.action!==`allow`)return n}return`allow`}function nt(n,r){let i=n.trim();if(i.startsWith(`(`)&&i.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let a=H(n);if(!a.ok)return{action:`passthrough`,rule:null,reason:a.error};let o=a.tokens;if(o.length===0)return{action:`passthrough`,rule:null,reason:null};let s=0;for(;s<o.length&&/^\\w+=/.test(o[s]);){let e=o[s].split(`=`)[0];if(q(e))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${e}`};s++}if(s>=o.length)return{action:`allow`,rule:`env-only`,reason:null};o=o.slice(s);let c=Ue(o,n);if(!c.safe)return{action:`passthrough`,rule:null,reason:c.reason};for(let e=0;e<o.length;e++){let t=o[e];if(!(t===`2>&1`||t===`2>/dev/null`||t===`>/dev/null`||t===`1>/dev/null`)){if(t===`>`||t===`>>`){if(o[e+1]===`/dev/null`)continue;return{action:`passthrough`,rule:null,reason:`output redirect to file`}}if(t===`<`)return{action:`passthrough`,rule:null,reason:`input redirect`}}}let l=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);o=o.filter((e,t)=>!(l.has(e)||(e===`>`||e===`>>`)&&o[t+1]===`/dev/null`||e===`/dev/null`&&t>0&&(o[t-1]===`>`||o[t-1]===`>>`)));let u=d.basename(o[0]);if(u===`command`&&o.length>1)return J(o.slice(1).join(` `),{...r,depth:r.depth+1});if(u===`env`){let e=1;for(;e<o.length&&/^\\w+=/.test(o[e]);){let t=o[e].split(`=`)[0];if(q(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=o.length?{action:`allow`,rule:`env-only`,reason:null}:J(o.slice(e).join(` `),{...r,depth:r.depth+1})}if(u===`pnpm`)return rt(o,r);if(u===`npm`)return at(o);if(u===`bash`||u===`sh`||u===`zsh`)return ct(o,r);if(u===`node`&&(o.includes(`-e`)||o.includes(`-p`)||o.includes(`--eval`)||o.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(u===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let t of e)if(t.match.test(u)&&(!t.when||t.when(o,r)))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let e of t)if(e.match.test(u)&&(!e.when||e.when(o,r)))return{action:`allow`,rule:e.name,reason:null};return{action:`passthrough`,rule:null,reason:`no rule matched: ${u}`}}function rt(e,n){let r=e.slice(1),a,o=-1,s=!1,c=!1,l;for(let t=0;t<r.length;t++){let u=r[t];if(u===`--filter`||u.startsWith(`--filter=`))return ot(e,r,t,n);if(u===`-r`||u===`--recursive`){s=!0;continue}if(u===`-w`||u===`--workspace-root`){c=!0;continue}if(u===`-C`||u===`--dir`){l=r[++t];continue}if(u.startsWith(`-C=`)||u.startsWith(`--dir=`)){l=u.split(`=`,2)[1];continue}if(!(u.startsWith(`-`)&&!i.has(u))){a=u,o=t;break}}if(!a)return{action:`passthrough`,rule:null,reason:null};if(a===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(a===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(a===`exec`){let e=r.slice(o+1);return e.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:J(e.join(` `),{...n,depth:n.depth+1})}let u;if(a===`run`){let e=r[o+1];if(!e||e.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm run without script name`};u=e}else if(!i.has(a))u=a;else{for(let r of t)if(r.match.test(`pnpm`)&&(!r.when||r.when(e,n)))return{action:`allow`,rule:r.name,reason:null};return{action:`passthrough`,rule:null,reason:null}}if(s)return it(u,n);if(c){let e=qe(n.cwd);return e?X(u,{...n,cwd:e}):{action:`passthrough`,rule:null,reason:`workspace root not found`}}if(l){let e=d.resolve(n.cwd,l);return X(u,{...n,cwd:e})}return X(u,n)}function X(e,t){let n=G(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r=Y(n,{...t,depth:t.depth+1});return r===`allow`?{action:`allow`,rule:`pnpm-script:${e}`,reason:null}:r}function it(e,t){let n=Ke(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n=Y(e,{...t,depth:t.depth+1,cwd:e.pkgDir??t.cwd});if(n!==`allow`)return n}return{action:`allow`,rule:`pnpm-recursive:${e}`,reason:null}}function at(e){for(let n of t)if(n.match.test(`npm`)&&(!n.when||n.when(e,{cwd:``,depth:0})))return{action:`allow`,rule:n.name,reason:null};return{action:`deny`,rule:`npm-redirect`,reason:r(e)}}function ot(e,t,n,r){let a=t[n],o,s;if(a.startsWith(`--filter=`))o=a.slice(9),s=n+1;else{if(o=t[n+1],!o||o.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};s=n+2}if(!o)return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};let c;for(let e=s;e<t.length;e++){let n=t[e];if(!(n.startsWith(`-`)&&!i.has(n))){c=n;break}}if(!c)return{action:`passthrough`,rule:null,reason:`pnpm --filter without subcommand`};if(c===`install`||c===`i`)return t.slice(t.indexOf(c)+1).every(e=>e.startsWith(`-`))?{action:`allow`,rule:`pnpm`,reason:null}:{action:`passthrough`,rule:null,reason:`pnpm --filter install with package arg`};if(c===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(c===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`,`licenses`,`audit`]).has(c))return{action:`allow`,rule:`pnpm`,reason:null};if(c===`exec`){let e=t.indexOf(`exec`,s),n=t.slice(e+1);return n.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:J(n.join(` `),{...r,depth:r.depth+1})}let l=c===`run`?t[t.indexOf(`run`,s)+1]:c;if(!l||l.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter run without script name`};if(i.has(l)&&l!==`run`)return{action:`passthrough`,rule:null,reason:null};let u=Ge(l,o,r.cwd);if(!u)return{action:`passthrough`,rule:null,reason:`filtered script resolution failed`};let d=Y(u,{...r,depth:r.depth+1,cwd:u.pkgDir??r.cwd});return d===`allow`?{action:`allow`,rule:`pnpm-filter:${o}:${l}`,reason:null}:d}const st=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function ct(e,t){let n=e.indexOf(`-c`);if(n===-1)return{action:`passthrough`,rule:null,reason:`shell without -c`};for(let t=1;t<e.length;t++){if(t===n||t===n+1)continue;let r=e[t];if(r.startsWith(`-`)||st.has(r))return{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}}let r=e[n+1];return r?e.length>n+2?{action:`passthrough`,rule:null,reason:`shell -c with positional args`}:n===1?J(r,{...t,depth:t.depth+1}):{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}:{action:`passthrough`,rule:null,reason:`shell -c without command`}}const lt=new Set([`Read`,`Write`,`Edit`]),ut=new Set([`Glob`,`Grep`]);function dt(e,t,n){return lt.has(e)?Ie(t)?.file_path??null:ut.has(e)?Le(t)?.path??n:null}function ft(e,t,r){let i=dt(e,t,r.cwd);if(i===null)return{action:`passthrough`,rule:null,reason:`unknown tool input shape`};for(let t of n)if(t.tools.has(e)&&t.when(i,r))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let t of a)if(t.tools.has(e)&&t.when(i,r))return{action:`allow`,rule:t.name,reason:null};return{action:`passthrough`,rule:null,reason:`no file rule matched`}}const Z=await je();Z.hook_event_name!==`PreToolUse`&&process.exit(0),new Set([`Bash`,`Read`,`Write`,`Edit`,`Glob`,`Grep`]).has(Z.tool_name)||process.exit(0);let Q,$;if(Z.tool_name===`Bash`){let e=Fe(Z.tool_input);e||process.exit(0),Q=J(e.command,{cwd:Z.cwd,depth:0}),$=e.command}else{let e=process.env.CLAUDE_PROJECT_DIR??Z.cwd;Q=ft(Z.tool_name,Z.tool_input,{cwd:e});let t=String(Z.tool_input.file_path??Z.tool_input.path??``);$=`${Z.tool_name} ${t}`}Ve({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:$,decision:Q.action,rule:Q.rule,reason:Q.reason}),Q.action===`allow`?V(`allow`):Q.action===`deny`&&V(`deny`,Q.reason);export{};", "bst-hook.mjs"), writeIfChanged(ctx, GUIDE_PATH, "# bst-hook Permission Guide\n\nThis project uses a PreToolUse hook (`bst-hook`) that evaluates Bash commands\nand file tool operations before execution. Commands fall into three tiers:\n\n- **Denied** — blocked with a redirect message\n- **Allowed** — auto-approved, no prompt\n- **Passthrough** — falls through to Claude Code's normal permission prompt\n\n## Denied commands\n\nThese are blocked. The hook tells you what to do instead.\n\n### Bash\n\n- **rm-recursive**: Don't use recursive `rm`. Use more targeted deletes, or remove specific files/directories.\n- **sudo**: Don't use sudo. If you need elevated permissions, ask me to do it manually.\n- **git-force-push**: Use `git push --force-with-lease` instead of `--force`.\n- **git-reset-hard**: Don't `reset --hard` on main. Create a branch first.\n- **git-write**: Git write operations (push, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.\n- **chmod-777**: Don't use 777 permissions. Use more restrictive permissions.\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **npx-redirect**: This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\n\n### File tools (Read, Write, Edit, Glob, Grep)\n\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **outside-cwd-write**: Don't write files outside the project directory.\n\n## Auto-approved commands\n\nThese run without prompting.\n\n### Bash\n\n- **Test runners**: vitest, jest\n- **Type checking**: tsc\n- **Build tools**: tsdown\n- **Linters & formatters**: oxlint, oxfmt, eslint, prettier, biome, knip\n- **Read-only commands**: cat, head, tail, grep, ls, wc, uniq, diff, echo, pwd, which, dirname, basename, realpath\n- **Package managers (read-only)**: npm (view, info, show only)\n- **Shell builtins**: test, bracket-test\n- **Navigation**: cd, true, colon\n- **Conditional commands**: find (without -exec, -delete), sort (without -o / --output)\n- **Git read operations**: git (status, log, diff, show, blame, branch, remote, fetch, rev-parse, etc.)\n- **Git safe writes**: git-safe-write (add, switch -c, checkout -b, stash push/pop/apply/show)\n- **pnpm (safe built-ins)**: pnpm (list, why, view, info, show, outdated, install (bare only))\n- **File operations (within project)**: cp (paths must be within project directory), mkdir (paths must be within project directory), touch (paths must be within project directory)\n\n### File tools\n\n- **Read/Glob/Grep**: Allowed within project directory\n- **Write/Edit**: Allowed within project directory\n\n## Passthrough\n\nEverything not listed above triggers Claude Code's normal permission prompt.\nCommon examples:\n\n- `pnpm add <pkg>`, `pnpm dlx <pkg>` (dependency changes)\n- `mv` (file moves)\n- `node`, `npx` scripts\n- Commands with output redirection (`> file`)\n- Any unrecognized command\n\n## How commands are evaluated\n\n- **Pipelines and chains**: every segment must be allowed; any deny blocks the whole command.\n- **pnpm scripts**: `pnpm <script>` resolves against `package.json` (including pre/post hooks and `--filter` workspaces) and evaluates the resolved command recursively.\n- **cd propagation**: `cd dir && cmd` evaluates `cmd` relative to the new directory.\n- **Audit log**: all decisions are logged to `.claude/hooks/bst-hook.audit.local.jsonl`.\n", "bst-hook-guide.md")];
2670
+ return [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.6.0\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync — do not edit manually.\n */\nimport{a as e,i as t,n,o as r,r as i,t as a}from\"./file-rules-RB-fEyic.mjs\";import*as o from\"node:fs\";import{existsSync as s,readFileSync as c,readdirSync as l,statSync as u}from\"node:fs\";import*as d from\"node:path\";import{dirname as ee,join as f,resolve as p}from\"node:path\";import{fileURLToPath as te}from\"node:url\";Object.freeze({status:`aborted`});function m(e,t,n){function r(n,r){if(n._zod||Object.defineProperty(n,`_zod`,{value:{def:r,constr:o,traits:new Set},enumerable:!1}),n._zod.traits.has(e))return;n._zod.traits.add(e),t(n,r);let i=o.prototype,a=Object.keys(i);for(let e=0;e<a.length;e++){let t=a[e];t in n||(n[t]=i[t].bind(n))}}let i=n?.Parent??Object;class a extends i{}Object.defineProperty(a,`name`,{value:e});function o(e){var t;let i=n?.Parent?new a:this;r(i,e),(t=i._zod).deferred??(t.deferred=[]);for(let e of i._zod.deferred)e();return i}return Object.defineProperty(o,`init`,{value:r}),Object.defineProperty(o,Symbol.hasInstance,{value:t=>n?.Parent&&t instanceof n.Parent?!0:t?._zod?.traits?.has(e)}),Object.defineProperty(o,`name`,{value:e}),o}var h=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const g={};function _(e){return e&&Object.assign(g,e),g}function ne(e,t){return typeof t==`bigint`?t.toString():t}function re(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function ie(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const v=Symbol(`evaluating`);function y(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==v)return r===void 0&&(r=v,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const b=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function x(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ae(e){if(x(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(x(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function oe(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function S(e){let t=e;if(!t)return{};if(typeof t==`string`)return{error:()=>t};if(t?.message!==void 0){if(t?.error!==void 0)throw Error(\"Cannot specify both `message` and `error` params\");t.error=t.message}return delete t.message,typeof t.error==`string`?{...t,error:()=>t.error}:t}function se(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function C(e,t=0){if(e.aborted===!0)return!0;for(let n=t;n<e.issues.length;n++)if(e.issues[n]?.continue!==!0)return!0;return!1}function w(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function T(e){return typeof e==`string`?e:e?.message}function E(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=T(e.inst?._zod.def?.error?.(e))??T(t?.error?.(e))??T(n.customError?.(e))??T(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const ce=(e,t)=>{e.name=`$ZodError`,Object.defineProperty(e,`_zod`,{value:e._zod,enumerable:!1}),Object.defineProperty(e,`issues`,{value:t,enumerable:!1}),e.message=JSON.stringify(t,ne,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},le=m(`$ZodError`,ce),D=m(`$ZodError`,ce,{Parent:Error}),ue=(e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new h;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>E(e,a,_())));throw b(t,i?.callee),t}return o.value})(D),de=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>E(e,a,_())));throw b(t,i?.callee),t}return o.value})(D),O=(e=>(t,n,r)=>{let i=r?{...r,async:!1}:{async:!1},a=t._zod.run({value:n,issues:[]},i);if(a instanceof Promise)throw new h;return a.issues.length?{success:!1,error:new(e??le)(a.issues.map(e=>E(e,i,_())))}:{success:!0,data:a.value}})(D),k=(e=>async(t,n,r)=>{let i=r?Object.assign(r,{async:!0}):{async:!0},a=t._zod.run({value:n,issues:[]},i);return a instanceof Promise&&(a=await a),a.issues.length?{success:!1,error:new e(a.issues.map(e=>E(e,i,_())))}:{success:!0,data:a.value}})(D),fe=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},pe=/^-?\\d+(?:\\.\\d+)?$/,me={major:4,minor:3,patch:6},A=m(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=me;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=C(e),i;for(let a of t){if(a._zod.def.when){if(!a._zod.def.when(e))continue}else if(r)continue;let t=e.issues.length,o=a._zod.check(e);if(o instanceof Promise&&n?.async===!1)throw new h;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=C(e,t))});else{if(e.issues.length===t)continue;r||=C(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(C(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new h;return o.then(t=>e._zod.parse(t,a))}return e._zod.parse(o,a)};e._zod.run=(i,a)=>{if(a.skipChecks)return e._zod.parse(i,a);if(a.direction===`backward`){let t=e._zod.parse({value:i.value,issues:[]},{...a,skipChecks:!0});return t instanceof Promise?t.then(e=>n(e,i,a)):n(t,i,a)}let o=e._zod.parse(i,a);if(o instanceof Promise){if(a.async===!1)throw new h;return o.then(e=>t(e,r,a))}return t(o,r,a)}}y(e,`~standard`,()=>({validate:t=>{try{let n=O(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return k(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),he=m(`$ZodString`,(e,t)=>{A.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??fe(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),ge=m(`$ZodUnknown`,(e,t)=>{A.init(e,t),e._zod.parse=e=>e});function j(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...w(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function _e(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key \"${n}\": expected a Zod schema`);let n=se(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function ve(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optout===`optional`;for(let i in t){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>j(e,n,i,t,u))):j(a,n,i,t,u)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}const ye=m(`$ZodObject`,(e,t)=>{if(A.init(e,t),!Object.getOwnPropertyDescriptor(t,`shape`)?.get){let e=t.shape;Object.defineProperty(t,`shape`,{get:()=>{let n={...e};return Object.defineProperty(t,`shape`,{value:n}),n}})}let n=re(()=>_e(t));y(e._zod,`propValues`,()=>{let e=t.shape,n={};for(let t in e){let r=e[t]._zod;if(r.values){n[t]??(n[t]=new Set);for(let e of r.values)n[t].add(e)}}return n});let r=x,i=t.catchall,a;e._zod.parse=(t,o)=>{a??=n.value;let s=t.value;if(!r(s))return t.issues.push({expected:`object`,code:`invalid_type`,input:s,inst:e}),t;t.value={};let c=[],l=a.shape;for(let e of a.keys){let n=l[e],r=n._zod.optout===`optional`,i=n._zod.run({value:s[e],issues:[]},o);i instanceof Promise?c.push(i.then(n=>j(n,t,e,s,r))):j(i,t,e,s,r)}return i?ve(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),be=m(`$ZodRecord`,(e,t)=>{A.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!ae(i))return n.issues.push({expected:`record`,code:`invalid_type`,input:i,inst:e}),n;let a=[],o=t.keyType._zod.values;if(o){n.value={};let s=new Set;for(let e of o)if(typeof e==`string`||typeof e==`number`||typeof e==`symbol`){s.add(typeof e==`number`?e.toString():e);let o=t.valueType._zod.run({value:i[e],issues:[]},r);o instanceof Promise?a.push(o.then(t=>{t.issues.length&&n.issues.push(...w(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...w(e,o.issues)),n.value[e]=o.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&pe.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>E(e,r,_())),input:o,path:[o],inst:e});continue}let c=t.valueType._zod.run({value:i[o],issues:[]},r);c instanceof Promise?a.push(c.then(e=>{e.issues.length&&n.issues.push(...w(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...w(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function M(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const xe=m(`$ZodOptional`,(e,t)=>{A.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,y(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),y(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${ie(e.source)})?$`):void 0}),e._zod.parse=(e,n)=>{if(t.innerType._zod.optin===`optional`){let r=t.innerType._zod.run(e,n);return r instanceof Promise?r.then(t=>M(t,e.value)):M(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var N,Se=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function Ce(){return new Se}(N=globalThis).__zod_globalRegistry??(N.__zod_globalRegistry=Ce()),globalThis.__zod_globalRegistry;function we(e,t){return new e({type:`string`,...S(t)})}function Te(e){return new e({type:`unknown`})}const P=m(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);A.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>ue(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>O(e,t,n),e.parseAsync=async(t,n)=>de(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>k(e,t,n),e.check=(...n)=>e.clone({...t,checks:[...t.checks??[],...n.map(e=>typeof e==`function`?{_zod:{check:e,def:{check:`custom`},onattach:[]}}:e)]},{parent:!0}),e.with=e.check,e.clone=(t,n)=>oe(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),Ee=m(`ZodMiniString`,(e,t)=>{he.init(e,t),P.init(e,t)});function F(e){return we(Ee,e)}const De=m(`ZodMiniUnknown`,(e,t)=>{ge.init(e,t),P.init(e,t)});function I(){return Te(De)}const Oe=m(`ZodMiniObject`,(e,t)=>{ye.init(e,t),P.init(e,t),y(e,`shape`,()=>t.shape)});function L(e,t){return new Oe({type:`object`,shape:e??{},...S(t)})}const ke=m(`ZodMiniRecord`,(e,t)=>{be.init(e,t),P.init(e,t)});function R(e,t,n){return new ke({type:`record`,keyType:e,valueType:t,...S(n)})}const Ae=m(`ZodMiniOptional`,(e,t)=>{xe.init(e,t),P.init(e,t)});function z(e){return new Ae({type:`optional`,innerType:e})}const je=L({session_id:F(),transcript_path:F(),cwd:F(),permission_mode:F(),hook_event_name:F(),tool_name:F(),tool_input:R(F(),I())});async function Me(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t=JSON.parse(e.join(``)),n=je.safeParse(t);return n.success||(process.stderr.write(`[bst-hook] invalid hook input: ${n.error.message}\\n`),process.exit(0)),n.data}const Ne=L({command:F(),description:z(F())}),Pe=L({file_path:F()}),Fe=L({pattern:F(),path:z(F())});function Ie(e){let t=Ne.safeParse(e);return t.success?t.data:null}function Le(e){let t=Pe.safeParse(e);return t.success?t.data:null}function Re(e){let t=Fe.safeParse(e);return t.success?t.data:null}function B(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function ze(){let e=d.dirname(te(import.meta.url));return d.join(e,`bst-hook.audit.local.jsonl`)}function Be(e){try{if(o.statSync(e).size>1048576){let t=`${e}.1`;o.renameSync(e,t)}}catch{}}let Ve=!1;function He(e){try{let t=ze();Ve||=(Be(t),!0);let n={timestamp:new Date().toISOString(),...e};o.appendFileSync(t,JSON.stringify(n)+`\n`)}catch(e){process.stderr.write(`[bst-hook] audit write failed: ${e instanceof Error?e.message:String(e)}\\n`)}}function V(e){let t=[],n=``,r=0,i=!1;for(;r<e.length;){let a=e[r];if(a===`'`){for(i=!0,r++;r<e.length&&e[r]!==`'`;)n+=e[r],r++;if(r>=e.length)return{ok:!1,error:`unterminated single quote`};r++}else if(a===`\"`){for(i=!0,r++;r<e.length&&e[r]!==`\"`;){if(e[r]===`\\\\`&&r+1<e.length){let t=e[r+1];if(t===`\"`||t===`\\\\`||t===`$`||t===\"`\"){n+=t,r+=2;continue}}n+=e[r],r++}if(r>=e.length)return{ok:!1,error:`unterminated double quote`};r++}else a===`\\\\`&&r+1<e.length?(i=!0,n+=e[r+1],r+=2):a===` `||a===` `?(i&&=(t.push(n),n=``,!1),r++):(i=!0,n+=a,r++)}return i&&t.push(n),{ok:!0,tokens:t}}function Ue(e){if(!e||!e.trim())return[];let t=[],n=``,r=0,i=!1,a=!1;for(;r<e.length;){let o=e[r];if(o===`'`&&!a){i=!i,n+=o,r++;continue}if(o===`\"`&&!i){a=!a,n+=o,r++;continue}if(o===`\\\\`&&!i&&r+1<e.length){n+=o+e[r+1],r+=2;continue}if(!i&&!a){if(o===`&`&&r+1<e.length&&e[r+1]===`&`){H(t,n,`&&`),n=``,r+=2;continue}if(o===`&`&&!(r+1<e.length&&e[r+1]===`>`)&&!(r>0&&(e[r-1]===`>`||e[r-1]===`<`))){H(t,n,`&`),n=``,r++;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){H(t,n,`||`),n=``,r+=2;continue}if(o===`|`){H(t,n,`|`),n=``,r++;continue}if(o===`;`){H(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function H(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const U=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function We(e,t){if(/[\\n\\r]/.test(t))return{safe:!1,reason:`unquoted newline in command`};for(let t of e){if(t===\"`\")return{safe:!1,reason:`backtick command substitution`};if(t.includes(`$(`))return{safe:!1,reason:`dollar command substitution`};if(t.includes(\"`\"))return{safe:!1,reason:`backtick command substitution`};if(t.includes(`<(`)||t.includes(`>(`)&&!U.has(t))return{safe:!1,reason:`process substitution`}}for(let t=0;t<e.length;t++){let n=e[t];if(!U.has(n)){if(n.includes(`<`)&&!n.includes(`<(`))return{safe:!1,reason:`input redirect`};if(n.includes(`>`)){if(n.endsWith(`>/dev/null`)||n.match(/^(&>>|&>|>>|>|1>|2>)$/)&&e[t+1]===`/dev/null`)continue;return{safe:!1,reason:`output redirect to file`}}}}return{safe:!0}}const Ge=L({scripts:R(F(),I())});function W(e,t){let n=$e(t);if(!n)return null;let r=n[e];if(typeof r!=`string`)return null;let i=n[`pre${e}`],a=n[`post${e}`];return{pre:typeof i==`string`?i:null,main:r,post:typeof a==`string`?a:null}}function Ke(e,t,n){let r=Ye(t,n);if(!r)return null;let i=W(e,r);return i?{...i,pkgDir:r}:null}function qe(e,t){let n=G(t);if(!n)return null;let r=[];for(let t of n){let n=W(e,t);n&&r.push({...n,pkgDir:t})}return r}function Je(e){let t=p(e);for(;;){if(s(f(t,`pnpm-workspace.yaml`)))return t;let e=ee(t);if(e===t)return null;t=e}}function G(e){let t=Xe(e);if(!t)return null;let n=[];for(let r of t){if(!r.endsWith(`/*`))continue;let t=p(e,r.slice(0,-2)),i;try{i=l(t)}catch{continue}for(let e of i){let r=f(t,e);try{if(!u(r).isDirectory())continue}catch{continue}n.push(r)}}return n.length>0?n:null}function Ye(e,t){let n=G(t);if(!n)return null;for(let t of n){let n=Qe(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function Xe(e){try{let t=c(f(e,`pnpm-workspace.yaml`),`utf-8`).split(`\n`),n=t.findIndex(e=>/^packages\\s*:/.test(e));if(n===-1)return null;let r=[];for(let e=n+1;e<t.length;e++){let n=t[e],i=/^\\s+-\\s+(.+)$/.exec(n);if(!i)break;r.push(i[1].trim().replace(/['\"`]/g,``))}return r.length>0?r:null}catch{return null}}const Ze=L({name:F()});function Qe(e){try{let t=c(f(e,`package.json`),`utf-8`),n=Ze.safeParse(JSON.parse(t));return n.success?n.data.name:null}catch{return null}}function $e(e){try{let t=c(f(e,`package.json`),`utf-8`),n=Ge.safeParse(JSON.parse(t));return n.success?n.data.scripts:null}catch{return null}}const et=new Set([`LD_PRELOAD`,`LD_LIBRARY_PATH`,`DYLD_INSERT_LIBRARIES`,`NODE_OPTIONS`,`PYTHONPATH`,`PYTHONSTARTUP`,`PERL5OPT`,`PERL5LIB`,`RUBYOPT`,`RUBYLIB`,`BASH_ENV`,`ENV`,`npm_config_prefix`,`npm_config_registry`,`EDITOR`,`VISUAL`]),tt=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function K(e){return et.has(e)?!0:tt.some(t=>e.startsWith(t))}function nt(e,t){let n=V(e);if(!n.ok||n.tokens.length!==2)return null;let r=n.tokens;if(r[0]!==`cd`)return null;let i=r[1];return d.isAbsolute(i)?i:d.resolve(t.cwd,i)}function q(e,t){if(t.depth>=5)return{action:`passthrough`,rule:null,reason:`max evaluation depth exceeded`};let n=e.trim();if(!n)return{action:`passthrough`,rule:null,reason:null};if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=Ue(n);if(r.length===0)return{action:`passthrough`,rule:null,reason:null};let i=null,a=null,o=t;for(let e of r){if(e.operator===`&&`){let t=nt(e.command,o);t&&(o={...o,cwd:t})}let t=rt(e.command,o);if(t.action===`deny`)return t;t.action===`passthrough`&&(i||=t),t.action===`allow`&&(a=t)}return i||(r.length===1&&a?a:{action:`allow`,rule:`all-segments`,reason:null})}function J(e,t){if(e.pre){let n=q(e.pre,t);if(n.action!==`allow`)return n}let n=q(e.main,t);if(n.action!==`allow`)return n;if(e.post){let n=q(e.post,t);if(n.action!==`allow`)return n}return`allow`}function rt(n,r){let i=n.trim();if(i.startsWith(`(`)&&i.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let a=V(n);if(!a.ok)return{action:`passthrough`,rule:null,reason:a.error};let o=a.tokens;if(o.length===0)return{action:`passthrough`,rule:null,reason:null};let s=0;for(;s<o.length&&/^\\w+=/.test(o[s]);){let e=o[s].split(`=`)[0];if(K(e))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${e}`};s++}if(s>=o.length)return{action:`allow`,rule:`env-only`,reason:null};o=o.slice(s);let c=We(o,n);if(!c.safe)return{action:`passthrough`,rule:null,reason:c.reason};for(let e=0;e<o.length;e++){let t=o[e];if(!(t===`2>&1`||t===`2>/dev/null`||t===`>/dev/null`||t===`1>/dev/null`)){if(t===`>`||t===`>>`){if(o[e+1]===`/dev/null`)continue;return{action:`passthrough`,rule:null,reason:`output redirect to file`}}if(t===`<`)return{action:`passthrough`,rule:null,reason:`input redirect`}}}let l=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);o=o.filter((e,t)=>!(l.has(e)||(e===`>`||e===`>>`)&&o[t+1]===`/dev/null`||e===`/dev/null`&&t>0&&(o[t-1]===`>`||o[t-1]===`>>`)));let u=d.basename(o[0]);if(u===`command`&&o.length>1)return q(o.slice(1).join(` `),{...r,depth:r.depth+1});if(u===`env`){let e=1;for(;e<o.length&&/^\\w+=/.test(o[e]);){let t=o[e].split(`=`)[0];if(K(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=o.length?{action:`allow`,rule:`env-only`,reason:null}:q(o.slice(e).join(` `),{...r,depth:r.depth+1})}if(u===`pnpm`)return it(o,r);if(u===`npm`)return ot(o);if(u===`bash`||u===`sh`||u===`zsh`)return lt(o,r);if(u===`xargs`)return dt(o,r);if(u===`node`&&(o.includes(`-e`)||o.includes(`-p`)||o.includes(`--eval`)||o.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(u===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let t of e)if(t.match.test(u)&&(!t.when||t.when(o,r)))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let e of t)if(e.match.test(u)&&(!e.when||e.when(o,r)))return{action:`allow`,rule:e.name,reason:null};return{action:`passthrough`,rule:null,reason:`no rule matched: ${u}`}}function it(e,n){let r=e.slice(1),a,o=-1,s=!1,c=!1,l;for(let t=0;t<r.length;t++){let u=r[t];if(u===`--filter`||u.startsWith(`--filter=`))return st(e,r,t,n);if(u===`-r`||u===`--recursive`){s=!0;continue}if(u===`-w`||u===`--workspace-root`){c=!0;continue}if(u===`-C`||u===`--dir`){l=r[++t];continue}if(u.startsWith(`-C=`)||u.startsWith(`--dir=`)){l=u.split(`=`,2)[1];continue}if(!(u.startsWith(`-`)&&!i.has(u))){a=u,o=t;break}}if(!a)return{action:`passthrough`,rule:null,reason:null};if(a===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(a===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(a===`exec`){let e=r.slice(o+1);return e.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:q(e.join(` `),{...n,depth:n.depth+1})}let u;if(a===`run`){let e=r[o+1];if(!e||e.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm run without script name`};u=e}else if(!i.has(a))u=a;else{for(let r of t)if(r.match.test(`pnpm`)&&(!r.when||r.when(e,n)))return{action:`allow`,rule:r.name,reason:null};return{action:`passthrough`,rule:null,reason:null}}if(s)return at(u,n);if(c){let e=Je(n.cwd);return e?Y(u,{...n,cwd:e}):{action:`passthrough`,rule:null,reason:`workspace root not found`}}if(l){let e=d.resolve(n.cwd,l);return Y(u,{...n,cwd:e})}return Y(u,n)}function Y(e,t){let n=W(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r=J(n,{...t,depth:t.depth+1});return r===`allow`?{action:`allow`,rule:`pnpm-script:${e}`,reason:null}:r}function at(e,t){let n=qe(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n=J(e,{...t,depth:t.depth+1,cwd:e.pkgDir??t.cwd});if(n!==`allow`)return n}return{action:`allow`,rule:`pnpm-recursive:${e}`,reason:null}}function ot(e){for(let n of t)if(n.match.test(`npm`)&&(!n.when||n.when(e,{cwd:``,depth:0})))return{action:`allow`,rule:n.name,reason:null};return{action:`deny`,rule:`npm-redirect`,reason:r(e)}}function st(e,t,n,r){let a=t[n],o,s;if(a.startsWith(`--filter=`))o=a.slice(9),s=n+1;else{if(o=t[n+1],!o||o.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};s=n+2}if(!o)return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};let c;for(let e=s;e<t.length;e++){let n=t[e];if(!(n.startsWith(`-`)&&!i.has(n))){c=n;break}}if(!c)return{action:`passthrough`,rule:null,reason:`pnpm --filter without subcommand`};if(c===`install`||c===`i`)return t.slice(t.indexOf(c)+1).every(e=>e.startsWith(`-`))?{action:`allow`,rule:`pnpm`,reason:null}:{action:`passthrough`,rule:null,reason:`pnpm --filter install with package arg`};if(c===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(c===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`,`licenses`,`audit`]).has(c))return{action:`allow`,rule:`pnpm`,reason:null};if(c===`exec`){let e=t.indexOf(`exec`,s),n=t.slice(e+1);return n.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:q(n.join(` `),{...r,depth:r.depth+1})}let l=c===`run`?t[t.indexOf(`run`,s)+1]:c;if(!l||l.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter run without script name`};if(i.has(l)&&l!==`run`)return{action:`passthrough`,rule:null,reason:null};let u=Ke(l,o,r.cwd);if(!u)return{action:`passthrough`,rule:null,reason:`filtered script resolution failed`};let d=J(u,{...r,depth:r.depth+1,cwd:u.pkgDir??r.cwd});return d===`allow`?{action:`allow`,rule:`pnpm-filter:${o}:${l}`,reason:null}:d}const ct=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function lt(e,t){let n=e.indexOf(`-c`);if(n===-1)return{action:`passthrough`,rule:null,reason:`shell without -c`};for(let t=1;t<e.length;t++){if(t===n||t===n+1)continue;let r=e[t];if(r.startsWith(`-`)||ct.has(r))return{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}}let r=e[n+1];return r?e.length>n+2?{action:`passthrough`,rule:null,reason:`shell -c with positional args`}:n===1?q(r,{...t,depth:t.depth+1}):{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}:{action:`passthrough`,rule:null,reason:`shell -c without command`}}const X=new Set([`-I`,`-d`,`-n`,`-P`,`-s`,`-L`,`-E`,`-a`,`--max-args`,`--max-procs`,`--delimiter`,`--replace`,`--max-lines`,`--max-chars`,`--arg-file`,`--process-slot-var`]),ut=new Set([`-0`,`--null`,`-t`,`--verbose`,`-r`,`--no-run-if-empty`,`-p`,`--interactive`,`-x`,`--exit`,`--show-limits`]);function dt(e,t){let n=1;for(;n<e.length;){let t=e[n];if(!t.startsWith(`-`))break;if(X.has(t)){n+=2;continue}let r=t.indexOf(`=`);if(r!==-1){let e=t.slice(0,r);if(X.has(e)){n++;continue}}if(ut.has(t)){n++;continue}return{action:`passthrough`,rule:null,reason:`xargs with unrecognised flags`}}return n>=e.length?{action:`passthrough`,rule:null,reason:`xargs without command`}:q(e.slice(n).join(` `),{...t,depth:t.depth+1})}const ft=new Set([`Read`,`Write`,`Edit`]),pt=new Set([`Glob`,`Grep`]);function mt(e,t,n){return ft.has(e)?Le(t)?.file_path??null:pt.has(e)?Re(t)?.path??n:null}function ht(e,t,r){let i=mt(e,t,r.cwd);if(i===null)return{action:`passthrough`,rule:null,reason:`unknown tool input shape`};for(let t of n)if(t.tools.has(e)&&t.when(i,r))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let t of a)if(t.tools.has(e)&&t.when(i,r))return{action:`allow`,rule:t.name,reason:null};return{action:`passthrough`,rule:null,reason:`no file rule matched`}}const Z=await Me();Z.hook_event_name!==`PreToolUse`&&process.exit(0),new Set([`Bash`,`Read`,`Write`,`Edit`,`Glob`,`Grep`]).has(Z.tool_name)||process.exit(0);let Q,$;if(Z.tool_name===`Bash`){let e=Ie(Z.tool_input);e||process.exit(0),Q=q(e.command,{cwd:Z.cwd,depth:0}),$=e.command}else{let e=process.env.CLAUDE_PROJECT_DIR??Z.cwd;Q=ht(Z.tool_name,Z.tool_input,{cwd:e});let t=String(Z.tool_input.file_path??Z.tool_input.path??``);$=`${Z.tool_name} ${t}`}He({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:$,decision:Q.action,rule:Q.rule,reason:Q.reason}),Q.action===`allow`?B(`allow`):Q.action===`deny`&&B(`deny`,Q.reason);export{};", "bst-hook.mjs"), writeIfChanged(ctx, GUIDE_PATH, "# bst-hook Permission Guide\n\nThis project uses a PreToolUse hook (`bst-hook`) that evaluates Bash commands\nand file tool operations before execution. Commands fall into three tiers:\n\n- **Denied** — blocked with a redirect message\n- **Allowed** — auto-approved, no prompt\n- **Passthrough** — falls through to Claude Code's normal permission prompt\n\n## Denied commands\n\nThese are blocked. The hook tells you what to do instead.\n\n### Bash\n\n- **rm-recursive**: Don't use recursive `rm`. Use more targeted deletes, or remove specific files/directories.\n- **sudo**: Don't use sudo. If you need elevated permissions, ask me to do it manually.\n- **git-force-push**: Use `git push --force-with-lease` instead of `--force`.\n- **git-reset-hard**: Don't `reset --hard` on main. Create a branch first.\n- **git-write**: Git write operations (push, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.\n- **chmod-777**: Don't use 777 permissions. Use more restrictive permissions.\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **npx-redirect**: This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\n\n### File tools (Read, Write, Edit, Glob, Grep)\n\n- **env-file-read**: Don't read .env files. These contain secrets that should not appear in the conversation.\n- **env-file-write**: Don't write to .env files. These contain secrets.\n- **outside-cwd-write**: Don't write files outside the project directory.\n\n## Auto-approved commands\n\nThese run without prompting.\n\n### Bash\n\n- **Test runners**: vitest, jest\n- **Type checking**: tsc\n- **Build tools**: tsdown\n- **Linters & formatters**: oxlint, oxfmt, eslint, prettier, biome, knip\n- **Read-only commands**: cat, head, tail, grep, ls, wc, uniq, diff, echo, pwd, which, dirname, basename, realpath\n- **Package managers (read-only)**: npm (view, info, show only)\n- **Shell builtins**: test, bracket-test\n- **Navigation**: cd, true, colon\n- **Conditional commands**: find (without -exec, -delete), sort (without -o / --output)\n- **Git read operations**: git (status, log, diff, show, blame, branch, remote, fetch, rev-parse, etc.)\n- **Git safe writes**: git-safe-write (add, switch -c, checkout -b, stash push/pop/apply/show)\n- **pnpm (safe built-ins)**: pnpm (list, why, view, info, show, outdated, install (bare only))\n- **File operations (within project)**: cp (paths must be within project directory), mkdir (paths must be within project directory), touch (paths must be within project directory)\n\n### File tools\n\n- **Read/Glob/Grep**: Allowed within project directory\n- **Write/Edit**: Allowed within project directory\n\n## Passthrough\n\nEverything not listed above triggers Claude Code's normal permission prompt.\nCommon examples:\n\n- `pnpm add <pkg>`, `pnpm dlx <pkg>` (dependency changes)\n- `mv` (file moves)\n- `node`, `npx` scripts\n- Commands with output redirection (`> file`)\n- Any unrecognized command\n\n## How commands are evaluated\n\n- **Pipelines and chains**: every segment must be allowed; any deny blocks the whole command.\n- **pnpm scripts**: `pnpm <script>` resolves against `package.json` (including pre/post hooks and `--filter` workspaces) and evaluates the resolved command recursively.\n- **cd propagation**: `cd dir && cmd` evaluates `cmd` relative to the new directory.\n- **Audit log**: all decisions are logged to `.claude/hooks/bst-hook.audit.local.jsonl`.\n", "bst-hook-guide.md")];
2477
2671
  }
2478
2672
  //#endregion
2479
2673
  //#region src/generators/claude-settings.ts
@@ -2742,175 +2936,6 @@ async function generateChangesets(ctx) {
2742
2936
  };
2743
2937
  }
2744
2938
  //#endregion
2745
- //#region src/generators/release-ci.ts
2746
- /** Common setup steps shared by release-it and simple strategies. */
2747
- function commonSteps(nodeVersionYaml, publishesNpm) {
2748
- const isNodeVersionFile = nodeVersionYaml.startsWith("node-version-file");
2749
- return [
2750
- { step: {
2751
- uses: "actions/checkout@v6",
2752
- with: { "fetch-depth": 0 }
2753
- } },
2754
- { step: { uses: "pnpm/action-setup@v5" } },
2755
- { step: {
2756
- uses: "actions/setup-node@v6",
2757
- with: {
2758
- ...isNodeVersionFile ? { "node-version-file": "package.json" } : { "node-version": "24" },
2759
- cache: "pnpm",
2760
- ...publishesNpm && { "registry-url": actionsExpr("vars.NPM_REGISTRY_URL || 'https://registry.npmjs.org'") }
2761
- }
2762
- } },
2763
- { step: { run: "pnpm install --frozen-lockfile" } }
2764
- ];
2765
- }
2766
- function releaseItSteps(ci, nodeVersionYaml, publishesNpm) {
2767
- const tokenEnv = ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : { RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN") };
2768
- const npmEnv = publishesNpm ? { NODE_AUTH_TOKEN: actionsExpr("secrets.NPM_TOKEN") } : {};
2769
- return [...commonSteps(nodeVersionYaml, publishesNpm), { step: {
2770
- run: "pnpm release-it --ci",
2771
- env: {
2772
- ...tokenEnv,
2773
- ...npmEnv,
2774
- RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2775
- }
2776
- } }];
2777
- }
2778
- /** Build the workflow_dispatch trigger with optional inputs for the simple strategy. */
2779
- function simpleWorkflowDispatchTrigger() {
2780
- return { workflow_dispatch: { inputs: {
2781
- bump: {
2782
- description: "Version bump type (default: conventional-commits auto-detect)",
2783
- required: false,
2784
- type: "choice",
2785
- default: "auto",
2786
- options: [
2787
- "auto",
2788
- "major",
2789
- "minor",
2790
- "patch",
2791
- "first-release"
2792
- ]
2793
- },
2794
- prerelease: {
2795
- description: "Create a prerelease with the given tag (e.g., beta, alpha)",
2796
- required: false,
2797
- type: "string"
2798
- }
2799
- } } };
2800
- }
2801
- /** Build the release:simple run command with conditional flags from workflow inputs. */
2802
- function simpleReleaseCommand() {
2803
- return [
2804
- "FLAGS=",
2805
- `case "${actionsExpr("inputs.bump")}" in`,
2806
- " major|minor|patch) FLAGS=\"$FLAGS --release-as " + actionsExpr("inputs.bump") + "\" ;;",
2807
- " first-release) FLAGS=\"$FLAGS --first-release\" ;;",
2808
- "esac",
2809
- `if [ -n "${actionsExpr("inputs.prerelease")}" ]; then FLAGS="$FLAGS --prerelease ${actionsExpr("inputs.prerelease")}"; fi`,
2810
- "pnpm exec bst release:simple $FLAGS"
2811
- ].join("\n");
2812
- }
2813
- /** Build the docker publish env block, omitting registry vars when derivable at runtime. */
2814
- function dockerPublishEnv(registryDerivable) {
2815
- return {
2816
- ...registryDerivable ? {} : {
2817
- DOCKER_REGISTRY_HOST: actionsExpr("vars.DOCKER_REGISTRY_HOST"),
2818
- DOCKER_REGISTRY_NAMESPACE: actionsExpr("vars.DOCKER_REGISTRY_NAMESPACE")
2819
- },
2820
- DOCKER_USERNAME: actionsExpr("secrets.DOCKER_USERNAME"),
2821
- DOCKER_PASSWORD: actionsExpr("secrets.DOCKER_PASSWORD"),
2822
- RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2823
- };
2824
- }
2825
- function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable) {
2826
- const releaseStep = { step: {
2827
- name: "Release",
2828
- env: {
2829
- ...ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : {
2830
- FORGEJO_SERVER_URL: actionsExpr("github.server_url"),
2831
- FORGEJO_REPOSITORY: actionsExpr("github.repository"),
2832
- RELEASE_TOKEN: actionsExpr("secrets.RELEASE_TOKEN")
2833
- },
2834
- RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2835
- },
2836
- run: simpleReleaseCommand()
2837
- } };
2838
- const dockerStep = { step: {
2839
- name: "Publish Docker images",
2840
- if: "success()",
2841
- env: dockerPublishEnv(registryDerivable),
2842
- run: "pnpm exec bst docker:publish"
2843
- } };
2844
- return [
2845
- ...commonSteps(nodeVersionYaml, publishesNpm),
2846
- releaseStep,
2847
- ...hasDocker ? [dockerStep] : []
2848
- ];
2849
- }
2850
- function buildSteps(strategy, ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable) {
2851
- switch (strategy) {
2852
- case "release-it": return releaseItSteps(ci, nodeVersionYaml, publishesNpm);
2853
- case "simple": return simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable);
2854
- default: return null;
2855
- }
2856
- }
2857
- async function generateReleaseCi(ctx) {
2858
- const filePath = "release-ci";
2859
- if (ctx.config.releaseStrategy === "none" || ctx.config.ci === "none") return {
2860
- filePath,
2861
- action: "skipped",
2862
- description: "Release CI workflow not applicable"
2863
- };
2864
- const publishesNpm = ctx.config.publishNpm === true;
2865
- if (ctx.config.releaseStrategy === "changesets") return {
2866
- filePath,
2867
- action: "skipped",
2868
- description: "Release step included in CI workflow"
2869
- };
2870
- const isGitHub = ctx.config.ci === "github";
2871
- const workflowPath = isGitHub ? ".github/workflows/release.yml" : ".forgejo/workflows/release.yml";
2872
- const nodeVersionYaml = computeNodeVersionYaml(ctx);
2873
- const hasDocker = hasDockerPackages(ctx);
2874
- const registryDerivable = hasRepositoryUrl(ctx);
2875
- const steps = buildSteps(ctx.config.releaseStrategy, ctx.config.ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable);
2876
- if (!steps) return {
2877
- filePath,
2878
- action: "skipped",
2879
- description: "Release CI workflow not applicable"
2880
- };
2881
- const on = ctx.config.releaseStrategy === "simple" ? simpleWorkflowDispatchTrigger() : { workflow_dispatch: null };
2882
- const content = buildWorkflowYaml({
2883
- ci: ctx.config.ci,
2884
- name: "Release",
2885
- on,
2886
- ...isGitHub && { permissions: { contents: "write" } },
2887
- jobName: "release",
2888
- steps
2889
- });
2890
- const alreadyExists = ctx.exists(workflowPath);
2891
- const existing = alreadyExists ? ctx.read(workflowPath) : void 0;
2892
- if (existing) {
2893
- if (isToolingIgnored(existing)) return {
2894
- filePath: workflowPath,
2895
- action: "skipped",
2896
- description: "Release workflow has ignore comment"
2897
- };
2898
- if (normalizeWorkflow(existing) === normalizeWorkflow(content)) return {
2899
- filePath: workflowPath,
2900
- action: "skipped",
2901
- description: "Release workflow already up to date"
2902
- };
2903
- }
2904
- const withDevPath = preserveDevBinaryPath(content, existing);
2905
- const final = existing ? insertCustomBlocks(withDevPath, extractCustomBlocks(existing)) : withDevPath;
2906
- ctx.write(workflowPath, final);
2907
- return {
2908
- filePath: workflowPath,
2909
- action: alreadyExists ? "updated" : "created",
2910
- description: `Generated ${isGitHub ? "GitHub" : "Forgejo"} Actions release workflow`
2911
- };
2912
- }
2913
- //#endregion
2914
2939
  //#region src/generators/lefthook.ts
2915
2940
  function requiredCommands(formatter) {
2916
2941
  return {
@@ -3268,7 +3293,7 @@ async function runGenerators(ctx) {
3268
3293
  function generateMigratePrompt(results, config, detected) {
3269
3294
  const sections = [];
3270
3295
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3271
- sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.41.0 repo:sync\` on ${timestamp}_\n`);
3296
+ sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.42.0 repo:sync\` on ${timestamp}_\n`);
3272
3297
  const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
3273
3298
  if (changed.length > 0) {
3274
3299
  const items = changed.map((r) => {
@@ -5506,37 +5531,36 @@ const TOKEN_SCOPES = [
5506
5531
  "write:package",
5507
5532
  "write:repository"
5508
5533
  ];
5534
+ function createForgejoSecretsProvider(executor, conn) {
5535
+ return { async setSecret(_repo, name, value) {
5536
+ await setActionSecret(executor, conn, name, value);
5537
+ } };
5538
+ }
5539
+ function createGitHubSecretsProvider(executor) {
5540
+ return { async setSecret(repo, name, value) {
5541
+ const result = executor.exec("gh", [
5542
+ "secret",
5543
+ "set",
5544
+ name,
5545
+ "--body",
5546
+ value,
5547
+ "-R",
5548
+ repo
5549
+ ]);
5550
+ if (result.exitCode !== 0) throw new FatalError(`Failed to set GitHub secret ${name}: ${result.stderr}`);
5551
+ } };
5552
+ }
5509
5553
  /**
5510
5554
  * Core logic for setup:secrets, separated from the command handler for testability.
5555
+ * Platform-agnostic — uses the injected SecretsProvider to set secrets.
5511
5556
  */
5512
- async function runSetupSecrets(executor, config) {
5557
+ async function runSetupSecrets(executor, provider, config) {
5513
5558
  const repoName = config.repository.split("/")[1] ?? config.repository;
5514
- const tokenName = `bst-release-${repoName}`;
5515
5559
  const secretsSet = [];
5516
- let token;
5517
- if (config.dryRun) {
5518
- log.info(`[dry-run] Would create access token "${tokenName}" for ${config.username}`);
5519
- log.info(`[dry-run] Scopes: ${TOKEN_SCOPES.join(", ")}`);
5520
- token = "dry-run-token";
5521
- } else {
5522
- log.info(`Creating access token "${tokenName}"...`);
5523
- try {
5524
- token = await createAccessToken(executor, config.serverUrl, config.username, config.password, tokenName, TOKEN_SCOPES);
5525
- log.success(`Created access token "${tokenName}"`);
5526
- } catch (error) {
5527
- if (error instanceof Error && error.message.includes("422")) throw new FatalError(`Access token "${tokenName}" already exists. Delete it in Forgejo settings and re-run, or set secrets manually.`);
5528
- throw error;
5529
- }
5530
- }
5531
- const conn = {
5532
- serverUrl: config.serverUrl,
5533
- repository: config.repository,
5534
- token
5535
- };
5536
5560
  if (config.dryRun) log.info(`[dry-run] Would set secret RELEASE_TOKEN on ${config.repository}`);
5537
5561
  else {
5538
5562
  log.info("Setting RELEASE_TOKEN secret...");
5539
- await setActionSecret(executor, conn, "RELEASE_TOKEN", token);
5563
+ await provider.setSecret(config.repository, "RELEASE_TOKEN", config.releaseToken);
5540
5564
  log.success("Set RELEASE_TOKEN");
5541
5565
  }
5542
5566
  secretsSet.push("RELEASE_TOKEN");
@@ -5546,27 +5570,105 @@ async function runSetupSecrets(executor, config) {
5546
5570
  const dockerPackages = detectDockerPackages(executor, config.cwd, rootName);
5547
5571
  if (dockerPackages.length > 0) {
5548
5572
  log.info(`Detected ${String(dockerPackages.length)} Docker package(s) — setting Docker secrets`);
5573
+ const dockerUser = config.dockerCredentials?.username ?? repoName;
5574
+ const dockerPass = config.dockerCredentials?.password ?? config.releaseToken;
5575
+ if (!config.dockerCredentials) log.info(`Using defaults for Docker secrets: username="${dockerUser}", password=<release token>`);
5549
5576
  if (config.dryRun) {
5550
5577
  log.info(`[dry-run] Would set secret DOCKER_USERNAME on ${config.repository}`);
5551
5578
  log.info(`[dry-run] Would set secret DOCKER_PASSWORD on ${config.repository}`);
5552
5579
  } else {
5553
- await setActionSecret(executor, conn, "DOCKER_USERNAME", config.username);
5580
+ await provider.setSecret(config.repository, "DOCKER_USERNAME", dockerUser);
5554
5581
  log.success("Set DOCKER_USERNAME");
5555
- await setActionSecret(executor, conn, "DOCKER_PASSWORD", token);
5582
+ await provider.setSecret(config.repository, "DOCKER_PASSWORD", dockerPass);
5556
5583
  log.success("Set DOCKER_PASSWORD");
5557
5584
  }
5558
5585
  secretsSet.push("DOCKER_USERNAME", "DOCKER_PASSWORD");
5559
5586
  }
5560
5587
  }
5588
+ return { secretsSet };
5589
+ }
5590
+ /**
5591
+ * Create a Forgejo access token with user-friendly error wrapping.
5592
+ * Exported for testability — called by the command handler's Forgejo path.
5593
+ */
5594
+ async function createForgejoReleaseToken(executor, config) {
5595
+ const tokenName = `bst-release-${config.repository.split("/")[1] ?? config.repository}`;
5596
+ if (config.dryRun) {
5597
+ log.info(`[dry-run] Would create access token "${tokenName}" for ${config.username}`);
5598
+ log.info(`[dry-run] Scopes: ${TOKEN_SCOPES.join(", ")}`);
5599
+ return {
5600
+ token: "dry-run-token",
5601
+ tokenName
5602
+ };
5603
+ }
5604
+ log.info(`Creating access token "${tokenName}"...`);
5605
+ try {
5606
+ const token = await createAccessToken(executor, config.serverUrl, config.username, config.password, tokenName, TOKEN_SCOPES);
5607
+ log.success(`Created access token "${tokenName}"`);
5608
+ return {
5609
+ token,
5610
+ tokenName
5611
+ };
5612
+ } catch (error) {
5613
+ if (error instanceof Error && error.message.includes("422")) throw new FatalError(`Access token "${tokenName}" already exists. Delete it in Forgejo settings and re-run, or set secrets manually.`);
5614
+ throw error;
5615
+ }
5616
+ }
5617
+ async function setupForgejo(executor, serverUrl, repository, dryRun) {
5618
+ const username = await p.text({
5619
+ message: "Forgejo username",
5620
+ validate: (v) => !v || v.length === 0 ? "Username is required" : void 0
5621
+ });
5622
+ if (isCancelled(username)) {
5623
+ p.cancel("Cancelled.");
5624
+ process.exit(0);
5625
+ }
5626
+ const password = await p.password({
5627
+ message: "Forgejo password",
5628
+ validate: (v) => !v || v.length === 0 ? "Password is required" : void 0
5629
+ });
5630
+ if (isCancelled(password)) {
5631
+ p.cancel("Cancelled.");
5632
+ process.exit(0);
5633
+ }
5634
+ const { token } = await createForgejoReleaseToken(executor, {
5635
+ serverUrl,
5636
+ repository,
5637
+ username,
5638
+ password,
5639
+ dryRun
5640
+ });
5641
+ return {
5642
+ provider: createForgejoSecretsProvider(executor, {
5643
+ serverUrl,
5644
+ repository,
5645
+ token
5646
+ }),
5647
+ releaseToken: token,
5648
+ dockerCredentials: {
5649
+ username,
5650
+ password: token
5651
+ }
5652
+ };
5653
+ }
5654
+ async function setupGitHub(executor) {
5655
+ const token = await p.password({
5656
+ message: "GitHub Personal Access Token (PAT)",
5657
+ validate: (v) => !v || v.length === 0 ? "Token is required" : void 0
5658
+ });
5659
+ if (isCancelled(token)) {
5660
+ p.cancel("Cancelled.");
5661
+ process.exit(0);
5662
+ }
5561
5663
  return {
5562
- tokenCreated: !config.dryRun,
5563
- secretsSet
5664
+ provider: createGitHubSecretsProvider(executor),
5665
+ releaseToken: token
5564
5666
  };
5565
5667
  }
5566
5668
  const setupSecretsCommand = defineCommand({
5567
5669
  meta: {
5568
5670
  name: "setup:secrets",
5569
- description: "Create a Forgejo access token and configure CI secrets for this repository"
5671
+ description: "Configure CI secrets for this repository (Forgejo or GitHub)"
5570
5672
  },
5571
5673
  args: {
5572
5674
  "dry-run": {
@@ -5576,49 +5678,41 @@ const setupSecretsCommand = defineCommand({
5576
5678
  "no-docker": {
5577
5679
  type: "boolean",
5578
5680
  description: "Skip Docker secrets setup even if Docker packages are detected"
5579
- },
5580
- verbose: {
5581
- type: "boolean",
5582
- description: "Enable verbose output"
5583
5681
  }
5584
5682
  },
5585
5683
  async run({ args }) {
5586
5684
  const cwd = process.cwd();
5587
- const verbose = args.verbose === true || isEnvVerbose();
5588
5685
  const dryRun = args["dry-run"] === true;
5589
5686
  const noDocker = args["no-docker"] === true;
5590
5687
  p.intro("setup:secrets");
5591
5688
  const parsed = parseRepositoryUrl(cwd);
5592
- if (!parsed) throw new FatalError("No repository URL found in package.json. Add a `repository` field pointing to your Forgejo instance.");
5593
- if (parsed.hostname === "github.com") throw new FatalError("setup:secrets is for Forgejo repositories. GitHub uses different auth.");
5594
- const serverUrl = `${parsed.protocol}//${parsed.hostname}`;
5689
+ if (!parsed) throw new FatalError("No repository URL found in package.json. Add a `repository` field.");
5595
5690
  const repository = parsed.repository;
5596
- log.info(`Forgejo: ${serverUrl} / ${repository}`);
5597
- const username = await p.text({
5598
- message: "Forgejo username",
5599
- validate: (v) => !v || v.length === 0 ? "Username is required" : void 0
5600
- });
5601
- if (isCancelled(username)) {
5602
- p.cancel("Cancelled.");
5603
- process.exit(0);
5604
- }
5605
- const password = await p.password({
5606
- message: "Forgejo password",
5607
- validate: (v) => !v || v.length === 0 ? "Password is required" : void 0
5608
- });
5609
- if (isCancelled(password)) {
5610
- p.cancel("Cancelled.");
5611
- process.exit(0);
5691
+ const isGitHub = parsed.hostname === "github.com";
5692
+ const executor = createRealExecutor();
5693
+ let provider;
5694
+ let releaseToken;
5695
+ let dockerCredentials;
5696
+ if (isGitHub) {
5697
+ log.info(`GitHub: ${repository}`);
5698
+ const setup = await setupGitHub(executor);
5699
+ provider = setup.provider;
5700
+ releaseToken = setup.releaseToken;
5701
+ } else {
5702
+ const serverUrl = `${parsed.protocol}//${parsed.hostname}`;
5703
+ log.info(`Forgejo: ${serverUrl} / ${repository}`);
5704
+ const setup = await setupForgejo(executor, serverUrl, repository, dryRun);
5705
+ provider = setup.provider;
5706
+ releaseToken = setup.releaseToken;
5707
+ dockerCredentials = setup.dockerCredentials;
5612
5708
  }
5613
- const summary = (await runSetupSecrets(createRealExecutor(), {
5614
- serverUrl,
5709
+ const summary = (await runSetupSecrets(executor, provider, {
5615
5710
  repository,
5616
- username,
5617
- password,
5711
+ releaseToken,
5618
5712
  cwd,
5619
5713
  dryRun,
5620
5714
  noDocker,
5621
- verbose
5715
+ dockerCredentials
5622
5716
  })).secretsSet.map((s) => ` - ${s}`).join("\n");
5623
5717
  p.outro(`Done! Secrets ${dryRun ? "that would be" : ""} configured:\n${summary}`);
5624
5718
  }
@@ -5756,7 +5850,7 @@ const forgejoSecretsCommand = defineCommand({
5756
5850
  const main = defineCommand({
5757
5851
  meta: {
5758
5852
  name: "bst",
5759
- version: "0.41.0",
5853
+ version: "0.42.0",
5760
5854
  description: "Bootstrap and maintain standardized TypeScript project tooling"
5761
5855
  },
5762
5856
  subCommands: {
@@ -5775,7 +5869,7 @@ const main = defineCommand({
5775
5869
  "forgejo:secrets": forgejoSecretsCommand
5776
5870
  }
5777
5871
  });
5778
- console.log(`@bensandee/tooling v0.41.0`);
5872
+ console.log(`@bensandee/tooling v0.42.0`);
5779
5873
  async function run() {
5780
5874
  await runMain(main);
5781
5875
  process.exit(process.exitCode ?? 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bensandee/tooling",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,8 +40,8 @@
40
40
  "citty": "^0.2.1",
41
41
  "json5": "^2.2.3",
42
42
  "jsonc-parser": "^3.3.1",
43
- "picomatch": "^4.0.3",
44
- "yaml": "^2.8.2",
43
+ "picomatch": "^4.0.4",
44
+ "yaml": "^2.8.3",
45
45
  "zod": "^4.3.6",
46
46
  "@bensandee/common": "0.1.2"
47
47
  },
@@ -53,9 +53,9 @@
53
53
  "commit-and-tag-version": "12.7.1",
54
54
  "prettier": "3.8.1",
55
55
  "release-it": "19.2.4",
56
- "tsdown": "0.21.4",
57
- "@bensandee/config": "0.10.0",
58
- "@bensandee/claude-hooks": "0.5.0"
56
+ "tsdown": "0.21.6",
57
+ "@bensandee/config": "0.10.1",
58
+ "@bensandee/claude-hooks": "0.6.0"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsdown",
@@ -168,6 +168,10 @@
168
168
  "registryNamespace": {
169
169
  "description": "Docker registry namespace (e.g. ghcr.io/myorg, code.example.com/myorg)",
170
170
  "type": "string"
171
+ },
172
+ "postReleaseHook": {
173
+ "description": "Override the post-release hook command (default: curl POST_RELEASE_HOOK_URL secret if set)",
174
+ "type": "string"
171
175
  }
172
176
  },
173
177
  "additionalProperties": false