@seanmozeik/tripwire 0.5.2 → 0.5.3
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/tripwire-cli.js +5 -5
- package/dist/tripwire-cli.js.jsc +0 -0
- package/dist/tripwire.js.jsc +0 -0
- package/package.json +1 -1
- package/src/cli.ts +13 -8
package/dist/tripwire-cli.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun @bytecode @bun-cjs
|
|
3
|
-
(function(exports, require, module, __filename, __dirname) {var
|
|
4
|
-
`),{success:!0,message:`Updated ${b}`}}catch(
|
|
5
|
-
`),{success:!0,message:`Updated ${b}`}}catch(
|
|
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: ${
|
|
3
|
+
(function(exports, require, module, __filename, __dirname) {var S=require("path"),U=require("@effect/platform-bun"),C=globalThis.Bun,Q=require("effect"),x=require("effect/unstable/cli");var W={name:"@seanmozeik/tripwire",version:"0.5.3",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 w=!1,q=b.map((L)=>({hooks:L.hooks.map((D)=>{if(D.command===X||D.command.endsWith("/tripwire-hook")){if(D.command!==X)return w=!0,{...D,command:X};return D}return D})}));if(q.some((L)=>L.hooks.some((D)=>D.command===X)))return[q,!w];return[[...q,{hooks:[{type:"command",command:X}]}],!1]},v=async()=>{let b=`${N.homedir()}/.claude/settings.json`,w=z.file(b);try{let q=await w.text(),y=JSON.parse(q);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 w.write(`${JSON.stringify(y,null,2)}
|
|
4
|
+
`),{success:!0,message:`Updated ${b}`}}catch(q){let y=q instanceof Error?q.message:String(q);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}`}}},J=async()=>{let b=`${N.homedir()}/.pi/agent/settings.json`,w=z.file(b);try{let q=await w.text(),y=JSON.parse(q);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 w.write(`${JSON.stringify(y,null,2)}
|
|
5
|
+
`),{success:!0,message:`Updated ${b}`}}catch(q){let y=q instanceof Error?q.message:String(q);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}`}}},K=async()=>{let b=`${N.homedir()}/.codex/config.toml`,w=`${N.homedir()}/.codex/hooks.json`,q=z.file(w),y=z.file(b),G=!1,L=!1;try{let D=await q.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=(R)=>{return R?.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=Z(j.hooks.PreToolUse),j.hooks.PostToolUse=Z(j.hooks.PostToolUse),G)await q.write(`${JSON.stringify(j,null,2)}
|
|
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: ${w}`};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 ${
|
|
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 ${w}`};return{success:!0,message:`Updated ${b} and ${w}`}},_=async()=>{return[{target:"claude",...await v()},{target:"codex",...await K()},{target:"pi",...await J()}]};var O=()=>{return/\/bun(\.exe)?$/.test(process.argv[0]??"")?process.argv[1]:process.argv[0]},u=async()=>{let b=O(),w=S.dirname(b),q=`${w}/tripwire-hook`;try{return await C.file(q).text(),q}catch{return`${w}/tripwire.js`}},T=(b,w,q,y)=>{if(b==="Bash")return{command:w??""};if(b==="Read")return{file_path:q??""};if(b==="Write")return{file_path:q??"",content:y??""};if(b==="Edit"||b==="MultiEdit")return{file_path:q??"",old_string:"",new_string:y??""};return},I=(b)=>{let{tool:w,post:q,command:y,path:G,stdout:L,stderr:D,content:j}=b,V={hook_event_name:q?"PostToolUse":"PreToolUse",tool_name:w,cwd:process.cwd(),session_id:"tripwire-cli-test",tool_input:T(w,y,G,j)};if(q)V.tool_response=w==="Bash"?{stdout:L??"",stderr:D??""}:{content:j??""};return V},F=(b)=>Q.Effect.gen(function*(){let{command:w,content:q,path:y,post:G,stderr:L,stdout:D,tool:j}=b,Y=I({tool:j,post:G,command:w,path:y,stdout:D,stderr:L,content:q}),V=yield*Q.Effect.promise(()=>u()),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)}}),P=x.Command.make("test",{command:x.Argument.string("command").pipe(x.Argument.optional,x.Argument.withDescription("Command to test (for Bash tool)")),content:x.Flag.string("content").pipe(x.Flag.optional,x.Flag.withDescription("Content for Write/Edit tools")),path:x.Flag.string("path").pipe(x.Flag.optional,x.Flag.withDescription("File path for Read/Write/Edit tools")),post:x.Flag.boolean("post").pipe(x.Flag.withDescription("Test PostToolUse instead of PreToolUse")),stderr:x.Flag.string("stderr").pipe(x.Flag.optional,x.Flag.withDescription("Stderr for PostToolUse Bash")),stdout:x.Flag.string("stdout").pipe(x.Flag.optional,x.Flag.withDescription("Stdout for PostToolUse Bash")),tool:x.Flag.string("tool").pipe(x.Flag.withDefault("Bash"),x.Flag.withDescription("Tool name (Bash, Read, Write, Edit, MultiEdit)"))},({command:b,content:w,path:q,post:y,stderr:G,stdout:L,tool:D})=>F({command:Q.Option.getOrUndefined(b),content:Q.Option.getOrUndefined(w),path:Q.Option.getOrUndefined(q),post:y,stderr:Q.Option.getOrUndefined(G),stdout:Q.Option.getOrUndefined(L),tool:D})).pipe(x.Command.withDescription("Test a synthetic hook event")),p=(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 w;switch(b){case"claude":{w=[{target:"claude",result:yield*Q.Effect.promise(()=>v())}];break}case"codex":{w=[{target:"codex",result:yield*Q.Effect.promise(()=>K())}];break}case"pi":{w=[{target:"pi",result:yield*Q.Effect.promise(()=>J())}];break}case"all":{w=(yield*Q.Effect.promise(()=>_())).map((G)=>({target:G.target,result:G}));break}default:{w=[];break}}let q=!1;for(let{target:y,result:G}of w)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}`),q=!0;if(q)process.exit(1)}),k=x.Command.make("install",{target:x.Argument.string("target").pipe(x.Argument.withDescription("Target agent (claude, codex, pi, or all)"))},({target:b})=>p(b)).pipe(x.Command.withDescription("Install tripwire hooks for AI agents")),c=x.Command.make("tripwire").pipe(x.Command.withDescription("Opinionated hooks dispatcher for AI coding agents"),x.Command.withSubcommands([P,k])),f=x.Command.run(c,{version:W.version}),h=async()=>{try{await Q.Effect.runPromise(f.pipe(Q.Effect.provide(U.BunServices.layer)))}catch(b){let w=b instanceof Error?b.message:String(b);console.error(w),process.exitCode=1}};h();})
|
package/dist/tripwire-cli.js.jsc
CHANGED
|
Binary file
|
package/dist/tripwire.js.jsc
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// Bun src/cli.ts install pi
|
|
15
15
|
// Bun src/cli.ts install all
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { dirname } from 'node:path';
|
|
18
18
|
|
|
19
19
|
import { BunServices } from '@effect/platform-bun';
|
|
20
20
|
import { file } from 'bun';
|
|
@@ -24,19 +24,24 @@ import { Argument, Command, Flag } from 'effect/unstable/cli';
|
|
|
24
24
|
import pkg from '../package.json' with { type: 'json' };
|
|
25
25
|
import { installAll, installClaude, installCodex, installPi } from './lib/install';
|
|
26
26
|
|
|
27
|
-
// Resolve tripwire-hook path
|
|
28
|
-
|
|
27
|
+
// Resolve tripwire-hook path at runtime using process.argv
|
|
28
|
+
// This works in both script mode (bun run) and compiled/bundled mode
|
|
29
|
+
const runtimeSelf = (): string => {
|
|
30
|
+
const isBunCli = /\/bun(\.exe)?$/.test(process.argv[0] ?? '');
|
|
31
|
+
return isBunCli ? process.argv[1]! : process.argv[0]!;
|
|
32
|
+
};
|
|
29
33
|
|
|
30
|
-
// Try installed location first (both binaries in same dir), fall back to dev location
|
|
31
|
-
const installedPath = resolve(cliDir, 'tripwire-hook');
|
|
32
|
-
const devPath = resolve(cliDir, '../dist/tripwire.js');
|
|
33
34
|
const dispatchBin = async (): Promise<string> => {
|
|
35
|
+
const cliPath = runtimeSelf();
|
|
36
|
+
const cliDir = dirname(cliPath);
|
|
37
|
+
// Try tripwire-hook in same directory first (installed scenario)
|
|
38
|
+
const installedPath = `${cliDir}/tripwire-hook`;
|
|
34
39
|
try {
|
|
35
|
-
// Check if installed path exists
|
|
36
40
|
await file(installedPath).text();
|
|
37
41
|
return installedPath;
|
|
38
42
|
} catch {
|
|
39
|
-
|
|
43
|
+
// Fallback to development scenario: tripwire-hook in ../dist relative to CLI
|
|
44
|
+
return `${cliDir}/tripwire.js`;
|
|
40
45
|
}
|
|
41
46
|
};
|
|
42
47
|
|