@seanmozeik/tripwire 0.5.0 → 0.5.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.
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun @bytecode @bun-cjs
3
- (function(exports, require, module, __filename, __dirname) {var S=require("@effect/platform-bun"),Q=require("effect"),w=require("effect/unstable/cli");var W={name:"@seanmozeik/tripwire",version:"0.5.0",description:"Opinionated hooks dispatcher for AI coding agents with configurable safety rules",license:"MIT",bin:{tripwire:"./dist/tripwire-cli.js","tripwire-hook":"./dist/tripwire.js"},files:["dist","package.json","src","README.md"],type:"module",exports:{".":"./src/index.ts"},publishConfig:{access:"public"},scripts:{build:"bun scripts/build.ts",prepublishOnly:"bun run build",check:"bun run format && bun run lint:fix && bun run typecheck",format:"oxfmt --write .",lint:"oxlint --tsconfig tsconfig.oxlint.json .","lint:fix":"oxlint --format agent --tsconfig tsconfig.oxlint.json --fix .",test:"bun test",typecheck:"tsc --noEmit"},dependencies:{"@effect/platform-bun":"^4.0.0-beta.66",effect:"^4.0.0-beta.66","shell-quote":"^1.8.3"},devDependencies:{"@types/bun":"^1.3.14","@types/shell-quote":"^1.7.5",oxfmt:"^0.50.0",oxlint:"^1.65.0","oxlint-tsgolint":"^0.22.1",typescript:"^6.0.3"},engines:{bun:">=1.0"}};var B=require("os"),N=globalThis.Bun,X="tripwire-hook",Z=(b)=>{if(!b)return[[{hooks:[{type:"command",command:X}]}],!1];let q=!1,x=b.map((L)=>({hooks:L.hooks.map((D)=>{if(D.command===X||D.command.endsWith("/tripwire-hook")){if(D.command!==X)return q=!0,{...D,command:X};return D}return D})}));if(x.some((L)=>L.hooks.some((D)=>D.command===X)))return[x,!q];return[[...x,{hooks:[{type:"command",command:X}]}],!1]},v=async()=>{let b=`${B.homedir()}/.claude/settings.json`,q=N.file(b);try{let x=await q.text(),y=JSON.parse(x);y.hooks??={};let[G,L]=Z(y.hooks.PreToolUse),[D,j]=Z(y.hooks.PostToolUse);if(y.hooks.PreToolUse=G,y.hooks.PostToolUse=D,L&&j)return{success:!0,message:`Already configured: ${b}`};return await q.write(`${JSON.stringify(y,null,2)}
4
- `),{success:!0,message:`Updated ${b}`}}catch(x){let y=x instanceof Error?x.message:String(x);if(y.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update Claude config: ${y}`}}},A=async()=>{let b=`${B.homedir()}/.pi/agent/settings.json`,q=N.file(b);try{let x=await q.text(),y=JSON.parse(x);y.hooks??={};let[G,L]=Z(y.hooks.PreToolUse),[D,j]=Z(y.hooks.PostToolUse);if(y.hooks.PreToolUse=G,y.hooks.PostToolUse=D,L&&j)return{success:!0,message:`Already configured: ${b}`};return await q.write(`${JSON.stringify(y,null,2)}
5
- `),{success:!0,message:`Updated ${b}`}}catch(x){let y=x instanceof Error?x.message:String(x);if(y.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update pi config: ${y}`}}},J=async()=>{let b=`${B.homedir()}/.codex/config.toml`,q=`${B.homedir()}/.codex/hooks.json`,x=N.file(q),y=N.file(b),G=!1,L=!1;try{let D=await x.text(),j=JSON.parse(D);j.hooks??={};let[Y,V]=Z(j.hooks.PreToolUse),[z,$]=Z(j.hooks.PostToolUse);if(j.hooks.PreToolUse=Y,j.hooks.PostToolUse=z,!V||!$)G=!0;let K=(U)=>{return U?.map((E)=>({hooks:E.hooks.map((M)=>{if(M.command===X&&M.timeout===void 0)return{...M,timeout:10};return M})}))??[]};if(j.hooks.PreToolUse=K(j.hooks.PreToolUse),j.hooks.PostToolUse=K(j.hooks.PostToolUse),G)await x.write(`${JSON.stringify(j,null,2)}
3
+ (function(exports, require, module, __filename, __dirname) {var _=require("path"),C=require("@effect/platform-bun"),R=globalThis.Bun,Q=require("effect"),w=require("effect/unstable/cli");var v={name:"@seanmozeik/tripwire",version:"0.5.2",description:"Opinionated hooks dispatcher for AI coding agents with configurable safety rules",license:"MIT",bin:{tripwire:"./dist/tripwire-cli.js","tripwire-hook":"./dist/tripwire.js"},files:["dist","package.json","src","README.md"],type:"module",exports:{".":"./src/index.ts"},publishConfig:{access:"public"},scripts:{build:"bun scripts/build.ts",prepublishOnly:"bun run build",check:"bun run format && bun run lint:fix && bun run typecheck",format:"oxfmt --write .",lint:"oxlint --tsconfig tsconfig.oxlint.json .","lint:fix":"oxlint --format agent --tsconfig tsconfig.oxlint.json --fix .",test:"bun test",typecheck:"tsc --noEmit"},dependencies:{"@effect/platform-bun":"^4.0.0-beta.66",effect:"^4.0.0-beta.66","shell-quote":"^1.8.3"},devDependencies:{"@types/bun":"^1.3.14","@types/shell-quote":"^1.7.5",oxfmt:"^0.50.0",oxlint:"^1.65.0","oxlint-tsgolint":"^0.22.1",typescript:"^6.0.3"},engines:{bun:">=1.0"}};var N=require("os"),z=globalThis.Bun,X="tripwire-hook",$=(b)=>{if(!b)return[[{hooks:[{type:"command",command:X}]}],!1];let q=!1,x=b.map((L)=>({hooks:L.hooks.map((D)=>{if(D.command===X||D.command.endsWith("/tripwire-hook")){if(D.command!==X)return q=!0,{...D,command:X};return D}return D})}));if(x.some((L)=>L.hooks.some((D)=>D.command===X)))return[x,!q];return[[...x,{hooks:[{type:"command",command:X}]}],!1]},J=async()=>{let b=`${N.homedir()}/.claude/settings.json`,q=z.file(b);try{let x=await q.text(),y=JSON.parse(x);y.hooks??={};let[G,L]=$(y.hooks.PreToolUse),[D,j]=$(y.hooks.PostToolUse);if(y.hooks.PreToolUse=G,y.hooks.PostToolUse=D,L&&j)return{success:!0,message:`Already configured: ${b}`};return await q.write(`${JSON.stringify(y,null,2)}
4
+ `),{success:!0,message:`Updated ${b}`}}catch(x){let y=x instanceof Error?x.message:String(x);if(y.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update Claude config: ${y}`}}},K=async()=>{let b=`${N.homedir()}/.pi/agent/settings.json`,q=z.file(b);try{let x=await q.text(),y=JSON.parse(x);y.hooks??={};let[G,L]=$(y.hooks.PreToolUse),[D,j]=$(y.hooks.PostToolUse);if(y.hooks.PreToolUse=G,y.hooks.PostToolUse=D,L&&j)return{success:!0,message:`Already configured: ${b}`};return await q.write(`${JSON.stringify(y,null,2)}
5
+ `),{success:!0,message:`Updated ${b}`}}catch(x){let y=x instanceof Error?x.message:String(x);if(y.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update pi config: ${y}`}}},W=async()=>{let b=`${N.homedir()}/.codex/config.toml`,q=`${N.homedir()}/.codex/hooks.json`,x=z.file(q),y=z.file(b),G=!1,L=!1;try{let D=await x.text(),j=JSON.parse(D);j.hooks??={};let[Y,V]=$(j.hooks.PreToolUse),[B,A]=$(j.hooks.PostToolUse);if(j.hooks.PreToolUse=Y,j.hooks.PostToolUse=B,!V||!A)G=!0;let Z=(H)=>{return H?.map((O)=>({hooks:O.hooks.map((M)=>{if(M.command===X&&M.timeout===void 0)return{...M,timeout:10};return M})}))??[]};if(j.hooks.PreToolUse=Z(j.hooks.PreToolUse),j.hooks.PostToolUse=Z(j.hooks.PostToolUse),G)await x.write(`${JSON.stringify(j,null,2)}
6
6
  `)}catch(D){let j=D instanceof Error?D.message:String(D);if(j.includes("No such file"))return{success:!1,message:`Config file not found: ${q}`};return{success:!1,message:`Failed to update Codex hooks.json: ${j}`}}try{let j=await y.text();if(j.includes("hooks = true"));else if(L=!0,j.includes("[features]")){let Y=j.indexOf("[features]"),V=j.indexOf(`
7
7
  [`,Y+1);if(V===-1)j+=`
8
8
  hooks = true`;else j=`${j.slice(0,V)}
9
9
  hooks = true${j.slice(V)}`}else j+=`
10
10
  [features]
11
- hooks = true`;if(L)await y.write(j)}catch(D){let j=D instanceof Error?D.message:String(D);if(j.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update Codex config.toml: ${j}`}}if(!G&&!L)return{success:!0,message:`Already configured: ${b} and ${q}`};return{success:!0,message:`Updated ${b} and ${q}`}},_=async()=>{return[{target:"claude",...await v()},{target:"codex",...await J()},{target:"pi",...await A()}]};var C="/Users/sean/dev/tripwire/src/../dist/tripwire.js",H=(b,q,x,y)=>{if(b==="Bash")return{command:q??""};if(b==="Read")return{file_path:x??""};if(b==="Write")return{file_path:x??"",content:y??""};if(b==="Edit"||b==="MultiEdit")return{file_path:x??"",old_string:"",new_string:y??""};return},O=(b)=>{let{tool:q,post:x,command:y,path:G,stdout:L,stderr:D,content:j}=b,V={hook_event_name:x?"PostToolUse":"PreToolUse",tool_name:q,cwd:process.cwd(),session_id:"tripwire-cli-test",tool_input:H(q,y,G,j)};if(x)V.tool_response=q==="Bash"?{stdout:L??"",stderr:D??""}:{content:j??""};return V},u=(b)=>Q.Effect.sync(()=>{let{command:q,content:x,path:y,post:G,stderr:L,stdout:D,tool:j}=b,Y=O({tool:j,post:G,command:q,path:y,stdout:D,stderr:L,content:x}),V=Bun.spawnSync([C],{stdin:new TextEncoder().encode(JSON.stringify(Y)),timeout:1e4,stdout:"pipe",stderr:"pipe"});if(V.exitCode!==0){let $=new TextDecoder().decode(V.stderr);console.error(`error: ${$}`),process.exit(1)}let z=new TextDecoder().decode(V.stdout);try{let $=JSON.parse(z);console.log(JSON.stringify($,null,2))}catch{console.log(z)}}),T=w.Command.make("test",{command:w.Argument.string("command").pipe(w.Argument.optional,w.Argument.withDescription("Command to test (for Bash tool)")),content:w.Flag.string("content").pipe(w.Flag.optional,w.Flag.withDescription("Content for Write/Edit tools")),path:w.Flag.string("path").pipe(w.Flag.optional,w.Flag.withDescription("File path for Read/Write/Edit tools")),post:w.Flag.boolean("post").pipe(w.Flag.withDescription("Test PostToolUse instead of PreToolUse")),stderr:w.Flag.string("stderr").pipe(w.Flag.optional,w.Flag.withDescription("Stderr for PostToolUse Bash")),stdout:w.Flag.string("stdout").pipe(w.Flag.optional,w.Flag.withDescription("Stdout for PostToolUse Bash")),tool:w.Flag.string("tool").pipe(w.Flag.withDefault("Bash"),w.Flag.withDescription("Tool name (Bash, Read, Write, Edit, MultiEdit)"))},({command:b,content:q,path:x,post:y,stderr:G,stdout:L,tool:D})=>u({command:Q.Option.getOrUndefined(b),content:Q.Option.getOrUndefined(q),path:Q.Option.getOrUndefined(x),post:y,stderr:Q.Option.getOrUndefined(G),stdout:Q.Option.getOrUndefined(L),tool:D})).pipe(w.Command.withDescription("Test a synthetic hook event")),I=(b)=>Q.Effect.gen(function*(){if(!["claude","codex","pi","all"].includes(b))console.error(`error: unknown target "${b}"`),console.error("Valid targets: claude, codex, pi, all"),process.exit(1);let q;switch(b){case"claude":{q=[{target:"claude",result:yield*Q.Effect.promise(()=>v())}];break}case"codex":{q=[{target:"codex",result:yield*Q.Effect.promise(()=>J())}];break}case"pi":{q=[{target:"pi",result:yield*Q.Effect.promise(()=>A())}];break}case"all":{q=(yield*Q.Effect.promise(()=>_())).map((G)=>({target:G.target,result:G}));break}default:{q=[];break}}let x=!1;for(let{target:y,result:G}of q)if(G.success){let L=G.message.startsWith("Already configured")?"\u2299":"\u2713";console.log(`${L} [${y}] ${G.message}`)}else console.error(`\u2717 [${y}] ${G.message}`),x=!0;if(x)process.exit(1)}),P=w.Command.make("install",{target:w.Argument.string("target").pipe(w.Argument.withDescription("Target agent (claude, codex, pi, or all)"))},({target:b})=>I(b)).pipe(w.Command.withDescription("Install tripwire hooks for AI agents")),F=w.Command.make("tripwire").pipe(w.Command.withDescription("Opinionated hooks dispatcher for AI coding agents"),w.Command.withSubcommands([T,P])),p=w.Command.run(F,{version:W.version}),c=async()=>{try{await Q.Effect.runPromise(p.pipe(Q.Effect.provide(S.BunServices.layer)))}catch(b){let q=b instanceof Error?b.message:String(b);console.error(q),process.exitCode=1}};c();})
11
+ hooks = true`;if(L)await y.write(j)}catch(D){let j=D instanceof Error?D.message:String(D);if(j.includes("No such file"))return{success:!1,message:`Config file not found: ${b}`};return{success:!1,message:`Failed to update Codex config.toml: ${j}`}}if(!G&&!L)return{success:!0,message:`Already configured: ${b} and ${q}`};return{success:!0,message:`Updated ${b} and ${q}`}},S=async()=>{return[{target:"claude",...await J()},{target:"codex",...await W()},{target:"pi",...await K()}]};var E="/Users/sean/dev/tripwire/src",U=_.resolve(E,"tripwire-hook"),T=_.resolve(E,"../dist/tripwire.js"),I=async()=>{try{return await R.file(U).text(),U}catch{return T}},F=(b,q,x,y)=>{if(b==="Bash")return{command:q??""};if(b==="Read")return{file_path:x??""};if(b==="Write")return{file_path:x??"",content:y??""};if(b==="Edit"||b==="MultiEdit")return{file_path:x??"",old_string:"",new_string:y??""};return},P=(b)=>{let{tool:q,post:x,command:y,path:G,stdout:L,stderr:D,content:j}=b,V={hook_event_name:x?"PostToolUse":"PreToolUse",tool_name:q,cwd:process.cwd(),session_id:"tripwire-cli-test",tool_input:F(q,y,G,j)};if(x)V.tool_response=q==="Bash"?{stdout:L??"",stderr:D??""}:{content:j??""};return V},p=(b)=>Q.Effect.gen(function*(){let{command:q,content:x,path:y,post:G,stderr:L,stdout:D,tool:j}=b,Y=P({tool:j,post:G,command:q,path:y,stdout:D,stderr:L,content:x}),V=yield*Q.Effect.promise(()=>I()),B=Bun.spawnSync([V],{stdin:new TextEncoder().encode(JSON.stringify(Y)),timeout:1e4,stdout:"pipe",stderr:"pipe"});if(B.exitCode!==0){let Z=new TextDecoder().decode(B.stderr);console.error(`error: ${Z}`),process.exit(1)}let A=new TextDecoder().decode(B.stdout);try{let Z=JSON.parse(A);console.log(JSON.stringify(Z,null,2))}catch{console.log(A)}}),k=w.Command.make("test",{command:w.Argument.string("command").pipe(w.Argument.optional,w.Argument.withDescription("Command to test (for Bash tool)")),content:w.Flag.string("content").pipe(w.Flag.optional,w.Flag.withDescription("Content for Write/Edit tools")),path:w.Flag.string("path").pipe(w.Flag.optional,w.Flag.withDescription("File path for Read/Write/Edit tools")),post:w.Flag.boolean("post").pipe(w.Flag.withDescription("Test PostToolUse instead of PreToolUse")),stderr:w.Flag.string("stderr").pipe(w.Flag.optional,w.Flag.withDescription("Stderr for PostToolUse Bash")),stdout:w.Flag.string("stdout").pipe(w.Flag.optional,w.Flag.withDescription("Stdout for PostToolUse Bash")),tool:w.Flag.string("tool").pipe(w.Flag.withDefault("Bash"),w.Flag.withDescription("Tool name (Bash, Read, Write, Edit, MultiEdit)"))},({command:b,content:q,path:x,post:y,stderr:G,stdout:L,tool:D})=>p({command:Q.Option.getOrUndefined(b),content:Q.Option.getOrUndefined(q),path:Q.Option.getOrUndefined(x),post:y,stderr:Q.Option.getOrUndefined(G),stdout:Q.Option.getOrUndefined(L),tool:D})).pipe(w.Command.withDescription("Test a synthetic hook event")),c=(b)=>Q.Effect.gen(function*(){if(!["claude","codex","pi","all"].includes(b))console.error(`error: unknown target "${b}"`),console.error("Valid targets: claude, codex, pi, all"),process.exit(1);let q;switch(b){case"claude":{q=[{target:"claude",result:yield*Q.Effect.promise(()=>J())}];break}case"codex":{q=[{target:"codex",result:yield*Q.Effect.promise(()=>W())}];break}case"pi":{q=[{target:"pi",result:yield*Q.Effect.promise(()=>K())}];break}case"all":{q=(yield*Q.Effect.promise(()=>S())).map((G)=>({target:G.target,result:G}));break}default:{q=[];break}}let x=!1;for(let{target:y,result:G}of q)if(G.success){let L=G.message.startsWith("Already configured")?"\u2299":"\u2713";console.log(`${L} [${y}] ${G.message}`)}else console.error(`\u2717 [${y}] ${G.message}`),x=!0;if(x)process.exit(1)}),f=w.Command.make("install",{target:w.Argument.string("target").pipe(w.Argument.withDescription("Target agent (claude, codex, pi, or all)"))},({target:b})=>c(b)).pipe(w.Command.withDescription("Install tripwire hooks for AI agents")),h=w.Command.make("tripwire").pipe(w.Command.withDescription("Opinionated hooks dispatcher for AI coding agents"),w.Command.withSubcommands([k,f])),i=w.Command.run(h,{version:v.version}),m=async()=>{try{await Q.Effect.runPromise(i.pipe(Q.Effect.provide(C.BunServices.layer)))}catch(b){let q=b instanceof Error?b.message:String(b);console.error(q),process.exitCode=1}};m();})
Binary file