@bensandee/tooling 0.47.0 → 0.48.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.
Files changed (2) hide show
  1. package/dist/bin.mjs +67 -30
  2. package/package.json +3 -3
package/dist/bin.mjs CHANGED
@@ -1585,7 +1585,7 @@ async function generatePackageJson(ctx) {
1585
1585
  const devDeps = { ...ROOT_DEV_DEPS };
1586
1586
  if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
1587
1587
  devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.10.1";
1588
- devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.47.0";
1588
+ devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.48.0";
1589
1589
  if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.12.0";
1590
1590
  if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
1591
1591
  "@changesets/cli": "2.30.0",
@@ -2203,11 +2203,7 @@ async function generateGitignore(ctx) {
2203
2203
  }
2204
2204
  //#endregion
2205
2205
  //#region src/generators/release-ci.ts
2206
- const DEFAULT_POST_RELEASE_COMMAND = [
2207
- "if [ -n \"$POST_RELEASE_HOOK_URL\" ]; then",
2208
- " pnpm exec bst webhook:send",
2209
- " \"$POST_RELEASE_HOOK_URL\"; fi"
2210
- ].join("");
2206
+ const DEFAULT_POST_RELEASE_COMMAND = ["pnpm exec bst webhook:send", " \"$POST_RELEASE_HOOK_URL\""].join("");
2211
2207
  /** Build env vars for hook secrets to inject into a workflow step. */
2212
2208
  function hookSecretEnv(secrets) {
2213
2209
  const env = {
@@ -2282,17 +2278,12 @@ function simpleWorkflowDispatchTrigger() {
2282
2278
  }
2283
2279
  } } };
2284
2280
  }
