cc-safety-net 2.4.12 → 2.4.14
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/README.md +1 -1
- package/dist/amp/cc-safety-net/index.ts +11 -11
- package/dist/api.js +1 -1
- package/dist/bin/hook.js +40 -40
- package/dist/chunks/{index-q47ejf84.js → index-jb96wweh.js} +2 -2
- package/dist/chunks/{index-kwg376ge.js → index-qbhpx1zs.js} +9 -9
- package/dist/cli.js +126 -117
- package/dist/index.js +7 -7
- package/dist/openclaw/cc-safety-net/index.js +36 -36
- package/dist/openclaw/cc-safety-net/openclaw.plugin.json +1 -1
- package/dist/openclaw/cc-safety-net/package.json +1 -1
- package/dist/pi/index.js +2 -2
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,53 +1,52 @@
|
|
|
1
|
-
import{a,s,ke,Ue,k,Ze,Ge,c,o,He,R,L,ve,Qe,f,We,d,r,v,i,re,n,g,le,F,tt,nt,l,ue,de,rt,I,K,oe,S,ot,M,b,Se,j,u,J,Be,ze,O,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,Y,U,Pe,me,G,Ke,H,Z,X,ae,Je,Ye,C,W,Ae,Oe,De,E,Ne,Ee,Fe,h,t,Q,ge,y,x,_,st,Me,$e,p,B}from"./chunks/index-
|
|
2
|
-
${
|
|
3
|
-
`).filter(Boolean).flatMap((q)=>{try{let V=JSON.parse(q);if(!
|
|
4
|
-
`)}function Gi(P,A){let q=new Date(P);if(Number.isNaN(q.getTime()))return P;return new Intl.DateTimeFormat("sv-SE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:A}).format(q)}function ji(P){let A=Number(P);return Number.isFinite(A)&&A>0?A:null}var Bi={name:"doctor",aliases:["--doctor"],description:"Run diagnostic checks to verify installation and configuration",usage:"doctor [options]",options:[{flags:"--json",description:"Output diagnostics as JSON"},{flags:"--skip-update-check",description:"Skip npm registry version check"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net doctor","cc-safety-net doctor --json","cc-safety-net doctor --skip-update-check"]};var qi={name:"explain",description:"Show step-by-step analysis trace of how a command would be analyzed",usage:"explain [options] <command>",argument:"<command>",options:[{flags:"--json",description:"Output analysis as JSON"},{flags:"--cwd",argument:"<path>",description:"Use custom working directory"},{flags:"-h, --help",description:"Show this help"}],examples:['cc-safety-net explain "git reset --hard"','cc-safety-net explain --json "rm -rf /"','cc-safety-net explain --cwd /tmp "git status"']};var Vi={name:"gui",description:"Open the local policy editor GUI",usage:"gui [options]",options:[{flags:"--no-open",description:"Print the URL without opening a browser"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net gui","cc-safety-net gui --no-open"]};var Yn=[{id:"antigravity-cli",displayName:"Antigravity CLI",doctorOrder:3,runtime:{order:1,flags:["-ac","--agy-cli"],description:"Run as Antigravity CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:2,flag:"--agy-cli",artifactKind:"hook config",probeCommand:["agy","--version"]}},{id:"claude-code",displayName:"Claude Code",doctorOrder:1,runtime:{order:2,displayName:"Coding CLI",flags:["-cc","--coding-cli"],legacyFlags:["--claude-code"],description:"Run as Coding CLI PreToolUse hook",legacyTopLevelFlags:["-cc","--claude-code"]},install:{order:3,flag:"--claude-code",artifactKind:"plugin",probeCommand:["claude","--version"]}},{id:"codex",displayName:"Codex",doctorOrder:4,runtime:{order:3,flags:["-cx","--codex"],description:"Run as a Codex PreToolUse hook",legacyTopLevelFlags:[]},install:{order:4,flag:"--codex",artifactKind:"plugin",probeCommand:["codex","--version"]}},{id:"copilot-cli",displayName:"GitHub Copilot CLI",doctorOrder:7,runtime:{order:6,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:7,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:6,runtime:{order:5,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:6,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:8,runtime:{order:7,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:9,runtime:{order:8,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:9,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:10,runtime:{order:9,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:10,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:11,install:{order:11,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:12,install:{order:12,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:13,install:{order:13,flag:"--pi",artifactKind:"package",probeCommand:["pi","--version"]}},{id:"cursor",displayName:"Cursor",doctorOrder:5,runtime:{order:4,flags:["-cu","--cursor"],description:"Run as Cursor preToolUse hook",legacyTopLevelFlags:[]},install:{order:5,flag:"--cursor",artifactKind:"hook config",probeCommand:["cursor","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],Zn=Yn.slice().sort((P,A)=>P.doctorOrder-A.doctorOrder).map((P)=>P.id),Sn=Yn.filter((P)=>("runtime"in P)).slice().sort((P,A)=>P.runtime.order-A.runtime.order).map((P)=>({id:P.id,displayName:"displayName"in P.runtime?P.runtime.displayName:P.displayName,flags:P.runtime.flags,legacyFlags:"legacyFlags"in P.runtime?P.runtime.legacyFlags:[],description:P.runtime.description,legacyTopLevelFlags:P.runtime.legacyTopLevelFlags})),$t=Yn.slice().sort((P,A)=>P.install.order-A.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...A})=>A),fu=Object.fromEntries(Yn.map((P)=>[P.id,P.displayName]));function vt(P){return fu[P]}var mu=Sn.map((P)=>({flags:P.flags.join(", "),description:P.description})),gu=Sn.flatMap((P)=>P.flags.map((A)=>`cc-safety-net hook ${A}`)),zi={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...mu,{flags:"-h, --help",description:"Show this help"}],examples:gu};var Ji={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Install ${vt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...$t.map((P)=>`cc-safety-net install ${P.flag}`)]},Wi={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Uninstall ${vt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...$t.map((P)=>`cc-safety-net uninstall ${P.flag}`)]},Ki={name:"update",description:"Update every installed CC Safety Net integration to the latest version",usage:"update",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net update"]};var Yi={name:"logs",description:"Browse audit log entries recorded by hooks",usage:"logs [options]",options:[{flags:"--id",argument:"<id>",description:"Show one entry from retained history by its 16-character id (not guaranteed once it is older than the configured retention)"},{flags:"--limit",argument:"<n>",description:"Maximum entries to print",default:"20"},{flags:"--since",argument:"<days>",description:"Only include entries newer than this many days (max: the configured audit retention, 1-365)",default:"30"},{flags:"--agent",argument:"<name>",description:"Filter by agent name"},{flags:"--rule",argument:"<ruleId>",description:"Filter by rule id"},{flags:"--session",argument:"<id>",description:"Filter by session id"},{flags:"--project",argument:"<path>",description:"Filter by project path"},{flags:"--suspect",description:"Only denials that look like false positives"},{flags:"--all",description:"Include allow entries"},{flags:"--prune-legacy",description:"Permanently delete all legacy root-level logs; nested logs are untouched"},{flags:"--dry-run",description:"With --prune-legacy, report what would be deleted and delete nothing"},{flags:"--json",description:"Output entries as JSON"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net logs --id 3fa9c2d1a70e8b42","cc-safety-net logs --agent claude-code","cc-safety-net logs --project . --since 7","cc-safety-net logs --suspect --since 7","cc-safety-net logs --json","cc-safety-net logs --prune-legacy --dry-run","cc-safety-net logs --prune-legacy"]};var Xn={name:"policy",description:"Check and apply project or user policy proposals",usage:"policy <subcommand>",subcommands:[{usage:"check <file>",description:"Validate a policy proposal and print its diff"},{usage:"apply <file>",description:"Apply a proposal after confirming in a terminal"}],options:[{flags:"-g, --global",description:"Use the user-scope policy instead of the project one"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net policy check proposal.json","cc-safety-net policy apply proposal.json","cc-safety-net policy apply proposal.json --global"]};var eo=[{flags:"--ref",argument:"<ref>",description:"Use a branch, tag, or commit"},{flags:"--only",argument:"<rulebook...>",description:"Add only these repository rulebooks"},{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"-h, --help",description:"Show this help"}],to=["cc-safety-net rule add project-rules","cc-safety-net rule add acme/safety-rules","cc-safety-net rule add acme/safety-rules --only aws gcloud","cc-safety-net rule add acme/safety-rules --ref v2 --only aws","cc-safety-net rule add --only terraform aws"],fn={name:"rule",description:"Manage CC Safety Net rule config and rulebook sources",usage:"rule <subcommand>",subcommands:[{usage:"init [--example]",description:"Create inert rule config"},{usage:"add [source] [--ref <ref>] [--only <rulebook...>]",description:"Add rulebook sources and sync"},{usage:"remove <source>",description:"Remove a rulebook source and sync"},{usage:"update [source]",description:"Re-fetch and vendor remote rulebooks"},{usage:"sync",description:"Deprecated: migrate lock and cache leftovers"},{usage:"list",description:"List active rulebooks"},{usage:"wrapper add <command>",description:"Trust a transparent command wrapper"},{usage:"wrapper remove <command>",description:"Remove a transparent command wrapper"},{usage:"wrapper list",description:"List transparent command wrappers"},{usage:"migrate [--cleanup]",description:"Migrate legacy inline rules"},{usage:"doc",description:"Print the rulebook authoring guide"},{usage:"verify",description:"Validate rule config files"}],options:[{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"--cleanup",description:"Delete legacy files after rule migrate verifies them"},{flags:"--delete-source",description:"Delete clean local source directory on remove"},{flags:"--example",description:"Create an inactive example rulebook with rule init"},...eo.slice(0,2),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net rule init","cc-safety-net rule init --example","cc-safety-net rule wrapper add rtk",...to,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var Zi={name:"status",description:"Show what the runtime is enforcing right now",usage:"status",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net status"]};var Xi={name:"statusline",description:"Print status line with mode indicators for shell integration",usage:"statusline --claude-code",options:[{flags:"-cc, --claude-code",description:"Print status line for Claude Code"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net statusline -cc","cc-safety-net statusline --claude-code"]};var Qn=[Zi,Bi,Yi,qi,fn,Xn,Ji,Ki,Wi,zi,Vi,Xi];function hu(P){return P.aliases??[]}function er(P){let A=P.toLowerCase();return Qn.find((q)=>q.name.toLowerCase()===A||hu(q).some((V)=>V.toLowerCase()===A))}import{basename as yu}from"node:path";function tr(P,A=7,q=D(P)){let V=Date.now()-A*24*60*60*1000,ee=[],ne=new Set,ce=0,he,ye,be,xe;if(q)z(P,q);let Ie={count:0},Xe=q?Yt(q,Ie):[];for(let Re of Xe)for(let at of pn(Re,Ie)){if(at.decision==="allow")continue;let ct=new Date(at.ts).getTime();if(ct>=V){if(ce++,ne.add(at.sessionId??yu(Re,".jsonl")),ye===void 0||ct<=ye)he=at.ts,ye=ct;if(xe===void 0||ct>xe)be=at.ts,xe=ct;vu(ee,at,ct)}}let et=ee.map((Re)=>({timestamp:Re.ts,command:Re.command,reason:Re.reason,relativeTime:Oi(new Date(Re.ts))}));return{totalBlocked:ce,sessionCount:ne.size,recentEntries:et,oldestEntry:he,newestEntry:be,unreadable:Ie.count}}function vu(P,A,q){let V=P.findIndex((ee)=>q>new Date(ee.ts).getTime());if(V===-1){if(P.length<3)P.push(A);return}if(P.splice(V,0,A),P.length>3)P.pop()}import{dirname as Au}from"node:path";import{dirname as bu,join as Lu,resolve as wu}from"node:path";var ku="config.json";function Rt(P,A,q,V){g(xu(P),`${JSON.stringify(A,null,2)}
|
|
5
|
-
`,q,V)}function xu(P){return typeof P==="string"?re(P):P}function ro(P){return{errors:ie(Su(P),": "," "),ruleNames:new Set(_e(P).map((A)=>A.toLowerCase()))}}var Cu="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",Qi="must match pattern (letters, numbers, hyphens, underscores)";function Su(P){if(!es(P))return[e([],"Config must be an object")];return[...P.version===1?[]:[e(["version"],"must be 1")],...Ru(P.rules)]}function Ru(P){if(P===void 0)return[];if(!Array.isArray(P))return[e(["rules"],"must be an array")];return[...P.flatMap((A,q)=>es(A)?Pu(A,["rules",q]):[e(["rules",q],"must be an object")]),...Te(P)]}function Pu(P,A){return[...no(P.name,[...A,"name"],"required string",l,Cu),...no(P.command,[...A,"command"],"required string",w,Qi),...P.subcommand===void 0?[]:no(P.subcommand,[...A,"subcommand"],"must be a string if provided",w,Qi),...Eu(P.block_args,[...A,"block_args"]),...Du(P.reason,[...A,"reason"]),...P.intent===void 0||Ce(P.intent)?[]:[e([...A,"intent"],we)]]}function no(P,A,q,V,ee){if(typeof P!=="string")return[e(A,q)];return V.test(P)?[]:[e(A,ee)]}function Eu(P,A){if(!Array.isArray(P))return[e(A,"required array")];if(P.length===0)return[e(A,"must have at least one element")];return P.flatMap((q,V)=>{if(typeof q!=="string")return[e([...A,V],"must be a string")];return q===""?[e([...A,V],"must not be empty")]:[]})}function Du(P,A){if(typeof P!=="string")return[e(A,"required string")];if(P==="")return[e(A,"must not be empty")];return P.length>T?[e(A,`must be at most ${T} characters`)]:[]}function es(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function oo(P){let A=ts(P);if(!A.ok)return A.result;return ro(A.parsed)}function ts(P){let A=[],q=new Set;try{let V=typeof P==="string"?re(P):P,ee=n(V);if(ee===null)return A.push(`File not found: ${V.path}`),{ok:!1,result:{errors:A,ruleNames:q}};if(!ee.trim())return A.push("Config file is empty"),{ok:!1,result:{errors:A,ruleNames:q}};return{ok:!0,parsed:JSON.parse(ee)}}catch(V){if(V instanceof r)return A.push(V.message),{ok:!1,result:{errors:A,ruleNames:q}};let ee=V instanceof Error?V.message:String(V);return A.push(V instanceof SyntaxError?"Invalid JSON":ee),{ok:!1,result:{errors:A,ruleNames:q}}}}function ns(P){return wu(P,".safety-net.json")}function Jt(P){let A=ts(P);if(!A.ok)return A.result;let q=Ve(A.parsed);return{errors:q.errors,ruleNames:q.sources}}function nr(P,A={}){return Lu(bu(Se(P,A)),ku)}function rs(P,A,q){let V;try{if(n(A)===null)return{path:P,exists:!1,valid:!1,ruleCount:0};V=Jt(A),V.errors.push(...U(P,q))}catch(ee){if(!(ee instanceof r))throw ee;V={errors:[ee.message],ruleNames:new Set}}return{path:P,exists:!0,valid:V.errors.length===0,ruleCount:V.ruleNames.size,...V.errors.length>0?{errors:V.errors}:{}}}function _u(P,A){return{source:A,name:P.name,command:P.command,subcommand:P.subcommand,blockArgs:[...P.block_args],reason:P.reason}}function os(P,A){let q=j(P),V=M(A),ee=Au(q),ne=Y(P,{cwd:A,userConfigPath:q,projectConfigPath:V,userConfigDir:ee}),ce=J(P,{cwd:A,userConfigPath:q,projectConfigPath:V,userConfigDir:ee}),he=new Map(ne.rulebooks.flatMap((ye)=>ye.rules.map((be)=>[be,ye.source])));return{userConfig:rs(q,ce.userConfigTarget,ce.userScope),projectConfig:rs(V,ce.projectConfigTarget,ce.projectScope),effectiveRules:ne.rules.map((ye)=>_u(ye,he.get(ye.name)??"project"))}}var Tu=[{flag:o.level,description:"Safety level preset: standard, strict, or paranoid",defaultBehavior:"standard"},{flag:o.strict,description:"Legacy; equivalent to safety.overrides.fail_closed",defaultBehavior:"permissive"},{flag:o.paranoid,description:"Legacy; equivalent to safety.overrides.paranoid_rm and paranoid_interpreters",defaultBehavior:"off"},{flag:o.paranoidRm,description:"Legacy; equivalent to safety.overrides.paranoid_rm",defaultBehavior:"off"},{flag:o.paranoidInterpreters,description:"Legacy; equivalent to safety.overrides.paranoid_interpreters",defaultBehavior:"off"},{flag:o.worktree,description:"Allow local git discards in linked worktrees",defaultBehavior:"off"},{flag:o.debug,description:"Print diagnostic messages to stderr",defaultBehavior:"off"},{flag:o.auditScope,description:"Command decisions recorded: all, or blocked (privacy-minimizing, denials only)",defaultBehavior:"all"}];function is(P){return[...Tu.map((A)=>({name:A.flag.name,value:ve(A.flag,P.env),isSet:Qe(A.flag,P.env),legacyName:A.flag.legacyName,legacyValue:A.flag.legacyName?P.env.get(A.flag.legacyName):void 0,legacyIsSet:A.flag.legacyName?P.env.get(A.flag.legacyName)!==void 0:void 0,description:A.description,defaultBehavior:A.defaultBehavior})),{name:"CC_SAFETY_NET_HOME",value:P.env.get("CC_SAFETY_NET_HOME"),isSet:P.env.get("CC_SAFETY_NET_HOME")!==void 0,description:"Override user-scope config/cache directory",defaultBehavior:"~/.cc-safety-net"}]}var ss={error:0,warning:1,info:2},$u=["policy","config","audit"];function Iu(P){return P.map((A)=>{if(A==="ownership")return"is not owned by the current user";if(A==="permissions")return"has unsafe permissions";if(A==="symlink")return"is a symbolic link";return"is not a directory"}).join(" and ")}var Ou=[{derive:(P)=>P.hooks.length>0&&P.hooks.every((A)=>!A.configured)?[{checkId:"integration.none-configured",severity:"error",title:"No integration configured",detail:"CC Safety Net is not connected to any supported coding-agent integration.",fixHint:"Run `cc-safety-net install` and configure at least one integration."}]:[]},{derive:(P)=>P.hooks.filter((A)=>A.inspectionStatus==="failed").map((A)=>{let q=vt(A.platform);return{checkId:"integration.inspection-failed",severity:"error",title:`${q} inspection failed`,detail:`Doctor could not verify the ${q} integration configuration.`,fixHint:`Correct the reported ${q} configuration error, then run \`cc-safety-net doctor\` again.`,integration:A.platform}})},{derive:(P)=>P.userConfig.exists&&!P.userConfig.valid?[{checkId:"config.user-invalid",severity:"error",title:"User configuration is invalid",detail:"Doctor could not load a valid user rules configuration.",fixHint:"Run `cc-safety-net rule verify`, correct the reported error, then rerun doctor.",path:P.userConfig.path}]:[]},{derive:(P)=>P.projectConfig.exists&&!P.projectConfig.valid?[{checkId:"config.project-invalid",severity:"error",title:"Project configuration is invalid",detail:"Doctor could not load a valid project rules configuration.",fixHint:"Run `cc-safety-net rule verify`, correct the reported error, then rerun doctor.",path:P.projectConfig.path}]:[]},{derive:(P)=>P.configState.state==="degraded"?[{checkId:"config.runtime-degraded",severity:"warning",title:"Runtime is enforcing a fallback configuration",detail:`The rejected candidate configuration is not active: ${P.configState.reason}`,fixHint:"Fix the file named in the reason, or run `cc-safety-net rule update` to vendor a remote source, then rerun doctor."}]:[]},{derive:(P)=>P.v2Leftovers&&P.v2Leftovers.length>0?[{checkId:"config.v2-leftovers",severity:"info",title:"Rulebook lock and cache leftovers detected",detail:`Files an earlier version left behind are no longer read: ${P.v2Leftovers.join(", ")}.`,fixHint:"Run `cc-safety-net rule sync` (add `--global` for user scope) to migrate them, then rerun doctor."}]:[]},{derive:(P)=>{let A=P.environment.find((q)=>q.name==="CC_SAFETY_NET_AUDIT_SCOPE");return He(A?.value)==="invalid"?[{checkId:"environment.audit-scope-invalid",severity:"warning",title:"Audit scope value is invalid",detail:"CC_SAFETY_NET_AUDIT_SCOPE is not `all` or `blocked`, so allowed command decisions are not recorded.",fixHint:"Set CC_SAFETY_NET_AUDIT_SCOPE to `all` or `blocked`, then restart the integration."}]:[]}},...$u.map((P)=>({derive:(A)=>A.posture.directories.filter((q)=>q.kind===P&&q.status==="unsafe").map((q)=>({checkId:`posture.${P}-directory-unsafe`,severity:"error",title:`${P[0]?.toUpperCase()}${P.slice(1)} directory is unsafe`,detail:`The ${P} directory ${Iu(q.issues)}.`,fixHint:"Ensure this is a real directory owned by the current user with no group or other write access, then rerun doctor.",...q.path?{path:q.path}:{}}))})),{derive:(P)=>{let A=[...P.effectiveSafety.weakenedRuleOverrides].sort();return A.length>0?[{checkId:"posture.rule-overrides-weaken-preset",severity:"warning",title:"Rule overrides weaken the selected preset",detail:`Explicit overrides disable rules the resolved preset would enable: ${A.join(", ")}.`,fixHint:`Remove these \`off\` overrides or set them to \`on\`: ${A.join(", ")}.`}]:[]}}];function as(P){return Ou.flatMap((A,q)=>A.derive(P).map((V,ee)=>({finding:V,catalogOrder:q,occurrence:ee}))).sort((A,q)=>ss[A.finding.severity]-ss[q.finding.severity]||A.catalogOrder-q.catalogOrder||A.occurrence-q.occurrence).map((A)=>A.finding)}function Ut(){return Boolean(process.stdout.isTTY&&!process.env.NO_COLOR)}var Fu=(P)=>Ut()?`\x1B[32m${P}\x1B[0m`:P,ju=(P)=>Ut()?`\x1B[33m${P}\x1B[0m`:P,Nu=(P)=>Ut()?`\x1B[34m${P}\x1B[0m`:P,Mu=(P)=>Ut()?`\x1B[36m${P}\x1B[0m`:P,Hu=(P)=>Ut()?`\x1B[31m${P}\x1B[0m`:P,Uu=(P)=>Ut()?`\x1B[2m${P}\x1B[0m`:P,Gu=(P)=>Ut()?`\x1B[1m${P}\x1B[0m`:P,dt={green:Fu,yellow:ju,blue:Nu,cyan:Mu,red:Hu,dim:Uu,bold:Gu},Bu="\x1B[0m",qu=[39,82,198,226,208,51,196,46,201,214,93,154,220,27,49,190,200,33,129,227,45,160,63,118,123,202];function Vu(P){let A=P;return()=>(A=(A*1664525+1013904223)%4294967296,A/4294967296)}function zu(P){let A=[...qu],q=Vu(P);for(let V=A.length-1;V>0;V--){let ee=Math.floor(q()*(V+1)),ne=A[V];A[V]=A[ee],A[ee]=ne}return A}function Ju(P,A=0){if(!Ut())return"";let q=zu(A);return`\x1B[38;5;${q[P%q.length]}m`}function ls(P,A,q=0){if(!Ut())return`"${P}"`;return`${Ju(A,q)}"${P}"${Bu}`}function rr(P){return P==="default"?"built-in default":`${P} policy`}var Wu=new RegExp("\x1B\\[[0-9;]*m","g"),io=(P)=>P.replace(Wu,"").length;function Zt(P){let A=(P.headers??P.rows[0]??[]).map((ce,he)=>{let ye=Math.max(...P.rows.map((be)=>io(be[he]??"")));return Math.max(io(ce),ye)}),q=(ce,he)=>ce+" ".repeat(Math.max(0,he-io(ce))),V=(ce,he)=>he[0]+A.map((ye)=>ce.repeat(ye+2)).join(he[1])+he[2],ee=(ce)=>`│ ${ce.map((he,ye)=>q(he,A[ye]??0)).join(" │ ")} │`,ne=P.headers?[` ${ee(P.headers)}`,` ${V("─",["├","┼","┤"])}`]:[];return[` ${V("─",["┌","┬","┐"])}`,...ne,...P.rows.map((ce)=>` ${ee(ce)}`),` ${V("─",["└","┴","┘"])}`].join(`
|
|
6
|
-
`)}function
|
|
7
|
-
`)}function
|
|
8
|
-
`)}function
|
|
9
|
-
`)}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
1
|
+
import{a,s,ke,Ue,k,Ze,Ge,c,o,He,R,L,ve,Qe,f,We,d,r,v,i,re,n,g,le,F,tt,nt,l,ue,de,rt,I,K,oe,S,ot,M,b,Se,j,u,J,Be,ze,O,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,Y,U,Pe,me,G,Ke,H,Z,X,ae,Je,Ye,C,W,Ae,Oe,De,E,Ne,Ee,Fe,h,t,Q,ge,y,x,_,st,Me,$e,p,B}from"./chunks/index-qbhpx1zs.js";import{te,z,je,D,N}from"./chunks/index-jb96wweh.js";var Zd=["-h","--help"];function xt(P,A){let q=Object.entries(P.booleans??{}),V=Object.entries(P.values??{}),ee=Object.entries(P.lists??{}),ne=Object.fromEntries(q.map(([Xe])=>[Xe,!1])),ce={},he=Object.fromEntries(ee.map(([Xe])=>[Xe,[]])),ye=[],be=[],xe=!1,Ie=-1;for(let[Xe,et]of A.entries()){if(Xe<=Ie)continue;if(et==="--"){ye.push(...A.slice(Xe+1));break}if(Zd.includes(et)){xe=!0;continue}let Re=q.find(([,lt])=>lt.includes(et));if(Re){ne[Re[0]]=!0;continue}let at=V.find(([,lt])=>lt.includes(et));if(at){let lt=A[Xe+1];if(lt===void 0||lt.startsWith("-")){be.push(`${et} requires a value`);continue}ce[at[0]]=lt,Ie=Xe+1;continue}let ct=ee.find(([,lt])=>lt.includes(et));if(ct){let lt=A.slice(Xe+1),ft=lt.findIndex((gt)=>gt.startsWith("-")),mt=lt.slice(0,ft===-1?lt.length:ft);if(mt.length===0){be.push(`${et} requires at least one value`);continue}he[ct[0]]=[...he[ct[0]]??[],...mt],Ie=Xe+mt.length;continue}if(et.startsWith("-")){be.push(`Unknown option for ${P.label}: ${et}`);continue}if(P.positionals==="tail"){ye.push(...A.slice(Xe));break}ye.push(et)}if(P.positionals!=="list"&&P.positionals!=="tail")be.push(...ye.map((Xe)=>`Unexpected argument for ${P.label}: ${Xe}`));return{flags:ne,values:ce,lists:he,positionals:ye,help:xe,errors:be}}function Ut(P){for(let A of P)console.error(A);return P.length>0}import{readdirSync as iu,statSync as Vi,unlinkSync as su}from"node:fs";import{basename as zi,dirname as au,isAbsolute as lu,join as cu,relative as du,resolve as uu,sep as pu}from"node:path";var Gi=(P)=>{let A=Date.now()-new Date(P).getTime();if(!Number.isFinite(A))return"";let q=Math.floor(A/60000),V=Math.floor(q/60),ee=Math.floor(V/24);if(ee>0)return`${ee}d ago`;if(V>0)return`${V}h ago`;if(q>0)return`${q}m ago`;return"just now"},so=(P)=>{let A=(P??"").trim().split(/\s+/).filter((ee)=>ee&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(ee)),q=A[0]?.split("/").pop();if(!q)return null;let V=A[1];return V&&/^[a-z][a-z0-9-]*$/.test(V)?`${q} ${V}`:q};function Bi(P){let A=(ee)=>`${ee.sessionId}
|
|
2
|
+
${so(ee.segment||ee.command)}`,q=P.filter((ee)=>ee.decision!=="allow"),V=q.filter((ee)=>ee.sessionId).reduce((ee,ne)=>ee.set(A(ne),(ee.get(A(ne))??0)+1),new Map);return new Set(q.filter((ee)=>ee.failureStage||(V.get(A(ee))??0)>=2))}import{existsSync as Qd,readdirSync as eu,readFileSync as tu}from"node:fs";import{join as nu}from"node:path";function Xt(P,A){try{return eu(P,{withFileTypes:!0,encoding:"utf8"}).flatMap((q)=>{let V=nu(P,q.name);if(q.isDirectory())return Xt(V,A);if(q.name.endsWith(".jsonl"))return[V];return[]})}catch{if(A&&Qd(P))A.count++;return[]}}var ru=["segment","reason","sessionId","decision","agent","ruleId","failureStage"];function ou(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let A=P;if(typeof A.ts!=="string"||typeof A.command!=="string")return!1;return ru.every((q)=>A[q]===void 0||typeof A[q]==="string")}function mn(P,A){try{return tu(P,"utf-8").split(`
|
|
3
|
+
`).filter(Boolean).flatMap((q)=>{try{let V=JSON.parse(q);if(!ou(V)){if(A)A.count++;return[]}return[V]}catch{if(A)A.count++;return[]}})}catch{if(A)A.count++;return[]}}function Ct(P){return Array.from(P,(A)=>{let q=A.charCodeAt(0);if(q<=31||q>=127&&q<=159)return`\\x${q.toString(16).padStart(2,"0")}`;return A}).join("")}function fu(P,A){let q=te(P),V=xt({label:"logs",booleans:{all:["--all"],suspect:["--suspect"],json:["--json"],pruneLegacy:["--prune-legacy"],dryRun:["--dry-run"]},values:{id:["--id"],limit:["--limit"],since:["--since"],agent:["--agent"],rule:["--rule"],session:["--session"],project:["--project"]}},A);if(Ut(V.errors))return null;if(V.values.id!==void 0&&!/^[a-f0-9]{16}$/.test(V.values.id))return console.error("--id must be 16 hexadecimal characters"),null;let ee=V.values.limit===void 0?20:qi(V.values.limit);if(ee===null)return console.error("--limit must be a positive number"),null;let ne=V.values.since===void 0?Math.min(30,q):qi(V.values.since);if(ne===null||ne>q)return console.error(`--since must be a positive number of days no greater than ${q}`),null;let ce={limit:ee,limitExplicit:V.values.limit!==void 0,since:ne,sinceExplicit:V.values.since!==void 0,all:V.flags.all,json:V.flags.json,suspect:V.flags.suspect,pruneLegacy:V.flags.pruneLegacy,dryRun:V.flags.dryRun,id:V.values.id,agent:V.values.agent,rule:V.values.rule,session:V.values.session,project:V.values.project===void 0?void 0:uu(V.values.project)};if(ce.id&&(ce.agent!==void 0||ce.rule!==void 0||ce.session!==void 0||ce.project!==void 0||ce.suspect||ce.sinceExplicit||ce.limitExplicit))return console.error("--id cannot be combined with --agent, --rule, --session, --project, --suspect, --since, or --limit"),null;if(ce.pruneLegacy&&(ce.id!==void 0||ce.agent!==void 0||ce.rule!==void 0||ce.session!==void 0||ce.project!==void 0||ce.suspect||ce.all||ce.sinceExplicit||ce.limitExplicit))return console.error("--prune-legacy cannot be combined with --id, --agent, --rule, --session, --project, --suspect, --all, --since, or --limit"),null;if(ce.dryRun&&!ce.pruneLegacy)return console.error("--dry-run requires --prune-legacy"),null;return ce}async function Ji(P,A,q={}){let V=fu(P,A);if(!V)return 1;let ee=q.logsDir??D(P);if(V.pruneLegacy)return mu(ee,V.json,V.dryRun);if(!ee)return console.log(V.json?"[]":V.id?`No retained audit log entry found for id ${Ct(V.id)}.`:"No audit log entries found."),0;z(P,ee);let ne={count:0},ce=Xt(ee,ne).flatMap((Ie)=>mn(Ie,ne).map((Xe)=>({entry:Xe,file:Ie})));if(ne.count>0)console.error(`warning: ${ne.count} audit log ${ne.count===1?"source":"sources"} could not be read; these results are incomplete`);if(V.id)return vu(ce,V,q.timeZone);let he=Date.now()-V.since*24*60*60*1000,ye=ce.filter((Ie)=>bu(Ie,V,ee,he)),be=V.suspect?Bi(ye.map((Ie)=>Ie.entry)):null,xe=(be?ye.filter((Ie)=>be.has(Ie.entry)):ye).sort((Ie,Xe)=>Date.parse(Xe.entry.ts)-Date.parse(Ie.entry.ts)).slice(0,V.limit);if(V.json)return console.log(JSON.stringify(xe.map((Ie)=>Ie.entry),null,2)),0;if(xe.length===0)return console.log("No audit log entries found."),0;for(let Ie of xe)console.log(ku(Ie.entry,q.timeZone));return 0}function mu(P,A,q){let V=P?hu(P).map((he)=>cu(P,he)):[];if(q)return gu(V,A);let ee=[],ne=0,ce=0;for(let he of V){let ye=Vi(he,{throwIfNoEntry:!1})?.size??0,be=yu(he);if(be){ee.push(`${zi(he)}: ${be}`);continue}ne++,ce+=ye}if(A)return console.log(JSON.stringify({removedFiles:ne,removedBytes:ce,failedFiles:ee.length})),ee.length===0?0:1;console.log(ne===0&&ee.length===0?"No legacy audit log files found.":`Removed ${ne} legacy audit log ${ne===1?"file":"files"} (${Wi(ce)}).`);for(let he of ee)console.error(`Could not remove ${Ct(he)}`);if(console.log("Nested v2 audit logs were not changed."),ne>0)console.log("This deletion cannot be undone.");return ee.length===0?0:1}function gu(P,A){let q=P.reduce((V,ee)=>V+(Vi(ee,{throwIfNoEntry:!1})?.size??0),0);if(A)return console.log(JSON.stringify({dryRun:!0,files:P.length,bytes:q})),0;if(console.log(P.length===0?"No legacy audit log files found.":`Would remove ${P.length} legacy audit log ${P.length===1?"file":"files"} (${Wi(q)}).`),console.log("Nested v2 audit logs are not included."),P.length>0)console.log("Run the same command without --dry-run to delete them.");return 0}function hu(P){try{return iu(P,{withFileTypes:!0}).filter((A)=>A.isFile()&&A.name.endsWith(".jsonl")).map((A)=>A.name)}catch{return[]}}function yu(P){try{return su(P),null}catch(A){return A instanceof Error?A.message:String(A)}}function Wi(P){let A=["B","KiB","MiB","GiB"],q=Math.min(Math.floor(Math.log2(Math.max(P,1))/10),A.length-1);return`${Math.round(P/1024**q*10)/10} ${A[q]}`}function vu(P,A,q){let V=P.filter((ne)=>ne.entry.id===A.id);if(V.length>1)return console.error(`Multiple audit log entries found for id ${Ct(A.id??"")}.`),1;if(A.json)return console.log(JSON.stringify(V.map((ne)=>ne.entry),null,2)),0;let ee=V[0];if(!ee)return console.log(`No retained audit log entry found for id ${Ct(A.id??"")}.`),0;return console.log(xu(ee.entry,q)),0}function bu(P,A,q,V){if(!A.all&&P.entry.decision==="allow")return!1;if(Date.parse(P.entry.ts)<V)return!1;if(A.agent!==void 0&&P.entry.agent!==A.agent)return!1;if(A.rule!==void 0&&P.entry.ruleId!==A.rule)return!1;if(A.session!==void 0&&!Lu(P,q,A.session))return!1;if(A.project!==void 0&&!wu(P.entry.cwd,A.project))return!1;return!0}function Lu(P,A,q){if(P.entry.sessionId===q)return!0;return au(P.file)===A&&zi(P.file,".jsonl")===q}function wu(P,A){if(!P)return!1;let q=du(A,P);return q!==".."&&!q.startsWith(`..${pu}`)&&!lu(q)}function ku(P,A){let q=Ct(P.id??"-"),V=Ct(P.decision??"deny"),ee=P.cwd?` [${Ct(P.cwd)}]`:"",ne=P.segment||P.command,ce=ne===P.command?"":"↳ ",he=ne.length>50?`${ne.slice(0,50)}…`:ne;return`${q.padEnd(16)} ${Ct(Ki(P.ts,A))} ${V.padEnd(5)} ${Ct(P.agent??"-").padEnd(15)} ${Ct(P.ruleId??"-").padEnd(20)} ${ce}${Ct(he)}${ee}`}function xu(P,A){let q=(ee)=>Ct(ee===void 0||ee===null||ee===""?"-":ee),V=P.shape?`${P.agent??"-"} (shape: ${P.shape})`:P.agent??"-";return[`id: ${q(P.id)}`,`ts: ${q(Ki(P.ts,A))}`,`decision: ${q(P.decision)}`,`agent: ${q(V)}`,`level: ${q(P.level)}`,`tool: ${q(P.toolName)}`,`rule: ${q(P.ruleId)}`,`intent: ${q(P.intent)}`,`stage: ${q(P.failureStage)}`,`error: ${q(P.errorCode)}`,`session: ${q(P.sessionId)}`,`cwd: ${q(P.cwd)}`,`version: ${q(P.v)}`,`truncated: ${q(P.truncated===!0?"yes":void 0)}`,`reason: ${q(P.reason)}`,`command: ${q(P.command)}`,`segment: ${q(P.segment)}`].join(`
|
|
4
|
+
`)}function Ki(P,A){let q=new Date(P);if(Number.isNaN(q.getTime()))return P;return new Intl.DateTimeFormat("sv-SE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:A}).format(q)}function qi(P){let A=Number(P);return Number.isFinite(A)&&A>0?A:null}var Yi={name:"doctor",aliases:["--doctor"],description:"Run diagnostic checks to verify installation and configuration",usage:"doctor [options]",options:[{flags:"--json",description:"Output diagnostics as JSON"},{flags:"--skip-update-check",description:"Skip npm registry version check"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net doctor","cc-safety-net doctor --json","cc-safety-net doctor --skip-update-check"]};var Xi={name:"explain",description:"Show step-by-step analysis trace of how a command would be analyzed",usage:"explain [options] <command>",argument:"<command>",options:[{flags:"--json",description:"Output analysis as JSON"},{flags:"--cwd",argument:"<path>",description:"Use custom working directory"},{flags:"-h, --help",description:"Show this help"}],examples:['cc-safety-net explain "git reset --hard"','cc-safety-net explain --json "rm -rf /"','cc-safety-net explain --cwd /tmp "git status"']};var Zi={name:"gui",description:"Open the local policy editor GUI",usage:"gui [options]",options:[{flags:"--no-open",description:"Print the URL without opening a browser"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net gui","cc-safety-net gui --no-open"]};var nr=[{id:"antigravity-cli",displayName:"Antigravity CLI",doctorOrder:3,runtime:{order:1,flags:["-ac","--agy-cli"],description:"Run as Antigravity CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:2,flag:"--agy-cli",artifactKind:"hook config",probeCommand:["agy","--version"]}},{id:"claude-code",displayName:"Claude Code",doctorOrder:1,runtime:{order:2,displayName:"Coding CLI",flags:["-cc","--coding-cli"],legacyFlags:["--claude-code"],description:"Run as Coding CLI PreToolUse hook",legacyTopLevelFlags:["-cc","--claude-code"]},install:{order:3,flag:"--claude-code",artifactKind:"plugin",probeCommand:["claude","--version"]}},{id:"codex",displayName:"Codex",doctorOrder:4,runtime:{order:3,flags:["-cx","--codex"],description:"Run as a Codex PreToolUse hook",legacyTopLevelFlags:[]},install:{order:4,flag:"--codex",artifactKind:"plugin",probeCommand:["codex","--version"]}},{id:"copilot-cli",displayName:"GitHub Copilot CLI",doctorOrder:7,runtime:{order:6,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:7,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:6,runtime:{order:5,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:6,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:8,runtime:{order:7,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:9,runtime:{order:8,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:9,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:10,runtime:{order:9,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:10,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:11,install:{order:11,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:12,install:{order:12,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:13,install:{order:13,flag:"--pi",artifactKind:"package",probeCommand:["pi","--version"]}},{id:"cursor",displayName:"Cursor",doctorOrder:5,runtime:{order:4,flags:["-cu","--cursor"],description:"Run as Cursor preToolUse hook",legacyTopLevelFlags:[]},install:{order:5,flag:"--cursor",artifactKind:"hook config",probeCommand:["cursor","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],rr=nr.slice().sort((P,A)=>P.doctorOrder-A.doctorOrder).map((P)=>P.id),En=nr.filter((P)=>("runtime"in P)).slice().sort((P,A)=>P.runtime.order-A.runtime.order).map((P)=>({id:P.id,displayName:"displayName"in P.runtime?P.runtime.displayName:P.displayName,flags:P.runtime.flags,legacyFlags:"legacyFlags"in P.runtime?P.runtime.legacyFlags:[],description:P.runtime.description,legacyTopLevelFlags:P.runtime.legacyTopLevelFlags})),$t=nr.slice().sort((P,A)=>P.install.order-A.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...A})=>A),Cu=Object.fromEntries(nr.map((P)=>[P.id,P.displayName]));function bt(P){return Cu[P]}var Su=En.map((P)=>({flags:P.flags.join(", "),description:P.description})),Ru=En.flatMap((P)=>P.flags.map((A)=>`cc-safety-net hook ${A}`)),Qi={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...Su,{flags:"-h, --help",description:"Show this help"}],examples:Ru};var es={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Install ${bt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...$t.map((P)=>`cc-safety-net install ${P.flag}`)]},ts={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Uninstall ${bt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...$t.map((P)=>`cc-safety-net uninstall ${P.flag}`)]},ns={name:"update",description:"Update every installed CC Safety Net integration to the latest version",usage:"update",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net update"]};var rs={name:"logs",description:"Browse audit log entries recorded by hooks",usage:"logs [options]",options:[{flags:"--id",argument:"<id>",description:"Show one entry from retained history by its 16-character id (not guaranteed once it is older than the configured retention)"},{flags:"--limit",argument:"<n>",description:"Maximum entries to print",default:"20"},{flags:"--since",argument:"<days>",description:"Only include entries newer than this many days (max: the configured audit retention, 1-365)",default:"30"},{flags:"--agent",argument:"<name>",description:"Filter by agent name"},{flags:"--rule",argument:"<ruleId>",description:"Filter by rule id"},{flags:"--session",argument:"<id>",description:"Filter by session id"},{flags:"--project",argument:"<path>",description:"Filter by project path"},{flags:"--suspect",description:"Only denials that look like false positives"},{flags:"--all",description:"Include allow entries"},{flags:"--prune-legacy",description:"Permanently delete all legacy root-level logs; nested logs are untouched"},{flags:"--dry-run",description:"With --prune-legacy, report what would be deleted and delete nothing"},{flags:"--json",description:"Output entries as JSON"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net logs --id 3fa9c2d1a70e8b42","cc-safety-net logs --agent claude-code","cc-safety-net logs --project . --since 7","cc-safety-net logs --suspect --since 7","cc-safety-net logs --json","cc-safety-net logs --prune-legacy --dry-run","cc-safety-net logs --prune-legacy"]};var or={name:"policy",description:"Check and apply project or user policy proposals",usage:"policy <subcommand>",subcommands:[{usage:"check <file>",description:"Validate a policy proposal and print its diff"},{usage:"apply <file>",description:"Apply a proposal after confirming in a terminal"}],options:[{flags:"-g, --global",description:"Use the user-scope policy instead of the project one"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net policy check proposal.json","cc-safety-net policy apply proposal.json","cc-safety-net policy apply proposal.json --global"]};var ao=[{flags:"--ref",argument:"<ref>",description:"Use a branch, tag, or commit"},{flags:"--only",argument:"<rulebook...>",description:"Add only these repository rulebooks"},{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"-h, --help",description:"Show this help"}],lo=["cc-safety-net rule add project-rules","cc-safety-net rule add acme/safety-rules","cc-safety-net rule add acme/safety-rules --only aws gcloud","cc-safety-net rule add acme/safety-rules --ref v2 --only aws","cc-safety-net rule add --only terraform aws"],gn={name:"rule",description:"Manage CC Safety Net rule config and rulebook sources",usage:"rule <subcommand>",subcommands:[{usage:"init [--example]",description:"Create inert rule config"},{usage:"add [source] [--ref <ref>] [--only <rulebook...>]",description:"Add rulebook sources and sync"},{usage:"remove <source>",description:"Remove a rulebook source and sync"},{usage:"update [source]",description:"Re-fetch and vendor remote rulebooks"},{usage:"sync",description:"Deprecated: migrate lock and cache leftovers"},{usage:"list",description:"List active rulebooks"},{usage:"wrapper add <command>",description:"Trust a transparent command wrapper"},{usage:"wrapper remove <command>",description:"Remove a transparent command wrapper"},{usage:"wrapper list",description:"List transparent command wrappers"},{usage:"migrate [--cleanup]",description:"Migrate legacy inline rules"},{usage:"doc",description:"Print the rulebook authoring guide"},{usage:"verify",description:"Validate rule config files"}],options:[{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"--cleanup",description:"Delete legacy files after rule migrate verifies them"},{flags:"--delete-source",description:"Delete clean local source directory on remove"},{flags:"--example",description:"Create an inactive example rulebook with rule init"},...ao.slice(0,2),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net rule init","cc-safety-net rule init --example","cc-safety-net rule wrapper add rtk",...lo,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var os={name:"status",description:"Show what the runtime is enforcing right now",usage:"status",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net status"]};var is={name:"statusline",description:"Print status line with mode indicators for shell integration",usage:"statusline --claude-code",options:[{flags:"-cc, --claude-code",description:"Print status line for Claude Code"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net statusline -cc","cc-safety-net statusline --claude-code"]};var ir=[os,Yi,rs,Xi,gn,or,es,ns,ts,Qi,Zi,is];function Pu(P){return P.aliases??[]}function sr(P){let A=P.toLowerCase();return ir.find((q)=>q.name.toLowerCase()===A||Pu(q).some((V)=>V.toLowerCase()===A))}import{basename as Eu}from"node:path";function ar(P,A=7,q=D(P)){let V=Date.now()-A*24*60*60*1000,ee=[],ne=new Set,ce=0,he,ye,be,xe;if(q)z(P,q);let Ie={count:0},Xe=q?Xt(q,Ie):[];for(let Re of Xe)for(let at of mn(Re,Ie)){if(at.decision==="allow")continue;let ct=new Date(at.ts).getTime();if(ct>=V){if(ce++,ne.add(at.sessionId??Eu(Re,".jsonl")),ye===void 0||ct<=ye)he=at.ts,ye=ct;if(xe===void 0||ct>xe)be=at.ts,xe=ct;Du(ee,at,ct)}}let et=ee.map((Re)=>({timestamp:Re.ts,command:Re.command,reason:Re.reason,relativeTime:Gi(new Date(Re.ts))}));return{totalBlocked:ce,sessionCount:ne.size,recentEntries:et,oldestEntry:he,newestEntry:be,unreadable:Ie.count}}function Du(P,A,q){let V=P.findIndex((ee)=>q>new Date(ee.ts).getTime());if(V===-1){if(P.length<3)P.push(A);return}if(P.splice(V,0,A),P.length>3)P.pop()}import{dirname as Uu}from"node:path";import{dirname as Au,join as _u,resolve as Tu}from"node:path";var Iu="config.json";function Pt(P,A,q,V){g($u(P),`${JSON.stringify(A,null,2)}
|
|
5
|
+
`,q,V)}function $u(P){return typeof P==="string"?re(P):P}function uo(P){return{errors:ie(Fu(P),": "," "),ruleNames:new Set(_e(P).map((A)=>A.toLowerCase()))}}var Ou="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",ss="must match pattern (letters, numbers, hyphens, underscores)";function Fu(P){if(!as(P))return[e([],"Config must be an object")];return[...P.version===1?[]:[e(["version"],"must be 1")],...Nu(P.rules)]}function Nu(P){if(P===void 0)return[];if(!Array.isArray(P))return[e(["rules"],"must be an array")];return[...P.flatMap((A,q)=>as(A)?ju(A,["rules",q]):[e(["rules",q],"must be an object")]),...Te(P)]}function ju(P,A){return[...co(P.name,[...A,"name"],"required string",l,Ou),...co(P.command,[...A,"command"],"required string",w,ss),...P.subcommand===void 0?[]:co(P.subcommand,[...A,"subcommand"],"must be a string if provided",w,ss),...Mu(P.block_args,[...A,"block_args"]),...Hu(P.reason,[...A,"reason"]),...P.intent===void 0||Ce(P.intent)?[]:[e([...A,"intent"],we)]]}function co(P,A,q,V,ee){if(typeof P!=="string")return[e(A,q)];return V.test(P)?[]:[e(A,ee)]}function Mu(P,A){if(!Array.isArray(P))return[e(A,"required array")];if(P.length===0)return[e(A,"must have at least one element")];return P.flatMap((q,V)=>{if(typeof q!=="string")return[e([...A,V],"must be a string")];return q===""?[e([...A,V],"must not be empty")]:[]})}function Hu(P,A){if(typeof P!=="string")return[e(A,"required string")];if(P==="")return[e(A,"must not be empty")];return P.length>T?[e(A,`must be at most ${T} characters`)]:[]}function as(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function po(P){let A=ls(P);if(!A.ok)return A.result;return uo(A.parsed)}function ls(P){let A=[],q=new Set;try{let V=typeof P==="string"?re(P):P,ee=n(V);if(ee===null)return A.push(`File not found: ${V.path}`),{ok:!1,result:{errors:A,ruleNames:q}};if(!ee.trim())return A.push("Config file is empty"),{ok:!1,result:{errors:A,ruleNames:q}};return{ok:!0,parsed:JSON.parse(ee)}}catch(V){if(V instanceof r)return A.push(V.message),{ok:!1,result:{errors:A,ruleNames:q}};let ee=V instanceof Error?V.message:String(V);return A.push(V instanceof SyntaxError?"Invalid JSON":ee),{ok:!1,result:{errors:A,ruleNames:q}}}}function cs(P){return Tu(P,".safety-net.json")}function Jt(P){let A=ls(P);if(!A.ok)return A.result;let q=Ve(A.parsed);return{errors:q.errors,ruleNames:q.sources}}function lr(P,A={}){return _u(Au(Se(P,A)),Iu)}function ds(P,A,q){let V;try{if(n(A)===null)return{path:P,exists:!1,valid:!1,ruleCount:0};V=Jt(A),V.errors.push(...U(P,q))}catch(ee){if(!(ee instanceof r))throw ee;V={errors:[ee.message],ruleNames:new Set}}return{path:P,exists:!0,valid:V.errors.length===0,ruleCount:V.ruleNames.size,...V.errors.length>0?{errors:V.errors}:{}}}function Gu(P,A){return{source:A,name:P.name,command:P.command,subcommand:P.subcommand,blockArgs:[...P.block_args],reason:P.reason}}function us(P,A){let q=j(P),V=M(A),ee=Uu(q),ne=Y(P,{cwd:A,userConfigPath:q,projectConfigPath:V,userConfigDir:ee}),ce=J(P,{cwd:A,userConfigPath:q,projectConfigPath:V,userConfigDir:ee}),he=new Map(ne.rulebooks.flatMap((ye)=>ye.rules.map((be)=>[be,ye.source])));return{userConfig:ds(q,ce.userConfigTarget,ce.userScope),projectConfig:ds(V,ce.projectConfigTarget,ce.projectScope),effectiveRules:ne.rules.map((ye)=>Gu(ye,he.get(ye.name)??"project"))}}var Bu=[{flag:o.level,description:"Safety level preset: standard, strict, or paranoid",defaultBehavior:"standard"},{flag:o.strict,description:"Legacy; equivalent to safety.overrides.fail_closed",defaultBehavior:"permissive"},{flag:o.paranoid,description:"Legacy; equivalent to safety.overrides.paranoid_rm and paranoid_interpreters",defaultBehavior:"off"},{flag:o.paranoidRm,description:"Legacy; equivalent to safety.overrides.paranoid_rm",defaultBehavior:"off"},{flag:o.paranoidInterpreters,description:"Legacy; equivalent to safety.overrides.paranoid_interpreters",defaultBehavior:"off"},{flag:o.worktree,description:"Allow local git discards in linked worktrees",defaultBehavior:"off"},{flag:o.debug,description:"Print diagnostic messages to stderr",defaultBehavior:"off"},{flag:o.auditScope,description:"Command decisions recorded: all, or blocked (privacy-minimizing, denials only)",defaultBehavior:"all"}];function ps(P){return[...Bu.map((A)=>({name:A.flag.name,value:ve(A.flag,P.env),isSet:Qe(A.flag,P.env),legacyName:A.flag.legacyName,legacyValue:A.flag.legacyName?P.env.get(A.flag.legacyName):void 0,legacyIsSet:A.flag.legacyName?P.env.get(A.flag.legacyName)!==void 0:void 0,description:A.description,defaultBehavior:A.defaultBehavior})),{name:"CC_SAFETY_NET_HOME",value:P.env.get("CC_SAFETY_NET_HOME"),isSet:P.env.get("CC_SAFETY_NET_HOME")!==void 0,description:"Override user-scope config/cache directory",defaultBehavior:"~/.cc-safety-net"}]}var fs={error:0,warning:1,info:2},qu=["policy","config","audit"];function Vu(P){return P.map((A)=>{if(A==="ownership")return"is not owned by the current user";if(A==="permissions")return"has unsafe permissions";if(A==="symlink")return"is a symbolic link";return"is not a directory"}).join(" and ")}var zu=[{derive:(P)=>P.hooks.length>0&&P.hooks.every((A)=>!A.configured)?[{checkId:"integration.none-configured",severity:"error",title:"No integration configured",detail:"CC Safety Net is not connected to any supported coding-agent integration.",fixHint:"Run `cc-safety-net install` and configure at least one integration."}]:[]},{derive:(P)=>P.hooks.filter((A)=>A.inspectionStatus==="failed").map((A)=>{let q=bt(A.platform);return{checkId:"integration.inspection-failed",severity:"error",title:`${q} inspection failed`,detail:`Doctor could not verify the ${q} integration configuration.`,fixHint:`Correct the reported ${q} configuration error, then run \`cc-safety-net doctor\` again.`,integration:A.platform}})},{derive:(P)=>P.userConfig.exists&&!P.userConfig.valid?[{checkId:"config.user-invalid",severity:"error",title:"User configuration is invalid",detail:"Doctor could not load a valid user rules configuration.",fixHint:"Run `cc-safety-net rule verify`, correct the reported error, then rerun doctor.",path:P.userConfig.path}]:[]},{derive:(P)=>P.projectConfig.exists&&!P.projectConfig.valid?[{checkId:"config.project-invalid",severity:"error",title:"Project configuration is invalid",detail:"Doctor could not load a valid project rules configuration.",fixHint:"Run `cc-safety-net rule verify`, correct the reported error, then rerun doctor.",path:P.projectConfig.path}]:[]},{derive:(P)=>P.configState.state==="degraded"?[{checkId:"config.runtime-degraded",severity:"warning",title:"Runtime is enforcing a fallback configuration",detail:`The rejected candidate configuration is not active: ${P.configState.reason}`,fixHint:"Fix the file named in the reason, or run `cc-safety-net rule update` to vendor a remote source, then rerun doctor."}]:[]},{derive:(P)=>P.v2Leftovers&&P.v2Leftovers.length>0?[{checkId:"config.v2-leftovers",severity:"info",title:"Rulebook lock and cache leftovers detected",detail:`Files an earlier version left behind are no longer read: ${P.v2Leftovers.join(", ")}.`,fixHint:"Run `cc-safety-net rule sync` (add `--global` for user scope) to migrate them, then rerun doctor."}]:[]},{derive:(P)=>{let A=P.environment.find((q)=>q.name==="CC_SAFETY_NET_AUDIT_SCOPE");return He(A?.value)==="invalid"?[{checkId:"environment.audit-scope-invalid",severity:"warning",title:"Audit scope value is invalid",detail:"CC_SAFETY_NET_AUDIT_SCOPE is not `all` or `blocked`, so allowed command decisions are not recorded.",fixHint:"Set CC_SAFETY_NET_AUDIT_SCOPE to `all` or `blocked`, then restart the integration."}]:[]}},...qu.map((P)=>({derive:(A)=>A.posture.directories.filter((q)=>q.kind===P&&q.status==="unsafe").map((q)=>({checkId:`posture.${P}-directory-unsafe`,severity:"error",title:`${P[0]?.toUpperCase()}${P.slice(1)} directory is unsafe`,detail:`The ${P} directory ${Vu(q.issues)}.`,fixHint:"Ensure this is a real directory owned by the current user with no group or other write access, then rerun doctor.",...q.path?{path:q.path}:{}}))})),{derive:(P)=>{let A=[...P.effectiveSafety.weakenedRuleOverrides].sort();return A.length>0?[{checkId:"posture.rule-overrides-weaken-preset",severity:"warning",title:"Rule overrides weaken the selected preset",detail:`Explicit overrides disable rules the resolved preset would enable: ${A.join(", ")}.`,fixHint:`Remove these \`off\` overrides or set them to \`on\`: ${A.join(", ")}.`}]:[]}}];function ms(P){return zu.flatMap((A,q)=>A.derive(P).map((V,ee)=>({finding:V,catalogOrder:q,occurrence:ee}))).sort((A,q)=>fs[A.finding.severity]-fs[q.finding.severity]||A.catalogOrder-q.catalogOrder||A.occurrence-q.occurrence).map((A)=>A.finding)}function Gt(){return Boolean(process.stdout.isTTY&&!process.env.NO_COLOR)}var Ju=(P)=>Gt()?`\x1B[32m${P}\x1B[0m`:P,Wu=(P)=>Gt()?`\x1B[33m${P}\x1B[0m`:P,Ku=(P)=>Gt()?`\x1B[34m${P}\x1B[0m`:P,Yu=(P)=>Gt()?`\x1B[36m${P}\x1B[0m`:P,Xu=(P)=>Gt()?`\x1B[31m${P}\x1B[0m`:P,Zu=(P)=>Gt()?`\x1B[2m${P}\x1B[0m`:P,Qu=(P)=>Gt()?`\x1B[1m${P}\x1B[0m`:P,dt={green:Ju,yellow:Wu,blue:Ku,cyan:Yu,red:Xu,dim:Zu,bold:Qu},ep="\x1B[0m",tp=[39,82,198,226,208,51,196,46,201,214,93,154,220,27,49,190,200,33,129,227,45,160,63,118,123,202];function np(P){let A=P;return()=>(A=(A*1664525+1013904223)%4294967296,A/4294967296)}function rp(P){let A=[...tp],q=np(P);for(let V=A.length-1;V>0;V--){let ee=Math.floor(q()*(V+1)),ne=A[V];A[V]=A[ee],A[ee]=ne}return A}function op(P,A=0){if(!Gt())return"";let q=rp(A);return`\x1B[38;5;${q[P%q.length]}m`}function gs(P,A,q=0){if(!Gt())return`"${P}"`;return`${op(A,q)}"${P}"${ep}`}function cr(P){return P==="default"?"built-in default":`${P} policy`}var ip=new RegExp("\x1B\\[[0-9;]*m","g"),fo=(P)=>P.replace(ip,"").length;function Zt(P){let A=(P.headers??P.rows[0]??[]).map((ce,he)=>{let ye=Math.max(...P.rows.map((be)=>fo(be[he]??"")));return Math.max(fo(ce),ye)}),q=(ce,he)=>ce+" ".repeat(Math.max(0,he-fo(ce))),V=(ce,he)=>he[0]+A.map((ye)=>ce.repeat(ye+2)).join(he[1])+he[2],ee=(ce)=>`│ ${ce.map((he,ye)=>q(he,A[ye]??0)).join(" │ ")} │`,ne=P.headers?[` ${ee(P.headers)}`,` ${V("─",["├","┼","┤"])}`]:[];return[` ${V("─",["┌","┬","┐"])}`,...ne,...P.rows.map((ce)=>` ${ee(ce)}`),` ${V("─",["└","┴","┘"])}`].join(`
|
|
6
|
+
`)}function hs(P){let A=[];A.push("Hook Integration"),A.push(sp(P));let q=[],V=[];for(let ee of P){let ne=bt(ee.platform);if(ee.errors&&ee.errors.length>0)for(let ce of ee.errors)if(ee.configured)q.push({platform:ne,message:ce});else V.push({platform:ne,message:ce})}for(let ee of q)A.push(` Warning (${ee.platform}): ${ee.message}`);for(let ee of V)A.push(dt.red(` Error (${ee.platform}): ${ee.message}`));return A.join(`
|
|
7
|
+
`)}function sp(P){let A=["Platform","Discovery","Configuration","Inspection"],q=P.map((V)=>{let ee=bt(V.platform);if(V.inspectionStatus==="not-inspected"){let ye=dt.dim("Not inspected");return[ee,ye,ye,ye]}let ne=V.detected?dt.green("Detected"):V.inspectionStatus==="failed"?dt.red("Unknown"):dt.dim("Not detected"),ce=V.configured?dt.green("Configured"):V.detected?dt.yellow("Not configured"):V.inspectionStatus==="failed"?dt.red("Unknown"):dt.dim("Not applicable"),he=V.inspectionStatus==="verified"?dt.green("Verified"):V.inspectionStatus==="failed"?dt.red("Failed"):dt.dim("Not applicable");return[ee,ne,ce,he]});return Zt({headers:A,rows:q})}function ys(P){let q=["Guard Engine Verification",` Synthetic self-test: ${P.failed>0?dt.red(`${P.passed}/${P.total} FAIL`):dt.green(`${P.passed}/${P.total} passed`)}`],V=P.results.filter((ee)=>!ee.passed);if(V.length>0){q.push(""),q.push(dt.red(" Failures:"));for(let ee of V)q.push(dt.red(` • ${ee.description}`)),q.push(dt.red(` expected ${ee.expected}, got ${ee.actual}`))}return q.join(`
|
|
8
|
+
`)}function ap(P){if(P.length===0)return" (no custom rules)";let A=["Source","Name","Command","Block Args"],q=P.map((V)=>[V.source,V.name,V.subcommand?`${V.command} ${V.subcommand}`:V.command,V.blockArgs.join(", ")]);return Zt({headers:A,rows:q})}function vs(P){let A=[];if(A.push("Configuration"),A.push(lp(P.userConfig,P.projectConfig)),A.push(""),P.effectiveRules.length>0)A.push(` Effective rules (${P.effectiveRules.length} total):`),A.push(ap(P.effectiveRules));else A.push(" Effective rules: (none - using built-in rules only)");return A.join(`
|
|
9
|
+
`)}function lp(P,A){let q=["Scope","Status"],V=(ne)=>{if(!ne.exists)return dt.dim("N/A");if(!ne.valid)return dt.red(`Invalid (${ne.errors?.[0]??"unknown error"})`);return dt.green("Configured")},ee=[["User",V(P)],["Project",V(A)]];return Zt({headers:q,rows:ee})}function bs(P){let A=[];return A.push("Environment"),A.push(cp(P)),A.join(`
|
|
10
|
+
`)}function Ls(P){let A=P.effectiveSafety.policyScopes,q=["Effective Safety",` Selected preset: ${P.effectiveSafety.selectedPreset}${A?` (${cr(A.levelScope)})`:""}`,` Effective: ${P.effectiveSafety.level}`],V=[["fail_closed","fail_closed"],["paranoid_rm","paranoid_rm"],["paranoid_interpreters","paranoid_interpreters"]];for(let[ee,ne]of V){let ce=P.effectiveSafety.capabilities[ee],he=ce.enabled?dt.green("ON"):dt.dim("OFF"),ye=ce.sources.length>0?` (${ce.sources.join(", ")})`:"";q.push(` ${ne}: ${he} via ${ce.source}${ye}`)}if(A&&A.weakenings.length>0){q.push(" Project policy deltas:");for(let ee of A.weakenings)q.push(` ${ee}`)}q.push(` Stored rule customizations: ${P.effectiveSafety.ruleCounts.stored}`),q.push(` Effective rule customizations: ${P.effectiveSafety.ruleCounts.effective}`);for(let[ee,ne]of Object.entries(P.effectiveSafety.ruleOverrides))q.push(` ${ee}: ${ne}`);return q.join(`
|
|
11
|
+
`)}function ws(P){let A=["Findings"];if(P.length===0)return A.push(" No findings from inspected doctor facts."),A.join(`
|
|
12
12
|
`);for(let q of P){let V=`[${q.severity.toUpperCase()}] ${q.checkId}: ${Ct(q.title)}`,ee=q.severity==="error"?dt.red:q.severity==="warning"?dt.yellow:dt.blue;if(A.push(` ${ee(V)}`),A.push(` ${Ct(q.detail)}`),q.path)A.push(` Path: ${Ct(q.path)}`);if(q.fixHint)A.push(` Fix: ${Ct(q.fixHint)}`)}return A.join(`
|
|
13
|
-
`)}function
|
|
14
|
-
`)}function
|
|
15
|
-
`);if(P.error)return A.push(
|
|
16
|
-
`);if(P.updateAvailable)return A.push(
|
|
17
|
-
`);return A.push(
|
|
18
|
-
`)}function
|
|
19
|
-
`)}function
|
|
13
|
+
`)}function cp(P){let A=["Variable","Status","Legacy"],q=P.map((V)=>{let ee=V.isSet?dt.green("✓"):dt.dim("✗"),ne=V.legacyName&&V.legacyIsSet?`${V.legacyName} ${dt.green("✓")}`:V.legacyName??"";return[V.name,ee,ne]});return Zt({headers:A,rows:q})}function ks(P){let A=[];if(P.totalBlocked===0)A.push("Recent Activity"),A.push(" No blocked commands in the last 7 days"),A.push(" Tip: This is normal for new installations");else A.push(`Recent Activity · last 7 days (${P.totalBlocked} blocked / ${P.sessionCount} sessions)`),A.push(dp(P.recentEntries));if(P.unreadable>0)A.push(` Warning: ${P.unreadable} audit log ${P.unreadable===1?"source":"sources"} could not be read; this summary is incomplete`);return A.join(`
|
|
14
|
+
`)}function dp(P){let A=["Time","Command"],q=P.map((V)=>{let ee=Ct(V.command.replace(/\r\n|\r|\n/g," ↵ ").replace(/\t/g," ")),ne=ee.length>40?`${ee.slice(0,37)}...`:ee;return[V.relativeTime,ne]});return Zt({headers:A,rows:q})}function xs(P){let A=[];if(A.push("Update Check"),P.latestVersion===null&&!P.error)return A.push(dr([["Status",dt.dim("Skipped")],["Installed",P.currentVersion]])),A.join(`
|
|
15
|
+
`);if(P.error)return A.push(dr([["Status",`${dt.yellow("⚠")} Error`],["Installed",P.currentVersion],["Error",dt.dim(P.error)]])),A.join(`
|
|
16
|
+
`);if(P.updateAvailable)return A.push(dr([["Status",`${dt.yellow("⚠")} Update Available`],["Current",P.currentVersion],["Latest",dt.green(P.latestVersion??"")]])),A.push(""),A.push(" Run: bunx cc-safety-net@latest doctor"),A.push(" Or: npx cc-safety-net@latest doctor"),A.join(`
|
|
17
|
+
`);return A.push(dr([["Status",`${dt.green("✓")} Up to date`],["Version",P.currentVersion]])),A.join(`
|
|
18
|
+
`)}function dr(P){return Zt({rows:P})}function Cs(P){let A=[];return A.push("System Info"),A.push(up(P)),A.join(`
|
|
19
|
+
`)}function up(P){let A=["Component","Version"],q=(ne)=>{if(ne===null)return dt.dim("not found");return ne},ee=[{label:"cc-safety-net",value:P.version},...rr.map((ne)=>({label:bt(ne),value:P.versions[ne]??null})),{label:"Node.js",value:P.nodeVersion},{label:"npm",value:P.npmVersion},{label:"Bun",value:P.bunVersion},{label:"Platform",value:P.platform}].map((ne)=>[ne.label,q(ne.value)]);return Zt({headers:A,rows:ee})}function Ss(P){if(P.findings.length===0)return dt.green(`
|
|
20
20
|
No findings from inspected doctor facts.`);let A={error:P.findings.filter((ne)=>ne.severity==="error").length,warning:P.findings.filter((ne)=>ne.severity==="warning").length,info:P.findings.filter((ne)=>ne.severity==="info").length},q=["error","warning","info"].filter((ne)=>A[ne]>0).map((ne)=>`${A[ne]} ${ne}`),V=P.findings.length===1?"finding":"findings",ee=`
|
|
21
|
-
${P.findings.length} ${V}: ${q.join(", ")}.`;if(A.error>0)return dt.red(ee);if(A.warning>0)return dt.yellow(ee);return dt.blue(ee)}import{lstatSync as
|
|
22
|
-
`)[0]?.trim()||null}async function
|
|
21
|
+
${P.findings.length} ${V}: ${q.join(", ")}.`;if(A.error>0)return dt.red(ee);if(A.warning>0)return dt.yellow(ee);return dt.blue(ee)}import{lstatSync as pp}from"node:fs";import{dirname as mo}from"node:path";function go(P,A){try{let q=pp(A);if(q.isSymbolicLink())return{kind:P,path:A,status:"unsafe",issues:["symlink"]};if(!q.isDirectory())return{kind:P,path:A,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:A,status:"unknown",issues:[]};let V=[...q.uid!==process.getuid()?["ownership"]:[],...(q.mode&18)!==0?["permissions"]:[]];return{kind:P,path:A,status:V.length>0?"unsafe":"safe",issues:V}}catch(q){if(typeof q==="object"&&q!==null&&"code"in q&&q.code==="ENOENT")return{kind:P,path:A,status:"not-applicable",issues:[]};return{kind:P,path:A,status:"unknown",issues:[]}}}function Rs(P,A){let q=D(P);return{directories:[go("policy",mo(mo(A))),go("config",mo(A)),...q?[go("audit",q)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as fp}from"node:child_process";import{existsSync as Ps}from"node:fs";import{delimiter as mp,extname as gp,join as hp}from"node:path";import{stripVTControlCharacters as Es}from"node:util";var As="2.4.14",yp=5000,vp="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function wt(){return As}function ho(P,A){let q=P[A];if(q)return q;let V=Object.keys(P).find((ee)=>ee.toLowerCase()===A.toLowerCase()&&!!P[ee]);return V?P[V]:q}function bp(P){return(ho(P,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((A)=>A.length>0)}function Lp(P,A){let q=gp(P)?[P]:[...bp(A).map((V)=>`${P}${V}`),P];if(P.includes("/")||P.includes("\\"))return q.find((V)=>Ps(V))??P;return(ho(A,"PATH")??"").split(mp).flatMap((V)=>q.map((ee)=>hp(V,ee))).find((V)=>Ps(V))??P}function Ds(P){if(!/[\s"&|<>^]/.test(P))return P;return`"${P.replace(/"/g,'""')}"`}function Qt(P,A){let[q,...V]=P,ee=A[vp]==="win32"?"win32":process.platform;if(!q||ee!=="win32")return{cmd:q??"",args:V};let ne=Lp(q,A);if(!/\.(?:bat|cmd)$/i.test(ne))return{cmd:ne,args:V};return{cmd:ho(A,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",Ds(ne),...V.map(Ds)].join(" ")]}}var hn=async(P,A=yp)=>{let q=await wp(P,{timeoutMs:A});if(q.code!==0)return null;return Es(q.stdout).trim()||Es(q.stderr).trim()||null};function wp(P,A){let[q,...V]=P;if(!q)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((ee)=>{try{let ne=Qt([q,...V],process.env),ce=fp(ne.cmd,ne.args,{stdio:["ignore","pipe","pipe"]}),he=!1,ye="",be="";ce.stdout.on("data",(Xe)=>{ye+=Xe.toString()}),ce.stderr.on("data",(Xe)=>{be+=Xe.toString()});let xe=(Xe)=>{if(he)return;he=!0,clearTimeout(Ie),ee(Xe)},Ie=setTimeout(()=>{ce.kill(),xe({code:null,stdout:ye,stderr:be})},A.timeoutMs);ce.on("close",(Xe)=>{xe({code:Xe,stdout:ye,stderr:be})}),ce.on("error",()=>{xe({code:null,stdout:ye,stderr:be})})}catch{ee({code:null,stdout:"",stderr:""})}})}function ur(P){if(!P)return null;let A=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(P);if(A)return A[1]??null;let q=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(P);if(q)return q[1]??null;return P.split(`
|
|
22
|
+
`)[0]?.trim()||null}async function pr(P,A=hn,q=process.cwd()){let V=Promise.all($t.map(async(Ie)=>[Ie.id,ur(await A([...Ie.probeCommand]))])),[ee,ne,ce,he,ye,be,xe]=await Promise.all([V,V.then(async(Ie)=>{let Xe=Ie.find(([at])=>at==="opencode")?.[1];if(!Xe?.startsWith("2.")||!P(Xe))return null;let et=["--param",`location[directory]=${q}`],Re=["opencode","api","integration.list",...et];return await A(Re,30000),A(["opencode","api","plugin.list",...et],30000)}),A(["codex","plugin","list"],30000),A(["amp","plugins","list"],30000),A(["node","--version"]),A(["npm","--version"]),A(["bun","--version"])]);return{version:As,versions:Object.fromEntries(ee),codexPluginListOutput:ce,ampPluginListOutput:he,openCodePluginListOutput:ne,nodeVersion:ur(ye),npmVersion:ur(be),bunVersion:ur(xe),platform:`${process.platform} ${process.arch}`}}function yo(P,A){if(A==="dev")return!1;let q=P.split(".").map(Number),V=A.split(".").map(Number),[ee=0,ne=0,ce=0]=q,[he=0,ye=0,be=0]=V;if(ee!==he)return ee>he;if(ne!==ye)return ne>ye;return ce>be}async function Wt(){let P=wt(),A=new AbortController,q=setTimeout(()=>A.abort(),3000);try{let V=await fetch("https://registry.npmjs.org/cc-safety-net/latest",{signal:A.signal});if(!V.ok)return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:`npm registry returned ${V.status}`};let ee=await V.json(),ne=yo(ee.version,P);return{currentVersion:P,latestVersion:ee.version,updateAvailable:ne}}catch(V){return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:V instanceof Error?V.message:"Network error"}}finally{clearTimeout(q)}}import*as js from"node:readline";var $s=(P)=>`\x1B[${P}B`,kp=(P)=>`\x1B[${P}A`;var _s=["░","▒","▓","╱","╲","┃","━","┏","┓","┗","┛","╋"];function xp(P){return new Promise((A)=>setTimeout(A,P))}function Cp(P,A,q){if(!q)return A(P);if(q.aborted)return Promise.resolve();return new Promise((V,ee)=>{let ne=()=>q.removeEventListener("abort",ce),ce=()=>{ne(),V()};q.addEventListener("abort",ce,{once:!0}),A(P).then(()=>{ne(),V()},(he)=>{ne(),ee(he)})})}function fr(P){return Math.max(0,Math.min(1,P))}function yn(P){return Math.max(0,Math.min(255,Math.round(P)))}function vo(P){return P<=0.0031308?12.92*P:1.055*P**0.4166666666666667-0.055}function Sp(P,A,q){let V=q*Math.PI/180,ee=A*Math.cos(V),ne=A*Math.sin(V),ce=(P+0.3963377774*ee+0.2158037573*ne)**3,he=(P-0.1055613458*ee-0.0638541728*ne)**3,ye=(P-0.0894841775*ee-1.291485548*ne)**3;return{blue:yn(vo(fr(-0.0041960863*ce-0.7034186147*he+1.707614701*ye))*255),green:yn(vo(fr(-1.2684380046*ce+2.6097574011*he-0.3413193965*ye))*255),red:yn(vo(fr(4.0767416621*ce-3.3077115913*he+0.2309699292*ye))*255)}}function bo(P,A){let q=(A*P*180/Math.PI%360+360)%360;return Sp(0.72,0.15,q)}function Os(P,A=0.1){let q=bo(A,P);return`\x1B[38;2;${q.red};${q.green};${q.blue}m`}function Rp(P,A){return{blue:yn(P.blue+(255-P.blue)*A),green:yn(P.green+(255-P.green)*A),red:yn(P.red+(255-P.red)*A)}}function Fs(P,A,q){let V=Math.imul(P+2654435769,2246822507)^Math.imul(A+3266489909,668265263)^Math.imul(q+374761393,2654435761),ee=V^V>>>15,ne=Math.imul(ee,739982445),ce=ne^ne>>>12,he=Math.imul(ce,695872825);return((he^he>>>15)>>>0)/4294967296}function Pp(P,A,q){let V=Math.floor(Fs(P,A,q)*_s.length);return _s[V]??"░"}function Ts(P){let A=fr(P);return A*A*A*(A*(A*6-15)+10)}function Ep(P){if(P.length===0)return"";let A=[],q=!1,V="";for(let ee of P){let ne=`${ee.red};${ee.green};${ee.blue}`;if(ee.bold!==q)A.push(ee.bold?"\x1B[1m":"\x1B[22m"),q=ee.bold;if(ne!==V)A.push(`\x1B[38;2;${ne}m`),V=ne;A.push(ee.character)}return`${A.join("")}\x1B[22m\x1B[39m`}function Dp(P,A,q,V,ee){return P.map((ne,ce)=>({...bo(q,V+A+ce/ee),bold:!1,character:ne}))}function Ap(P,A,q,V,ee,ne,ce,he){let ye=Math.max(1,V*0.75),be=Math.min(1,q/ye),xe=ee*Ts(be),Ie=Math.max(0,(q-ye)/Math.max(1,V-ye)),Xe=(1-Ts(q/V))*he*2,et=0.35*Math.max(0,1-Ie*2),Re=be>=1,at=Math.min(P.length,Math.ceil(xe+2+1));return P.slice(0,at).map((ct,lt)=>{let ft=bo(ne,ce+A+lt/he+Xe),mt=lt+Fs(A,lt,7919)*2-1;if(mt>xe+2)return{...ft,bold:!1,character:" "};let gt=xe-mt,ut=0.8*Math.exp(-(gt*gt)/12.5),vt=Math.min(0.9,ut+et),At=!Re&&mt>xe-4;return{...Rp(ft,vt),bold:vt>0.3,character:At?Pp(A,lt,q):ct}})}function Is(P){return`\x1B[?2026h${P.map((A,q)=>`\x1B8${q>0?$s(q):""}${Ep(A)}`).join("")}\x1B[?2026l`}async function Lo(P,A={}){if(!P)return;let q=A.output??process.stdout,V=A.sleep??xp,ee=A.seed??0,ne=P.split(`
|
|
23
23
|
`).map((xe)=>Array.from(xe)),ce=Math.max(...ne.map((xe)=>xe.length)),he=12000*ne.filter((xe)=>xe.length>0).length/40,ye=ce>0?Math.max(1,Math.ceil(he/16.666666666666668)):0,be=ye>0?he/ye:0;q.write(`\x1B[?25l${ne.length>1?`${`
|
|
24
|
-
`.repeat(ne.length-1)}${
|
|
25
|
-
\x1B[0m\x1B[?25h`)}}var
|
|
26
|
-
`);function
|
|
27
|
-
`).map((q)=>
|
|
28
|
-
`).find((q)=>q.includes("https://github.com/kenryu42/cc-safety-net.git"));if(!A)return{platform:"codex",status:"n/a"};if(!A.includes("installed,"))return{platform:"codex",status:"n/a"};if(!A.includes("installed, enabled"))return{platform:"codex",status:"disabled",method:"codex plugin list",configPath:"codex plugin list",errors:["Codex plugin line for https://github.com/kenryu42/cc-safety-net.git must contain installed, enabled."]};return{platform:"codex",status:"configured",method:"codex plugin list",configPath:"codex plugin list"}}import{existsSync as
|
|
29
|
-
`)q++;continue}if(ce==="/"&&he==="*"){q+=2;while(q<P.length-1){if(P[q]==="*"&&P[q+1]==="/"){q+=2;break}q++}continue}if(ce===","){ne=A.length,A+=ce,q++;continue}if(ce==="}"||ce==="]"){if(ne!==-1){let ye=A.slice(ne+1);if(/^\s*$/.test(ye))A=A.slice(0,ne)+ye}ne=-1,A+=ce,q++;continue}if(!/\s/.test(ce))ne=-1;A+=ce,q++}return A}function
|
|
30
|
-
`,A)+1;return/^[ \t]*/.exec(P.slice(q))?.[0]??""}function
|
|
24
|
+
`.repeat(ne.length-1)}${kp(ne.length-1)}`:""}\x1B7`);try{for(let xe=1;xe<=ye;xe+=1){if(A.signal?.aborted)break;q.write(Is(ne.map((Ie,Xe)=>Ap(Ie,Xe,xe,ye,ce,0.1,ee,3)))),await Cp(be,V,A.signal)}}finally{if(q.write(Is(ne.map((xe,Ie)=>Dp(xe,Ie,0.1,ee,3)))),q.write("\x1B8"),ne.length>1)q.write($s(ne.length-1));q.write(`
|
|
25
|
+
\x1B[0m\x1B[?25h`)}}var Ns=["┏━┛┏━┛ ┏━┛┏━┃┏━┛┏━┛━┏┛┃ ┃ ┏━ ┏━┛━┏┛","┃ ┃ ━━┃┏━┃┏━┛┏━┛ ┃ ━┏┛ ┃ ┃┏━┛ ┃ ","━━┛━━┛ ━━┛┛ ┛┛ ━━┛ ┛ ┛ ┛ ┛━━┛ ┛ "].join(`
|
|
26
|
+
`);function _p(P){return Boolean(P.isTTY)}async function Dn(P={}){let A=P.output??process.stdout;if(!_p(A))return;let q=P.input??process.stdin,V={output:A,seed:P.seed??Math.random()*8192,sleep:P.sleep};if(!q.isTTY||typeof q.setRawMode!=="function"){await Lo(Ns,V);return}let ee=new AbortController,ne=q.readableFlowing===!0,ce=q.isRaw===!0,he=!1,ye=(be,xe)=>{if(xe.ctrl&&xe.name==="c")he=!0;if(he||xe.name==="return"||xe.name==="enter")ee.abort()};js.emitKeypressEvents(q),q.on("keypress",ye),q.setRawMode(!0),q.resume();try{await Lo(Ns,{...V,signal:ee.signal})}finally{if(q.off("keypress",ye),q.setRawMode(ce),!ne)q.pause()}if(!he)return;if(P.onInterrupt){P.onInterrupt();return}process.kill(process.pid,"SIGINT")}import{createHash as Fp}from"node:crypto";import{existsSync as Us}from"node:fs";import{dirname as mr,join as Gs}from"node:path";import{dirname as Ms,join as Tp,resolve as Ip}from"node:path";var $p="rule.lock";function Op(P){return Tp(Ms(P),$p)}function Hs(P={}){return Ip(P.cwd??process.cwd(),".safety-net.json")}function Et(P,A){let q=A.global?A.userConfigPath??j(P,A):A.projectConfigPath??M(A.cwd??process.cwd()),V=A.global?Be(P,A):ze(q,A.cwd??process.cwd()),ee=Op(q);return{configDir:Ms(q),configPath:q,lockPath:ee,filesystemScope:V,configTarget:i(V,q),lockTarget:i(V,ee)}}var Np="`cc-safety-net rule sync` is deprecated: rulebooks are live files that need no synchronization. This run only migrates the lock and cache an earlier version left behind.",jp="cache",Mp="rulebooks";function Bs(P,A={}){let q=Et(P,A),V=i(q.filesystemScope,Vs(q.configDir)),ee=n(q.lockTarget);if(console.log(Np),ee===null&&!Us(V.path))return console.log(`No v2 lock or cache leftovers found in ${mr(q.configDir)}; nothing to migrate.`),0;let ne=qp(ee),ce=m(q.configTarget);if(!ce.config&&(n(q.configTarget)!==null||ne.size>0))return console.error(`Cannot migrate: the rules config in ${mr(q.configDir)} is missing or unreadable while v2 leftovers remain. Restore rule.json, then re-run rule sync.`),1;let he=ce.config?.rules??[];for(let ye of he.flatMap((be)=>Hp(be,ne,q,V,A.global===!0)))console.log(ye);return F(q.lockTarget),tt(V),console.log(`Removed the v2 lock and cache under ${mr(q.configDir)}.`),0}function qs(P,A){return[...new Set([{cwd:A},{cwd:A,global:!0}].flatMap((q)=>{let V=Et(P,q);return[V.lockPath,Vs(V.configDir)]}))].filter((q)=>Us(q))}function Hp(P,A,q,V,ee){if(!S(P))return[];let ne=I(P).name,ce=i(q.filesystemScope,O(q.configDir,ne)),he=n(ce);if(he!==null&&Up(he,ne))return[];let ye=A.get(P),be=ye?Gp(ye,ne,V.path,q.filesystemScope):null;if(be===null)return[`Could not migrate ${P} from the v2 cache. Run \`cc-safety-net rule update ${P}${ee?" --global":""}\` to vendor it.`];if(g(ce,be),he!==null)return[`Restored ${P} from the v2 cache over an invalid file.`];return[`Vendored ${P} from the v2 cache.`]}function Up(P,A){let q=me(P);return!("problem"in q)&&q.rulebook.name===A}function Gp(P,A,q,V){let ee=Gs(q,Mp,`${Bp(P)}--${P.digest.replace("sha256:","").slice(0,12)}`,ue),ne=n(i(V,ee));if(ne===null||Jp(ne)!==P.digest)return null;let ce=me(ne);if("problem"in ce||ce.rulebook.name!==A)return null;return ne}function Vs(P){return Gs(mr(P),jp)}function Bp(P){return([P.owner,P.repo,P.display_ref,P.name].every((V)=>typeof V==="string"&&V!=="")?`${P.owner}/${P.repo}#${P.display_ref}/${P.name}`:P.spec).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"rulebook"}function qp(P){let A=P===null?null:zp(P),q=zs(A)&&Array.isArray(A.rulebooks)?A.rulebooks:[];return new Map(q.filter(Vp).map((V)=>[V.spec,V]))}function Vp(P){return zs(P)&&typeof P.spec==="string"&&typeof P.digest==="string"}function zs(P){return!!P&&typeof P==="object"}function zp(P){try{return JSON.parse(P)}catch{return null}}function Jp(P){return`sha256:${Fp("sha256").update(P).digest("hex")}`}var Js="\r\x1B[2K",Wp="\x1B[?25l",Kp="\x1B[39m",Yp="\x1B[?25h",Xp=100,Zp=0.55,Qp=80,Ws=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];function e2(P){return new Promise((A)=>setTimeout(A,P))}async function gr(P,A={}){let q=A.output??process.stdout;if(!q.isTTY)return P;let V=A.sleep??e2,ee=!1,ne=P.then((he)=>(ee=!0,he),(he)=>{throw ee=!0,he});if(await Promise.race([ne.then(()=>!0),V(Xp).then(()=>!1)]))return ne;q.write(Wp);try{for(let he=0;!ee;he+=1)q.write(`${Js}${Os(he*Zp)}${Ws[he%Ws.length]}${Kp} ${A.loadingMessage??"Loading…"}`),await Promise.race([ne,V(Qp)]);return await ne}finally{q.write(`${Js}${Yp}`)}}async function An(P,A,q,V={}){let ee=A();if(P)await q();if(P&&ee.ready)await gr(ee.ready,V);return ee.finish()}import{stripVTControlCharacters as t2}from"node:util";var hr="amp plugins list",n2=/^\s*[✓✗]\s+cc-safety-net(?:\.ts)?\s+\(User Plugins\)\s+(\S+)\s*$/;function Ks(P){if(!P.ampPluginListOutput)return{platform:"amp",status:"n/a"};let A=t2(P.ampPluginListOutput).split(`
|
|
27
|
+
`).map((q)=>n2.exec(q)?.[1]).find((q)=>q!==void 0);if(!A)return{platform:"amp",status:"n/a"};if(A!=="active")return{platform:"amp",status:"disabled",method:hr,configPath:hr,errors:[`Amp personal plugin cc-safety-net is ${A}; run "plugins: reload" in Amp or reinstall with install --amp`]};return{platform:"amp",status:"configured",method:hr,configPath:hr}}import{existsSync as a2,readFileSync as l2}from"node:fs";import{isAbsolute as kv,join as s2}from"node:path";function _n(P){return s2(P,".gemini","config","hooks.json")}var c2=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*(?:--agy-cli|-ac)(\s|["']|$)/;function d2(P){if(!P||typeof P!=="object"||Array.isArray(P))return[];return Object.values(P).flatMap((A)=>{if(!A||typeof A!=="object"||Array.isArray(A))return[];let q=A,V=q.PreToolUse;if(!Array.isArray(V))return[];return V.flatMap((ee)=>{if(!ee||typeof ee!=="object"||Array.isArray(ee))return[];let ne=ee.hooks;if(!Array.isArray(ne))return[];return ne.flatMap((ce)=>{if(!ce||typeof ce!=="object"||Array.isArray(ce))return[];let he=ce.command;if(typeof he!=="string"||!c2.test(he))return[];return[{command:he,enabled:q.enabled!==!1}]})})})}function Ys(P){let A=_n(P.environment.home);if(!a2(A))return{platform:"antigravity-cli",status:"n/a",configPath:A};let q;try{q=d2(JSON.parse(l2(A,"utf-8")))}catch(V){return{platform:"antigravity-cli",status:"n/a",configPath:A,errors:[`Failed to parse Antigravity hooks config ${A}: ${V instanceof Error?V.message:String(V)}`]}}if(q.some((V)=>V.enabled))return{platform:"antigravity-cli",status:"configured",method:"hook config",configPath:A};if(q.length>0)return{platform:"antigravity-cli",status:"disabled",method:"hook config",configPath:A};return{platform:"antigravity-cli",status:"n/a",configPath:A}}import{join as ko}from"node:path";import{existsSync as u2,lstatSync as p2,readFileSync as f2}from"node:fs";import{join as m2}from"node:path";function Ft(P,A=(q)=>q){if(!u2(P))return{kind:"missing"};try{return{kind:"ok",value:JSON.parse(A(f2(P,"utf-8")))}}catch{return{kind:"unreadable"}}}function en(P,A){if(P==="~")return A;if(P.startsWith("~/")||P.startsWith("~\\"))return m2(A,P.slice(2));return P}function Lt(P){try{return p2(P)}catch{return}}function yr(P,A){let q=Lt(A);if(!q)return{platform:P,status:"n/a",configPath:A};if(!q.isSymbolicLink()&&q.isDirectory())return;return{platform:P,status:"n/a",configPath:A,errors:[`${A} is a symlink or not a directory; move or remove it before installing`]}}function pt(P,A){return typeof P==="object"&&P!==null?P[A]:void 0}var wo="cc-safety-net@cc-marketplace";function vr(P){return P.env.get("CLAUDE_CONFIG_DIR")||ko(P.home,".claude")}function Xs(P){return ko(vr(P),"plugins","installed_plugins.json")}function Zs(P,A){let q=pt(pt(P,"plugins"),A);return Array.isArray(q)&&q.length>0}function br(P,A){let q=Ft(Xs(P));return q.kind==="ok"&&Zs(q.value,A)}function xo(P){let A=Xs(P),q=Ft(A);if(q.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(q.kind==="missing")return{platform:"claude-code",status:"n/a"};if(!Zs(q.value,wo))return{platform:"claude-code",status:"n/a"};let V=ko(vr(P),"settings.json"),ee=Ft(V);if(ee.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(!(ee.kind==="ok"&&pt(pt(ee.value,"enabledPlugins"),wo)===!0))return{platform:"claude-code",status:"disabled",method:"plugin config",configPath:V,errors:[`${wo} is installed but not enabled in Claude Code`]};return{platform:"claude-code",status:"configured",method:"plugin config",configPath:A}}function Qs(P){return xo(P.environment)}var ea="Start Codex, open `/hooks`, select the cc-safety-net PreToolUse hook, and press `t` to trust it.";function ta(P){if(!P.codexPluginListOutput)return{platform:"codex",status:"n/a"};let A=P.codexPluginListOutput.split(`
|
|
28
|
+
`).find((q)=>q.includes("https://github.com/kenryu42/cc-safety-net.git"));if(!A)return{platform:"codex",status:"n/a"};if(!A.includes("installed,"))return{platform:"codex",status:"n/a"};if(!A.includes("installed, enabled"))return{platform:"codex",status:"disabled",method:"codex plugin list",configPath:"codex plugin list",errors:["Codex plugin line for https://github.com/kenryu42/cc-safety-net.git must contain installed, enabled."]};return{platform:"codex",status:"configured",method:"codex plugin list",configPath:"codex plugin list",errors:["Codex runs only trusted hooks, and doctor cannot check trust. Start Codex, open `/hooks`, select the cc-safety-net PreToolUse hook, and press `t` to trust it."]}}import{existsSync as Cr,readdirSync as g2,readFileSync as h2}from"node:fs";import{join as Rt}from"node:path";function _t(P){let A="",q=0,V=!1,ee=!1,ne=-1;while(q<P.length){let ce=P[q],he=P[q+1];if(ee){A+=ce,ee=!1,q++;continue}if(ce==='"'&&!V){V=!0,ne=-1,A+=ce,q++;continue}if(ce==='"'&&V){V=!1,A+=ce,q++;continue}if(ce==="\\"&&V){ee=!0,A+=ce,q++;continue}if(V){A+=ce,q++;continue}if(ce==="/"&&he==="/"){while(q<P.length&&P[q]!==`
|
|
29
|
+
`)q++;continue}if(ce==="/"&&he==="*"){q+=2;while(q<P.length-1){if(P[q]==="*"&&P[q+1]==="/"){q+=2;break}q++}continue}if(ce===","){ne=A.length,A+=ce,q++;continue}if(ce==="}"||ce==="]"){if(ne!==-1){let ye=A.slice(ne+1);if(/^\s*$/.test(ye))A=A.slice(0,ne)+ye}ne=-1,A+=ce,q++;continue}if(!/\s/.test(ce))ne=-1;A+=ce,q++}return A}function ra(P,A,q){let V=A+1,ee=!1;while(V<P.length){if(ee){ee=!1,V++;continue}if(P[V]==="\\"){ee=!0,V++;continue}if(P[V]==='"')return V+1;V++}throw Error(q)}function So(P,A,q){let V=P[A],ee=V==="["?"]":"}",ne=0,ce=A;while(ce<P.length){let he=q.skipComment?.(P,ce)??ce;if(he!==ce){ce=he;continue}if(P[ce]==='"'){ce=ra(P,ce,q.stringError);continue}if(P[ce]===V)ne++;if(P[ce]===ee){if(ne--,ne===0)return ce}ce++}throw Error(q.bracketError)}function oa(P,A){let q=P.lastIndexOf(`
|
|
30
|
+
`,A)+1;return/^[ \t]*/.exec(P.slice(q))?.[0]??""}function Lr(P,A){let q=A.end+(/^\s*/.exec(P.slice(A.end))?.[0].length??0);if(P[q]===","){let ce=P[q+1]===`
|
|
31
31
|
`?q+2:q+1;return`${P.slice(0,A.start)}${P.slice(ce)}`}let V=P.slice(0,A.start).search(/\s*$/)-1;if(P[V]!==",")return`${P.slice(0,A.start)}${P.slice(A.end)}`;let ee=P.lastIndexOf(`
|
|
32
|
-
`,V-1),ne=ee!==-1&&/^\s*$/.test(P.slice(ee+1,V))?ee:V;return`${P.slice(0,ne)}${P.slice(A.end)}`}function
|
|
33
|
-
`,A+2);return q===-1?P.length:q+1}if(P.startsWith("/*",A)){let q=P.indexOf("*/",A+2);return q===-1?P.length:q+2}return A}function
|
|
34
|
-
`)}function
|
|
35
|
-
`),{path:A,alreadyInstalled:!1};let q=
|
|
36
|
-
`)}function
|
|
32
|
+
`,V-1),ne=ee!==-1&&/^\s*$/.test(P.slice(ee+1,V))?ee:V;return`${P.slice(0,ne)}${P.slice(A.end)}`}function Co(P,A){if(P.startsWith("//",A)){let q=P.indexOf(`
|
|
33
|
+
`,A+2);return q===-1?P.length:q+1}if(P.startsWith("/*",A)){let q=P.indexOf("*/",A+2);return q===-1?P.length:q+2}return A}function na(P,A){let q=A;while(q<P.length){if(/\s/.test(P[q]??"")){q++;continue}let V=Co(P,q);if(V===q)return q;q=V}return q}function ia(P,A,q){let V=0,ee=0;while(ee<P.length){let ne=Co(P,ee);if(ne!==ee){ee=ne;continue}if(P[ee]==='"'){let ce=ra(P,ee,q.stringError);if(V===1&&JSON.parse(P.slice(ee,ce))===A){let he=na(P,ce),ye=na(P,he+1);if(P[he]===":"&&P[ye]==="[")return{start:ye,end:So(P,ye,{skipComment:Co,...q})}}ee=ce;continue}if(P[ee]==="{"||P[ee]==="[")V++;if(P[ee]==="}"||P[ee]==="]")V--;ee++}return}var Nt="cc-safety-net@cc-marketplace",wr=["cc-marketplace","cc-safety-net"],sa=["_direct","copilot-safety-net"],aa=["cc-marketplace","safety-net"],la="safety-net@cc-marketplace";function kr(P,A){let q=A.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`(^|[^a-z0-9-])${q}([^a-z0-9-]|$)`,"m").test(P??"")}function ca(P){return kr(P,"cc-safety-net@cc-marketplace")}function da(P){return kr(P,"cc-marketplace")}function ua(P){return kr(P,"copilot-safety-net")}function pa(P){return kr(P,"safety-net@cc-marketplace")}function xr(P){if(!P?.includes("cc-safety-net"))return!1;return/(^|\s)hook\s+(?:[^\s]+\s+)*(--copilot-cli|-cp)(\s|$)/.test(P)}function ma(P,A){if(!P)return null;let q=P.match(/(\d+)\.(\d+)\.(\d+)/);if(!q)return null;let V=[Number(q[1]),Number(q[2]),Number(q[3])];for(let ee=0;ee<A.length;ee++){let ne=V[ee]??0,ce=A[ee]??0;if(ne!==ce)return ne>ce}return!0}function y2(P){return ma(P,[0,0,422])}function v2(P){return ma(P,[1,0,8])}function In(P){return P.env.get("COPILOT_HOME")||Rt(P.home,".copilot")}function Ro(P){return(P.hooks?.preToolUse??[]).some((q)=>{if(q.type!==void 0&&q.type!=="command")return!1;return xr(q.command)||xr(q.bash)||xr(q.powershell)||xr(q.exec&&[q.exec,...q.args??[]].join(" "))})}function Tn(P){return P===void 0||typeof P==="string"}function b2(P){return P===void 0||Array.isArray(P)&&P.every((A)=>typeof A==="string")}function L2(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let A=P;if(A.disableAllHooks!==void 0&&typeof A.disableAllHooks!=="boolean")return!1;if(A.hooks===void 0)return!0;if(!A.hooks||typeof A.hooks!=="object"||Array.isArray(A.hooks))return!1;let q=A.hooks.preToolUse;if(q===void 0)return!0;return Array.isArray(q)&&q.every((V)=>V!==null&&typeof V==="object"&&!Array.isArray(V)&&Tn(V.type)&&Tn(V.command)&&Tn(V.bash)&&Tn(V.powershell)&&Tn(V.exec)&&b2(V.args))}function Po(P,A){try{let q=JSON.parse(_t(h2(P,"utf-8")));if(!L2(q)){A?.push(`Invalid hook config ${P}: hooks.preToolUse must be an array of hook objects`);return}return q}catch(q){A?.push(`Failed to parse ${P}: ${q instanceof Error?q.message:String(q)}`);return}}function ga(P,A){try{return g2(P).filter((q)=>q.endsWith(".json")).sort((q,V)=>q.localeCompare(V))}catch(q){return A?.push(`Failed to read ${P}: ${q instanceof Error?q.message:String(q)}`),[]}}function w2(P,A){if(!Cr(P))return[];let q=[];for(let V of ga(P,A)){let ee=Rt(P,V),ne=Po(ee,A);if(ne&&Ro(ne))q.push(ee)}return q}function vn(P,A){if(!Cr(P))return;let q=Po(P,A);if(!q)return;return{path:P,config:q}}function fa(P,A,q,V){if(A){P.push(`GitHub Copilot CLI ${A} does not support ${q}; requires ${V}+`);return}P.push(`GitHub Copilot CLI version unavailable; skipping ${q} because it requires ${V}+`)}function k2(P){for(let A of P){if(A?.config.disableAllHooks===!0)return A.path;if(A?.config.disableAllHooks===!1)return}return}function x2(P,A,q,V){let ee=In(P),ne=Rt(A,".github","hooks"),ce=Rt(ee,"hooks"),he=Rt(A,".github","copilot"),ye=Rt(A,".claude"),be=v2(q),xe=be===!0?V:void 0,Ie=[vn(Rt(he,"settings.local.json"),xe),vn(Rt(he,"settings.json"),xe),vn(Rt(ye,"settings.local.json"),xe),vn(Rt(ye,"settings.json"),xe)],Xe=[vn(Rt(ee,"settings.json"),xe),vn(Rt(ee,"config.json"),xe)];if(be!==!1){let gt=k2([...Ie,...Xe]);if(gt){if(be===null)V.push(`GitHub Copilot CLI version unavailable; treating disableAllHooks in ${gt} as active`);return{activeConfigPaths:[],repoInlineSources:Ie,disabledBy:gt}}}let et=w2(ne,V),Re=y2(q),at=Re===!0?V:void 0,ct=Cr(ce)?ga(ce,at):[],lt=[];for(let gt of ct){let ut=Rt(ce,gt),vt=Po(ut,at);if(vt&&Ro(vt))lt.push(ut)}if(Re!==!0&<.length>0)fa(V,q,`user hook files in ${ce}`,"0.0.422"),lt.length=0;let ft=[];for(let gt of[...Ie,...Xe]){if(!gt)continue;if(!Ro(gt.config))continue;if(be===!0){ft.push(gt);continue}fa(V,q,"inline hook definitions in Copilot config files","1.0.8");break}let mt=(gt)=>gt.filter((ut)=>!!ut&&ft.includes(ut)).map((ut)=>ut.path);return{activeConfigPaths:[...mt(Ie),...et,...mt(Xe),...lt],repoInlineSources:Ie}}function ha(P){let A=[],q=x2(P.environment,P.cwd,P.copilotCliVersion,A);if(q.disabledBy)return{platform:"copilot-cli",status:"disabled",method:"hook config",configPath:q.disabledBy,configPaths:[q.disabledBy],errors:A.length>0?A:void 0};let V=In(P.environment),ee=Rt(V,"installed-plugins",...wr),ne=Cr(ee),ce=Rt(V,"settings.json"),he=Ft(ce,_t),ye=(et)=>pt(pt(et,"enabledPlugins"),Nt),be=q.repoInlineSources.find((et)=>typeof ye(et?.config)==="boolean"),xe=be??(he.kind==="ok"?{path:ce,config:he.value}:void 0);if(ne&&!be&&he.kind==="unreadable")return{platform:"copilot-cli",status:"not-inspected"};let Ie=ne&&xe!==void 0&&ye(xe.config)===!1;if(Ie&&q.activeConfigPaths.length===0)return{platform:"copilot-cli",status:"disabled",method:"plugin config",configPath:xe.path,errors:[`${Nt} is installed but not enabled in Copilot CLI`]};let Xe=ne&&!Ie;if(Xe||q.activeConfigPaths.length>0){let et=q.activeConfigPaths[0];return{platform:"copilot-cli",status:"configured",method:Xe?"plugin config":"hook config",configPath:et??(Xe?ee:void 0),configPaths:q.activeConfigPaths.length>0?q.activeConfigPaths:void 0,errors:A.length>0?A:void 0}}return{platform:"copilot-cli",status:"n/a",errors:A.length>0?A:void 0}}import{existsSync as I2,readFileSync as $2}from"node:fs";import{existsSync as ya,mkdirSync as R2,readFileSync as P2}from"node:fs";import{dirname as E2,join as D2}from"node:path";import{renameSync as C2,writeFileSync as S2}from"node:fs";function kt(P,A){let q=`${P}.${process.pid}.tmp`;S2(q,A),C2(q,P)}var jt=Object.fromEntries(En.map((P)=>[P.id,`npx -y cc-safety-net hook ${P.flags[1]}`]));var $n=jt.cursor,va=30;function Rr(P){return D2(P.home,".cursor","hooks.json")}function tn(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Eo(){return{command:$n,timeout:va,failClosed:!0}}function Sr(P){return tn(P)&&P.command===$n}function A2(P){return Object.keys(P).length===3&&P.command===$n&&P.timeout===va&&P.failClosed===!0}function _2(P){try{return JSON.parse(P2(P,"utf-8"))}catch(A){if(A instanceof SyntaxError)throw Error(`Failed to parse Cursor hooks config ${P}: ${A.message}`);throw A}}function ba(P){let A=_2(P);if(!tn(A))throw Error(`Cursor hooks config ${P} must be a JSON object`);if(A.version!==1)throw Error(`Cursor hooks config ${P} must set "version": 1`);if(A.hooks!==void 0&&!tn(A.hooks))throw Error(`Cursor hooks config ${P} "hooks" must be an object`);let q=tn(A.hooks)?A.hooks.preToolUse:void 0;if(q!==void 0&&!Array.isArray(q))throw Error(`Cursor hooks config ${P} "hooks.preToolUse" must be an array`);return A}function La(P){let A=tn(P.hooks)?P.hooks.preToolUse:void 0;return Array.isArray(A)?A:[]}function T2(P){if(!P.some(Sr))return[...P,Eo()];return P.reduce((A,q)=>{if(!Sr(q))return A.result.push(q),A;if(!A.inserted)A.result.push(Eo()),A.inserted=!0;return A},{result:[],inserted:!1}).result}function wa(P,A,q){let V=tn(A.hooks)?A.hooks:{},ee={...A,hooks:{...V,preToolUse:q}};kt(P,`${JSON.stringify(ee,null,2)}
|
|
34
|
+
`)}function ka(P){let A=Rr(P);if(!ya(A))return R2(E2(A),{recursive:!0}),kt(A,`${JSON.stringify({version:1,hooks:{preToolUse:[Eo()]}},null,2)}
|
|
35
|
+
`),{path:A,alreadyInstalled:!1};let q=ba(A),V=La(q),ee=V.filter(Sr);if(tn(q.hooks)&&Array.isArray(q.hooks.preToolUse)&&ee.length===1&&ee[0]!==void 0&&A2(ee[0]))return{path:A,alreadyInstalled:!0};return wa(A,q,T2(V)),{path:A,alreadyInstalled:!1}}function xa(P){let A=Rr(P);if(!ya(A))return{path:A,alreadyInstalled:!1};let q=ba(A),V=La(q),ee=V.filter((ne)=>!Sr(ne));if(ee.length===V.length)return{path:A,alreadyInstalled:!1};return wa(A,q,ee),{path:A,alreadyInstalled:!0}}function O2(P){if(!P||typeof P!=="object"||Array.isArray(P))return[];let A=P.hooks;if(!A||typeof A!=="object"||Array.isArray(A))return[];let q=A.preToolUse;if(!Array.isArray(q))return[];return q.filter((V)=>!!V&&typeof V==="object"&&!Array.isArray(V)&&V.command===$n)}function F2(P){let A=[];if(P.length>1)A.push("Multiple managed cc-safety-net hooks found; reinstall to collapse duplicates");let q=P[0];if(q&&q.failClosed!==!0)A.push('Managed hook is missing "failClosed": true; reinstall to repair');if(q&&q.timeout!==30)A.push('Managed hook "timeout" is not 30; reinstall to repair');return A}function Ca(P){let A=Rr(P.environment);if(!I2(A))return{platform:"cursor",status:"n/a",configPath:A};let q;try{q=JSON.parse($2(A,"utf-8"))}catch(ne){return{platform:"cursor",status:"n/a",configPath:A,errors:[`Failed to parse Cursor hooks config ${A}: ${ne instanceof Error?ne.message:String(ne)}`]}}let V=O2(q);if(V.length===0)return{platform:"cursor",status:"n/a",configPath:A};let ee=F2(V);return{platform:"cursor",status:"configured",method:"hook config",configPath:A,errors:ee.length>0?ee:void 0}}import{existsSync as N2}from"node:fs";import{join as Do}from"node:path";var Ao="gemini-safety-net";function _o(P){let A=Do(P.home,".gemini","extensions"),q=Do(A,Ao);if(!N2(q))return{platform:"gemini-cli",status:"n/a"};let V=Do(A,"extension-enablement.json"),ee=Ft(V);if(ee.kind==="unreadable")return{platform:"gemini-cli",status:"not-inspected"};let ne=ee.kind==="ok"?pt(pt(ee.value,Ao),"overrides"):void 0;if(Array.isArray(ne)&&ne.some((he)=>typeof he==="string"&&he.startsWith("!")))return{platform:"gemini-cli",status:"disabled",method:"extension config",configPath:V,errors:[`${Ao} is disabled in Gemini CLI`]};return{platform:"gemini-cli",status:"configured",method:"extension config",configPath:q}}function Sa(P){return _o(P.environment)}import{existsSync as U2,readFileSync as G2}from"node:fs";import{existsSync as Pa,mkdirSync as j2,readFileSync as Ea,rmSync as M2}from"node:fs";import{dirname as H2,join as Ra}from"node:path";var On=jt["grok-build"],Dr=30;function Ar(P){return Ra(P.env.get("GROK_HOME")??Ra(P.home,".grok"),"hooks","cc-safety-net.json")}function nn(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Pr(){return{hooks:[{type:"command",command:On,timeout:Dr}]}}function Da(P){return nn(P)&&P.command===On}function Aa(P){return P.flatMap((A)=>{if(!nn(A)||!Array.isArray(A.hooks))return[A];let q=A.hooks.filter((V)=>!Da(V));if(q.length===A.hooks.length)return[A];return q.length===0?[]:[{...A,hooks:q}]})}function _a(P){try{let A=JSON.parse(P);return nn(A)?A:null}catch{return null}}function Ta(P){let A=nn(P.hooks)?P.hooks.PreToolUse:void 0;return Array.isArray(A)?A:[]}function Er(P,A,q){let V=nn(A.hooks)?A.hooks:{};kt(P,`${JSON.stringify({...A,hooks:{...V,PreToolUse:q}},null,2)}
|
|
36
|
+
`)}function Ia(P){let A=Ar(P);if(!Pa(A))return j2(H2(A),{recursive:!0}),Er(A,{},[Pr()]),{path:A,alreadyInstalled:!1};let q=_a(Ea(A,"utf-8"));if(!q)return Er(A,{},[Pr()]),{path:A,alreadyInstalled:!1};let V=Ta(q),ee=V.filter((ne)=>nn(ne)&&Array.isArray(ne.hooks)&&ne.hooks.some(Da));if(ee.length===1&&JSON.stringify(ee[0])===JSON.stringify(Pr()))return{path:A,alreadyInstalled:!0};return Er(A,q,[...Aa(V),Pr()]),{path:A,alreadyInstalled:!1}}function $a(P){let A=Ar(P);if(!Pa(A))return{path:A,alreadyInstalled:!1};let q=_a(Ea(A,"utf-8"));if(!q)return{path:A,alreadyInstalled:!1};let V=Ta(q),ee=Aa(V);if(JSON.stringify(ee)===JSON.stringify(V))return{path:A,alreadyInstalled:!1};let ne=nn(q.hooks)?q.hooks:{};if(ee.length===0&&Object.keys(q).length===1&&Object.keys(ne).length===1)return M2(A),{path:A,alreadyInstalled:!0};return Er(A,q,ee),{path:A,alreadyInstalled:!0}}function Fn(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function B2(P){if(!Fn(P)||!Fn(P.hooks))return[];let A=P.hooks.PreToolUse;if(!Array.isArray(A))return[];return A.filter((q)=>Fn(q)&&Array.isArray(q.hooks)&&q.hooks.some((V)=>Fn(V)&&V.command===On))}function q2(P){let q=(Array.isArray(P.hooks)?P.hooks.filter(Fn):[]).find((V)=>V.command===On);return[...P.matcher===void 0||P.matcher===""||P.matcher==="*"?[]:['Managed hook has a "matcher" that narrows coverage; reinstall to repair'],...q?.type==="command"?[]:['Managed hook "type" is not "command"; reinstall to repair'],...q?.timeout===Dr?[]:[`Managed hook "timeout" is not ${Dr}; reinstall to repair`]]}function Oa(P){let A=Ar(P.environment);if(!U2(A))return{platform:"grok-build",status:"n/a",configPath:A};let q;try{q=JSON.parse(G2(A,"utf-8"))}catch(ne){return{platform:"grok-build",status:"n/a",configPath:A,errors:[`Failed to parse Grok Build hooks config ${A}: ${ne instanceof Error?ne.message:String(ne)}`]}}let V=B2(q)[0];if(!V)return{platform:"grok-build",status:"n/a",configPath:A};let ee=q2(V);return{platform:"grok-build",status:"configured",method:"hook config",configPath:A,errors:ee.length>0?ee:void 0}}import{readFileSync as qa}from"node:fs";import{join as Va}from"node:path";var It="cc-safety-net",To="# cc-safety-net managed Hermes Agent plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --hermes-agent",V2=30;function Fa(P){return`${To}
|
|
37
37
|
# version: ${P}
|
|
38
|
-
`}function
|
|
38
|
+
`}function z2(P){return`${Fa(P)}name: ${It}
|
|
39
39
|
version: "${P}"
|
|
40
40
|
description: "Block destructive commands and secret-file access before Hermes runs a tool."
|
|
41
41
|
author: "cc-safety-net"
|
|
42
42
|
provides_hooks:
|
|
43
43
|
- pre_tool_call
|
|
44
|
-
`}function
|
|
44
|
+
`}function J2(P){return`${Fa(P)}"""CC Safety Net guard for Hermes Agent.
|
|
45
45
|
|
|
46
46
|
Registers pre_tool_call and forwards the tool call to the packaged CC Safety Net
|
|
47
47
|
adapter (cc-safety-net hook --hermes-agent) over JSON stdin. The adapter prints nothing
|
|
48
48
|
when the call is allowed and an {"action": "block", ...} directive when it is denied.
|
|
49
|
-
|
|
50
|
-
into an explicit block here instead.
|
|
49
|
+
Every transport and analysis failure is returned as a block that names its cause.
|
|
51
50
|
"""
|
|
52
51
|
|
|
53
52
|
import json
|
|
@@ -58,15 +57,15 @@ import subprocess
|
|
|
58
57
|
|
|
59
58
|
HOOK_EVENT = "pre_tool_call"
|
|
60
59
|
SUPPORTED_TOOLS = ("patch", "read_file", "terminal", "write_file")
|
|
61
|
-
ANALYZER = [${
|
|
62
|
-
TIMEOUT_SECONDS = ${
|
|
60
|
+
ANALYZER = [${jt["hermes-agent"].split(" ").map((A)=>`"${A}"`).join(", ")}]
|
|
61
|
+
TIMEOUT_SECONDS = ${V2}
|
|
63
62
|
|
|
64
63
|
|
|
65
64
|
def _block(detail):
|
|
66
65
|
return {"action": "block", "message": "CC Safety Net failed closed: " + detail}
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
def _terminal_cwd(task_id
|
|
68
|
+
def _terminal_cwd(task_id):
|
|
70
69
|
"""Return the directory Hermes will run this terminal command in.
|
|
71
70
|
|
|
72
71
|
A \`terminal\` call without \`workdir\` runs in the session's own cwd RECORD, not in the
|
|
@@ -84,10 +83,22 @@ def _terminal_cwd(task_id, process_cwd):
|
|
|
84
83
|
return (
|
|
85
84
|
get_session_cwd(get_current_session_key(default="") or (task_id or ""))
|
|
86
85
|
or os.environ.get("TERMINAL_CWD")
|
|
87
|
-
or
|
|
86
|
+
or os.getcwd()
|
|
88
87
|
)
|
|
89
88
|
|
|
90
89
|
|
|
90
|
+
def _file_tool_cwd(task_id):
|
|
91
|
+
"""Return the directory Hermes resolves this file tool's relative paths against.
|
|
92
|
+
|
|
93
|
+
tools/file_tools.py passes \`task_id or "default"\` to \`_resolve_base_dir\`, which walks the
|
|
94
|
+
session's cwd record, the task's cwd override, \`TERMINAL_CWD\`, then the process directory.
|
|
95
|
+
tools.file_tools defines it up to v2026.8.31 and re-exports it from tools.file_tools_paths since.
|
|
96
|
+
"""
|
|
97
|
+
from tools.file_tools import _resolve_base_dir
|
|
98
|
+
|
|
99
|
+
return str(_resolve_base_dir(task_id or "default"))
|
|
100
|
+
|
|
101
|
+
|
|
91
102
|
def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
92
103
|
if tool_name not in SUPPORTED_TOOLS:
|
|
93
104
|
return None
|
|
@@ -97,20 +108,17 @@ def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
|
97
108
|
return _block(ANALYZER[0] + " was not found on PATH.")
|
|
98
109
|
|
|
99
110
|
try:
|
|
100
|
-
cwd =
|
|
111
|
+
cwd = _terminal_cwd(task_id) if tool_name == "terminal" else _file_tool_cwd(task_id)
|
|
101
112
|
except OSError as error:
|
|
102
113
|
return _block("the working directory could not be resolved (%s)." % error)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"the Hermes session directory could not be read (%s). Update cc-safety-net and "
|
|
112
|
-
"reinstall the plugin with: npx -y cc-safety-net install --hermes-agent." % error
|
|
113
|
-
)
|
|
114
|
+
except ImportError as error:
|
|
115
|
+
# Without Hermes' own directory lookup we cannot tell which directory the call acts in,
|
|
116
|
+
# and analysing the wrong one clears every path-scoped protection.
|
|
117
|
+
return _block(
|
|
118
|
+
"the Hermes %s directory could not be read (%s). Update cc-safety-net and "
|
|
119
|
+
"reinstall the plugin with: npx -y cc-safety-net install --hermes-agent."
|
|
120
|
+
% ("session" if tool_name == "terminal" else "file tool", error)
|
|
121
|
+
)
|
|
114
122
|
|
|
115
123
|
payload = json.dumps(
|
|
116
124
|
{
|
|
@@ -133,8 +141,8 @@ def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
|
133
141
|
stdout=subprocess.PIPE,
|
|
134
142
|
stderr=subprocess.PIPE,
|
|
135
143
|
# Decode explicitly: the analyzer writes UTF-8, and a locale decoder would raise
|
|
136
|
-
# UnicodeDecodeError on output it cannot read
|
|
137
|
-
#
|
|
144
|
+
# UnicodeDecodeError on output it cannot read. "replace" turns that into unreadable
|
|
145
|
+
# output, which blocks with its cause named.
|
|
138
146
|
encoding="utf-8",
|
|
139
147
|
errors="replace",
|
|
140
148
|
# Resolve the analyzer from a neutral directory: npx prefers a repository-local
|
|
@@ -203,83 +211,84 @@ def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
|
203
211
|
|
|
204
212
|
def register(ctx):
|
|
205
213
|
ctx.register_hook("pre_tool_call", _pre_tool_call)
|
|
206
|
-
`}function
|
|
207
|
-
`)}function
|
|
208
|
-
`),ye=A?.timeoutMs??120000,be=setTimeout(()=>{ne.kill(),V(Error(
|
|
209
|
-
${he()}`.trim())))},ye);ne.on("error",(xe)=>{clearTimeout(be),V(Error(
|
|
210
|
-
${he()}`.trim())))}),ne.on("close",(xe)=>{if(clearTimeout(be),xe!==0){V(Error(
|
|
211
|
-
`));return{path:ee[0]??q??uf(P),alreadyInstalled:ee.length>0}}function sl(P){let A=[],q=Dr(P.environment),V=["opencode.json","opencode.jsonc"];for(let ee of V){let ne=hf(q,ee);if(mf(ne))try{let ce=gf(ne,"utf-8"),he=At(ce),ye=JSON.parse(he);if(qo(ye))return{platform:"opencode",status:"configured",method:"plugin array",configPath:ne,errors:A.length>0?A:void 0}}catch(ce){A.push(`Failed to parse ${ee}: ${ce instanceof Error?ce.message:String(ce)}`)}}return{platform:"opencode",status:"n/a",errors:A.length>0?A:void 0}}import{join as yf}from"node:path";function Vo(P){return yf(P.home,".pi","agent","settings.json")}function zo(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function al(P){let A=Vo(P.environment),q=Ft(A);if(q.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(q.kind==="missing")return{platform:"pi",status:"n/a"};let V=ft(q.value,"packages");if(!Array.isArray(V))return{platform:"pi",status:"n/a"};let ee=V.find((he)=>zo(typeof he==="string"?he:ft(he,"source")));if(ee===void 0)return{platform:"pi",status:"n/a"};let ne=ft(ee,"extensions");if(Array.isArray(ne)&&ne.some((he)=>typeof he==="string"&&he.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:A,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:A}}var vf={amp:Gs,"antigravity-cli":Bs,"claude-code":Js,codex:Ws,"copilot-cli":ca,cursor:ya,"gemini-cli":va,"grok-build":Ea,"hermes-agent":Ma,"kimi-code":In,openclaw:Xa,opencode:sl,pi:al};function Ln(P,A,q){let V={...q,cwd:A,environment:P};return Zn.map((ee)=>bf(vf[ee](V)))}function bf(P){if(P.status==="not-inspected")return{platform:P.platform,detected:!1,configured:!1,inspectionStatus:"not-inspected"};return{platform:P.platform,detected:P.status!=="n/a",configured:P.status==="configured",inspectionStatus:P.status!=="n/a"?"verified":P.errors&&P.errors.length>0?"failed":"not-applicable",method:P.method,configPath:P.configPath,configPaths:P.configPaths,errors:P.errors}}import{join as Lf}from"node:path";var wf=Object.freeze([{command:"git reset --hard",description:"git reset --hard",expectBlocked:!0},{command:"rm -rf /",description:"rm -rf /",expectBlocked:!0},{command:"rm -rf ./node_modules",description:"rm in cwd (safe)",expectBlocked:!1}]),kf=Object.freeze({state:"ready",diagnostics:Object.freeze([]),ruleMetadata:Object.freeze({}),policy:Object.freeze({rules:Object.freeze([]),transparentWrappers:Object.freeze([]),safety:Object.freeze({}),worktreeMode:!1,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:Object.freeze({}),destructiveCommandAllowPaths:Object.freeze([]),secretProtection:Object.freeze({enabled:!0,disabledRules:Object.freeze([]),denyPaths:Object.freeze([]),allowPaths:Object.freeze([])})})}),xf={strict:!1,paranoidRm:!1,paranoidInterpreters:!1,worktreeMode:!1,effectiveLevel:"standard",capabilities:{fail_closed:{enabled:!1,source:"preset",sources:[]},paranoid_rm:{enabled:!1,source:"preset",sources:[]},paranoid_interpreters:{enabled:!1,source:"preset",sources:[]}}};function ll(P){let A=Lf(P.tmpdir,"cc-safety-net-self-test"),q=wf.map((V)=>{let ee=N(P,d("self-test",{command:V.command},{kind:"command",shell:"auto"},{configCwd:A,executionCwd:A},V.command),{guard:{dependencies:{loadPolicySnapshot:()=>kf,getModes:()=>xf,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),ne=V.expectBlocked?"blocked":"allowed",ce=ee.decision.kind==="deny"?"blocked":"allowed";return{command:V.command,description:V.description,expected:ne,actual:ce,passed:ne===ce,reason:ee.decision.kind==="deny"?ee.decision.reason:void 0,ruleId:ee.decision.kind==="deny"?ee.decision.ruleId:void 0}});return{passed:q.filter((V)=>V.passed).length,failed:q.filter((V)=>!V.passed).length,total:q.length,results:q}}function Jo(P){let A=xt({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Ht(A.errors))return null;return{json:A.flags.json,skipUpdateCheck:A.flags.skipUpdateCheck}}async function cl(P,A={}){let q=await Pn(!A.json,()=>{let V=Cf(P,A);return{ready:V,finish:()=>V}},()=>Rn(),{loadingMessage:"Checking system status…"});if(A.json)console.log(JSON.stringify(q,null,2));else Sf(q);return q.engineSelfTest.failed>0||q.findings.some((V)=>V.severity==="error")?1:0}async function Cf(P,A){let q=A.cwd??process.cwd(),V=await sr(),ee=Ln(P,q,{ampPluginListOutput:V.ampPluginListOutput,codexPluginListOutput:V.codexPluginListOutput,copilotCliVersion:V.versions["copilot-cli"]}),ne=os(P,q),ce=is(P),he=E(P,{cwd:q}),ye=he.policy,be=R(ye,P.env),xe=H(ye,be.capabilities),Ie=tr(P,7),Xe=js(P,q),et=A.skipUpdateCheck?{currentVersion:wt(),latestVersion:null,updateAvailable:!1}:await Wt(),Re={hooks:ee,engineSelfTest:ll(P),userConfig:ne.userConfig,projectConfig:ne.projectConfig,configState:Ne(he),effectiveRules:ne.effectiveRules,environment:ce,effectiveSafety:{selectedPreset:ye.safety.level??"standard",level:be.effectiveLevel,capabilities:be.capabilities,ruleOverrides:ye.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(xe).filter(([,at])=>at.source==="rule_override"&&at.override==="off"&&at.inheritedEnabled&&at.changesInherited).map(([at])=>at),ruleCounts:{stored:Object.keys(ye.destructiveCommandRuleOverrides).length,effective:Object.values(xe).filter((at)=>at.changesInherited).length},...he.policyScopes?{policyScopes:he.policyScopes}:{}},...Xe.length>0?{v2Leftovers:Xe}:{},posture:bs(P,ne.userConfig.path),activity:Ie,update:et,system:V};return{...Re,findings:as(Re)}}function Sf(P){console.log(),console.log(cs(P.hooks)),console.log(),console.log(ds(P.engineSelfTest)),console.log(),console.log(us(P)),console.log(),console.log(ps(P.environment)),console.log(),console.log(fs(P)),console.log(),console.log(ms(P.findings)),console.log(),console.log(gs(P.activity)),console.log(),console.log(ys(P.system)),console.log(),console.log(hs(P.update)),console.log(vs(P))}import{existsSync as Rf}from"node:fs";var Pf=/^[A-Za-z0-9_@%+=:,./-]+$/,dl="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function Wo(P){let A=xt({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Ht(A.errors))return console.error(dl),console.error("Pass -- before a command that starts with dashes."),null;if(A.values.cwd!==void 0&&!Rf(A.values.cwd))return console.error(`Error: --cwd path does not exist: ${A.values.cwd}`),null;let q=A.positionals.length===1?A.positionals[0]:A.positionals.map((V)=>Pf.test(V)?V:`'${V.replaceAll("'","'\\''")}'`).join(" ");if(!q)return console.error("Error: No command provided"),console.error(dl),null;return{json:A.flags.json,cwd:A.values.cwd,command:q}}function ul(P){if(P)return{dh:"=",dv:"|",dtl:"+",dtr:"+",dbl:"+",dbr:"+",h:"-",v:"|",tl:"+",tr:"+",bl:"+",br:"+",sh:"="};return{dh:"═",dv:"║",dtl:"╔",dtr:"╗",dbl:"╚",dbr:"╝",h:"─",v:"│",tl:"┌",tr:"┐",bl:"└",br:"┘",sh:"━"}}function pl(P,A){let V=A-18;return[`${P.dtl}${P.dh.repeat(A)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(V)}${P.dv}`,`${P.dbl}${P.dh.repeat(A)}${P.dbr}`]}function Ko(P){return JSON.stringify(P)}function fl(P,A=0){return`[${P.map((V,ee)=>ls(V,ee,A)).join(",")}]`}function jn(P,A,q=70){let V=P.split(" "),ee=[],ne="";for(let ce of V)if(ne&&ne.length+ce.length+1>q)ee.push(ne),ne=ce;else ne=ne?`${ne} ${ce}`:ce;if(ne)ee.push(ne);return ee.map((ce,he)=>he===0?ce:`${A}${ce}`)}function ml(P,A,q){let V=[];switch(P.type){case"parse":return null;case"env-strip":return V.push(""),V.push(`STEP ${A} ${q.h} Strip environment variables`),V.push(` Removed: ${P.envVars.map((ee)=>`${ee}=<redacted>`).join(", ")}`),V.push(` Tokens: ${Ko(P.output)}`),{lines:V,incrementStep:!0};case"leading-tokens-stripped":return V.push(""),V.push(`STEP ${A} ${q.h} Strip wrappers`),V.push(` Removed: ${P.removed.join(", ")}`),V.push(` Tokens: ${Ko(P.output)}`),{lines:V,incrementStep:!0};case"shell-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Detect shell wrapper`),V.push(` Wrapper: ${P.wrapper} -c`),V.push(` Inner: ${P.innerCommand}`),{lines:V,incrementStep:!0};case"interpreter":{if(V.push(""),V.push(`STEP ${A} ${q.h} Detect interpreter`),V.push(` Interpreter: ${P.interpreter}`),V.push(` Code: ${P.codeArg}`),P.paranoidBlocked)V.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:V,incrementStep:!0}}case"busybox":return V.push(""),V.push(`STEP ${A} ${q.h} Busybox wrapper`),V.push(` Subcommand: ${P.subcommand}`),{lines:V,incrementStep:!0};case"transparent-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Transparent wrapper`),V.push(` Wrapper: ${P.wrapper}`),V.push(` Tokens: ${Ko(P.output)}`),{lines:V,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(V.push(""),V.push(`STEP ${A} ${q.h} Match rules`),V.push(` Rule: ${P.rule}()`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}case"worktree-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Worktree relaxation`),V.push(` Mode: ${o.worktree.name}`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed local discard in linked worktree"),{lines:V,incrementStep:!0};case"temp-root-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Temp-root relaxation`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed git discard in a temp-root repository"),{lines:V,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return V.push(""),V.push(`STEP ${A} ${q.h} Fallback scan`),V.push(` Found: ${P.embeddedCommandFound}`),{lines:V,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(V.push(""),V.push(`STEP ${A} ${q.h} Custom rules`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return V.push(""),V.push(`STEP ${A} ${q.h} Dangerous text check`),V.push(` Token: ${P.token}`),V.push(" Result: MATCHED"),{lines:V,incrementStep:!0};return null}case"strict-unparseable":return V.push(""),V.push(`STEP ${A} ${q.h} Strict mode check`),V.push(` Command: ${P.rawCommand}`),V.push(" Result: ✗ UNPARSEABLE"),{lines:V,incrementStep:!0};case"segment-skipped":return null;case"error":return V.push(""),V.push(`ERROR: ${P.message}`),{lines:V,incrementStep:!1};default:return P}}function Yo(P,A){let q=ul(A?.asciiOnly??!1),V=58,ee=[],ne=1;ee.push(...pl(q,58)),ee.push("");let ce=P.trace.steps.find((Re)=>Re.type==="error");if(ce&&ce.type==="error"){ee.push("ERROR"),ee.push(` ${ce.message}`),ee.push(""),ee.push("RESULT"),ee.push(` Status: ${P.result==="blocked"?dt.red("BLOCKED"):dt.green("ALLOWED")}`),ee.push(""),ee.push("CONFIG");let Re=P.configSource??"none";return ee.push(` Path: ${Re}`),ee.join(`
|
|
212
|
-
`)}let he=P.trace.steps.find((Re)=>Re.type==="parse");if(he&&he.type==="parse"){ee.push("INPUT"),ee.push(` ${he.input}`),ee.push(""),ee.push(`STEP ${ne} ${q.h} Split shell commands`),ne++;for(let Re=0;Re<he.segments.length;Re++){let at=he.segments[Re];if(at){let ct=Math.random();ee.push(` Segment ${Re+1}: ${fl(at,ct)}`)}}}let ye=P.trace.segments,be=ye.length>1;for(let Re of ye){if(be){ee.push("");let pt="";if(he&&he.type==="parse"){let Xr=he.segments[Re.index];if(Xr)pt=Xr.join(" ")}let mt=54,gt=pt,ut=` Segment ${Re.index+1}: `,yt=" ";if(pt){if(ut.length+pt.length+yt.length>mt){let Hd=mt-ut.length-yt.length;gt=`${pt.substring(0,Hd-1)}…`}}let Dt=pt?`${ut}${gt}${yt}`:` Segment ${Re.index+1} `,Nd=pt?`${ut}${dt.cyan(gt)}${yt}`:Dt,$i=58-Dt.length,Ii=Math.floor($i/2),Md=$i-Ii;ee.push(`${q.sh.repeat(Ii)}${Nd}${q.sh.repeat(Md)}`)}if(Re.steps.find((pt)=>pt.type==="segment-skipped")){ee.push(""),ee.push(" (skipped — prior segment blocked)");continue}let ct=!1,lt=!1;for(let pt of Re.steps){let mt=ml(pt,ne,q);if(mt){if(lt=!0,pt.type==="recurse"){ee.push("");let gt=" RECURSING ",ut=58-gt.length-4;ee.push(` ${q.tl}${q.h}${gt}${q.h.repeat(ut)}`),ee.push(` ${q.v}`),ct=!0;continue}for(let gt of mt.lines)if(ct)ee.push(` ${q.v} ${gt}`);else ee.push(gt);if(mt.incrementStep)ne++}}if(ct)ee.push(` ${q.v}`),ee.push(` ${q.bl}${q.h.repeat(56)}`);if(!lt)ee.push(""),ee.push(` ${dt.green("✓")} Allowed (no matching rules)`)}if(ee.push(""),ee.push("RESULT"),P.result==="blocked"){if(ee.push(` Status: ${dt.red("BLOCKED")}`),P.customRule){if(ee.push(` Rule: ${P.customRule.id}`),P.customRule.rulebook)ee.push(` Rulebook: ${P.customRule.rulebook.name} ${P.customRule.rulebook.version}`);if(P.customRule.source)ee.push(` Source: ${P.customRule.source}`);if(P.customRule.override)ee.push(` Override: reason ${P.customRule.override.reason}`)}if(P.reason){let Re=jn(P.reason," ");ee.push(` Reason: ${Re[0]}`);for(let at=1;at<Re.length;at++)ee.push(Re[at]??"")}}else ee.push(` Status: ${dt.green("ALLOWED")}`);ee.push(""),ee.push("CONFIG");let xe=P.configSource??"none",Ie=P.configValid?"":" (invalid)";ee.push(` Path: ${xe}${Ie}`);let Xe=P.safetyPresetScope;ee.push(` Safety preset: ${P.selectedPreset??"standard"}${Xe?` (${rr(Xe)})`:""}`),ee.push(` Effective capabilities: ${P.effectiveLevel}`);let et=Object.entries(P.destructiveCommandRuleOverrides??{});if(ee.push(` Rule customizations: ${et.length}`),P.ruleActivation)ee.push(` Rule activation: ${P.ruleActivation.id} — ${P.ruleActivation.enabled?"on":"off"} via ${P.ruleActivation.source}`);return ee.join(`
|
|
213
|
-
`)}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
`))}function
|
|
217
|
-
`)}
|
|
218
|
-
`)}function
|
|
214
|
+
`}function Nn(P){return[{name:"__init__.py",content:J2(P)},{name:"plugin.yaml",content:z2(P)}]}import{mkdirSync as W2,readdirSync as K2,readFileSync as Na,rmSync as Io}from"node:fs";import{basename as Y2,dirname as X2,join as Bt}from"node:path";var Z2="__pycache__",Q2=/^[a-z0-9][a-z0-9_-]{0,63}$/;function ef(P){try{return Na(P,"utf-8").trim()}catch{return}}function $o(P){let A=P.env.get("HERMES_HOME")?.trim();if(A&&Y2(X2(A))==="profiles")return A;let q=A||Bt(P.home,".hermes"),V=Bt(q,"active_profile"),ee=ef(V),ne=ee?.toLowerCase();if(!ne||ne==="default")return q;if(!Q2.test(ne))throw Error(`Invalid Hermes profile name "${ee}" in ${V}; run \`hermes profile use <name>\` with a valid profile.`);return Bt(q,"profiles",ne)}function Oo(P){return Bt($o(P),"plugins",It)}function Fo(P){return P.startsWith(To)}function No(P,A){let q=Oo(P),V=Lt(q);if(V&&(V.isSymbolicLink()||!V.isDirectory()))throw Error(`Refusing to ${A} ${q}: not a regular directory. Move or remove it and rerun ${A==="install"?"install":"uninstall"} --hermes-agent.`);return q}function ja(P,A){let q=Lt(P);if(!q)return;if(q.isSymbolicLink()||!q.isFile())throw Error(`Refusing to ${A} ${P}: not a regular file. Move or remove it.`);let V=Na(P,"utf-8");if(!Fo(V))throw Error(`Refusing to ${A} unmanaged file at ${P}. Move or remove it.`);return V}function Ma(P){let A=No(P,"install"),q=Nn(wt());if(q.map((ee)=>ja(Bt(A,ee.name),"overwrite")).every((ee,ne)=>ee===q[ne]?.content))return{path:A,alreadyInstalled:!0};return W2(A,{recursive:!0}),q.forEach((ee)=>{kt(Bt(A,ee.name),ee.content)}),{path:A,alreadyInstalled:!1}}function jo(P){let A=No(P,"remove");if(!Lt(A))return[];return Nn(wt()).filter((q)=>ja(Bt(A,q.name),"remove")!==void 0)}function Ha(P){let A=No(P,"remove");if(!Lt(A))return{path:A,alreadyInstalled:!1};let q=jo(P);if(q.forEach((V)=>{Io(Bt(A,V.name))}),Io(Bt(A,Z2),{recursive:!0,force:!0}),K2(A).length===0)Io(A,{recursive:!0});return{path:A,alreadyInstalled:q.length>0}}var jn="hermes-agent",Ua=/^([^\s#][^:]*):/,tf=/^\s+([A-Za-z_][\w-]*):/,Ga=/^\s+-\s*(.*)$/;function nf(P){return P.trim().replace(/^(["'])(.*)\1$/,"$2")}function rf(P){let A=P.split(/\r?\n/),q=A.findIndex((ne)=>Ua.exec(ne)?.[1]?.trim()==="plugins");if(q===-1)return[];let V=A.slice(q+1),ee=V.findIndex((ne)=>Ua.test(ne));return ee===-1?V:V.slice(0,ee)}function Ba(P,A){let q=rf(P),V=q.findIndex((ce)=>tf.exec(ce)?.[1]===A);if(V===-1)return[];let ee=q.slice(V+1),ne=ee.findIndex((ce)=>!Ga.test(ce));return(ne===-1?ee:ee.slice(0,ne)).map((ce)=>nf(Ga.exec(ce)?.[1]??""))}function of(P){try{return qa(Va($o(P),"config.yaml"),"utf-8")}catch{return}}function Mo(P){let A=of(P)??"";return Ba(A,"enabled").includes(It)&&!Ba(A,"disabled").includes(It)}function za(P){return/^# version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function sf(P,A){let q=Lt(P);if(!q)return{error:`${A.name} is missing from ${P}; run install --hermes-agent`};if(q.isSymbolicLink()||!q.isFile())return{error:`${P} is a symlink or not a regular file; move or remove it`};try{let V=qa(P,"utf-8");if(!Fo(V))return{error:`Unmanaged ${A.name} occupies ${P}; move or remove it`};if(za(V)===wt()&&V!==A.content)return{error:`Modified ${A.name} occupies ${P}; run install --hermes-agent to restore it`};return{content:V}}catch(V){return{error:`Failed to read ${P}: ${V instanceof Error?V.message:String(V)}`}}}function af(P){try{return{path:Oo(P)}}catch(A){return{error:A instanceof Error?A.message:String(A)}}}function Ja(P){let A=af(P.environment);if("error"in A)return{platform:jn,status:"n/a",errors:[A.error]};let q=A.path,V=yr(jn,q);if(V)return V;let ee=Nn(wt()).map((ye)=>sf(Va(q,ye.name),ye)),ne=ee.flatMap((ye)=>("error"in ye)?[ye.error]:[]);if(ne.length>0)return{platform:jn,status:"n/a",configPath:q,errors:ne};let ce=ee.some((ye)=>("content"in ye)&&za(ye.content)!==wt()),he=ce?["Installed Hermes Agent plugin is outdated; run install --hermes-agent to update"]:[];if(!Mo(P.environment))return{platform:jn,status:"disabled",method:"plugin directory",configPath:q,errors:[`${It} is not enabled in Hermes; run \`hermes plugins enable ${It}\``,...he]};return{platform:jn,status:"configured",method:"plugin directory",configPath:q,errors:ce?he:void 0}}import{existsSync as lf,readFileSync as cf}from"node:fs";import{join as Wa}from"node:path";var df=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-code(\s|["']|$)/;function uf(P){return Wa(P.env.get("KIMI_CODE_HOME")||Wa(P.home,".kimi-code"),"config.toml")}function Mn(P){let A=uf(P.environment);if(!lf(A))return{platform:"kimi-code",status:"n/a",configPath:A};try{if(!df.test(cf(A,"utf-8")))return{platform:"kimi-code",status:"n/a",configPath:A}}catch(q){return{platform:"kimi-code",status:"n/a",configPath:A,errors:[`Failed to read ${A}: ${q instanceof Error?q.message:String(q)}`]}}return{platform:"kimi-code",status:"configured",method:"hook config",configPath:A}}import{readFileSync as ol}from"node:fs";import{join as Un}from"node:path";var yt="cc-safety-net",Tt="index.js",bn="openclaw.plugin.json",Ln="package.json";var _r="// cc-safety-net managed OpenClaw plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --openclaw";import{existsSync as mf,lstatSync as gf,readdirSync as hf,readFileSync as yf}from"node:fs";import{dirname as Ya,join as Kt}from"node:path";import{fileURLToPath as vf}from"node:url";import{spawn as pf}from"node:child_process";function ff(P){return P.join(" ")}function Ho(P,A,q){return[`Failed to run ${ff(P)}${A===null?"":` (exit ${A})`}.`,q.trim()].filter(Boolean).join(`
|
|
215
|
+
`)}function Uo(P){let A={stdout:"",stderr:""};return P.stdout.setEncoding("utf-8"),P.stderr.setEncoding("utf-8"),P.stdout.on("data",(q)=>{A.stdout+=q}),P.stderr.on("data",(q)=>{A.stderr+=q}),A}function St(P,A){return new Promise((q,V)=>{let ee=Qt([...P],process.env),ne=pf(ee.cmd,ee.args,{stdio:["ignore","pipe","pipe"]}),ce=Uo(ne),he=()=>[ce.stdout,ce.stderr].filter(Boolean).join(`
|
|
216
|
+
`),ye=A?.timeoutMs??120000,be=setTimeout(()=>{ne.kill(),V(Error(Ho(P,null,`Timed out after ${ye}ms.
|
|
217
|
+
${he()}`.trim())))},ye);ne.on("error",(xe)=>{clearTimeout(be),V(Error(Ho(P,null,`${xe.message}
|
|
218
|
+
${he()}`.trim())))}),ne.on("close",(xe)=>{if(clearTimeout(be),xe!==0){V(Error(Ho(P,xe,he())));return}q(A?.stdoutOnly?ce.stdout:he())})})}async function Go(P){for(let A of P)await St(A)}async function Ka(P){for(let A of P)try{await St(A)}catch(q){console.warn(q instanceof Error?q.message:String(q))}}var Bo=Kt("openclaw",yt),rn=`run \`openclaw plugins enable ${yt}\``,bf="config reload superseded by a newer runtime config source",Lf=[Tt,bn,Ln];function Xa(P){let A=P.env.get("OPENCLAW_HOME")?.trim();return A?en(A,P.home):P.home}function Za(P){let A=Xa(P),q=P.env.get("OPENCLAW_STATE_DIR")?.trim();if(q)return en(q,A);let V=P.env.get("OPENCLAW_CONFIG_PATH")?.trim();return V?Ya(en(V,A)):Kt(A,".openclaw")}function Qa(P){let A=P.env.get("OPENCLAW_CONFIG_PATH")?.trim();return A?en(A,Xa(P)):Kt(Za(P),"openclaw.json")}function Hn(P){return Kt(Za(P),"extensions",yt)}function wf(P){let A=hf(P);if(A.length===0)return!0;if(A.some((ee)=>!Lf.includes(ee)))return!1;let q=Kt(P,Tt),V=Lt(q);return V!==void 0&&!V.isSymbolicLink()&&V.isFile()&&yf(q,"utf-8").startsWith(_r)}function qo(P){let A=Hn(P),q=Lt(A);if(!q)return;if(!q.isSymbolicLink()&&q.isDirectory()&&wf(A))return;throw Error(`Refusing to modify ${A}: it does not hold a cc-safety-net managed OpenClaw plugin. Move or remove it, then run the command again.`)}function el(){let P=Ya(vf(import.meta.url));return[Kt(P,Bo),Kt(P,"..",Bo),Kt(P,"..","..","..","dist",Bo)]}function Vo(P=el()){return P.find((A)=>mf(A)&&gf(A).isDirectory())}function kf(P=el()){let A=Vo(P);if(!A)throw Error("Packaged OpenClaw plugin directory not found. Reinstall cc-safety-net and try again.");return A}function tl(P=kf()){return[["openclaw","plugins","install",P,"--force","--accept-capabilities"]]}function xf(P){let A=(()=>{try{return JSON.parse(P)}catch{return}})(),q=pt(pt(A,"plugin"),"status");return typeof q==="string"?q:void 0}async function Cf(){await St(["openclaw","plugins","enable",yt]).catch((P)=>{if(!(P instanceof Error&&P.message.includes(bf)))throw P})}async function nl(P){let A=async()=>xf(await St(["openclaw","plugins","inspect",yt,"--runtime","--json"],{stdoutOnly:!0})),q=await A(),V=q==="disabled"&&P;if(V)await Cf();let ee=V?await A():q;if(ee==="loaded")return;throw Error(`${ee===void 0?`The ${yt} plugin's load state could not be verified: OpenClaw's runtime inspect report was unreadable.`:ee==="disabled"?`OpenClaw reports the ${yt} plugin with status "disabled"; ${rn}.`:`OpenClaw reports the ${yt} plugin with status "${ee}".`} Run \`openclaw plugins inspect ${yt} --runtime\` for details.`)}var Tr="openclaw";function wn(P,A){let q=Un(P,A),V=Lt(q);if(!V)return{error:`${A} is missing from ${q}; run install --openclaw`};if(V.isSymbolicLink()||!V.isFile())return{error:`${q} is a symlink or not a regular file; move or remove it`};try{return{content:ol(q,"utf-8")}}catch(ee){return{error:`Failed to read ${q}: ${ee instanceof Error?ee.message:String(ee)}`}}}function il(P){try{return JSON.parse(_t(P))}catch{return}}function Sf(P){let A=wn(P,bn);if("error"in A)return A.error;if(pt(il(A.content),"id")===yt)return;return`${Un(P,bn)} is not a valid ${yt} manifest; run install --openclaw`}function Rf(P){let A=wn(P,Ln);if("error"in A)return A.error;let q=pt(pt(il(A.content),"openclaw"),"extensions");if(Array.isArray(q)&&q.includes(`./${Tt}`))return;return`${Un(P,Ln)} does not point OpenClaw at ${Tt}; run install --openclaw`}function rl(P){return Array.isArray(P)?P.filter((A)=>typeof A==="string"):[]}function Pf(P){let A=Qa(P);if(!Lt(A))return`${yt} is not enabled; ${rn}`;let q=(()=>{try{return JSON.parse(_t(ol(A,"utf-8")))}catch{return}})();if(q===void 0)return`Failed to read ${A}; fix it, then ${rn}`;let V=pt(q,"plugins");if(pt(V,"enabled")===!1)return`plugins.enabled is false in ${A}; no OpenClaw plugin loads`;let ee=pt(pt(pt(V,"entries"),yt),"enabled");if(rl(pt(V,"deny")).includes(yt)||ee===!1)return`${yt} is disabled in ${A}; ${rn}`;let ne=rl(pt(V,"allow"));if(ne.length>0&&!ne.includes(yt))return`plugins.allow in ${A} does not list ${yt}; add it, then ${rn}`;if(ne.includes(yt)||ee===!0)return;return`${yt} is not enabled; ${rn}`}function sl(P){return/^\/\/ version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function Ef(P,A,q){if(q===void 0)return[];let V=wn(q,Tt);if(!(("content"in V)&&sl(V.content)===A))return[];return[Tt,bn,Ln].flatMap((ne)=>{let ce=wn(P,ne),he=wn(q,ne);if("error"in ce||"error"in he||ce.content===he.content)return[];return[`Modified ${ne} occupies ${Un(P,ne)}; run install --openclaw to restore it`]})}function al(P){let A=Hn(P.environment),q=yr(Tr,A);if(q)return q;let V=wn(A,Tt),ne=["error"in V?V.error:V.content.startsWith(_r)?void 0:`Unmanaged ${Tt} occupies ${Un(A,Tt)}; move or remove it`,Sf(A),Rf(A)].filter((xe)=>xe!==void 0),ce="content"in V?sl(V.content):void 0,he=ne.length>0?ne:Ef(A,ce,Vo());if(he.length>0)return{platform:Tr,status:"n/a",configPath:A,errors:he};let ye=ce===wt()?[]:["Installed OpenClaw plugin is outdated; run install --openclaw to update"],be=Pf(P.environment);if(be)return{platform:Tr,status:"disabled",method:"plugin directory",configPath:A,errors:[be,...ye]};return{platform:Tr,status:"configured",method:"plugin directory",configPath:A,errors:ye.length>0?ye:void 0}}import{existsSync as Nf,readFileSync as jf}from"node:fs";import{basename as Mf}from"node:path";import{existsSync as Ir,readFileSync as zo,rmSync as Df}from"node:fs";import{join as Mt}from"node:path";import{pathToFileURL as Af}from"node:url";var Gn="cc-safety-net",on=`${Gn}@latest`,Jo=["opencode.json","opencode.jsonc"],_f=60,Tf=250,ll="CCSafetyNetPlugin",If={stringError:"Unterminated string in OpenCode config",bracketError:"Unmatched plugin array in OpenCode config"};function cl(P){return Mt(P.env.get("XDG_CONFIG_HOME")||Mt(P.home,".config"),"opencode")}function Wo(P){return P.env.get("OPENCODE_CONFIG_DIR")||cl(P)}function Ko(P){return Jo.map((A)=>Mt(Wo(P),A))}function Yo(P){return[...new Set([Wo(P),cl(P)])].flatMap((A)=>Jo.map((q)=>Mt(A,q)))}function dl(P){return Mt(P.env.get("XDG_CACHE_HOME")||Mt(P.home,".cache"),"opencode","packages",on)}function ul(P){Df(dl(P),{recursive:!0,force:!0})}async function pl(P){let A=(await St(["opencode","--version"],{stdoutOnly:!0})).trim(),q=/^(?:opencode\s+)?v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(A),V=Number(q?.[1]),ee=Number(q?.[2]),ne=Number(q?.[3]);if(!q||V!==1&&V!==2||V===1&&(ee<18||ee===18&&ne<29)||V===2&&ee===0&&ne<6)throw Error(`OpenCode 1.18.29+ or 2.0.6+ is required; found ${A||"an unknown version"}.`);if(V===2){for(let ce of Ko(P)){if(!Ir(ce))continue;let he=Zo(zo(ce,"utf-8"),ce);if(["plugin","plugins"].some((be)=>{let xe=pt(he,be);return Array.isArray(xe)&&xe.some((Ie)=>$r(Ie)&&(typeof Ie==="string"?Ie:pt(Ie,"package"))!==on)}))throw Error(`Change the cc-safety-net package spec in ${ce} to ${on}, preserving its options, then retry. Adding another spec would create a duplicate plugin ID.`)}return{commands:[],afterInstall:async()=>{if((await St(["opencode","plugin","add",on],{stdoutOnly:!0})).includes("is already configured in"))await St(["opencode","plugin","update",on]);let he=await gl();if(!/^cc-safety-net\s+\S+\s+cc-safety-net@latest\s*$/m.test(he))throw Error("OpenCode did not load cc-safety-net from cc-safety-net@latest. Run `opencode plugin list` for details.");let ye=["--param",`location[directory]=${process.cwd()}`];await St(["opencode","api","integration.list",...ye]);let be=await St(["opencode","api","plugin.list",...ye],{stdoutOnly:!0}),xe=Xo(be);if(xe)throw Error(xe);if(!fl(be).some(ml))throw Error("OpenCode lists no active cc-safety-net for this directory. Run `opencode api plugin.list` for details.")}}}return ul(P),{commands:[["opencode","plugin","-g","-f",on]],afterInstall:()=>Of(P)}}function fl(P){return $f(P).filter((A)=>pt(A,"id")===Gn||$r(pt(pt(A,"source"),"target"))).map((A)=>pt(A,"state"))}function ml(P){return pt(P,"status")==="active"}function Xo(P){let A=fl(P);if(A.some(ml))return;let q=A.find((V)=>pt(V,"status")==="failed");if(!q)return;return`OpenCode reports cc-safety-net failed: ${String(pt(q,"error")).split(`
|
|
219
|
+
`)[0]}`}function $f(P){if(!P)return[];try{let A=pt(JSON.parse(P),"data");return Array.isArray(A)?A:[]}catch{return[]}}async function gl(P=1){let A=await St(["opencode","plugin","list"],{stdoutOnly:!0});if(/^cc-safety-net\s|\scc-safety-net@latest\s*$/m.test(A)||P===_f)return A;return await new Promise((q)=>setTimeout(q,Tf)),gl(P+1)}async function Of(P){let A=Mt(dl(P),"node_modules",Gn),q=Mt(A,"package.json");if(!Ir(q))throw Error(`The OpenCode plugin cache at ${A} is missing its package, so OpenCode would load nothing and fail open. Run \`opencode plugin -g -f ${on}\` for details.`);let V=pt(JSON.parse(zo(q,"utf-8")),"main");if(typeof V!=="string")throw Error(`The cached OpenCode plugin at ${A} declares no "main" entry.`);let ee=Mt(A,V);if(typeof(await import(Af(ee).href))[ll]==="function")return;throw Error(`The cached OpenCode plugin at ${ee} does not export a callable ${ll}, so OpenCode would load nothing and fail open.`)}function Zo(P,A){try{return JSON.parse(_t(P))}catch(q){if(q instanceof SyntaxError)throw Error(`Failed to parse OpenCode config ${A}: ${q.message}`);throw q}}function $r(P){let A=typeof P==="string"?P:pt(P,"package");return typeof A==="string"&&(A===Gn||A.startsWith(`${Gn}@`))}function Qo(P){return["plugin","plugins"].some((A)=>{let q=pt(P,A);return Array.isArray(q)&&q.some($r)})}function Ff(P,A){let V=["plugin","plugins"].flatMap((ee)=>{let ne=ia(P,ee,If);if(!ne)return[];let ce=[],he=0,ye=ne.start+1,be=P.slice(ne.start+1,ne.end).matchAll(/\/\/[^\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|[^"\s{}[\],/]+|[{}[\],]/g);for(let xe of be){if(xe[0].startsWith("//")||xe[0].startsWith("/*"))continue;let Ie=ne.start+1+xe.index;if(he===0)ye=Ie;if(xe[0]==="{"||xe[0]==="[")he++;if(xe[0]==="}"||xe[0]==="]")he--;if(he!==0||xe[0]===",")continue;let Xe=Ie+xe[0].length;if($r(JSON.parse(_t(P.slice(ye,Xe)))))ce.push({start:ye,end:Xe})}return ce}).sort((ee,ne)=>ee.start-ne.start).reverse().reduce((ee,ne)=>{let ce=/^(?:\s|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*,/.exec(ee.slice(ne.end));if(ce?.[0].includes("/")){let he=ne.end+ce[0].length-1;return ee.slice(0,ne.start)+ee.slice(ne.end,he)+ee.slice(he+1)}return Lr(ee,ne)},P);return Zo(V,A),V}function hl(P){ul(P);let A=Yo(P),q=A.find((ne)=>Ir(ne)),V=[],ee=[];for(let ne of A){if(!Ir(ne))continue;try{let ce=zo(ne,"utf-8");if(!Qo(Zo(ce,ne)))continue;kt(ne,Ff(ce,ne)),ee.push(ne)}catch(ce){V.push(ce instanceof Error?ce.message:String(ce))}}if(V.length>0)throw Error(V.join(`
|
|
220
|
+
`));return{path:ee[0]??q??Mt(Wo(P),Jo[0]),alreadyInstalled:ee.length>0}}function kn(P){let A=[];for(let q of P.openCodeVersion?.startsWith("2.")?Ko(P.environment):Yo(P.environment))if(Nf(q))try{let V=jf(q,"utf-8"),ee=_t(V),ne=JSON.parse(ee);if(Qo(ne)){let ce=Xo(P.openCodePluginListOutput);if(ce)return{platform:"opencode",status:"disabled",method:"opencode api plugin.list",configPath:q,errors:[...A,ce]};return{platform:"opencode",status:"configured",method:"plugin array",configPath:q,errors:A.length>0?A:void 0}}}catch(V){A.push(`Failed to parse ${Mf(q)}: ${V instanceof Error?V.message:String(V)}`)}return{platform:"opencode",status:"n/a",errors:A.length>0?A:void 0}}import{join as yl}from"node:path";function ei(P){let A=P.env.get("PI_CODING_AGENT_DIR");return yl(A?en(A,P.home):yl(P.home,".pi","agent"),"settings.json")}function ti(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function vl(P){let A=ei(P.environment),q=Ft(A);if(q.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(q.kind==="missing")return{platform:"pi",status:"n/a"};let V=pt(q.value,"packages");if(!Array.isArray(V))return{platform:"pi",status:"n/a"};let ee=V.find((he)=>ti(typeof he==="string"?he:pt(he,"source")));if(ee===void 0)return{platform:"pi",status:"n/a"};let ne=pt(ee,"extensions");if(Array.isArray(ne)&&ne.some((he)=>typeof he==="string"&&he.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:A,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:A}}var Hf={amp:Ks,"antigravity-cli":Ys,"claude-code":Qs,codex:ta,"copilot-cli":ha,cursor:Ca,"gemini-cli":Sa,"grok-build":Oa,"hermes-agent":Ja,"kimi-code":Mn,openclaw:al,opencode:kn,pi:vl};function xn(P,A,q){let V={...q,cwd:A,environment:P};return rr.map((ee)=>Uf(Hf[ee](V)))}function Uf(P){if(P.status==="not-inspected")return{platform:P.platform,detected:!1,configured:!1,inspectionStatus:"not-inspected"};return{platform:P.platform,detected:P.status!=="n/a",configured:P.status==="configured",inspectionStatus:P.status!=="n/a"?"verified":P.errors&&P.errors.length>0?"failed":"not-applicable",method:P.method,configPath:P.configPath,configPaths:P.configPaths,errors:P.errors}}import{join as Gf}from"node:path";var Bf=Object.freeze([{command:"git reset --hard",description:"git reset --hard",expectBlocked:!0},{command:"rm -rf /",description:"rm -rf /",expectBlocked:!0},{command:"rm -rf ./node_modules",description:"rm in cwd (safe)",expectBlocked:!1}]),qf=Object.freeze({state:"ready",diagnostics:Object.freeze([]),ruleMetadata:Object.freeze({}),policy:Object.freeze({rules:Object.freeze([]),transparentWrappers:Object.freeze([]),safety:Object.freeze({}),worktreeMode:!1,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:Object.freeze({}),destructiveCommandAllowPaths:Object.freeze([]),secretProtection:Object.freeze({enabled:!0,disabledRules:Object.freeze([]),denyPaths:Object.freeze([]),allowPaths:Object.freeze([])})})}),Vf={strict:!1,paranoidRm:!1,paranoidInterpreters:!1,worktreeMode:!1,effectiveLevel:"standard",capabilities:{fail_closed:{enabled:!1,source:"preset",sources:[]},paranoid_rm:{enabled:!1,source:"preset",sources:[]},paranoid_interpreters:{enabled:!1,source:"preset",sources:[]}}};function bl(P){let A=Gf(P.tmpdir,"cc-safety-net-self-test"),q=Bf.map((V)=>{let ee=N(P,d("self-test",{command:V.command},{kind:"command",shell:"auto"},{configCwd:A,executionCwd:A},V.command),{guard:{dependencies:{loadPolicySnapshot:()=>qf,getModes:()=>Vf,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),ne=V.expectBlocked?"blocked":"allowed",ce=ee.decision.kind==="deny"?"blocked":"allowed";return{command:V.command,description:V.description,expected:ne,actual:ce,passed:ne===ce,reason:ee.decision.kind==="deny"?ee.decision.reason:void 0,ruleId:ee.decision.kind==="deny"?ee.decision.ruleId:void 0}});return{passed:q.filter((V)=>V.passed).length,failed:q.filter((V)=>!V.passed).length,total:q.length,results:q}}function ni(P){let A=xt({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Ut(A.errors))return null;return{json:A.flags.json,skipUpdateCheck:A.flags.skipUpdateCheck}}async function Ll(P,A={}){let q=await An(!A.json,()=>{let V=zf(P,A);return{ready:V,finish:()=>V}},()=>Dn(),{loadingMessage:"Checking system status…"});if(A.json)console.log(JSON.stringify(q,null,2));else Jf(q);return q.engineSelfTest.failed>0||q.findings.some((V)=>V.severity==="error")?1:0}async function zf(P,A){let q=A.cwd??process.cwd(),V=await pr((at)=>kn({environment:P,cwd:q,openCodeVersion:at}).status!=="n/a",void 0,q),ee=xn(P,q,{ampPluginListOutput:V.ampPluginListOutput,codexPluginListOutput:V.codexPluginListOutput,copilotCliVersion:V.versions["copilot-cli"],openCodeVersion:V.versions.opencode,openCodePluginListOutput:V.openCodePluginListOutput}),ne=us(P,q),ce=ps(P),he=E(P,{cwd:q}),ye=he.policy,be=R(ye,P.env),xe=H(ye,be.capabilities),Ie=ar(P,7),Xe=qs(P,q),et=A.skipUpdateCheck?{currentVersion:wt(),latestVersion:null,updateAvailable:!1}:await Wt(),Re={hooks:ee,engineSelfTest:bl(P),userConfig:ne.userConfig,projectConfig:ne.projectConfig,configState:Ne(he),effectiveRules:ne.effectiveRules,environment:ce,effectiveSafety:{selectedPreset:ye.safety.level??"standard",level:be.effectiveLevel,capabilities:be.capabilities,ruleOverrides:ye.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(xe).filter(([,at])=>at.source==="rule_override"&&at.override==="off"&&at.inheritedEnabled&&at.changesInherited).map(([at])=>at),ruleCounts:{stored:Object.keys(ye.destructiveCommandRuleOverrides).length,effective:Object.values(xe).filter((at)=>at.changesInherited).length},...he.policyScopes?{policyScopes:he.policyScopes}:{}},...Xe.length>0?{v2Leftovers:Xe}:{},posture:Rs(P,ne.userConfig.path),activity:Ie,update:et,system:V};return{...Re,findings:ms(Re)}}function Jf(P){console.log(),console.log(hs(P.hooks)),console.log(),console.log(ys(P.engineSelfTest)),console.log(),console.log(vs(P)),console.log(),console.log(bs(P.environment)),console.log(),console.log(Ls(P)),console.log(),console.log(ws(P.findings)),console.log(),console.log(ks(P.activity)),console.log(),console.log(Cs(P.system)),console.log(),console.log(xs(P.update)),console.log(Ss(P))}import{existsSync as Wf}from"node:fs";var Kf=/^[A-Za-z0-9_@%+=:,./-]+$/,wl="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function ri(P){let A=xt({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Ut(A.errors))return console.error(wl),console.error("Pass -- before a command that starts with dashes."),null;if(A.values.cwd!==void 0&&!Wf(A.values.cwd))return console.error(`Error: --cwd path does not exist: ${A.values.cwd}`),null;let q=A.positionals.length===1?A.positionals[0]:A.positionals.map((V)=>Kf.test(V)?V:`'${V.replaceAll("'","'\\''")}'`).join(" ");if(!q)return console.error("Error: No command provided"),console.error(wl),null;return{json:A.flags.json,cwd:A.values.cwd,command:q}}function kl(P){if(P)return{dh:"=",dv:"|",dtl:"+",dtr:"+",dbl:"+",dbr:"+",h:"-",v:"|",tl:"+",tr:"+",bl:"+",br:"+",sh:"="};return{dh:"═",dv:"║",dtl:"╔",dtr:"╗",dbl:"╚",dbr:"╝",h:"─",v:"│",tl:"┌",tr:"┐",bl:"└",br:"┘",sh:"━"}}function xl(P,A){let V=A-18;return[`${P.dtl}${P.dh.repeat(A)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(V)}${P.dv}`,`${P.dbl}${P.dh.repeat(A)}${P.dbr}`]}function oi(P){return JSON.stringify(P)}function Cl(P,A=0){return`[${P.map((V,ee)=>gs(V,ee,A)).join(",")}]`}function Bn(P,A,q=70){let V=P.split(" "),ee=[],ne="";for(let ce of V)if(ne&&ne.length+ce.length+1>q)ee.push(ne),ne=ce;else ne=ne?`${ne} ${ce}`:ce;if(ne)ee.push(ne);return ee.map((ce,he)=>he===0?ce:`${A}${ce}`)}function Sl(P,A,q){let V=[];switch(P.type){case"parse":return null;case"env-strip":return V.push(""),V.push(`STEP ${A} ${q.h} Strip environment variables`),V.push(` Removed: ${P.envVars.map((ee)=>`${ee}=<redacted>`).join(", ")}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"leading-tokens-stripped":return V.push(""),V.push(`STEP ${A} ${q.h} Strip wrappers`),V.push(` Removed: ${P.removed.join(", ")}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"shell-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Detect shell wrapper`),V.push(` Wrapper: ${P.wrapper} -c`),V.push(` Inner: ${P.innerCommand}`),{lines:V,incrementStep:!0};case"interpreter":{if(V.push(""),V.push(`STEP ${A} ${q.h} Detect interpreter`),V.push(` Interpreter: ${P.interpreter}`),V.push(` Code: ${P.codeArg}`),P.paranoidBlocked)V.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:V,incrementStep:!0}}case"busybox":return V.push(""),V.push(`STEP ${A} ${q.h} Busybox wrapper`),V.push(` Subcommand: ${P.subcommand}`),{lines:V,incrementStep:!0};case"transparent-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Transparent wrapper`),V.push(` Wrapper: ${P.wrapper}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(V.push(""),V.push(`STEP ${A} ${q.h} Match rules`),V.push(` Rule: ${P.rule}()`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}case"worktree-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Worktree relaxation`),V.push(` Mode: ${o.worktree.name}`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed local discard in linked worktree"),{lines:V,incrementStep:!0};case"temp-root-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Temp-root relaxation`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed git discard in a temp-root repository"),{lines:V,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return V.push(""),V.push(`STEP ${A} ${q.h} Fallback scan`),V.push(` Found: ${P.embeddedCommandFound}`),{lines:V,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(V.push(""),V.push(`STEP ${A} ${q.h} Custom rules`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return V.push(""),V.push(`STEP ${A} ${q.h} Dangerous text check`),V.push(` Token: ${P.token}`),V.push(" Result: MATCHED"),{lines:V,incrementStep:!0};return null}case"strict-unparseable":return V.push(""),V.push(`STEP ${A} ${q.h} Strict mode check`),V.push(` Command: ${P.rawCommand}`),V.push(" Result: ✗ UNPARSEABLE"),{lines:V,incrementStep:!0};case"segment-skipped":return null;case"error":return V.push(""),V.push(`ERROR: ${P.message}`),{lines:V,incrementStep:!1};default:return P}}function ii(P,A){let q=kl(A?.asciiOnly??!1),V=58,ee=[],ne=1;ee.push(...xl(q,58)),ee.push("");let ce=P.trace.steps.find((Re)=>Re.type==="error");if(ce&&ce.type==="error"){ee.push("ERROR"),ee.push(` ${ce.message}`),ee.push(""),ee.push("RESULT"),ee.push(` Status: ${P.result==="blocked"?dt.red("BLOCKED"):dt.green("ALLOWED")}`),ee.push(""),ee.push("CONFIG");let Re=P.configSource??"none";return ee.push(` Path: ${Re}`),ee.join(`
|
|
221
|
+
`)}let he=P.trace.steps.find((Re)=>Re.type==="parse");if(he&&he.type==="parse"){ee.push("INPUT"),ee.push(` ${he.input}`),ee.push(""),ee.push(`STEP ${ne} ${q.h} Split shell commands`),ne++;for(let Re=0;Re<he.segments.length;Re++){let at=he.segments[Re];if(at){let ct=Math.random();ee.push(` Segment ${Re+1}: ${Cl(at,ct)}`)}}}let ye=P.trace.segments,be=ye.length>1;for(let Re of ye){if(be){ee.push("");let ft="";if(he&&he.type==="parse"){let io=he.segments[Re.index];if(io)ft=io.join(" ")}let mt=54,gt=ft,ut=` Segment ${Re.index+1}: `,vt=" ";if(ft){if(ut.length+ft.length+vt.length>mt){let Xd=mt-ut.length-vt.length;gt=`${ft.substring(0,Xd-1)}…`}}let At=ft?`${ut}${gt}${vt}`:` Segment ${Re.index+1} `,Kd=ft?`${ut}${dt.cyan(gt)}${vt}`:At,Hi=58-At.length,Ui=Math.floor(Hi/2),Yd=Hi-Ui;ee.push(`${q.sh.repeat(Ui)}${Kd}${q.sh.repeat(Yd)}`)}if(Re.steps.find((ft)=>ft.type==="segment-skipped")){ee.push(""),ee.push(" (skipped — prior segment blocked)");continue}let ct=!1,lt=!1;for(let ft of Re.steps){let mt=Sl(ft,ne,q);if(mt){if(lt=!0,ft.type==="recurse"){ee.push("");let gt=" RECURSING ",ut=58-gt.length-4;ee.push(` ${q.tl}${q.h}${gt}${q.h.repeat(ut)}`),ee.push(` ${q.v}`),ct=!0;continue}for(let gt of mt.lines)if(ct)ee.push(` ${q.v} ${gt}`);else ee.push(gt);if(mt.incrementStep)ne++}}if(ct)ee.push(` ${q.v}`),ee.push(` ${q.bl}${q.h.repeat(56)}`);if(!lt)ee.push(""),ee.push(` ${dt.green("✓")} Allowed (no matching rules)`)}if(ee.push(""),ee.push("RESULT"),P.result==="blocked"){if(ee.push(` Status: ${dt.red("BLOCKED")}`),P.customRule){if(ee.push(` Rule: ${P.customRule.id}`),P.customRule.rulebook)ee.push(` Rulebook: ${P.customRule.rulebook.name} ${P.customRule.rulebook.version}`);if(P.customRule.source)ee.push(` Source: ${P.customRule.source}`);if(P.customRule.override)ee.push(` Override: reason ${P.customRule.override.reason}`)}if(P.reason){let Re=Bn(P.reason," ");ee.push(` Reason: ${Re[0]}`);for(let at=1;at<Re.length;at++)ee.push(Re[at]??"")}}else ee.push(` Status: ${dt.green("ALLOWED")}`);ee.push(""),ee.push("CONFIG");let xe=P.configSource??"none",Ie=P.configValid?"":" (invalid)";ee.push(` Path: ${xe}${Ie}`);let Xe=P.safetyPresetScope;ee.push(` Safety preset: ${P.selectedPreset??"standard"}${Xe?` (${cr(Xe)})`:""}`),ee.push(` Effective capabilities: ${P.effectiveLevel}`);let et=Object.entries(P.destructiveCommandRuleOverrides??{});if(ee.push(` Rule customizations: ${et.length}`),P.ruleActivation)ee.push(` Rule activation: ${P.ruleActivation.id} — ${P.ruleActivation.enabled?"on":"off"} via ${P.ruleActivation.source}`);return ee.join(`
|
|
222
|
+
`)}function si(P){return JSON.stringify(P,null,2)}import{resolve as em}from"node:path";var Yf=["AKIA","ASIA","ghp_","gho_","ghu_","ghs_","ghr_","github_pat_","glpat-","xox","npm_","pypi-","rk_","sk-","sk_","gsk_","xai-","pplx-","bastn_","tgp_v1_","flp_","wfr_","fw_","fwp_","tp-","psk-"];function Rl(P){let A=0,q={allocateSegment(){return A++},getNextSegmentIndex(){return A},recordGlobal(V){P.record({kind:"step",scope:"global",step:V})},recordSegment(V,ee=q.currentSegmentIndex){if(ee===void 0)return;P.record({kind:"step",scope:"segment",segmentIndex:ee,step:V})}};return q}function Pl(P={}){let A=[],q=P.maxEvents??512,V={maxTextLength:P.maxTextLength??2048,maxListLength:P.maxListLength??128,maxObjectProperties:P.maxObjectProperties??P.maxListLength??128,maxDepth:P.maxDepth??16},ee,ne=new Set;return{record(ce){if(ee)return;if(!ce||A.length>=q)return;try{A.push(ci(Xf(ce,V,ne)))}catch{}},finish(){if(ee)return ee;return ee=ci({events:Object.freeze(A)}),ee}}}function Xf(P,A,q){if(P.kind!=="step")throw TypeError("invalid trace event");let{scope:V,step:ee}=P;Or(ee,q,A);let ne=ai(ee,A,q);if(V==="global")return{kind:"step",scope:"global",step:ne};if(V!=="segment")throw TypeError("invalid trace event scope");return{kind:"step",scope:"segment",segmentIndex:P.segmentIndex,step:ne}}function Or(P,A,q,V=0,ee=new WeakSet){if(typeof P==="string"){let he=P.slice(0,q.maxTextLength);if(!Ge(he))return;for(let ye of Ze(he))for(let be of ye.match(/[^\s"'()$]+/g)??[])A.add(El(be));return}if(!P||typeof P!=="object"||V>=q.maxDepth||ee.has(P))return;if(ee.add(P),Array.isArray(P)){let he=Math.min(P.length,q.maxListLength);for(let ye=0;ye<he;ye++)Or(P[ye],A,q,V+1,ee);return}let ne=0,ce=new Set;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ne>=q.maxObjectProperties)break;ne++,Or(he,A,q);let ye=li(he,q,A);if(ce.has(ye))continue;ce.add(ye),Or(P[he],A,q,V+1,ee)}}function ai(P,A,q,V=0,ee=new WeakSet){if(typeof P==="string")return li(P,A,q);if(!P||typeof P!=="object")return P;if(V>=A.maxDepth)return;if(ee.has(P))return;if(ee.add(P),Array.isArray(P)){let he=[],ye=Math.min(P.length,A.maxListLength);for(let be=0;be<ye;be++)he.push(ai(P[be],A,q,V+1,ee));return he}let ne={},ce=0;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ce>=A.maxObjectProperties)break;ce++;let ye=li(he,A,q);if(Object.hasOwn(ne,ye))continue;Object.defineProperty(ne,ye,{value:ai(P[he],A,q,V+1,ee),enumerable:!0,configurable:!0,writable:!0})}return ne}function li(P,A,q){let V=P.slice(0,A.maxTextLength),ee=Ge(V)?Ue(V):V,ne=q.size>0?Qf(ee,q):ee;return(Zf(ne)?ke(ne):ne).slice(0,A.maxTextLength)}function Zf(P){return P.includes("PRIVATE KEY")||P.includes("://")||P.includes("eyJ")||P.includes(":")&&/(?:authorization|cookie|x-api-key|api-key|(?:^|\s)(?:-u|--user)(?:\s|=))/i.test(P)||P.length>=14&&Yf.some((A)=>P.includes(A))||P.length>=49&&/\b[a-f0-9]{32}\.[A-Za-z0-9]{16}\b/.test(P)}function Qf(P,A){return P.replace(/[^\s"'()$]+/g,(q)=>A.has(El(q))?"<redacted>":q)}function El(P){let A=2166136261,q=2166136261;for(let V=0;V<P.length;V++)A=Math.imul(A^P.charCodeAt(V),16777619),q=Math.imul(q^P.charCodeAt(P.length-V-1),16777619);return`${A>>>0}:${q>>>0}:${P.length}`}function ci(P){if(P&&typeof P==="object"&&!Object.isFrozen(P)){for(let A of Object.values(P))ci(A);Object.freeze(P)}return P}function qn(P,A={},q){let V=em(A.cwd??process.cwd()),ee=A.policySnapshot??E(q,{cwd:V,userConfigDir:A.userConfigDir}),ne=R(ee.policy,q.env),ce=Fe({policySnapshot:ee,effectiveCapabilities:ne.capabilities,strict:ne.strict,paranoidRm:ne.paranoidRm,paranoidInterpreters:ne.paranoidInterpreters,worktreeMode:ne.worktreeMode}),he={effectiveLevel:ce.effectiveLevel,selectedPreset:ee.policy.safety.level??"standard",...ee.policyScopes?{safetyPresetScope:ee.policyScopes.levelScope}:{},effectiveCapabilities:ce.effectiveCapabilities,destructiveCommandRuleOverrides:ee.policy.destructiveCommandRuleOverrides},{configSource:ye,configValid:be}=nm(q,{cwd:V,userConfigDir:A.userConfigDir});if(!P||!P.trim())return{trace:{steps:[{type:"error",message:"No command provided"}],segments:[]},result:"allowed",configSource:ye,configValid:be,...he};let xe=h(P,"auto");if(xe.status==="limited")throw new y;let Ie=xe.dialect==="powershell"?h(P,"posix"):xe,Xe=st(Ie),et=Pl(),Re=Rl(et);Re.recordGlobal({type:"parse",input:P,segments:Xe.map((At)=>[...At])});let at=d("Bash",{command:P},{kind:"command",shell:"auto"},{configCwd:V,executionCwd:V},P),ct=B(at,{environment:q,trace:Re,dependencies:{loadPolicySnapshot:()=>ee}}),lt=ct.decision.kind==="deny"?ct.decision:null;if(lt&&(ct.stage==="policy-protection"||ct.stage==="secret-protection")){let At=tm(lt);return{trace:{steps:[],segments:[{index:0,steps:[{type:"rule-check",rule:At.rule,matched:!0,reason:lt.reason}]}]},result:"blocked",reason:k(lt.reason),segment:k(Dl(lt,P)),...At.ruleId?{ruleId:k(At.ruleId)}:{},configSource:ye,configValid:be,...he}}let ft=Re.getNextSegmentIndex();if(lt&&ft>0&&ft<Xe.length)Re.recordSegment({type:"segment-skipped",index:ft,reason:"prior-segment-blocked"},ft);let mt=et.finish(),gt=lt?.ruleId??rm(at,ee,ne,q),ut=G.find((At)=>At.id===gt&&At.activationCapability),vt=ut?ce.policy.effectiveDestructiveCommandRules[ut.id]:void 0;return{trace:im(mt),result:lt?"blocked":"allowed",reason:lt?k(lt.reason):void 0,segment:lt?k(Dl(lt,P)):void 0,ruleId:lt?.ruleId?k(lt.ruleId):void 0,customRule:om(sm(lt?.ruleId,ee)),configSource:ye,configValid:be,...he,...ut&&vt?{ruleActivation:{id:ut.id,...vt}}:{}}}function Dl(P,A){return P.evidence?.segment??A}function tm(P){if(P.reason===$e)return{ruleId:"policy-protection",rule:"policy-protection:findPolicyConfigMutationTargetInSemanticFacts"};if(P.reason===Me)return{ruleId:"policy-apply-protection",rule:"policy-apply-protection:findPolicyApplyInvocationInSemanticFacts"};if(P.reason===x)return{ruleId:"git-metadata-protection",rule:"git-metadata-protection:findGitMetadataMutationTargetInSemanticFacts"};return{ruleId:P.ruleId,rule:"secret-protection:findSensitiveTargetInSemanticFacts"}}function nm(P,A){let q=M(A.cwd),V=j(P,A),ee=J(P,{cwd:A.cwd,userConfigDir:A.userConfigDir});try{if(n(ee.projectConfigTarget)!==null){if(Jt(ee.projectConfigTarget).errors.length===0)return{configSource:q,configValid:!0};return{configSource:q,configValid:!1}}}catch(ne){if(ne instanceof r)return{configSource:q,configValid:!1};throw ne}try{if(n(ee.userConfigTarget)!==null){let ne=Jt(ee.userConfigTarget);return{configSource:V,configValid:ne.errors.length===0}}return{configSource:null,configValid:!0}}catch(ne){if(ne instanceof r)return{configSource:V,configValid:!1};throw ne}}function rm(P,A,q,V){let ee=A.policy,ne=Ee({...ee,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:{...ee.destructiveCommandRuleOverrides,...Object.fromEntries(G.flatMap((he)=>he.activationCapability?[[he.id,"on"]]:[]))}},A.state==="degraded"?{diagnostics:A.diagnostics,reason:A.reason}:void 0),ce=B(P,{environment:V,dependencies:{loadPolicySnapshot:()=>ne,getModes:()=>({...q,strict:!0,paranoidRm:!0,paranoidInterpreters:!0}),findSensitiveTarget:()=>null}});return ce.decision.kind==="deny"?ce.decision.ruleId:void 0}function om(P){if(!P)return;return{id:k(P.id),...P.rulebook?{rulebook:{name:k(P.rulebook.name),version:k(P.rulebook.version)}}:{},...P.source?{source:k(P.source)}:{},...P.override?{override:{type:"reason",reason:k(P.override.reason)}}:{}}}function im(P){let A=P.events.flatMap((V)=>V.kind==="step"&&V.scope==="global"?[V.step]:[]),q=new Map;for(let V of P.events){if(V.kind!=="step"||V.scope!=="segment")continue;let ee=q.get(V.segmentIndex)??{index:V.segmentIndex,steps:[]};ee.steps.push(V.step),q.set(V.segmentIndex,ee)}return{steps:A,segments:[...q.values()]}}function sm(P,A){let q=P?.replace(/^custom\./,"");if(!q||!A.policy.rules.some((V)=>V.name===q))return;return A.ruleMetadata[q]??Object.freeze({id:q})}function Al(P){return new Promise((A)=>{process.stdout.write(`${P}
|
|
223
|
+
`,()=>A())})}async function _l(P,A){let q=ri(A);if(!q)return 1;try{let V=qn(q.command,{cwd:q.cwd},P),ee=!!process.env.NO_COLOR||!process.stdout.isTTY;return await Al(q.json?si(V):ii(V,{asciiOnly:ee})),0}catch(V){let ee=am(V instanceof p?V.cause:V);if(ee===void 0)throw V;if(q.json)return await Al(JSON.stringify({error:ee})),1;return console.error(ee),1}}function am(P){if(P instanceof y)return P.message;if(P instanceof f)return P.message;if(P instanceof s&&a[P.kind].errorCode==="path-canonicalization-limit")return"Path canonicalization work limit exceeded.";return}var Tl="2.4.14",Ot=" ",sn="cc-safety-net";function Il(P){return P.argument?`${P.flags} ${P.argument}`:P.flags}function lm(P){return Math.max(...P.map((A)=>Il(A).length))}function cm(P){return Math.max(...P.map((A)=>A.usage.length))}function dm(P){return Math.max(...P.map((A)=>`${sn} ${A.usage}`.length))}function um(P,A){let q=`${sn} ${P.usage}`;return`${Ot}${q.padEnd(A+2)}${P.description}`}function qt(P,A){return`${Ot}${P.padEnd(Math.max(40,P.length+2))}${A}`}function Cn(P,A=console.log){let q=[];if(q.push(`${sn} ${P.name}`),q.push(""),q.push(`${Ot}${P.description}`),q.push(""),q.push("USAGE:"),q.push(`${Ot}${sn} ${P.usage}`),q.push(""),P.subcommands&&P.subcommands.length>0){q.push("SUBCOMMANDS:");let V=cm(P.subcommands);for(let ee of P.subcommands)q.push(`${Ot}${ee.usage.padEnd(V+2)}${ee.description}`);q.push("")}if(P.options.length>0){q.push("OPTIONS:");let V=lm(P.options);for(let ee of P.options){let ne=Il(ee),ce=ee.default?`${ee.description} (default: ${ee.default})`:ee.description;q.push(`${Ot}${ne.padEnd(V+2)}${ce}`)}q.push("")}if(P.examples&&P.examples.length>0){q.push("EXAMPLES:");for(let V of P.examples)q.push(`${Ot}${V}`)}A(q.join(`
|
|
224
|
+
`))}function di(){let P=dm(ir),A=[];A.push(`${sn} v${Tl}`),A.push(""),A.push("Blocks destructive commands and secret access."),A.push(""),A.push("COMMANDS:");for(let q of ir)A.push(um(q,P));A.push(""),A.push("GLOBAL OPTIONS:"),A.push(`${Ot}-h, --help Show help (use with command for command-specific help)`),A.push(`${Ot}-V, --version Show version`),A.push(""),A.push("HELP:"),A.push(`${Ot}${sn} help <command> Show help for a specific command`),A.push(`${Ot}${sn} <command> --help Show help for a specific command`),A.push(""),A.push("ENVIRONMENT VARIABLES:"),A.push(qt(`${o.level.name}=standard|strict|paranoid`,"Set session safety level")),A.push(qt(`${o.worktree.name}=1`,"Allow local git discards in linked worktrees")),A.push(qt(`${o.debug.name}=1`,"Print diagnostic messages to stderr")),A.push(qt(`${o.auditScope.name}=all|blocked`,"Record all command decisions, or denials only")),A.push(qt("CC_SAFETY_NET_HOME","Override rule config home directory")),A.push(""),A.push("LEGACY ENVIRONMENT VARIABLES (STILL SUPPORTED):"),A.push(qt(`${o.strict.name}=1`,"Force safety.overrides.fail_closed on")),A.push(qt(`${o.paranoid.name}=1`,"Force paranoid_rm and paranoid_interpreters on")),A.push(qt(`${o.paranoidRm.name}=1`,"Force safety.overrides.paranoid_rm on")),A.push(qt(`${o.paranoidInterpreters.name}=1`,"Force safety.overrides.paranoid_interpreters on")),A.push(""),A.push("Documentation: https://ccsafetynet.com/docs"),console.log(A.join(`
|
|
225
|
+
`))}function $l(){console.log(Tl)}function Vn(P,A=console.log){let q=sr(P);if(!q)return!1;if(q.name.toLowerCase()!==P.toLowerCase())return!1;return Cn(q,A),!0}import{existsSync as Br,readFileSync as Ac}from"node:fs";import{join as Gr}from"node:path";import*as Yt from"node:readline";function pm(P){return P==="install"?"Install":"Uninstall"}function fm(P){return P==="install"?"Installing":"Uninstalling"}function mm(P){return P==="install"?"into":"from"}function Nl(P){return P?.available===!0}function gm(P,A){let q=new Set(A);return P.filter((V)=>q.has(V.target)).map((V)=>V.target)}function Ol(P,A,q){if(P.every((V)=>!V.available))return A;return Array.from({length:P.length},(V,ee)=>ee+1).map((V)=>(A+V*q+P.length)%P.length).find((V)=>Nl(P[V]))}function hm(P,A,q){if(q.ctrl&&q.name==="c")return"interrupt";if(q.name==="escape"||A==="q")return"abort";if(P==="install"&&(A==="u"||A==="U"))return"update";if(q.name==="up"||A==="k")return"up";if(q.name==="down"||A==="j")return"down";if(q.name==="space"||A===" ")return"toggle";if(q.name==="return"||q.name==="enter")return"confirm";return null}function ym(P){return{cursor:P.findIndex((A)=>A.available),selected:[]}}function vm(P,A,q){if(q==="confirm"||q==="update"||q==="abort"||q==="interrupt")return{state:P,done:q};if(q==="up")return{state:{...P,cursor:Ol(A,P.cursor,-1)}};if(q==="down")return{state:{...P,cursor:Ol(A,P.cursor,1)}};let V=A[P.cursor];if(!Nl(V))return{state:P};let ee=P.selected.includes(V.target)?P.selected.filter((ne)=>ne!==V.target):gm(A,[...P.selected,V.target]);return{state:{...P,selected:ee}}}var jl="◉",Ml="◯",Hl=">",Ul=" ";function bm(P,A,q,V={}){let ee=V.color!==!1,ne=ee?dt.dim:(ye)=>ye,ce=ee?dt.green:(ye)=>ye,he=ee?dt.bold:(ye)=>ye;return["",`${pm(P)} CC Safety Net ${mm(P)}:`,"",...A.map((ye,be)=>{let xe=q.selected.includes(ye.target),Ie=be===q.cursor,Xe=xe?jl:Ml,et=Ie?Hl:Ul,Re=ye.available?"":` (${ye.unavailableReason??"not installed"})`,at=`${Xe} ${ye.label}${Re}`,ct=!ye.available?ne(at):xe?ce(at):Ie?he(at):at;return`${et} ${ct}`}),"",P==="install"?"Space: select Enter: confirm u: update installed Up/Down: move q/Esc: cancel":A.some((ye)=>ye.available)?"Space: select Enter: confirm Up/Down: move q/Esc: cancel":`No selectable integrations found for ${P}. q/Esc: close`].join(`
|
|
226
|
+
`)}var Fl=["global-hook","plugin"];function Lm(P,A,q={}){let V=q.color!==!1?dt.bold:(ne)=>ne;return["","Install the Kimi Code integration as:","",...[`Global hook — ${A?"already installed; selecting it reports the current state":"write the hook into ~/.kimi-code/config.toml now"}`,"Native Kimi plugin — print the steps to run inside Kimi Code"].map((ne,ce)=>{let he=ce===P,ye=`${he?jl:Ml} ${ne}`;return`${he?Hl:Ul} ${he?V(ye):ye}`}),"","Enter: confirm Up/Down: move q/Esc: cancel"].join(`
|
|
227
|
+
`)}function Gl(P){let{input:A,output:q}=P;Yt.emitKeypressEvents(A);let V=A.isRaw===!0;A.setRawMode(!0),A.resume();let ee=0,ne=()=>{if(ee===0)return;Yt.moveCursor(q,0,-ee),Yt.cursorTo(q,0),Yt.clearScreenDown(q)},ce=()=>{ne();let he=P.render();q.write(`${he}
|
|
219
228
|
`),ee=he.split(`
|
|
220
|
-
`).length};return new Promise((he)=>{let ye=(xe)=>{A.off("keypress",be),A.setRawMode(V),A.pause(),ne(),he(xe)};function be(xe,Ie){P.onKey(xe,Ie,{finish:ye,draw:ce})}A.on("keypress",be),ce()})}function
|
|
221
|
-
`);return}he.draw()}})}import{existsSync as
|
|
222
|
-
`)})}),ee.on("close",(ye)=>{clearTimeout(he),V({status:ce?null:ye,errorCode:ce?"ETIMEDOUT":void 0,stdout:ne.stdout,stderr:ne.stderr})})})};var
|
|
229
|
+
`).length};return new Promise((he)=>{let ye=(xe)=>{A.off("keypress",be),A.setRawMode(V),A.pause(),ne(),he(xe)};function be(xe,Ie){P.onKey(xe,Ie,{finish:ye,draw:ce})}A.on("keypress",be),ce()})}function Bl(P={}){let A=0;return Gl({input:P.input??process.stdin,output:P.output??process.stdout,render:()=>Lm(A,P.globalHookInstalled===!0),onKey:(q,V,ee)=>{if(V.ctrl&&V.name==="c"){ee.finish(null),(P.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(V.name==="escape"||q==="q")return ee.finish(null);if(V.name==="return"||V.name==="enter")return ee.finish(Fl[A]);if(V.name==="up"||V.name==="down"||q==="k"||q==="j")A=(A+1)%Fl.length,ee.draw()}})}function ui(P=process.stdin,A=process.stdout){return Boolean(P.isTTY&&A.isTTY&&typeof P.setRawMode==="function")}function ql(P,A,q={}){let V=q.output??process.stdout,ee=ym(A);return Gl({input:q.input??process.stdin,output:V,render:()=>bm(P,A,ee),onKey:(ne,ce,he)=>{let ye=hm(P,ne,ce);if(!ye)return;let be=vm(ee,A,ye);if(ee=be.state,be.done==="interrupt"){he.finish(null),(q.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(be.done==="abort")return he.finish(null);if(be.done==="update")return he.finish("update");if(be.done==="confirm"){if(ee.selected.length===0){V.write("\x07"),he.draw();return}he.finish([...ee.selected]),V.write(`${fm(P)} selected integrations...
|
|
230
|
+
`);return}he.draw()}})}import{existsSync as Vl,lstatSync as km,mkdirSync as xm,mkdtempSync as Cm,readdirSync as Sm,readFileSync as Rn,rmSync as Nr}from"node:fs";import{basename as Rm,dirname as Pm,join as Dt}from"node:path";import{fileURLToPath as Em}from"node:url";var pi="// cc-safety-net managed Amp plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --amp",an="cc-safety-net",ln="cc-safety-net/index.ts";import{spawn as wm}from"node:child_process";var fi=(P,A)=>{let q=Qt([...P],process.env);return new Promise((V)=>{let ee=wm(q.cmd,q.args,{cwd:A,stdio:["ignore","pipe","pipe"]}),ne=Uo(ee),ce=!1,he=setTimeout(()=>{ce=!0,ee.kill()},120000);ee.on("error",(ye)=>{clearTimeout(he),V({status:null,errorCode:ye.code,stdout:ne.stdout,stderr:[ye.message,ne.stderr].filter(Boolean).join(`
|
|
231
|
+
`)})}),ee.on("close",(ye)=>{clearTimeout(he),V({status:ce?null:ye,errorCode:ce?"ETIMEDOUT":void 0,stdout:ne.stdout,stderr:ne.stderr})})})};var Sn="cc-safety-net.ts",mi=Dt("amp",ln);function Dm(P){return Dt(P.home,".config","amp","plugins","cc-safety-net.ts")}function Am(){let P=Pm(Em(import.meta.url)),A=Dt(P,mi),q=Dt(P,"..",mi),V=Dt(P,"..","..","..","dist",mi);return[A,q,V]}function _m(P=Am()){let A=P.find((q)=>Vl(q)&&km(q).isFile());if(!A)throw Error("Packaged Amp plugin artifact not found. Reinstall cc-safety-net and try again.");return A}function zl(P){try{return JSON.parse(P)}catch{return}}function jr(P){return P.subarray(0,Buffer.byteLength(pi)).toString("utf-8")===pi}async function zn(P,A,q){let V=await P(A,q);if(V.status===0)return V;throw Error([`Failed to run ${A.join(" ")}${V.status===null?"":` (exit ${V.status})`}.`,[V.stdout,V.stderr].filter(Boolean).join(`
|
|
223
232
|
`).trim()].filter(Boolean).join(`
|
|
224
|
-
`))}async function
|
|
233
|
+
`))}async function Jl(P){let A=await P(["amp","plugins","repositories","--json"]);if(A.status===null)throw Error(`${A.errorCode==="ENOENT"?'Amp CLI not found. Install the amp CLI, sign in with "amp login", and rerun install --amp.':`amp plugins repositories --json did not finish (${A.errorCode??"terminated"}). Check that the amp CLI responds and rerun install --amp.`}
|
|
225
234
|
${A.stderr}`.trim());if(A.status!==0)throw Error(`Failed to run amp plugins repositories --json (exit ${A.status}). Sign in with "amp login" and rerun install --amp.
|
|
226
235
|
${[A.stdout,A.stderr].filter(Boolean).join(`
|
|
227
|
-
`)}`.trim());let q=
|
|
228
|
-
`}async function
|
|
229
|
-
`)}function
|
|
230
|
-
`,A+1);return q===-1?P.length:q+1}function
|
|
231
|
-
`)){if(/^\s*\[/.test(ne))return;let ce=V.exec(ne);if(ce){let he=ee+ce[0].lastIndexOf("[");return{start:he,end:
|
|
232
|
-
${ne}${q}${P.slice(A.end)}`}function
|
|
236
|
+
`)}`.trim());let q=zl(A.stdout),V=(Array.isArray(q)?q:[]).filter((ee)=>pt(ee,"scope")==="user"&&pt(ee,"exists")===!0&&pt(ee,"viewerCanWrite")===!0).map((ee)=>pt(ee,"cloneRef")).find((ee)=>typeof ee==="string"&&ee.length>0);if(!V)throw Error('Your Amp account has no writable Personal Plugins repository. Sign in with "amp login", open Amp once to create it, and rerun install --amp.');return V}async function Wl(P,A,q){let V=Cm(Dt(A.tmpdir,"cc-safety-net-amp-"));try{return await zn(P,["amp","clone","user-plugins",V]),await q(V)}finally{Nr(V,{recursive:!0,force:!0})}}function gi(P){return`rerun ${P==="overwrite"?"install":"uninstall"} --amp`}function Kl(P,A,q){let V=Dt(P,A),ee=Lt(V);if(!ee)return;if(ee.isSymbolicLink()||!ee.isFile())throw Error(`Refusing to ${q} ${A} in your Amp personal plugins repository: not a regular file. Remove it there and ${gi(q)}.`);let ne=Rn(V);if(jr(ne))return ne;throw Error(`Refusing to ${q} unmanaged file ${A} in your Amp personal plugins repository. Remove it there and ${gi(q)}.`)}function Yl(P,A){let q=Dt(P,an),V=Lt(q);if(!V)return;if(V.isSymbolicLink()||!V.isDirectory())throw Error(`Refusing to ${A} ${an} in your Amp personal plugins repository: not a regular directory. Remove it there and ${gi(A)}.`);return Kl(P,ln,A)}function Tm(P){let A=Dt(P,Sn),q=Lt(A);if(!q||q.isSymbolicLink()||!q.isFile())return;let V=Rn(A);return jr(V)?V:void 0}async function Xl(P,A,q,V){if(await zn(P,q,A),(await zn(P,["git","status","--porcelain"],A)).stdout.trim()==="")return!1;return await zn(P,["git","-c","commit.gpgsign=false","-c","user.name=cc-safety-net","-c","user.email=cc-safety-net@localhost","commit","-m",V],A),await zn(P,["git","push","origin","HEAD"],A),!0}function Fr(P,A){Im(P,A),$m(P,A)}function Zl(P,A){if(A==="keep")return;throw Error(`Local Amp plugin ${P} is not a managed copy and masks the personal plugin. Remove it and rerun install --amp.`)}function Im(P,A){let q=Dm(P),V=Lt(q);if(!V)return;if(!V.isSymbolicLink()&&V.isFile()&&jr(Rn(q))){Nr(q);return}Zl(q,A)}function $m(P,A){let q=Dt(P.home,".config","amp","plugins",an),V=Lt(q);if(!V)return;if(!V.isSymbolicLink()&&V.isDirectory()&&Om(q)){Nr(q,{recursive:!0});return}Zl(q,A)}function Om(P){let A=Rm(ln);if(Sm(P).join("\x00")!==A)return!1;let q=Dt(P,A),V=Lt(q);return!!V&&!V.isSymbolicLink()&&V.isFile()&&jr(Rn(q))}function Fm(P){let A=u(P);if(!Vl(A))return"";let q=zl(Rn(A,"utf-8"));if(!q||typeof q!=="object"||Array.isArray(q))return"";return`;globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__ = ${JSON.stringify(C(q,P.home))};
|
|
237
|
+
`}async function Ql(P,A=_m(),q=fi){let V=Buffer.concat([Rn(A),Buffer.from(Fm(P),"utf-8")]),ee=await Jl(q);return Wl(q,P,async(ne)=>{let ce=`${ee}/${an}`,he=Yl(ne,"overwrite"),ye=Kl(ne,Sn,"overwrite");if(he?.equals(V)&&!ye)return Fr(P,"fail"),{path:ce,alreadyInstalled:!0};if(xm(Dt(ne,an),{recursive:!0}),kt(Dt(ne,ln),V),ye)Nr(Dt(ne,Sn));let be=await Xl(q,ne,["git","add","--",ln,...ye?[Sn]:[]],`chore: update cc-safety-net plugin to v${wt()}`);return Fr(P,"fail"),{path:ce,alreadyInstalled:!be}})}async function ec(P,A=fi){let q=await Jl(A);return Wl(A,P,async(V)=>{let ee=Yl(V,"remove"),ne=Tm(V),ce=`${q}/${ne&&!ee?Sn:an}`;if(!ee&&!ne)return Fr(P,"keep"),{path:ce,alreadyInstalled:!1};return await Xl(A,V,["git","rm","--",...ee?[ln]:[],...ne?[Sn]:[]],`chore: remove cc-safety-net plugin v${wt()}`),Fr(P,"keep"),{path:ce,alreadyInstalled:!0}})}import{existsSync as tc,mkdirSync as Nm,readFileSync as jm}from"node:fs";import{dirname as Mm}from"node:path";var hi=jt["antigravity-cli"],cn="cc-safety-net";function dn(P){return Boolean(P)&&typeof P==="object"&&!Array.isArray(P)}function Hr(){return{PreToolUse:[{hooks:[{type:"command",command:hi,timeout:30}]}]}}function nc(P){try{let A=JSON.parse(jm(P,"utf-8"));if(!A||typeof A!=="object"||Array.isArray(A))throw Error("Antigravity hooks config must be a JSON object");return A}catch(A){if(A instanceof SyntaxError)throw Error(`Failed to parse Antigravity hooks config ${P}: ${A.message}`);throw A}}function rc(P){let A=P[cn];if(A===void 0){let V=Hr();return P[cn]=V,{definition:V,preToolUse:V.PreToolUse??[]}}if(!dn(A))throw Error(`Antigravity hooks config entry "${cn}" must be an object`);let q=Array.isArray(A.PreToolUse)?A.PreToolUse:[];return A.PreToolUse=q,{definition:A,preToolUse:q}}function oc(P){if(!Array.isArray(P.PreToolUse))return!1;return P.PreToolUse.some((A)=>dn(A)&&Array.isArray(A.hooks)&&A.hooks.some((q)=>dn(q)&&q.command===hi))}function Hm(P){return Object.values(P).some((A)=>dn(A)&&A.enabled!==!1&&oc(A))}function Um(P){if(P[cn]===void 0)return!1;let A=rc(P);if(A.definition.enabled!==!1||!oc(A.definition))return!1;return A.definition.enabled=!0,!0}function Gm(P){if(P[cn]===void 0){P[cn]=Hr();return}let A=rc(P);A.definition.enabled=!0,A.preToolUse.push(Hr().PreToolUse?.[0]??{hooks:[]})}function Bm(P){let A=!1;for(let q of Object.values(P)){if(!dn(q)||!Array.isArray(q.PreToolUse))continue;q.PreToolUse=q.PreToolUse.flatMap((V)=>{if(!dn(V)||!Array.isArray(V.hooks))return[V];let ee=V.hooks.filter((ne)=>!dn(ne)||ne.command!==hi);if(ee.length!==V.hooks.length)A=!0;return ee.length===0?[]:[{...V,hooks:ee}]})}return A}function Mr(P,A){kt(P,`${JSON.stringify(A,null,2)}
|
|
238
|
+
`)}function ic(P){let A=_n(P.home);if(Nm(Mm(A),{recursive:!0}),!tc(A))return Mr(A,{[cn]:Hr()}),{path:A,alreadyInstalled:!1};let q=nc(A);if(Hm(q))return{path:A,alreadyInstalled:!0};if(Um(q))return Mr(A,q),{path:A,alreadyInstalled:!1};return Gm(q),Mr(A,q),{path:A,alreadyInstalled:!1}}function sc(P){let A=_n(P.home);if(!tc(A))return{path:A,alreadyInstalled:!1};let q=nc(A);if(!Bm(q))return{path:A,alreadyInstalled:!1};return Mr(A,q),{path:A,alreadyInstalled:!0}}import{existsSync as qm,readdirSync as Vm,rmSync as zm}from"node:fs";import{join as Jm}from"node:path";function ac(P,A=process.platform,q){if(!qm(P))return;let V=A==="win32"?/^bunx-\d+-cc-safety-net@/:new RegExp(`^bunx-${process.getuid?.()??0}-cc-safety-net@`);Vm(P).filter((ee)=>ee!==q&&V.test(ee)).forEach((ee)=>{zm(Jm(P,ee),{recursive:!0,force:!0})})}import{spawn as Wm}from"node:child_process";var Ht=$t.map((P)=>({target:P.id,flag:P.flag,label:bt(P.id),probeCommand:P.probeCommand}));function yi(P){let A=new Set(P);return Ht.map((q)=>q.target).filter((q)=>A.has(q))}async function lc(P,A){for(let q of P)await A(q)}var Km=5000;function vi(P,A=Km){return new Promise((q)=>{let V=Qt([...P],process.env),ee=Wm(V.cmd,V.args,{env:process.env,stdio:"ignore"}),ne=!1,ce=(ye)=>{if(ne)return;ne=!0,clearTimeout(he),q(ye)},he=setTimeout(()=>{ee.kill(),ce(!1)},A);ee.on("error",()=>ce(!1)),ee.on("close",(ye)=>ce(ye===0))})}function cc(P=vi,A={}){let q=new Set(A.configuredTargets??[]);return Promise.all(Ht.map(async(V)=>({target:V.target,flag:V.flag,label:V.label,...uc(A.action,await P(V.probeCommand),q.has(V.target))})))}function dc(P,A){let q=new Set(A.configuredTargets??[]);return P.map((V)=>({...V,...uc(A.action,V.available,q.has(V.target))}))}function uc(P,A,q){if(P==="uninstall")return q?{available:!0}:{available:!1,unavailableReason:"not installed"};if(P==="install"&&q)return{available:!1,unavailableReason:"already installed"};if(!A)return{available:!1,unavailableReason:"CLI not installed"};return{available:!0}}import{existsSync as pc,readdirSync as Ym,rmSync as Xm}from"node:fs";import{join as Pn}from"node:path";function Ur(P,A=process.platform){let q=Pn(P.env.get("npm_config_cache")||(A==="win32"?Pn(P.env.get("LOCALAPPDATA")||Pn(P.home,"AppData","Local"),"npm-cache"):Pn(P.home,".npm")),"_npx");if(!pc(q))return;Ym(q).filter((V)=>pc(Pn(q,V,"node_modules","cc-safety-net"))).forEach((V)=>{Xm(Pn(q,V),{recursive:!0,force:!0})})}import{existsSync as vc,mkdirSync as Qm,readFileSync as bc}from"node:fs";import{dirname as eg,join as yc}from"node:path";function Zm(P,A){if(P[A]!=="#")return A;let q=P.indexOf(`
|
|
239
|
+
`,A+1);return q===-1?P.length:q+1}function bi(P,A,q){let V=new RegExp(`^(\\s*)${A}\\s*=\\s*\\[`),ee=0;for(let ne of P.split(`
|
|
240
|
+
`)){if(/^\s*\[/.test(ne))return;let ce=V.exec(ne);if(ce){let he=ee+ce[0].lastIndexOf("[");return{start:he,end:So(P,he,{skipComment:Zm,...q})}}ee+=ne.length+1}return}function fc(P,A,q){let V=P.slice(0,A.end).trimEnd(),ee=oa(P,A.end),ne=ee===""?" ":`${ee} `,ce=!V.endsWith("[")&&!V.endsWith(",");return`${V}${ce?",":""}
|
|
241
|
+
${ne}${q}${P.slice(A.end)}`}function mc(P,A,q){let V=P.indexOf(q,A.start);if(V===-1||V>A.end)return P;return Lr(P,{start:V,end:V+q.length})}function gc(P,A){let q=new RegExp(`^\\s*${A}\\s*=\\s*\\[\\s*]\\s*(?:#.*)?$`),V=P.split(`
|
|
233
242
|
`),ee=V.findIndex((he)=>/^\s*\[/.test(he)),ne=ee===-1?V:V.slice(0,ee),ce=ee===-1?[]:V.slice(ee);return[...ne.filter((he)=>!q.test(he)),...ce].join(`
|
|
234
|
-
`)}function
|
|
243
|
+
`)}function hc(P,A,q){let V=new RegExp(`^\\s*\\[\\[${A}]]\\s*$`,"m");return P.split(/(?=^\s*\[)/m).filter((ee)=>!V.test(ee)||!ee.includes(q)).join("").trimEnd()}var Jn=jt["kimi-code"],Li=`[[hooks]]
|
|
235
244
|
event = "PreToolUse"
|
|
236
|
-
command = "${
|
|
245
|
+
command = "${Jn}"`,Lc=`{ event = "PreToolUse", command = "${Jn}" }`,wc={stringError:"Unterminated string in Kimi Code config",bracketError:"Unmatched hooks array in Kimi Code config"};function kc(P){return yc(P.env.get("KIMI_CODE_HOME")??yc(P.home,".kimi-code"),"config.toml")}function tg(P){let A=bi(P,"hooks",wc);if(A&&P.slice(A.start+1,A.end).trim())return fc(P,A,Lc);let q=gc(P,"hooks").trimEnd();if(q==="")return`${Li}
|
|
237
246
|
`;return`${q}
|
|
238
247
|
|
|
239
|
-
${
|
|
240
|
-
`}function
|
|
241
|
-
`),{path:A,alreadyInstalled:!1};let q=
|
|
242
|
-
`;return kt(A,ee),{path:A,alreadyInstalled:!0}}var
|
|
243
|
-
`)},opencode:{installCommands:
|
|
244
|
-
`),`Enabled ${
|
|
245
|
-
`),`Enabled npm:cc-safety-net extensions in ${A}`}function
|
|
246
|
-
`)}async function
|
|
247
|
-
Removing the plugin files anyway; ${
|
|
248
|
-
`)}var
|
|
249
|
-
`);function
|
|
248
|
+
${Li}
|
|
249
|
+
`}function xc(P){let A=kc(P);if(Qm(eg(A),{recursive:!0}),!vc(A))return kt(A,`${Li}
|
|
250
|
+
`),{path:A,alreadyInstalled:!1};let q=bc(A,"utf-8");if(q.includes(Jn))return{path:A,alreadyInstalled:!0};return kt(A,tg(q)),{path:A,alreadyInstalled:!1}}function Cc(P){let A=kc(P);if(!vc(A))return{path:A,alreadyInstalled:!1};let q=bc(A,"utf-8");if(!q.includes(Jn))return{path:A,alreadyInstalled:!1};let V=bi(q,"hooks",wc),ee=V?mc(q,V,Lc):`${hc(q,"hooks",Jn)}
|
|
251
|
+
`;return kt(A,ee),{path:A,alreadyInstalled:!0}}var wi="safety-net@cc-marketplace",Sc=new Set(["claude-code","codex","copilot-cli","gemini-cli","hermes-agent","openclaw","opencode","pi"]),Rc=new Set(["antigravity-cli","cursor","grok-build","hermes-agent","kimi-code"]);function ki(P){return/^\s*safety-net@cc-marketplace[^a-z0-9-][^\n]*installed,/m.test(P??"")}function _c(P){return/^\s*cc-safety-net[^a-z0-9-][^\n]*installed,/m.test(P??"")}function ng(P){return/^Marketplace `cc-marketplace`\s*$/m.test(P??"")}var Tc={"claude-code":{installCommands:(P)=>{let A=br(P,"cc-safety-net@cc-marketplace");return{commands:[...A?[["claude","plugin","marketplace","update","cc-marketplace"],["claude","plugin","update","cc-safety-net@cc-marketplace"]]:[["claude","plugin","marketplace","add","kenryu42/cc-marketplace"],["claude","plugin","marketplace","update","cc-marketplace"],["claude","plugin","install","cc-safety-net@cc-marketplace"]],...xo(P).status==="disabled"?[["claude","plugin","enable","cc-safety-net@cc-marketplace"]]:[]],cleanupCommands:br(P,wi)?[["claude","plugin","uninstall",wi]]:[],update:A}},uninstallCommands:[["claude","plugin","uninstall","cc-safety-net@cc-marketplace"],["claude","plugin","marketplace","remove","cc-marketplace"]]},codex:{installCommands:async(P,A)=>{let q=A??await St(["codex","plugin","list"]),V=_c(q);return{commands:[V||ng(q)?["codex","plugin","marketplace","upgrade","cc-marketplace"]:["codex","plugin","marketplace","add","kenryu42/cc-marketplace"],["codex","plugin","add","cc-safety-net@cc-marketplace"]],cleanupCommands:ki(q)?[["codex","plugin","remove","safety-net@cc-marketplace"]]:[],update:V}},uninstallCommands:[["codex","plugin","remove","cc-safety-net@cc-marketplace"],["codex","plugin","marketplace","remove","cc-marketplace"]],postInstallMessage:ea},"copilot-cli":{installCommands:async()=>{let P=await St(["copilot","plugin","list"]),A=[...ua(P)?[["copilot","plugin","uninstall","copilot-safety-net"]]:[],...pa(P)?[["copilot","plugin","uninstall",la]]:[]];if(ca(P))return{commands:[["copilot","plugin","marketplace","update","cc-marketplace"],["copilot","plugin","update",Nt]],cleanupCommands:A,update:!0};return{commands:[da(await St(["copilot","plugin","marketplace","list"]))?["copilot","plugin","marketplace","update","cc-marketplace"]:["copilot","plugin","marketplace","add","kenryu42/cc-marketplace"],["copilot","plugin","install",Nt]],cleanupCommands:A}},uninstallCommands:[["copilot","plugin","uninstall","cc-safety-net@cc-marketplace"],["copilot","plugin","marketplace","remove","cc-marketplace"]]},"gemini-cli":{installCommands:(P)=>{let A=_o(P);if(A.status==="configured")return{commands:[["gemini","extensions","update","gemini-safety-net"]],update:!0};if(A.status==="disabled")return{commands:[["gemini","extensions","update","gemini-safety-net"],["gemini","extensions","enable","gemini-safety-net"]],update:!0};return{commands:[["gemini","extensions","install","https://github.com/kenryu42/gemini-safety-net","--consent"]]}},uninstallCommands:[["gemini","extensions","uninstall","gemini-safety-net"]]},openclaw:{beforeInstall:qo,installCommands:(P)=>{let A=!Br(Gr(Hn(P),Tt));return{commands:tl(),afterInstall:()=>nl(A)}},uninstallCommands:[["openclaw","plugins","uninstall",yt,"--force"]],postInstallMessage:["Restart the OpenClaw Gateway to apply the change.","If plugins.allow is set in openclaw.json, it must also list cc-safety-net."].join(`
|
|
252
|
+
`)},opencode:{installCommands:pl},pi:{installCommands:[["pi","install","npm:cc-safety-net"]],uninstallCommands:[["pi","uninstall","npm:cc-safety-net"]]}};function Ic(P,A=(q)=>q){try{let q=JSON.parse(A(Ac(P,"utf-8")));if(!q||typeof q!=="object"||Array.isArray(q))throw Error(`Settings file ${P} must be a JSON object`);return q}catch(q){if(q instanceof SyntaxError)throw Error(`Failed to parse ${P}: ${q.message}`);throw q}}function rg(P){let A=Gr(In(P),"settings.json");if(!Br(A))return;let q=Ic(A,_t),V=q.enabledPlugins;if(!V||typeof V!=="object"||Array.isArray(V))return;if(V[Nt]!==!1)return;let ee=Ac(A,"utf-8"),ne=ee.replace(new RegExp(`("${Nt}"\\s*:\\s*)false`),"$1true");return V[Nt]=!0,kt(A,ne!==ee?ne:`${JSON.stringify(q,null,2)}
|
|
253
|
+
`),`Enabled ${Nt} plugin in ${A}`}function og(P){let A=ei(P);if(!Br(A))return;let q=Ic(A);if(!Array.isArray(q.packages))return;let V=q.packages.find((ee)=>!!ee&&typeof ee==="object"&&!Array.isArray(ee)&&ti(ee.source)&&("extensions"in ee));if(!V)return;return delete V.extensions,kt(A,`${JSON.stringify(q,null,2)}
|
|
254
|
+
`),`Enabled npm:cc-safety-net extensions in ${A}`}function Pc(P,A){let q=xt({label:A,booleans:Object.fromEntries(Ht.map((ne)=>[ne.target,[ne.flag]]))},P),V=q.errors[0];if(V)throw Error(V);let ee=Ht.filter((ne)=>q.flags[ne.target]).map((ne)=>ne.target);if(ee.length!==1)throw Error(`Choose exactly one ${A} target: ${Ht.map((ne)=>ne.flag).join(", ")}`);return ee[0]}async function $c(P,A=hn){let[q,V,ee]=await Promise.all([A(["amp","plugins","list"],30000),A(["codex","plugin","list"],30000),A(["copilot","--binary-version"])]);return{codexPluginListOutput:V,hooks:xn(P,process.cwd(),{ampPluginListOutput:q,codexPluginListOutput:V,copilotCliVersion:ee})}}async function ig(P,A,q=hn){let V=await $c(P,q);return V.hooks.filter((ee)=>A==="install"?ee.configured:ee.detected||ee.inspectionStatus==="not-inspected").filter((ee)=>ee.platform!=="codex"||!ki(V.codexPluginListOutput)||_c(V.codexPluginListOutput)).map((ee)=>ee.platform)}function sg(P,A,q,V){if(q.length>0)return{finish:async()=>[Pc(q,A)]};if(!V.selectTargets&&!ui(V.input,V.output))return{finish:async()=>[Pc(q,A)]};let ee=V.detectConfiguredTargets??(()=>ig(P,A,V.fetchVersion)),ne=Promise.all([cc(V.probeTargets),ee()]);return{ready:ne,finish:async()=>{let[ce,he]=await ne,ye=dc(ce,{action:A,configuredTargets:he}),be=V.selectTargets?await V.selectTargets(A,Dc(A,ye)):await ql(A,Dc(A,ye),{input:V.input,output:V.output});if(be==="update")return be;if(!be||be.length===0)return null;return yi(be)}}}async function ag(P,A,q=!1,V){let ee=Tc[P];ee.beforeInstall?.(A);let ne=typeof ee.installCommands==="function"?await ee.installCommands(A,V):{commands:ee.installCommands};return await Go(ne.commands),await Ka(ne.cleanupCommands??[]),await ne.afterInstall?.(),[`${ne.update||q?"Updated":"Installed"} ${bt(P)} integration`,ee.postInstallMessage].filter(Boolean).join(`
|
|
255
|
+
`)}async function lg(P){let A=Tc[P];if(!A.uninstallCommands)throw Error(`${bt(P)} uninstall is not supported`);return await Go(A.uninstallCommands),`Uninstalled ${bt(P)} integration`}function cg(P){let A=hl(P);return A.alreadyInstalled?`Uninstalled OpenCode plugin from ${A.path}`:`OpenCode plugin not installed in ${A.path}`}var Oc={"antigravity-cli":{install:ic,uninstall:sc},cursor:{install:ka,uninstall:xa},"grok-build":{install:Ia,uninstall:$a},"kimi-code":{install:xc,uninstall:Cc}};function dg(P,A,q,V=!1){if(P==="install"&&!V)Ur(q);let ee=Oc[A][P](q),ne=bt(A),ce=P!=="install"?"Uninstalled":V?"Updated":"Installed";return P==="install"&&ee.alreadyInstalled?V?`${ne} hook up to date in ${ee.path}`:`${ne} hook already installed in ${ee.path}`:P==="uninstall"&&!ee.alreadyInstalled?`${ne} hook not installed in ${ee.path}`:`${ce} ${ne} hook ${P==="install"?"in":"from"} ${ee.path}`}var Fc={amp:{install:Ql,uninstall:ec,restartNote:'Amp personal plugins apply to every Amp session, including Orb threads. Restart Amp or run "plugins: reload" to apply the change.'},"hermes-agent":{install:Ma,uninstall:Ha,afterInstall:async(P)=>{let A=Mo(P);return await St(["hermes","plugins","enable",It,"--no-allow-tool-override"]),!A},beforeUninstall:async(P)=>{jo(P);try{await St(["hermes","plugins","disable",It])}catch(A){console.warn(`${A instanceof Error?A.message:String(A)}
|
|
256
|
+
Removing the plugin files anyway; ${It} may still be listed in the Hermes config.`)}},restartNote:"Restart Hermes to apply the change."}};async function ug(P,A,q,V=!1){let ee=Fc[A];if(P==="uninstall")await ee.beforeUninstall?.(q);let ne=P==="install"?await ee.install(q):await ee.uninstall(q),ce=P==="install"&&await ee.afterInstall?.(q),he=bt(A),ye=!ce&&(P==="install"&&ne.alreadyInstalled||P==="uninstall"&&!ne.alreadyInstalled);return[ye?P==="install"?`${he} plugin ${V?"up to date":"already installed"} at ${ne.path}`:`${he} plugin not installed at ${ne.path}`:`${P!=="install"?"Uninstalled":V?"Updated":"Installed"} ${he} plugin ${P==="install"?"at":"from"} ${ne.path}`,ye?void 0:ee.restartNote].filter(Boolean).join(`
|
|
257
|
+
`)}var pg={"copilot-cli":{afterInstall:rg},"hermes-agent":{beforeInstall:(P,A)=>{if(!A)Ur(P)}},openclaw:{beforeUninstall:qo},pi:{afterInstall:og}};function fg(P){return P in Oc}function mg(P){return P in Fc}var Ec=["Install CC Safety Net as a native Kimi Code plugin:",""," 1. Start Kimi Code and run: /plugins install https://github.com/kenryu42/cc-safety-net"," Confirm the trust prompt; it defaults to cancel."," 2. Run /reload, or start a new session.","","Note: Kimi Code hooks are fail-open. When the hook process cannot start, crashes, or times","out, Kimi Code allows the tool call."].join(`
|
|
258
|
+
`);function gg(P){if(Mn({environment:P,cwd:process.cwd()}).status!=="configured")return Ec;return[Ec,"",dt.red(["CAUTION: the global Kimi Code hook is installed and will run alongside the plugin.","After the plugin is active, remove it with: cc-safety-net uninstall --kimi-code"].join(`
|
|
250
259
|
`))].join(`
|
|
251
|
-
`)}function
|
|
252
|
-
`)}function
|
|
260
|
+
`)}function Dc(P,A){return A.map((q)=>P==="install"&&q.target==="kimi-code"&&q.unavailableReason==="already installed"?{...q,available:!0,unavailableReason:void 0,label:`${q.label} (global hook installed)`}:q)}function hg(P,A){if(P.selectKimiInstallMethod)return P.selectKimiInstallMethod();if(!ui(P.input,P.output))return Promise.resolve("global-hook");return Bl({input:P.input,output:P.output,globalHookInstalled:Mn({environment:A,cwd:process.cwd()}).status==="configured"})}async function Nc(P,A,q,V=!1,ee){let ne=pg[A];if(P==="install")ne?.beforeInstall?.(q,V);if(P==="uninstall")ne?.beforeUninstall?.(q);if(fg(A))return dg(P,A,q,V);if(mg(A))return ug(P,A,q,V);if(P==="uninstall")return A==="opencode"?cg(q):lg(A);return[await ag(A,q,V,ee),await ne?.afterInstall?.(q)].filter(Boolean).join(`
|
|
261
|
+
`)}function yg(P){let A=xt({label:"update"},P).errors[0];if(A)throw Error(A)}async function vg(P,A=hn){let q=await $c(P,A),V=Gr(In(P),"installed-plugins");return{targets:yi([...q.hooks.filter((ne)=>ne.platform!=="copilot-cli"&&ne.detected).map((ne)=>ne.platform),...[wr,aa,sa].flatMap((ne)=>Br(Gr(V,...ne))?["copilot-cli"]:[]),...br(P,wi)?["claude-code"]:[],...ki(q.codexPluginListOutput)?["codex"]:[]]),codexPluginListOutput:q.codexPluginListOutput}}async function bg(P){let A=c(),q=P.output??process.stdout,V=(P.scriptPath??process.argv[1]??"").split(/[\\/]/),ee=V.find((et)=>/^bunx-\d+-/.test(et)),ne=ee!==void 0||V.includes("_npx")?null:(P.checkLatestVersion??Wt)(),ce=async()=>{let et=ne&&await ne;if(et?.updateAvailable)q.write(`
|
|
253
262
|
Update available: cc-safety-net ${et.currentVersion} → ${et.latestVersion}. Update this CLI with your package manager, e.g. \`npm i -g cc-safety-net@latest\` for a global install.
|
|
254
|
-
`)},he=
|
|
255
|
-
`)),await ce(),Xe.some((et)=>et.failed)?1:0}function
|
|
256
|
-
`),0;if(ee==="update")return(q.runUpdate??(()=>
|
|
257
|
-
`);return}if(ye==="plugin"){ne.write(`${
|
|
258
|
-
`);return}}let he=await
|
|
259
|
-
`)}),0}catch(V){return console.error(
|
|
263
|
+
`)},he=vg(A,P.fetchVersion??hn).then(async(et)=>{let Re=new Set(et.targets);return{targets:et.targets,codexPluginListOutput:et.codexPluginListOutput,available:new Map(await Promise.all(Ht.filter((at)=>Re.has(at.target)&&Sc.has(at.target)).map(async(at)=>[at.target,await vi(at.probeCommand)])))}}),ye=await An(P.showBanner??!0,()=>({ready:he,finish:()=>he}),()=>Dn({input:P.input??process.stdin,output:q}),{loadingMessage:"Checking installed integrations…",output:q}),be=await Promise.resolve().then(()=>(ac(A.tmpdir,process.platform,ee),null)).catch((et)=>Wn(et));if(ye.targets.length===0){if(q.write("No installed integrations found. Run `cc-safety-net install` to set one up.\n"),be!==null)console.error(be);return await ce(),be===null?0:1}let xe=ye.targets.some((et)=>Rc.has(et))?await Promise.resolve().then(()=>(Ur(A),null)).catch((et)=>Wn(et)):null,Ie=await gr(Promise.all(ye.targets.map((et)=>{if(Sc.has(et)&&!ye.available.get(et))return Promise.resolve({message:`${bt(et)} not found; skipped`,failed:!1});if(xe!==null&&Rc.has(et))return Promise.resolve({message:xe,failed:!0});return Nc("install",et,A,!0,ye.codexPluginListOutput).then((Re)=>({message:Re,failed:!1}),(Re)=>({message:Wn(Re),failed:!0}))})),{loadingMessage:`Updating ${ye.targets.length} integration${ye.targets.length===1?"":"s"}…`,output:q}),Xe=be===null?Ie:[...Ie,{message:be,failed:!0}];return Xe.forEach((et)=>et.failed?console.error(et.message):q.write(`${et.message}
|
|
264
|
+
`)),await ce(),Xe.some((et)=>et.failed)?1:0}function xi(P,A={}){return Promise.resolve().then(()=>yg(P)).then(()=>bg(A)).catch((q)=>(console.error(Wn(q)),1))}async function Kn(P,A,q={}){try{let V=c(),ee=await An(!0,()=>sg(V,P,A,q),()=>Dn({input:q.input??process.stdin,output:q.output??process.stdout}),{loadingMessage:P==="install"?"Checking available integrations…":"Checking installed integrations…",output:q.output??process.stdout});if(!ee)return(q.output??process.stdout).write(`Cancelled: nothing was ${P}ed.
|
|
265
|
+
`),0;if(ee==="update")return(q.runUpdate??(()=>xi([],{fetchVersion:q.fetchVersion,input:q.input,output:q.output,showBanner:!1})))();let ne=q.output??process.stdout;return await lc(ee,async(ce)=>{if(ce==="kimi-code"&&P==="install"){let ye=await hg(q,V);if(ye===null){ne.write(`Cancelled: Kimi Code integration was not installed.
|
|
266
|
+
`);return}if(ye==="plugin"){ne.write(`${gg(V)}
|
|
267
|
+
`);return}}let he=await gr(Nc(P,ce,V),{loadingMessage:`${P==="install"?"Installing":"Uninstalling"} ${bt(ce)} integration…`,output:ne});ne.write(`${he}
|
|
268
|
+
`)}),0}catch(V){return console.error(Wn(V)),1}}function Wn(P){let A=P instanceof Error?P.message:String(P),q=typeof P==="object"&&P!==null&&"code"in P?P.code:null;if(q==="EACCES"||q==="EPERM")return`${A}
|
|
260
269
|
Check file permissions for the target config file and parent directory.`;if(q==="ENOENT")return`${A}
|
|
261
270
|
Check that the target config path and parent directory exist.`;if(q==="ENOTDIR")return`${A}
|
|
262
|
-
Check that every parent path component is a directory.`;return A}import{mkdirSync as
|
|
263
|
-
`,384),eg(V,384),{path:V,policy:ne,errors:[]}}function Tc(P,A){let q=cn(A,P.home);if(q.errors.length>0)return{errors:q.errors};return{preview:Ae(q.policy,P.env),errors:[]}}function $c(P,A={}){let q=u(P,A);if(!Dc(q))return Vt(P,X,A);let V=Ac(q,"utf-8");if(!V.trim())return Vt(P,X,A);try{return Vt(P,C(JSON.parse(V),P.home),A)}catch{return Vt(P,X,A)}}var Ic=new Set(["check","apply"]),Oc="(unset)";async function jc(P,A,q={}){let V=xt({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},A),ee=V.positionals[0],ne=[...V.errors,...ee&&!Ic.has(ee)?[`Unknown policy subcommand: ${ee}`]:[],...ee&&Ic.has(ee)&&!V.positionals[1]?[`policy ${ee} requires a file`]:[],...V.positionals.slice(2).map((Re)=>`Unexpected policy argument: ${Re}`)];if(ne.length>0){for(let Re of ne)console.error(Re);return 1}let ce=V.positionals[1];if(!ee||!ce)return wn(Xn,console.error),1;let he=V.flags.global?u(P):b(q.cwd??process.cwd()),ye=dn(ce),be=[...ye.errors,...qn(ye.value,P.home).map((Re)=>`${ce}: ${Re}`),...!V.flags.global&&lg(ye.value)&&ye.value.audit!==void 0?[`${ce}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(be.length>0){for(let Re of be)console.error(Re);return 1}let xe=C(ye.value,P.home);if(console.log(`Scope: ${V.flags.global?"user":"project"} (${he})`),console.log(`Proposal: ${ce}`),V.flags.global)Fc(C(dn(he).value,P.home),xe,!0);if(!V.flags.global){let Re=Vn(P).baseline;console.log("Effective policy (user + project merged):"),Fc(Z(Re,ae(dn(he).value,P.home).policy).policy,Z(Re,ae(ye.value,P.home).policy).policy,!1)}if(ee==="check")return 0;let Ie=q.input??process.stdin,Xe=q.output??process.stdout;if(!Ie.isTTY||!Xe.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${ce}${V.flags.global?" --global":""}`),1;if(!await sg(`Apply this policy to ${he}? [y/N] `,Ie,Xe))return console.log("Cancelled; nothing was written."),0;return ag(P,he,ye.value,xe,V.flags.global),console.log(`Policy applied: ${he}`),0}function sg(P,A,q){let V=ig({input:A,output:q,terminal:!1});return new Promise((ee)=>{V.once("close",()=>ee(!1)),V.question(P,(ne)=>{ee(/^y(es)?$/i.test(ne.trim())),V.close()})})}function ag(P,A,q,V,ee){if(ee){Vt(P,V);return}rg(og(A),{recursive:!0}),Rt(A,Nr(q,V))}function Fc(P,A,q){let V=jr(P,A,q);if(V.length===0){console.log("No changes.");return}console.log(`Changes (${V.length}):`);for(let ee of V)console.log(` ${ee.field}: ${ee.before??Oc} -> ${ee.after??Oc}`)}function lg(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as wh}from"node:path";var Nc="# Custom Rules Reference\n\nAgent reference for generating CC Safety Net rulebook configuration.\n\n## Config Locations\n\n| Scope | Config path | Rulebook path | Priority |\n|-------|-------------|---------------|----------|\n| User | `~/.cc-safety-net/rules/rule.json` | `~/.cc-safety-net/rules/<rulebook-name>/rulebook.json` | First |\n| Project | `.cc-safety-net/rules/rule.json` | `.cc-safety-net/rules/<rulebook-name>/rulebook.json` | Second |\n| GitHub source | Listed in a local `rule.json` | Vendored into the consumer's `<rulebook-name>/rulebook.json` by `rule add` | Source order |\n\nEvery rulebook is a live file: the runtime reads it on each tool call, so an edit applies to the next command with no publishing step.\n\nUser scope is evaluated before project scope; within a scope, sources apply in `rules` array order. A duplicate active rulebook name keeps the first claim and ignores the later rulebook with a warning, so a user-scoped name shadows a project-scoped one.\n\nUse `cc-safety-net rule init` to create an inert local config. Use `--global` for user scope. Use `cc-safety-net rule init --example` to also create an inactive example rulebook. `CC_SAFETY_NET_HOME` overrides the `~/.cc-safety-net` user root.\n\nLegacy inline `.safety-net.json` and `~/.cc-safety-net/config.json` files are not loaded at runtime. Convert them with `cc-safety-net rule migrate`.\n\n## rule.json Schema\n\n```json\n{\n \"version\": 1,\n \"rules\": [\"project-rules\", \"owner/repo#main/team-rules\"],\n \"overrides\": {\n \"project-rules/block-docker-system-prune\": {\n \"reason\": \"Use targeted Docker cleanup commands.\"\n },\n \"team-rules/block-npm-global\": \"off\"\n },\n \"transparent_wrappers\": [\"rtk\"]\n}\n```\n\n- `version`: Required. Must be `1`.\n- `$schema`: Optional. `cc-safety-net rule verify` inserts it into a valid `rule.json` that lacks it.\n- `rules`: Optional array of rulebook source strings. Missing `rules` is treated as `[]`.\n- `overrides`: Optional object keyed by `<rulebook-name>/<rule-name>`.\n- `overrides` values are either `\"off\"` to disable a rule or an object with a required `reason` (replacement block reason) and an optional `intent` (one of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain`).\n- A project override cannot target a user-scoped rule: only that override is ignored, the user rule keeps its configured state, and `rule verify` reports the diagnostic as a failure.\n- `transparent_wrappers`: Optional array of command names that transparently execute a visible child command.\n- Transparent wrappers have no built-in defaults. Configure only wrappers you intentionally trust, such as `\"rtk\"`.\n- Use `cc-safety-net rule wrapper add rtk` to configure RTK without manually editing `rule.json`.\n\n## Rulebook Sources\n\n- Local sources are bare rulebook names such as `project-rules`; the rulebook file is `.cc-safety-net/rules/project-rules/rulebook.json`.\n- Run `cc-safety-net rule add owner/repo` to add every rulebook currently present on the repository's default branch.\n- Use `--only` to select one or more rulebooks while preserving their order: `cc-safety-net rule add owner/repo --only aws gcloud`.\n- Use `--ref` to select a branch, tag, or commit instead of the default branch: `cc-safety-net rule add owner/repo --ref v2 --only aws`.\n- GitHub sources are stored in canonical form as `owner/repo#ref/<rulebook-name>`. That form remains valid in `rule.json` and as direct CLI input.\n- GitHub refs may contain `/`-separated path segments, such as `feature/rulebook-v2`.\n- The GitHub source name, the repository directory name, and the rulebook `name` must match exactly.\n- Rulebook source strings must be unique in a config.\n\n## rulebook.json Schema\n\n```json\n{\n \"rulebook_version\": 1,\n \"name\": \"project-rules\",\n \"version\": \"1.0.0\",\n \"description\": \"Project-specific CC Safety Net rules.\",\n \"author\": \"project\",\n \"allowed_commands\": [\"docker\"],\n \"rules\": [\n {\n \"name\": \"block-docker-system-prune\",\n \"command\": \"docker\",\n \"subcommand\": \"system\",\n \"block_args\": [\"prune\"],\n \"reason\": \"Use targeted cleanup instead.\"\n }\n ],\n \"tests\": [\n {\n \"command\": \"docker system prune\",\n \"expect\": \"blocked\",\n \"rule\": \"block-docker-system-prune\"\n },\n {\n \"command\": \"docker ps\",\n \"expect\": \"allowed\"\n }\n ]\n}\n```\n\n### Rulebook Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `rulebook_version` | Yes | Must be `1` or `2` |\n| `name` | Yes | `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$` |\n| `version` | Yes | Non-empty string |\n| `description` | No | Free text; not type-checked at runtime |\n| `author` | No | Free text; not type-checked at runtime |\n| `allowed_commands` | Yes | Unique command names matching `^[a-zA-Z][a-zA-Z0-9_-]*$` |\n| `rules` | Yes | Array of rule objects |\n| `tests` | No | Array of fixtures |\n\n### Rule Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Unique within the rulebook (case-insensitive); same pattern as rulebook `name` |\n| `command` | Yes | Must be listed in `allowed_commands`; basename only, not path |\n| `subcommand` | No | Same pattern as `command`; omit to match any subcommand |\n| `intent` | No | One of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain` |\n| `block_args` | Yes | Non-empty array of non-empty strings |\n| `reason` | Yes | Non-empty string, max 256 chars |\n\n### Rule Fields (`rulebook_version` 2)\n\nVersion 2 replaces `subcommand` and `block_args` with an exact-token `match` object. Version 1 rulebooks keep their fields and their behavior; a client that does not support version 2 rejects the rulebook instead of applying broader version 1 semantics.\n\n```json\n{\n \"name\": \"block-terraform-apply-destroy\",\n \"command\": \"terraform\",\n \"match\": {\n \"command_path\": [\"apply\"],\n \"any_args\": [\"-destroy\", \"--destroy\"]\n },\n \"reason\": \"Review a destroy plan first with 'terraform plan -destroy'.\",\n \"intent\": \"use_alternative\"\n}\n```\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Same as version 1 |\n| `command` | Yes | Same as version 1 |\n| `match.command_path` | Yes | Non-empty array of non-empty command words |\n| `match.any_args` | No | Non-empty array of unique non-empty argument tokens |\n| `match.exclude_args` | No | Non-empty array of unique non-empty argument tokens |\n| `intent` | No | Same as version 1 |\n| `reason` | Yes | Same as version 1 |\n\n### Matching Behavior (`rulebook_version` 2)\n\n- **Command**: Normalized to lowercase basename, as in version 1.\n- **Command path**: After recognized global options and their values are skipped, the next command words must equal `command_path` exactly. AWS, gcloud, and Azure CLI value-taking global options are built in; Terraform's `-chdir=dir` is `=`-joined and is skipped with its own token.\n- **Unrecognized options**: A token starting with `-` that is not a recognized global option is skipped without consuming a value, so an unlisted value-taking option with a separate value (`--newflag value`) makes the rule miss. This fails open deliberately; document such gaps in the rulebook.\n- **`any_args`**: At least one listed token must appear literally among the arguments.\n- **`exclude_args`**: Any listed token appearing literally among the arguments prevents the match, which is how a safe preview such as `aws s3 rm --dryrun` stays allowed.\n- **No short-option expansion**: Arguments compare as exact tokens, so list every accepted spelling (`\"-destroy\"` and `\"--destroy\"`).\n- **Literal and case-sensitive**: No regex, glob, or substring matching. The first matching rule wins.\n- Release channels are separate rules: `gcloud beta compute instances delete` needs its own `command_path`.\n\n### Test Fixture Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `command` | Yes | Non-empty shell command string |\n| `expect` | Yes | `\"blocked\"` or `\"allowed\"` |\n| `rule` | Required for blocked fixtures | Rule name expected to block the command |\n\nFixtures are optional documentation of intended behavior. Version 1 fixtures are shape-validated only. Version 2 fixtures are evaluated against the rulebook's own rules when a source is fetched by `rule add` or `rule update`, and by `rule verify`; a failing fixture rejects that source before it is written. Loading a rulebook does not re-evaluate fixtures. CC Safety Net never executes fixture commands; they are analyzer inputs only.\n\n## Matching Behavior\n\nThe subcommand, argument, and option rules below describe `rulebook_version` 1 rules; version 2 rules match as described in Matching Behavior (`rulebook_version` 2). Execution order and transparent wrappers apply to both.\n\n- **Command**: Normalized to lowercase basename with any trailing `.exe` removed (`/usr/bin/git` → `git`).\n- **Subcommand**: The first command token after recognized Git and Docker global options and their values; `--` ends option parsing. An unrecognized option without `=` may consume the following token as its value.\n- **Arguments**: Each `block_args` value is compared literally against every command token, including expanded short options. The command is blocked if **any** item matches.\n- **Short options**: Expanded (`-Ap` matches `-A`).\n- **Long options**: Exact match (`--all-files` does not match `--all`).\n- **Execution order**: Built-in rules first, then custom rulebooks. Custom rules only add restrictions.\n- **Transparent wrappers**: A configured wrapper such as `rtk` lets `rtk git commit` be analyzed as `git commit` only when `git` is protected by built-in analyzers or active custom rules. `rtk -- git commit` is also supported.\n\n## Workflow\n\n1. Run `cc-safety-net rule init` or create `rule.json` manually.\n2. Optionally run `cc-safety-net rule init --example` to create an inactive example rulebook.\n3. Use `cc-safety-net rule wrapper add rtk` for trusted transparent wrappers.\n4. Run `cc-safety-net rule add <source>` after creating or choosing a rulebook source; add `--only <rulebook...>` or `--ref <ref>` for repository selection. The command adds the selected sources and syncs them.\n5. Edit a local rulebook whenever you like: the edit is enforced on the next command, so there is nothing to run afterwards.\n6. Run `cc-safety-net rule update [source]` to re-fetch remote sources and rewrite the vendored copies; the command prints what changed. A source with an ordinary update failure keeps its vendored copy while the other selected sources still update. Resource-limit failures remain fatal for the whole update.\n7. Run `cc-safety-net rule verify` to validate config, local rulebooks, and shareable GitHub-source rulebook directories in the current repository (it does not fetch remote content).\n8. Run `cc-safety-net rule list` to inspect active rulebooks and transparent wrappers.\n\nA missing or invalid rulebook file makes that source inactive, and an unreadable or invalid `rule.json` makes every source in its scope inactive. Inactive sources stop applying their rules while other custom rules and all built-in protections stay active. Fix the file named in the diagnostic, or run `cc-safety-net rule update` when a remote source has not been vendored yet. Run `cc-safety-net status` to see degraded sources.\n";function Mr(P,A){if(!P.ok){Bc(P);return}Uc(P,A)}function Hc(P,A,q){if(P.ok)console.log(q);if(!P.add){Mr(P,`Added rulebook source: ${A}`);return}if(!P.ok){Bc(P);return}if(P.add.added.length>0)console.log(`Added ${P.add.added.length} ${P.add.added.length===1?"rulebook":"rulebooks"} from ${P.add.source} at ${P.add.ref}:`),P.add.added.forEach((V)=>{console.log(` - ${V}`)});if(P.add.alreadyConfigured.length>0)console.log(`Rulebooks already configured from ${P.add.source} at ${P.add.ref}: ${P.add.alreadyConfigured.join(", ")}`);if(P.add.commits.length>0)console.log(`Vendored at ${P.add.commits.map((V)=>V.slice(0,7)).join(", ")}.`);Uc(P,"Rule config updated.")}function Uc(P,A){for(let q of P.changes??[])console.log(q);console.log(A),console.log(""),cg(P.entries)}function cg(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let A of P)console.log(` - ${A.name} ${A.version} (${dg(A.ruleCount)})`),console.log(` Source: ${A.spec}`)}function dg(P){return`${P} ${P===1?"rule":"rules"}`}function Gc(P){un("Active sources",P.rulebooks,(A)=>[`[${A.source}] ${A.name} ${A.version}`,` Source: ${A.spec}`]),un("Active rules",P.rules,(A)=>[`[${pg(P,A.name)}] ${A.name}`,...ug(A),` Reason: ${A.reason}`]),un("Disabled rules",Mc(P,"off"),(A)=>[A.key]),un("Reason overrides",Mc(P,"reason"),(A)=>[A.key,` Reason: ${A.value.reason}`]),un("Transparent wrappers",P.transparent_wrappers,(A)=>[A]),un("Issues",P.errors,(A)=>[A]),un("Warnings",P.warnings,(A)=>[A])}function un(P,A,q){if(A.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${A.length}):`);for(let V of A){let[ee,...ne]=q(V);console.log(` - ${ee}`);for(let ce of ne)console.log(` ${ce}`)}}function ug(P){if(!P.match)return[` Command: ${P.subcommand?`${P.command} ${P.subcommand}`:P.command}`,` Block args: ${P.block_args.join(", ")}`];return[` Command: ${[P.command,...P.match.command_path].join(" ")}`,...P.match.any_args?[` Any args: ${P.match.any_args.join(", ")}`]:[],...P.match.exclude_args?[` Exclude args: ${P.match.exclude_args.join(", ")}`]:[]]}function pg(P,A){return P.rulebooks.find((q)=>q.rules.includes(A))?.source??"project"}function Mc(P,A){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((q)=>{if(A==="off")return q[1]==="off";return!!q[1]&&typeof q[1]==="object"}).map(([q,V])=>({key:q,value:V}))}function Bc(P){for(let A of P.errors)console.error(A)}import{dirname as pd,join as Jr}from"node:path";import{join as xi,resolve as Cg}from"node:path";function bi(P){let A=m(P);if(A.errors.length>0)return{ok:!1,result:{ok:!1,errors:A.errors,entries:[]}};return{ok:!0,config:A.config??it}}function qc(P){Rt(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function Vc(P){Rt(P,{rulebook_version:1,name:"example-rules",version:"1.0.0",description:"Project-specific CC Safety Net rules.",author:"project",allowed_commands:["docker"],rules:[{name:"block-docker-system-prune",command:"docker",subcommand:"system",block_args:["prune"],reason:"Use targeted cleanup instead."}],tests:[{command:"docker system prune",expect:"blocked",rule:"block-docker-system-prune"}]})}import{dirname as Br}from"node:path";var fg="custom.";function Hr(P){if(P.rulebook_version!==2)return[];let A=P.rules.map((q)=>({name:q.name,command:q.command,block_args:[],match:q.match,reason:q.reason,intent:q.intent}));return(P.tests??[]).flatMap((q,V)=>{let ee=Li(h(q.command));if(ee.length===0)return[`tests[${V}]: could not parse fixture command: ${q.command}`];let ne=ee.reduce((ce,he)=>ce??_(he,A)?.id.slice(fg.length),void 0);if(q.expect==="blocked"){if(ne===q.rule)return[];let ce=ne?`"${ne}" matched first`:"no rule matched";return[`tests[${V}]: expected "${q.rule}" to block "${q.command}" but ${ce}`]}return ne?[`tests[${V}]: expected "${q.command}" to be allowed but "${ne}" matched`]:[]})}function Li(P){return P.nodes.flatMap((A)=>{if(A.kind==="group"||A.kind==="function")return Li(A.body);if(A.kind!=="command")return[];let q=ge(Q(A.dialect,A.words)).words.map(t);return[...q.length>0?[q]:[],...A.nested.flatMap((V)=>Li(V))]})}var Ur=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function Gr(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??Ur.maxRequests,maxResponseBytes:P.maxResponseBytes??Ur.maxResponseBytes}}function zt(P){return{controller:new AbortController,budget:Gr(),resolveUrl:P}}function zc(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function Jc(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function Wc(P,A){if(A>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=A,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=A}var Zc=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function Kc(P,A,q=v(Br(Br(A)),"rules policy"),V=zt()){if(S(P))return yg(P,V);return hg(P,A,q)}async function Xc(P,A,q,V,ee,ne){if(!S(P))return Kc(P,A,q,V);let ce=ee?null:mg(P,A,q);if(ce)return ce;if(!ee&&!ne)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return Kc(P,A,q,V)}function mg(P,A,q=v(Br(Br(A)),"rules policy")){let V=I(P),ee=O(A,V.name),ne=n(i(q,ee));if(ne===null)return null;let ce=se(wi(ne,`Invalid rulebook ${ee}.`));if(ce.name!==V.name)throw Error(`rulebook name "${ce.name}" in ${ee} must match "${V.name}"`);return{spec:P,rulebook:ce,content:ne}}async function Qc(P,A={}){if(!K(P))throw Error(`Invalid GitHub repository source: ${P}`);let[q,V]=P.split("/");if(!q||!V)throw Error(`Invalid GitHub repository source: ${P}`);if(A.ref!==void 0&&!oe(A.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${A.ref}`);let ee=A.operation??zt(),ne=A.ref??await gg(q,V,P,ee),ce=await td(q,V,ne,P,ee),he=await qr(`https://api.github.com/repos/${q}/${V}/git/trees/${ce}?recursive=1`,"tree",ee),ye=he.response;if(!ye.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${ye.status}`);let be=JSON.parse(he.content);if(!Array.isArray(be?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let xe=be.tree,Ie=[...new Set(xe.flatMap((Xe)=>{if(!Xe||typeof Xe!=="object")return[];let et=Xe;if(et.type!=="blob"||typeof et.path!=="string")return[];let Re=et.path.match(rt);return Re?.[1]?[Re[1]]:[]}))].sort();if(Ie.length===0)throw Error(`No rulebooks found in ${P} under ${de}/`);return{source:P,owner:q,repo:V,ref:ne,commit:ce,names:Ie}}async function gg(P,A,q,V){let ee=await qr(`https://api.github.com/repos/${P}/${A}`,"metadata",V),ne=ee.response;if(!ne.ok)throw Error(`Failed to inspect ${q}: GitHub returned ${ne.status}`);let he=JSON.parse(ee.content)?.default_branch;if(typeof he!=="string"||he==="")throw Error(`Failed to inspect ${q}: missing default branch`);if(!oe(he))throw Error(`GitHub returned an invalid default branch: ${he}`);return he}function hg(P,A,q){ot(P);let V=O(A,P),ee=n(i(q,V));if(ee===null)throw Error(`Rulebook source not found: ${P}`);let ne=ed(wi(ee,"Invalid local rulebook source."));if(ne.name!==P)throw Error(`rulebook name "${ne.name}" must match local source "${P}"`);return{spec:P,rulebook:ne,content:ee}}async function yg(P,A){let q=I(P),V=await td(q.owner,q.repo,q.ref,P,A),ee=await qr(`https://raw.githubusercontent.com/${q.owner}/${q.repo}/${V}/${q.path}`,"raw",A),ne=ee.response;if(!ne.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${ne.status}`);let ce=ee.content,he=ed(wi(ce,"Invalid GitHub rulebook response."));if(he.name!==q.name)throw Error(`rulebook name "${he.name}" must match GitHub source "${q.name}"`);return{spec:P,rulebook:he,content:ce}}function ed(P){let A=se(P),q=Hr(A);if(q.length>0)throw Error(q.join("; "));return A}function wi(P,A){try{return JSON.parse(P)}catch{throw Error(A)}}async function td(P,A,q,V,ee){let ne=await qr(`https://api.github.com/repos/${P}/${A}/commits/${encodeURIComponent(q)}`,"commit",ee),ce=ne.response;if(!ce.ok)throw Error(`Failed to resolve ${V}: GitHub returned ${ce.status}`);let he=JSON.parse(ne.content);if(typeof he?.sha!=="string"||he.sha==="")throw Error(`Failed to resolve commit for ${V}`);return he.sha}async function vg(P,A,q={}){if(q.signal?.aborted)throw q.signal.reason;let V=q.budget??Gr(),ee=new AbortController,ne=()=>ee.abort(q.signal?.reason);q.signal?.addEventListener("abort",ne,{once:!0});let ce=!1,he=setTimeout(()=>{if(ee.signal.aborted)return;ce=!0,ee.abort()},q.timeoutMs??Zc.timeoutMs);try{if(q.signal?.aborted)throw q.signal.reason;Jc(V);let ye=await fetch(P,{signal:ee.signal,redirect:"error"});if(!ye.ok)return nd(ye),{response:ye,content:""};return{response:ye,content:await bg(ye,A,V,()=>ee.abort())}}catch(ye){if(ce)throw Error("GitHub request timed out",{cause:ye});if(q.signal?.aborted)throw q.signal.reason;throw ye}finally{clearTimeout(he),q.signal?.removeEventListener("abort",ne)}}function qr(P,A,q){return vg(q.resolveUrl?.(P)??P,A,{budget:q.budget,signal:q.controller.signal})}async function bg(P,A,q=Gr(),V){let ee=Zc[`${A}Bytes`],ne=Number(P.headers.get("content-length"));if(Number.isFinite(ne)&&ne>ee)throw nd(P),Error(`GitHub ${A} response exceeds ${ee} bytes`);if(!P.body)return"";let ce=P.body.getReader(),he=[],ye=0;while(!0){let be=await ce.read();if(be.done)break;try{Wc(q,be.value.byteLength)}catch(xe){throw V?.(),Yc(ce),xe}if(ye+=be.value.byteLength,ye>ee)throw V?.(),Yc(ce),Error(`GitHub ${A} response exceeds ${ee} bytes`);he.push(Buffer.from(be.value))}return Buffer.concat(he,ye).toString("utf-8")}function nd(P){if(!P.body)return;rd(()=>P.body?.cancel())}function Yc(P){rd(()=>P.cancel())}function rd(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var Lg=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function od(P,A){let q=ad(P.rules,A);if(q.length>0)return{ok:!0,specs:q};return sd(P.rules,A)}function id(P,A){let q=ad(P,A);if(q.length>0)return{ok:!0,specs:q};let V=kg(P,A);if(V.length>0)return{ok:!0,specs:V};let ee=xg(P,A);if(!ee.ok)return ee;if(ee.specs.length>0)return{ok:!0,specs:ee.specs};return sd(P,A)}function sd(P,A){let q=P.filter((V)=>ki(V)?.name===A);if(q.length===1)return{ok:!0,specs:q};return wg(A,q)}function wg(P,A){return{ok:!1,result:{ok:!1,errors:A.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${A.join(", ")}`],entries:[]}}}function ad(P,A){return P.filter((q)=>q===A)}function kg(P,A){let q=A.match(Lg),V=q?.[1],ee=q?.[2],ne=q?.[3];if(!V||!ee||!ne||!oe(ne))return[];return ld(P,(ce)=>ce.owner===V&&ce.repo===ee&&ce.ref===ne)}function xg(P,A){if(!K(A))return{ok:!0,specs:[]};let[q,V]=A.split("/"),ee=ld(P,(ce)=>ce.owner===q&&ce.repo===V);if(new Set(ee.map((ce)=>ki(ce)?.ref).filter((ce)=>!!ce)).size<2)return{ok:!0,specs:ee};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${A}. Use an explicit ref:`,` cc-safety-net rule remove ${A}#<ref>`],entries:[]}}}function ki(P){try{return I(P)}catch{return null}}function ld(P,A){return P.filter((q)=>{let V=ki(q);return V?A(V):!1})}async function zr(P,A={}){let q=Ci(A);return Sg(P,q,await Vr(P,q,zt()))}function Sg(P,A,q){if(!q.ok)return q;let V=Pt(P,A),ee=[...new Set(U(V.configPath,V.filesystemScope))];if(ee.length===0)return q;return{ok:!1,errors:ee,entries:q.entries}}async function Vr(P,A,q,V={},ee=new Set,ne=new Set){try{let ce=Pt(P,A),he=bi(ce.configTarget);if(!he.ok)return he.result;let ye=he.config,be=A.only?od(ye,A.only):{ok:!0,specs:ye.rules};if(!be.ok)return be.result;let xe=new Set([...A.refresh?be.specs:[],...ee]),Ie=(ut)=>Xc(ut,ce.configDir,ce.filesystemScope,q,xe.has(ut),!A.refresh||xe.has(ut)),Xe=await Fg(ye.rules,A.refresh?(ut)=>Ie(ut).then((yt)=>({ok:!0,item:yt})).catch((yt)=>{if(zc(yt))throw yt;return{ok:!1,spec:ut,message:yt instanceof Error?yt.message:String(yt)}}):async(ut)=>({ok:!0,item:await Ie(ut)}),q),et=Xe.filter((ut)=>!ut.ok),Re=Xe.filter((ut)=>ut.ok).map((ut)=>ut.item),at=Re.flatMap((ut)=>Rg(ut,ye.rules)),ct=Re.flatMap((ut)=>Pg(ut,ne,ce)),lt=new Set([...at,...ct].map((ut)=>ut.spec)),pt=[...et,...at,...ct],mt=[],gt=Dg(mt,()=>Re.flatMap((ut)=>lt.has(ut.spec)||pt.length>0&&ne.has(ut.spec)?[]:Eg(ut,ce,V,mt)));return{ok:pt.length===0,errors:pt.map((ut)=>`Failed to update ${ut.spec}: ${ut.message}`),entries:Re.map(_g),changes:gt}}catch(ce){return Jn(ce)}}function Rg(P,A){if(!S(P.spec))return[];let q=Pe(P.spec),V=A.filter((ee)=>ee!==P.spec&&Pe(ee).toLowerCase()===q.toLowerCase());if(V.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${q}" is also claimed by ${V.join(", ")}; rename one of them`}]}function Pg(P,A,q){if(!A.has(P.spec)||!S(P.spec))return[];let V=O(q.configDir,P.rulebook.name),ee=n(i(q.filesystemScope,V));if(ee===null||ee===P.content)return[];return[{ok:!1,spec:P.spec,message:`${V} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function Eg(P,A,q,V){if(!S(P.spec))return[];let ee=O(A.configDir,P.rulebook.name),ne=i(A.filesystemScope,ee),ce=n(ne);if(ce===P.content)return[];return V?.push({target:ne,previous:ce}),g(ne,P.content,void 0,q._testAfterPolicyRename),Ag(P,ce)}function Dg(P,A){try{return A()}catch(q){for(let V of[...P].reverse()){if(V.previous===null){F(V.target);continue}g(V.target,V.previous)}throw q}}function Ag(P,A){if(A===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let q=me(A),V="problem"in q?null:q.rulebook,ee=new Map(V?.rules.map((ce)=>[ce.name,JSON.stringify(ce)])??[]),ne=new Set(P.rulebook.rules.map((ce)=>ce.name));return[`Updated ${P.spec} (${V?.version??"unreadable"} -> ${P.rulebook.version})`,...[...ne].filter((ce)=>!ee.has(ce)).map((ce)=>` + ${ce}`),...[...ee.keys()].filter((ce)=>!ne.has(ce)).map((ce)=>` - ${ce}`),...P.rulebook.rules.filter((ce)=>{let he=ee.get(ce.name);return he!==void 0&&he!==JSON.stringify(ce)}).map((ce)=>` ~ ${ce.name}`)]}function _g(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function cd(P,A,q={}){return Tg(P,A,Ng(q),zt())}async function Tg(P,A,q,V,ee={}){let ne=null,ce=!1;try{let he=Pt(P,q),ye=n(he.configTarget);ne={target:he.configTarget,content:ye};let be=bi(he.configTarget);if(!be.ok)return be.result;let xe=be.config,Ie=K(A);$g(A,q,Ie);let Xe=Ie?await Qc(A,{ref:q.ref,operation:V}):null,et=Xe?Ig(Xe,q.rulebooks):[],Re=Xe?et.map((mt)=>Og(xe.rules,Xe,mt)??`${A}#${Xe.ref}/${mt}`):[A],at=Re.filter((mt)=>!xe.rules.includes(mt)),ct=[...xe.rules,...at];if(ct.length>pe)return jg();if(ct.length!==xe.rules.length)ce=!0,Rt(he.configTarget,{version:1,rules:ct,overrides:xe.overrides??{},transparent_wrappers:xe.transparent_wrappers??[]},void 0,ee._testAfterPolicyRename);let lt=await Vr(P,q,V,ee,new Set(at),new Set(at));if(!lt.ok)zn(he.configTarget,ye);if(!lt.ok||!Xe)return lt;let pt=et.filter((mt,gt)=>at.includes(Re[gt]??""));return{...lt,add:{source:A,ref:Xe.ref,selected:et,added:pt,alreadyConfigured:et.filter((mt)=>!pt.includes(mt)),commits:at.length>0?[Xe.commit]:[]}}}catch(he){if(ce&&ne)try{zn(ne.target,ne.content)}catch(ye){return Jn(ye)}return Jn(he)}}function $g(P,A,q){if(!q&&A.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!q&&A.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(A.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let V=A.rulebooks?.filter((ee)=>!l.test(ee))??[];if(V.length>0)throw Error(`Invalid rulebook names: ${V.join(", ")}`)}function Ig(P,A){let q=A?[...new Set(A)]:P.names,V=q.filter((ee)=>!P.names.includes(ee));if(V.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${V.join(", ")}
|
|
264
|
-
Available rulebooks: ${P.names.join(", ")}`);return q}function
|
|
265
|
-
No config files found. Using built-in rules only.`),0;for(let Re of Ie)if(Re.inactive)
|
|
266
|
-
Added $schema to ${Re.scope.toLowerCase()} config.`);
|
|
267
|
-
${dt.red(Re)}`);if(et)if(et.result.errors.length>0)
|
|
271
|
+
Check that every parent path component is a directory.`;return A}import{mkdirSync as Sg}from"node:fs";import{dirname as Rg}from"node:path";import{createInterface as Pg}from"node:readline";import{existsSync as Mc,readFileSync as Lg}from"node:fs";function un(P,A){let q=Ye(P,A);return{policy:q.policy,errors:ie(qe(q.issues,Je,(V)=>V.kind==="custom")," "," ")}}function Yn(P,A){return un(P,A).errors}function jc(P,A){return{"safety.level":P.safety.level,...Ci("safety.overrides",P.safety.overrides),"workflow.worktree_mode":String(P.workflow.worktree_mode),"destructive_command_protection.enabled":String(P.destructive_command_protection.enabled),...Ci("destructive_command_protection.overrides",P.destructive_command_protection.overrides),"destructive_command_protection.allow_paths":Si(P.destructive_command_protection.allow_paths),"secret_protection.enabled":String(P.secret_protection.enabled),...Ci("secret_protection.overrides",P.secret_protection.overrides),"secret_protection.deny_paths":Si(P.secret_protection.deny_paths),"secret_protection.allow_paths":Si(P.secret_protection.allow_paths),...A?{"audit.retention_days":String(P.audit.retention_days)}:{}}}function qr(P,A,q){let V=jc(P,q),ee=jc(A,q);return[...new Set([...Object.keys(V),...Object.keys(ee)])].flatMap((ne)=>V[ne]===ee[ne]?[]:[{field:ne,before:V[ne],after:ee[ne]}])}function Xn(P,A){let q=u(P,A);if(!Mc(q))return{baseline:C(globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__,P.home),diagnostics:[]};let V=pn(q),ee=un(V.value,P.home);return{baseline:ee.policy,diagnostics:V.errors.length>0?V.errors:ee.errors}}function pn(P){if(!Mc(P))return{errors:[`${P}: file not found`]};try{return{value:JSON.parse(Lg(P,"utf-8")),errors:[]}}catch(A){let q=A instanceof Error?A.message:String(A);return{errors:[`${P}: ${A instanceof SyntaxError?`Invalid JSON: ${q}`:q}`]}}}function Vr(P,A){let q=wg(P)?P:{};return{version:A.version,...Object.fromEntries(["safety","workflow","destructive_command_protection","secret_protection"].filter((V)=>q[V]!==void 0).map((V)=>[V,q[V]]))}}function Ci(P,A){return Object.fromEntries(Object.entries(A).flatMap(([q,V])=>V===void 0?[]:[[`${P}.${q}`,String(V)]]))}function Si(P){return P.length===0?"(none)":P.join(", ")}function wg(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{chmodSync as kg,existsSync as Hc,mkdirSync as xg,readFileSync as Uc}from"node:fs";import{dirname as Cg}from"node:path";function Gc(P,A={}){let q=u(P,A);if(!Hc(q))return{path:q,exists:!1,raw:"",policy:W(),errors:[]};let V=Uc(q,"utf-8");if(!V.trim())return{path:q,exists:!0,raw:V,policy:W(),errors:["Config file is empty"]};try{let ee=un(JSON.parse(V),P.home);return{path:q,exists:!0,raw:V,policy:ee.policy,errors:ee.errors}}catch(ee){return{path:q,exists:!0,raw:V,policy:W(),errors:[`Invalid JSON: ${ee instanceof Error?ee.message:String(ee)}`]}}}function Vt(P,A,q={}){let V=u(P,q),ee=un(A,P.home);if(ee.errors.length>0)return{path:V,policy:W(),errors:ee.errors};let ne=ee.policy;return xg(Cg(V),{recursive:!0,mode:448}),g(re(V),`${JSON.stringify(ne,null,2)}
|
|
272
|
+
`,384),kg(V,384),{path:V,policy:ne,errors:[]}}function Bc(P,A){let q=un(A,P.home);if(q.errors.length>0)return{errors:q.errors};return{preview:Ae(q.policy,P.env),errors:[]}}function qc(P,A={}){let q=u(P,A);if(!Hc(q))return Vt(P,X,A);let V=Uc(q,"utf-8");if(!V.trim())return Vt(P,X,A);try{return Vt(P,C(JSON.parse(V),P.home),A)}catch{return Vt(P,X,A)}}var Vc=new Set(["check","apply"]),zc="(unset)";async function Wc(P,A,q={}){let V=xt({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},A),ee=V.positionals[0],ne=[...V.errors,...ee&&!Vc.has(ee)?[`Unknown policy subcommand: ${ee}`]:[],...ee&&Vc.has(ee)&&!V.positionals[1]?[`policy ${ee} requires a file`]:[],...V.positionals.slice(2).map((Re)=>`Unexpected policy argument: ${Re}`)];if(ne.length>0){for(let Re of ne)console.error(Re);return 1}let ce=V.positionals[1];if(!ee||!ce)return Cn(or,console.error),1;let he=V.flags.global?u(P):b(q.cwd??process.cwd()),ye=pn(ce),be=[...ye.errors,...Yn(ye.value,P.home).map((Re)=>`${ce}: ${Re}`),...!V.flags.global&&Ag(ye.value)&&ye.value.audit!==void 0?[`${ce}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(be.length>0){for(let Re of be)console.error(Re);return 1}let xe=C(ye.value,P.home);if(console.log(`Scope: ${V.flags.global?"user":"project"} (${he})`),console.log(`Proposal: ${ce}`),V.flags.global)Jc(C(pn(he).value,P.home),xe,!0);if(!V.flags.global){let Re=Xn(P).baseline;console.log("Effective policy (user + project merged):"),Jc(Z(Re,ae(pn(he).value,P.home).policy).policy,Z(Re,ae(ye.value,P.home).policy).policy,!1)}if(ee==="check")return 0;let Ie=q.input??process.stdin,Xe=q.output??process.stdout;if(!Ie.isTTY||!Xe.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${ce}${V.flags.global?" --global":""}`),1;if(!await Eg(`Apply this policy to ${he}? [y/N] `,Ie,Xe))return console.log("Cancelled; nothing was written."),0;return Dg(P,he,ye.value,xe,V.flags.global),console.log(`Policy applied: ${he}`),0}function Eg(P,A,q){let V=Pg({input:A,output:q,terminal:!1});return new Promise((ee)=>{V.once("close",()=>ee(!1)),V.question(P,(ne)=>{ee(/^y(es)?$/i.test(ne.trim())),V.close()})})}function Dg(P,A,q,V,ee){if(ee){Vt(P,V);return}Sg(Rg(A),{recursive:!0}),Pt(A,Vr(q,V))}function Jc(P,A,q){let V=qr(P,A,q);if(V.length===0){console.log("No changes.");return}console.log(`Changes (${V.length}):`);for(let ee of V)console.log(` ${ee.field}: ${ee.before??zc} -> ${ee.after??zc}`)}function Ag(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as Bh}from"node:path";var Kc="# Custom Rules Reference\n\nAgent reference for generating CC Safety Net rulebook configuration.\n\n## Config Locations\n\n| Scope | Config path | Rulebook path | Priority |\n|-------|-------------|---------------|----------|\n| User | `~/.cc-safety-net/rules/rule.json` | `~/.cc-safety-net/rules/<rulebook-name>/rulebook.json` | First |\n| Project | `.cc-safety-net/rules/rule.json` | `.cc-safety-net/rules/<rulebook-name>/rulebook.json` | Second |\n| GitHub source | Listed in a local `rule.json` | Vendored into the consumer's `<rulebook-name>/rulebook.json` by `rule add` | Source order |\n\nEvery rulebook is a live file: the runtime reads it on each tool call, so an edit applies to the next command with no publishing step.\n\nUser scope is evaluated before project scope; within a scope, sources apply in `rules` array order. A duplicate active rulebook name keeps the first claim and ignores the later rulebook with a warning, so a user-scoped name shadows a project-scoped one.\n\nUse `cc-safety-net rule init` to create an inert local config. Use `--global` for user scope. Use `cc-safety-net rule init --example` to also create an inactive example rulebook. `CC_SAFETY_NET_HOME` overrides the `~/.cc-safety-net` user root.\n\nLegacy inline `.safety-net.json` and `~/.cc-safety-net/config.json` files are not loaded at runtime. Convert them with `cc-safety-net rule migrate`.\n\n## rule.json Schema\n\n```json\n{\n \"version\": 1,\n \"rules\": [\"project-rules\", \"owner/repo#main/team-rules\"],\n \"overrides\": {\n \"project-rules/block-docker-system-prune\": {\n \"reason\": \"Use targeted Docker cleanup commands.\"\n },\n \"team-rules/block-npm-global\": \"off\"\n },\n \"transparent_wrappers\": [\"rtk\"]\n}\n```\n\n- `version`: Required. Must be `1`.\n- `$schema`: Optional. `cc-safety-net rule verify` inserts it into a valid `rule.json` that lacks it.\n- `rules`: Optional array of rulebook source strings. Missing `rules` is treated as `[]`.\n- `overrides`: Optional object keyed by `<rulebook-name>/<rule-name>`.\n- `overrides` values are either `\"off\"` to disable a rule or an object with a required `reason` (replacement block reason) and an optional `intent` (one of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain`).\n- A project override cannot target a user-scoped rule: only that override is ignored, the user rule keeps its configured state, and `rule verify` reports the diagnostic as a failure.\n- `transparent_wrappers`: Optional array of command names that transparently execute a visible child command.\n- Transparent wrappers have no built-in defaults. Configure only wrappers you intentionally trust, such as `\"rtk\"`.\n- Use `cc-safety-net rule wrapper add rtk` to configure RTK without manually editing `rule.json`.\n\n## Rulebook Sources\n\n- Local sources are bare rulebook names such as `project-rules`; the rulebook file is `.cc-safety-net/rules/project-rules/rulebook.json`.\n- Run `cc-safety-net rule add owner/repo` to add every rulebook currently present on the repository's default branch.\n- Use `--only` to select one or more rulebooks while preserving their order: `cc-safety-net rule add owner/repo --only aws gcloud`.\n- Use `--ref` to select a branch, tag, or commit instead of the default branch: `cc-safety-net rule add owner/repo --ref v2 --only aws`.\n- GitHub sources are stored in canonical form as `owner/repo#ref/<rulebook-name>`. That form remains valid in `rule.json` and as direct CLI input.\n- GitHub refs may contain `/`-separated path segments, such as `feature/rulebook-v2`.\n- The GitHub source name, the repository directory name, and the rulebook `name` must match exactly.\n- Rulebook source strings must be unique in a config.\n\n## rulebook.json Schema\n\n```json\n{\n \"rulebook_version\": 1,\n \"name\": \"project-rules\",\n \"version\": \"1.0.0\",\n \"description\": \"Project-specific CC Safety Net rules.\",\n \"author\": \"project\",\n \"allowed_commands\": [\"docker\"],\n \"rules\": [\n {\n \"name\": \"block-docker-system-prune\",\n \"command\": \"docker\",\n \"subcommand\": \"system\",\n \"block_args\": [\"prune\"],\n \"reason\": \"Use targeted cleanup instead.\"\n }\n ],\n \"tests\": [\n {\n \"command\": \"docker system prune\",\n \"expect\": \"blocked\",\n \"rule\": \"block-docker-system-prune\"\n },\n {\n \"command\": \"docker ps\",\n \"expect\": \"allowed\"\n }\n ]\n}\n```\n\n### Rulebook Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `rulebook_version` | Yes | Must be `1` or `2` |\n| `name` | Yes | `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$` |\n| `version` | Yes | Non-empty string |\n| `description` | No | Free text; not type-checked at runtime |\n| `author` | No | Free text; not type-checked at runtime |\n| `allowed_commands` | Yes | Unique command names matching `^[a-zA-Z][a-zA-Z0-9_-]*$` |\n| `rules` | Yes | Array of rule objects |\n| `tests` | No | Array of fixtures |\n\n### Rule Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Unique within the rulebook (case-insensitive); same pattern as rulebook `name` |\n| `command` | Yes | Must be listed in `allowed_commands`; basename only, not path |\n| `subcommand` | No | Same pattern as `command`; omit to match any subcommand |\n| `intent` | No | One of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain` |\n| `block_args` | Yes | Non-empty array of non-empty strings |\n| `reason` | Yes | Non-empty string, max 256 chars |\n\n### Rule Fields (`rulebook_version` 2)\n\nVersion 2 replaces `subcommand` and `block_args` with an exact-token `match` object. Version 1 rulebooks keep their fields and their behavior; a client that does not support version 2 rejects the rulebook instead of applying broader version 1 semantics.\n\n```json\n{\n \"name\": \"block-terraform-apply-destroy\",\n \"command\": \"terraform\",\n \"match\": {\n \"command_path\": [\"apply\"],\n \"any_args\": [\"-destroy\", \"--destroy\"]\n },\n \"reason\": \"Review a destroy plan first with 'terraform plan -destroy'.\",\n \"intent\": \"use_alternative\"\n}\n```\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Same as version 1 |\n| `command` | Yes | Same as version 1 |\n| `match.command_path` | Yes | Non-empty array of non-empty command words |\n| `match.any_args` | No | Non-empty array of unique non-empty argument tokens |\n| `match.exclude_args` | No | Non-empty array of unique non-empty argument tokens |\n| `intent` | No | Same as version 1 |\n| `reason` | Yes | Same as version 1 |\n\n### Matching Behavior (`rulebook_version` 2)\n\n- **Command**: Normalized to lowercase basename, as in version 1.\n- **Command path**: After recognized global options and their values are skipped, the next command words must equal `command_path` exactly. AWS, gcloud, and Azure CLI value-taking global options are built in; Terraform's `-chdir=dir` is `=`-joined and is skipped with its own token.\n- **Unrecognized options**: A token starting with `-` that is not a recognized global option is skipped without consuming a value, so an unlisted value-taking option with a separate value (`--newflag value`) makes the rule miss. This fails open deliberately; document such gaps in the rulebook.\n- **`any_args`**: At least one listed token must appear literally among the arguments.\n- **`exclude_args`**: Any listed token appearing literally among the arguments prevents the match, which is how a safe preview such as `aws s3 rm --dryrun` stays allowed.\n- **No short-option expansion**: Arguments compare as exact tokens, so list every accepted spelling (`\"-destroy\"` and `\"--destroy\"`).\n- **Literal and case-sensitive**: No regex, glob, or substring matching. The first matching rule wins.\n- Release channels are separate rules: `gcloud beta compute instances delete` needs its own `command_path`.\n\n### Test Fixture Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `command` | Yes | Non-empty shell command string |\n| `expect` | Yes | `\"blocked\"` or `\"allowed\"` |\n| `rule` | Required for blocked fixtures | Rule name expected to block the command |\n\nFixtures are optional documentation of intended behavior. Version 1 fixtures are shape-validated only. Version 2 fixtures are evaluated against the rulebook's own rules when a source is fetched by `rule add` or `rule update`, and by `rule verify`; a failing fixture rejects that source before it is written. Loading a rulebook does not re-evaluate fixtures. CC Safety Net never executes fixture commands; they are analyzer inputs only.\n\n## Matching Behavior\n\nThe subcommand, argument, and option rules below describe `rulebook_version` 1 rules; version 2 rules match as described in Matching Behavior (`rulebook_version` 2). Execution order and transparent wrappers apply to both.\n\n- **Command**: Normalized to lowercase basename with any trailing `.exe` removed (`/usr/bin/git` → `git`).\n- **Subcommand**: The first command token after recognized Git and Docker global options and their values; `--` ends option parsing. An unrecognized option without `=` may consume the following token as its value.\n- **Arguments**: Each `block_args` value is compared literally against every command token, including expanded short options. The command is blocked if **any** item matches.\n- **Short options**: Expanded (`-Ap` matches `-A`).\n- **Long options**: Exact match (`--all-files` does not match `--all`).\n- **Execution order**: Built-in rules first, then custom rulebooks. Custom rules only add restrictions.\n- **Transparent wrappers**: A configured wrapper such as `rtk` lets `rtk git commit` be analyzed as `git commit` only when `git` is protected by built-in analyzers or active custom rules. `rtk -- git commit` is also supported.\n\n## Workflow\n\n1. Run `cc-safety-net rule init` or create `rule.json` manually.\n2. Optionally run `cc-safety-net rule init --example` to create an inactive example rulebook.\n3. Use `cc-safety-net rule wrapper add rtk` for trusted transparent wrappers.\n4. Run `cc-safety-net rule add <source>` after creating or choosing a rulebook source; add `--only <rulebook...>` or `--ref <ref>` for repository selection. The command adds the selected sources and syncs them.\n5. Edit a local rulebook whenever you like: the edit is enforced on the next command, so there is nothing to run afterwards.\n6. Run `cc-safety-net rule update [source]` to re-fetch remote sources and rewrite the vendored copies; the command prints what changed. A source with an ordinary update failure keeps its vendored copy while the other selected sources still update. Resource-limit failures remain fatal for the whole update.\n7. Run `cc-safety-net rule verify` to validate config, local rulebooks, and shareable GitHub-source rulebook directories in the current repository (it does not fetch remote content).\n8. Run `cc-safety-net rule list` to inspect active rulebooks and transparent wrappers.\n\nA missing or invalid rulebook file makes that source inactive, and an unreadable or invalid `rule.json` makes every source in its scope inactive. Inactive sources stop applying their rules while other custom rules and all built-in protections stay active. Fix the file named in the diagnostic, or run `cc-safety-net rule update` when a remote source has not been vendored yet. Run `cc-safety-net status` to see degraded sources.\n";function zr(P,A){if(!P.ok){ed(P);return}Zc(P,A)}function Xc(P,A,q){if(P.ok)console.log(q);if(!P.add){zr(P,`Added rulebook source: ${A}`);return}if(!P.ok){ed(P);return}if(P.add.added.length>0)console.log(`Added ${P.add.added.length} ${P.add.added.length===1?"rulebook":"rulebooks"} from ${P.add.source} at ${P.add.ref}:`),P.add.added.forEach((V)=>{console.log(` - ${V}`)});if(P.add.alreadyConfigured.length>0)console.log(`Rulebooks already configured from ${P.add.source} at ${P.add.ref}: ${P.add.alreadyConfigured.join(", ")}`);if(P.add.commits.length>0)console.log(`Vendored at ${P.add.commits.map((V)=>V.slice(0,7)).join(", ")}.`);Zc(P,"Rule config updated.")}function Zc(P,A){for(let q of P.changes??[])console.log(q);console.log(A),console.log(""),_g(P.entries)}function _g(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let A of P)console.log(` - ${A.name} ${A.version} (${Tg(A.ruleCount)})`),console.log(` Source: ${A.spec}`)}function Tg(P){return`${P} ${P===1?"rule":"rules"}`}function Qc(P){fn("Active sources",P.rulebooks,(A)=>[`[${A.source}] ${A.name} ${A.version}`,` Source: ${A.spec}`]),fn("Active rules",P.rules,(A)=>[`[${$g(P,A.name)}] ${A.name}`,...Ig(A),` Reason: ${A.reason}`]),fn("Disabled rules",Yc(P,"off"),(A)=>[A.key]),fn("Reason overrides",Yc(P,"reason"),(A)=>[A.key,` Reason: ${A.value.reason}`]),fn("Transparent wrappers",P.transparent_wrappers,(A)=>[A]),fn("Issues",P.errors,(A)=>[A]),fn("Warnings",P.warnings,(A)=>[A])}function fn(P,A,q){if(A.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${A.length}):`);for(let V of A){let[ee,...ne]=q(V);console.log(` - ${ee}`);for(let ce of ne)console.log(` ${ce}`)}}function Ig(P){if(!P.match)return[` Command: ${P.subcommand?`${P.command} ${P.subcommand}`:P.command}`,` Block args: ${P.block_args.join(", ")}`];return[` Command: ${[P.command,...P.match.command_path].join(" ")}`,...P.match.any_args?[` Any args: ${P.match.any_args.join(", ")}`]:[],...P.match.exclude_args?[` Exclude args: ${P.match.exclude_args.join(", ")}`]:[]]}function $g(P,A){return P.rulebooks.find((q)=>q.rules.includes(A))?.source??"project"}function Yc(P,A){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((q)=>{if(A==="off")return q[1]==="off";return!!q[1]&&typeof q[1]==="object"}).map(([q,V])=>({key:q,value:V}))}function ed(P){for(let A of P.errors)console.error(A)}import{dirname as xd,join as eo}from"node:path";import{join as Ai,resolve as zg}from"node:path";function Ri(P){let A=m(P);if(A.errors.length>0)return{ok:!1,result:{ok:!1,errors:A.errors,entries:[]}};return{ok:!0,config:A.config??it}}function td(P){Pt(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function nd(P){Pt(P,{rulebook_version:1,name:"example-rules",version:"1.0.0",description:"Project-specific CC Safety Net rules.",author:"project",allowed_commands:["docker"],rules:[{name:"block-docker-system-prune",command:"docker",subcommand:"system",block_args:["prune"],reason:"Use targeted cleanup instead."}],tests:[{command:"docker system prune",expect:"blocked",rule:"block-docker-system-prune"}]})}import{dirname as Yr}from"node:path";var Og="custom.";function Jr(P){if(P.rulebook_version!==2)return[];let A=P.rules.map((q)=>({name:q.name,command:q.command,block_args:[],match:q.match,reason:q.reason,intent:q.intent}));return(P.tests??[]).flatMap((q,V)=>{let ee=Pi(h(q.command));if(ee.length===0)return[`tests[${V}]: could not parse fixture command: ${q.command}`];let ne=ee.reduce((ce,he)=>ce??_(he,A)?.id.slice(Og.length),void 0);if(q.expect==="blocked"){if(ne===q.rule)return[];let ce=ne?`"${ne}" matched first`:"no rule matched";return[`tests[${V}]: expected "${q.rule}" to block "${q.command}" but ${ce}`]}return ne?[`tests[${V}]: expected "${q.command}" to be allowed but "${ne}" matched`]:[]})}function Pi(P){return P.nodes.flatMap((A)=>{if(A.kind==="group"||A.kind==="function")return Pi(A.body);if(A.kind!=="command")return[];let q=ge(Q(A.dialect,A.words)).words.map(t);return[...q.length>0?[q]:[],...A.nested.flatMap((V)=>Pi(V))]})}var Wr=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function Kr(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??Wr.maxRequests,maxResponseBytes:P.maxResponseBytes??Wr.maxResponseBytes}}function zt(P){return{controller:new AbortController,budget:Kr(),resolveUrl:P}}function rd(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function od(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function id(P,A){if(A>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=A,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=A}var ld=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function sd(P,A,q=v(Yr(Yr(A)),"rules policy"),V=zt()){if(S(P))return Mg(P,V);return jg(P,A,q)}async function cd(P,A,q,V,ee,ne){if(!S(P))return sd(P,A,q,V);let ce=ee?null:Fg(P,A,q);if(ce)return ce;if(!ee&&!ne)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return sd(P,A,q,V)}function Fg(P,A,q=v(Yr(Yr(A)),"rules policy")){let V=I(P),ee=O(A,V.name),ne=n(i(q,ee));if(ne===null)return null;let ce=se(Ei(ne,`Invalid rulebook ${ee}.`));if(ce.name!==V.name)throw Error(`rulebook name "${ce.name}" in ${ee} must match "${V.name}"`);return{spec:P,rulebook:ce,content:ne}}async function dd(P,A={}){if(!K(P))throw Error(`Invalid GitHub repository source: ${P}`);let[q,V]=P.split("/");if(!q||!V)throw Error(`Invalid GitHub repository source: ${P}`);if(A.ref!==void 0&&!oe(A.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${A.ref}`);let ee=A.operation??zt(),ne=A.ref??await Ng(q,V,P,ee),ce=await pd(q,V,ne,P,ee),he=await Xr(`https://api.github.com/repos/${q}/${V}/git/trees/${ce}?recursive=1`,"tree",ee),ye=he.response;if(!ye.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${ye.status}`);let be=JSON.parse(he.content);if(!Array.isArray(be?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let xe=be.tree,Ie=[...new Set(xe.flatMap((Xe)=>{if(!Xe||typeof Xe!=="object")return[];let et=Xe;if(et.type!=="blob"||typeof et.path!=="string")return[];let Re=et.path.match(rt);return Re?.[1]?[Re[1]]:[]}))].sort();if(Ie.length===0)throw Error(`No rulebooks found in ${P} under ${de}/`);return{source:P,owner:q,repo:V,ref:ne,commit:ce,names:Ie}}async function Ng(P,A,q,V){let ee=await Xr(`https://api.github.com/repos/${P}/${A}`,"metadata",V),ne=ee.response;if(!ne.ok)throw Error(`Failed to inspect ${q}: GitHub returned ${ne.status}`);let he=JSON.parse(ee.content)?.default_branch;if(typeof he!=="string"||he==="")throw Error(`Failed to inspect ${q}: missing default branch`);if(!oe(he))throw Error(`GitHub returned an invalid default branch: ${he}`);return he}function jg(P,A,q){ot(P);let V=O(A,P),ee=n(i(q,V));if(ee===null)throw Error(`Rulebook source not found: ${P}`);let ne=ud(Ei(ee,"Invalid local rulebook source."));if(ne.name!==P)throw Error(`rulebook name "${ne.name}" must match local source "${P}"`);return{spec:P,rulebook:ne,content:ee}}async function Mg(P,A){let q=I(P),V=await pd(q.owner,q.repo,q.ref,P,A),ee=await Xr(`https://raw.githubusercontent.com/${q.owner}/${q.repo}/${V}/${q.path}`,"raw",A),ne=ee.response;if(!ne.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${ne.status}`);let ce=ee.content,he=ud(Ei(ce,"Invalid GitHub rulebook response."));if(he.name!==q.name)throw Error(`rulebook name "${he.name}" must match GitHub source "${q.name}"`);return{spec:P,rulebook:he,content:ce}}function ud(P){let A=se(P),q=Jr(A);if(q.length>0)throw Error(q.join("; "));return A}function Ei(P,A){try{return JSON.parse(P)}catch{throw Error(A)}}async function pd(P,A,q,V,ee){let ne=await Xr(`https://api.github.com/repos/${P}/${A}/commits/${encodeURIComponent(q)}`,"commit",ee),ce=ne.response;if(!ce.ok)throw Error(`Failed to resolve ${V}: GitHub returned ${ce.status}`);let he=JSON.parse(ne.content);if(typeof he?.sha!=="string"||he.sha==="")throw Error(`Failed to resolve commit for ${V}`);return he.sha}async function Hg(P,A,q={}){if(q.signal?.aborted)throw q.signal.reason;let V=q.budget??Kr(),ee=new AbortController,ne=()=>ee.abort(q.signal?.reason);q.signal?.addEventListener("abort",ne,{once:!0});let ce=!1,he=setTimeout(()=>{if(ee.signal.aborted)return;ce=!0,ee.abort()},q.timeoutMs??ld.timeoutMs);try{if(q.signal?.aborted)throw q.signal.reason;od(V);let ye=await fetch(P,{signal:ee.signal,redirect:"error"});if(!ye.ok)return fd(ye),{response:ye,content:""};return{response:ye,content:await Ug(ye,A,V,()=>ee.abort())}}catch(ye){if(ce)throw Error("GitHub request timed out",{cause:ye});if(q.signal?.aborted)throw q.signal.reason;throw ye}finally{clearTimeout(he),q.signal?.removeEventListener("abort",ne)}}function Xr(P,A,q){return Hg(q.resolveUrl?.(P)??P,A,{budget:q.budget,signal:q.controller.signal})}async function Ug(P,A,q=Kr(),V){let ee=ld[`${A}Bytes`],ne=Number(P.headers.get("content-length"));if(Number.isFinite(ne)&&ne>ee)throw fd(P),Error(`GitHub ${A} response exceeds ${ee} bytes`);if(!P.body)return"";let ce=P.body.getReader(),he=[],ye=0;while(!0){let be=await ce.read();if(be.done)break;try{id(q,be.value.byteLength)}catch(xe){throw V?.(),ad(ce),xe}if(ye+=be.value.byteLength,ye>ee)throw V?.(),ad(ce),Error(`GitHub ${A} response exceeds ${ee} bytes`);he.push(Buffer.from(be.value))}return Buffer.concat(he,ye).toString("utf-8")}function fd(P){if(!P.body)return;md(()=>P.body?.cancel())}function ad(P){md(()=>P.cancel())}function md(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var Gg=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function gd(P,A){let q=vd(P.rules,A);if(q.length>0)return{ok:!0,specs:q};return yd(P.rules,A)}function hd(P,A){let q=vd(P,A);if(q.length>0)return{ok:!0,specs:q};let V=qg(P,A);if(V.length>0)return{ok:!0,specs:V};let ee=Vg(P,A);if(!ee.ok)return ee;if(ee.specs.length>0)return{ok:!0,specs:ee.specs};return yd(P,A)}function yd(P,A){let q=P.filter((V)=>Di(V)?.name===A);if(q.length===1)return{ok:!0,specs:q};return Bg(A,q)}function Bg(P,A){return{ok:!1,result:{ok:!1,errors:A.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${A.join(", ")}`],entries:[]}}}function vd(P,A){return P.filter((q)=>q===A)}function qg(P,A){let q=A.match(Gg),V=q?.[1],ee=q?.[2],ne=q?.[3];if(!V||!ee||!ne||!oe(ne))return[];return bd(P,(ce)=>ce.owner===V&&ce.repo===ee&&ce.ref===ne)}function Vg(P,A){if(!K(A))return{ok:!0,specs:[]};let[q,V]=A.split("/"),ee=bd(P,(ce)=>ce.owner===q&&ce.repo===V);if(new Set(ee.map((ce)=>Di(ce)?.ref).filter((ce)=>!!ce)).size<2)return{ok:!0,specs:ee};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${A}. Use an explicit ref:`,` cc-safety-net rule remove ${A}#<ref>`],entries:[]}}}function Di(P){try{return I(P)}catch{return null}}function bd(P,A){return P.filter((q)=>{let V=Di(q);return V?A(V):!1})}async function Qr(P,A={}){let q=_i(A);return Jg(P,q,await Zr(P,q,zt()))}function Jg(P,A,q){if(!q.ok)return q;let V=Et(P,A),ee=[...new Set(U(V.configPath,V.filesystemScope))];if(ee.length===0)return q;return{ok:!1,errors:ee,entries:q.entries}}async function Zr(P,A,q,V={},ee=new Set,ne=new Set){try{let ce=Et(P,A),he=Ri(ce.configTarget);if(!he.ok)return he.result;let ye=he.config,be=A.only?gd(ye,A.only):{ok:!0,specs:ye.rules};if(!be.ok)return be.result;let xe=new Set([...A.refresh?be.specs:[],...ee]),Ie=(ut)=>cd(ut,ce.configDir,ce.filesystemScope,q,xe.has(ut),!A.refresh||xe.has(ut)),Xe=await oh(ye.rules,A.refresh?(ut)=>Ie(ut).then((vt)=>({ok:!0,item:vt})).catch((vt)=>{if(rd(vt))throw vt;return{ok:!1,spec:ut,message:vt instanceof Error?vt.message:String(vt)}}):async(ut)=>({ok:!0,item:await Ie(ut)}),q),et=Xe.filter((ut)=>!ut.ok),Re=Xe.filter((ut)=>ut.ok).map((ut)=>ut.item),at=Re.flatMap((ut)=>Wg(ut,ye.rules)),ct=Re.flatMap((ut)=>Kg(ut,ne,ce)),lt=new Set([...at,...ct].map((ut)=>ut.spec)),ft=[...et,...at,...ct],mt=[],gt=Xg(mt,()=>Re.flatMap((ut)=>lt.has(ut.spec)||ft.length>0&&ne.has(ut.spec)?[]:Yg(ut,ce,V,mt)));return{ok:ft.length===0,errors:ft.map((ut)=>`Failed to update ${ut.spec}: ${ut.message}`),entries:Re.map(Qg),changes:gt}}catch(ce){return Qn(ce)}}function Wg(P,A){if(!S(P.spec))return[];let q=Pe(P.spec),V=A.filter((ee)=>ee!==P.spec&&Pe(ee).toLowerCase()===q.toLowerCase());if(V.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${q}" is also claimed by ${V.join(", ")}; rename one of them`}]}function Kg(P,A,q){if(!A.has(P.spec)||!S(P.spec))return[];let V=O(q.configDir,P.rulebook.name),ee=n(i(q.filesystemScope,V));if(ee===null||ee===P.content)return[];return[{ok:!1,spec:P.spec,message:`${V} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function Yg(P,A,q,V){if(!S(P.spec))return[];let ee=O(A.configDir,P.rulebook.name),ne=i(A.filesystemScope,ee),ce=n(ne);if(ce===P.content)return[];return V?.push({target:ne,previous:ce}),g(ne,P.content,void 0,q._testAfterPolicyRename),Zg(P,ce)}function Xg(P,A){try{return A()}catch(q){for(let V of[...P].reverse()){if(V.previous===null){F(V.target);continue}g(V.target,V.previous)}throw q}}function Zg(P,A){if(A===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let q=me(A),V="problem"in q?null:q.rulebook,ee=new Map(V?.rules.map((ce)=>[ce.name,JSON.stringify(ce)])??[]),ne=new Set(P.rulebook.rules.map((ce)=>ce.name));return[`Updated ${P.spec} (${V?.version??"unreadable"} -> ${P.rulebook.version})`,...[...ne].filter((ce)=>!ee.has(ce)).map((ce)=>` + ${ce}`),...[...ee.keys()].filter((ce)=>!ne.has(ce)).map((ce)=>` - ${ce}`),...P.rulebook.rules.filter((ce)=>{let he=ee.get(ce.name);return he!==void 0&&he!==JSON.stringify(ce)}).map((ce)=>` ~ ${ce.name}`)]}function Qg(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function Ld(P,A,q={}){return eh(P,A,sh(q),zt())}async function eh(P,A,q,V,ee={}){let ne=null,ce=!1;try{let he=Et(P,q),ye=n(he.configTarget);ne={target:he.configTarget,content:ye};let be=Ri(he.configTarget);if(!be.ok)return be.result;let xe=be.config,Ie=K(A);th(A,q,Ie);let Xe=Ie?await dd(A,{ref:q.ref,operation:V}):null,et=Xe?nh(Xe,q.rulebooks):[],Re=Xe?et.map((mt)=>rh(xe.rules,Xe,mt)??`${A}#${Xe.ref}/${mt}`):[A],at=Re.filter((mt)=>!xe.rules.includes(mt)),ct=[...xe.rules,...at];if(ct.length>pe)return ih();if(ct.length!==xe.rules.length)ce=!0,Pt(he.configTarget,{version:1,rules:ct,overrides:xe.overrides??{},transparent_wrappers:xe.transparent_wrappers??[]},void 0,ee._testAfterPolicyRename);let lt=await Zr(P,q,V,ee,new Set(at),new Set(at));if(!lt.ok)Zn(he.configTarget,ye);if(!lt.ok||!Xe)return lt;let ft=et.filter((mt,gt)=>at.includes(Re[gt]??""));return{...lt,add:{source:A,ref:Xe.ref,selected:et,added:ft,alreadyConfigured:et.filter((mt)=>!ft.includes(mt)),commits:at.length>0?[Xe.commit]:[]}}}catch(he){if(ce&&ne)try{Zn(ne.target,ne.content)}catch(ye){return Qn(ye)}return Qn(he)}}function th(P,A,q){if(!q&&A.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!q&&A.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(A.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let V=A.rulebooks?.filter((ee)=>!l.test(ee))??[];if(V.length>0)throw Error(`Invalid rulebook names: ${V.join(", ")}`)}function nh(P,A){let q=A?[...new Set(A)]:P.names,V=q.filter((ee)=>!P.names.includes(ee));if(V.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${V.join(", ")}
|
|
273
|
+
Available rulebooks: ${P.names.join(", ")}`);return q}function rh(P,A,q){let V=`${A.source}#${A.ref}/${q}`;if(P.includes(V))return V;let ee=`${A.source}#${A.commit}/${q}`;return P.find((ne)=>ne===ee)}async function oh(P,A,q=zt()){if(P.length>pe)throw Error(fe);let V=[],ee=0,ne,ce=Array.from({length:Math.min(P.length,Wr.concurrency)},async()=>{while(!ne){let he=ee;if(he>=P.length)return;ee++;try{V[he]=await A(P[he],he,q.controller.signal)}catch(ye){if(!ne)ne={value:ye},ee=P.length,q.controller.abort(ye);return}}});if(await Promise.all(ce),ne)throw ne.value;return V}function ih(){return{ok:!1,errors:[fe],entries:[]}}function _i(P){return{cwd:P.cwd,userConfigDir:P.userConfigDir,userConfigPath:P.userConfigPath,projectConfigPath:P.projectConfigPath,global:P.global,only:P.only,refresh:P.refresh}}function sh(P){return{..._i(P),ref:P.ref,rulebooks:P.rulebooks}}function ah(P){return{..._i(P),deleteSource:P.deleteSource}}async function wd(P,A,q={}){try{return await lh(P,A,ah(q),{})}catch(V){return Qn(V)}}async function lh(P,A,q,V){let ee=Et(P,q),ne=m(ee.configTarget);if(ne.errors.length>0)return{ok:!1,errors:ne.errors,entries:[]};if(!ne.config)return{ok:!1,errors:[`No config found at ${ee.configPath}`],entries:[]};let ce=hd(ne.config.rules,A);if(!ce.ok)return ce.result;let he=q.deleteSource?ch(ee.configDir,ce.specs,ee.filesystemScope):{ok:!0,dirs:[]};if(!he.ok)return he.result;let ye=n(ee.configTarget);if(ye===null)return Qn(Error("Rules config is unavailable."));try{Pt(ee.configTarget,{version:1,rules:ne.config.rules.filter((Ie)=>!ce.specs.includes(Ie)),overrides:ne.config.overrides??{},transparent_wrappers:ne.config.transparent_wrappers??[]},void 0,V._testAfterPolicyRename)}catch(Ie){throw Zn(ee.configTarget,ye),Ie}let be=await Zr(P,q,zt(),V);if(!be.ok)return Zn(ee.configTarget,ye),be;let xe=dh(he.dirs,V,ee.filesystemScope);if(!xe.ok){Zn(ee.configTarget,ye);let Ie=await Zr(P,q,zt(),V);if(!Ie.ok)return{ok:!1,errors:[...xe.result.errors,...Ie.errors],entries:Ie.entries};return xe.result}return be}function ch(P,A,q){let V=A.flatMap((he)=>l.test(he)?[]:["--delete-source can only delete local rulebook sources"]),ee=A.map((he)=>Ai(P,he)),ne=V.length>0?[]:ee.flatMap((he)=>kd(he,q)),ce=[...V,...ne];return ce.length>0?{ok:!1,result:{ok:!1,errors:ce,entries:[]}}:{ok:!0,dirs:ee}}function kd(P,A){let q=zg(P),V=i(A,q),ee=le(V);if(!ee)return[`Local rulebook source directory not found: ${P}`];let ne=ee.find((ce)=>ce.name==="rulebook.json");if(!ne)return[`Local rulebook source directory is missing rulebook.json: ${P}`];if(ne.kind!=="file")throw new r(A.label);if(n(i(A,Ai(q,"rulebook.json"))),ee.length>1)return[`Local rulebook source directory contains extra files: ${P}. delete manually if you really want to remove the directory.`];return[]}function dh(P,A,q){let V=P.flatMap((ee)=>{try{if(!le(i(q,ee)))return[];let ne=kd(ee,q);if(ne.length>0)return ne;return uh(ee,A,q),[]}catch(ne){return[`Failed to delete local rulebook source ${ee}: ${ne instanceof Error?ne.message:String(ne)}`]}});return V.length>0?{ok:!1,result:{ok:!1,errors:V,entries:[]}}:{ok:!0}}function uh(P,A,q){if(A._testDeleteLocalSourceDir){A._testDeleteLocalSourceDir(P);return}F(i(q,Ai(P,ue))),nt(i(q,P))}function Zn(P,A){if(A===null){F(P);return}g(P,A)}function Qn(P){return{ok:!1,errors:[P instanceof Error?P.message:String(P)],entries:[]}}var ph=".safety-net.json",fh="~/.cc-safety-net/config.json";async function Rd(P,A){return[await Cd(P,{legacyPath:Hs({cwd:A.cwd}),configPath:M(A.cwd),defaultRulebookName:"project-rules",migratedFrom:ph,cleanup:A.cleanup,syncOptions:{cwd:A.cwd}}),await Cd(P,{legacyPath:lr(P),configPath:j(P),defaultRulebookName:"user-rules",migratedFrom:fh,cleanup:A.cleanup,syncOptions:{cwd:A.cwd,global:!0}})].every((V)=>V)?0:1}async function Cd(P,A){let q=Et(P,A.syncOptions),V=i(q.filesystemScope,A.legacyPath),ee=n(V);if(ee===null)return console.log(`No legacy config found at ${A.legacyPath}`),!0;let ne=gh(ee);if(!ne.ok){for(let et of ne.errors)console.error(et);return!1}let ce=m(q.configTarget);if(ce.errors.length>0){for(let et of ce.errors)console.error(et);return!1}let he=ce.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},ye=hh(xd(A.configPath),he.rules,A.defaultRulebookName,A.migratedFrom,q.filesystemScope),be=eo(xd(A.configPath),ye,"rulebook.json"),xe=i(q.filesystemScope,be),Ie=[Sd(q.configTarget),Sd(xe)],Xe=await mh(P,A,q.configTarget,xe,ye,ne.config.rules,he.rules.includes(ye)?he.rules:[...he.rules,ye],he.overrides??{},he.transparent_wrappers??[]);if(!Xe.ok){bh(Ie);for(let et of Xe.errors)console.error(et);return!1}if(!A.cleanup)return console.log(`Migrated legacy config at ${A.legacyPath}. Legacy file is no longer used.`),!0;if(!vh(q.configTarget,xe,ye,A.migratedFrom,ne.config.rules))return console.error(`Migration cleanup verification failed for ${A.legacyPath}`),!1;return F(V),console.log(`Deleted legacy config at ${A.legacyPath}`),!0}async function mh(P,A,q,V,ee,ne,ce,he,ye){try{return Pt(q,{version:1,rules:ce,overrides:he,transparent_wrappers:ye}),Pt(V,yh(ee,A.migratedFrom,ne)),await Qr(P,A.syncOptions)}catch(be){return{ok:!1,errors:[be instanceof Error?be.message:String(be)]}}}function gh(P){try{let A=JSON.parse(P),q=uo(A);if(q.errors.length>0)return{ok:!1,errors:q.errors};return{ok:!0,config:{version:1,rules:A.rules??[]}}}catch{return{ok:!1,errors:["Invalid JSON"]}}}function hh(P,A,q,V,ee){let ne=A.find((ce)=>Lh(i(ee,eo(P,ce,"rulebook.json")))===V);if(ne)return ne;if(n(i(ee,eo(P,q,"rulebook.json")))===null)return q;for(let ce=2;;ce++){let he=`${q}-${ce}`;if(n(i(ee,eo(P,he,"rulebook.json")))===null)return he}}function yh(P,A,q){return{rulebook_version:1,name:P,version:"1.0.0",description:"Migrated CC Safety Net rules.",author:"project",migrated_from:A,allowed_commands:[...new Set(q.map((V)=>V.command))],rules:q,tests:q.map((V)=>({command:[V.command,V.subcommand,V.block_args[0]].filter(Boolean).join(" "),expect:"blocked",rule:V.name}))}}function vh(P,A,q,V,ee){if(!m(P).config?.rules.includes(q))return!1;try{let ce=n(A);if(ce===null)return!1;let he=JSON.parse(ce);return he.migrated_from===V&&JSON.stringify(he.rules)===JSON.stringify(ee)}catch{return!1}}function Sd(P){return{target:P,content:n(P)}}function bh(P){for(let A of P){if(A.content===null){F(A.target);continue}g(A.target,A.content)}}function Lh(P){let A=n(P);if(A===null)return null;try{let q=JSON.parse(A);return typeof q.migrated_from==="string"?q.migrated_from:null}catch{return null}}import{mkdir as wh,readFile as kh,writeFile as xh}from"node:fs/promises";import{dirname as Ch,join as Sh}from"node:path";var Rh=86400000,Ph=604800000;async function Ed(P,A=Date.now()){if(P.env.get("CC_SAFETY_NET_NO_UPDATE_CHECK"))return null;let q=je(P);if(!q)return null;let V=Sh(q,".cc-safety-net","update-check.json"),ee=await Eh(V,A);if(!ee.lastCheck||A-ee.lastCheck>Rh){let he=await Wt();if(ee.lastCheck=A,he.latestVersion)ee.latestVersion=he.latestVersion;if(!await Pd(V,ee))return null;if(he.error)return null}let ne=ee.latestVersion,ce=wt();if(!ne||!yo(ne,ce))return null;if(ee.notifiedVersion===ne&&ee.notifiedAt!==void 0&&A-ee.notifiedAt<Ph)return null;if(ee.notifiedVersion=ne,ee.notifiedAt=A,!await Pd(V,ee))return null;return`UPDATE_AVAILABLE: cc-safety-net v${ne} is available (running v${ce}). Ask the user once whether to run \`npx -y cc-safety-net@latest update\`; continue the current task either way and do not raise this again.`}async function Eh(P,A){let q=await kh(P,"utf8").then((ne)=>JSON.parse(ne)).catch(()=>{return});if(!q||typeof q!=="object"||Array.isArray(q))return{};let V=q,ee=(ne)=>typeof ne==="number"&&Number.isFinite(ne)&&ne<=A?ne:void 0;return{lastCheck:ee(V.lastCheck),latestVersion:typeof V.latestVersion==="string"?V.latestVersion:void 0,notifiedVersion:typeof V.notifiedVersion==="string"?V.notifiedVersion:void 0,notifiedAt:ee(V.notifiedAt)}}async function Pd(P,A){return wh(Ch(P),{recursive:!0,mode:448}).then(()=>xh(P,JSON.stringify(A),{mode:384})).then(()=>!0).catch(()=>!1)}import{join as Dh,resolve as Ti}from"node:path";var Dd="CC Safety Net Config",Ah="═".repeat(Dd.length),_h="https://raw.githubusercontent.com/kenryu42/cc-safety-net/main/assets/cc-safety-net.schema.json",Th=new Set(["rule.json","rule.lock","cache"]);function Ad(P,A={}){try{return Ih(P,A)}catch(q){if(q instanceof r)return console.error(q.message),1;throw q}}function Ih(P,A){let q=A.cwd??process.cwd(),V=J(P,{cwd:q}),ee=lr(P),ne=cs(q),ce=Ti(q,de),he=i(V.userScope,ee),ye=i(V.projectScope,ne),be=!1,xe=!1,Ie=[],Xe=[],et=$h(i(V.projectScope,ce));if(Fh(),n(V.userConfigTarget)!==null){let Re=Jt(V.userConfigTarget);if(Re.errors.push(...U(V.userConfigPath,V.userScope)),Ie.push({scope:"User",path:V.userConfigPath,result:Re,schema:"rules",target:V.userConfigTarget}),Re.errors.length>0)be=!0}if(n(he)!==null)if(xe=!0,n(V.userConfigTarget)!==null)Xe.push(to("user","cleanup"));else{let Re=po(he);if(Ie.push({scope:"User",path:ee,result:Re,schema:"legacy",inactive:!0,target:he}),Xe.push(to("user",Re.errors.length>0?"fix-or-delete":"migrate")),Re.errors.length>0)be=!0}if(n(V.projectConfigTarget)!==null){let Re=Jt(V.projectConfigTarget);if(Re.errors.push(...U(V.projectConfigPath,V.projectScope)),Ie.push({scope:"Project",path:Ti(V.projectConfigPath),result:Re,schema:"rules",target:V.projectConfigTarget}),Re.errors.length>0)be=!0;if(n(ye)!==null)xe=!0,Xe.push(to("project","cleanup"))}else if(n(ye)!==null){xe=!0,be=!0;let Re=po(ye);Ie.push({scope:"Project",path:Ti(ne),result:Re,schema:"legacy",inactive:!0,target:ye}),Xe.push(to("project",Re.errors.length>0?"fix-or-delete":"migrate"))}if(et?.result.errors.length)be=!0;if(Ie.length===0&&!et)return console.log(`
|
|
274
|
+
No config files found. Using built-in rules only.`),0;for(let Re of Ie)if(Re.inactive)jh(Re.scope,Re.path,Re.result);else if(Re.result.errors.length>0)Mh(Re.scope,Re.path,Re.result.errors);else{if(Re.schema==="rules"&&Gh(Re.target))console.log(`
|
|
275
|
+
Added $schema to ${Re.scope.toLowerCase()} config.`);Nh(Re.scope,Re.path,Re.result,Re.schema)}for(let Re of Xe)console.error(`
|
|
276
|
+
${dt.red(Re)}`);if(et)if(et.result.errors.length>0)Uh(et.path,et.result.errors);else Hh(et.path,et.result);if(be)return console.error(`
|
|
268
277
|
Config validation failed.`),1;return console.log(xe?`
|
|
269
278
|
Configs valid with warnings.`:`
|
|
270
|
-
All configs valid.`),0}function
|
|
271
|
-
✓ ${P} config: ${A}`),console.log(` Schema: ${V==="rules"?"rulebook sources":"legacy inline rules"}`),q.ruleNames.size>0){console.log(` ${V==="rules"?"Sources":"Rules"}:`);let ee=1;for(let ne of q.ruleNames)console.log(` ${ee}. ${ne}`),ee++}else console.log(` ${V==="rules"?"Sources":"Rules"}: (none)`)}function
|
|
272
|
-
✗ Legacy ${P.toLowerCase()} config: ${A}`),console.error(" Schema: legacy inline rules"),console.error(" Status: ignored by CC Safety Net"),q.errors.length>0){console.error(" Errors:");let V=1;for(let ee of q.errors)for(let ne of ee.split("; "))console.error(` ${V}. ${ne}`),V++;return}if(q.ruleNames.size>0){console.error(" Rules:");let V=1;for(let ee of q.ruleNames)console.error(` ${V}. ${ee}`),V++;return}console.error(" Rules: (none)")}function
|
|
273
|
-
✓ GitHub source rules: ${P}`),console.log(" Rulebooks:");let q=1;for(let V of A.ruleNames)console.log(` ${q}. ${V}`),q++}function
|
|
274
|
-
✗ ${P}: ${A}`),console.error(" Errors:");let V=1;for(let ee of q)for(let ne of ee.split("; "))console.error(` ${V}. ${ne}`),V++}function
|
|
275
|
-
`))}import{spawn as
|
|
276
|
-
return POSIX path of (choose folder with prompt "${
|
|
279
|
+
All configs valid.`),0}function to(P,A){let q=`legacy ${P} config`;if(A==="cleanup")return`Warning: Legacy ${P} config is no longer needed. Run \`npx -y cc-safety-net rule migrate --cleanup\` to clean it up safely.`;if(A==="migrate")return`Warning: Legacy ${P} config is ignored by CC Safety Net. Run \`npx -y cc-safety-net rule migrate\`.`;return`Warning: Legacy ${P} config is no longer supported. Fix or delete the ${q}, then run \`npx -y cc-safety-net rule migrate\`.`}function $h(P){if(le(P)===null)return null;let A=Oh(P);if(A.ruleNames.size===0&&A.errors.length===0)return null;return{path:P.path,result:A}}function Oh(P){let A=[],q=new Set,V=(le(P)??[]).filter((ee)=>!Th.has(ee.name)).sort((ee,ne)=>ee.name.localeCompare(ne.name));if(V.length===0)return{errors:A,ruleNames:q};for(let ee of V){if(!l.test(ee.name)){A.push(`rulebook directory names must match ${l}: ${ee.name}`);continue}if(ee.kind!=="directory"){A.push(`${ee.name} must be a rulebook directory`);continue}let ne=i(P.scope,Dh(P.path,ee.name,"rulebook.json")),ce=n(ne);if(ce===null){A.push(`${ee.name}/rulebook.json is required`);continue}try{let he;try{he=JSON.parse(ce)}catch{A.push(`${ee.name}/rulebook.json: invalid JSON`);continue}let ye=se(he);if(ye.name!==ee.name){A.push(`rulebook name "${ye.name}" must match folder "${ee.name}"`);continue}let be=Jr(ye);if(be.length>0){A.push(...be.map((xe)=>`${ee.name}/rulebook.json: ${xe}`));continue}q.add(ee.name)}catch(he){A.push(he instanceof Error?`${ee.name}/rulebook.json: ${he.message}`:`${ee.name}/rulebook.json: ${String(he)}`)}}return{errors:A,ruleNames:q}}function Fh(){console.log(Dd),console.log(Ah)}function Nh(P,A,q,V){if(console.log(`
|
|
280
|
+
✓ ${P} config: ${A}`),console.log(` Schema: ${V==="rules"?"rulebook sources":"legacy inline rules"}`),q.ruleNames.size>0){console.log(` ${V==="rules"?"Sources":"Rules"}:`);let ee=1;for(let ne of q.ruleNames)console.log(` ${ee}. ${ne}`),ee++}else console.log(` ${V==="rules"?"Sources":"Rules"}: (none)`)}function jh(P,A,q){if(console.error(`
|
|
281
|
+
✗ Legacy ${P.toLowerCase()} config: ${A}`),console.error(" Schema: legacy inline rules"),console.error(" Status: ignored by CC Safety Net"),q.errors.length>0){console.error(" Errors:");let V=1;for(let ee of q.errors)for(let ne of ee.split("; "))console.error(` ${V}. ${ne}`),V++;return}if(q.ruleNames.size>0){console.error(" Rules:");let V=1;for(let ee of q.ruleNames)console.error(` ${V}. ${ee}`),V++;return}console.error(" Rules: (none)")}function Mh(P,A,q){_d(`${P} config`,A,q)}function Hh(P,A){console.log(`
|
|
282
|
+
✓ GitHub source rules: ${P}`),console.log(" Rulebooks:");let q=1;for(let V of A.ruleNames)console.log(` ${q}. ${V}`),q++}function Uh(P,A){_d("GitHub source rules",P,A)}function _d(P,A,q){console.error(`
|
|
283
|
+
✗ ${P}: ${A}`),console.error(" Errors:");let V=1;for(let ee of q)for(let ne of ee.split("; "))console.error(` ${V}. ${ne}`),V++}function Gh(P){try{let A=n(P);if(A===null)return!1;let q=JSON.parse(A);if(q.$schema)return!1;return g(P,JSON.stringify({$schema:_h,...q},null,2)),!0}catch(A){if(A instanceof r)throw A;return!1}}var Td=new Set(["init","add","remove","update","sync","list","wrapper","migrate","doc","verify"]),qh=new Set(["add","remove","list"]),Vh="cc-safety-net/rulebooks";async function Id(P,A){try{return await zh(P,A)}catch(q){if(q instanceof r)return console.error(q.message),1;throw q}}async function zh(P,A){let q=Wh(A),V=q.help?Jh(q.positionals):null;if(V)return Cn(V),0;if(q.errors.length>0){for(let he of q.errors)console.error(he);return 1}let ee=q.positionals[0];if(!ee)return Cn(gn,console.error),1;let ne=q.positionals[1],ce={global:q.global};if(ee==="init"){let he=Et(P,ce);Zh(he.configTarget);let ye=Bh(he.configDir,"example-rules","rulebook.json"),be=i(he.filesystemScope,ye);if(q.example&&n(be)===null)nd(be);let xe=U(he.configPath,he.filesystemScope);for(let Ie of xe)console.error(Ie);if(xe.length>0)return 1;return console.log("Rule config initialized."),0}if(ee==="add"){let he=$d(q);if(!he)return console.error("rule add requires a source (pass --only <rulebook...> to select from cc-safety-net/rulebooks)"),1;let ye=Et(P,ce),be=await Ld(P,he,{...ce,ref:q.ref,rulebooks:q.only.length>0?q.only:void 0});return Xc(be,he,`Scope: ${q.global?"user":"project"} (${ye.configDir})`),be.ok?0:1}if(ee==="remove"){if(!ne)return console.error("rule remove requires a source"),1;let he=await wd(P,ne,{...ce,deleteSource:q.deleteSource});return zr(he,`Removed rulebook source: ${ne}`),he.ok?0:1}if(ee==="update"){let he=await Qr(P,{...ce,only:ne,refresh:!0});return zr(he,"Rule config updated."),he.ok?0:1}if(ee==="sync")return Bs(P,{global:q.global});if(ee==="list"){let he=Y(P,{cwd:process.cwd()});return Qc(he),he.errors.length>0?1:0}if(ee==="wrapper")return Qh(P,q);if(ee==="migrate")return Rd(P,{cleanup:q.cleanup,cwd:process.cwd()});if(ee==="doc"){console.log(Kc);let he=await Ed(P);if(he)console.error(he);return 0}if(ee==="verify")return Ad(P);return 1}function Jh(P){if(P.length===0)return gn;let A=gn.subcommands.filter((V)=>V.usage.split(" ")[0]===P[0]);if(A.length===0)return null;if(P.length===1&&A.length>1)return{name:`rule ${P[0]}`,description:`Subcommands of rule ${P[0]}`,usage:`rule ${P[0]} <subcommand>`,subcommands:A,options:[]};let q=P.length===1?A[0]:A.find((V)=>V.usage.split(" ")[1]===P[1]);if(!q)return null;return{name:`rule ${P[0]}`,description:q.description,usage:`rule ${q.usage}`,options:P[0]==="add"?ao:[],examples:P[0]==="add"?lo:void 0}}function Wh(P){let A=xt({label:"rule",booleans:{global:["-g","--global"],cleanup:["--cleanup"],deleteSource:["--delete-source"],example:["--example"]},values:{ref:["--ref"]},lists:{only:["--only"]},positionals:"list"},P),q={...A.flags,ref:A.values.ref,only:A.lists.only??[],help:A.help,positionals:A.positionals,errors:A.errors};return Kh(q),q}function Kh(P){let[A]=P.positionals;if(A&&!Td.has(A))P.errors.push(`Unknown rule subcommand: ${A}`);if(P.deleteSource&&A!=="remove")if(A&&Td.has(A))P.errors.push(`Unknown option for rule ${A}: --delete-source`);else P.errors.push("--delete-source is only valid with 'rule remove'");if(P.cleanup&&A!=="migrate")P.errors.push(er(A,"--cleanup"));if(P.example&&A!=="init")P.errors.push(er(A,"--example"));if(P.ref&&A!=="add")P.errors.push(er(A,"--ref"));if(P.only.length>0&&A!=="add")P.errors.push(er(A,"--only"));if(A==="add")Yh(P);if(A==="migrate"){if(P.global)P.errors.push(er(A,"--global"));if(P.positionals.length>1)P.errors.push(`Unexpected rule migrate argument: ${P.positionals[1]}`)}else if(A==="wrapper")Xh(P);else if(P.positionals.length>2)P.errors.push(`Unexpected rule argument: ${P.positionals[2]}`);if(A==="list"&&P.global)P.errors.push("Unknown option for rule list: --global")}function $d(P){if(P.positionals[1])return P.positionals[1];if(P.ref||P.only.length>0)return Vh;return}function Yh(P){let A=$d(P);if(!A)return;if((P.ref||P.only.length>0)&&!K(A)){if(P.ref)P.errors.push(`--ref can only select a ref for an owner/repo source: ${A}`);if(P.only.length>0)P.errors.push("--only can only select rulebooks from an owner/repo source");return}if(P.ref&&!oe(P.ref))P.errors.push(`--ref must use valid path segments: ${P.ref}`);let q=P.only.filter((V)=>!l.test(V));if(q.length>0)P.errors.push(`Invalid rulebook names: ${q.join(", ")}`)}function er(P,A){return P?`Unknown option for rule ${P}: ${A}`:`Unknown option for rule: ${A}`}function Xh(P){let A=P.positionals[1],q=P.positionals[2];if(!A){P.errors.push("rule wrapper requires add, remove, or list");return}if(!qh.has(A)){P.errors.push(`Unknown rule wrapper action: ${A}`);return}if(A==="list"){if(q)P.errors.push(`Unexpected rule wrapper argument: ${q}`);return}if(!q){P.errors.push(`rule wrapper ${A} requires a command`);return}if(P.positionals.length>3)P.errors.push(`Unexpected rule wrapper argument: ${P.positionals[3]}`)}function Zh(P){if(n(P)===null){td(P);return}let A=m(P);if(!A.config)return;Pt(P,{version:1,rules:A.config.rules,overrides:A.config.overrides??{},transparent_wrappers:A.config.transparent_wrappers??[]})}async function Qh(P,A){let q=A.positionals[1],V=A.positionals[2],ee=Et(P,{global:A.global}).configTarget;if(q==="list"){let ye=m(ee);if(ye.errors.length>0){for(let be of ye.errors)console.error(be);return 1}return ey(ye.config?.transparent_wrappers??[]),0}if(!V||!w.test(V))return console.error("transparent wrapper must match command pattern"),1;if(Le(V))return console.error(`reserved command "${V}" cannot be a wrapper`),1;let ne=m(ee);if(ne.errors.length>0){for(let ye of ne.errors)console.error(ye);return 1}let ce=ne.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},he=q==="add"?[...new Set([...ce.transparent_wrappers??[],V])]:(ce.transparent_wrappers??[]).filter((ye)=>ye!==V);return Pt(ee,{version:1,rules:ce.rules,overrides:ce.overrides??{},transparent_wrappers:he}),console.log(q==="add"?`Added transparent wrapper: ${V}`:`Removed transparent wrapper: ${V}`),0}function ey(P){if(P.length===0){console.log("Transparent wrappers: (none)");return}console.log(`Transparent wrappers (${P.length}):`);for(let A of P)console.log(` - ${A}`)}import{sep as sy}from"node:path";import{existsSync as ty,readFileSync as ny}from"node:fs";import{join as ry}from"node:path";async function oy(P){if(P.isTTY)return null;return(await We(P).catch(()=>null))?.trim()||null}function iy(P){let A=P.env.get("CLAUDE_SETTINGS_PATH");if(A)return A;return ry(vr(P),"settings.json")}function Ii(P){let A=iy(P);if(!ty(A))return!1;try{let q=ny(A,"utf-8"),V=JSON.parse(q);if(!V.enabledPlugins)return!1;let ee="cc-safety-net@cc-marketplace";if(!(ee in V.enabledPlugins))return!1;return V.enabledPlugins[ee]===!0}catch(q){if(L(o.debug,P.env))console.error(`CC Safety Net debug: failed to read Claude settings: ${A}: ${q instanceof Error?q.message:String(q)}`);return!1}}async function $i(P,A=process.stdin){let q=Ii(P),V;if(!q)V="\uD83D\uDEE1️ CC Safety Net ❌";else{let ne=E(P,{cwd:process.cwd()}),ce=ne.policy,he=R(ce,P.env),ye=Object.values(H(ce,he.capabilities)).some((Ie)=>Ie.changesInherited),be={standard:"✅",strict:"\uD83D\uDD12",paranoid:"\uD83D\uDC41️",custom:"\uD83D\uDD27"}[ye?"custom":he.effectiveLevel],xe=(ne.policyScopes?.weakenings.length??0)>0?"\uD83D\uDD3B":"";V=`\uD83D\uDEE1️ CC Safety Net ${be}${he.worktreeMode?"\uD83C\uDF33":""}${xe}${ne.state==="degraded"?"⚠️":""}`}let ee=await oy(A);if(ee&&!ee.startsWith("{"))console.log(`${ee} | ${V}`);else console.log(V)}function Od(P){let A=E(P,{cwd:process.cwd()}),q=A.policy,V=R(q,P.env),ee=!!process.env.NO_COLOR||!process.stdout.isTTY,ne=Math.min(process.stdout.columns||80,100),ce=ee?"ok":"✔",he=ee?"OFF":"✘",ye=(at,ct)=>{let lt=` ${at.padEnd(13)}${ct}`;return(lt.length>ne?`${lt.slice(0,ne-1)}…`:lt).replaceAll(he,dt.red(he))},be=Object.values(H(q,V.capabilities)).some((at)=>at.changesInherited),xe=(at)=>at===P.home||at.startsWith(`${P.home}${sy}`)?`~${at.slice(P.home.length)}`:at,Ie={ready:dt.green,degraded:dt.yellow}[A.state],Xe=A.policyScopes?.weakenings??[],et=[...Ii(P)?[]:["plugin cc-safety-net@cc-marketplace is disabled in Claude Code; nothing is enforced in Claude Code until it is re-enabled. Other integrations are not affected."],...A.diagnostics],Re=ee?"-":"·";console.log([`${ee?"":"\uD83D\uDEE1️ "}CC Safety Net — ${Ie(A.state)}`,"",ye("Protection",`destructive ${q.destructiveCommandProtectionEnabled?ce:he} secrets ${q.secretProtection.enabled?ce:he}`),ye("Level",be?`${V.effectiveLevel} (customised)`:V.effectiveLevel),ye("Rules",q.rules.length===0?"none active":`${q.rules.length} active`),ye("Policy",xe(u(P))),...A.policyScopes?[ye("Project",xe(b(process.cwd())))]:[],...V.worktreeMode?[ye("Worktree","relaxations active")]:[],"",...Xe.length===0?[]:[" Project policy",...Xe.flatMap((at)=>Bn(at," ",ne-6).map((ct,lt)=>lt===0?` ${ct}`:ct)),""],...et.length===0?[" Everything configured is active."]:[" Not active",...et.flatMap((at)=>Bn(at," ",ne-6).map((ct,lt)=>lt===0?` ${Re} ${ct}`:ct)),""," Full report: cc-safety-net doctor"]].join(`
|
|
284
|
+
`))}import{spawn as zd}from"node:child_process";import{randomBytes as hy}from"node:crypto";import{existsSync as yy}from"node:fs";import{createServer as vy}from"node:http";import{Writable as by}from"node:stream";var no=500;function ay(P){let A=P.filter((ee)=>ee.decision!=="allow"),q=P.filter((ee)=>ee.decision==="allow"),V=Math.min(A.length,Math.max(no-q.length,Math.ceil(no/2)));return[...A.slice(0,V),...q.slice(0,no-V)]}function Fd(P,A,q=D(P)){if(q)z(P,q);let V=(ct)=>new Date(ct.getFullYear(),ct.getMonth(),ct.getDate()).getTime(),ee=V(new Date),ne=new Date(ee);ne.setDate(ne.getDate()-(A-1));let ce=ne.getTime(),he=[],ye={count:0};for(let ct of q?Xt(q,ye):[])for(let lt of mn(ct,ye)){let ft=new Date(lt.ts).getTime();if(!Number.isFinite(ft))continue;if(ft>=ce)he.push(lt)}he.sort((ct,lt)=>new Date(lt.ts).getTime()-new Date(ct.ts).getTime());let be=Array.from({length:A},()=>0),xe=Array.from({length:A},()=>0),Ie={},Xe={},et={},Re=0,at=0;for(let ct of he){let lt=ct.agent||"unknown";Ie[lt]=(Ie[lt]??0)+1;let ft=Math.round((ee-V(new Date(ct.ts)))/86400000),mt=A-1-ft,gt=ft>=0&&ft<A;if(gt)xe[mt]=(xe[mt]??0)+1;if(ct.decision!=="allow"){if(Re++,ct.ruleId)Xe[ct.ruleId]=(Xe[ct.ruleId]??0)+1;let ut=so(ct.segment||ct.command);if(ut)et[ut]=(et[ut]??0)+1;if(ct.failureStage)at++;if(gt)be[mt]=(be[mt]??0)+1}}return{days:A,logsDir:q,homeDir:P.home,totalInWindow:he.length,truncated:he.length>no,unreadable:ye.count,counts:{blocked:Re,allowed:he.length-Re,agents:Ie,blockedByDay:be,analyzedByDay:xe,rules:Xe,commands:et,errors:at},entries:ay(he).sort((ct,lt)=>new Date(lt.ts).getTime()-new Date(ct.ts).getTime())}}import{spawn as ly}from"node:child_process";import{existsSync as cy,statSync as Nd}from"node:fs";import{delimiter as dy,join as uy}from"node:path";var py=120000,ro="Choose the project folder",fy=`try
|
|
285
|
+
return POSIX path of (choose folder with prompt "${ro}")
|
|
277
286
|
on error number -128
|
|
278
287
|
return ""
|
|
279
|
-
end try`,
|
|
288
|
+
end try`,my=`Add-Type -AssemblyName System.Windows.Forms
|
|
280
289
|
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
281
|
-
$dialog.Description = '${
|
|
282
|
-
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,
|
|
290
|
+
$dialog.Description = '${ro}'
|
|
291
|
+
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,jd=[{binary:"zenity",args:["--file-selection","--directory",`--title=${ro}`]},{binary:"kdialog",args:["--getexistingdirectory",".","--title",ro]}],Md=(P,A)=>(A.PATH??"").split(dy).some((q)=>{if(q.length===0)return!1;try{let V=Nd(uy(q,P));return V.isFile()&&(V.mode&73)!==0}catch{return!1}});function Oi(P,A){if(P==="darwin"||P==="win32")return!0;if(P!=="linux")return!1;if(!A.DISPLAY&&!A.WAYLAND_DISPLAY)return!1;return jd.some((q)=>Md(q.binary,A))}function gy(P,A){if(P==="darwin")return{cmd:"osascript",args:["-e",fy]};if(P==="win32")return{cmd:"powershell.exe",args:["-NoProfile","-STA","-Command",my]};let q=jd.find((V)=>Md(V.binary,A));return q?{cmd:q.binary,args:q.args}:null}function Fi(P=process.platform,A=process.env){let q=gy(P,A);if(!q)return Promise.resolve({error:"No folder dialog is available on this system"});return new Promise((V)=>{let ee=ly(q.cmd,q.args,{env:A,stdio:["ignore","pipe","pipe"]}),ne="",ce=!1,he=(be)=>{if(ce)return;ce=!0,clearTimeout(ye),V(be)},ye=setTimeout(()=>{ee.kill(),he({error:"The folder dialog timed out"})},py);ee.stdout.on("data",(be)=>{ne+=be.toString()}),ee.on("error",()=>he({error:`Could not open the folder dialog (${q.cmd})`})),ee.on("close",()=>{let be=ne.trim().replace(/\/+$/,"");if(!be)return he({cancelled:!0});if(!cy(be)||!Nd(be).isDirectory())return he({error:"That selection is not a folder on disk"});he({path:be})})})}var Hd=`<!doctype html>
|
|
283
292
|
<html lang="en">
|
|
284
293
|
<head>
|
|
285
294
|
<meta charset="utf-8">
|
|
@@ -5916,6 +5925,6 @@ load().then((loaded) => {
|
|
|
5916
5925
|
</script>
|
|
5917
5926
|
</body>
|
|
5918
5927
|
</html>
|
|
5919
|
-
`;var
|
|
5920
|
-
`),{path:V,errors:[]}}catch(ne){return{path:V,errors:[ne instanceof Error?ne.message:String(ne)]}}}function
|
|
5921
|
-
`)}}finally{console.log=ce,console.error=he}},ee=
|
|
5928
|
+
`;var Ud='<script id="ccsn-data" type="application/json">';function Gd(P){return Hd.replace(Ud,()=>Ud+JSON.stringify({token:P}).replaceAll("<","\\u003c"))}var oo="kenryu42/cc-safety-net",Ly=`https://github.com/${oo}`,Mi=1e4,wy=7,ky="The project draft directory changed; reload the draft before applying.",xy="audit settings are user scope only; remove the audit section from a project proposal";async function Jd(P,A={}){let q=xt({label:"gui",booleans:{noOpen:["--no-open"]}},P),V=A.log??console.log,ee=A.error??console.error;if(q.errors.length>0){for(let ce of q.errors)ee(ce);return ee("Usage: cc-safety-net gui [--no-open]"),1}let ne=await Cy(c,A);if(V(`CC Safety Net policy GUI: ${ne.url}`),!q.flags.noOpen)try{await(A.openBrowser??Oy)(ne.url)}catch(ce){ee(`Failed to open browser: ${ce instanceof Error?ce.message:String(ce)}`),ee(`Open this URL manually: ${ne.url}`)}if(A.keepAlive===!1)return await ne.close(),0;return await $y(ne),0}async function Cy(P,A={}){let q=hy(24).toString("base64url"),V={dir:null,revision:0},ee=vy((he,ye)=>{Sy(P,he,ye,q,A,V)});await new Promise((he,ye)=>{ee.once("error",ye),ee.listen(0,"127.0.0.1",()=>{ee.off("error",ye),he()})});let ce=`http://127.0.0.1:${ee.address().port}`;return{origin:ce,token:q,url:`${ce}/?token=${encodeURIComponent(q)}`,close:()=>Iy(ee)}}async function Sy(P,A,q,V,ee,ne){let ce=P(),he=new URL(A.url??"/","http://127.0.0.1");if(A.method==="GET"&&he.pathname==="/favicon.ico"){q.writeHead(204,{"cache-control":"no-store"}),q.end();return}if(!Ay(A,he,V)){ht(q,403,{error:"Forbidden"});return}if(A.method==="GET"&&he.pathname==="/"){Ty(q,Gd(V));return}if(A.method==="GET"&&he.pathname==="/api/policy"){let ye=Gc(ce,ee),be=E(ce,Ni(ee));ht(q,200,{...ye,configState:Ne(be),...be.policyScopes?{projectPolicy:{path:b(ee.cwd??process.cwd()),weakenings:be.policyScopes.weakenings}}:{},destructiveCommandRules:G,secretPatterns:Ke,version:wt(),preview:ye.errors.length>0?null:Ae(ye.policy,ce.env)});return}if(A.method==="POST"&&he.pathname==="/api/policy/preview"){let ye=await tr(A);if(!ye.ok){ht(q,ye.status,{errors:[ye.error]});return}let be=Bc(ce,ye.value);ht(q,be.errors.length>0?400:200,be);return}if(A.method==="POST"&&he.pathname==="/api/policy/explain"){let ye=await tr(A);if(!ye.ok){ht(q,ye.status,{errors:[ye.error]});return}let be=ye.value;if(be===null||typeof be.command!=="string"){ht(q,400,{errors:["command must be a string"]});return}let xe=Yn(be.policy,ce.home);if(xe.length>0){ht(q,400,{errors:xe});return}ht(q,200,Ey(ce,be.command,be.policy,ee));return}if(A.method==="POST"&&he.pathname==="/api/policy"){let ye=await tr(A);if(!ye.ok){ht(q,ye.status,{errors:[ye.error]});return}let be=Vt(ce,ye.value,ee);ht(q,be.errors.length>0?400:200,be);return}if(A.method==="POST"&&he.pathname==="/api/reset"){ht(q,200,Vt(ce,X,ee));return}if(A.method==="POST"&&he.pathname==="/api/repair"){ht(q,200,qc(ce,ee));return}if(A.method==="POST"&&he.pathname==="/api/policy/project/choose-directory"){let ye=await(ee.chooseDirectory??Fi)();if("path"in ye)ne.dir=ye.path,ne.revision+=1;ht(q,200,{cancelled:"cancelled"in ye,..."error"in ye?{error:ye.error}:{}});return}if(A.method==="GET"&&he.pathname==="/api/policy/project"){let ye=Wd(ne,ee),be=Bd(ye,ce.home),xe=Xn(ce,ee);ht(q,200,{path:b(ye),revision:ne.revision,baseline:xe.baseline,userPolicyDiagnostics:xe.diagnostics,projection:be.projection,projectionDiagnostics:be.diagnostics,canPickDirectory:Oi(process.platform,process.env)});return}if(A.method==="POST"&&he.pathname==="/api/policy/project/diff"){let ye=await qd(ce,A,q,ne,ee);if(!ye)return;let be=Bd(ye.dir,ce.home),xe=Xn(ce,ee).baseline,Ie=Z(xe,ae(ye.proposal,ce.home).policy);ht(q,200,{rows:qr(Z(xe,be.projection).policy,Ie.policy,!1),weakenings:Ie.weakenings,existingFileDiagnostics:be.diagnostics});return}if(A.method==="POST"&&he.pathname==="/api/policy/project/apply"){let ye=await qd(ce,A,q,ne,ee);if(!ye)return;let be=Py(ye.dir,ye.proposal,ce.home);ht(q,be.errors.length>0?500:200,be);return}if(A.method==="GET"&&he.pathname==="/api/activity"){let ye=te(ce,ee),be=Dy(he.searchParams.get("days"),ye);if(be===null){ht(q,400,{error:`days must be an integer between 1 and ${ye}`});return}ht(q,200,Fd(ce,be,ee.activityLogsDir));return}if(A.method==="POST"&&he.pathname==="/api/rules/choose-directory"){ht(q,200,await Fi());return}if(A.method==="GET"&&he.pathname==="/api/rules"){let ye=Y(ce,Ni(ee)),be=new Map(ye.rules.map((xe)=>[xe.name,xe]));ht(q,200,{projectPath:ee.cwd??process.cwd(),canPickDirectory:Oi(process.platform,process.env),rulebooks:ye.rulebooks.map((xe)=>({source:xe.source,spec:xe.spec,name:xe.name,version:xe.version,rules:xe.rules.flatMap((Ie)=>{let Xe=be.get(Ie);if(!Xe)return[];return[{name:Xe.name,command:Xe.command,subcommand:Xe.subcommand,block_args:Xe.block_args,reason:Xe.reason}]})})),errors:ye.errors,warnings:ye.warnings});return}if(A.method==="GET"&&he.pathname==="/api/star/context"){ht(q,200,await(ee.fetchStarContext??(()=>Uy(ce,{logsDir:ee.activityLogsDir})))());return}if(A.method==="POST"&&he.pathname==="/api/star"){let ye=await(ee.starRepo??Fy)();ht(q,200,ye.ok?{ok:!0}:{ok:!1,fallbackUrl:Ly});return}if(A.method==="GET"&&he.pathname==="/api/integrations"){ht(q,200,await(ee.fetchIntegrations??(()=>Ny(ce)))());return}if(A.method==="GET"&&he.pathname==="/api/health"){ht(q,200,await(ee.fetchHealth??My)());return}if(A.method==="POST"&&(he.pathname==="/api/install"||he.pathname==="/api/uninstall")){let ye=await tr(A);if(!ye.ok){ht(q,ye.status,{errors:[ye.error]});return}let be=ye.value?.target;if(typeof be!=="string"||!Ht.some((Ie)=>Ie.target===be)){ht(q,400,{error:"unknown target"});return}let xe=he.pathname==="/api/install"?"install":"uninstall";ht(q,200,await(ee.runIntegration??Hy)(xe,be));return}ht(q,404,{error:"Not found"})}function Ni(P){return{...P,cwd:P.cwd??process.cwd()}}function Wd(P,A){return P.dir??A.cwd??process.cwd()}function Bd(P,A){let q=b(P),V=yy(q)?pn(q):{value:void 0,errors:[]},ee=ae(V.value,A);return{projection:ee.policy,diagnostics:[...V.errors,...ee.diagnostics]}}async function qd(P,A,q,V,ee){let ne=Wd(V,ee),ce=V.revision,he=await tr(A);if(!he.ok)return ht(q,he.status,{errors:[he.error]}),null;let ye=he.value;if(typeof ye?.revision!=="number")return ht(q,400,{errors:["revision must be a number"]}),null;if(ye.revision!==ce)return ht(q,409,{errors:[ky]}),null;let be=Ry(ye.proposal,P.home);if(be.length>0)return ht(q,400,{errors:be}),null;return{dir:ne,proposal:ye.proposal}}function Ry(P,A){let q=Yn(P,A);if(q.length>0)return q;return P?.audit===void 0?[]:[xy]}function Py(P,A,q){let V=b(P),ee=Vr(A,C(A,q));try{return g(i(v(P,"project policy"),V),`${JSON.stringify(ee,null,2)}
|
|
5929
|
+
`),{path:V,errors:[]}}catch(ne){return{path:V,errors:[ne instanceof Error?ne.message:String(ne)]}}}function Ey(P,A,q,V){let ee=C(q,P.home),ne=E(P,Ni(V)),ce=Ee({rules:ne.policy.rules,transparentWrappers:ne.policy.transparentWrappers,safety:De(ee.safety),worktreeMode:ee.workflow.worktree_mode,destructiveCommandProtectionEnabled:ee.destructive_command_protection.enabled,destructiveCommandRuleOverrides:ee.destructive_command_protection.overrides,destructiveCommandAllowPaths:ee.destructive_command_protection.allow_paths,secretProtection:{enabled:ee.secret_protection.enabled,disabledRules:Oe(ee.secret_protection.overrides),denyPaths:ee.secret_protection.deny_paths,allowPaths:ee.secret_protection.allow_paths}});return qn(A,{policySnapshot:ce,cwd:V.cwd,userConfigDir:V.userConfigDir},P)}function Dy(P,A){if(P===null)return Math.min(wy,A);let q=Number(P);if(!Number.isInteger(q)||q<1||q>A)return null;return q}function Ay(P,A,q){if(A.searchParams.get("token")!==q)return!1;if(P.method!=="POST")return!0;return P.headers["x-cc-safety-net-token"]===q}var _y=1048576;async function tr(P){let A=[],q=0;for await(let V of P){let ee=V;if(q+=ee.byteLength,q>_y)return{ok:!1,status:413,error:"Request body is too large"};A.push(ee)}try{return{ok:!0,value:JSON.parse(Buffer.concat(A).toString("utf-8")||"{}")}}catch(V){return{ok:!1,status:400,error:`Invalid JSON: ${V instanceof Error?V.message:String(V)}`}}}function Ty(P,A){P.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),P.end(A)}function ht(P,A,q){P.writeHead(A,{"content-type":"application/json; charset=utf-8","cache-control":"no-store"}),P.end(JSON.stringify(q))}function Iy(P){return new Promise((A,q)=>{P.close((V)=>V?q(V):A())})}function $y(P){return new Promise((A)=>{let q=()=>{process.off("SIGINT",V),process.off("SIGTERM",V)},V=()=>{q(),P.close().then(A)};process.once("SIGINT",V),process.once("SIGTERM",V)})}function Oy(P){let A=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",q=process.platform==="win32"?["/c","start","",P]:[P];return new Promise((V,ee)=>{let ne=zd(A,q,{detached:!0,stdio:"ignore"}),ce=(ye)=>{ne.off("spawn",he),ee(ye)},he=()=>{ne.off("error",ce),ne.unref(),V()};ne.once("error",ce),ne.once("spawn",he)})}async function Fy(P="gh",A=Mi){return{ok:await ji(P,["api","-X","PUT",`/user/starred/${oo}`],A)===0}}async function Ny(P,A={}){let q=await pr((ee)=>kn({environment:P,cwd:process.cwd(),openCodeVersion:ee}).status!=="n/a",A.fetcher),V=jy(P,q);return{targets:$t.map((ee)=>{let ne=V.find((ce)=>ce.platform===ee.id);return{target:ee.id,label:bt(ee.id),version:q.versions[ee.id]??null,status:ne?.configured?"active":ne?.detected?"disabled":ne?.inspectionStatus==="not-inspected"?"not-inspected":"not-installed"}}),system:{version:q.version,nodeVersion:q.nodeVersion,platform:q.platform}}}function jy(P,A){return xn(P,process.cwd(),{ampPluginListOutput:A.ampPluginListOutput,codexPluginListOutput:A.codexPluginListOutput,copilotCliVersion:A.versions["copilot-cli"],openCodeVersion:A.versions.opencode,openCodePluginListOutput:A.openCodePluginListOutput})}async function My(P={}){let A=await(P.checkUpdates??Wt)();return{update:{latestVersion:A.latestVersion??null,updateAvailable:A.updateAvailable}}}var Vd=Promise.resolve();function Hy(P,A,q={}){let V=async()=>{let ne=[],{log:ce,error:he}=console;console.log=(...ye)=>ne.push(ye.map(String).join(" ")),console.error=console.log;try{return{ok:await Kn(P,[],{selectTargets:async()=>[A],output:new by({write(be,xe,Ie){ne.push(String(be).replace(/\n$/,"")),Ie()}}),...q})===0,output:ne.join(`
|
|
5930
|
+
`)}}finally{console.log=ce,console.error=he}},ee=Vd.then(V);return Vd=ee.then(()=>{return},()=>{return}),ee}async function Uy(P,A={}){let[q,V,ee]=await Promise.all([Gy(A.command),By(A.fetchRepo),Promise.resolve(ar(P,te(P),A.logsDir).totalBlocked)]);return{starred:q,starCount:V,blockedTotal:ee}}async function Gy(P="gh",A=Mi){if(await ji(P,["auth","status"],A)!==0)return null;let q=await ji(P,["api",`/user/starred/${oo}`],A);if(q===0)return!0;if(q===null)return null;return!1}function ji(P,A,q){return new Promise((V)=>{let ee=zd(P,A,{stdio:"ignore",windowsHide:!0}),ne=!1,ce=setTimeout(()=>{ee.kill(),he(null)},q),he=(ye)=>{if(ne)return;ne=!0,clearTimeout(ce),V(ye)};ee.once("error",()=>he(null)),ee.once("close",he)})}async function By(P=fetch){try{let A=await P(`https://api.github.com/repos/${oo}`,{headers:{accept:"application/vnd.github+json"},signal:AbortSignal.timeout(Mi)});if(!A.ok)return null;let q=await A.json();return typeof q.stargazers_count==="number"?q.stargazers_count:null}catch{return null}}function qy(P){if(P[0]!=="help")return!1;let A=P[1];if(!A)di(),process.exit(0);if(Vn(A))process.exit(0);console.error(`Unknown command: ${A}`),console.error("Run 'cc-safety-net --help' for available commands."),process.exit(1)}var Vy={hook:async()=>{console.error("hook requires exactly one integration flag. Try: cc-safety-net hook --kimi-code"),Vn("hook",console.error),process.exit(1)},install:async(P)=>{process.exit(await Kn("install",P))},update:async(P)=>{process.exit(await xi(P))},uninstall:async(P)=>{process.exit(await Kn("uninstall",P))},rule:async(P)=>{process.exit(await Id(c(),P))},policy:async(P)=>{process.exit(await Wc(c(),P))},status:async(P)=>{if(Ut(xt({label:"status"},P).errors))process.exit(1);Od(c())},statusline:async(P)=>{let A=xt({label:"statusline",booleans:{claudeCode:["-cc","--claude-code"]}},P);if(A.errors.length===0&&A.flags.claudeCode){await $i(c());return}if(Ut(A.errors),!A.flags.claudeCode)console.error("statusline requires --claude-code (-cc)");Vn("statusline",console.error),process.exit(1)},doctor:async(P)=>{let A=ni(P);if(!A)process.exit(1);let q=await Ll(c(),{json:A.json,skipUpdateCheck:A.skipUpdateCheck});process.exit(q)},logs:async(P)=>{process.exit(await Ji(c(),P))},gui:async(P)=>{process.exit(await Jd(P))},explain:async(P)=>{process.exit(await _l(c(),P))}};async function zy(P){let A=xt({label:"cc-safety-net",booleans:{version:["-V","--version"]},positionals:"list"},P);if(qy(P))return;let q=P[0],V=q?sr(q):void 0;if(A.help&&V&&V.name!=="rule")Vn(V.name),process.exit(0);if(!q||A.help&&!V)di(),process.exit(0);if(A.flags.version)$l(),process.exit(0);if(V){await Vy[V.name](P.slice(1));return}if(q==="--statusline"){await $i(c());return}console.error(q.startsWith("-")?`Unknown option: ${q}`:`Unknown command: ${q}`),console.error("Run 'cc-safety-net --help' for usage."),process.exit(1)}export{zy as runCli};
|