@bensandee/tooling 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.mjs +200 -93
- package/package.json +3 -2
package/dist/bin.mjs
CHANGED
|
@@ -1563,9 +1563,9 @@ async function generatePackageJson(ctx) {
|
|
|
1563
1563
|
}
|
|
1564
1564
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
1565
1565
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
1566
|
-
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.
|
|
1567
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
1568
|
-
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.
|
|
1566
|
+
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.9.3";
|
|
1567
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.39.0";
|
|
1568
|
+
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.11.0";
|
|
1569
1569
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = {
|
|
1570
1570
|
"@changesets/cli": "2.30.0",
|
|
1571
1571
|
"@release-it/bumper": "7.0.5",
|
|
@@ -1932,6 +1932,7 @@ import { defineConfig } from "oxlint";
|
|
|
1932
1932
|
|
|
1933
1933
|
export default defineConfig({
|
|
1934
1934
|
extends: [recommended],
|
|
1935
|
+
ignorePatterns: [".claude/hooks/bst-hook.mjs"],
|
|
1935
1936
|
});
|
|
1936
1937
|
`;
|
|
1937
1938
|
const CONFIG_PRESET_ONLY = `import { presetRules } from "@bensandee/config/oxlint";
|
|
@@ -1939,6 +1940,7 @@ import { defineConfig } from "oxlint";
|
|
|
1939
1940
|
|
|
1940
1941
|
export default defineConfig({
|
|
1941
1942
|
rules: presetRules,
|
|
1943
|
+
ignorePatterns: [".claude/hooks/bst-hook.mjs"],
|
|
1942
1944
|
});
|
|
1943
1945
|
`;
|
|
1944
1946
|
async function generateOxlint(ctx) {
|
|
@@ -1966,7 +1968,10 @@ async function generateOxlint(ctx) {
|
|
|
1966
1968
|
}
|
|
1967
1969
|
//#endregion
|
|
1968
1970
|
//#region src/generators/formatter.ts
|
|
1969
|
-
const OXFMT_CONFIG = `{
|
|
1971
|
+
const OXFMT_CONFIG = `{
|
|
1972
|
+
"ignorePatterns": [".claude"]
|
|
1973
|
+
}
|
|
1974
|
+
`;
|
|
1970
1975
|
const PRETTIER_CONFIG = `{
|
|
1971
1976
|
"semi": true,
|
|
1972
1977
|
"singleQuote": false,
|
|
@@ -2055,7 +2060,11 @@ const REQUIRED_ENTRIES = [
|
|
|
2055
2060
|
"!.env.example"
|
|
2056
2061
|
];
|
|
2057
2062
|
/** Tooling-specific entries added during init/update but not required for repo:sync --check. */
|
|
2058
|
-
const OPTIONAL_ENTRIES = [
|
|
2063
|
+
const OPTIONAL_ENTRIES = [
|
|
2064
|
+
".tooling-migrate.md",
|
|
2065
|
+
".tooling-archived/",
|
|
2066
|
+
".claude/hooks/bst-hook.audit.local.jsonl"
|
|
2067
|
+
];
|
|
2059
2068
|
const ALL_ENTRIES = [...REQUIRED_ENTRIES, ...OPTIONAL_ENTRIES];
|
|
2060
2069
|
/** Normalize a gitignore entry for comparison: strip leading `/` and trailing `/`. */
|
|
2061
2070
|
function normalizeEntry(entry) {
|
|
@@ -2214,12 +2223,13 @@ const KNIP_CONFIG_SINGLE = `import type { KnipConfig } from "knip";
|
|
|
2214
2223
|
export default {
|
|
2215
2224
|
entry: ["src/index.ts"],
|
|
2216
2225
|
project: ["src/**/*.ts"],
|
|
2217
|
-
ignore: ["dist/**"],
|
|
2226
|
+
ignore: ["dist/**", ".claude/hooks/**"],
|
|
2218
2227
|
} satisfies KnipConfig;
|
|
2219
2228
|
`;
|
|
2220
2229
|
const KNIP_CONFIG_MONOREPO = `import type { KnipConfig } from "knip";
|
|
2221
2230
|
|
|
2222
2231
|
export default {
|
|
2232
|
+
ignore: [".claude/hooks/**"],
|
|
2223
2233
|
workspaces: {
|
|
2224
2234
|
".": {
|
|
2225
2235
|
entry: [],
|
|
@@ -2350,7 +2360,33 @@ async function generatePnpmWorkspace(ctx) {
|
|
|
2350
2360
|
};
|
|
2351
2361
|
}
|
|
2352
2362
|
//#endregion
|
|
2363
|
+
//#region src/generators/claude-hooks.ts
|
|
2364
|
+
const HOOK_PATH = ".claude/hooks/bst-hook.mjs";
|
|
2365
|
+
async function generateClaudeHooks(ctx) {
|
|
2366
|
+
const content = "/**\n * bst-hook — Claude Code PreToolUse hook\n * @bensandee/claude-hooks v0.1.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,j=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 M(){return new j}(A=globalThis).__zod_globalRegistry??(A.__zod_globalRegistry=M()),globalThis.__zod_globalRegistry;function N(e,t){return new e({type:`string`,...g(t)})}function P(e){return new e({type:`unknown`})}const F=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)}),I=c(`ZodMiniString`,(e,t)=>{ue.init(e,t),F.init(e,t)});function L(e){return N(I,e)}const _e=c(`ZodMiniUnknown`,(e,t)=>{de.init(e,t),F.init(e,t)});function ve(){return P(_e)}const ye=c(`ZodMiniObject`,(e,t)=>{me.init(e,t),F.init(e,t),p(e,`shape`,()=>t.shape)});function R(e,t){return new ye({type:`object`,shape:e??{},...g(t)})}const be=c(`ZodMiniRecord`,(e,t)=>{he.init(e,t),F.init(e,t)});function xe(e,t,n){return new be({type:`record`,keyType:e,valueType:t,...g(n)})}const Se=c(`ZodMiniOptional`,(e,t)=>{ge.init(e,t),F.init(e,t)});function Ce(e){return new Se({type:`optional`,innerType:e})}const we=R({session_id:L(),transcript_path:L(),cwd:L(),permission_mode:L(),hook_event_name:L(),tool_name:L(),tool_input:R({command:L(),description:Ce(L())})});async function Te(){let e=[];for await(let t of process.stdin)e.push(typeof t==`string`?t:String(t));let t=JSON.parse(e.join(``)),n=we.safeParse(t);return n.success||(process.stderr.write(`[bst-hook] invalid hook input: ${n.error.message}\\n`),process.exit(0)),n.data}function z(e,t){let n={hookSpecificOutput:{hookEventName:`PreToolUse`,permissionDecision:e,...e===`deny`&&t?{permissionDecisionReason:t}:{}}};process.stdout.write(JSON.stringify(n))}function Ee(){let e=i.dirname(s(import.meta.url));return i.join(e,`bst-hook.audit.local.jsonl`)}function De(t){try{if(e.statSync(t).size>1048576){let n=`${t}.1`;e.renameSync(t,n)}}catch{}}let Oe=!1;function ke(t){try{let n=Ee();Oe||=(De(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 B(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 Ae(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]===`&`){V(t,n,`&&`),n=``,r+=2;continue}if(o===`|`&&r+1<e.length&&e[r+1]===`|`){V(t,n,`||`),n=``,r+=2;continue}if(o===`|`){V(t,n,`|`),n=``,r++;continue}if(o===`;`){V(t,n,`;`),n=``,r++;continue}}n+=o,r++}let o=n.trim();return o&&t.push({command:o,operator:null}),t}function V(e,t,n){let r=t.trim();r&&e.push({command:r,operator:n})}const H=new Set([`2>&1`,`2>/dev/null`,`>/dev/null`,`1>/dev/null`]);function je(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(`>(`)&&!H.has(t))return{safe:!1,reason:`process substitution`}}for(let t=0;t<e.length;t++){let n=e[t];if(!H.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 Me=new Set([`status`,`log`,`diff`,`show`,`branch`,`remote`,`fetch`,`rev-parse`]),Ne=new Set([`-c`,`--exec-path`,`--pager`,`--upload-pack`,`--receive-pack`]);function Pe(e){for(let t of e.slice(1))if(Ne.has(t)||t.startsWith(`--exec-path=`)||t.startsWith(`--pager=`)||t.startsWith(`--upload-pack=`)||t.startsWith(`--receive-pack=`))return!1;for(let t of e.slice(1))if(!t.startsWith(`-`)){if(t===`stash`){let t=e[e.indexOf(`stash`)+1];return t===`list`||t===void 0}return Me.has(t)}return!1}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(`.`)),Fe=new Set([`list`,`ls`,`ll`,`la`,`why`,`view`,`info`,`show`,`outdated`]);function Ie(e){let t=e.slice(1),n;for(let e of t)if(!(e.startsWith(`-`)&&!U.has(e))){n=e;break}return n?Fe.has(n)?!0:n===`install`||n===`i`?t.slice(t.indexOf(n)+1).every(e=>e.startsWith(`-`)):!1:!1}const Le=new Set([`view`,`info`,`show`]);function Re(e){let t=e[1];return t!==void 0&&Le.has(t)}function ze(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 Be=new Set([`-exec`,`-execdir`,`-ok`,`-delete`]);function Ve(e){return!e.some(e=>Be.has(e))}function He(e){return!e.includes(`-o`)&&!e.some(e=>e.startsWith(`--output`))}function Ue(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 We(e){return e.some(e=>e===`777`||e===`0777`)}const Ge=/(^|[/\\\\])(\\.env(\\..*)?|.*\\.env(\\..*)?|\\.env)$/;function G(e){return e.slice(1).some(e=>!e.startsWith(`-`)&&Ge.test(e))}function Ke(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 qe(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 Je=[{name:`rm-recursive`,match:/^rm$/,action:`deny`,when:e=>Ue(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=>Ke(e),reason:\"Use `git push --force-with-lease` instead of `--force`.\"},{name:`git-reset-hard`,match:/^git$/,action:`deny`,when:e=>qe(e),reason:\"Don't `reset --hard` on main. Create a branch first.\"},{name:`chmod-777`,match:/^chmod$/,action:`deny`,when:e=>We(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=>Re(e)},{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=>Ve(e)},{name:`sort`,match:/^sort$/,action:`allow`,when:e=>He(e)},{name:`git`,match:/^git$/,action:`allow`,when:e=>Pe(e)},{name:`pnpm`,match:/^pnpm$/,action:`allow`,when:e=>Ie(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)}],Ye=R({scripts:xe(L(),ve())});function q(e,t){let n=et(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 Xe(e,t,n){let r=Ze(t,n);if(!r)return null;let i=q(e,r);return i?{...i,pkgDir:r}:null}function Ze(e,t){let i=Qe(t);if(!i)return null;for(let s of i){if(!s.endsWith(`/*`))continue;let i=o(t,s.slice(0,-2)),c;try{c=n(i)}catch{continue}for(let t of c){let n=a(i,t);try{if(!r(n).isDirectory())continue}catch{continue}let o=J(n);if(o===e||o===`@${e}`||t===e||o&&(o.startsWith(`@`)?o.split(`/`)[1]:o)===e)return n}}return null}function Qe(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 $e=R({name:L()});function J(e){try{let n=t(a(e,`package.json`),`utf-8`),r=$e.safeParse(JSON.parse(n));return r.success?r.data.name:null}catch{return null}}function et(e){try{let n=t(a(e,`package.json`),`utf-8`),r=Ye.safeParse(JSON.parse(n));return r.success?r.data.scripts:null}catch{return null}}const tt=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`]),nt=[`GIT_CONFIG_`,`GIT_PAGER`,`GIT_SSH_COMMAND`,`GIT_EDITOR`,`GIT_EXEC_PATH`];function Y(e){return tt.has(e)?!0:nt.some(t=>e.startsWith(t))}function rt(e,t){let n=B(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 X(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=Ae(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=rt(e.command,o);t&&(o={...o,cwd:t})}let t=it(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 it(e,t){let n=e.trim();if(n.startsWith(`(`)&&n.endsWith(`)`))return{action:`passthrough`,rule:null,reason:`subshell`};let r=B(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(Y(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=je(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 X(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(Y(t))return{action:`passthrough`,rule:null,reason:`dangerous env var: ${t}`};e++}return e>=a.length?{action:`allow`,rule:`env-only`,reason:null}:X(a.slice(e).join(` `),{...t,depth:t.depth+1})}if(c===`pnpm`)return at(a,t);if(c===`npm`)return ot(a);if(c===`bash`||c===`sh`||c===`zsh`)return lt(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 Je)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:null}}function at(e,t){let n=e.slice(1),r,i=-1;for(let a=0;a<n.length;a++){let o=n[a];if(o===`--filter`||o.startsWith(`--filter=`))return st(e,n,a,t);if(o===`-r`||o===`--recursive`)return{action:`passthrough`,rule:null,reason:`pnpm recursive`};if(!(o.startsWith(`-`)&&!U.has(o))){r=o,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`}:X(e.join(` `),{...t,depth:t.depth+1})}if(r===`run`){let e=n[i+1];return!e||e.startsWith(`-`)?{action:`passthrough`,rule:null,reason:`pnpm run without script name`}:Z(e,t)}if(U.has(r)){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 Z(r,t)}function Z(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=X(n.pre,r);if(e.action!==`allow`)return e}let i=X(n.main,r);if(i.action!==`allow`)return i;if(n.post){let e=X(n.post,r);if(e.action!==`allow`)return e}return{action:`allow`,rule:`pnpm-script:${e}`,reason:null}}function ot(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:ze(e)}}function st(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`}:X(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=Xe(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=X(l.pre,u);if(e.action!==`allow`)return e}let d=X(l.main,u);if(d.action!==`allow`)return d;if(l.post){let e=X(l.post,u);if(e.action!==`allow`)return e}return{action:`allow`,rule:`pnpm-filter:${a}:${c}`,reason:null}}const ct=new Set([`-i`,`--interactive`,`-l`,`--login`,`--init-file`,`--rcfile`,`--norc`,`--noprofile`,`-s`,`--stdin`]);function lt(e,t){let n=e.indexOf(`-c`);if(n===-1)return{action:`passthrough`,rule:null,reason:`shell without -c`};for(let t=1;t<e.length;t++){if(t===n||t===n+1)continue;let r=e[t];if(r.startsWith(`-`)||ct.has(r))return{action:`passthrough`,rule:null,reason:`shell -c with extra flags`}}let r=e[n+1];return r?e.length>n+2?{action:`passthrough`,rule:null,reason:`shell -c with positional args`}:n===1?X(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 Te();(Q.hook_event_name!==`PreToolUse`||Q.tool_name!==`Bash`)&&process.exit(0);const $=X(Q.tool_input.command,{cwd:Q.cwd,depth:0});ke({sessionId:Q.session_id,command:Q.tool_input.command,decision:$.action,rule:$.rule,reason:$.reason}),$.action===`allow`?z(`allow`):$.action===`deny`&&z(`deny`,$.reason);export{};";
|
|
2367
|
+
const existing = ctx.read(HOOK_PATH);
|
|
2368
|
+
if (existing === content) return {
|
|
2369
|
+
filePath: HOOK_PATH,
|
|
2370
|
+
action: "skipped",
|
|
2371
|
+
description: "Hook is up to date"
|
|
2372
|
+
};
|
|
2373
|
+
ctx.write(HOOK_PATH, content);
|
|
2374
|
+
return {
|
|
2375
|
+
filePath: HOOK_PATH,
|
|
2376
|
+
action: existing ? "updated" : "created",
|
|
2377
|
+
description: existing ? "Updated bst-hook.mjs" : "Generated bst-hook.mjs"
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
//#endregion
|
|
2353
2381
|
//#region src/generators/claude-settings.ts
|
|
2382
|
+
const HookEntrySchema = z.object({
|
|
2383
|
+
type: z.string(),
|
|
2384
|
+
command: z.string()
|
|
2385
|
+
});
|
|
2386
|
+
const HookMatcherSchema = z.object({
|
|
2387
|
+
matcher: z.string(),
|
|
2388
|
+
hooks: z.array(HookEntrySchema).default([])
|
|
2389
|
+
});
|
|
2354
2390
|
const ClaudeSettingsSchema = z.object({
|
|
2355
2391
|
permissions: z.object({
|
|
2356
2392
|
allow: z.array(z.string()).default([]),
|
|
@@ -2359,6 +2395,7 @@ const ClaudeSettingsSchema = z.object({
|
|
|
2359
2395
|
allow: [],
|
|
2360
2396
|
deny: []
|
|
2361
2397
|
}),
|
|
2398
|
+
hooks: z.record(z.string(), z.array(HookMatcherSchema)).default({}),
|
|
2362
2399
|
instructions: z.array(z.string()).default([]),
|
|
2363
2400
|
enabledPlugins: z.record(z.string(), z.boolean()).default({}),
|
|
2364
2401
|
extraKnownMarketplaces: z.record(z.string(), z.record(z.string(), z.unknown())).default({})
|
|
@@ -2380,64 +2417,11 @@ function parseClaudeSettings(raw) {
|
|
|
2380
2417
|
function buildSettings(ctx) {
|
|
2381
2418
|
const pm = "pnpm";
|
|
2382
2419
|
const allow = [
|
|
2383
|
-
`Bash(${pm} install)`,
|
|
2384
|
-
`Bash(${pm} install *)`,
|
|
2385
|
-
`Bash(${pm} add *)`,
|
|
2386
|
-
`Bash(${pm} update *)`,
|
|
2387
|
-
`Bash(${pm} view *)`,
|
|
2388
|
-
`Bash(${pm} why *)`,
|
|
2389
|
-
`Bash(${pm} list)`,
|
|
2390
|
-
`Bash(${pm} list *)`,
|
|
2391
|
-
`Bash(${pm} ls)`,
|
|
2392
|
-
`Bash(${pm} ls *)`,
|
|
2393
|
-
"Bash(npm view *)",
|
|
2394
|
-
"Bash(npm info *)",
|
|
2395
|
-
"Bash(npm show *)",
|
|
2396
|
-
`Bash(${pm} build)`,
|
|
2397
|
-
`Bash(${pm} -r build)`,
|
|
2398
|
-
`Bash(${pm} dev)`,
|
|
2399
|
-
`Bash(${pm} test)`,
|
|
2400
|
-
`Bash(${pm} test *)`,
|
|
2401
|
-
`Bash(${pm} -r test)`,
|
|
2402
|
-
`Bash(${pm} vitest)`,
|
|
2403
|
-
`Bash(${pm} vitest *)`,
|
|
2404
|
-
`Bash(${pm} exec vitest)`,
|
|
2405
|
-
`Bash(${pm} exec vitest *)`,
|
|
2406
|
-
`Bash(${pm} exec oxfmt)`,
|
|
2407
|
-
`Bash(${pm} exec oxfmt *)`,
|
|
2408
|
-
`Bash(${pm} typecheck)`,
|
|
2409
|
-
`Bash(${pm} -r --parallel run typecheck)`,
|
|
2410
|
-
`Bash(${pm} tsc *)`,
|
|
2411
|
-
`Bash(${pm} exec tsc *)`,
|
|
2412
|
-
`Bash(${pm} lint)`,
|
|
2413
|
-
`Bash(${pm} lint *)`,
|
|
2414
|
-
`Bash(${pm} format)`,
|
|
2415
|
-
`Bash(${pm} format *)`,
|
|
2416
|
-
`Bash(${pm} knip)`,
|
|
2417
|
-
`Bash(${pm} knip *)`,
|
|
2418
|
-
`Bash(${pm} check)`,
|
|
2419
|
-
`Bash(${pm} ci:check)`,
|
|
2420
|
-
`Bash(${pm} exec oxlint *)`,
|
|
2421
|
-
`Bash(${pm} exec knip *)`,
|
|
2422
|
-
"Bash(git status *)",
|
|
2423
|
-
"Bash(git log *)",
|
|
2424
|
-
"Bash(git diff *)",
|
|
2425
|
-
"Bash(git branch *)",
|
|
2426
|
-
"Bash(git show *)",
|
|
2427
|
-
"Bash(git rev-parse *)",
|
|
2428
|
-
"Bash(ls *)",
|
|
2429
|
-
"Bash(cat *)",
|
|
2430
|
-
"Bash(head *)",
|
|
2431
|
-
"Bash(tail *)",
|
|
2432
|
-
"Bash(wc *)",
|
|
2433
2420
|
"Bash(test *)",
|
|
2434
2421
|
"Bash([ *)",
|
|
2435
|
-
"Bash(
|
|
2436
|
-
"Bash(which *)",
|
|
2437
|
-
"Bash(node -e *)",
|
|
2438
|
-
"Bash(node -p *)"
|
|
2422
|
+
"Bash(git -C * diff HEAD -- *)"
|
|
2439
2423
|
];
|
|
2440
|
-
if (ctx.config.structure === "monorepo") allow.push(`Bash(${pm}
|
|
2424
|
+
if (ctx.config.structure === "monorepo") allow.push(`Bash(${pm} -r *)`);
|
|
2441
2425
|
const enabledPlugins = { "code-simplifier@claude-plugins-official": true };
|
|
2442
2426
|
const extraKnownMarketplaces = {};
|
|
2443
2427
|
if (ctx.config.structure !== "monorepo") {
|
|
@@ -2447,7 +2431,6 @@ function buildSettings(ctx) {
|
|
|
2447
2431
|
permissions: {
|
|
2448
2432
|
allow,
|
|
2449
2433
|
deny: [
|
|
2450
|
-
"Bash(npx *)",
|
|
2451
2434
|
"Bash(git push *)",
|
|
2452
2435
|
"Bash(git push)",
|
|
2453
2436
|
"Bash(git add *)",
|
|
@@ -2466,35 +2449,19 @@ function buildSettings(ctx) {
|
|
|
2466
2449
|
"Bash(git clean *)",
|
|
2467
2450
|
"Bash(git rm *)",
|
|
2468
2451
|
"Bash(git mv *)",
|
|
2469
|
-
"Bash(rm -rf *)",
|
|
2470
|
-
"Bash(cat *.env)",
|
|
2471
|
-
"Bash(cat *.env.*)",
|
|
2472
|
-
"Bash(cat .env)",
|
|
2473
|
-
"Bash(cat .env.*)",
|
|
2474
|
-
"Bash(head *.env)",
|
|
2475
|
-
"Bash(head *.env.*)",
|
|
2476
|
-
"Bash(head .env)",
|
|
2477
|
-
"Bash(head .env.*)",
|
|
2478
|
-
"Bash(tail *.env)",
|
|
2479
|
-
"Bash(tail *.env.*)",
|
|
2480
|
-
"Bash(tail .env)",
|
|
2481
|
-
"Bash(tail .env.*)",
|
|
2482
|
-
"Bash(less *.env)",
|
|
2483
|
-
"Bash(less *.env.*)",
|
|
2484
|
-
"Bash(less .env)",
|
|
2485
|
-
"Bash(less .env.*)",
|
|
2486
|
-
"Bash(more *.env)",
|
|
2487
|
-
"Bash(more *.env.*)",
|
|
2488
|
-
"Bash(more .env)",
|
|
2489
|
-
"Bash(more .env.*)",
|
|
2490
|
-
"Bash(grep * .env)",
|
|
2491
|
-
"Bash(grep * .env.*)",
|
|
2492
2452
|
"Read(.env)",
|
|
2493
2453
|
"Read(.env.*)",
|
|
2494
2454
|
"Read(*.env)",
|
|
2495
2455
|
"Read(*.env.*)"
|
|
2496
2456
|
]
|
|
2497
2457
|
},
|
|
2458
|
+
hooks: { PreToolUse: [{
|
|
2459
|
+
matcher: "Bash",
|
|
2460
|
+
hooks: [{
|
|
2461
|
+
type: "command",
|
|
2462
|
+
command: `node "$CLAUDE_PROJECT_DIR/.claude/hooks/bst-hook.mjs"`
|
|
2463
|
+
}]
|
|
2464
|
+
}] },
|
|
2498
2465
|
instructions: [
|
|
2499
2466
|
"Use pnpm, not npm/yarn/npx. Run binaries with `pnpm exec`.",
|
|
2500
2467
|
"No typecasts (as/any). Use zod schemas, type guards, or narrowing instead.",
|
|
@@ -2505,10 +2472,16 @@ function buildSettings(ctx) {
|
|
|
2505
2472
|
extraKnownMarketplaces
|
|
2506
2473
|
};
|
|
2507
2474
|
}
|
|
2508
|
-
|
|
2475
|
+
const BST_HOOK_MARKER = "bst-hook.mjs";
|
|
2476
|
+
/** Check if a hook entry list already contains the bst-hook command. */
|
|
2477
|
+
function hasBstHook(matchers) {
|
|
2478
|
+
return matchers.some((m) => m.matcher === "Bash" && m.hooks.some((h) => h.command.includes(BST_HOOK_MARKER)));
|
|
2479
|
+
}
|
|
2480
|
+
/** Remove enabledPlugins/extraKnownMarketplaces/hooks when empty to keep the file clean. */
|
|
2509
2481
|
function serializeSettings$1(settings) {
|
|
2510
|
-
const { enabledPlugins, extraKnownMarketplaces, ...rest } = settings;
|
|
2482
|
+
const { enabledPlugins, extraKnownMarketplaces, hooks, ...rest } = settings;
|
|
2511
2483
|
const out = { ...rest };
|
|
2484
|
+
if (Object.keys(hooks).length > 0) out["hooks"] = hooks;
|
|
2512
2485
|
if (Object.keys(enabledPlugins).length > 0) out["enabledPlugins"] = enabledPlugins;
|
|
2513
2486
|
if (Object.keys(extraKnownMarketplaces).length > 0) out["extraKnownMarketplaces"] = extraKnownMarketplaces;
|
|
2514
2487
|
return JSON.stringify(out, null, 2) + "\n";
|
|
@@ -2534,9 +2507,18 @@ function writeOrMergeSettings(ctx, filePath, generated) {
|
|
|
2534
2507
|
else mergedInstructions.push(inst);
|
|
2535
2508
|
instructionChanges++;
|
|
2536
2509
|
}
|
|
2510
|
+
let hookChanges = 0;
|
|
2511
|
+
const mergedHooks = { ...settings.hooks };
|
|
2512
|
+
for (const [event, generatedMatchers] of Object.entries(generated.hooks)) {
|
|
2513
|
+
const existingMatchers = mergedHooks[event] ?? [];
|
|
2514
|
+
if (!hasBstHook(existingMatchers)) {
|
|
2515
|
+
mergedHooks[event] = [...existingMatchers, ...generatedMatchers];
|
|
2516
|
+
hookChanges++;
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2537
2519
|
const missingPlugins = Object.entries(generated.enabledPlugins).filter(([key]) => !(key in settings.enabledPlugins));
|
|
2538
2520
|
const missingMarketplaces = Object.entries(generated.extraKnownMarketplaces).filter(([key]) => !(key in settings.extraKnownMarketplaces));
|
|
2539
|
-
const changed = missingAllow.length + missingDeny.length + instructionChanges + missingPlugins.length + missingMarketplaces.length;
|
|
2521
|
+
const changed = missingAllow.length + missingDeny.length + hookChanges + instructionChanges + missingPlugins.length + missingMarketplaces.length;
|
|
2540
2522
|
if (changed === 0) return {
|
|
2541
2523
|
filePath,
|
|
2542
2524
|
action: "skipped",
|
|
@@ -2546,6 +2528,7 @@ function writeOrMergeSettings(ctx, filePath, generated) {
|
|
|
2546
2528
|
allow: [...settings.permissions.allow, ...missingAllow],
|
|
2547
2529
|
deny: [...settings.permissions.deny, ...missingDeny]
|
|
2548
2530
|
};
|
|
2531
|
+
if (Object.keys(mergedHooks).length > 0) rawJson["hooks"] = mergedHooks;
|
|
2549
2532
|
rawJson["instructions"] = mergedInstructions;
|
|
2550
2533
|
const updatedPlugins = { ...settings.enabledPlugins };
|
|
2551
2534
|
for (const [key, value] of missingPlugins) updatedPlugins[key] = value;
|
|
@@ -2581,6 +2564,7 @@ async function generateClaudeSettings(ctx) {
|
|
|
2581
2564
|
allow: [],
|
|
2582
2565
|
deny: []
|
|
2583
2566
|
},
|
|
2567
|
+
hooks: {},
|
|
2584
2568
|
instructions: [],
|
|
2585
2569
|
enabledPlugins: { "frontend-design@claude-plugins-official": true },
|
|
2586
2570
|
extraKnownMarketplaces: {}
|
|
@@ -3181,6 +3165,7 @@ async function runGenerators(ctx) {
|
|
|
3181
3165
|
results.push(await generateKnip(ctx));
|
|
3182
3166
|
results.push(await generateRenovate(ctx));
|
|
3183
3167
|
results.push(await generateCi(ctx));
|
|
3168
|
+
results.push(await generateClaudeHooks(ctx));
|
|
3184
3169
|
results.push(...await generateClaudeSettings(ctx));
|
|
3185
3170
|
results.push(await generateReleaseIt(ctx));
|
|
3186
3171
|
results.push(await generateVersionrc(ctx));
|
|
@@ -3201,7 +3186,7 @@ async function runGenerators(ctx) {
|
|
|
3201
3186
|
function generateMigratePrompt(results, config, detected) {
|
|
3202
3187
|
const sections = [];
|
|
3203
3188
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3204
|
-
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.
|
|
3189
|
+
sections.push(`# Migration Prompt\n\n_Generated by \`@bensandee/tooling@0.39.0 repo:sync\` on ${timestamp}_\n`);
|
|
3205
3190
|
const changed = results.filter((r) => r.action === "created" || r.action === "updated" || r.action === "archived");
|
|
3206
3191
|
if (changed.length > 0) {
|
|
3207
3192
|
const items = changed.map((r) => {
|
|
@@ -3501,7 +3486,8 @@ async function runCheck(targetDir) {
|
|
|
3501
3486
|
if (!newContent) continue;
|
|
3502
3487
|
const existingPath = path.join(targetDir, r.filePath);
|
|
3503
3488
|
const existing = existsSync(existingPath) ? readFileSync(existingPath, "utf-8") : void 0;
|
|
3504
|
-
if (
|
|
3489
|
+
if (isBundledFile(r.filePath)) log.info(` bundled file changed (${formatBytes(existing?.length ?? 0)} → ${formatBytes(newContent.length)})`);
|
|
3490
|
+
else if (!existing) {
|
|
3505
3491
|
const lineCount = newContent.split("\n").length - 1;
|
|
3506
3492
|
log.info(` + ${lineCount} new lines`);
|
|
3507
3493
|
} else {
|
|
@@ -3511,6 +3497,14 @@ async function runCheck(targetDir) {
|
|
|
3511
3497
|
}
|
|
3512
3498
|
return 1;
|
|
3513
3499
|
}
|
|
3500
|
+
/** Files that are minified/bundled and produce unreadable diffs. */
|
|
3501
|
+
function isBundledFile(filePath) {
|
|
3502
|
+
return filePath.endsWith("bst-hook.mjs");
|
|
3503
|
+
}
|
|
3504
|
+
function formatBytes(bytes) {
|
|
3505
|
+
if (bytes < 1024) return `${String(bytes)} B`;
|
|
3506
|
+
return `${(bytes / 1024).toFixed(1)} kB`;
|
|
3507
|
+
}
|
|
3514
3508
|
const normalize = (line) => line.trimEnd();
|
|
3515
3509
|
function lineDiff(oldText, newText) {
|
|
3516
3510
|
const oldLines = oldText.split("\n").map(normalize);
|
|
@@ -3818,6 +3812,30 @@ async function ensureRelease(executor, conn, tag) {
|
|
|
3818
3812
|
//#endregion
|
|
3819
3813
|
//#region src/release/version.ts
|
|
3820
3814
|
const BRANCH = "changeset-release/main";
|
|
3815
|
+
/** Pattern matching a dateless version heading like `## 1.2.3` or `## 1.2.3-beta.0`. */
|
|
3816
|
+
const DATELESS_VERSION_RE = /^## (\d+\.\d+\.\d+(?:-[^\s)]+)?)$/gm;
|
|
3817
|
+
/** Format today's date as YYYY-MM-DD. */
|
|
3818
|
+
function todayIso() {
|
|
3819
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3820
|
+
}
|
|
3821
|
+
/** Add today's date to dateless version headings in a changelog string. */
|
|
3822
|
+
function stampDates(content, date) {
|
|
3823
|
+
return content.replace(DATELESS_VERSION_RE, `## $1 (${date})`);
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Stamp release dates on CHANGELOGs for packages that were just versioned.
|
|
3827
|
+
* Only modifies headings that don't already have a date (idempotent).
|
|
3828
|
+
*/
|
|
3829
|
+
function stampChangelogDates(executor, cwd, changedDirs, isMonorepo) {
|
|
3830
|
+
const date = todayIso();
|
|
3831
|
+
const changelogPaths = isMonorepo ? changedDirs.map((dir) => path.join(cwd, "packages", dir, "CHANGELOG.md")) : [path.join(cwd, "CHANGELOG.md")];
|
|
3832
|
+
for (const changelogPath of changelogPaths) {
|
|
3833
|
+
const content = executor.readFile(changelogPath);
|
|
3834
|
+
if (!content) continue;
|
|
3835
|
+
const updated = stampDates(content, date);
|
|
3836
|
+
if (updated !== content) executor.writeFile(changelogPath, updated);
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3821
3839
|
/** Extract the latest changelog entry (content between first and second `## ` heading). */
|
|
3822
3840
|
function extractLatestEntry(changelog) {
|
|
3823
3841
|
const lines = changelog.split("\n");
|
|
@@ -3905,6 +3923,12 @@ async function runVersionMode(executor, config) {
|
|
|
3905
3923
|
debugExec(config, "pnpm changeset version", versionResult);
|
|
3906
3924
|
if (originalConfig) executor.writeFile(changesetConfigPath, originalConfig);
|
|
3907
3925
|
if (versionResult.exitCode !== 0) throw new FatalError(`pnpm changeset version failed (exit code ${String(versionResult.exitCode)}):\n${versionResult.stderr}`);
|
|
3926
|
+
const packagesAfterVersion = executor.listWorkspacePackages(config.cwd);
|
|
3927
|
+
if (packagesBefore.length > 0 || packagesAfterVersion.length > 0) {
|
|
3928
|
+
const beforeMap = new Map(packagesBefore.map((pkg) => [pkg.name, pkg.version]));
|
|
3929
|
+
const changedDirs = packagesAfterVersion.filter((pkg) => beforeMap.get(pkg.name) !== pkg.version).map((pkg) => pkg.dir);
|
|
3930
|
+
stampChangelogDates(executor, config.cwd, changedDirs, true);
|
|
3931
|
+
} else stampChangelogDates(executor, config.cwd, [], false);
|
|
3908
3932
|
debugExec(config, "pnpm install --no-frozen-lockfile", executor.exec("pnpm", ["install", "--no-frozen-lockfile"], { cwd: config.cwd }));
|
|
3909
3933
|
const { title, body } = buildPrContent(executor, config.cwd, packagesBefore);
|
|
3910
3934
|
debug(config, `PR title: ${title}`);
|
|
@@ -5173,11 +5197,93 @@ const dockerCheckCommand = defineCommand({
|
|
|
5173
5197
|
}
|
|
5174
5198
|
});
|
|
5175
5199
|
//#endregion
|
|
5200
|
+
//#region src/commands/hooks-audit.ts
|
|
5201
|
+
const AuditEntrySchema = z.object({
|
|
5202
|
+
timestamp: z.string(),
|
|
5203
|
+
sessionId: z.string(),
|
|
5204
|
+
command: z.string(),
|
|
5205
|
+
decision: z.enum([
|
|
5206
|
+
"allow",
|
|
5207
|
+
"deny",
|
|
5208
|
+
"passthrough"
|
|
5209
|
+
]),
|
|
5210
|
+
rule: z.string().nullable(),
|
|
5211
|
+
reason: z.string().nullable()
|
|
5212
|
+
});
|
|
5213
|
+
function parseAuditLog(logPath) {
|
|
5214
|
+
if (!existsSync(logPath)) return [];
|
|
5215
|
+
const lines = readFileSync(logPath, "utf-8").split("\n").filter(Boolean);
|
|
5216
|
+
const entries = [];
|
|
5217
|
+
for (const line of lines) try {
|
|
5218
|
+
const result = AuditEntrySchema.safeParse(JSON.parse(line));
|
|
5219
|
+
if (result.success) entries.push(result.data);
|
|
5220
|
+
} catch (_error) {}
|
|
5221
|
+
return entries;
|
|
5222
|
+
}
|
|
5223
|
+
const hooksAuditCommand = defineCommand({
|
|
5224
|
+
meta: {
|
|
5225
|
+
name: "hooks:audit",
|
|
5226
|
+
description: "Summarize the bst-hook audit log"
|
|
5227
|
+
},
|
|
5228
|
+
args: {
|
|
5229
|
+
dir: {
|
|
5230
|
+
type: "positional",
|
|
5231
|
+
description: "Project directory (default: current directory)",
|
|
5232
|
+
required: false
|
|
5233
|
+
},
|
|
5234
|
+
tail: {
|
|
5235
|
+
type: "string",
|
|
5236
|
+
description: "Show the last N entries (default: 20)"
|
|
5237
|
+
},
|
|
5238
|
+
decision: {
|
|
5239
|
+
type: "string",
|
|
5240
|
+
description: "Filter by decision: allow, deny, or passthrough"
|
|
5241
|
+
},
|
|
5242
|
+
json: {
|
|
5243
|
+
type: "boolean",
|
|
5244
|
+
description: "Output raw JSON lines instead of formatted summary"
|
|
5245
|
+
}
|
|
5246
|
+
},
|
|
5247
|
+
async run({ args }) {
|
|
5248
|
+
const targetDir = path.resolve(args.dir ?? ".");
|
|
5249
|
+
const logPath = path.join(targetDir, ".claude/hooks/bst-hook.audit.local.jsonl");
|
|
5250
|
+
let entries = parseAuditLog(logPath);
|
|
5251
|
+
if (entries.length === 0) {
|
|
5252
|
+
log.info("No audit log entries found.");
|
|
5253
|
+
return;
|
|
5254
|
+
}
|
|
5255
|
+
const decisionFilter = args.decision;
|
|
5256
|
+
if (decisionFilter) entries = entries.filter((e) => e.decision === decisionFilter);
|
|
5257
|
+
const tailCount = args.tail ? Number.parseInt(args.tail, 10) : 20;
|
|
5258
|
+
if (entries.length > tailCount) entries = entries.slice(-tailCount);
|
|
5259
|
+
if (args.json === true) {
|
|
5260
|
+
for (const entry of entries) console.log(JSON.stringify(entry));
|
|
5261
|
+
return;
|
|
5262
|
+
}
|
|
5263
|
+
const allEntries = parseAuditLog(logPath);
|
|
5264
|
+
const counts = {
|
|
5265
|
+
allow: 0,
|
|
5266
|
+
deny: 0,
|
|
5267
|
+
passthrough: 0
|
|
5268
|
+
};
|
|
5269
|
+
for (const e of allEntries) counts[e.decision]++;
|
|
5270
|
+
log.info(`Audit log: ${String(allEntries.length)} entries — ${String(counts.allow)} allow, ${String(counts.deny)} deny, ${String(counts.passthrough)} passthrough`);
|
|
5271
|
+
log.info("");
|
|
5272
|
+
for (const entry of entries) {
|
|
5273
|
+
const ts = entry.timestamp.replace("T", " ").replace(/\.\d+Z$/, "");
|
|
5274
|
+
const icon = entry.decision === "allow" ? "+" : entry.decision === "deny" ? "x" : "?";
|
|
5275
|
+
const rule = entry.rule ? ` [${entry.rule}]` : "";
|
|
5276
|
+
const reason = entry.reason ? ` — ${entry.reason}` : "";
|
|
5277
|
+
log.info(` ${icon} ${ts} ${entry.decision.padEnd(11)} ${entry.command}${rule}${reason}`);
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
});
|
|
5281
|
+
//#endregion
|
|
5176
5282
|
//#region src/bin.ts
|
|
5177
5283
|
const main = defineCommand({
|
|
5178
5284
|
meta: {
|
|
5179
5285
|
name: "bst",
|
|
5180
|
-
version: "0.
|
|
5286
|
+
version: "0.39.0",
|
|
5181
5287
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
5182
5288
|
},
|
|
5183
5289
|
subCommands: {
|
|
@@ -5190,10 +5296,11 @@ const main = defineCommand({
|
|
|
5190
5296
|
"release:simple": releaseSimpleCommand,
|
|
5191
5297
|
"docker:publish": publishDockerCommand,
|
|
5192
5298
|
"docker:build": dockerBuildCommand,
|
|
5193
|
-
"docker:check": dockerCheckCommand
|
|
5299
|
+
"docker:check": dockerCheckCommand,
|
|
5300
|
+
"hooks:audit": hooksAuditCommand
|
|
5194
5301
|
}
|
|
5195
5302
|
});
|
|
5196
|
-
console.log(`@bensandee/tooling v0.
|
|
5303
|
+
console.log(`@bensandee/tooling v0.39.0`);
|
|
5197
5304
|
async function run() {
|
|
5198
5305
|
await runMain(main);
|
|
5199
5306
|
process.exit(process.exitCode ?? 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -60,7 +60,8 @@
|
|
|
60
60
|
"tsdown": "0.21.4",
|
|
61
61
|
"typescript": "5.9.3",
|
|
62
62
|
"vitest": "4.1.0",
|
|
63
|
-
"@bensandee/
|
|
63
|
+
"@bensandee/claude-hooks": "0.1.0",
|
|
64
|
+
"@bensandee/config": "0.9.3"
|
|
64
65
|
},
|
|
65
66
|
"scripts": {
|
|
66
67
|
"build": "tsdown",
|