2285
- /** Build the release:simple run command with conditional flags from workflow inputs. */
2286
- function simpleReleaseCommand() {
2287
- return [
2288
- "FLAGS=",
2289
- `case "${actionsExpr("inputs.bump")}" in`,
2290
- " major|minor|patch) FLAGS=\"$FLAGS --release-as " + actionsExpr("inputs.bump") + "\" ;;",
2291
- " first-release) FLAGS=\"$FLAGS --first-release\" ;;",
2292
- "esac",
2293
- `if [ -n "${actionsExpr("inputs.prerelease")}" ]; then FLAGS="$FLAGS --prerelease ${actionsExpr("inputs.prerelease")}"; fi`,
2294
- "pnpm exec bst release:simple $FLAGS"
2295
- ].join("\n");
2281
+ /** Env vars that forward workflow dispatch inputs to the release:simple CLI. */
2282
+ function simpleReleaseEnv() {
2283
+ return {
2284
+ RELEASE_BUMP: actionsExpr("inputs.bump"),
2285
+ RELEASE_PRERELEASE: actionsExpr("inputs.prerelease")
2286
+ };
2296
2287
  }
2297
2288
  function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, registryDerivable) {
2298
2289
  const releaseEnv = ci === "github" ? { GITHUB_TOKEN: actionsExpr("github.token") } : {
@@ -2306,9 +2297,10 @@ function simpleReleaseSteps(ci, nodeVersionYaml, publishesNpm, hasDocker, regist
2306
2297
  name: "Release",
2307
2298
  env: {
2308
2299
  ...releaseEnv,
2300
+ ...simpleReleaseEnv(),
2309
2301
  RELEASE_DEBUG: actionsExpr("vars.RELEASE_DEBUG || 'false'")
2310
2302
  },
2311
- run: simpleReleaseCommand()
2303
+ run: "pnpm exec bst release:simple"
2312
2304
  } };
2313
2305
  const dockerStep = { step: {
2314
2306
  name: "Publish Docker images",
@@ -2415,7 +2407,6 @@ function changesetsReleaseStep(ci, publishesNpm) {
2415
2407
  return { step: {
2416
2408
  id: "release",
2417
2409
  name: "Release",
2418
- if: "github.ref == 'refs/heads/main'",
2419
2410
  env: {
2420
2411
  FORGEJO_SERVER_URL: actionsExpr("github.server_url"),
2421
2412
  FORGEJO_REPOSITORY: actionsExpr("github.repository"),
@@ -2734,7 +2725,7 @@ function writeIfChanged(ctx, filePath, content, label) {
2734
2725
  };
2735
2726
  }
2736
2727
  async function generateClaudeHooks(ctx) {
2737
- return [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.6.4\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync — do not edit manually.\n */\nimport*as e from\"node:fs\";import{existsSync as t,readFileSync as n,readdirSync as r,statSync as i}from\"node:fs\";import*as a from\"node:path\";import{dirname as o,join as s,resolve as c}from\"node:path\";import{fileURLToPath as l}from\"node:url\";import{execFileSync as u}from\"node:child_process\";Object.freeze({status:`aborted`});function d(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 f=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const ee={};function p(e){return e&&Object.assign(ee,e),ee}function te(e,t){return typeof t==`bigint`?t.toString():t}function ne(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function re(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const ie=Symbol(`evaluating`);function m(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==ie)return r===void 0&&(r=ie,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const ae=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function h(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oe(e){if(h(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(h(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function se(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function g(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 ce(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function _(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 v(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function y(e){return typeof e==`string`?e:e?.message}function b(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=y(e.inst?._zod.def?.error?.(e))??y(t?.error?.(e))??y(n.customError?.(e))??y(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const le=(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,te,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},ue=d(`$ZodError`,le),x=d(`$ZodError`,le,{Parent:Error}),de=(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 f;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>b(e,a,p())));throw ae(t,i?.callee),t}return o.value})(x),fe=(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=>b(e,a,p())));throw ae(t,i?.callee),t}return o.value})(x),S=(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 f;return a.issues.length?{success:!1,error:new(e??ue)(a.issues.map(e=>b(e,i,p())))}:{success:!0,data:a.value}})(x),C=(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=>b(e,i,p())))}:{success:!0,data:a.value}})(x),pe=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},me=/^-?\\d+(?:\\.\\d+)?$/,he={major:4,minor:3,patch:6},w=d(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=he;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=_(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 f;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=_(e,t))});else{if(e.issues.length===t)continue;r||=_(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(_(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new f;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 f;return o.then(e=>t(e,r,a))}return t(o,r,a)}}m(e,`~standard`,()=>({validate:t=>{try{let n=S(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return C(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),ge=d(`$ZodString`,(e,t)=>{w.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??pe(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}}),_e=d(`$ZodUnknown`,(e,t)=>{w.init(e,t),e._zod.parse=e=>e});function ve(e,t,n){e.issues.length&&t.issues.push(...v(n,e.issues)),t.value[n]=e.value}const ye=d(`$ZodArray`,(e,t)=>{w.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;e<i.length;e++){let o=i[e],s=t.element._zod.run({value:o,issues:[]},r);s instanceof Promise?a.push(s.then(t=>ve(t,n,e))):ve(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function T(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...v(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function be(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=ce(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function xe(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=>T(e,n,i,t,u))):T(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 Se=d(`$ZodObject`,(e,t)=>{if(w.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=ne(()=>be(t));m(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=h,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=>T(n,t,e,s,r))):T(i,t,e,s,r)}return i?xe(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),Ce=d(`$ZodRecord`,(e,t)=>{w.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!oe(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(...v(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...v(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`&&me.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=>b(e,r,p())),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(...v(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...v(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function E(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const we=d(`$ZodOptional`,(e,t)=>{w.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,m(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),m(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${re(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=>E(t,e.value)):E(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var D,Te=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 Ee(){return new Te}(D=globalThis).__zod_globalRegistry??(D.__zod_globalRegistry=Ee()),globalThis.__zod_globalRegistry;function De(e,t){return new e({type:`string`,...g(t)})}function Oe(e){return new e({type:`unknown`})}const O=d(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);w.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>de(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>S(e,t,n),e.parseAsync=async(t,n)=>fe(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>C(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)=>se(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),ke=d(`ZodMiniString`,(e,t)=>{ge.init(e,t),O.init(e,t)});function k(e){return De(ke,e)}const Ae=d(`ZodMiniUnknown`,(e,t)=>{_e.init(e,t),O.init(e,t)});function je(){return Oe(Ae)}const Me=d(`ZodMiniArray`,(e,t)=>{ye.init(e,t),O.init(e,t)});function Ne(e,t){return new Me({type:`array`,element:e,...g(t)})}const Pe=d(`ZodMiniObject`,(e,t)=>{Se.init(e,t),O.init(e,t),m(e,`shape`,()=>t.shape)});function A(e,t){return new Pe({type:`object`,shape:e??{},...g(t)})}const Fe=d(`ZodMiniRecord`,(e,t)=>{Ce.init(e,t),O.init(e,t)});function j(e,t,n){return new Fe({type:`record`,keyType:e,valueType:t,...g(n)})}const Ie=d(`ZodMiniOptional`,(e,t)=>{we.init(e,t),O.init(e,t)});function M(e){return new Ie({type:`optional`,innerType:e})}const Le=A({session_id:k(),transcript_path:k(),cwd:k(),permission_mode:k(),hook_event_name:k(),tool_name:k(),tool_input:j(k(),je())});async function Re(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t;try{t=JSON.parse(e.join(``))}catch{return{ok:!1,raw:e.join(``).slice(0,200),error:`invalid JSON`}}let n=Le.safeParse(t);return n.success?{ok:!0,data:n.data}:{ok:!1,raw:t,error:n.error.message}}const ze=A({command:k(),description:M(k())}),Be=A({file_path:k()}),Ve=A({pattern:k(),path:M(k())});function He(e){let t=ze.safeParse(e);return t.success?t.data:null}function Ue(e){let t=Be.safeParse(e);return t.success?t.data:null}function We(e){let t=Ve.safeParse(e);return t.success?t.data:null}function N(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function Ge(){let e=a.dirname(l(import.meta.url));return a.join(e,`bst-hook.audit.local.jsonl`)}function Ke(t){try{if(e.statSync(t).size>1048576){let n=`${t}.1`;e.renameSync(t,n)}}catch{}}let qe=!1;function P(t){try{let n=Ge();qe||=(Ke(n),!0);let r={timestamp:new Date().toISOString(),...t};e.appendFileSync(n,JSON.stringify(r)+`\n`)}catch(e){process.stderr.write(`[bst-hook] audit write failed: ${e instanceof Error?e.message:String(e)}\\n`)}}function F(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 Je(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]===`&`){I(t,n,`&&`),n=``,r+=2;continue}if(o===`&`&&!(r+1<e.length&&e[r+1]===`>`)&&!(r>0&&(e[r-1]===`>`||e[r-1]===`<`))){I(t,n,`&`),n=``,r++;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){I(t,n,`||`),n=``,r+=2;continue}if(o===`|`){I(t,n,`|`),n=``,r++;continue}if(o===`;`){I(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function I(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const L=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function Ye(e,t,n=!1){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(`>(`)&&!L.has(t))return{safe:!1,reason:`process substitution`}}if(!n)for(let t=0;t<e.length;t++){let n=e[t];if(!L.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 Xe=[`.sample`,`.example`,`.template`],Ze=/(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;function Qe(e){return Xe.some(t=>e.endsWith(t))}function R(e){return Qe(a.basename(e))?!1:Ze.test(e)}function $e(e){return e.slice(1).some(e=>!e.startsWith(`-`)&&R(e))}const et=new Set([`status`,`log`,`diff`,`show`,`blame`,`grep`,`shortlog`,`range-diff`,`describe`,`branch`,`remote`,`fetch`,`ls-files`,`ls-tree`,`ls-remote`,`show-ref`,`for-each-ref`,`rev-parse`,`rev-list`,`cat-file`,`diff-tree`,`name-rev`,`merge-base`,`check-ignore`]),tt=new Set([`push`,`add`,`commit`,`reset`,`merge`,`rebase`,`cherry-pick`,`checkout`,`switch`,`stash`,`tag`,`revert`,`clean`,`rm`,`mv`]),nt=new Set([`-c`,`--exec-path`,`--pager`,`--upload-pack`,`--receive-pack`]),rt=new Set([`-C`,`-c`,`--git-dir`,`--work-tree`]);function z(e){let t=e.slice(1);for(let e=0;e<t.length;e++){let n=t[e];if(rt.has(n)){e++;continue}if(!n.startsWith(`-`))return n}}function it(e){for(let t of e.slice(1))if(nt.has(t)||t===`-o`||t.startsWith(`--output`)||t.startsWith(`--exec-path=`)||t.startsWith(`--pager=`)||t.startsWith(`--upload-pack=`)||t.startsWith(`--receive-pack=`)||t.startsWith(`-o=`)||t.startsWith(`--output=`))return!1;let t=z(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===`list`||t===void 0}return et.has(t)}function at(e){let t=z(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];if(t===`list`||t===void 0)return!1}return tt.has(t)}const ot=new Set([`push`,`pop`,`apply`,`show`]);function B(e){let t=z(e);if(!t)return!1;if(t===`add`)return!0;if(t===`checkout`)return e.slice(e.indexOf(`checkout`)+1).some(e=>e===`-b`||e===`-B`);if(t===`switch`)return!0;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===void 0?!0:ot.has(t)}return!1}const V=new Set(`install.i.add.remove.rm.uninstall.un.update.up.upgrade.list.ls.ll.la.why.view.info.show.outdated.audit.licenses.init.create.link.unlink.pack.publish.config.set.get.store.cache.rebuild.rb.prune.dedupe.dlx.exec.run.patch.patch-commit.recursive.-r`.split(`.`)),st=new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`]);function ct(e){let t=e.slice(1),n;for(let e of t)if(!(e.startsWith(`-`)&&!V.has(e))){n=e;break}return n?st.has(n)?!0:n===`install`||n===`i`?t.slice(t.indexOf(n)+1).every(e=>e.startsWith(`-`)):!1:!1}const lt=new Set([`view`,`info`,`show`]);function ut(e){let t=e[1];return t!==void 0&&lt.has(t)}function dt(e){let t=e[1],n=`This project uses pnpm.`;return t?t===`install`||t===`i`||t===`ci`?e.slice(2).some(e=>!e.startsWith(`-`))?`${n} Use \\`pnpm add <pkg>\\` instead of \\`npm install <pkg>\\`.`:`${n} Use \\`pnpm install\\` instead of npm.`:t===`add`?`${n} Use \\`pnpm add <pkg>\\` instead of npm.`:t===`uninstall`||t===`remove`||t===`rm`||t===`un`||t===`r`?`${n} Use \\`pnpm remove <pkg>\\` instead of npm.`:t===`run`||t===`run-script`?`${n} Use \\`pnpm run <script>\\` or \\`pnpm <script>\\` instead of npm.`:t===`test`||t===`t`||t===`tst`?`${n} Use \\`pnpm test\\` instead of npm.`:t===`start`?`${n} Use \\`pnpm start\\` instead of npm.`:t===`stop`?`${n} Use \\`pnpm stop\\` instead of npm.`:t===`restart`?`${n} Use \\`pnpm restart\\` instead of npm.`:t===`exec`?`${n} Use \\`pnpm exec <cmd>\\` instead of npm.`:t===`list`||t===`ls`||t===`ll`||t===`la`?`${n} Use \\`pnpm list\\` instead of npm.`:t===`why`?`${n} Use \\`pnpm why\\` instead of npm.`:t===`outdated`?`${n} Use \\`pnpm outdated\\` instead of npm.`:t===`audit`?`${n} Use \\`pnpm audit\\` instead of npm.`:t===`update`||t===`up`||t===`upgrade`?`${n} Use \\`pnpm update\\` instead of npm.`:t===`init`?`${n} Use \\`pnpm init\\` instead of npm.`:t===`publish`?`${n} Use \\`pnpm publish\\` instead of npm.`:t===`link`?`${n} Use \\`pnpm link\\` instead of npm.`:t===`unlink`?`${n} Use \\`pnpm unlink\\` instead of npm.`:t===`pack`?`${n} Use \\`pnpm pack\\` instead of npm.`:t===`cache`?`${n} Use \\`pnpm store\\` instead of \\`npm cache\\`.`:t===`config`||t===`set`||t===`get`?`${n} Use \\`pnpm config\\` instead of npm.`:`${n} Use the equivalent pnpm command instead of npm.`:`${n} Use \\`pnpm\\` instead of npm.`}function H(e,t){for(let n of e.slice(1)){if(n.startsWith(`-`)){if(n===`-t`||n.startsWith(`--target-directory`))return!1;continue}if(a.basename(n).startsWith(`.`)||n.startsWith(`~/`)||n===`~`)return!1;let e=a.resolve(t.cwd,n);if(e!==t.cwd&&!e.startsWith(t.cwd+a.sep))return!1}return!0}const ft=new Set([`-exec`,`-execdir`,`-ok`,`-okdir`,`-delete`,`-fprint`,`-fprint0`,`-fprintf`,`-fls`]);function pt(e){return!e.some(e=>ft.has(e))}function mt(e){return!e.includes(`-o`)&&!e.some(e=>e.startsWith(`--output`))}function ht(e){let t=e.findIndex(e=>a.basename(e)===`rm`);if(t===-1)return!1;let n=e.slice(t+1),r=n.filter(e=>e.startsWith(`-`)&&!e.startsWith(`--`)),i=n.filter(e=>e.startsWith(`--`)),o=r.some(e=>/[rR]/.test(e)),s=i.includes(`--recursive`);return o||s}function gt(e){return e.some(e=>e===`777`||e===`0777`)}function _t(e){let t=e.indexOf(`push`);if(t===-1)return!1;let n=e.slice(t+1);return n.some(e=>e===`--force`||e===`-f`&&!n.includes(`--force-with-lease`))}function vt(e){if(!(e.includes(`reset`)&&e.includes(`--hard`)))return!1;if(e.some(e=>e===`main`||e===`master`))return!0;let t=e.filter(e=>e!==`git`&&e!==`reset`&&!e.startsWith(`-`));return t.length===0||t.includes(`HEAD`)}const yt=[{name:`rm-recursive`,match:/^rm$/,action:`deny`,when:e=>ht(e),reason:\"Don't use recursive `rm`. Use more targeted deletes, or remove specific files/directories.\"},{name:`sudo`,match:/^sudo$/,action:`deny`,reason:`Don't use sudo. If you need elevated permissions, ask me to do it manually.`},{name:`git-force-push`,match:/^git$/,action:`deny`,when:e=>_t(e),reason:\"Use `git push --force-with-lease` instead of `--force`.\"},{name:`git-reset-hard`,match:/^git$/,action:`deny`,when:e=>vt(e),reason:\"Don't `reset --hard` on main. Create a branch first.\"},{name:`git-write`,match:/^git$/,action:`deny`,when:e=>at(e)&&!B(e),reason:`Git write operations (push, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.`},{name:`chmod-777`,match:/^chmod$/,action:`deny`,when:e=>gt(e),reason:`Don't use 777 permissions. Use more restrictive permissions.`},{name:`env-file-read`,match:/^(cat|head|tail|less|more|grep)$/,action:`deny`,when:e=>$e(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`npx-redirect`,match:/^npx$/,action:`deny`,reason:\"This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\"}],U=[{name:`vitest`,match:/^vitest$/,action:`allow`},{name:`jest`,match:/^jest$/,action:`allow`},{name:`tsc`,match:/^tsc$/,action:`allow`},{name:`tsdown`,match:/^tsdown$/,action:`allow`},{name:`oxlint`,match:/^oxlint$/,action:`allow`},{name:`oxfmt`,match:/^oxfmt$/,action:`allow`},{name:`eslint`,match:/^eslint$/,action:`allow`},{name:`prettier`,match:/^prettier$/,action:`allow`},{name:`biome`,match:/^biome$/,action:`allow`},{name:`knip`,match:/^knip$/,action:`allow`},{name:`cat`,match:/^cat$/,action:`allow`},{name:`head`,match:/^head$/,action:`allow`},{name:`tail`,match:/^tail$/,action:`allow`},{name:`grep`,match:/^grep$/,action:`allow`},{name:`ls`,match:/^ls$/,action:`allow`},{name:`wc`,match:/^wc$/,action:`allow`},{name:`uniq`,match:/^uniq$/,action:`allow`},{name:`diff`,match:/^diff$/,action:`allow`},{name:`echo`,match:/^echo$/,action:`allow`},{name:`pwd`,match:/^pwd$/,action:`allow`},{name:`which`,match:/^which$/,action:`allow`},{name:`dirname`,match:/^dirname$/,action:`allow`},{name:`basename`,match:/^basename$/,action:`allow`},{name:`realpath`,match:/^realpath$/,action:`allow`},{name:`npm`,match:/^npm$/,action:`allow`,when:e=>ut(e)},{name:`test`,match:/^test$/,action:`allow`},{name:`bracket-test`,match:/^\\[$/,action:`allow`},{name:`cd`,match:/^cd$/,action:`allow`},{name:`true`,match:/^true$/,action:`allow`},{name:`colon`,match:/^:$/,action:`allow`},{name:`find`,match:/^find$/,action:`allow`,when:e=>pt(e)},{name:`sort`,match:/^sort$/,action:`allow`,when:e=>mt(e)},{name:`git`,match:/^git$/,action:`allow`,when:e=>it(e)},{name:`git-safe-write`,match:/^git$/,action:`allow`,when:e=>B(e)},{name:`pnpm`,match:/^pnpm$/,action:`allow`,when:e=>ct(e)},{name:`cp`,match:/^cp$/,action:`allow`,when:(e,t)=>H(e,t)},{name:`mkdir`,match:/^mkdir$/,action:`allow`,when:(e,t)=>H(e,t)},{name:`touch`,match:/^touch$/,action:`allow`,when:(e,t)=>H(e,t)}],bt=`.claude/hooks/bst-hook.settings.json`,xt=A({allow:Ne(k())});function St(e,t){try{return u(`git`,[`ls-files`,`--error-unmatch`,t],{cwd:e,stdio:`ignore`}),u(`git`,[`diff`,`--quiet`,`HEAD`,`--`,t],{cwd:e,stdio:`ignore`}),!0}catch{return!1}}function Ct(e){let r=Ot(e)??e,i=s(r,bt);if(!t(i)||!St(r,bt))return[];try{let e=n(i,`utf-8`),t=JSON.parse(e),r=xt.safeParse(t);return r.success?r.data.allow.filter(e=>typeof e==`string`&&e.length>0):[]}catch{return[]}}function wt(e,t,n){let r=n.split(/\\s+/).filter(Boolean);if(r.length===0||r[0]!==e)return!1;let i=r.slice(1),a=t.slice(1);for(let e=0;e<i.length;e++)if(e>=a.length||a[e]!==i[e])return!1;return!0}const Tt=A({scripts:j(k(),je())});function W(e,t){let n=Pt(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 Et(e,t,n){let r=At(t,n);if(!r)return null;let i=W(e,r);return i?{...i,pkgDir:r}:null}function Dt(e,t){let n=kt(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 Ot(e){let n=c(e);for(;;){if(t(s(n,`pnpm-workspace.yaml`)))return n;let e=o(n);if(e===n)return null;n=e}}function kt(e){let t=jt(e);if(!t)return null;let n=[];for(let a of t){if(!a.endsWith(`/*`))continue;let t=c(e,a.slice(0,-2)),o;try{o=r(t)}catch{continue}for(let e of o){let r=s(t,e);try{if(!i(r).isDirectory())continue}catch{continue}n.push(r)}}return n.length>0?n:null}function At(e,t){let n=kt(t);if(!n)return null;for(let t of n){let n=Nt(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function jt(e){try{let t=n(s(e,`pnpm-workspace.yaml`),`utf-8`).split(`\n`),r=t.findIndex(e=>/^packages\\s*:/.test(e));if(r===-1)return null;let i=[];for(let e=r+1;e<t.length;e++){let n=t[e],r=/^\\s+-\\s+(.+)$/.exec(n);if(!r)break;i.push(r[1].trim().replace(/['\"`]/g,``))}return i.length>0?i:null}catch{return null}}const Mt=A({name:k()});function Nt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=Mt.safeParse(JSON.parse(t));return r.success?r.data.name:null}catch{return null}}function Pt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=Tt.safeParse(JSON.parse(t));return r.success?r.data.scripts:null}catch{return null}}function Ft(e){return St(e,`package.json`)}const It=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`]),Lt=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function Rt(e){return It.has(e)?!0:Lt.some(t=>e.startsWith(t))}function zt(e,t){let n=F(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 a.isAbsolute(i)?i:a.resolve(t.cwd,i)}function G(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=Je(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=zt(e.command,o);t&&(o={...o,cwd:t})}let t=Bt(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 K(e,t){let n=e.pkgDir??t.cwd,r=t.insideResolvedScript??Ft(n)?{...t,insideResolvedScript:!0}:t;if(e.pre){let t=G(e.pre,r);if(t.action!==`allow`)return t}let i=G(e.main,r);if(i.action!==`allow`)return i;if(e.post){let t=G(e.post,r);if(t.action!==`allow`)return t}return`allow`}function Bt(e,t){let n=e.trim();if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=F(e);if(!r.ok)return{action:`passthrough`,rule:null,reason:r.error};let i=r.tokens;if(i.length===0)return{action:`passthrough`,rule:null,reason:null};let o=0;for(;o<i.length&&/^\\w+=/.test(i[o]);){let e=i[o].split(`=`)[0];if(Rt(e))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${e}`};o++}if(o>=i.length)return{action:`allow`,rule:`env-only`,reason:null};i=i.slice(o);let s=Ye(i,e,t.insideResolvedScript);if(!s.safe)return{action:`passthrough`,rule:null,reason:s.reason};if(!t.insideResolvedScript)for(let e=0;e<i.length;e++){let t=i[e];if(!(t===`2>&1`||t===`2>/dev/null`||t===`>/dev/null`||t===`1>/dev/null`)){if(t===`>`||t===`>>`){if(i[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 c=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);i=i.filter((e,t)=>!(c.has(e)||(e===`>`||e===`>>`)&&i[t+1]===`/dev/null`||e===`/dev/null`&&t>0&&(i[t-1]===`>`||i[t-1]===`>>`)));let l=a.basename(i[0]);if(l===`command`&&i.length>1)return G(i.slice(1).join(` `),{...t,depth:t.depth+1});if(l===`env`){let e=1;for(;e<i.length&&/^\\w+=/.test(i[e]);){let t=i[e].split(`=`)[0];if(Rt(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=i.length?{action:`allow`,rule:`env-only`,reason:null}:G(i.slice(e).join(` `),{...t,depth:t.depth+1})}if(l===`pnpm`)return Vt(i,t);if(l===`npm`)return Ut(i);if(l===`bash`||l===`sh`||l===`zsh`)return Kt(i,t);if(l===`xargs`)return Jt(i,t);if(l===`node`&&(i.includes(`-e`)||i.includes(`-p`)||i.includes(`--eval`)||i.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(l===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let e of yt)if(e.match.test(l)&&(!e.when||e.when(i,t)))return{action:`deny`,rule:e.name,reason:e.reason??null};for(let e of U)if(e.match.test(l)&&(!e.when||e.when(i,t)))return{action:`allow`,rule:e.name,reason:null};if(t.repoAllowPatterns){for(let e of t.repoAllowPatterns)if(wt(l,i,e))return{action:`allow`,rule:`repo:${l}`,reason:null}}return t.insideResolvedScript?{action:`allow`,rule:`resolved-script`,reason:null}:{action:`passthrough`,rule:null,reason:`no rule matched: ${l}`}}function Vt(e,t){let n=e.slice(1),r,i=-1,o=!1,s=!1,c;for(let a=0;a<n.length;a++){let l=n[a];if(l===`--filter`||l.startsWith(`--filter=`))return Wt(e,n,a,t);if(l===`-r`||l===`--recursive`){o=!0;continue}if(l===`-w`||l===`--workspace-root`){s=!0;continue}if(l===`-C`||l===`--dir`){c=n[++a];continue}if(l.startsWith(`-C=`)||l.startsWith(`--dir=`)){c=l.split(`=`,2)[1];continue}if(!(l.startsWith(`-`)&&!V.has(l))){r=l,i=a;break}}if(!r)return{action:`passthrough`,rule:null,reason:null};if(r===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(r===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(r===`exec`){let e=n.slice(i+1);return e.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:G(e.join(` `),{...t,depth:t.depth+1})}let l;if(r===`run`){let e=n[i+1];if(!e||e.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm run without script name`};l=e}else if(!V.has(r))l=r;else{for(let n of U)if(n.match.test(`pnpm`)&&(!n.when||n.when(e,t)))return{action:`allow`,rule:n.name,reason:null};return{action:`passthrough`,rule:null,reason:null}}if(o)return Ht(l,t);if(s){let e=Ot(t.cwd);return e?q(l,{...t,cwd:e}):{action:`passthrough`,rule:null,reason:`workspace root not found`}}if(c){let e=a.resolve(t.cwd,c);return q(l,{...t,cwd:e})}return q(l,t)}function q(e,t){let n=W(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r=K(n,{...t,depth:t.depth+1});return r===`allow`?{action:`allow`,rule:`pnpm-script:${e}`,reason:null}:r}function Ht(e,t){let n=Dt(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n=K(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 Ut(e){for(let t of U)if(t.match.test(`npm`)&&(!t.when||t.when(e,{cwd:``,depth:0})))return{action:`allow`,rule:t.name,reason:null};return{action:`deny`,rule:`npm-redirect`,reason:dt(e)}}function Wt(e,t,n,r){let i=t[n],a,o;if(i.startsWith(`--filter=`))a=i.slice(9),o=n+1;else{if(a=t[n+1],!a||a.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};o=n+2}if(!a)return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};let s;for(let e=o;e<t.length;e++){let n=t[e];if(!(n.startsWith(`-`)&&!V.has(n))){s=n;break}}if(!s)return{action:`passthrough`,rule:null,reason:`pnpm --filter without subcommand`};if(s===`install`||s===`i`)return t.slice(t.indexOf(s)+1).every(e=>e.startsWith(`-`))?{action:`allow`,rule:`pnpm`,reason:null}:{action:`passthrough`,rule:null,reason:`pnpm --filter install with package arg`};if(s===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(s===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`,`licenses`,`audit`]).has(s))return{action:`allow`,rule:`pnpm`,reason:null};if(s===`exec`){let e=t.indexOf(`exec`,o),n=t.slice(e+1);return n.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:G(n.join(` `),{...r,depth:r.depth+1})}let c=s===`run`?t[t.indexOf(`run`,o)+1]:s;if(!c||c.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter run without script name`};if(V.has(c)&&c!==`run`)return{action:`passthrough`,rule:null,reason:null};let l=Et(c,a,r.cwd);if(!l)return{action:`passthrough`,rule:null,reason:`filtered script resolution failed`};let u=K(l,{...r,depth:r.depth+1,cwd:l.pkgDir??r.cwd});return u===`allow`?{action:`allow`,rule:`pnpm-filter:${a}:${c}`,reason:null}:u}const Gt=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function Kt(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(`-`)||Gt.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?G(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 J=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`]),qt=new Set([`-0`,`--null`,`-t`,`--verbose`,`-r`,`--no-run-if-empty`,`-p`,`--interactive`,`-x`,`--exit`,`--show-limits`]);function Jt(e,t){let n=1;for(;n<e.length;){let t=e[n];if(!t.startsWith(`-`))break;if(J.has(t)){n+=2;continue}let r=t.indexOf(`=`);if(r!==-1){let e=t.slice(0,r);if(J.has(e)){n++;continue}}if(t.length>2&&J.has(t.slice(0,2))){n++;continue}if(qt.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`}:G(e.slice(n).join(` `),{...t,depth:t.depth+1})}const Yt=new Set([`Read`,`Glob`,`Grep`]),Y=new Set([`Write`,`Edit`]),Xt=[{name:`env-file-read`,tools:Yt,action:`deny`,when:e=>R(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-write`,tools:Y,action:`deny`,when:e=>R(e),reason:`Don't write to .env files. These contain secrets.`},{name:`hooks-folder-write`,tools:Y,action:`deny`,when:(e,t)=>{let n=a.resolve(t.cwd,e),r=a.join(t.cwd,`.claude`,`hooks`);return n.startsWith(r+a.sep)||n===r},reason:`Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.`},{name:`claude-settings-write`,tools:Y,action:`deny`,when:(e,t)=>a.resolve(t.cwd,e)===a.join(t.cwd,`.claude`,`settings.json`),reason:`Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.`},{name:`outside-cwd-write`,tools:Y,action:`deny`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n!==t.cwd&&!n.startsWith(t.cwd+a.sep)},reason:`Don't write files outside the project directory.`}],Zt=[{name:`cwd-read`,tools:Yt,action:`allow`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n===t.cwd||n.startsWith(t.cwd+a.sep)}},{name:`cwd-write`,tools:Y,action:`allow`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n===t.cwd||n.startsWith(t.cwd+a.sep)}}],Qt=new Set([`Read`,`Write`,`Edit`]),$t=new Set([`Glob`,`Grep`]);function en(e,t,n){return Qt.has(e)?Ue(t)?.file_path??null:$t.has(e)?We(t)?.path??n:null}function tn(e,t,n){let r=en(e,t,n.cwd);if(r===null)return{action:`passthrough`,rule:null,reason:`unknown tool input shape`};for(let t of Xt)if(t.tools.has(e)&&t.when(r,n))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let t of Zt)if(t.tools.has(e)&&t.when(r,n))return{action:`allow`,rule:t.name,reason:null};return{action:`passthrough`,rule:null,reason:`no file rule matched`}}const X=await Re();X.ok||(process.stderr.write(`[bst-hook] invalid hook input: ${X.error}\\n`),P({sessionId:`unknown`,cwd:`unknown`,command:`<invalid input: ${X.error}>`,decision:`passthrough`,rule:null,reason:`validation failed: ${X.error}`}),process.exit(0));const Z=X.data;Z.hook_event_name!==`PreToolUse`&&process.exit(0),new Set([`Bash`,`Read`,`Write`,`Edit`,`Glob`,`Grep`]).has(Z.tool_name)||process.exit(0);const nn=Ct(Z.cwd);let Q,$;if(Z.tool_name===`Bash`){let e=He(Z.tool_input);e||(P({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:`<invalid Bash input: ${JSON.stringify(Z.tool_input).slice(0,200)}>`,decision:`passthrough`,rule:null,reason:`Bash tool_input missing or invalid 'command' field`}),process.exit(0)),Q=G(e.command,{cwd:Z.cwd,depth:0,repoAllowPatterns:nn}),$=e.command}else{let e=process.env.CLAUDE_PROJECT_DIR??Z.cwd;Q=tn(Z.tool_name,Z.tool_input,{cwd:e});let t=String(Z.tool_input.file_path??Z.tool_input.path??``);$=`${Z.tool_name} ${t}`}P({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:$,decision:Q.action,rule:Q.rule,reason:Q.reason}),Q.action===`allow`?N(`allow`):Q.action===`deny`&&N(`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- **hooks-folder-write**: Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\n- **claude-settings-write**: Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\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")];
2728
+ return [writeIfChanged(ctx, HOOK_PATH, "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.6.5\n * https://github.com/bensandee/tooling\n *\n * Auto-generated by @bensandee/tooling repo:sync — do not edit manually.\n */\nimport*as e from\"node:fs\";import{existsSync as t,readFileSync as n,readdirSync as r,statSync as i}from\"node:fs\";import*as a from\"node:path\";import{dirname as o,join as s,resolve as c}from\"node:path\";import{fileURLToPath as l}from\"node:url\";import{execFileSync as u}from\"node:child_process\";Object.freeze({status:`aborted`});function d(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 f=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const ee={};function p(e){return e&&Object.assign(ee,e),ee}function te(e,t){return typeof t==`bigint`?t.toString():t}function ne(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function re(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const ie=Symbol(`evaluating`);function m(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==ie)return r===void 0&&(r=ie,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const ae=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function h(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function oe(e){if(h(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(h(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function se(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function g(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 ce(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function _(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 v(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function y(e){return typeof e==`string`?e:e?.message}function b(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=y(e.inst?._zod.def?.error?.(e))??y(t?.error?.(e))??y(n.customError?.(e))??y(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const le=(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,te,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},ue=d(`$ZodError`,le),x=d(`$ZodError`,le,{Parent:Error}),de=(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 f;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>b(e,a,p())));throw ae(t,i?.callee),t}return o.value})(x),fe=(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=>b(e,a,p())));throw ae(t,i?.callee),t}return o.value})(x),S=(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 f;return a.issues.length?{success:!1,error:new(e??ue)(a.issues.map(e=>b(e,i,p())))}:{success:!0,data:a.value}})(x),pe=(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=>b(e,i,p())))}:{success:!0,data:a.value}})(x),me=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},he=/^-?\\d+(?:\\.\\d+)?$/,ge={major:4,minor:3,patch:6},C=d(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=ge;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=_(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 f;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=_(e,t))});else{if(e.issues.length===t)continue;r||=_(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(_(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new f;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 f;return o.then(e=>t(e,r,a))}return t(o,r,a)}}m(e,`~standard`,()=>({validate:t=>{try{let n=S(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return pe(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),_e=d(`$ZodString`,(e,t)=>{C.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??me(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}}),ve=d(`$ZodUnknown`,(e,t)=>{C.init(e,t),e._zod.parse=e=>e});function ye(e,t,n){e.issues.length&&t.issues.push(...v(n,e.issues)),t.value[n]=e.value}const be=d(`$ZodArray`,(e,t)=>{C.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!Array.isArray(i))return n.issues.push({expected:`array`,code:`invalid_type`,input:i,inst:e}),n;n.value=Array(i.length);let a=[];for(let e=0;e<i.length;e++){let o=i[e],s=t.element._zod.run({value:o,issues:[]},r);s instanceof Promise?a.push(s.then(t=>ye(t,n,e))):ye(s,n,e)}return a.length?Promise.all(a).then(()=>n):n}});function w(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...v(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function xe(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=ce(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function Se(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=>w(e,n,i,t,u))):w(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 Ce=d(`$ZodObject`,(e,t)=>{if(C.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=ne(()=>xe(t));m(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=h,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=>w(n,t,e,s,r))):w(i,t,e,s,r)}return i?Se(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),we=d(`$ZodRecord`,(e,t)=>{C.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!oe(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(...v(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...v(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`&&he.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=>b(e,r,p())),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(...v(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...v(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function T(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const Te=d(`$ZodOptional`,(e,t)=>{C.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,m(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),m(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${re(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=>T(t,e.value)):T(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var E,Ee=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 De(){return new Ee}(E=globalThis).__zod_globalRegistry??(E.__zod_globalRegistry=De()),globalThis.__zod_globalRegistry;function Oe(e,t){return new e({type:`string`,...g(t)})}function ke(e){return new e({type:`unknown`})}const D=d(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);C.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>de(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>S(e,t,n),e.parseAsync=async(t,n)=>fe(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>pe(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)=>se(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),Ae=d(`ZodMiniString`,(e,t)=>{_e.init(e,t),D.init(e,t)});function O(e){return Oe(Ae,e)}const je=d(`ZodMiniUnknown`,(e,t)=>{ve.init(e,t),D.init(e,t)});function Me(){return ke(je)}const Ne=d(`ZodMiniArray`,(e,t)=>{be.init(e,t),D.init(e,t)});function Pe(e,t){return new Ne({type:`array`,element:e,...g(t)})}const Fe=d(`ZodMiniObject`,(e,t)=>{Ce.init(e,t),D.init(e,t),m(e,`shape`,()=>t.shape)});function k(e,t){return new Fe({type:`object`,shape:e??{},...g(t)})}const Ie=d(`ZodMiniRecord`,(e,t)=>{we.init(e,t),D.init(e,t)});function A(e,t,n){return new Ie({type:`record`,keyType:e,valueType:t,...g(n)})}const Le=d(`ZodMiniOptional`,(e,t)=>{Te.init(e,t),D.init(e,t)});function j(e){return new Le({type:`optional`,innerType:e})}const Re=k({session_id:O(),transcript_path:O(),cwd:O(),permission_mode:O(),hook_event_name:O(),tool_name:O(),tool_input:A(O(),Me())});async function ze(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t;try{t=JSON.parse(e.join(``))}catch{return{ok:!1,raw:e.join(``).slice(0,200),error:`invalid JSON`}}let n=Re.safeParse(t);return n.success?{ok:!0,data:n.data}:{ok:!1,raw:t,error:n.error.message}}const Be=k({command:O(),description:j(O())}),Ve=k({file_path:O()}),He=k({pattern:O(),path:j(O())});function Ue(e){let t=Be.safeParse(e);return t.success?t.data:null}function We(e){let t=Ve.safeParse(e);return t.success?t.data:null}function Ge(e){let t=He.safeParse(e);return t.success?t.data:null}function M(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function Ke(){let e=a.dirname(l(import.meta.url));return a.join(e,`bst-hook.audit.local.jsonl`)}function qe(t){try{if(e.statSync(t).size>1048576){let n=`${t}.1`;e.renameSync(t,n)}}catch{}}let Je=!1;function N(t){try{let n=Ke();Je||=(qe(n),!0);let r={timestamp:new Date().toISOString(),...t};e.appendFileSync(n,JSON.stringify(r)+`\n`)}catch(e){process.stderr.write(`[bst-hook] audit write failed: ${e instanceof Error?e.message:String(e)}\\n`)}}function P(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 Ye(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]===`&`){F(t,n,`&&`),n=``,r+=2;continue}if(o===`&`&&!(r+1<e.length&&e[r+1]===`>`)&&!(r>0&&(e[r-1]===`>`||e[r-1]===`<`))){F(t,n,`&`),n=``,r++;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){F(t,n,`||`),n=``,r+=2;continue}if(o===`|`){F(t,n,`|`),n=``,r++;continue}if(o===`;`){F(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function F(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const I=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function Xe(e,t,n=!1){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(`>(`)&&!I.has(t))return{safe:!1,reason:`process substitution`}}if(!n)for(let t=0;t<e.length;t++){let n=e[t];if(!I.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 Ze=[`.sample`,`.example`,`.template`],Qe=/(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;function $e(e){return Ze.some(t=>e.endsWith(t))}function L(e){return $e(a.basename(e))?!1:Qe.test(e)}function et(e){return e.slice(1).some(e=>!e.startsWith(`-`)&&L(e))}const tt=new Set([`status`,`log`,`diff`,`show`,`blame`,`grep`,`shortlog`,`range-diff`,`describe`,`branch`,`remote`,`fetch`,`ls-files`,`ls-tree`,`ls-remote`,`show-ref`,`for-each-ref`,`rev-parse`,`rev-list`,`cat-file`,`diff-tree`,`name-rev`,`merge-base`,`check-ignore`]),nt=new Set([`push`,`add`,`commit`,`reset`,`merge`,`rebase`,`cherry-pick`,`checkout`,`switch`,`stash`,`tag`,`revert`,`clean`,`rm`,`mv`]),rt=new Set([`-c`,`--exec-path`,`--pager`,`--upload-pack`,`--receive-pack`]),it=new Set([`-C`,`-c`,`--git-dir`,`--work-tree`]);function R(e){let t=e.slice(1);for(let e=0;e<t.length;e++){let n=t[e];if(it.has(n)){e++;continue}if(!n.startsWith(`-`))return n}}function at(e){for(let t of e.slice(1))if(rt.has(t)||t===`-o`||t.startsWith(`--output`)||t.startsWith(`--exec-path=`)||t.startsWith(`--pager=`)||t.startsWith(`--upload-pack=`)||t.startsWith(`--receive-pack=`)||t.startsWith(`-o=`)||t.startsWith(`--output=`))return!1;let t=R(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===`list`||t===void 0}return tt.has(t)}function ot(e){let t=R(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];if(t===`list`||t===void 0)return!1}return nt.has(t)}const st=new Set([`push`,`pop`,`apply`,`show`]);function z(e){let t=R(e);if(!t)return!1;if(t===`add`)return!0;if(t===`checkout`)return e.slice(e.indexOf(`checkout`)+1).some(e=>e===`-b`||e===`-B`);if(t===`switch`)return!0;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===void 0?!0:st.has(t)}return!1}const B=new Set(`install.i.add.remove.rm.uninstall.un.update.up.upgrade.list.ls.ll.la.why.view.info.show.outdated.audit.licenses.init.create.link.unlink.pack.publish.config.set.get.store.cache.rebuild.rb.prune.dedupe.dlx.exec.run.patch.patch-commit.recursive.-r`.split(`.`)),ct=new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`]);function lt(e){let t=e.slice(1),n;for(let e of t)if(!(e.startsWith(`-`)&&!B.has(e))){n=e;break}return n?ct.has(n)?!0:n===`install`||n===`i`?t.slice(t.indexOf(n)+1).every(e=>e.startsWith(`-`)):!1:!1}const ut=new Set([`view`,`info`,`show`]);function dt(e){let t=e[1];return t!==void 0&&ut.has(t)}function ft(e){let t=e[1],n=`This project uses pnpm.`;return t?t===`install`||t===`i`||t===`ci`?e.slice(2).some(e=>!e.startsWith(`-`))?`${n} Use \\`pnpm add <pkg>\\` instead of \\`npm install <pkg>\\`.`:`${n} Use \\`pnpm install\\` instead of npm.`:t===`add`?`${n} Use \\`pnpm add <pkg>\\` instead of npm.`:t===`uninstall`||t===`remove`||t===`rm`||t===`un`||t===`r`?`${n} Use \\`pnpm remove <pkg>\\` instead of npm.`:t===`run`||t===`run-script`?`${n} Use \\`pnpm run <script>\\` or \\`pnpm <script>\\` instead of npm.`:t===`test`||t===`t`||t===`tst`?`${n} Use \\`pnpm test\\` instead of npm.`:t===`start`?`${n} Use \\`pnpm start\\` instead of npm.`:t===`stop`?`${n} Use \\`pnpm stop\\` instead of npm.`:t===`restart`?`${n} Use \\`pnpm restart\\` instead of npm.`:t===`exec`?`${n} Use \\`pnpm exec <cmd>\\` instead of npm.`:t===`list`||t===`ls`||t===`ll`||t===`la`?`${n} Use \\`pnpm list\\` instead of npm.`:t===`why`?`${n} Use \\`pnpm why\\` instead of npm.`:t===`outdated`?`${n} Use \\`pnpm outdated\\` instead of npm.`:t===`audit`?`${n} Use \\`pnpm audit\\` instead of npm.`:t===`update`||t===`up`||t===`upgrade`?`${n} Use \\`pnpm update\\` instead of npm.`:t===`init`?`${n} Use \\`pnpm init\\` instead of npm.`:t===`publish`?`${n} Use \\`pnpm publish\\` instead of npm.`:t===`link`?`${n} Use \\`pnpm link\\` instead of npm.`:t===`unlink`?`${n} Use \\`pnpm unlink\\` instead of npm.`:t===`pack`?`${n} Use \\`pnpm pack\\` instead of npm.`:t===`cache`?`${n} Use \\`pnpm store\\` instead of \\`npm cache\\`.`:t===`config`||t===`set`||t===`get`?`${n} Use \\`pnpm config\\` instead of npm.`:`${n} Use the equivalent pnpm command instead of npm.`:`${n} Use \\`pnpm\\` instead of npm.`}function V(e,t){for(let n of e.slice(1)){if(n.startsWith(`-`)){if(n===`-t`||n.startsWith(`--target-directory`))return!1;continue}if(a.basename(n).startsWith(`.`)||n.startsWith(`~/`)||n===`~`)return!1;let e=a.resolve(t.cwd,n);if(e!==t.cwd&&!e.startsWith(t.cwd+a.sep))return!1}return!0}const pt=new Set([`-exec`,`-execdir`,`-ok`,`-okdir`,`-delete`,`-fprint`,`-fprint0`,`-fprintf`,`-fls`]);function mt(e){return!e.some(e=>pt.has(e))}function ht(e){return!e.includes(`-o`)&&!e.some(e=>e.startsWith(`--output`))}function gt(e){let t=e.findIndex(e=>a.basename(e)===`rm`);if(t===-1)return!1;let n=e.slice(t+1),r=n.filter(e=>e.startsWith(`-`)&&!e.startsWith(`--`)),i=n.filter(e=>e.startsWith(`--`)),o=r.some(e=>/[rR]/.test(e)),s=i.includes(`--recursive`);return o||s}function _t(e){return e.some(e=>e===`777`||e===`0777`)}function vt(e){let t=e.indexOf(`push`);if(t===-1)return!1;let n=e.slice(t+1);return n.some(e=>e===`--force`||e===`-f`&&!n.includes(`--force-with-lease`))}function yt(e){if(!(e.includes(`reset`)&&e.includes(`--hard`)))return!1;if(e.some(e=>e===`main`||e===`master`))return!0;let t=e.filter(e=>e!==`git`&&e!==`reset`&&!e.startsWith(`-`));return t.length===0||t.includes(`HEAD`)}const bt=[{name:`rm-recursive`,match:/^rm$/,action:`deny`,when:e=>gt(e),reason:\"Don't use recursive `rm`. Use more targeted deletes, or remove specific files/directories.\"},{name:`sudo`,match:/^sudo$/,action:`deny`,reason:`Don't use sudo. If you need elevated permissions, ask me to do it manually.`},{name:`git-force-push`,match:/^git$/,action:`deny`,when:e=>vt(e),reason:\"Use `git push --force-with-lease` instead of `--force`.\"},{name:`git-reset-hard`,match:/^git$/,action:`deny`,when:e=>yt(e),reason:\"Don't `reset --hard` on main. Create a branch first.\"},{name:`git-write`,match:/^git$/,action:`deny`,when:e=>ot(e)&&!z(e),reason:`Git write operations (push, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.`},{name:`chmod-777`,match:/^chmod$/,action:`deny`,when:e=>_t(e),reason:`Don't use 777 permissions. Use more restrictive permissions.`},{name:`env-file-read`,match:/^(cat|head|tail|less|more|grep)$/,action:`deny`,when:e=>et(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`npx-redirect`,match:/^npx$/,action:`deny`,reason:\"This project uses pnpm. Use `pnpm dlx <pkg>` or `pnpm exec <cmd>` instead of npx.\"}],H=[{name:`vitest`,match:/^vitest$/,action:`allow`},{name:`jest`,match:/^jest$/,action:`allow`},{name:`tsc`,match:/^tsc$/,action:`allow`},{name:`tsdown`,match:/^tsdown$/,action:`allow`},{name:`oxlint`,match:/^oxlint$/,action:`allow`},{name:`oxfmt`,match:/^oxfmt$/,action:`allow`},{name:`eslint`,match:/^eslint$/,action:`allow`},{name:`prettier`,match:/^prettier$/,action:`allow`},{name:`biome`,match:/^biome$/,action:`allow`},{name:`knip`,match:/^knip$/,action:`allow`},{name:`cat`,match:/^cat$/,action:`allow`},{name:`head`,match:/^head$/,action:`allow`},{name:`tail`,match:/^tail$/,action:`allow`},{name:`grep`,match:/^grep$/,action:`allow`},{name:`ls`,match:/^ls$/,action:`allow`},{name:`wc`,match:/^wc$/,action:`allow`},{name:`uniq`,match:/^uniq$/,action:`allow`},{name:`diff`,match:/^diff$/,action:`allow`},{name:`echo`,match:/^echo$/,action:`allow`},{name:`pwd`,match:/^pwd$/,action:`allow`},{name:`which`,match:/^which$/,action:`allow`},{name:`dirname`,match:/^dirname$/,action:`allow`},{name:`basename`,match:/^basename$/,action:`allow`},{name:`realpath`,match:/^realpath$/,action:`allow`},{name:`npm`,match:/^npm$/,action:`allow`,when:e=>dt(e)},{name:`test`,match:/^test$/,action:`allow`},{name:`bracket-test`,match:/^\\[$/,action:`allow`},{name:`cd`,match:/^cd$/,action:`allow`},{name:`true`,match:/^true$/,action:`allow`},{name:`colon`,match:/^:$/,action:`allow`},{name:`find`,match:/^find$/,action:`allow`,when:e=>mt(e)},{name:`sort`,match:/^sort$/,action:`allow`,when:e=>ht(e)},{name:`git`,match:/^git$/,action:`allow`,when:e=>at(e)},{name:`git-safe-write`,match:/^git$/,action:`allow`,when:e=>z(e)},{name:`pnpm`,match:/^pnpm$/,action:`allow`,when:e=>lt(e)},{name:`cp`,match:/^cp$/,action:`allow`,when:(e,t)=>V(e,t)},{name:`mkdir`,match:/^mkdir$/,action:`allow`,when:(e,t)=>V(e,t)},{name:`touch`,match:/^touch$/,action:`allow`,when:(e,t)=>V(e,t)}],xt=`.claude/hooks/bst-hook.settings.json`,St=k({allow:Pe(O())});function Ct(e,t){try{return u(`git`,[`ls-files`,`--error-unmatch`,t],{cwd:e,stdio:`ignore`}),u(`git`,[`diff`,`--quiet`,`HEAD`,`--`,t],{cwd:e,stdio:`ignore`}),!0}catch{return!1}}function wt(e){let r=W(e)??e,i=s(r,xt);if(!t(i)||!Ct(r,xt))return[];try{let e=n(i,`utf-8`),t=JSON.parse(e),r=St.safeParse(t);return r.success?r.data.allow.filter(e=>typeof e==`string`&&e.length>0):[]}catch{return[]}}function Tt(e,t,n){let r=n.split(/\\s+/).filter(Boolean);if(r.length===0||r[0]!==e)return!1;let i=r.slice(1),a=t.slice(1);for(let e=0;e<i.length;e++)if(e>=a.length||a[e]!==i[e])return!1;return!0}const Et=k({scripts:A(O(),Me())});function U(e,t){let n=Pt(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 Dt(e,t,n){let r=At(t,n);if(!r)return null;let i=U(e,r);return i?{...i,pkgDir:r}:null}function Ot(e,t){let n=kt(t);if(!n)return null;let r=[];for(let t of n){let n=U(e,t);n&&r.push({...n,pkgDir:t})}return r}function W(e){let n=c(e);for(;;){if(t(s(n,`pnpm-workspace.yaml`)))return n;let e=o(n);if(e===n)return null;n=e}}function kt(e){let t=jt(e);if(!t)return null;let n=[];for(let a of t){if(!a.endsWith(`/*`))continue;let t=c(e,a.slice(0,-2)),o;try{o=r(t)}catch{continue}for(let e of o){let r=s(t,e);try{if(!i(r).isDirectory())continue}catch{continue}n.push(r)}}return n.length>0?n:null}function At(e,t){let n=kt(t);if(!n)return null;for(let t of n){let n=Nt(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function jt(e){try{let t=n(s(e,`pnpm-workspace.yaml`),`utf-8`).split(`\n`),r=t.findIndex(e=>/^packages\\s*:/.test(e));if(r===-1)return null;let i=[];for(let e=r+1;e<t.length;e++){let n=t[e],r=/^\\s+-\\s+(.+)$/.exec(n);if(!r)break;i.push(r[1].trim().replace(/['\"`]/g,``))}return i.length>0?i:null}catch{return null}}const Mt=k({name:O()});function Nt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=Mt.safeParse(JSON.parse(t));return r.success?r.data.name:null}catch{return null}}function Pt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=Et.safeParse(JSON.parse(t));return r.success?r.data.scripts:null}catch{return null}}function Ft(e){return Ct(e,`package.json`)}const It=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`]),Lt=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function Rt(e){return It.has(e)?!0:Lt.some(t=>e.startsWith(t))}function zt(e,t){let n=P(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 a.isAbsolute(i)?i:a.resolve(t.cwd,i)}function G(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=Ye(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=zt(e.command,o);t&&(o={...o,cwd:t})}let t=Bt(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 K(e,t){let n=e.pkgDir??t.cwd,r=t.insideResolvedScript??Ft(n)?{...t,insideResolvedScript:!0}:t;if(e.pre){let t=G(e.pre,r);if(t.action!==`allow`)return t}let i=G(e.main,r);if(i.action!==`allow`)return i;if(e.post){let t=G(e.post,r);if(t.action!==`allow`)return t}return`allow`}function Bt(e,t){let n=e.trim();if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=P(e);if(!r.ok)return{action:`passthrough`,rule:null,reason:r.error};let i=r.tokens;if(i.length===0)return{action:`passthrough`,rule:null,reason:null};let o=0;for(;o<i.length&&/^\\w+=/.test(i[o]);){let e=i[o].split(`=`)[0];if(Rt(e))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${e}`};o++}if(o>=i.length)return{action:`allow`,rule:`env-only`,reason:null};i=i.slice(o);let s=Xe(i,e,t.insideResolvedScript);if(!s.safe)return{action:`passthrough`,rule:null,reason:s.reason};if(!t.insideResolvedScript)for(let e=0;e<i.length;e++){let t=i[e];if(!(t===`2>&1`||t===`2>/dev/null`||t===`>/dev/null`||t===`1>/dev/null`)){if(t===`>`||t===`>>`){if(i[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 c=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);i=i.filter((e,t)=>!(c.has(e)||(e===`>`||e===`>>`)&&i[t+1]===`/dev/null`||e===`/dev/null`&&t>0&&(i[t-1]===`>`||i[t-1]===`>>`)));let l=a.basename(i[0]);if(l===`command`&&i.length>1)return G(i.slice(1).join(` `),{...t,depth:t.depth+1});if(l===`env`){let e=1;for(;e<i.length&&/^\\w+=/.test(i[e]);){let t=i[e].split(`=`)[0];if(Rt(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=i.length?{action:`allow`,rule:`env-only`,reason:null}:G(i.slice(e).join(` `),{...t,depth:t.depth+1})}if(l===`pnpm`)return Vt(i,t);if(l===`npm`)return Ut(i);if(l===`bash`||l===`sh`||l===`zsh`)return Kt(i,t);if(l===`xargs`)return Jt(i,t);if(l===`node`&&(i.includes(`-e`)||i.includes(`-p`)||i.includes(`--eval`)||i.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(l===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let e of bt)if(e.match.test(l)&&(!e.when||e.when(i,t)))return{action:`deny`,rule:e.name,reason:e.reason??null};for(let e of H)if(e.match.test(l)&&(!e.when||e.when(i,t)))return{action:`allow`,rule:e.name,reason:null};if(t.repoAllowPatterns){for(let e of t.repoAllowPatterns)if(Tt(l,i,e))return{action:`allow`,rule:`repo:${l}`,reason:null}}return t.insideResolvedScript?{action:`allow`,rule:`resolved-script`,reason:null}:{action:`passthrough`,rule:null,reason:`no rule matched: ${l}`}}function Vt(e,t){let n=e.slice(1),r,i=-1,o=!1,s=!1,c;for(let a=0;a<n.length;a++){let l=n[a];if(l===`--filter`||l.startsWith(`--filter=`))return Wt(e,n,a,t);if(l===`-r`||l===`--recursive`){o=!0;continue}if(l===`-w`||l===`--workspace-root`){s=!0;continue}if(l===`-C`||l===`--dir`){c=n[++a];continue}if(l.startsWith(`-C=`)||l.startsWith(`--dir=`)){c=l.split(`=`,2)[1];continue}if(!(l.startsWith(`-`)&&!B.has(l))){r=l,i=a;break}}if(!r)return{action:`passthrough`,rule:null,reason:null};if(r===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(r===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(r===`exec`){let e=n.slice(i+1);return e.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:G(e.join(` `),{...t,depth:t.depth+1})}let l;if(r===`run`){let e=n[i+1];if(!e||e.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm run without script name`};l=e}else if(!B.has(r))l=r;else{for(let n of H)if(n.match.test(`pnpm`)&&(!n.when||n.when(e,t)))return{action:`allow`,rule:n.name,reason:null};return{action:`passthrough`,rule:null,reason:null}}if(o)return Ht(l,t);if(s){let e=W(t.cwd);return e?q(l,{...t,cwd:e}):{action:`passthrough`,rule:null,reason:`workspace root not found`}}if(c){let e=a.resolve(t.cwd,c);return q(l,{...t,cwd:e})}return q(l,t)}function q(e,t){let n=U(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r=K(n,{...t,depth:t.depth+1});return r===`allow`?{action:`allow`,rule:`pnpm-script:${e}`,reason:null}:r}function Ht(e,t){let n=Ot(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n=K(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 Ut(e){for(let t of H)if(t.match.test(`npm`)&&(!t.when||t.when(e,{cwd:``,depth:0})))return{action:`allow`,rule:t.name,reason:null};return{action:`deny`,rule:`npm-redirect`,reason:ft(e)}}function Wt(e,t,n,r){let i=t[n],a,o;if(i.startsWith(`--filter=`))a=i.slice(9),o=n+1;else{if(a=t[n+1],!a||a.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};o=n+2}if(!a)return{action:`passthrough`,rule:null,reason:`pnpm --filter without package`};let s;for(let e=o;e<t.length;e++){let n=t[e];if(!(n.startsWith(`-`)&&!B.has(n))){s=n;break}}if(!s)return{action:`passthrough`,rule:null,reason:`pnpm --filter without subcommand`};if(s===`install`||s===`i`)return t.slice(t.indexOf(s)+1).every(e=>e.startsWith(`-`))?{action:`allow`,rule:`pnpm`,reason:null}:{action:`passthrough`,rule:null,reason:`pnpm --filter install with package arg`};if(s===`dlx`)return{action:`passthrough`,rule:null,reason:`pnpm dlx`};if(s===`add`)return{action:`passthrough`,rule:null,reason:`pnpm add`};if(new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`,`licenses`,`audit`]).has(s))return{action:`allow`,rule:`pnpm`,reason:null};if(s===`exec`){let e=t.indexOf(`exec`,o),n=t.slice(e+1);return n.length===0?{action:`passthrough`,rule:null,reason:`bare pnpm exec`}:G(n.join(` `),{...r,depth:r.depth+1})}let c=s===`run`?t[t.indexOf(`run`,o)+1]:s;if(!c||c.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm --filter run without script name`};if(B.has(c)&&c!==`run`)return{action:`passthrough`,rule:null,reason:null};let l=Dt(c,a,r.cwd);if(!l)return{action:`passthrough`,rule:null,reason:`filtered script resolution failed`};let u=K(l,{...r,depth:r.depth+1,cwd:l.pkgDir??r.cwd});return u===`allow`?{action:`allow`,rule:`pnpm-filter:${a}:${c}`,reason:null}:u}const Gt=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function Kt(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(`-`)||Gt.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?G(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 J=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`]),qt=new Set([`-0`,`--null`,`-t`,`--verbose`,`-r`,`--no-run-if-empty`,`-p`,`--interactive`,`-x`,`--exit`,`--show-limits`]);function Jt(e,t){let n=1;for(;n<e.length;){let t=e[n];if(!t.startsWith(`-`))break;if(J.has(t)){n+=2;continue}let r=t.indexOf(`=`);if(r!==-1){let e=t.slice(0,r);if(J.has(e)){n++;continue}}if(t.length>2&&J.has(t.slice(0,2))){n++;continue}if(qt.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`}:G(e.slice(n).join(` `),{...t,depth:t.depth+1})}const Yt=new Set([`Read`,`Glob`,`Grep`]),Y=new Set([`Write`,`Edit`]),Xt=[{name:`env-file-read`,tools:Yt,action:`deny`,when:e=>L(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-write`,tools:Y,action:`deny`,when:e=>L(e),reason:`Don't write to .env files. These contain secrets.`},{name:`hooks-folder-write`,tools:Y,action:`deny`,when:(e,t)=>{let n=a.resolve(t.cwd,e),r=a.join(t.cwd,`.claude`,`hooks`);return n.startsWith(r+a.sep)||n===r},reason:`Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.`},{name:`claude-settings-write`,tools:Y,action:`deny`,when:(e,t)=>a.resolve(t.cwd,e)===a.join(t.cwd,`.claude`,`settings.json`),reason:`Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.`},{name:`outside-cwd-write`,tools:Y,action:`deny`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n!==t.cwd&&!n.startsWith(t.cwd+a.sep)},reason:`Don't write files outside the project directory.`}],Zt=[{name:`cwd-read`,tools:Yt,action:`allow`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n===t.cwd||n.startsWith(t.cwd+a.sep)}},{name:`cwd-write`,tools:Y,action:`allow`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n===t.cwd||n.startsWith(t.cwd+a.sep)}}],Qt=new Set([`Read`,`Write`,`Edit`]),$t=new Set([`Glob`,`Grep`]);function en(e,t,n){return Qt.has(e)?We(t)?.file_path??null:$t.has(e)?Ge(t)?.path??n:null}function tn(e,t,n){let r=en(e,t,n.cwd);if(r===null)return{action:`passthrough`,rule:null,reason:`unknown tool input shape`};for(let t of Xt)if(t.tools.has(e)&&t.when(r,n))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let t of Zt)if(t.tools.has(e)&&t.when(r,n))return{action:`allow`,rule:t.name,reason:null};return{action:`passthrough`,rule:null,reason:`no file rule matched`}}const X=await ze();X.ok||(process.stderr.write(`[bst-hook] invalid hook input: ${X.error}\\n`),N({sessionId:`unknown`,cwd:`unknown`,command:`<invalid input: ${X.error}>`,decision:`passthrough`,rule:null,reason:`validation failed: ${X.error}`}),process.exit(0));const Z=X.data;Z.hook_event_name!==`PreToolUse`&&process.exit(0),new Set([`Bash`,`Read`,`Write`,`Edit`,`Glob`,`Grep`]).has(Z.tool_name)||process.exit(0);const nn=wt(Z.cwd);let Q,$;if(Z.tool_name===`Bash`){let e=Ue(Z.tool_input);e||(N({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:`<invalid Bash input: ${JSON.stringify(Z.tool_input).slice(0,200)}>`,decision:`passthrough`,rule:null,reason:`Bash tool_input missing or invalid 'command' field`}),process.exit(0)),Q=G(e.command,{cwd:Z.cwd,depth:0,repoAllowPatterns:nn}),$=e.command}else{let e=W(Z.cwd)??process.env.CLAUDE_PROJECT_DIR??Z.cwd;Q=tn(Z.tool_name,Z.tool_input,{cwd:e});let t=String(Z.tool_input.file_path??Z.tool_input.path??``);$=`${Z.tool_name} ${t}`}N({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:$,decision:Q.action,rule:Q.rule,reason:Q.reason}),Q.action===`allow`?M(`allow`):Q.action===`deny`&&M(`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- **hooks-folder-write**: Don't write to .claude/hooks/. These files are managed by the project and should not be modified directly.\n- **claude-settings-write**: Don't write to .claude/settings.json. This file is managed by the project and should not be modified directly.\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")];
2738
2729
  }
2739
2730
  //#endregion
2740
2731
  //#region src/generators/claude-settings.ts
@@ -2789,7 +2780,7 @@ function buildSettings(ctx) {
2789
2780
  matcher: "Bash|Read|Write|Edit|Glob|Grep",
2790
2781
  hooks: [{
2791
2782
  type: "command",
2792
- command: `node "$CLAUDE_PROJECT_DIR/.claude/hooks/bst-hook.mjs"`
2783
+ command: `node "$(git rev-parse --show-toplevel)/.claude/hooks/bst-hook.mjs"`
2793
2784
  }]
2794
2785
  }] },
2795
2786
  instructions: [
@@ -2848,7 +2839,23 @@ function writeOrMergeSettings(ctx, filePath, generated) {
2848
2839
  for (const [event, generatedMatchers] of Object.entries(generated.hooks)) {
2849
2840
  const existingMatchers = mergedHooks[event] ?? [];
2850
2841
  for (const gm of generatedMatchers) {
2851
- if (existingMatchers.some((m) => m.matcher === gm.matcher && m.hooks.some((h) => h.command.includes(BST_HOOK_MARKER)))) continue;
2842
+ const existingIdx = existingMatchers.findIndex((m) => m.matcher === gm.matcher && m.hooks.some((h) => h.command.includes(BST_HOOK_MARKER)));
2843
+ if (existingIdx !== -1) {
2844
+ const existingEntry = existingMatchers[existingIdx];
2845
+ const genCommand = gm.hooks.find((h) => h.command.includes(BST_HOOK_MARKER))?.command;
2846
+ const curCommand = existingEntry.hooks.find((h) => h.command.includes(BST_HOOK_MARKER))?.command;
2847
+ if (genCommand && curCommand && genCommand !== curCommand) {
2848
+ mergedHooks[event] = (mergedHooks[event] ?? []).map((m, i) => i === existingIdx ? {
2849
+ ...m,
2850
+ hooks: m.hooks.map((h) => h.command.includes(BST_HOOK_MARKER) ? {
2851
+ ...h,
2852
+ command: genCommand
2853
+ } : h)
2854
+ } : m);
2855
+ hookChanges++;
2856
+ }
2857
+ continue;
2858
+ }
2852
2859
  if (gm.matcher.includes("|")) {
2853
2860
  const before = (mergedHooks[event] ?? []).length;
2854
2861
  mergedHooks[event] = removeObsoleteBstHookEntries(mergedHooks[event] ?? [], gm.matcher);
@@ -3357,7 +3364,7 @@ async function runGenerators(ctx) {
3357
3364
  function generateMigratePrompt(results, config, detected) {
3358
3365
  const sections = [];
3359
3366
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3360
- sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.47.0 repo:sync\` on ${timestamp}_\n`);
3367
+ sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.48.0 repo:sync\` on ${timestamp}_\n`);
3361
3368
  const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
3362
3369
  if (changed.length > 0) {
3363
3370
  const items = changed.map((r) => {
@@ -4705,6 +4712,32 @@ function mergeGitHub(dryRun) {
4705
4712
  }
4706
4713
  //#endregion
4707
4714
  //#region src/release/simple.ts
4715
+ /**
4716
+ * Resolve the bump/first-release/release-as values from CLI flags and the
4717
+ * RELEASE_BUMP env var. CLI flags take precedence over the env var.
4718
+ *
4719
+ * RELEASE_BUMP values: "auto" (or empty) → no flags,
4720
+ * "major"/"minor"/"patch" → releaseAs, "first-release" → firstRelease.
4721
+ */
4722
+ function resolveReleaseBump(flagFirstRelease, flagReleaseAs, envBump) {
4723
+ if (flagFirstRelease || flagReleaseAs) return {
4724
+ firstRelease: flagFirstRelease,
4725
+ releaseAs: flagReleaseAs
4726
+ };
4727
+ const bump = envBump?.trim();
4728
+ if (!bump || bump === "auto") return {
4729
+ firstRelease: false,
4730
+ releaseAs: void 0
4731
+ };
4732
+ if (bump === "first-release") return {
4733
+ firstRelease: true,
4734
+ releaseAs: void 0
4735
+ };
4736
+ return {
4737
+ firstRelease: false,
4738
+ releaseAs: bump
4739
+ };
4740
+ }
4708
4741
  /** Build the commit-and-tag-version args. */
4709
4742
  function buildArgs(config) {
4710
4743
  const args = ["exec", "commit-and-tag-version"];
@@ -4890,6 +4923,7 @@ const releaseSimpleCommand = defineCommand({
4890
4923
  };
4891
4924
  else platform = { type: "github" };
4892
4925
  }
4926
+ const bump = resolveReleaseBump(args["first-release"] === true, args["release-as"], process.env["RELEASE_BUMP"]);
4893
4927
  const config = {
4894
4928
  cwd,
4895
4929
  dryRun: args["dry-run"] === true,
@@ -4897,9 +4931,9 @@ const releaseSimpleCommand = defineCommand({
4897
4931
  noPush: args["no-push"] === true,
4898
4932
  noSlidingTags: args["no-sliding-tags"] === true,
4899
4933
  noRelease,
4900
- firstRelease: args["first-release"] === true,
4901
- releaseAs: args["release-as"],
4902
- prerelease: args.prerelease,
4934
+ firstRelease: bump.firstRelease,
4935
+ releaseAs: bump.releaseAs,
4936
+ prerelease: args.prerelease ?? process.env["RELEASE_PRERELEASE"] ?? void 0,
4903
4937
  platform
4904
4938
  };
4905
4939
  if ((await runSimpleRelease(createRealExecutor(), config)).pushed) {
@@ -6048,8 +6082,7 @@ const webhookSendCommand = defineCommand({
6048
6082
  args: {
6049
6083
  url: {
6050
6084
  type: "positional",
6051
- description: "Webhook URL to POST to",
6052
- required: true
6085
+ description: "Webhook URL to POST to. If empty or missing, the command exits successfully (no-op)."
6053
6086
  },
6054
6087
  tag: {
6055
6088
  type: "string",
@@ -6073,6 +6106,10 @@ const webhookSendCommand = defineCommand({
6073
6106
  }
6074
6107
  },
6075
6108
  async run({ args }) {
6109
+ if (!args.url) {
6110
+ log.info("No webhook URL configured — skipping post-release hook");
6111
+ return;
6112
+ }
6076
6113
  const secret = process.env["WEBHOOK_SECRET"];
6077
6114
  const payload = args.tag || args.repository ? buildSimplePayload({
6078
6115
  tag: args.tag ?? "",
@@ -6089,7 +6126,7 @@ const webhookSendCommand = defineCommand({
6089
6126
  const main = defineCommand({
6090
6127
  meta: {
6091
6128
  name: "bst",
6092
- version: "0.47.0",
6129
+ version: "0.48.0",
6093
6130
  description: "Bootstrap and maintain standardized TypeScript project tooling"
6094
6131
  },
6095
6132
  subCommands: {
@@ -6109,7 +6146,7 @@ const main = defineCommand({
6109
6146
  "webhook:send": webhookSendCommand
6110
6147
  }
6111
6148
  });
6112
- console.log(`@bensandee/tooling v0.47.0`);
6149
+ console.log(`@bensandee/tooling v0.48.0`);
6113
6150
  async function run() {
6114
6151
  await runMain(main);
6115
6152
  process.exit(process.exitCode ?? 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bensandee/tooling",
3
- "version": "0.47.0",
3
+ "version": "0.48.0",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -54,8 +54,8 @@
54
54
  "prettier": "3.8.1",
55
55
  "release-it": "19.2.4",
56
56
  "tsdown": "0.21.6",
57
- "@bensandee/claude-hooks": "0.6.4",
58
- "@bensandee/config": "0.10.1"
57
+ "@bensandee/config": "0.10.1",
58
+ "@bensandee/claude-hooks": "0.6.5"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsdown",