@bensandee/tooling 0.40.0 → 0.40.2

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 +92 -58
  2. package/package.json +2 -2
package/dist/bin.mjs CHANGED
@@ -465,10 +465,14 @@ function buildDefaultConfig(targetDir, flags) {
465
465
  }
466
466
  //#endregion
467
467
  //#region src/utils/fs.ts
468
+ /** Check if a relative path resolves within the target directory. */
469
+ function isContained(targetDir, rel) {
470
+ const resolved = path.resolve(targetDir, rel);
471
+ return resolved.startsWith(targetDir + path.sep) || resolved === targetDir;
472
+ }
468
473
  /** Verify that a relative path resolves within the target directory. */
469
474
  function assertContained(targetDir, rel) {
470
- const resolved = path.resolve(targetDir, rel);
471
- if (!resolved.startsWith(targetDir + path.sep) && resolved !== targetDir) throw new FatalError(`Path traversal detected: ${rel} resolves outside target directory`);
475
+ if (!isContained(targetDir, rel)) throw new FatalError(`Path traversal detected: ${rel} resolves outside target directory`);
472
476
  }
473
477
  /**
474
478
  * Compare file content, using parsed comparison for JSON files to ignore
@@ -511,13 +515,11 @@ function createContext(config, confirmOverwrite) {
511
515
  targetDir: config.targetDir,
512
516
  packageJson: pkgRaw ? parsePackageJson(pkgRaw) : void 0,
513
517
  exists: (rel) => {
514
- const resolved = path.resolve(config.targetDir, rel);
515
- if (!resolved.startsWith(config.targetDir + path.sep) && resolved !== config.targetDir) return false;
518
+ if (!isContained(config.targetDir, rel)) return false;
516
519
  return fileExists(config.targetDir, rel);
517
520
  },
518
521
  read: (rel) => {
519
- const resolved = path.resolve(config.targetDir, rel);
520
- if (!resolved.startsWith(config.targetDir + path.sep) && resolved !== config.targetDir) return;
522
+ if (!isContained(config.targetDir, rel)) return void 0;
521
523
  return readFile(config.targetDir, rel);
522
524
  },
523
525
  write: (rel, content) => {
@@ -773,6 +775,17 @@ function findRootDockerfile(executor, cwd) {
773
775
  context: "."
774
776
  };
775
777
  }
778
+ /** Helper to create a DockerPackage object from resolved config. */
779
+ function createDockerPackage(executor, cwd, dir, repoName, docker) {
780
+ const isRoot = dir === ".";
781
+ const { name, version } = readPackageInfo(executor, isRoot ? path.join(cwd, "package.json") : path.join(cwd, "packages", dir, "package.json"));
782
+ return {
783
+ dir,
784
+ imageName: isRoot ? stripScope(name ?? repoName) : `${repoName}-${stripScope(name ?? dir)}`,
785
+ version,
786
+ docker
787
+ };
788
+ }
776
789
  /**
777
790
  * Discover Docker packages by convention and merge with .tooling.json overrides.
778
791
  *
@@ -794,37 +807,15 @@ function detectDockerPackages(executor, cwd, repoName) {
794
807
  const convention = findConventionDockerfile(executor, cwd, dir);
795
808
  const docker = overrides[dir] ?? convention;
796
809
  if (docker) {
797
- const { name, version } = readPackageInfo(executor, path.join(cwd, "packages", dir, "package.json"));
798
- packages.push({
799
- dir,
800
- imageName: `${repoName}-${stripScope(name ?? dir)}`,
801
- version,
802
- docker
803
- });
810
+ packages.push(createDockerPackage(executor, cwd, dir, repoName, docker));
804
811
  seen.add(dir);
805
812
  }
806
813
  }
807
- for (const [dir, docker] of Object.entries(overrides)) if (!seen.has(dir)) {
808
- const { name, version } = readPackageInfo(executor, path.join(cwd, "packages", dir, "package.json"));
809
- packages.push({
810
- dir,
811
- imageName: `${repoName}-${stripScope(name ?? dir)}`,
812
- version,
813
- docker
814
- });
815
- }
814
+ for (const [dir, docker] of Object.entries(overrides)) if (!seen.has(dir)) packages.push(createDockerPackage(executor, cwd, dir, repoName, docker));
816
815
  } else {
817
816
  const convention = findRootDockerfile(executor, cwd);
818
817
  const docker = overrides["."] ?? convention;
819
- if (docker) {
820
- const { name, version } = readPackageInfo(executor, path.join(cwd, "package.json"));
821
- packages.push({
822
- dir: ".",
823
- imageName: stripScope(name ?? repoName),
824
- version,
825
- docker
826
- });
827
- }
818
+ if (docker) packages.push(createDockerPackage(executor, cwd, ".", repoName, docker));
828
819
  }
829
820
  return packages;
830
821
  }
@@ -837,13 +828,7 @@ function readSinglePackageDocker(executor, cwd, packageDir, repoName) {
837
828
  const convention = findConventionDockerfile(executor, cwd, dir);
838
829
  const docker = loadDockerMap(executor, cwd)[dir] ?? convention;
839
830
  if (!docker) throw new FatalError(`No Dockerfile found for package "${dir}" (checked convention paths and .tooling.json)`);
840
- const { name, version } = readPackageInfo(executor, path.join(cwd, "packages", dir, "package.json"));
841
- return {
842
- dir,
843
- imageName: `${repoName}-${stripScope(name ?? dir)}`,
844
- version,
845
- docker
846
- };
831
+ return createDockerPackage(executor, cwd, dir, repoName, docker);
847
832
  }
848
833
  /** Parse semver version string into major, minor, patch components. */
849
834
  function parseSemver(version) {
@@ -1564,7 +1549,7 @@ async function generatePackageJson(ctx) {
1564
1549
  const devDeps = { ...ROOT_DEV_DEPS };
1565
1550
  if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
1566
1551
  devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.10.0";
1567
- devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.40.0";
1552
+ devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.40.2";
1568
1553
  if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.12.0";
1569
1554
  if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
1570
1555
  "@changesets/cli": "2.30.0",
@@ -2406,7 +2391,7 @@ async function generatePnpmWorkspace(ctx) {
2406
2391
  //#region src/generators/claude-hooks.ts
2407
2392
  const HOOK_PATH = ".claude/hooks/bst-hook.mjs";
2408
2393
  async function generateClaudeHooks(ctx) {
2409
- const content = "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.2.0\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{readFileSync as t,readdirSync as n,statSync as r}from\"node:fs\";import*as i from\"node:path\";import{join as a,resolve as o}from\"node:path\";import{fileURLToPath as s}from\"node:url\";Object.freeze({status:`aborted`});function c(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 l=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const u={};function d(e){return e&&Object.assign(u,e),u}function ee(e,t){return typeof t==`bigint`?t.toString():t}function te(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function ne(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const f=Symbol(`evaluating`);function p(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==f)return r===void 0&&(r=f,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const m=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function h(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function re(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 ie(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 _(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function v(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 y(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function b(e){return typeof e==`string`?e:e?.message}function x(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=b(e.inst?._zod.def?.error?.(e))??b(t?.error?.(e))??b(n.customError?.(e))??b(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const S=(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,ee,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},ae=c(`$ZodError`,S),C=c(`$ZodError`,S,{Parent:Error}),w=(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 l;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>x(e,a,d())));throw m(t,i?.callee),t}return o.value})(C),oe=(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=>x(e,a,d())));throw m(t,i?.callee),t}return o.value})(C),T=(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 l;return a.issues.length?{success:!1,error:new(e??ae)(a.issues.map(e=>x(e,i,d())))}:{success:!0,data:a.value}})(C),E=(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=>x(e,i,d())))}:{success:!0,data:a.value}})(C),se=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},ce=/^-?\\d+(?:\\.\\d+)?$/,le={major:4,minor:3,patch:6},D=c(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=le;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=v(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 l;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=v(e,t))});else{if(e.issues.length===t)continue;r||=v(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(v(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new l;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 l;return o.then(e=>t(e,r,a))}return t(o,r,a)}}p(e,`~standard`,()=>({validate:t=>{try{let n=T(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return E(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),ue=c(`$ZodString`,(e,t)=>{D.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??se(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}}),de=c(`$ZodUnknown`,(e,t)=>{D.init(e,t),e._zod.parse=e=>e});function O(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...y(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function fe(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=_(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function pe(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=>O(e,n,i,t,u))):O(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 me=c(`$ZodObject`,(e,t)=>{if(D.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=te(()=>fe(t));p(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=>O(n,t,e,s,r))):O(i,t,e,s,r)}return i?pe(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),he=c(`$ZodRecord`,(e,t)=>{D.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!re(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(...y(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...y(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`&&ce.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=>x(e,r,d())),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(...y(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...y(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function k(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const ge=c(`$ZodOptional`,(e,t)=>{D.init(e,t),e._zod.optin=`optional`,e._zod.optout=`optional`,p(e._zod,`values`,()=>t.innerType._zod.values?new Set([...t.innerType._zod.values,void 0]):void 0),p(e._zod,`pattern`,()=>{let e=t.innerType._zod.pattern;return e?RegExp(`^(${ne(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=>k(t,e.value)):k(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var A,_e=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 j(){return new _e}(A=globalThis).__zod_globalRegistry??(A.__zod_globalRegistry=j()),globalThis.__zod_globalRegistry;function M(e,t){return new e({type:`string`,...g(t)})}function N(e){return new e({type:`unknown`})}const P=c(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);D.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>w(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>T(e,t,n),e.parseAsync=async(t,n)=>oe(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>E(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)=>ie(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),F=c(`ZodMiniString`,(e,t)=>{ue.init(e,t),P.init(e,t)});function I(e){return M(F,e)}const ve=c(`ZodMiniUnknown`,(e,t)=>{de.init(e,t),P.init(e,t)});function ye(){return N(ve)}const be=c(`ZodMiniObject`,(e,t)=>{me.init(e,t),P.init(e,t),p(e,`shape`,()=>t.shape)});function L(e,t){return new be({type:`object`,shape:e??{},...g(t)})}const xe=c(`ZodMiniRecord`,(e,t)=>{he.init(e,t),P.init(e,t)});function Se(e,t,n){return new xe({type:`record`,keyType:e,valueType:t,...g(n)})}const Ce=c(`ZodMiniOptional`,(e,t)=>{ge.init(e,t),P.init(e,t)});function we(e){return new Ce({type:`optional`,innerType:e})}const Te=L({session_id:I(),transcript_path:I(),cwd:I(),permission_mode:I(),hook_event_name:I(),tool_name:I(),tool_input:L({command:I(),description:we(I())})});async function Ee(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t=JSON.parse(e.join(``)),n=Te.safeParse(t);return n.success||(process.stderr.write(`[bst-hook] invalid hook input: ${n.error.message}\\n`),process.exit(0)),n.data}function R(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function De(){let e=i.dirname(s(import.meta.url));return i.join(e,`bst-hook.audit.local.jsonl`)}function Oe(t){try{if(e.statSync(t).size>1048576){let n=`${t}.1`;e.renameSync(t,n)}}catch{}}let ke=!1;function Ae(t){try{let n=De();ke||=(Oe(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 z(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]===`&`){B(t,n,`&&`),n=``,r+=2;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){B(t,n,`||`),n=``,r+=2;continue}if(o===`|`){B(t,n,`|`),n=``,r++;continue}if(o===`;`){B(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function B(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const V=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function Me(e,t){if(/[\\n\\r]/.test(t))return{safe:!1,reason:`unquoted newline in command`};for(let t of e){if(t===\"`\")return{safe:!1,reason:`backtick command substitution`};if(t.includes(`$(`))return{safe:!1,reason:`dollar command substitution`};if(t.includes(\"`\"))return{safe:!1,reason:`backtick command substitution`};if(t.includes(`<(`)||t.includes(`>(`)&&!V.has(t))return{safe:!1,reason:`process substitution`}}for(let t=0;t<e.length;t++){let n=e[t];if(!V.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 Ne=new Set([`status`,`log`,`diff`,`show`,`branch`,`remote`,`fetch`,`rev-parse`]),Pe=new Set([`push`,`add`,`commit`,`reset`,`merge`,`rebase`,`cherry-pick`,`checkout`,`switch`,`stash`,`tag`,`revert`,`clean`,`rm`,`mv`]),Fe=new Set([`-c`,`--exec-path`,`--pager`,`--upload-pack`,`--receive-pack`]),Ie=new Set([`-C`,`-c`,`--git-dir`,`--work-tree`]);function H(e){let t=e.slice(1);for(let e=0;e<t.length;e++){let n=t[e];if(Ie.has(n)){e++;continue}if(!n.startsWith(`-`))return n}}function Le(e){for(let t of e.slice(1))if(Fe.has(t)||t.startsWith(`--exec-path=`)||t.startsWith(`--pager=`)||t.startsWith(`--upload-pack=`)||t.startsWith(`--receive-pack=`))return!1;let t=H(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===`list`||t===void 0}return Ne.has(t)}function Re(e){let t=H(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];if(t===`list`||t===void 0)return!1}return Pe.has(t)}const U=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(`.`)),ze=new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`]);function Be(e){let t=e.slice(1),n;for(let e of t)if(!(e.startsWith(`-`)&&!U.has(e))){n=e;break}return n?ze.has(n)?!0:n===`install`||n===`i`?t.slice(t.indexOf(n)+1).every(e=>e.startsWith(`-`)):!1:!1}const Ve=new Set([`view`,`info`,`show`]);function He(e){let t=e[1];return t!==void 0&&Ve.has(t)}function Ue(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 W(e,t){for(let n of e.slice(1)){if(n.startsWith(`-`))continue;if(i.basename(n).startsWith(`.`)||n.startsWith(`~/`)||n===`~`)return!1;let e=i.resolve(t.cwd,n);if(e!==t.cwd&&!e.startsWith(t.cwd+i.sep))return!1}return!0}const We=new Set([`-exec`,`-execdir`,`-ok`,`-delete`]);function Ge(e){return!e.some(e=>We.has(e))}function Ke(e){return!e.includes(`-o`)&&!e.some(e=>e.startsWith(`--output`))}function qe(e){let t=e.findIndex(e=>i.basename(e)===`rm`);if(t===-1)return!1;let n=e.slice(t+1),r=n.filter(e=>e.startsWith(`-`)&&!e.startsWith(`--`)),a=n.filter(e=>e.startsWith(`--`)),o=r.some(e=>/[rR]/.test(e)),s=a.includes(`--recursive`);return o||s}function Je(e){return e.some(e=>e===`777`||e===`0777`)}const Ye=/(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;function G(e){return e.slice(1).some(e=>!e.startsWith(`-`)&&Ye.test(e))}function Xe(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 Ze(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 Qe=[{name:`rm-recursive`,match:/^rm$/,action:`deny`,when:e=>qe(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=>Xe(e),reason:\"Use `git push --force-with-lease` instead of `--force`.\"},{name:`git-reset-hard`,match:/^git$/,action:`deny`,when:e=>Ze(e),reason:\"Don't `reset --hard` on main. Create a branch first.\"},{name:`git-write`,match:/^git$/,action:`deny`,when:e=>Re(e),reason:`Git write operations (push, add, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.`},{name:`chmod-777`,match:/^chmod$/,action:`deny`,when:e=>Je(e),reason:`Don't use 777 permissions. Use more restrictive permissions.`},{name:`env-file-cat`,match:/^cat$/,action:`deny`,when:e=>G(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-head`,match:/^head$/,action:`deny`,when:e=>G(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-tail`,match:/^tail$/,action:`deny`,when:e=>G(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-less`,match:/^less$/,action:`deny`,when:e=>G(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-more`,match:/^more$/,action:`deny`,when:e=>G(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-grep`,match:/^grep$/,action:`deny`,when:e=>G(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.\"}],K=[{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=>He(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=>Ge(e)},{name:`sort`,match:/^sort$/,action:`allow`,when:e=>Ke(e)},{name:`git`,match:/^git$/,action:`allow`,when:e=>Le(e)},{name:`pnpm`,match:/^pnpm$/,action:`allow`,when:e=>Be(e)},{name:`cp`,match:/^cp$/,action:`allow`,when:(e,t)=>W(e,t)},{name:`mkdir`,match:/^mkdir$/,action:`allow`,when:(e,t)=>W(e,t)},{name:`touch`,match:/^touch$/,action:`allow`,when:(e,t)=>W(e,t)}],$e=L({scripts:Se(I(),ye())});function q(e,t){let n=Y(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=nt(t,n);if(!r)return null;let i=q(e,r);return i?{...i,pkgDir:r}:null}function tt(e,t){let n=J(t);if(!n)return null;let r=[];for(let t of n){let n=q(e,t);n&&r.push({...n,pkgDir:t})}return r}function J(e){let t=rt(e);if(!t)return null;let i=[];for(let s of t){if(!s.endsWith(`/*`))continue;let t=o(e,s.slice(0,-2)),c;try{c=n(t)}catch{continue}for(let e of c){let n=a(t,e);try{if(!r(n).isDirectory())continue}catch{continue}i.push(n)}}return i.length>0?i:null}function nt(e,t){let n=J(t);if(!n)return null;for(let t of n){let n=at(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function rt(e){try{let n=t(a(e,`pnpm-workspace.yaml`),`utf-8`).split(`\n`),r=n.findIndex(e=>/^packages\\s*:/.test(e));if(r===-1)return null;let i=[];for(let e=r+1;e<n.length;e++){let t=n[e],r=/^\\s+-\\s+(.+)$/.exec(t);if(!r)break;i.push(r[1].trim().replace(/['\"`]/g,``))}return i.length>0?i:null}catch{return null}}const it=L({name:I()});function at(e){try{let n=t(a(e,`package.json`),`utf-8`),r=it.safeParse(JSON.parse(n));return r.success?r.data.name:null}catch{return null}}function Y(e){try{let n=t(a(e,`package.json`),`utf-8`),r=$e.safeParse(JSON.parse(n));return r.success?r.data.scripts:null}catch{return null}}const ot=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`]),st=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function X(e){return ot.has(e)?!0:st.some(t=>e.startsWith(t))}function ct(e,t){let n=z(e);if(!n.ok||n.tokens.length!==2)return null;let r=n.tokens;if(r[0]!==`cd`)return null;let a=r[1];return i.isAbsolute(a)?a:i.resolve(t.cwd,a)}function Z(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=ct(e.command,o);t&&(o={...o,cwd:t})}let t=lt(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 lt(e,t){let n=e.trim();if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=z(e);if(!r.ok)return{action:`passthrough`,rule:null,reason:r.error};let a=r.tokens;if(a.length===0)return{action:`passthrough`,rule:null,reason:null};let o=0;for(;o<a.length&&/^\\w+=/.test(a[o]);){let e=a[o].split(`=`)[0];if(X(e))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${e}`};o++}if(o>=a.length)return{action:`allow`,rule:`env-only`,reason:null};a=a.slice(o);let s=Me(a,e);if(!s.safe)return{action:`passthrough`,rule:null,reason:s.reason};for(let e=0;e<a.length;e++){let t=a[e];if(!(t===`2>&1`||t===`2>/dev/null`||t===`>/dev/null`||t===`1>/dev/null`)){if(t===`>`||t===`>>`){if(a[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=i.basename(a[0]);if(c===`command`&&a.length>1)return Z(a.slice(1).join(` `),{...t,depth:t.depth+1});if(c===`env`){let e=1;for(;e<a.length&&/^\\w+=/.test(a[e]);){let t=a[e].split(`=`)[0];if(X(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=a.length?{action:`allow`,rule:`env-only`,reason:null}:Z(a.slice(e).join(` `),{...t,depth:t.depth+1})}if(c===`pnpm`)return ut(a,t);if(c===`npm`)return pt(a);if(c===`bash`||c===`sh`||c===`zsh`)return gt(a,t);if(c===`node`&&(a.includes(`-e`)||a.includes(`-p`)||a.includes(`--eval`)||a.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(c===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let e of Qe)if(e.match.test(c)&&(!e.when||e.when(a,t)))return{action:`deny`,rule:e.name,reason:e.reason??null};for(let e of K)if(e.match.test(c)&&(!e.when||e.when(a,t)))return{action:`allow`,rule:e.name,reason:null};return{action:`passthrough`,rule:null,reason:`no rule matched: ${c}`}}function ut(e,t){let n=e.slice(1),r,i=-1,a=!1;for(let o=0;o<n.length;o++){let s=n[o];if(s===`--filter`||s.startsWith(`--filter=`))return mt(e,n,o,t);if(s===`-r`||s===`--recursive`){a=!0;continue}if(!(s.startsWith(`-`)&&!U.has(s))){r=s,i=o;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`}:Z(e.join(` `),{...t,depth:t.depth+1})}let o;if(r===`run`){let e=n[i+1];if(!e||e.startsWith(`-`))return{action:`passthrough`,rule:null,reason:`pnpm run without script name`};o=e}else if(!U.has(r))o=r;else{for(let n of K)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}}return a?ft(o,t):dt(o,t)}function dt(e,t){let n=q(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r={...t,depth:t.depth+1};if(n.pre){let e=Z(n.pre,r);if(e.action!==`allow`)return e}let i=Z(n.main,r);if(i.action!==`allow`)return i;if(n.post){let e=Z(n.post,r);if(e.action!==`allow`)return e}return{action:`allow`,rule:`pnpm-script:${e}`,reason:null}}function ft(e,t){let n=tt(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n={...t,depth:t.depth+1,cwd:e.pkgDir??t.cwd};if(e.pre){let t=Z(e.pre,n);if(t.action!==`allow`)return t}let r=Z(e.main,n);if(r.action!==`allow`)return r;if(e.post){let t=Z(e.post,n);if(t.action!==`allow`)return t}}return{action:`allow`,rule:`pnpm-recursive:${e}`,reason:null}}function pt(e){for(let t of K)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:Ue(e)}}function mt(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(`-`)&&!U.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`}:Z(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(U.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={...r,depth:r.depth+1,cwd:l.pkgDir??r.cwd};if(l.pre){let e=Z(l.pre,u);if(e.action!==`allow`)return e}let d=Z(l.main,u);if(d.action!==`allow`)return d;if(l.post){let e=Z(l.post,u);if(e.action!==`allow`)return e}return{action:`allow`,rule:`pnpm-filter:${a}:${c}`,reason:null}}const ht=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function gt(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(`-`)||ht.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?Z(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 Q=await Ee();(Q.hook_event_name!==`PreToolUse`||Q.tool_name!==`Bash`)&&process.exit(0);const $=Z(Q.tool_input.command,{cwd:Q.cwd,depth:0});Ae({sessionId:Q.session_id,cwd:Q.cwd,command:Q.tool_input.command,decision:$.action,rule:$.rule,reason:$.reason}),$.action===`allow`?R(`allow`):$.action===`deny`&&R(`deny`,$.reason);export{};";
2394
+ const content = "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.3.0\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\";Object.freeze({status:`aborted`});function u(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 d=class extends Error{constructor(){super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`)}};const f={};function p(e){return e&&Object.assign(f,e),f}function ee(e,t){return typeof t==`bigint`?t.toString():t}function te(e){return{get value(){{let t=e();return Object.defineProperty(this,`value`,{value:t}),t}throw Error(`cached value already set`)}}}function ne(e){let t=e.startsWith(`^`)?1:0,n=e.endsWith(`$`)?e.length-1:e.length;return e.slice(t,n)}const re=Symbol(`evaluating`);function m(e,t,n){let r;Object.defineProperty(e,t,{get(){if(r!==re)return r===void 0&&(r=re,r=n()),r},set(n){Object.defineProperty(e,t,{value:n})},configurable:!0})}const h=`captureStackTrace`in Error?Error.captureStackTrace:(...e)=>{};function g(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function ie(e){if(g(e)===!1)return!1;let t=e.constructor;if(t===void 0||typeof t!=`function`)return!0;let n=t.prototype;return!(g(n)===!1||Object.prototype.hasOwnProperty.call(n,`isPrototypeOf`)===!1)}function ae(e,t,n){let r=new e._zod.constr(t??e._zod.def);return(!t||n?.parent)&&(r._zod.parent=e),r}function _(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 oe(e){return Object.keys(e).filter(t=>e[t]._zod.optin===`optional`&&e[t]._zod.optout===`optional`)}-Number.MAX_VALUE,Number.MAX_VALUE;function v(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 y(e,t){return t.map(t=>{var n;return(n=t).path??(n.path=[]),t.path.unshift(e),t})}function b(e){return typeof e==`string`?e:e?.message}function x(e,t,n){let r={...e,path:e.path??[]};return e.message||(r.message=b(e.inst?._zod.def?.error?.(e))??b(t?.error?.(e))??b(n.customError?.(e))??b(n.localeError?.(e))??`Invalid input`),delete r.inst,delete r.continue,t?.reportInput||delete r.input,r}const se=(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,ee,2),Object.defineProperty(e,`toString`,{value:()=>e.message,enumerable:!1})},ce=u(`$ZodError`,se),S=u(`$ZodError`,se,{Parent:Error}),le=(e=>(t,n,r,i)=>{let a=r?Object.assign(r,{async:!1}):{async:!1},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise)throw new d;if(o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>x(e,a,p())));throw h(t,i?.callee),t}return o.value})(S),ue=(e=>async(t,n,r,i)=>{let a=r?Object.assign(r,{async:!0}):{async:!0},o=t._zod.run({value:n,issues:[]},a);if(o instanceof Promise&&(o=await o),o.issues.length){let t=new(i?.Err??e)(o.issues.map(e=>x(e,a,p())));throw h(t,i?.callee),t}return o.value})(S),C=(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 d;return a.issues.length?{success:!1,error:new(e??ce)(a.issues.map(e=>x(e,i,p())))}:{success:!0,data:a.value}})(S),w=(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=>x(e,i,p())))}:{success:!0,data:a.value}})(S),de=e=>{let t=e?`[\\\\s\\\\S]{${e?.minimum??0},${e?.maximum??``}}`:`[\\\\s\\\\S]*`;return RegExp(`^${t}$`)},fe=/^-?\\d+(?:\\.\\d+)?$/,pe={major:4,minor:3,patch:6},T=u(`$ZodType`,(e,t)=>{var n;e??={},e._zod.def=t,e._zod.bag=e._zod.bag||{},e._zod.version=pe;let r=[...e._zod.def.checks??[]];e._zod.traits.has(`$ZodCheck`)&&r.unshift(e);for(let t of r)for(let n of t._zod.onattach)n(e);if(r.length===0)(n=e._zod).deferred??(n.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{let t=(e,t,n)=>{let r=v(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 d;if(i||o instanceof Promise)i=(i??Promise.resolve()).then(async()=>{await o,e.issues.length!==t&&(r||=v(e,t))});else{if(e.issues.length===t)continue;r||=v(e,t)}}return i?i.then(()=>e):e},n=(n,i,a)=>{if(v(n))return n.aborted=!0,n;let o=t(i,r,a);if(o instanceof Promise){if(a.async===!1)throw new d;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 d;return o.then(e=>t(e,r,a))}return t(o,r,a)}}m(e,`~standard`,()=>({validate:t=>{try{let n=C(e,t);return n.success?{value:n.data}:{issues:n.error?.issues}}catch{return w(e,t).then(e=>e.success?{value:e.data}:{issues:e.error?.issues})}},vendor:`zod`,version:1}))}),me=u(`$ZodString`,(e,t)=>{T.init(e,t),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??de(e._zod.bag),e._zod.parse=(n,r)=>{if(t.coerce)try{n.value=String(n.value)}catch{}return typeof n.value==`string`||n.issues.push({expected:`string`,code:`invalid_type`,input:n.value,inst:e}),n}}),he=u(`$ZodUnknown`,(e,t)=>{T.init(e,t),e._zod.parse=e=>e});function E(e,t,n,r,i){if(e.issues.length){if(i&&!(n in r))return;t.issues.push(...y(n,e.issues))}e.value===void 0?n in r&&(t.value[n]=void 0):t.value[n]=e.value}function ge(e){let t=Object.keys(e.shape);for(let n of t)if(!e.shape?.[n]?._zod?.traits?.has(`$ZodType`))throw Error(`Invalid element at key \"${n}\": expected a Zod schema`);let n=oe(e.shape);return{...e,keys:t,keySet:new Set(t),numKeys:t.length,optionalKeys:new Set(n)}}function _e(e,t,n,r,i,a){let o=[],s=i.keySet,c=i.catchall._zod,l=c.def.type,u=c.optout===`optional`;for(let i in t){if(s.has(i))continue;if(l===`never`){o.push(i);continue}let a=c.run({value:t[i],issues:[]},r);a instanceof Promise?e.push(a.then(e=>E(e,n,i,t,u))):E(a,n,i,t,u)}return o.length&&n.issues.push({code:`unrecognized_keys`,keys:o,input:t,inst:a}),e.length?Promise.all(e).then(()=>n):n}const ve=u(`$ZodObject`,(e,t)=>{if(T.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=te(()=>ge(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=g,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=>E(n,t,e,s,r))):E(i,t,e,s,r)}return i?_e(c,s,t,o,n.value,e):c.length?Promise.all(c).then(()=>t):t}}),ye=u(`$ZodRecord`,(e,t)=>{T.init(e,t),e._zod.parse=(n,r)=>{let i=n.value;if(!ie(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(...y(e,t.issues)),n.value[e]=t.value})):(o.issues.length&&n.issues.push(...y(e,o.issues)),n.value[e]=o.value)}let c;for(let e in i)s.has(e)||(c??=[],c.push(e));c&&c.length>0&&n.issues.push({code:`unrecognized_keys`,input:i,inst:e,keys:c})}else{n.value={};for(let o of Reflect.ownKeys(i)){if(o===`__proto__`)continue;let s=t.keyType._zod.run({value:o,issues:[]},r);if(s instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);if(typeof o==`string`&&fe.test(o)&&s.issues.length){let e=t.keyType._zod.run({value:Number(o),issues:[]},r);if(e instanceof Promise)throw Error(`Async schemas not supported in object keys currently`);e.issues.length===0&&(s=e)}if(s.issues.length){t.mode===`loose`?n.value[o]=i[o]:n.issues.push({code:`invalid_key`,origin:`record`,issues:s.issues.map(e=>x(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(...y(o,e.issues)),n.value[s.value]=e.value})):(c.issues.length&&n.issues.push(...y(o,c.issues)),n.value[s.value]=c.value)}}return a.length?Promise.all(a).then(()=>n):n}});function D(e,t){return e.issues.length&&t===void 0?{issues:[],value:void 0}:e}const be=u(`$ZodOptional`,(e,t)=>{T.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(`^(${ne(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=>D(t,e.value)):D(r,e.value)}return e.value===void 0?e:t.innerType._zod.run(e,n)}});var O,xe=class{constructor(){this._map=new WeakMap,this._idmap=new Map}add(e,...t){let n=t[0];return this._map.set(e,n),n&&typeof n==`object`&&`id`in n&&this._idmap.set(n.id,e),this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(e){let t=this._map.get(e);return t&&typeof t==`object`&&`id`in t&&this._idmap.delete(t.id),this._map.delete(e),this}get(e){let t=e._zod.parent;if(t){let n={...this.get(t)??{}};delete n.id;let r={...n,...this._map.get(e)};return Object.keys(r).length?r:void 0}return this._map.get(e)}has(e){return this._map.has(e)}};function Se(){return new xe}(O=globalThis).__zod_globalRegistry??(O.__zod_globalRegistry=Se()),globalThis.__zod_globalRegistry;function Ce(e,t){return new e({type:`string`,..._(t)})}function we(e){return new e({type:`unknown`})}const k=u(`ZodMiniType`,(e,t)=>{if(!e._zod)throw Error(`Uninitialized schema in ZodMiniType.`);T.init(e,t),e.def=t,e.type=t.type,e.parse=(t,n)=>le(e,t,n,{callee:e.parse}),e.safeParse=(t,n)=>C(e,t,n),e.parseAsync=async(t,n)=>ue(e,t,n,{callee:e.parseAsync}),e.safeParseAsync=async(t,n)=>w(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)=>ae(e,t,n),e.brand=()=>e,e.register=((t,n)=>(t.add(e,n),e)),e.apply=t=>t(e)}),Te=u(`ZodMiniString`,(e,t)=>{me.init(e,t),k.init(e,t)});function A(e){return Ce(Te,e)}const Ee=u(`ZodMiniUnknown`,(e,t)=>{he.init(e,t),k.init(e,t)});function j(){return we(Ee)}const De=u(`ZodMiniObject`,(e,t)=>{ve.init(e,t),k.init(e,t),m(e,`shape`,()=>t.shape)});function M(e,t){return new De({type:`object`,shape:e??{},..._(t)})}const Oe=u(`ZodMiniRecord`,(e,t)=>{ye.init(e,t),k.init(e,t)});function N(e,t,n){return new Oe({type:`record`,keyType:e,valueType:t,..._(n)})}const ke=u(`ZodMiniOptional`,(e,t)=>{be.init(e,t),k.init(e,t)});function P(e){return new ke({type:`optional`,innerType:e})}const Ae=M({session_id:A(),transcript_path:A(),cwd:A(),permission_mode:A(),hook_event_name:A(),tool_name:A(),tool_input:N(A(),j())});async function je(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t=JSON.parse(e.join(``)),n=Ae.safeParse(t);return n.success||(process.stderr.write(`[bst-hook] invalid hook input: ${n.error.message}\\n`),process.exit(0)),n.data}const Me=M({command:A(),description:P(A())}),Ne=M({file_path:A()}),Pe=M({pattern:A(),path:P(A())});function Fe(e){let t=Me.safeParse(e);return t.success?t.data:null}function Ie(e){let t=Ne.safeParse(e);return t.success?t.data:null}function Le(e){let t=Pe.safeParse(e);return t.success?t.data:null}function F(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function Re(){let e=a.dirname(l(import.meta.url));return a.join(e,`bst-hook.audit.local.jsonl`)}function ze(t){try{if(e.statSync(t).size>1048576){let n=`${t}.1`;e.renameSync(t,n)}}catch{}}let Be=!1;function Ve(t){try{let n=Re();Be||=(ze(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 I(e){let t=[],n=``,r=0,i=!1;for(;r<e.length;){let a=e[r];if(a===`'`){for(i=!0,r++;r<e.length&&e[r]!==`'`;)n+=e[r],r++;if(r>=e.length)return{ok:!1,error:`unterminated single quote`};r++}else if(a===`\"`){for(i=!0,r++;r<e.length&&e[r]!==`\"`;){if(e[r]===`\\\\`&&r+1<e.length){let t=e[r+1];if(t===`\"`||t===`\\\\`||t===`$`||t===\"`\"){n+=t,r+=2;continue}}n+=e[r],r++}if(r>=e.length)return{ok:!1,error:`unterminated double quote`};r++}else a===`\\\\`&&r+1<e.length?(i=!0,n+=e[r+1],r+=2):a===` `||a===` `?(i&&=(t.push(n),n=``,!1),r++):(i=!0,n+=a,r++)}return i&&t.push(n),{ok:!0,tokens:t}}function He(e){if(!e||!e.trim())return[];let t=[],n=``,r=0,i=!1,a=!1;for(;r<e.length;){let o=e[r];if(o===`'`&&!a){i=!i,n+=o,r++;continue}if(o===`\"`&&!i){a=!a,n+=o,r++;continue}if(o===`\\\\`&&!i&&r+1<e.length){n+=o+e[r+1],r+=2;continue}if(!i&&!a){if(o===`&`&&r+1<e.length&&e[r+1]===`&`){L(t,n,`&&`),n=``,r+=2;continue}if(o===`&`&&!(r+1<e.length&&e[r+1]===`>`)&&!(r>0&&(e[r-1]===`>`||e[r-1]===`<`))){L(t,n,`&`),n=``,r++;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){L(t,n,`||`),n=``,r+=2;continue}if(o===`|`){L(t,n,`|`),n=``,r++;continue}if(o===`;`){L(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function L(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const R=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function Ue(e,t){if(/[\\n\\r]/.test(t))return{safe:!1,reason:`unquoted newline in command`};for(let t of e){if(t===\"`\")return{safe:!1,reason:`backtick command substitution`};if(t.includes(`$(`))return{safe:!1,reason:`dollar command substitution`};if(t.includes(\"`\"))return{safe:!1,reason:`backtick command substitution`};if(t.includes(`<(`)||t.includes(`>(`)&&!R.has(t))return{safe:!1,reason:`process substitution`}}for(let t=0;t<e.length;t++){let n=e[t];if(!R.has(n)){if(n.includes(`<`)&&!n.includes(`<(`))return{safe:!1,reason:`input redirect`};if(n.includes(`>`)){if(n.endsWith(`>/dev/null`)||n.match(/^(&>>|&>|>>|>|1>|2>)$/)&&e[t+1]===`/dev/null`)continue;return{safe:!1,reason:`output redirect to file`}}}}return{safe:!0}}const We=[`.sample`,`.example`,`.template`],Ge=/(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;function Ke(e){return We.some(t=>e.endsWith(t))}function z(e){return Ke(a.basename(e))?!1:Ge.test(e)}function qe(e){return e.slice(1).some(e=>!e.startsWith(`-`)&&z(e))}const Je=new Set([`status`,`log`,`diff`,`show`,`branch`,`remote`,`fetch`,`rev-parse`]),Ye=new Set([`push`,`add`,`commit`,`reset`,`merge`,`rebase`,`cherry-pick`,`checkout`,`switch`,`stash`,`tag`,`revert`,`clean`,`rm`,`mv`]),Xe=new Set([`-c`,`--exec-path`,`--pager`,`--upload-pack`,`--receive-pack`]),Ze=new Set([`-C`,`-c`,`--git-dir`,`--work-tree`]);function B(e){let t=e.slice(1);for(let e=0;e<t.length;e++){let n=t[e];if(Ze.has(n)){e++;continue}if(!n.startsWith(`-`))return n}}function Qe(e){for(let t of e.slice(1))if(Xe.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=B(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===`list`||t===void 0}return Je.has(t)}function $e(e){let t=B(e);if(!t)return!1;if(t===`stash`){let t=e[e.indexOf(`stash`)+1];if(t===`list`||t===void 0)return!1}return Ye.has(t)}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(`.`)),et=new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`]);function tt(e){let t=e.slice(1),n;for(let e of t)if(!(e.startsWith(`-`)&&!V.has(e))){n=e;break}return n?et.has(n)?!0:n===`install`||n===`i`?t.slice(t.indexOf(n)+1).every(e=>e.startsWith(`-`)):!1:!1}const nt=new Set([`view`,`info`,`show`]);function rt(e){let t=e[1];return t!==void 0&&nt.has(t)}function it(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 at=new Set([`-exec`,`-execdir`,`-ok`,`-okdir`,`-delete`,`-fprint`,`-fprint0`,`-fprintf`,`-fls`]);function ot(e){return!e.some(e=>at.has(e))}function st(e){return!e.includes(`-o`)&&!e.some(e=>e.startsWith(`--output`))}function ct(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 lt(e){return e.some(e=>e===`777`||e===`0777`)}function ut(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 dt(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 ft=[{name:`rm-recursive`,match:/^rm$/,action:`deny`,when:e=>ct(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=>ut(e),reason:\"Use `git push --force-with-lease` instead of `--force`.\"},{name:`git-reset-hard`,match:/^git$/,action:`deny`,when:e=>dt(e),reason:\"Don't `reset --hard` on main. Create a branch first.\"},{name:`git-write`,match:/^git$/,action:`deny`,when:e=>$e(e),reason:`Git write operations (push, add, commit, reset, merge, etc.) are not allowed. Ask the user to run these manually.`},{name:`chmod-777`,match:/^chmod$/,action:`deny`,when:e=>lt(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=>qe(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=>rt(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=>ot(e)},{name:`sort`,match:/^sort$/,action:`allow`,when:e=>st(e)},{name:`git`,match:/^git$/,action:`allow`,when:e=>Qe(e)},{name:`pnpm`,match:/^pnpm$/,action:`allow`,when:e=>tt(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)}],pt=M({scripts:N(A(),j())});function W(e,t){let n=xt(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 mt(e,t,n){let r=_t(t,n);if(!r)return null;let i=W(e,r);return i?{...i,pkgDir:r}:null}function ht(e,t){let n=G(t);if(!n)return null;let r=[];for(let t of n){let n=W(e,t);n&&r.push({...n,pkgDir:t})}return r}function gt(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 G(e){let t=vt(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 _t(e,t){let n=G(t);if(!n)return null;for(let t of n){let n=bt(t);if(n===e||n===`@${e}`||t.split(`/`).pop()===e||n&&(n.startsWith(`@`)?n.split(`/`)[1]:n)===e)return t}return null}function vt(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 yt=M({name:A()});function bt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=yt.safeParse(JSON.parse(t));return r.success?r.data.name:null}catch{return null}}function xt(e){try{let t=n(s(e,`package.json`),`utf-8`),r=pt.safeParse(JSON.parse(t));return r.success?r.data.scripts:null}catch{return null}}const St=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`]),Ct=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function K(e){return St.has(e)?!0:Ct.some(t=>e.startsWith(t))}function wt(e,t){let n=I(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 q(e,t){if(t.depth>=5)return{action:`passthrough`,rule:null,reason:`max evaluation depth exceeded`};let n=e.trim();if(!n)return{action:`passthrough`,rule:null,reason:null};if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=He(n);if(r.length===0)return{action:`passthrough`,rule:null,reason:null};let i=null,a=null,o=t;for(let e of r){if(e.operator===`&&`){let t=wt(e.command,o);t&&(o={...o,cwd:t})}let t=Tt(e.command,o);if(t.action===`deny`)return t;t.action===`passthrough`&&(i||=t),t.action===`allow`&&(a=t)}return i||(r.length===1&&a?a:{action:`allow`,rule:`all-segments`,reason:null})}function J(e,t){if(e.pre){let n=q(e.pre,t);if(n.action!==`allow`)return n}let n=q(e.main,t);if(n.action!==`allow`)return n;if(e.post){let n=q(e.post,t);if(n.action!==`allow`)return n}return`allow`}function Tt(e,t){let n=e.trim();if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=I(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(K(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=Ue(i,e);if(!s.safe)return{action:`passthrough`,rule:null,reason:s.reason};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=a.basename(i[0]);if(c===`command`&&i.length>1)return q(i.slice(1).join(` `),{...t,depth:t.depth+1});if(c===`env`){let e=1;for(;e<i.length&&/^\\w+=/.test(i[e]);){let t=i[e].split(`=`)[0];if(K(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=i.length?{action:`allow`,rule:`env-only`,reason:null}:q(i.slice(e).join(` `),{...t,depth:t.depth+1})}if(c===`pnpm`)return Et(i,t);if(c===`npm`)return Ot(i);if(c===`bash`||c===`sh`||c===`zsh`)return jt(i,t);if(c===`node`&&(i.includes(`-e`)||i.includes(`-p`)||i.includes(`--eval`)||i.includes(`--print`)))return{action:`passthrough`,rule:null,reason:`node eval`};if(c===`mv`)return{action:`passthrough`,rule:null,reason:`mv`};for(let e of ft)if(e.match.test(c)&&(!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(c)&&(!e.when||e.when(i,t)))return{action:`allow`,rule:e.name,reason:null};return{action:`passthrough`,rule:null,reason:`no rule matched: ${c}`}}function Et(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 kt(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`}:q(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 Dt(l,t);if(s){let e=gt(t.cwd);return e?Y(l,{...t,cwd:e}):{action:`passthrough`,rule:null,reason:`workspace root not found`}}if(c){let e=a.resolve(t.cwd,c);return Y(l,{...t,cwd:e})}return Y(l,t)}function Y(e,t){let n=W(e,t.cwd);if(!n)return{action:`passthrough`,rule:null,reason:`script resolution failed`};let r=J(n,{...t,depth:t.depth+1});return r===`allow`?{action:`allow`,rule:`pnpm-script:${e}`,reason:null}:r}function Dt(e,t){let n=ht(e,t.cwd);if(!n||n.length===0)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};for(let e of n){let n=J(e,{...t,depth:t.depth+1,cwd:e.pkgDir??t.cwd});if(n!==`allow`)return n}return{action:`allow`,rule:`pnpm-recursive:${e}`,reason:null}}function Ot(e){for(let 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:it(e)}}function kt(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`}:q(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=mt(c,a,r.cwd);if(!l)return{action:`passthrough`,rule:null,reason:`filtered script resolution failed`};let u=J(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 At=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function jt(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(`-`)||At.has(r))return{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}}let r=e[n+1];return r?e.length>n+2?{action:`passthrough`,rule:null,reason:`shell -c with positional args`}:n===1?q(r,{...t,depth:t.depth+1}):{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}:{action:`passthrough`,rule:null,reason:`shell -c without command`}}const Mt=new Set([`Read`,`Glob`,`Grep`]),X=new Set([`Write`,`Edit`]),Nt=[{name:`env-file-read`,tools:Mt,action:`deny`,when:e=>z(e),reason:`Don't read .env files. These contain secrets that should not appear in the conversation.`},{name:`env-file-write`,tools:X,action:`deny`,when:e=>z(e),reason:`Don't write to .env files. These contain secrets.`},{name:`outside-cwd-write`,tools:X,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.`}],Pt=[{name:`cwd-read`,tools:Mt,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:X,action:`allow`,when:(e,t)=>{let n=a.resolve(t.cwd,e);return n===t.cwd||n.startsWith(t.cwd+a.sep)}}],Ft=new Set([`Read`,`Write`,`Edit`]),It=new Set([`Glob`,`Grep`]);function Lt(e,t,n){return Ft.has(e)?Ie(t)?.file_path??null:It.has(e)?Le(t)?.path??n:null}function Rt(e,t,n){let r=Lt(e,t,n.cwd);if(r===null)return{action:`passthrough`,rule:null,reason:`unknown tool input shape`};for(let t of Nt)if(t.tools.has(e)&&t.when(r,n))return{action:`deny`,rule:t.name,reason:t.reason??null};for(let t of Pt)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 Z=await je();Z.hook_event_name!==`PreToolUse`&&process.exit(0),new Set([`Bash`,`Read`,`Write`,`Edit`,`Glob`,`Grep`]).has(Z.tool_name)||process.exit(0);let Q,$;if(Z.tool_name===`Bash`){let e=Fe(Z.tool_input);e||process.exit(0),Q=q(e.command,{cwd:Z.cwd,depth:0}),$=e.command}else{let e=process.env.CLAUDE_PROJECT_DIR??Z.cwd;Q=Rt(Z.tool_name,Z.tool_input,{cwd:e});let t=String(Z.tool_input.file_path??Z.tool_input.path??``);$=`${Z.tool_name} ${t}`}Ve({sessionId:Z.session_id,cwd:Z.cwd,toolName:Z.tool_name,command:$,decision:Q.action,rule:Q.rule,reason:Q.reason}),Q.action===`allow`?F(`allow`):Q.action===`deny`&&F(`deny`,Q.reason);export{};";
2410
2395
  const existing = ctx.read(HOOK_PATH);
2411
2396
  if (existing === content) return {
2412
2397
  filePath: HOOK_PATH,
@@ -2464,6 +2449,7 @@ function buildSettings(ctx) {
2464
2449
  if (ctx.config.structure !== "monorepo") {
2465
2450
  if (ctx.packageJson ? packageHasWebUIDeps(ctx.packageJson) : false) enabledPlugins["frontend-design@claude-plugins-official"] = true;
2466
2451
  }
2452
+ const hookCommand = `node "$CLAUDE_PROJECT_DIR/.claude/hooks/bst-hook.mjs"`;
2467
2453
  return {
2468
2454
  permissions: {
2469
2455
  allow,
@@ -2474,13 +2460,50 @@ function buildSettings(ctx) {
2474
2460
  "Read(*.env.*)"
2475
2461
  ]
2476
2462
  },
2477
- hooks: { PreToolUse: [{
2478
- matcher: "Bash",
2479
- hooks: [{
2480
- type: "command",
2481
- command: `node "$CLAUDE_PROJECT_DIR/.claude/hooks/bst-hook.mjs"`
2482
- }]
2483
- }] },
2463
+ hooks: { PreToolUse: [
2464
+ {
2465
+ matcher: "Bash",
2466
+ hooks: [{
2467
+ type: "command",
2468
+ command: hookCommand
2469
+ }]
2470
+ },
2471
+ {
2472
+ matcher: "Read",
2473
+ hooks: [{
2474
+ type: "command",
2475
+ command: hookCommand
2476
+ }]
2477
+ },
2478
+ {
2479
+ matcher: "Write",
2480
+ hooks: [{
2481
+ type: "command",
2482
+ command: hookCommand
2483
+ }]
2484
+ },
2485
+ {
2486
+ matcher: "Edit",
2487
+ hooks: [{
2488
+ type: "command",
2489
+ command: hookCommand
2490
+ }]
2491
+ },
2492
+ {
2493
+ matcher: "Glob",
2494
+ hooks: [{
2495
+ type: "command",
2496
+ command: hookCommand
2497
+ }]
2498
+ },
2499
+ {
2500
+ matcher: "Grep",
2501
+ hooks: [{
2502
+ type: "command",
2503
+ command: hookCommand
2504
+ }]
2505
+ }
2506
+ ] },
2484
2507
  instructions: [
2485
2508
  "Use pnpm, not npm/yarn/npx. Run binaries with `pnpm exec`.",
2486
2509
  "No typecasts (as/any). Use zod schemas, type guards, or narrowing instead.",
@@ -2492,9 +2515,9 @@ function buildSettings(ctx) {
2492
2515
  };
2493
2516
  }
2494
2517
  const BST_HOOK_MARKER = "bst-hook.mjs";
2495
- /** Check if a hook entry list already contains the bst-hook command. */
2496
- function hasBstHook(matchers) {
2497
- return matchers.some((m) => m.matcher === "Bash" && m.hooks.some((h) => h.command.includes(BST_HOOK_MARKER)));
2518
+ /** Check if a specific matcher already has the bst-hook command. */
2519
+ function matcherHasBstHook(matchers, matcherName) {
2520
+ return matchers.some((m) => m.matcher === matcherName && m.hooks.some((h) => h.command.includes(BST_HOOK_MARKER)));
2498
2521
  }
2499
2522
  /** Remove enabledPlugins/extraKnownMarketplaces/hooks when empty to keep the file clean. */
2500
2523
  function serializeSettings$1(settings) {
@@ -2530,9 +2553,10 @@ function writeOrMergeSettings(ctx, filePath, generated) {
2530
2553
  const mergedHooks = { ...settings.hooks };
2531
2554
  for (const [event, generatedMatchers] of Object.entries(generated.hooks)) {
2532
2555
  const existingMatchers = mergedHooks[event] ?? [];
2533
- if (!hasBstHook(existingMatchers)) {
2534
- mergedHooks[event] = [...existingMatchers, ...generatedMatchers];
2535
- hookChanges++;
2556
+ const missingMatchers = generatedMatchers.filter((gm) => !matcherHasBstHook(existingMatchers, gm.matcher));
2557
+ if (missingMatchers.length > 0) {
2558
+ mergedHooks[event] = [...existingMatchers, ...missingMatchers];
2559
+ hookChanges += missingMatchers.length;
2536
2560
  }
2537
2561
  }
2538
2562
  const missingPlugins = Object.entries(generated.enabledPlugins).filter(([key]) => !(key in settings.enabledPlugins));
@@ -3205,7 +3229,7 @@ async function runGenerators(ctx) {
3205
3229
  function generateMigratePrompt(results, config, detected) {
3206
3230
  const sections = [];
3207
3231
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3208
- sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.40.0 repo:sync\` on ${timestamp}_\n`);
3232
+ sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.40.2 repo:sync\` on ${timestamp}_\n`);
3209
3233
  const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
3210
3234
  if (changed.length > 0) {
3211
3235
  const items = changed.map((r) => {
@@ -3602,7 +3626,7 @@ function createRealExecutor() {
3602
3626
  const pkgPath = path.join(packagesDir, entry, "package.json");
3603
3627
  try {
3604
3628
  const pkg = parsePackageJson(readFileSync(pkgPath, "utf-8"));
3605
- if (pkg?.name && pkg.version && !pkg.private) packages.push({
3629
+ if (pkg?.name && pkg.version) packages.push({
3606
3630
  name: pkg.name,
3607
3631
  version: pkg.version,
3608
3632
  dir: entry
@@ -5217,6 +5241,16 @@ const dockerCheckCommand = defineCommand({
5217
5241
  });
5218
5242
  //#endregion
5219
5243
  //#region src/commands/hooks-audit.ts
5244
+ /** Walk up from `cwd` to find the nearest directory containing `.claude/`. */
5245
+ function findProjectRoot(cwd) {
5246
+ let dir = path.resolve(cwd);
5247
+ while (true) {
5248
+ if (existsSync(path.join(dir, ".claude"))) return dir;
5249
+ const parent = path.dirname(dir);
5250
+ if (parent === dir) return cwd;
5251
+ dir = parent;
5252
+ }
5253
+ }
5220
5254
  const AuditEntrySchema = z.object({
5221
5255
  timestamp: z.string(),
5222
5256
  sessionId: z.string(),
@@ -5290,7 +5324,7 @@ const hooksAuditCommand = defineCommand({
5290
5324
  }
5291
5325
  },
5292
5326
  async run({ args }) {
5293
- const targetDir = path.resolve(args.dir ?? ".");
5327
+ const targetDir = args.dir ? path.resolve(args.dir) : findProjectRoot(process.cwd());
5294
5328
  let entries = parseAuditLog(path.join(targetDir, ".claude/hooks/bst-hook.audit.local.jsonl"));
5295
5329
  if (entries.length === 0) {
5296
5330
  write("No audit log entries found.");
@@ -5331,7 +5365,7 @@ const hooksAuditCommand = defineCommand({
5331
5365
  const main = defineCommand({
5332
5366
  meta: {
5333
5367
  name: "bst",
5334
- version: "0.40.0",
5368
+ version: "0.40.2",
5335
5369
  description: "Bootstrap and maintain standardized TypeScript project tooling"
5336
5370
  },
5337
5371
  subCommands: {
@@ -5348,7 +5382,7 @@ const main = defineCommand({
5348
5382
  "hooks:audit": hooksAuditCommand
5349
5383
  }
5350
5384
  });
5351
- console.log(`@bensandee/tooling v0.40.0`);
5385
+ console.log(`@bensandee/tooling v0.40.2`);
5352
5386
  async function run() {
5353
5387
  await runMain(main);
5354
5388
  process.exit(process.exitCode ?? 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bensandee/tooling",
3
- "version": "0.40.0",
3
+ "version": "0.40.2",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -61,7 +61,7 @@
61
61
  "typescript": "5.9.3",
62
62
  "vitest": "4.1.0",
63
63
  "@bensandee/config": "0.10.0",
64
- "@bensandee/claude-hooks": "0.2.0"
64
+ "@bensandee/claude-hooks": "0.3.0"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsdown",