cc-safety-net 2.6.1 → 2.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/amp/cc-safety-net/index.ts +2 -2
- package/dist/bin/hook.js +1 -1
- package/dist/chunks/{index-tyab2dd4.js → index-7r6br8vt.js} +1 -1
- package/dist/chunks/{index-k4yfpsk1.js → index-tef36kbd.js} +1 -1
- package/dist/cli.js +103 -103
- package/dist/deepseek-harness/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/openclaw/cc-safety-net/index.js +2 -2
- 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 +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import{a,s,Pe,Be,R,nt,ze,c,n,qe,T,v,pe,ot,f,Ke,u,o,S,i,se,r,g,me,H,st,at,d,ge,he,ct,N,Z,ae,w,lt,_,h,ke,W,l,L,X,Je,Ye,M,C,I,ye,be,Ae,ut,m,e,Oe,Re,Ze,Xe,ce,Te,le,De,Q,B,Ne,xe,z,Qe,q,ee,te,ue,et,tt,E,F,Le,Fe,$e,k,je,_e,Ue,y,t,ne,ve,b,x,A,dt,Ge,He,p,V}from"./chunks/index-mqmy0cce.js";import{oe,K,We,U,G}from"./chunks/index-
|
|
2
|
-
${
|
|
3
|
-
`).filter(Boolean).flatMap((D)=>{try{let j=JSON.parse(D);if(!
|
|
4
|
-
`)}function xs(P,O){let D=new Date(P);if(Number.isNaN(D.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:O}).format(D)}function hs(P){let O=Number(P);return Number.isFinite(O)&&O>0?O:null}var Cs={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 Ss={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 Ps={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 xo=["npx","--offline","--no-install","@deepseek-ai/dsh","--version"],pr=[{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:10,runtime:{order:8,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:10,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:9,runtime:{order:7,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:9,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:11,runtime:{order:9,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:11,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:12,runtime:{order:10,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:12,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:13,runtime:{order:11,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:13,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:14,install:{order:14,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:15,install:{order:15,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:16,install:{order:16,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:"deepseek-harness",displayName:"DeepSeek Harness",doctorOrder:6,install:{order:6,flag:"--deepseek-harness",artifactKind:"package",probeCommand:xo}},{id:"devin",displayName:"Devin CLI",doctorOrder:7,runtime:{order:5,flags:["-dv","--devin"],description:"Run as Devin CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:7,flag:"--devin",artifactKind:"hook config",probeCommand:["devin","--version"]}},{id:"droid",displayName:"Factory Droid",doctorOrder:8,runtime:{order:6,flags:["-fd","--droid"],description:"Run as Factory Droid PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--droid",artifactKind:"hook config",probeCommand:["droid","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],fr=pr.slice().sort((P,O)=>P.doctorOrder-O.doctorOrder).map((P)=>P.id),Ht=pr.filter((P)=>("runtime"in P)).slice().sort((P,O)=>P.runtime.order-O.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})),En=pr.slice().sort((P,O)=>P.install.order-O.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...O})=>O),Rp=Object.fromEntries(pr.map((P)=>[P.id,P.displayName]));function mn(P){return Rp[P]}var Ep=Ht.map((P)=>({flags:P.flags.join(", "),description:P.description})),_p=Ht.flatMap((P)=>P.flags.map((O)=>`cc-safety-net hook ${O}`)),Rs={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...Ep,{flags:"-h, --help",description:"Show this help"}],examples:_p};var Es={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...En.map((P)=>({flags:P.flag,description:`Install ${mn(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...En.map((P)=>`cc-safety-net install ${P.flag}`)]},_s={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...En.map((P)=>({flags:P.flag,description:`Uninstall ${mn(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...En.map((P)=>`cc-safety-net uninstall ${P.flag}`)]},Ts={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 As={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 mr={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 Co=[{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"}],So=["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"],xt={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"},...Co.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",...So,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var Is={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 $s={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 gr=[Is,Cs,As,Ss,xt,mr,Es,Ts,_s,Rs,Ps,$s];function Tp(P){return P.aliases??[]}function yr(P){let O=P.toLowerCase();return gr.find((D)=>D.name.toLowerCase()===O||Tp(D).some((j)=>j.toLowerCase()===O))}import{existsSync as cy}from"node:fs";import{basename as Ap}from"node:path";function hr(P,O=7,D=U(P)){let j=Date.now()-O*24*60*60*1000,J=[],Y=new Set,re=0,ie,de,fe,we;if(D)K(P,D);let Ce={count:0},Se=D?Wn(D,Ce):[];for(let Ee of Se)for(let Ve of kt(Ee,Ce)){if(Ve.decision==="allow")continue;let Me=new Date(Ve.ts).getTime();if(Me>=j){if(re++,Y.add(Ve.sessionId??Ap(Ee,".jsonl")),de===void 0||Me<=de)ie=Ve.ts,de=Me;if(we===void 0||Me>we)fe=Ve.ts,we=Me;Ip(J,Ve,Me)}}let Ie=J.map((Ee)=>({timestamp:Ee.ts,command:Ee.command,reason:Ee.reason,relativeTime:gs(new Date(Ee.ts))}));return{totalBlocked:re,sessionCount:Y.size,recentEntries:Ie,oldestEntry:ie,newestEntry:fe,unreadable:Ce.count}}function Ip(P,O,D){let j=P.findIndex((J)=>D>new Date(J.ts).getTime());if(j===-1){if(P.length<3)P.push(O);return}if(P.splice(j,0,O),P.length>3)P.pop()}import{dirname as qp}from"node:path";import{dirname as $p,join as Op,resolve as Dp}from"node:path";var Lp="config.json";function wn(P,O,D,j){g(Np(P),`${JSON.stringify(O,null,2)}
|
|
5
|
-
`,D,j)}function Np(P){return typeof P==="string"?se(P):P}function Ro(P){return{errors:ce(Fp(P),": "," "),ruleNames:new Set(De(P).map((O)=>O.toLowerCase()))}}var jp="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",Os="must match pattern (letters, numbers, hyphens, underscores)";function Fp(P){if(!Ds(P))return[e([],"Config must be an object")];return[...P.version===1?[]:[e(["version"],"must be 1")],...Hp(P.rules)]}function Hp(P){if(P===void 0)return[];if(!Array.isArray(P))return[e(["rules"],"must be an array")];return[...P.flatMap((O,D)=>Ds(O)?Mp(O,["rules",D]):[e(["rules",D],"must be an object")]),...Oe(P)]}function Mp(P,O){return[...Po(P.name,[...O,"name"],"required string",d,jp),...Po(P.command,[...O,"command"],"required string",C,Os),...P.subcommand===void 0?[]:Po(P.subcommand,[...O,"subcommand"],"must be a string if provided",C,Os),...Up(P.block_args,[...O,"block_args"]),...Gp(P.reason,[...O,"reason"]),...P.intent===void 0||Te(P.intent)?[]:[e([...O,"intent"],Re)]]}function Po(P,O,D,j,J){if(typeof P!=="string")return[e(O,D)];return j.test(P)?[]:[e(O,J)]}function Up(P,O){if(!Array.isArray(P))return[e(O,"required array")];if(P.length===0)return[e(O,"must have at least one element")];return P.flatMap((D,j)=>{if(typeof D!=="string")return[e([...O,j],"must be a string")];return D===""?[e([...O,j],"must not be empty")]:[]})}function Gp(P,O){if(typeof P!=="string")return[e(O,"required string")];if(P==="")return[e(O,"must not be empty")];return P.length>I?[e(O,`must be at most ${I} characters`)]:[]}function Ds(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function Eo(P){let O=Ls(P);if(!O.ok)return O.result;return Ro(O.parsed)}function Ls(P){let O=[],D=new Set;try{let j=typeof P==="string"?se(P):P,J=r(j);if(J===null)return O.push(`File not found: ${j.path}`),{ok:!1,result:{errors:O,ruleNames:D}};if(!J.trim())return O.push("Config file is empty"),{ok:!1,result:{errors:O,ruleNames:D}};return{ok:!0,parsed:JSON.parse(J)}}catch(j){if(j instanceof o)return O.push(j.message),{ok:!1,result:{errors:O,ruleNames:D}};let J=j instanceof Error?j.message:String(j);return O.push(j instanceof SyntaxError?"Invalid JSON":J),{ok:!1,result:{errors:O,ruleNames:D}}}}function vr(P){return Dp(P,".safety-net.json")}function Un(P){let O=Ls(P);if(!O.ok)return O.result;let D=Ze(O.parsed);return{errors:D.errors,ruleNames:D.sources}}function Ct(P,O={}){return Op($p(ke(P,O)),Lp)}function Ns(P,O,D){let j;try{if(r(O)===null)return{path:P,exists:!1,valid:!1,ruleCount:0};j=Un(O),j.errors.push(...B(P,D))}catch(J){if(!(J instanceof o))throw J;j={errors:[J.message],ruleNames:new Set}}return{path:P,exists:!0,valid:j.errors.length===0,ruleCount:j.ruleNames.size,...j.errors.length>0?{errors:j.errors}:{}}}function Bp(P,O){return{source:O,name:P.name,command:P.command,subcommand:P.subcommand,blockArgs:[...P.block_args],reason:P.reason}}function js(P,O){let D=W(P),j=_(O),J=qp(D),Y=Q(P,{cwd:O,userConfigPath:D,projectConfigPath:j,userConfigDir:J}),re=X(P,{cwd:O,userConfigPath:D,projectConfigPath:j,userConfigDir:J}),ie=new Map(Y.rulebooks.flatMap((de)=>de.rules.map((fe)=>[fe,de.source])));return{userConfig:Ns(D,re.userConfigTarget,re.userScope),projectConfig:Ns(j,re.projectConfigTarget,re.projectScope),effectiveRules:Y.rules.map((de)=>Bp(de,ie.get(de.name)??"project"))}}var Vp=[{flag:n.level,description:"Safety level preset: standard, strict, or paranoid",defaultBehavior:"standard"},{flag:n.strict,description:"Legacy; equivalent to safety.overrides.fail_closed",defaultBehavior:"permissive"},{flag:n.paranoid,description:"Legacy; equivalent to safety.overrides.paranoid_rm and paranoid_interpreters",defaultBehavior:"off"},{flag:n.paranoidRm,description:"Legacy; equivalent to safety.overrides.paranoid_rm",defaultBehavior:"off"},{flag:n.paranoidInterpreters,description:"Legacy; equivalent to safety.overrides.paranoid_interpreters",defaultBehavior:"off"},{flag:n.worktree,description:"Allow local git discards in linked worktrees",defaultBehavior:"off"},{flag:n.debug,description:"Print diagnostic messages to stderr",defaultBehavior:"off"},{flag:n.auditScope,description:"Command decisions recorded: all, or blocked (privacy-minimizing, denials only)",defaultBehavior:"all"},{flag:n.projectTightenOnly,description:"Ignore project policy settings that weaken the user policy",defaultBehavior:"off"}];function Fs(P){return[...Vp.map((O)=>({name:O.flag.name,value:pe(O.flag,P.env),isSet:ot(O.flag,P.env),legacyName:O.flag.legacyName,legacyValue:O.flag.legacyName?P.env.get(O.flag.legacyName):void 0,legacyIsSet:O.flag.legacyName?P.env.get(O.flag.legacyName)!==void 0:void 0,description:O.description,defaultBehavior:O.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 Hs={error:0,warning:1,info:2},Jp=["policy","config","audit"];function Wp(P){return P.map((O)=>{if(O==="ownership")return"is not owned by the current user";if(O==="permissions")return"has unsafe permissions";if(O==="symlink")return"is a symbolic link";return"is not a directory"}).join(" and ")}var Kp=[{derive:(P)=>P.hooks.length>0&&P.hooks.every((O)=>!O.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((O)=>O.inspectionStatus==="failed").map((O)=>{let D=mn(O.platform);return{checkId:"integration.inspection-failed",severity:"error",title:`${D} inspection failed`,detail:`Doctor could not verify the ${D} integration configuration.`,fixHint:`Correct the reported ${D} configuration error, then run \`cc-safety-net doctor\` again.`,integration:O.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)=>P.legacyConfigs&&P.legacyConfigs.length>0?[{checkId:"config.legacy-ignored",severity:"warning",title:"Legacy inline rule configs are ignored",detail:`CC Safety Net no longer loads these files, so their rules enforce nothing: ${P.legacyConfigs.join(", ")}.`,fixHint:"Run `cc-safety-net rule migrate` to convert them (add `--cleanup` to delete each file once it is converted), then rerun doctor."}]:[]},{derive:(P)=>{let O=P.environment.find((D)=>D.name==="CC_SAFETY_NET_AUDIT_SCOPE");return qe(O?.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."}]:[]}},...Jp.map((P)=>({derive:(O)=>O.posture.directories.filter((D)=>D.kind===P&&D.status==="unsafe").map((D)=>({checkId:`posture.${P}-directory-unsafe`,severity:"error",title:`${P[0]?.toUpperCase()}${P.slice(1)} directory is unsafe`,detail:`The ${P} directory ${Wp(D.issues)}.`,fixHint:"Ensure this is a real directory owned by the current user with no group or other write access, then rerun doctor.",...D.path?{path:D.path}:{}}))})),{derive:(P)=>{let O=[...P.effectiveSafety.weakenedRuleOverrides].sort();return O.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: ${O.join(", ")}.`,fixHint:`Remove these \`off\` overrides or set them to \`on\`: ${O.join(", ")}.`}]:[]}}];function Ms(P){return Kp.flatMap((O,D)=>O.derive(P).map((j,J)=>({finding:j,catalogOrder:D,occurrence:J}))).sort((O,D)=>Hs[O.finding.severity]-Hs[D.finding.severity]||O.catalogOrder-D.catalogOrder||O.occurrence-D.occurrence).map((O)=>O.finding)}function Ln(){return Boolean(process.stdout.isTTY&&!process.env.NO_COLOR)}var zp=(P)=>Ln()?`\x1B[32m${P}\x1B[0m`:P,Yp=(P)=>Ln()?`\x1B[33m${P}\x1B[0m`:P,Zp=(P)=>Ln()?`\x1B[34m${P}\x1B[0m`:P,Xp=(P)=>Ln()?`\x1B[36m${P}\x1B[0m`:P,Qp=(P)=>Ln()?`\x1B[31m${P}\x1B[0m`:P,ef=(P)=>Ln()?`\x1B[2m${P}\x1B[0m`:P,nf=(P)=>Ln()?`\x1B[1m${P}\x1B[0m`:P,nn={green:zp,yellow:Yp,blue:Zp,cyan:Xp,red:Qp,dim:ef,bold:nf},tf="\x1B[0m",rf=[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 of(P){let O=P;return()=>(O=(O*1664525+1013904223)%4294967296,O/4294967296)}function sf(P){let O=[...rf],D=of(P);for(let j=O.length-1;j>0;j--){let J=Math.floor(D()*(j+1)),Y=O[j];O[j]=O[J],O[J]=Y}return O}function af(P,O=0){if(!Ln())return"";let D=sf(O);return`\x1B[38;5;${D[P%D.length]}m`}function Us(P,O,D=0){if(!Ln())return`"${P}"`;return`${af(O,D)}"${P}"${tf}`}function br(P){return P==="default"?"built-in default":`${P} policy`}var lf=new RegExp("\x1B\\[[0-9;]*m","g"),_o=(P)=>P.replace(lf,"").length;function Kn(P){let O=(P.headers??P.rows[0]??[]).map((re,ie)=>{let de=Math.max(...P.rows.map((fe)=>_o(fe[ie]??"")));return Math.max(_o(re),de)}),D=(re,ie)=>re+" ".repeat(Math.max(0,ie-_o(re))),j=(re,ie)=>ie[0]+O.map((de)=>re.repeat(de+2)).join(ie[1])+ie[2],J=(re)=>`│ ${re.map((ie,de)=>D(ie,O[de]??0)).join(" │ ")} │`,Y=P.headers?[` ${J(P.headers)}`,` ${j("─",["├","┼","┤"])}`]:[];return[` ${j("─",["┌","┬","┐"])}`,...Y,...P.rows.map((re)=>` ${J(re)}`),` ${j("─",["└","┴","┘"])}`].join(`
|
|
6
|
-
`)}function
|
|
7
|
-
`)}function
|
|
8
|
-
`)}function
|
|
9
|
-
`)}function
|
|
10
|
-
`)}function
|
|
11
|
-
`)}function
|
|
1
|
+
import{a,s,Pe,Be,R,nt,ze,c,n,qe,T,v,pe,ot,f,Ke,u,o,S,i,se,r,g,me,H,st,at,d,ge,he,ct,N,Z,ae,w,lt,_,h,ke,W,l,L,X,Je,Ye,M,C,I,ye,be,Ae,ut,m,e,Oe,Re,Ze,Xe,ce,Te,le,De,Q,B,Ne,xe,z,Qe,q,ee,te,ue,et,tt,E,F,Le,Fe,$e,k,je,_e,Ue,y,t,ne,ve,b,x,A,dt,Ge,He,p,V}from"./chunks/index-mqmy0cce.js";import{oe,K,We,U,G}from"./chunks/index-tef36kbd.js";var tp=["-h","--help"];function gn(P,O){let D=Object.entries(P.booleans??{}),j=Object.entries(P.values??{}),J=Object.entries(P.lists??{}),Y=Object.fromEntries(D.map(([Se])=>[Se,!1])),re={},ie=Object.fromEntries(J.map(([Se])=>[Se,[]])),de=[],fe=[],we=!1,Ce=-1;for(let[Se,Ie]of O.entries()){if(Se<=Ce)continue;if(Ie==="--"){de.push(...O.slice(Se+1));break}if(tp.includes(Ie)){we=!0;continue}let Ee=D.find(([,en])=>en.includes(Ie));if(Ee){Y[Ee[0]]=!0;continue}let Ve=j.find(([,en])=>en.includes(Ie));if(Ve){let en=O[Se+1];if(en===void 0||en.startsWith("-")){fe.push(`${Ie} requires a value`);continue}re[Ve[0]]=en,Ce=Se+1;continue}let Me=J.find(([,en])=>en.includes(Ie));if(Me){let en=O.slice(Se+1),on=en.findIndex((an)=>an.startsWith("-")),sn=en.slice(0,on===-1?en.length:on);if(sn.length===0){fe.push(`${Ie} requires at least one value`);continue}ie[Me[0]]=[...ie[Me[0]]??[],...sn],Ce=Se+sn.length;continue}if(Ie.startsWith("-")){fe.push(`Unknown option for ${P.label}: ${Ie}`);continue}if(P.positionals==="tail"){de.push(...O.slice(Se));break}de.push(Ie)}if(P.positionals!=="list"&&P.positionals!=="tail")fe.push(...de.map((Se)=>`Unexpected argument for ${P.label}: ${Se}`));return{flags:Y,values:re,lists:ie,positionals:de,help:we,errors:fe}}function Dn(P){for(let O of P)console.error(O);return P.length>0}import{readdirSync as cp,statSync as ws,unlinkSync as dp}from"node:fs";import{basename as ks,dirname as up,isAbsolute as pp,join as fp,relative as mp,resolve as gp,sep as yp}from"node:path";var hs=(P)=>{let O=Date.now()-new Date(P).getTime();if(!Number.isFinite(O))return"";let D=Math.floor(O/60000),j=Math.floor(D/60),J=Math.floor(j/24);if(J>0)return`${J}d ago`;if(j>0)return`${j}h ago`;if(D>0)return`${D}m ago`;return"just now"},Po=(P)=>{let O=(P??"").trim().split(/\s+/).filter((J)=>J&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(J)),D=O[0]?.split("/").pop();if(!D)return null;let j=O[1];return j&&/^[a-z][a-z0-9-]*$/.test(j)?`${D} ${j}`:D};function vs(P){let O=(J)=>`${J.sessionId}
|
|
2
|
+
${Po(J.segment||J.command)}`,D=P.filter((J)=>J.decision!=="allow"),j=D.filter((J)=>J.sessionId).reduce((J,Y)=>J.set(O(Y),(J.get(O(Y))??0)+1),new Map);return new Set(D.filter((J)=>J.failureStage||(j.get(O(J))??0)>=2))}import{existsSync as rp,readdirSync as op,readFileSync as ip}from"node:fs";import{join as sp}from"node:path";function Wn(P,O){try{return op(P,{withFileTypes:!0,encoding:"utf8"}).flatMap((D)=>{let j=sp(P,D.name);if(D.isDirectory())return Wn(j,O);if(D.name.endsWith(".jsonl"))return[j];return[]})}catch{if(O&&rp(P))O.count++;return[]}}var ap=["segment","reason","sessionId","decision","agent","ruleId","failureStage"];function lp(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let O=P;if(typeof O.ts!=="string"||typeof O.command!=="string")return!1;return ap.every((D)=>O[D]===void 0||typeof O[D]==="string")}function kt(P,O){try{return ip(P,"utf-8").split(`
|
|
3
|
+
`).filter(Boolean).flatMap((D)=>{try{let j=JSON.parse(D);if(!lp(j)){if(O)O.count++;return[]}return[j]}catch{if(O)O.count++;return[]}})}catch{if(O)O.count++;return[]}}function yn(P){return Array.from(P,(O)=>{let D=O.charCodeAt(0);if(D<=31||D>=127&&D<=159)return`\\x${D.toString(16).padStart(2,"0")}`;return O}).join("")}function hp(P,O){let D=oe(P),j=gn({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"]}},O);if(Dn(j.errors))return null;if(j.values.id!==void 0&&!/^[a-f0-9]{16}$/.test(j.values.id))return console.error("--id must be 16 hexadecimal characters"),null;let J=j.values.limit===void 0?20:bs(j.values.limit);if(J===null)return console.error("--limit must be a positive number"),null;let Y=j.values.since===void 0?Math.min(30,D):bs(j.values.since);if(Y===null||Y>D)return console.error(`--since must be a positive number of days no greater than ${D}`),null;let re={limit:J,limitExplicit:j.values.limit!==void 0,since:Y,sinceExplicit:j.values.since!==void 0,all:j.flags.all,json:j.flags.json,suspect:j.flags.suspect,pruneLegacy:j.flags.pruneLegacy,dryRun:j.flags.dryRun,id:j.values.id,agent:j.values.agent,rule:j.values.rule,session:j.values.session,project:j.values.project===void 0?void 0:gp(j.values.project)};if(re.id&&(re.agent!==void 0||re.rule!==void 0||re.session!==void 0||re.project!==void 0||re.suspect||re.sinceExplicit||re.limitExplicit))return console.error("--id cannot be combined with --agent, --rule, --session, --project, --suspect, --since, or --limit"),null;if(re.pruneLegacy&&(re.id!==void 0||re.agent!==void 0||re.rule!==void 0||re.session!==void 0||re.project!==void 0||re.suspect||re.all||re.sinceExplicit||re.limitExplicit))return console.error("--prune-legacy cannot be combined with --id, --agent, --rule, --session, --project, --suspect, --all, --since, or --limit"),null;if(re.dryRun&&!re.pruneLegacy)return console.error("--dry-run requires --prune-legacy"),null;return re}async function xs(P,O,D={}){let j=hp(P,O);if(!j)return 1;let J=D.logsDir??U(P);if(j.pruneLegacy)return vp(J,j.json,j.dryRun);if(!J)return console.log(j.json?"[]":j.id?`No retained audit log entry found for id ${yn(j.id)}.`:"No audit log entries found."),0;K(P,J);let Y={count:0},re=Wn(J,Y).flatMap((Ce)=>kt(Ce,Y).map((Se)=>({entry:Se,file:Ce})));if(Y.count>0)console.error(`warning: ${Y.count} audit log ${Y.count===1?"source":"sources"} could not be read; these results are incomplete`);if(j.id)return xp(re,j,D.timeZone);let ie=Date.now()-j.since*24*60*60*1000,de=re.filter((Ce)=>Cp(Ce,j,J,ie)),fe=j.suspect?vs(de.map((Ce)=>Ce.entry)):null,we=(fe?de.filter((Ce)=>fe.has(Ce.entry)):de).sort((Ce,Se)=>Date.parse(Se.entry.ts)-Date.parse(Ce.entry.ts)).slice(0,j.limit);if(j.json)return console.log(JSON.stringify(we.map((Ce)=>Ce.entry),null,2)),0;if(we.length===0)return console.log("No audit log entries found."),0;for(let Ce of we)console.log(Rp(Ce.entry,D.timeZone));return 0}function vp(P,O,D){let j=P?wp(P).map((ie)=>fp(P,ie)):[];if(D)return bp(j,O);let J=[],Y=0,re=0;for(let ie of j){let de=ws(ie,{throwIfNoEntry:!1})?.size??0,fe=kp(ie);if(fe){J.push(`${ks(ie)}: ${fe}`);continue}Y++,re+=de}if(O)return console.log(JSON.stringify({removedFiles:Y,removedBytes:re,failedFiles:J.length})),J.length===0?0:1;console.log(Y===0&&J.length===0?"No legacy audit log files found.":`Removed ${Y} legacy audit log ${Y===1?"file":"files"} (${Cs(re)}).`);for(let ie of J)console.error(`Could not remove ${yn(ie)}`);if(console.log("Nested v2 audit logs were not changed."),Y>0)console.log("This deletion cannot be undone.");return J.length===0?0:1}function bp(P,O){let D=P.reduce((j,J)=>j+(ws(J,{throwIfNoEntry:!1})?.size??0),0);if(O)return console.log(JSON.stringify({dryRun:!0,files:P.length,bytes:D})),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"} (${Cs(D)}).`),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 wp(P){try{return cp(P,{withFileTypes:!0}).filter((O)=>O.isFile()&&O.name.endsWith(".jsonl")).map((O)=>O.name)}catch{return[]}}function kp(P){try{return dp(P),null}catch(O){return O instanceof Error?O.message:String(O)}}function Cs(P){let O=["B","KiB","MiB","GiB"],D=Math.min(Math.floor(Math.log2(Math.max(P,1))/10),O.length-1);return`${Math.round(P/1024**D*10)/10} ${O[D]}`}function xp(P,O,D){let j=P.filter((Y)=>Y.entry.id===O.id);if(j.length>1)return console.error(`Multiple audit log entries found for id ${yn(O.id??"")}.`),1;if(O.json)return console.log(JSON.stringify(j.map((Y)=>Y.entry),null,2)),0;let J=j[0];if(!J)return console.log(`No retained audit log entry found for id ${yn(O.id??"")}.`),0;return console.log(Ep(J.entry,D)),0}function Cp(P,O,D,j){if(!O.all&&P.entry.decision==="allow")return!1;if(Date.parse(P.entry.ts)<j)return!1;if(O.agent!==void 0&&P.entry.agent!==O.agent)return!1;if(O.rule!==void 0&&P.entry.ruleId!==O.rule)return!1;if(O.session!==void 0&&!Sp(P,D,O.session))return!1;if(O.project!==void 0&&!Pp(P.entry.cwd,O.project))return!1;return!0}function Sp(P,O,D){if(P.entry.sessionId===D)return!0;return up(P.file)===O&&ks(P.file,".jsonl")===D}function Pp(P,O){if(!P)return!1;let D=mp(O,P);return D!==".."&&!D.startsWith(`..${yp}`)&&!pp(D)}function Rp(P,O){let D=yn(P.id??"-"),j=yn(P.decision??"deny"),J=P.cwd?` [${yn(P.cwd)}]`:"",Y=P.segment||P.command,re=Y===P.command?"":"↳ ",ie=Y.length>50?`${Y.slice(0,50)}…`:Y;return`${D.padEnd(16)} ${yn(Ss(P.ts,O))} ${j.padEnd(5)} ${yn(P.agent??"-").padEnd(15)} ${yn(P.ruleId??"-").padEnd(20)} ${re}${yn(ie)}${J}`}function Ep(P,O){let D=(J)=>yn(J===void 0||J===null||J===""?"-":J),j=P.shape?`${P.agent??"-"} (shape: ${P.shape})`:P.agent??"-";return[`id: ${D(P.id)}`,`ts: ${D(Ss(P.ts,O))}`,`decision: ${D(P.decision)}`,`agent: ${D(j)}`,`level: ${D(P.level)}`,`tool: ${D(P.toolName)}`,`rule: ${D(P.ruleId)}`,`intent: ${D(P.intent)}`,`stage: ${D(P.failureStage)}`,`error: ${D(P.errorCode)}`,`session: ${D(P.sessionId)}`,`cwd: ${D(P.cwd)}`,`version: ${D(P.v)}`,`truncated: ${D(P.truncated===!0?"yes":void 0)}`,`reason: ${D(P.reason)}`,`command: ${D(P.command)}`,`segment: ${D(P.segment)}`].join(`
|
|
4
|
+
`)}function Ss(P,O){let D=new Date(P);if(Number.isNaN(D.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:O}).format(D)}function bs(P){let O=Number(P);return Number.isFinite(O)&&O>0?O:null}var Ps={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 Rs={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 Es={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 Ro=["npx","--offline","--no-install","@deepseek-ai/dsh","--version"],fr=[{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:10,runtime:{order:8,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:10,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:9,runtime:{order:7,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:9,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:11,runtime:{order:9,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:11,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:12,runtime:{order:10,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:12,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:13,runtime:{order:11,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:13,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:14,install:{order:14,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:15,install:{order:15,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:16,install:{order:16,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:"deepseek-harness",displayName:"DeepSeek Harness",doctorOrder:6,install:{order:6,flag:"--deepseek-harness",artifactKind:"package",probeCommand:Ro}},{id:"devin",displayName:"Devin CLI",doctorOrder:7,runtime:{order:5,flags:["-dv","--devin"],description:"Run as Devin CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:7,flag:"--devin",artifactKind:"hook config",probeCommand:["devin","--version"]}},{id:"droid",displayName:"Factory Droid",doctorOrder:8,runtime:{order:6,flags:["-fd","--droid"],description:"Run as Factory Droid PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--droid",artifactKind:"hook config",probeCommand:["droid","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],mr=fr.slice().sort((P,O)=>P.doctorOrder-O.doctorOrder).map((P)=>P.id),Ht=fr.filter((P)=>("runtime"in P)).slice().sort((P,O)=>P.runtime.order-O.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})),En=fr.slice().sort((P,O)=>P.install.order-O.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...O})=>O),_p=Object.fromEntries(fr.map((P)=>[P.id,P.displayName]));function mn(P){return _p[P]}var Ap=Ht.map((P)=>({flags:P.flags.join(", "),description:P.description})),Tp=Ht.flatMap((P)=>P.flags.map((O)=>`cc-safety-net hook ${O}`)),_s={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...Ap,{flags:"-h, --help",description:"Show this help"}],examples:Tp};var As={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...En.map((P)=>({flags:P.flag,description:`Install ${mn(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...En.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:[...En.map((P)=>({flags:P.flag,description:`Uninstall ${mn(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...En.map((P)=>`cc-safety-net uninstall ${P.flag}`)]},Is={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 $s={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 gr={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"}],_o=["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"],xt={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",..._o,"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 Ds={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 yr=[Os,Ps,$s,Rs,xt,gr,As,Is,Ts,_s,Es,Ds];function Ip(P){return P.aliases??[]}function hr(P){let O=P.toLowerCase();return yr.find((D)=>D.name.toLowerCase()===O||Ip(D).some((j)=>j.toLowerCase()===O))}import{existsSync as fy}from"node:fs";import{basename as $p}from"node:path";function vr(P,O=7,D=U(P)){let j=Date.now()-O*24*60*60*1000,J=[],Y=new Set,re=0,ie,de,fe,we;if(D)K(P,D);let Ce={count:0},Se=D?Wn(D,Ce):[];for(let Ee of Se)for(let Ve of kt(Ee,Ce)){if(Ve.decision==="allow")continue;let Me=new Date(Ve.ts).getTime();if(Me>=j){if(re++,Y.add(Ve.sessionId??$p(Ee,".jsonl")),de===void 0||Me<=de)ie=Ve.ts,de=Me;if(we===void 0||Me>we)fe=Ve.ts,we=Me;Op(J,Ve,Me)}}let Ie=J.map((Ee)=>({timestamp:Ee.ts,command:Ee.command,reason:Ee.reason,relativeTime:hs(new Date(Ee.ts))}));return{totalBlocked:re,sessionCount:Y.size,recentEntries:Ie,oldestEntry:ie,newestEntry:fe,unreadable:Ce.count}}function Op(P,O,D){let j=P.findIndex((J)=>D>new Date(J.ts).getTime());if(j===-1){if(P.length<3)P.push(O);return}if(P.splice(j,0,O),P.length>3)P.pop()}import{dirname as Vp}from"node:path";import{dirname as Dp,join as Lp,resolve as Np}from"node:path";var jp="config.json";function wn(P,O,D,j){g(Fp(P),`${JSON.stringify(O,null,2)}
|
|
5
|
+
`,D,j)}function Fp(P){return typeof P==="string"?se(P):P}function To(P){return{errors:ce(Mp(P),": "," "),ruleNames:new Set(De(P).map((O)=>O.toLowerCase()))}}var Hp="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",Ls="must match pattern (letters, numbers, hyphens, underscores)";function Mp(P){if(!Ns(P))return[e([],"Config must be an object")];return[...P.version===1?[]:[e(["version"],"must be 1")],...Up(P.rules)]}function Up(P){if(P===void 0)return[];if(!Array.isArray(P))return[e(["rules"],"must be an array")];return[...P.flatMap((O,D)=>Ns(O)?Gp(O,["rules",D]):[e(["rules",D],"must be an object")]),...Oe(P)]}function Gp(P,O){return[...Ao(P.name,[...O,"name"],"required string",d,Hp),...Ao(P.command,[...O,"command"],"required string",C,Ls),...P.subcommand===void 0?[]:Ao(P.subcommand,[...O,"subcommand"],"must be a string if provided",C,Ls),...qp(P.block_args,[...O,"block_args"]),...Bp(P.reason,[...O,"reason"]),...P.intent===void 0||Te(P.intent)?[]:[e([...O,"intent"],Re)]]}function Ao(P,O,D,j,J){if(typeof P!=="string")return[e(O,D)];return j.test(P)?[]:[e(O,J)]}function qp(P,O){if(!Array.isArray(P))return[e(O,"required array")];if(P.length===0)return[e(O,"must have at least one element")];return P.flatMap((D,j)=>{if(typeof D!=="string")return[e([...O,j],"must be a string")];return D===""?[e([...O,j],"must not be empty")]:[]})}function Bp(P,O){if(typeof P!=="string")return[e(O,"required string")];if(P==="")return[e(O,"must not be empty")];return P.length>I?[e(O,`must be at most ${I} characters`)]:[]}function Ns(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function Io(P){let O=js(P);if(!O.ok)return O.result;return To(O.parsed)}function js(P){let O=[],D=new Set;try{let j=typeof P==="string"?se(P):P,J=r(j);if(J===null)return O.push(`File not found: ${j.path}`),{ok:!1,result:{errors:O,ruleNames:D}};if(!J.trim())return O.push("Config file is empty"),{ok:!1,result:{errors:O,ruleNames:D}};return{ok:!0,parsed:JSON.parse(J)}}catch(j){if(j instanceof o)return O.push(j.message),{ok:!1,result:{errors:O,ruleNames:D}};let J=j instanceof Error?j.message:String(j);return O.push(j instanceof SyntaxError?"Invalid JSON":J),{ok:!1,result:{errors:O,ruleNames:D}}}}function br(P){return Np(P,".safety-net.json")}function Un(P){let O=js(P);if(!O.ok)return O.result;let D=Ze(O.parsed);return{errors:D.errors,ruleNames:D.sources}}function Ct(P,O={}){return Lp(Dp(ke(P,O)),jp)}function Fs(P,O,D){let j;try{if(r(O)===null)return{path:P,exists:!1,valid:!1,ruleCount:0};j=Un(O),j.errors.push(...B(P,D))}catch(J){if(!(J instanceof o))throw J;j={errors:[J.message],ruleNames:new Set}}return{path:P,exists:!0,valid:j.errors.length===0,ruleCount:j.ruleNames.size,...j.errors.length>0?{errors:j.errors}:{}}}function Jp(P,O){return{source:O,name:P.name,command:P.command,subcommand:P.subcommand,blockArgs:[...P.block_args],reason:P.reason}}function Hs(P,O){let D=W(P),j=_(O),J=Vp(D),Y=Q(P,{cwd:O,userConfigPath:D,projectConfigPath:j,userConfigDir:J}),re=X(P,{cwd:O,userConfigPath:D,projectConfigPath:j,userConfigDir:J}),ie=new Map(Y.rulebooks.flatMap((de)=>de.rules.map((fe)=>[fe,de.source])));return{userConfig:Fs(D,re.userConfigTarget,re.userScope),projectConfig:Fs(j,re.projectConfigTarget,re.projectScope),effectiveRules:Y.rules.map((de)=>Jp(de,ie.get(de.name)??"project"))}}var Wp=[{flag:n.level,description:"Safety level preset: standard, strict, or paranoid",defaultBehavior:"standard"},{flag:n.strict,description:"Legacy; equivalent to safety.overrides.fail_closed",defaultBehavior:"permissive"},{flag:n.paranoid,description:"Legacy; equivalent to safety.overrides.paranoid_rm and paranoid_interpreters",defaultBehavior:"off"},{flag:n.paranoidRm,description:"Legacy; equivalent to safety.overrides.paranoid_rm",defaultBehavior:"off"},{flag:n.paranoidInterpreters,description:"Legacy; equivalent to safety.overrides.paranoid_interpreters",defaultBehavior:"off"},{flag:n.worktree,description:"Allow local git discards in linked worktrees",defaultBehavior:"off"},{flag:n.debug,description:"Print diagnostic messages to stderr",defaultBehavior:"off"},{flag:n.auditScope,description:"Command decisions recorded: all, or blocked (privacy-minimizing, denials only)",defaultBehavior:"all"},{flag:n.projectTightenOnly,description:"Ignore project policy settings that weaken the user policy",defaultBehavior:"off"}];function Ms(P){return[...Wp.map((O)=>({name:O.flag.name,value:pe(O.flag,P.env),isSet:ot(O.flag,P.env),legacyName:O.flag.legacyName,legacyValue:O.flag.legacyName?P.env.get(O.flag.legacyName):void 0,legacyIsSet:O.flag.legacyName?P.env.get(O.flag.legacyName)!==void 0:void 0,description:O.description,defaultBehavior:O.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 Us={error:0,warning:1,info:2},Kp=["policy","config","audit"];function zp(P){return P.map((O)=>{if(O==="ownership")return"is not owned by the current user";if(O==="permissions")return"has unsafe permissions";if(O==="symlink")return"is a symbolic link";return"is not a directory"}).join(" and ")}var Yp=[{derive:(P)=>P.hooks.length>0&&P.hooks.every((O)=>!O.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((O)=>O.inspectionStatus==="failed").map((O)=>{let D=mn(O.platform);return{checkId:"integration.inspection-failed",severity:"error",title:`${D} inspection failed`,detail:`Doctor could not verify the ${D} integration configuration.`,fixHint:`Correct the reported ${D} configuration error, then run \`cc-safety-net doctor\` again.`,integration:O.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)=>P.legacyConfigs&&P.legacyConfigs.length>0?[{checkId:"config.legacy-ignored",severity:"warning",title:"Legacy inline rule configs are ignored",detail:`CC Safety Net no longer loads these files, so their rules enforce nothing: ${P.legacyConfigs.join(", ")}.`,fixHint:"Run `cc-safety-net rule migrate` to convert them (add `--cleanup` to delete each file once it is converted), then rerun doctor."}]:[]},{derive:(P)=>{let O=P.environment.find((D)=>D.name==="CC_SAFETY_NET_AUDIT_SCOPE");return qe(O?.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."}]:[]}},...Kp.map((P)=>({derive:(O)=>O.posture.directories.filter((D)=>D.kind===P&&D.status==="unsafe").map((D)=>({checkId:`posture.${P}-directory-unsafe`,severity:"error",title:`${P[0]?.toUpperCase()}${P.slice(1)} directory is unsafe`,detail:`The ${P} directory ${zp(D.issues)}.`,fixHint:"Ensure this is a real directory owned by the current user with no group or other write access, then rerun doctor.",...D.path?{path:D.path}:{}}))})),{derive:(P)=>{let O=[...P.effectiveSafety.weakenedRuleOverrides].sort();return O.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: ${O.join(", ")}.`,fixHint:`Remove these \`off\` overrides or set them to \`on\`: ${O.join(", ")}.`}]:[]}}];function Gs(P){return Yp.flatMap((O,D)=>O.derive(P).map((j,J)=>({finding:j,catalogOrder:D,occurrence:J}))).sort((O,D)=>Us[O.finding.severity]-Us[D.finding.severity]||O.catalogOrder-D.catalogOrder||O.occurrence-D.occurrence).map((O)=>O.finding)}function Ln(){return Boolean(process.stdout.isTTY&&!process.env.NO_COLOR)}var Zp=(P)=>Ln()?`\x1B[32m${P}\x1B[0m`:P,Xp=(P)=>Ln()?`\x1B[33m${P}\x1B[0m`:P,Qp=(P)=>Ln()?`\x1B[34m${P}\x1B[0m`:P,ef=(P)=>Ln()?`\x1B[36m${P}\x1B[0m`:P,nf=(P)=>Ln()?`\x1B[31m${P}\x1B[0m`:P,tf=(P)=>Ln()?`\x1B[2m${P}\x1B[0m`:P,rf=(P)=>Ln()?`\x1B[1m${P}\x1B[0m`:P,nn={green:Zp,yellow:Xp,blue:Qp,cyan:ef,red:nf,dim:tf,bold:rf},of="\x1B[0m",sf=[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 af(P){let O=P;return()=>(O=(O*1664525+1013904223)%4294967296,O/4294967296)}function lf(P){let O=[...sf],D=af(P);for(let j=O.length-1;j>0;j--){let J=Math.floor(D()*(j+1)),Y=O[j];O[j]=O[J],O[J]=Y}return O}function cf(P,O=0){if(!Ln())return"";let D=lf(O);return`\x1B[38;5;${D[P%D.length]}m`}function qs(P,O,D=0){if(!Ln())return`"${P}"`;return`${cf(O,D)}"${P}"${of}`}function wr(P){return P==="default"?"built-in default":`${P} policy`}var df=new RegExp("\x1B\\[[0-9;]*m","g"),$o=(P)=>P.replace(df,"").length;function Kn(P){let O=(P.headers??P.rows[0]??[]).map((re,ie)=>{let de=Math.max(...P.rows.map((fe)=>$o(fe[ie]??"")));return Math.max($o(re),de)}),D=(re,ie)=>re+" ".repeat(Math.max(0,ie-$o(re))),j=(re,ie)=>ie[0]+O.map((de)=>re.repeat(de+2)).join(ie[1])+ie[2],J=(re)=>`│ ${re.map((ie,de)=>D(ie,O[de]??0)).join(" │ ")} │`,Y=P.headers?[` ${J(P.headers)}`,` ${j("─",["├","┼","┤"])}`]:[];return[` ${j("─",["┌","┬","┐"])}`,...Y,...P.rows.map((re)=>` ${J(re)}`),` ${j("─",["└","┴","┘"])}`].join(`
|
|
6
|
+
`)}function Bs(P){let O=[];O.push("Hook Integration"),O.push(uf(P));let D=[],j=[];for(let J of P){let Y=mn(J.platform);if(J.errors&&J.errors.length>0)for(let re of J.errors)if(J.configured)D.push({platform:Y,message:re});else j.push({platform:Y,message:re})}for(let J of D)O.push(` Warning (${J.platform}): ${J.message}`);for(let J of j)O.push(nn.red(` Error (${J.platform}): ${J.message}`));return O.join(`
|
|
7
|
+
`)}function uf(P){let O=["Platform","Discovery","Configuration","Inspection"],D=P.map((j)=>{let J=mn(j.platform);if(j.inspectionStatus==="not-inspected"){let de=nn.dim("Not inspected");return[J,de,de,de]}let Y=j.detected?nn.green("Detected"):j.inspectionStatus==="failed"?nn.red("Unknown"):nn.dim("Not detected"),re=j.configured?nn.green("Configured"):j.detected?nn.yellow("Not configured"):j.inspectionStatus==="failed"?nn.red("Unknown"):nn.dim("Not applicable"),ie=j.inspectionStatus==="verified"?nn.green("Verified"):j.inspectionStatus==="failed"?nn.red("Failed"):nn.dim("Not applicable");return[J,Y,re,ie]});return Kn({headers:O,rows:D})}function Vs(P){let D=["Guard Engine Verification",` Synthetic self-test: ${P.failed>0?nn.red(`${P.passed}/${P.total} FAIL`):nn.green(`${P.passed}/${P.total} passed`)}`],j=P.results.filter((J)=>!J.passed);if(j.length>0){D.push(""),D.push(nn.red(" Failures:"));for(let J of j)D.push(nn.red(` • ${J.description}`)),D.push(nn.red(` expected ${J.expected}, got ${J.actual}`))}return D.join(`
|
|
8
|
+
`)}function pf(P){if(P.length===0)return" (no custom rules)";let O=["Source","Name","Command","Block Args"],D=P.map((j)=>[j.source,j.name,j.subcommand?`${j.command} ${j.subcommand}`:j.command,j.blockArgs.join(", ")]);return Kn({headers:O,rows:D})}function Js(P){let O=[];if(O.push("Configuration"),O.push(ff(P.userConfig,P.projectConfig)),O.push(""),P.effectiveRules.length>0)O.push(` Effective rules (${P.effectiveRules.length} total):`),O.push(pf(P.effectiveRules));else O.push(" Effective rules: (none - using built-in rules only)");return O.join(`
|
|
9
|
+
`)}function ff(P,O){let D=["Scope","Status"],j=(Y)=>{if(!Y.exists)return nn.dim("N/A");if(!Y.valid)return nn.red(`Invalid (${Y.errors?.[0]??"unknown error"})`);return nn.green("Configured")},J=[["User",j(P)],["Project",j(O)]];return Kn({headers:D,rows:J})}function Ws(P){let O=[];return O.push("Environment"),O.push(mf(P)),O.join(`
|
|
10
|
+
`)}function Ks(P){let O=P.effectiveSafety.policyScopes,D=["Effective Safety",` Selected preset: ${P.effectiveSafety.selectedPreset}${O?` (${wr(O.levelScope)})`:""}`,` Effective: ${P.effectiveSafety.level}`],j=[["fail_closed","fail_closed"],["paranoid_rm","paranoid_rm"],["paranoid_interpreters","paranoid_interpreters"]];for(let[J,Y]of j){let re=P.effectiveSafety.capabilities[J],ie=re.enabled?nn.green("ON"):nn.dim("OFF"),de=re.sources.length>0?` (${re.sources.join(", ")})`:"";D.push(` ${Y}: ${ie} via ${re.source}${de}`)}if(O&&O.weakenings.length>0){D.push(` Project policy deltas${O.weakeningsIgnored?" (ignored)":""}:`);for(let J of O.weakenings)D.push(` ${J}`)}D.push(` Stored rule customizations: ${P.effectiveSafety.ruleCounts.stored}`),D.push(` Effective rule customizations: ${P.effectiveSafety.ruleCounts.effective}`);for(let[J,Y]of Object.entries(P.effectiveSafety.ruleOverrides))D.push(` ${J}: ${Y}`);return D.join(`
|
|
11
|
+
`)}function zs(P){let O=["Findings"];if(P.length===0)return O.push(" No findings from inspected doctor facts."),O.join(`
|
|
12
12
|
`);for(let D of P){let j=`[${D.severity.toUpperCase()}] ${D.checkId}: ${yn(D.title)}`,J=D.severity==="error"?nn.red:D.severity==="warning"?nn.yellow:nn.blue;if(O.push(` ${J(j)}`),O.push(` ${yn(D.detail)}`),D.path)O.push(` Path: ${yn(D.path)}`);if(D.fixHint)O.push(` Fix: ${yn(D.fixHint)}`)}return O.join(`
|
|
13
|
-
`)}function
|
|
14
|
-
`)}function
|
|
15
|
-
`);if(P.error)return O.push(
|
|
16
|
-
`);if(P.updateAvailable)return O.push(
|
|
17
|
-
`);return O.push(
|
|
18
|
-
`)}function
|
|
19
|
-
`)}function
|
|
13
|
+
`)}function mf(P){let O=["Variable","Status","Legacy"],D=P.map((j)=>{let J=j.isSet?nn.green("✓"):nn.dim("✗"),Y=j.legacyName&&j.legacyIsSet?`${j.legacyName} ${nn.green("✓")}`:j.legacyName??"";return[j.name,J,Y]});return Kn({headers:O,rows:D})}function Ys(P){let O=[];if(P.totalBlocked===0)O.push("Recent Activity"),O.push(" No blocked commands in the last 7 days"),O.push(" Tip: This is normal for new installations");else O.push(`Recent Activity · last 7 days (${P.totalBlocked} blocked / ${P.sessionCount} sessions)`),O.push(gf(P.recentEntries));if(P.unreadable>0)O.push(` Warning: ${P.unreadable} audit log ${P.unreadable===1?"source":"sources"} could not be read; this summary is incomplete`);return O.join(`
|
|
14
|
+
`)}function gf(P){let O=["Time","Command"],D=P.map((j)=>{let J=yn(j.command.replace(/\r\n|\r|\n/g," ↵ ").replace(/\t/g," ")),Y=J.length>40?`${J.slice(0,37)}...`:J;return[j.relativeTime,Y]});return Kn({headers:O,rows:D})}function Zs(P){let O=[];if(O.push("Update Check"),P.latestVersion===null&&!P.error)return O.push(kr([["Status",nn.dim("Skipped")],["Installed",P.currentVersion]])),O.join(`
|
|
15
|
+
`);if(P.error)return O.push(kr([["Status",`${nn.yellow("⚠")} Error`],["Installed",P.currentVersion],["Error",nn.dim(P.error)]])),O.join(`
|
|
16
|
+
`);if(P.updateAvailable)return O.push(kr([["Status",`${nn.yellow("⚠")} Update Available`],["Current",P.currentVersion],["Latest",nn.green(P.latestVersion??"")]])),O.push(""),O.push(" Run: bunx cc-safety-net@latest doctor"),O.push(" Or: npx cc-safety-net@latest doctor"),O.join(`
|
|
17
|
+
`);return O.push(kr([["Status",`${nn.green("✓")} Up to date`],["Version",P.currentVersion]])),O.join(`
|
|
18
|
+
`)}function kr(P){return Kn({rows:P})}function Xs(P){let O=[];return O.push("System Info"),O.push(yf(P)),O.join(`
|
|
19
|
+
`)}function yf(P){let O=["Component","Version"],D=(Y)=>{if(Y===null)return nn.dim("not found");return Y},J=[{label:"cc-safety-net",value:P.version},...mr.map((Y)=>({label:mn(Y),value:P.versions[Y]??null})),{label:"Node.js",value:P.nodeVersion},{label:"npm",value:P.npmVersion},{label:"Bun",value:P.bunVersion},{label:"Platform",value:P.platform}].map((Y)=>[Y.label,D(Y.value)]);return Kn({headers:O,rows:J})}function Qs(P){if(P.findings.length===0)return nn.green(`
|
|
20
20
|
No findings from inspected doctor facts.`);let O={error:P.findings.filter((Y)=>Y.severity==="error").length,warning:P.findings.filter((Y)=>Y.severity==="warning").length,info:P.findings.filter((Y)=>Y.severity==="info").length},D=["error","warning","info"].filter((Y)=>O[Y]>0).map((Y)=>`${O[Y]} ${Y}`),j=P.findings.length===1?"finding":"findings",J=`
|
|
21
|
-
${P.findings.length} ${j}: ${D.join(", ")}.`;if(O.error>0)return nn.red(J);if(O.warning>0)return nn.yellow(J);return nn.blue(J)}import{lstatSync as
|
|
22
|
-
`)[0]?.trim()||null}async function
|
|
21
|
+
${P.findings.length} ${j}: ${D.join(", ")}.`;if(O.error>0)return nn.red(J);if(O.warning>0)return nn.yellow(J);return nn.blue(J)}import{lstatSync as hf}from"node:fs";import{dirname as Oo}from"node:path";function Do(P,O){try{let D=hf(O);if(D.isSymbolicLink())return{kind:P,path:O,status:"unsafe",issues:["symlink"]};if(!D.isDirectory())return{kind:P,path:O,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:O,status:"unknown",issues:[]};let j=[...D.uid!==process.getuid()?["ownership"]:[],...(D.mode&18)!==0?["permissions"]:[]];return{kind:P,path:O,status:j.length>0?"unsafe":"safe",issues:j}}catch(D){if(typeof D==="object"&&D!==null&&"code"in D&&D.code==="ENOENT")return{kind:P,path:O,status:"not-applicable",issues:[]};return{kind:P,path:O,status:"unknown",issues:[]}}}function ea(P,O){let D=U(P);return{directories:[Do("policy",Oo(Oo(O))),Do("config",Oo(O)),...D?[Do("audit",D)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as vf}from"node:child_process";import{existsSync as na}from"node:fs";import{delimiter as bf,extname as wf,join as kf}from"node:path";import{stripVTControlCharacters as ta}from"node:util";var oa="2.6.2",xf=5000,Cf="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function pn(){return oa}function Lo(P,O){let D=P[O];if(D)return D;let j=Object.keys(P).find((J)=>J.toLowerCase()===O.toLowerCase()&&!!P[J]);return j?P[j]:D}function Sf(P){return(Lo(P,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((O)=>O.length>0)}function Pf(P,O){let D=wf(P)?[P]:[...Sf(O).map((j)=>`${P}${j}`),P];if(P.includes("/")||P.includes("\\"))return D.find((j)=>na(j))??P;return(Lo(O,"PATH")??"").split(bf).flatMap((j)=>D.map((J)=>kf(j,J))).find((j)=>na(j))??P}function ra(P){if(!/[\s"&|<>^]/.test(P))return P;return`"${P.replace(/"/g,'""')}"`}function zn(P,O){let[D,...j]=P,J=O[Cf]==="win32"?"win32":process.platform;if(!D||J!=="win32")return{cmd:D??"",args:j};let Y=Pf(D,O);if(!/\.(?:bat|cmd)$/i.test(Y))return{cmd:Y,args:j};return{cmd:Lo(O,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",ra(Y),...j.map(ra)].join(" ")]}}var St=async(P,O=xf)=>{let D=await Rf(P,{timeoutMs:O});if(D.code!==0)return null;return ta(D.stdout).trim()||ta(D.stderr).trim()||null};function Rf(P,O){let[D,...j]=P;if(!D)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((J)=>{try{let Y=zn([D,...j],process.env),re=vf(Y.cmd,Y.args,{stdio:["ignore","pipe","pipe"]}),ie=!1,de="",fe="";re.stdout.on("data",(Se)=>{de+=Se.toString()}),re.stderr.on("data",(Se)=>{fe+=Se.toString()});let we=(Se)=>{if(ie)return;ie=!0,clearTimeout(Ce),J(Se)},Ce=setTimeout(()=>{re.kill(),we({code:null,stdout:de,stderr:fe})},O.timeoutMs);re.on("close",(Se)=>{we({code:Se,stdout:de,stderr:fe})}),re.on("error",()=>{we({code:null,stdout:de,stderr:fe})})}catch{J({code:null,stdout:"",stderr:""})}})}function xr(P){if(!P)return null;let O=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(P);if(O)return O[1]??null;let D=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(P);if(D)return D[1]??null;return P.split(`
|
|
22
|
+
`)[0]?.trim()||null}async function Cr(P,O=St,D=process.cwd()){let j=Promise.all(En.map(async(Ce)=>[Ce.id,xr(await O([...Ce.probeCommand]))])),[J,Y,re,ie,de,fe,we]=await Promise.all([j,j.then(async(Ce)=>{let Se=Ce.find(([Ve])=>Ve==="opencode")?.[1];if(!Se?.startsWith("2.")||!P(Se))return null;let Ie=["--param",`location[directory]=${D}`],Ee=["opencode","api","integration.list",...Ie];return await O(Ee,30000),O(["opencode","api","plugin.list",...Ie],30000)}),O(["codex","plugin","list"],30000),O(["amp","plugins","list"],30000),O(["node","--version"]),O(["npm","--version"]),O(["bun","--version"])]);return{version:oa,versions:Object.fromEntries(J),codexPluginListOutput:re,ampPluginListOutput:ie,openCodePluginListOutput:Y,nodeVersion:xr(de),npmVersion:xr(fe),bunVersion:xr(we),platform:`${process.platform} ${process.arch}`}}function No(P,O){if(O==="dev")return!1;let D=P.split(".").map(Number),j=O.split(".").map(Number),[J=0,Y=0,re=0]=D,[ie=0,de=0,fe=0]=j;if(J!==ie)return J>ie;if(Y!==de)return Y>de;return re>fe}async function Gn(){let P=pn(),O=new AbortController,D=setTimeout(()=>O.abort(),3000);try{let j=await fetch("https://registry.npmjs.org/cc-safety-net/latest",{signal:O.signal});if(!j.ok)return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:`npm registry returned ${j.status}`};let J=await j.json(),Y=No(J.version,P);return{currentVersion:P,latestVersion:J.version,updateAvailable:Y}}catch(j){return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:j instanceof Error?j.message:"Network error"}}finally{clearTimeout(D)}}import*as pa from"node:readline";var la=(P)=>`\x1B[${P}B`,Ef=(P)=>`\x1B[${P}A`;var ia=["░","▒","▓","╱","╲","┃","━","┏","┓","┗","┛","╋"];function _f(P){return new Promise((O)=>setTimeout(O,P))}function Af(P,O,D){if(!D)return O(P);if(D.aborted)return Promise.resolve();return new Promise((j,J)=>{let Y=()=>D.removeEventListener("abort",re),re=()=>{Y(),j()};D.addEventListener("abort",re,{once:!0}),O(P).then(()=>{Y(),j()},(ie)=>{Y(),J(ie)})})}function Sr(P){return Math.max(0,Math.min(1,P))}function Pt(P){return Math.max(0,Math.min(255,Math.round(P)))}function jo(P){return P<=0.0031308?12.92*P:1.055*P**0.4166666666666667-0.055}function Tf(P,O,D){let j=D*Math.PI/180,J=O*Math.cos(j),Y=O*Math.sin(j),re=(P+0.3963377774*J+0.2158037573*Y)**3,ie=(P-0.1055613458*J-0.0638541728*Y)**3,de=(P-0.0894841775*J-1.291485548*Y)**3;return{blue:Pt(jo(Sr(-0.0041960863*re-0.7034186147*ie+1.707614701*de))*255),green:Pt(jo(Sr(-1.2684380046*re+2.6097574011*ie-0.3413193965*de))*255),red:Pt(jo(Sr(4.0767416621*re-3.3077115913*ie+0.2309699292*de))*255)}}function Fo(P,O){let D=(O*P*180/Math.PI%360+360)%360;return Tf(0.72,0.15,D)}function ca(P,O=0.1){let D=Fo(O,P);return`\x1B[38;2;${D.red};${D.green};${D.blue}m`}function If(P,O){return{blue:Pt(P.blue+(255-P.blue)*O),green:Pt(P.green+(255-P.green)*O),red:Pt(P.red+(255-P.red)*O)}}function da(P,O,D){let j=Math.imul(P+2654435769,2246822507)^Math.imul(O+3266489909,668265263)^Math.imul(D+374761393,2654435761),J=j^j>>>15,Y=Math.imul(J,739982445),re=Y^Y>>>12,ie=Math.imul(re,695872825);return((ie^ie>>>15)>>>0)/4294967296}function $f(P,O,D){let j=Math.floor(da(P,O,D)*ia.length);return ia[j]??"░"}function sa(P){let O=Sr(P);return O*O*O*(O*(O*6-15)+10)}function Of(P){if(P.length===0)return"";let O=[],D=!1,j="";for(let J of P){let Y=`${J.red};${J.green};${J.blue}`;if(J.bold!==D)O.push(J.bold?"\x1B[1m":"\x1B[22m"),D=J.bold;if(Y!==j)O.push(`\x1B[38;2;${Y}m`),j=Y;O.push(J.character)}return`${O.join("")}\x1B[22m\x1B[39m`}function Df(P,O,D,j,J){return P.map((Y,re)=>({...Fo(D,j+O+re/J),bold:!1,character:Y}))}function Lf(P,O,D,j,J,Y,re,ie){let de=Math.max(1,j*0.75),fe=Math.min(1,D/de),we=J*sa(fe),Ce=Math.max(0,(D-de)/Math.max(1,j-de)),Se=(1-sa(D/j))*ie*2,Ie=0.35*Math.max(0,1-Ce*2),Ee=fe>=1,Ve=Math.min(P.length,Math.ceil(we+2+1));return P.slice(0,Ve).map((Me,en)=>{let on=Fo(Y,re+O+en/ie+Se),sn=en+da(O,en,7919)*2-1;if(sn>we+2)return{...on,bold:!1,character:" "};let an=we-sn,rn=0.8*Math.exp(-(an*an)/12.5),fn=Math.min(0.9,rn+Ie),On=!Ee&&sn>we-4;return{...If(on,fn),bold:fn>0.3,character:On?$f(O,en,D):Me}})}function aa(P){return`\x1B[?2026h${P.map((O,D)=>`\x1B8${D>0?la(D):""}${Of(O)}`).join("")}\x1B[?2026l`}async function Ho(P,O={}){if(!P)return;let D=O.output??process.stdout,j=O.sleep??_f,J=O.seed??0,Y=P.split(`
|
|
23
23
|
`).map((we)=>Array.from(we)),re=Math.max(...Y.map((we)=>we.length)),ie=12000*Y.filter((we)=>we.length>0).length/40,de=re>0?Math.max(1,Math.ceil(ie/16.666666666666668)):0,fe=de>0?ie/de:0;D.write(`\x1B[?25l${Y.length>1?`${`
|
|
24
|
-
`.repeat(Y.length-1)}${
|
|
25
|
-
\x1B[0m\x1B[?25h`)}}var
|
|
26
|
-
`);function
|
|
27
|
-
`).map((D)=>
|
|
28
|
-
`).find((D)=>D.includes("https://github.com/kenryu42/cc-safety-net.git"));if(!O)return{platform:"codex",status:"n/a"};if(!O.includes("installed,"))return{platform:"codex",status:"n/a"};if(!O.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
|
|
29
|
-
`)D++;continue}if(re==="/"&&ie==="*"){D+=2;while(D<P.length-1){if(P[D]==="*"&&P[D+1]==="/"){D+=2;break}D++}continue}if(re===","){Y=O.length,O+=re,D++;continue}if(re==="}"||re==="]"){if(Y!==-1){let de=O.slice(Y+1);if(/^\s*$/.test(de))O=O.slice(0,Y)+de}Y=-1,O+=re,D++;continue}if(!/\s/.test(re))Y=-1;O+=re,D++}return O}function
|
|
30
|
-
`,O)+1;return/^[ \t]*/.exec(P.slice(D))?.[0]??""}function
|
|
24
|
+
`.repeat(Y.length-1)}${Ef(Y.length-1)}`:""}\x1B7`);try{for(let we=1;we<=de;we+=1){if(O.signal?.aborted)break;D.write(aa(Y.map((Ce,Se)=>Lf(Ce,Se,we,de,re,0.1,J,3)))),await Af(fe,j,O.signal)}}finally{if(D.write(aa(Y.map((we,Ce)=>Df(we,Ce,0.1,J,3)))),D.write("\x1B8"),Y.length>1)D.write(la(Y.length-1));D.write(`
|
|
25
|
+
\x1B[0m\x1B[?25h`)}}var ua=["┏━┛┏━┛ ┏━┛┏━┃┏━┛┏━┛━┏┛┃ ┃ ┏━ ┏━┛━┏┛","┃ ┃ ━━┃┏━┃┏━┛┏━┛ ┃ ━┏┛ ┃ ┃┏━┛ ┃ ","━━┛━━┛ ━━┛┛ ┛┛ ━━┛ ┛ ┛ ┛ ┛━━┛ ┛ "].join(`
|
|
26
|
+
`);function Nf(P){return Boolean(P.isTTY)}async function Mt(P={}){let O=P.output??process.stdout;if(!Nf(O))return;let D=P.input??process.stdin,j={output:O,seed:P.seed??Math.random()*8192,sleep:P.sleep};if(!D.isTTY||typeof D.setRawMode!=="function"){await Ho(ua,j);return}let J=new AbortController,Y=D.readableFlowing===!0,re=D.isRaw===!0,ie=!1,de=(fe,we)=>{if(we.ctrl&&we.name==="c")ie=!0;if(ie||we.name==="return"||we.name==="enter")J.abort()};pa.emitKeypressEvents(D),D.on("keypress",de),D.setRawMode(!0),D.resume();try{await Ho(ua,{...j,signal:J.signal})}finally{if(D.off("keypress",de),D.setRawMode(re),!Y)D.pause()}if(!ie)return;if(P.onInterrupt){P.onInterrupt();return}process.kill(process.pid,"SIGINT")}import{createHash as Uf}from"node:crypto";import{existsSync as ga}from"node:fs";import{dirname as Pr,join as ya}from"node:path";import{dirname as fa,join as jf,resolve as Ff}from"node:path";var Hf="rule.lock";function Mf(P){return jf(fa(P),Hf)}function ma(P={}){return Ff(P.cwd??process.cwd(),".safety-net.json")}function kn(P,O){let D=O.global?O.userConfigPath??W(P,O):O.projectConfigPath??_(O.cwd??process.cwd()),j=O.global?Je(P,O):Ye(D,O.cwd??process.cwd()),J=Mf(D);return{configDir:fa(D),configPath:D,lockPath:J,filesystemScope:j,configTarget:i(j,D),lockTarget:i(j,J)}}var Gf="`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.",qf="cache",Bf="rulebooks";function ha(P,O={}){let D=kn(P,O),j=i(D.filesystemScope,ba(D.configDir)),J=r(D.lockTarget);if(console.log(Gf),J===null&&!ga(j.path))return console.log(`No v2 lock or cache leftovers found in ${Pr(D.configDir)}; nothing to migrate.`),0;let Y=zf(J),re=m(D.configTarget);if(!re.config&&(r(D.configTarget)!==null||Y.size>0))return console.error(`Cannot migrate: the rules config in ${Pr(D.configDir)} is missing or unreadable while v2 leftovers remain. Restore rule.json, then re-run rule sync.`),1;let ie=re.config?.rules??[];for(let de of ie.flatMap((fe)=>Vf(fe,Y,D,j,O.global===!0)))console.log(de);return H(D.lockTarget),st(j),console.log(`Removed the v2 lock and cache under ${Pr(D.configDir)}.`),0}function va(P,O){return[...new Set([{cwd:O},{cwd:O,global:!0}].flatMap((D)=>{let j=kn(P,D);return[j.lockPath,ba(j.configDir)]}))].filter((D)=>ga(D))}function Vf(P,O,D,j,J){if(!w(P))return[];let Y=N(P).name,re=i(D.filesystemScope,M(D.configDir,Y)),ie=r(re);if(ie!==null&&Jf(ie,Y))return[];let de=O.get(P),fe=de?Wf(de,Y,j.path,D.filesystemScope):null;if(fe===null)return[`Could not migrate ${P} from the v2 cache. Run \`cc-safety-net rule update ${P}${J?" --global":""}\` to vendor it.`];if(g(re,fe),ie!==null)return[`Restored ${P} from the v2 cache over an invalid file.`];return[`Vendored ${P} from the v2 cache.`]}function Jf(P,O){let D=xe(P);return!("problem"in D)&&D.rulebook.name===O}function Wf(P,O,D,j){let J=ya(D,Bf,`${Kf(P)}--${P.digest.replace("sha256:","").slice(0,12)}`,ge),Y=r(i(j,J));if(Y===null||Xf(Y)!==P.digest)return null;let re=xe(Y);if("problem"in re||re.rulebook.name!==O)return null;return Y}function ba(P){return ya(Pr(P),qf)}function Kf(P){return([P.owner,P.repo,P.display_ref,P.name].every((j)=>typeof j==="string"&&j!=="")?`${P.owner}/${P.repo}#${P.display_ref}/${P.name}`:P.spec).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"rulebook"}function zf(P){let O=P===null?null:Zf(P),D=wa(O)&&Array.isArray(O.rulebooks)?O.rulebooks:[];return new Map(D.filter(Yf).map((j)=>[j.spec,j]))}function Yf(P){return wa(P)&&typeof P.spec==="string"&&typeof P.digest==="string"}function wa(P){return!!P&&typeof P==="object"}function Zf(P){try{return JSON.parse(P)}catch{return null}}function Xf(P){return`sha256:${Uf("sha256").update(P).digest("hex")}`}var ka="\r\x1B[2K",Qf="\x1B[?25l",em="\x1B[39m",nm="\x1B[?25h",tm=100,rm=0.55,om=80,xa=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];function im(P){return new Promise((O)=>setTimeout(O,P))}async function Rr(P,O={}){let D=O.output??process.stdout;if(!D.isTTY)return P;let j=O.sleep??im,J=!1,Y=P.then((ie)=>(J=!0,ie),(ie)=>{throw J=!0,ie});if(await Promise.race([Y.then(()=>!0),j(tm).then(()=>!1)]))return Y;D.write(Qf);try{for(let ie=0;!J;ie+=1)D.write(`${ka}${ca(ie*rm)}${xa[ie%xa.length]}${em} ${O.loadingMessage??"Loading…"}`),await Promise.race([Y,j(om)]);return await Y}finally{D.write(`${ka}${nm}`)}}async function Ut(P,O,D,j={}){let J=O();if(P)await D();if(P&&J.ready)await Rr(J.ready,j);return J.finish()}import{stripVTControlCharacters as sm}from"node:util";var Er="amp plugins list",am=/^\s*[✓✗]\s+cc-safety-net(?:\.ts)?\s+\(User Plugins\)\s+(\S+)\s*$/;function Ca(P){if(!P.ampPluginListOutput)return{platform:"amp",status:"n/a"};let O=sm(P.ampPluginListOutput).split(`
|
|
27
|
+
`).map((D)=>am.exec(D)?.[1]).find((D)=>D!==void 0);if(!O)return{platform:"amp",status:"n/a"};if(O!=="active")return{platform:"amp",status:"disabled",method:Er,configPath:Er,errors:[`Amp personal plugin cc-safety-net is ${O}; run "plugins: reload" in Amp or reinstall with install --amp`]};return{platform:"amp",status:"configured",method:Er,configPath:Er}}import{existsSync as pm,readFileSync as fm}from"node:fs";import{isAbsolute as Kx,join as um}from"node:path";function Gt(P){return um(P,".gemini","config","hooks.json")}var mm=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*(?:--agy-cli|-ac)(\s|["']|$)/;function gm(P){if(!P||typeof P!=="object"||Array.isArray(P))return[];return Object.values(P).flatMap((O)=>{if(!O||typeof O!=="object"||Array.isArray(O))return[];let D=O,j=D.PreToolUse;if(!Array.isArray(j))return[];return j.flatMap((J)=>{if(!J||typeof J!=="object"||Array.isArray(J))return[];let Y=J.hooks;if(!Array.isArray(Y))return[];return Y.flatMap((re)=>{if(!re||typeof re!=="object"||Array.isArray(re))return[];let ie=re.command;if(typeof ie!=="string"||!mm.test(ie))return[];return[{command:ie,enabled:D.enabled!==!1}]})})})}function Sa(P){let O=Gt(P.environment.home);if(!pm(O))return{platform:"antigravity-cli",status:"n/a",configPath:O};let D;try{D=gm(JSON.parse(fm(O,"utf-8")))}catch(j){return{platform:"antigravity-cli",status:"n/a",configPath:O,errors:[`Failed to parse Antigravity hooks config ${O}: ${j instanceof Error?j.message:String(j)}`]}}if(D.some((j)=>j.enabled))return{platform:"antigravity-cli",status:"configured",method:"hook config",configPath:O};if(D.length>0)return{platform:"antigravity-cli",status:"disabled",method:"hook config",configPath:O};return{platform:"antigravity-cli",status:"n/a",configPath:O}}import{existsSync as wm}from"node:fs";import{join as qt}from"node:path";import{existsSync as ym,lstatSync as hm,readFileSync as vm}from"node:fs";import{join as bm}from"node:path";function xn(P,O=(D)=>D){if(!ym(P))return{kind:"missing"};try{return{kind:"ok",value:JSON.parse(O(vm(P,"utf-8")))}}catch{return{kind:"unreadable"}}}function Nn(P,O){if(P==="~")return O;if(P.startsWith("~/")||P.startsWith("~\\"))return bm(O,P.slice(2));return P}function dn(P){try{return hm(P)}catch{return}}function _r(P,O){let D=dn(O);if(!D)return{platform:P,status:"n/a",configPath:O};if(!D.isSymbolicLink()&&D.isDirectory())return;return{platform:P,status:"n/a",configPath:O,errors:[`${O} is a symlink or not a directory; move or remove it before installing`]}}function tn(P,O){return typeof P==="object"&&P!==null?P[O]:void 0}var Ar="cc-safety-net@cc-marketplace";function Tr(P){return P.env.get("CLAUDE_CONFIG_DIR")||qt(P.home,".claude")}function Mo(P){return qt(Tr(P),"plugins","installed_plugins.json")}function Pa(P,O){let D=tn(tn(P,"plugins"),O);return Array.isArray(D)&&D.length>0}function Ir(P,O){let D=xn(Mo(P));return D.kind==="ok"&&Pa(D.value,O)}function $r(P){let O=Mo(P),D=xn(O);if(D.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(D.kind==="missing")return{platform:"claude-code",status:"n/a"};if(!Pa(D.value,Ar))return{platform:"claude-code",status:"n/a"};let j=qt(Tr(P),"settings.json"),J=xn(j);if(J.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(!(J.kind==="ok"&&tn(tn(J.value,"enabledPlugins"),Ar)===!0))return{platform:"claude-code",status:"disabled",method:"plugin config",configPath:j,errors:[`${Ar} is installed but not enabled in Claude Code`]};return{platform:"claude-code",status:"configured",method:"plugin config",configPath:O}}function Ra(P){if($r(P).status!=="configured")return;let O=xn(Mo(P)),D=O.kind==="ok"?tn(tn(O.value,"plugins"),Ar):void 0,j=(Array.isArray(D)?D:[]).filter((ie)=>tn(ie,"scope")==="user").map((ie)=>tn(ie,"installPath")).find((ie)=>typeof ie==="string");if(!j)return;let J=qt(j,".cursor-plugin","plugin.json"),Y=xn(J),re=Y.kind==="ok"?tn(Y.value,"hooks"):void 0;return typeof re==="string"&&wm(qt(j,re))?J:void 0}function Ea(P){return $r(P.environment)}var _a="Start Codex, open `/hooks`, select the cc-safety-net PreToolUse hook, and press `t` to trust it.";function Aa(P){if(!P.codexPluginListOutput)return{platform:"codex",status:"n/a"};let O=P.codexPluginListOutput.split(`
|
|
28
|
+
`).find((D)=>D.includes("https://github.com/kenryu42/cc-safety-net.git"));if(!O)return{platform:"codex",status:"n/a"};if(!O.includes("installed,"))return{platform:"codex",status:"n/a"};if(!O.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 Nr,readdirSync as km,readFileSync as xm}from"node:fs";import{join as bn}from"node:path";function vn(P){let O="",D=0,j=!1,J=!1,Y=-1;while(D<P.length){let re=P[D],ie=P[D+1];if(J){O+=re,J=!1,D++;continue}if(re==='"'&&!j){j=!0,Y=-1,O+=re,D++;continue}if(re==='"'&&j){j=!1,O+=re,D++;continue}if(re==="\\"&&j){J=!0,O+=re,D++;continue}if(j){O+=re,D++;continue}if(re==="/"&&ie==="/"){while(D<P.length&&P[D]!==`
|
|
29
|
+
`)D++;continue}if(re==="/"&&ie==="*"){D+=2;while(D<P.length-1){if(P[D]==="*"&&P[D+1]==="/"){D+=2;break}D++}continue}if(re===","){Y=O.length,O+=re,D++;continue}if(re==="}"||re==="]"){if(Y!==-1){let de=O.slice(Y+1);if(/^\s*$/.test(de))O=O.slice(0,Y)+de}Y=-1,O+=re,D++;continue}if(!/\s/.test(re))Y=-1;O+=re,D++}return O}function Ia(P,O,D){let j=O+1,J=!1;while(j<P.length){if(J){J=!1,j++;continue}if(P[j]==="\\"){J=!0,j++;continue}if(P[j]==='"')return j+1;j++}throw Error(D)}function Go(P,O,D){let j=P[O],J=j==="["?"]":"}",Y=0,re=O;while(re<P.length){let ie=D.skipComment?.(P,re)??re;if(ie!==re){re=ie;continue}if(P[re]==='"'){re=Ia(P,re,D.stringError);continue}if(P[re]===j)Y++;if(P[re]===J){if(Y--,Y===0)return re}re++}throw Error(D.bracketError)}function $a(P,O){let D=P.lastIndexOf(`
|
|
30
|
+
`,O)+1;return/^[ \t]*/.exec(P.slice(D))?.[0]??""}function Oa(P,O){let D=O.end+(/^\s*/.exec(P.slice(O.end))?.[0].length??0);if(P[D]===","){let re=P[D+1]===`
|
|
31
31
|
`?D+2:D+1;return`${P.slice(0,O.start)}${P.slice(re)}`}let j=P.slice(0,O.start).search(/\s*$/)-1;if(P[j]!==",")return`${P.slice(0,O.start)}${P.slice(O.end)}`;let J=P.lastIndexOf(`
|
|
32
|
-
`,j-1),Y=J!==-1&&/^\s*$/.test(P.slice(J+1,j))?J:j;return`${P.slice(0,Y)}${P.slice(O.end)}`}function
|
|
33
|
-
`,O+2);return D===-1?P.length:D+1}if(P.startsWith("/*",O)){let D=P.indexOf("*/",O+2);return D===-1?P.length:D+2}return O}function
|
|
34
|
-
`)}function
|
|
35
|
-
`),{path:O,alreadyInstalled:!1};let D=
|
|
36
|
-
`)}function
|
|
37
|
-
`)}function
|
|
38
|
-
`)}function
|
|
32
|
+
`,j-1),Y=J!==-1&&/^\s*$/.test(P.slice(J+1,j))?J:j;return`${P.slice(0,Y)}${P.slice(O.end)}`}function Uo(P,O){if(P.startsWith("//",O)){let D=P.indexOf(`
|
|
33
|
+
`,O+2);return D===-1?P.length:D+1}if(P.startsWith("/*",O)){let D=P.indexOf("*/",O+2);return D===-1?P.length:D+2}return O}function Ta(P,O){let D=O;while(D<P.length){if(/\s/.test(P[D]??"")){D++;continue}let j=Uo(P,D);if(j===D)return D;D=j}return D}function Da(P,O,D){let j=0,J=0;while(J<P.length){let Y=Uo(P,J);if(Y!==J){J=Y;continue}if(P[J]==='"'){let re=Ia(P,J,D.stringError);if(j===1&&JSON.parse(P.slice(J,re))===O){let ie=Ta(P,re),de=Ta(P,ie+1);if(P[ie]===":"&&P[de]==="[")return{start:de,end:Go(P,de,{skipComment:Uo,...D})}}J=re;continue}if(P[J]==="{"||P[J]==="[")j++;if(P[J]==="}"||P[J]==="]")j--;J++}return}var An="cc-safety-net@cc-marketplace",Or=["cc-marketplace","cc-safety-net"],La=["_direct","copilot-safety-net"],Na=["cc-marketplace","safety-net"],ja="safety-net@cc-marketplace";function Dr(P,O){let D=O.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`(^|[^a-z0-9-])${D}([^a-z0-9-]|$)`,"m").test(P??"")}function Fa(P){return Dr(P,"cc-safety-net@cc-marketplace")}function Ha(P){return Dr(P,"cc-marketplace")}function Ma(P){return Dr(P,"copilot-safety-net")}function Ua(P){return Dr(P,"safety-net@cc-marketplace")}function Lr(P){if(!P?.includes("cc-safety-net"))return!1;return/(^|\s)hook\s+(?:[^\s]+\s+)*(--copilot-cli|-cp)(\s|$)/.test(P)}function qa(P,O){if(!P)return null;let D=P.match(/(\d+)\.(\d+)\.(\d+)/);if(!D)return null;let j=[Number(D[1]),Number(D[2]),Number(D[3])];for(let J=0;J<O.length;J++){let Y=j[J]??0,re=O[J]??0;if(Y!==re)return Y>re}return!0}function Cm(P){return qa(P,[0,0,422])}function Sm(P){return qa(P,[1,0,8])}function Vt(P){return P.env.get("COPILOT_HOME")||bn(P.home,".copilot")}function qo(P){return(P.hooks?.preToolUse??[]).some((D)=>{if(D.type!==void 0&&D.type!=="command")return!1;return Lr(D.command)||Lr(D.bash)||Lr(D.powershell)||Lr(D.exec&&[D.exec,...D.args??[]].join(" "))})}function Bt(P){return P===void 0||typeof P==="string"}function Pm(P){return P===void 0||Array.isArray(P)&&P.every((O)=>typeof O==="string")}function Rm(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let O=P;if(O.disableAllHooks!==void 0&&typeof O.disableAllHooks!=="boolean")return!1;if(O.hooks===void 0)return!0;if(!O.hooks||typeof O.hooks!=="object"||Array.isArray(O.hooks))return!1;let D=O.hooks.preToolUse;if(D===void 0)return!0;return Array.isArray(D)&&D.every((j)=>j!==null&&typeof j==="object"&&!Array.isArray(j)&&Bt(j.type)&&Bt(j.command)&&Bt(j.bash)&&Bt(j.powershell)&&Bt(j.exec)&&Pm(j.args))}function Bo(P,O){try{let D=JSON.parse(vn(xm(P,"utf-8")));if(!Rm(D)){O?.push(`Invalid hook config ${P}: hooks.preToolUse must be an array of hook objects`);return}return D}catch(D){O?.push(`Failed to parse ${P}: ${D instanceof Error?D.message:String(D)}`);return}}function Ba(P,O){try{return km(P).filter((D)=>D.endsWith(".json")).sort((D,j)=>D.localeCompare(j))}catch(D){return O?.push(`Failed to read ${P}: ${D instanceof Error?D.message:String(D)}`),[]}}function Em(P,O){if(!Nr(P))return[];let D=[];for(let j of Ba(P,O)){let J=bn(P,j),Y=Bo(J,O);if(Y&&qo(Y))D.push(J)}return D}function Rt(P,O){if(!Nr(P))return;let D=Bo(P,O);if(!D)return;return{path:P,config:D}}function Ga(P,O,D,j){if(O){P.push(`GitHub Copilot CLI ${O} does not support ${D}; requires ${j}+`);return}P.push(`GitHub Copilot CLI version unavailable; skipping ${D} because it requires ${j}+`)}function _m(P){for(let O of P){if(O?.config.disableAllHooks===!0)return O.path;if(O?.config.disableAllHooks===!1)return}return}function Am(P,O,D,j){let J=Vt(P),Y=bn(O,".github","hooks"),re=bn(J,"hooks"),ie=bn(O,".github","copilot"),de=bn(O,".claude"),fe=Sm(D),we=fe===!0?j:void 0,Ce=[Rt(bn(ie,"settings.local.json"),we),Rt(bn(ie,"settings.json"),we),Rt(bn(de,"settings.local.json"),we),Rt(bn(de,"settings.json"),we)],Se=[Rt(bn(J,"settings.json"),we),Rt(bn(J,"config.json"),we)];if(fe!==!1){let an=_m([...Ce,...Se]);if(an){if(fe===null)j.push(`GitHub Copilot CLI version unavailable; treating disableAllHooks in ${an} as active`);return{activeConfigPaths:[],repoInlineSources:Ce,disabledBy:an}}}let Ie=Em(Y,j),Ee=Cm(D),Ve=Ee===!0?j:void 0,Me=Nr(re)?Ba(re,Ve):[],en=[];for(let an of Me){let rn=bn(re,an),fn=Bo(rn,Ve);if(fn&&qo(fn))en.push(rn)}if(Ee!==!0&&en.length>0)Ga(j,D,`user hook files in ${re}`,"0.0.422"),en.length=0;let on=[];for(let an of[...Ce,...Se]){if(!an)continue;if(!qo(an.config))continue;if(fe===!0){on.push(an);continue}Ga(j,D,"inline hook definitions in Copilot config files","1.0.8");break}let sn=(an)=>an.filter((rn)=>!!rn&&on.includes(rn)).map((rn)=>rn.path);return{activeConfigPaths:[...sn(Ce),...Ie,...sn(Se),...en],repoInlineSources:Ce}}function Va(P){let O=[],D=Am(P.environment,P.cwd,P.copilotCliVersion,O);if(D.disabledBy)return{platform:"copilot-cli",status:"disabled",method:"hook config",configPath:D.disabledBy,configPaths:[D.disabledBy],errors:O.length>0?O:void 0};let j=Vt(P.environment),J=bn(j,"installed-plugins",...Or),Y=Nr(J),re=bn(j,"settings.json"),ie=xn(re,vn),de=(Ie)=>tn(tn(Ie,"enabledPlugins"),An),fe=D.repoInlineSources.find((Ie)=>typeof de(Ie?.config)==="boolean"),we=fe??(ie.kind==="ok"?{path:re,config:ie.value}:void 0);if(Y&&!fe&&ie.kind==="unreadable")return{platform:"copilot-cli",status:"not-inspected"};let Ce=Y&&we!==void 0&&de(we.config)===!1;if(Ce&&D.activeConfigPaths.length===0)return{platform:"copilot-cli",status:"disabled",method:"plugin config",configPath:we.path,errors:[`${An} is installed but not enabled in Copilot CLI`]};let Se=Y&&!Ce;if(Se||D.activeConfigPaths.length>0){let Ie=D.activeConfigPaths[0];return{platform:"copilot-cli",status:"configured",method:Se?"plugin config":"hook config",configPath:Ie??(Se?J:void 0),configPaths:D.activeConfigPaths.length>0?D.activeConfigPaths:void 0,errors:O.length>0?O:void 0}}return{platform:"copilot-cli",status:"n/a",errors:O.length>0?O:void 0}}import{existsSync as Um,readFileSync as Gm}from"node:fs";import{existsSync as Ja,mkdirSync as Dm,readFileSync as Lm}from"node:fs";import{dirname as Nm,join as jm}from"node:path";import{existsSync as Tm,renameSync as Im,statSync as $m,writeFileSync as Om}from"node:fs";function cn(P,O){let D=`${P}.${process.pid}.tmp`;Om(D,O,Tm(P)?{mode:$m(P).mode&511}:{}),Im(D,P)}var Cn=Object.fromEntries(Ht.map((P)=>[P.id,`npx -y cc-safety-net hook ${P.flags[1]}`]));var Jt=Cn.cursor,Wa=30;function Fr(P){return jm(P.home,".cursor","hooks.json")}function Yn(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Vo(){return{command:Jt,timeout:Wa,failClosed:!0}}function jr(P){return Yn(P)&&P.command===Jt}function Fm(P){return Object.keys(P).length===3&&P.command===Jt&&P.timeout===Wa&&P.failClosed===!0}function Hm(P){try{return JSON.parse(Lm(P,"utf-8"))}catch(O){if(O instanceof SyntaxError)throw Error(`Failed to parse Cursor hooks config ${P}: ${O.message}`);throw O}}function Ka(P){let O=Hm(P);if(!Yn(O))throw Error(`Cursor hooks config ${P} must be a JSON object`);if(O.version!==1)throw Error(`Cursor hooks config ${P} must set "version": 1`);if(O.hooks!==void 0&&!Yn(O.hooks))throw Error(`Cursor hooks config ${P} "hooks" must be an object`);let D=Yn(O.hooks)?O.hooks.preToolUse:void 0;if(D!==void 0&&!Array.isArray(D))throw Error(`Cursor hooks config ${P} "hooks.preToolUse" must be an array`);return O}function za(P){let O=Yn(P.hooks)?P.hooks.preToolUse:void 0;return Array.isArray(O)?O:[]}function Mm(P){if(!P.some(jr))return[...P,Vo()];return P.reduce((O,D)=>{if(!jr(D))return O.result.push(D),O;if(!O.inserted)O.result.push(Vo()),O.inserted=!0;return O},{result:[],inserted:!1}).result}function Ya(P,O,D){let j=Yn(O.hooks)?O.hooks:{},J={...O,hooks:{...j,preToolUse:D}};cn(P,`${JSON.stringify(J,null,2)}
|
|
34
|
+
`)}function Za(P){let O=Fr(P);if(!Ja(O))return Dm(Nm(O),{recursive:!0}),cn(O,`${JSON.stringify({version:1,hooks:{preToolUse:[Vo()]}},null,2)}
|
|
35
|
+
`),{path:O,alreadyInstalled:!1};let D=Ka(O),j=za(D),J=j.filter(jr);if(Yn(D.hooks)&&Array.isArray(D.hooks.preToolUse)&&J.length===1&&J[0]!==void 0&&Fm(J[0]))return{path:O,alreadyInstalled:!0};return Ya(O,D,Mm(j)),{path:O,alreadyInstalled:!1}}function Xa(P){let O=Fr(P);if(!Ja(O))return{path:O,alreadyInstalled:!1};let D=Ka(O),j=za(D),J=j.filter((Y)=>!jr(Y));if(J.length===j.length)return{path:O,alreadyInstalled:!1};return Ya(O,D,J),{path:O,alreadyInstalled:!0}}function qm(P){if(!P||typeof P!=="object"||Array.isArray(P))return[];let O=P.hooks;if(!O||typeof O!=="object"||Array.isArray(O))return[];let D=O.preToolUse;if(!Array.isArray(D))return[];return D.filter((j)=>!!j&&typeof j==="object"&&!Array.isArray(j)&&j.command===Jt)}function Bm(P){let O=[];if(P.length>1)O.push("Multiple managed cc-safety-net hooks found; reinstall to collapse duplicates");let D=P[0];if(D&&D.failClosed!==!0)O.push('Managed hook is missing "failClosed": true; reinstall to repair');if(D&&D.timeout!==30)O.push('Managed hook "timeout" is not 30; reinstall to repair');return O}var Hr="Claude Code plugin";function Qa(P){let O=Vm(P),D=Ra(P.environment);if(!D)return O;let j="Cursor runs the Claude Code plugin only while it imports Claude Code plugins, which doctor cannot check.";if(O.status==="configured")return{...O,errors:[...O.errors??[],`The Claude Code plugin also runs this check in Cursor, so every tool call is checked twice. ${j} If Cursor runs it, run \`cc-safety-net uninstall --cursor\` to remove this hook.`]};return{platform:"cursor",status:"configured",method:Hr,configPath:D,errors:[...O.errors??[],`${j} If it stops, run \`cc-safety-net install --cursor\`.`]}}function Vm(P){let O=Fr(P.environment);if(!Um(O))return{platform:"cursor",status:"n/a",configPath:O};let D;try{D=JSON.parse(Gm(O,"utf-8"))}catch(Y){return{platform:"cursor",status:"n/a",configPath:O,errors:[`Failed to parse Cursor hooks config ${O}: ${Y instanceof Error?Y.message:String(Y)}`]}}let j=qm(D);if(j.length===0)return{platform:"cursor",status:"n/a",configPath:O};let J=Bm(j);return{platform:"cursor",status:"configured",method:"hook config",configPath:O,errors:J.length>0?J:void 0}}import{readdirSync as Jm}from"node:fs";import{join as Jo,resolve as Wm}from"node:path";var Wo="cc-safety-net";function Ko(P){let O=P.env.get("DSH_HOME");return Jo(O?.trim()?Wm(Nn(O,P.home)):Jo(P.home,".dsh"),"profiles")}function el(P){let O=Ko(P),D=dn(O)?.isDirectory()?Jm(O,{withFileTypes:!0}).filter((J)=>J.isDirectory()&&J.name!=="node_modules").map((J)=>{let Y=Jo(O,J.name,"package.json");return{name:J.name,configPath:Y,manifest:xn(Y)}}):[];return{installed:D.flatMap((J)=>{if(J.manifest.kind!=="ok")return[];let Y=J.manifest.value;if(tn(tn(Y,"dependencies"),Wo)===void 0)return[];let re=tn(tn(tn(Y,"dsh"),"profile"),"bundles");return[{name:J.name,configPath:J.configPath,enabled:Array.isArray(re)&&re.includes(Wo)}]}),unreadable:D.some((J)=>J.manifest.kind==="unreadable")}}function zo(P){return el(P).installed}function nl(P){let O=el(P.environment),D=O.installed.filter((Y)=>Y.enabled),j=O.installed.filter((Y)=>!Y.enabled),J=j.map((Y)=>`${Wo} is installed in the ${Y.name} profile but its bundle is disabled`);if(D.length>0)return{platform:"deepseek-harness",status:"configured",method:"dsh bundle",configPaths:D.map((Y)=>Y.configPath),...J.length>0?{errors:J}:{}};if(j.length>0)return{platform:"deepseek-harness",status:"disabled",method:"dsh bundle",configPaths:j.map((Y)=>Y.configPath),errors:J};return O.unreadable?{platform:"deepseek-harness",status:"not-inspected"}:{platform:"deepseek-harness",status:"n/a"}}import{existsSync as Qm}from"node:fs";import{existsSync as ol,mkdirSync as Km,readFileSync as zm}from"node:fs";import{dirname as Ym,join as Xo}from"node:path";function Yo(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Zo(P,O){return Yo(P)&&P.command===O}function tl(P,O){return Yo(P)&&Array.isArray(P.hooks)&&P.hooks.some((D)=>Zo(D,O))}function Zn(P,O){return{hooks:[{type:"command",command:P,timeout:O}]}}function jn(P,O){return P.flatMap((D)=>{if(!Yo(D)||!Array.isArray(D.hooks))return[D];let j=D.hooks.filter((J)=>!Zo(J,O));if(j.length===D.hooks.length)return[D];return j.length===0?[]:[{...D,hooks:j}]})}function Et(P,O,D){let j=P.filter((J)=>tl(J,O));return j.length===1&&JSON.stringify(j[0])===JSON.stringify(Zn(O,D))}function _t(P,O){return P.find((D)=>tl(D,O))}function At(P,O,D){let j=Array.isArray(P.hooks)?P.hooks.find((J)=>Zo(J,O)):void 0;return[...P.matcher===void 0||P.matcher===""||P.matcher==="*"?[]:['Managed hook has a "matcher" that narrows coverage; reinstall to repair'],...j?.type==="command"?[]:['Managed hook "type" is not "command"; reinstall to repair'],...j?.timeout===D?[]:[`Managed hook "timeout" is not ${D}; reinstall to repair`]]}var Xn=Cn.devin,Mr=30,Zm={config:{},hooks:{},preToolUse:[]};function Ur(P,O=process.platform){let D=O==="win32"?P.env.get("APPDATA")||Xo(P.home,"AppData","Roaming"):P.env.get("XDG_CONFIG_HOME")||Xo(P.home,".config");return Xo(D,"devin","config.json")}function rl(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Xm(P){try{return{ok:!0,value:JSON.parse(vn(zm(P,"utf-8")))}}catch(O){return{ok:!1,message:O instanceof Error?O.message:String(O)}}}function Qo(P){let O=Xm(P);if(!O.ok)return`Failed to parse Devin CLI config ${P}: ${O.message}`;let D=O.value;if(!rl(D))return`Devin CLI config ${P} must be a JSON object`;let j=D.hooks===void 0?{}:D.hooks;if(!rl(j))return`Devin CLI config ${P} "hooks" must be an object`;let J=j.PreToolUse===void 0?[]:j.PreToolUse;if(!Array.isArray(J))return`Devin CLI config ${P} "hooks.PreToolUse" must be an array`;return{config:D,hooks:j,preToolUse:J}}function il(P){let O=Qo(P);if(typeof O==="string")throw Error(O);return O}function sl(P,O,D){let j={...O.config,hooks:{...O.hooks,PreToolUse:D}};cn(P,`${JSON.stringify(j,null,2)}
|
|
36
|
+
`)}function al(P){let O=Ur(P),D=ol(O)?il(O):Zm;if(Et(D.preToolUse,Xn,Mr))return{path:O,alreadyInstalled:!0};return Km(Ym(O),{recursive:!0}),sl(O,D,[...jn(D.preToolUse,Xn),Zn(Xn,Mr)]),{path:O,alreadyInstalled:!1}}function ll(P){let O=Ur(P);if(!ol(O))return{path:O,alreadyInstalled:!1};let D=il(O),j=jn(D.preToolUse,Xn);if(JSON.stringify(j)===JSON.stringify(D.preToolUse))return{path:O,alreadyInstalled:!1};return sl(O,D,j),{path:O,alreadyInstalled:!0}}function cl(P){let O=Ur(P.environment);if(!Qm(O))return{platform:"devin",status:"n/a",configPath:O};let D=Qo(O);if(typeof D==="string")return{platform:"devin",status:"n/a",configPath:O,errors:[D]};let j=_t(D.preToolUse,Xn);if(!j)return{platform:"devin",status:"n/a",configPath:O};let J=At(j,Xn,Mr);return{platform:"devin",status:"configured",method:"hook config",configPath:O,errors:J.length>0?J:void 0}}import{existsSync as ig,readFileSync as sg}from"node:fs";import{existsSync as Gr,mkdirSync as eg,readFileSync as ng,rmSync as tg}from"node:fs";import{dirname as rg,join as dl}from"node:path";var Qn=Cn.droid,qr=30;function Br(P){return dl(P.home,".factory","hooks.json")}function ul(P){return dl(P.home,".factory","settings.json")}function ei(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function pl(P){try{return JSON.parse(ng(P,"utf-8"))}catch(O){if(O instanceof SyntaxError)throw Error(`Failed to parse Factory Droid hooks config ${P}: ${O.message}`);throw O}}function fl(P){let O=pl(P);if(!ei(O))throw Error(`Factory Droid hooks config ${P} must be a JSON object`);if(O.PreToolUse===void 0||Array.isArray(O.PreToolUse))return O;throw Error(`Factory Droid hooks config ${P} "PreToolUse" must be an array`)}function og(P){if(!Gr(P))return{};let O=pl(P);return ei(O)&&ei(O.hooks)?O.hooks:{}}function ml(P){return Array.isArray(P.PreToolUse)?P.PreToolUse:[]}function gl(P,O,D){cn(P,`${JSON.stringify({...O,PreToolUse:D},null,2)}
|
|
37
|
+
`)}function yl(P){let O=Br(P),D=Gr(O),j=D?fl(O):og(ul(P)),J=ml(j);if(D&&Et(J,Qn,qr))return{path:O,alreadyInstalled:!0};return eg(rg(O),{recursive:!0}),gl(O,j,[...jn(J,Qn),Zn(Qn,qr)]),{path:O,alreadyInstalled:!1}}function hl(P){let O=Br(P);if(!Gr(O))return{path:O,alreadyInstalled:!1};let D=fl(O),j=ml(D),J=jn(j,Qn);if(JSON.stringify(J)===JSON.stringify(j))return{path:O,alreadyInstalled:!1};let Y=Gr(ul(P));if(J.length===0&&Object.keys(D).length===1&&!Y)return tg(O),{path:O,alreadyInstalled:!0};return gl(O,D,J),{path:O,alreadyInstalled:!0}}function vl(P){let O=Br(P.environment);if(!ig(O))return{platform:"droid",status:"n/a",configPath:O};let D;try{D=JSON.parse(sg(O,"utf-8"))}catch(re){return{platform:"droid",status:"n/a",configPath:O,errors:[`Failed to parse Factory Droid hooks config ${O}: ${re instanceof Error?re.message:String(re)}`]}}let j=typeof D==="object"&&D!==null&&"PreToolUse"in D?D.PreToolUse:void 0,J=_t(Array.isArray(j)?j:[],Qn);if(!J)return{platform:"droid",status:"n/a",configPath:O};let Y=[...J.commandRegex===void 0||J.commandRegex===""?[]:['Managed hook has a "commandRegex" that narrows coverage; reinstall to repair'],...At(J,Qn,qr)];return{platform:"droid",status:"configured",method:"hook config",configPath:O,errors:Y.length>0?Y:void 0}}import{existsSync as ag}from"node:fs";import{join as ni}from"node:path";var ti="gemini-safety-net";function ri(P){let O=ni(P.home,".gemini","extensions"),D=ni(O,ti);if(!ag(D))return{platform:"gemini-cli",status:"n/a"};let j=ni(O,"extension-enablement.json"),J=xn(j);if(J.kind==="unreadable")return{platform:"gemini-cli",status:"not-inspected"};let Y=J.kind==="ok"?tn(tn(J.value,ti),"overrides"):void 0;if(Array.isArray(Y)&&Y.some((ie)=>typeof ie==="string"&&ie.startsWith("!")))return{platform:"gemini-cli",status:"disabled",method:"extension config",configPath:j,errors:[`${ti} is disabled in Gemini CLI`]};return{platform:"gemini-cli",status:"configured",method:"extension config",configPath:D}}function bl(P){return ri(P.environment)}import{existsSync as ug,readFileSync as pg}from"node:fs";import{existsSync as kl,mkdirSync as lg,readFileSync as xl,rmSync as cg}from"node:fs";import{dirname as dg,join as wl}from"node:path";var rt=Cn["grok-build"],Jr=30,oi=Zn(rt,Jr);function Wr(P){return wl(P.env.get("GROK_HOME")??wl(P.home,".grok"),"hooks","cc-safety-net.json")}function Kr(P){return typeof P==="object"&&P!==null&&!Array.isArray(P)}function Cl(P){try{let O=JSON.parse(P);return Kr(O)?O:null}catch{return null}}function Sl(P){let O=Kr(P.hooks)?P.hooks.PreToolUse:void 0;return Array.isArray(O)?O:[]}function Vr(P,O,D){let j=Kr(O.hooks)?O.hooks:{};cn(P,`${JSON.stringify({...O,hooks:{...j,PreToolUse:D}},null,2)}
|
|
38
|
+
`)}function Pl(P){let O=Wr(P);if(!kl(O))return lg(dg(O),{recursive:!0}),Vr(O,{},[oi]),{path:O,alreadyInstalled:!1};let D=Cl(xl(O,"utf-8"));if(!D)return Vr(O,{},[oi]),{path:O,alreadyInstalled:!1};let j=Sl(D);if(Et(j,rt,Jr))return{path:O,alreadyInstalled:!0};return Vr(O,D,[...jn(j,rt),oi]),{path:O,alreadyInstalled:!1}}function Rl(P){let O=Wr(P);if(!kl(O))return{path:O,alreadyInstalled:!1};let D=Cl(xl(O,"utf-8"));if(!D)return{path:O,alreadyInstalled:!1};let j=Sl(D),J=jn(j,rt);if(JSON.stringify(J)===JSON.stringify(j))return{path:O,alreadyInstalled:!1};let Y=Kr(D.hooks)?D.hooks:{};if(J.length===0&&Object.keys(D).length===1&&Object.keys(Y).length===1)return cg(O),{path:O,alreadyInstalled:!0};return Vr(O,D,J),{path:O,alreadyInstalled:!0}}function El(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function _l(P){let O=Wr(P.environment);if(!ug(O))return{platform:"grok-build",status:"n/a",configPath:O};let D;try{D=JSON.parse(pg(O,"utf-8"))}catch(re){return{platform:"grok-build",status:"n/a",configPath:O,errors:[`Failed to parse Grok Build hooks config ${O}: ${re instanceof Error?re.message:String(re)}`]}}let j=El(D)&&El(D.hooks)?D.hooks.PreToolUse:void 0,J=_t(Array.isArray(j)?j:[],rt);if(!J)return{platform:"grok-build",status:"n/a",configPath:O};let Y=At(J,rt,Jr);return{platform:"grok-build",status:"configured",method:"hook config",configPath:O,errors:Y.length>0?Y:void 0}}import{readFileSync as jl}from"node:fs";import{join as Fl}from"node:path";var Rn="cc-safety-net",ii="# cc-safety-net managed Hermes Agent plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --hermes-agent",fg=30;function Al(P){return`${ii}
|
|
39
39
|
# version: ${P}
|
|
40
|
-
`}function
|
|
40
|
+
`}function mg(P){return`${Al(P)}name: ${Rn}
|
|
41
41
|
version: "${P}"
|
|
42
42
|
description: "Block destructive commands and secret-file access before Hermes runs a tool."
|
|
43
43
|
author: "cc-safety-net"
|
|
44
44
|
provides_hooks:
|
|
45
45
|
- pre_tool_call
|
|
46
|
-
`}function
|
|
46
|
+
`}function gg(P){return`${Al(P)}"""CC Safety Net guard for Hermes Agent.
|
|
47
47
|
|
|
48
48
|
Registers pre_tool_call and forwards the tool call to the packaged CC Safety Net
|
|
49
49
|
adapter (cc-safety-net hook --hermes-agent) over JSON stdin. The adapter prints nothing
|
|
@@ -59,8 +59,8 @@ import subprocess
|
|
|
59
59
|
|
|
60
60
|
HOOK_EVENT = "pre_tool_call"
|
|
61
61
|
SUPPORTED_TOOLS = ("patch", "read_file", "terminal", "write_file")
|
|
62
|
-
ANALYZER = [${
|
|
63
|
-
TIMEOUT_SECONDS = ${
|
|
62
|
+
ANALYZER = [${Cn["hermes-agent"].split(" ").map((O)=>`"${O}"`).join(", ")}]
|
|
63
|
+
TIMEOUT_SECONDS = ${fg}
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def _block(detail):
|
|
@@ -213,85 +213,85 @@ def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
|
213
213
|
|
|
214
214
|
def register(ctx):
|
|
215
215
|
ctx.register_hook("pre_tool_call", _pre_tool_call)
|
|
216
|
-
`}function
|
|
217
|
-
`)}function
|
|
218
|
-
`),de=O?.timeoutMs??120000,fe=setTimeout(()=>{Y.kill(),j(Error(
|
|
219
|
-
${ie()}`.trim())))},de);Y.on("error",(we)=>{clearTimeout(fe),j(Error(
|
|
220
|
-
${ie()}`.trim())))}),Y.on("close",(we)=>{if(clearTimeout(fe),we!==0){j(Error(
|
|
221
|
-
`)[0]}`}function
|
|
222
|
-
`));return{path:J[0]??D??An(bi(P),vi[0]),alreadyInstalled:J.length>0}}function Ot(P){let O=[];for(let D of P.openCodeVersion?.startsWith("2.")?wi(P.environment):ki(P.environment))if(ey(D))try{let j=ny(D,"utf-8"),J=vn(j),Y=JSON.parse(J);if(Si(Y)){let re=xi(P.openCodePluginListOutput);if(re)return{platform:"opencode",status:"disabled",method:"opencode api plugin.list",configPath:D,errors:[...O,re]};return{platform:"opencode",status:"configured",method:"plugin array",configPath:D,errors:O.length>0?O:void 0}}}catch(j){O.push(`Failed to parse ${ty(D)}: ${j instanceof Error?j.message:String(j)}`)}return{platform:"opencode",status:"n/a",errors:O.length>0?O:void 0}}import{join as lc}from"node:path";function Pi(P){let O=P.env.get("PI_CODING_AGENT_DIR");return lc(O?Nn(O,P.home):lc(P.home,".pi","agent"),"settings.json")}function Ri(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function cc(P){let O=Pi(P.environment),D=Pn(O);if(D.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(D.kind==="missing")return{platform:"pi",status:"n/a"};let j=tn(D.value,"packages");if(!Array.isArray(j))return{platform:"pi",status:"n/a"};let J=j.find((ie)=>Ri(typeof ie==="string"?ie:tn(ie,"source")));if(J===void 0)return{platform:"pi",status:"n/a"};let Y=tn(J,"extensions");if(Array.isArray(Y)&&Y.some((ie)=>typeof ie==="string"&&ie.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:O,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:O}}var ry={amp:ka,"antigravity-cli":xa,"claude-code":Pa,codex:Ea,"copilot-cli":qa,cursor:Za,"deepseek-harness":Qa,devin:al,droid:yl,"gemini-cli":hl,"grok-build":Rl,"hermes-agent":Fl,"kimi-code":Kt,openclaw:Xl,opencode:Ot,pi:cc};function Dt(P,O,D){let j={...D,cwd:O,environment:P};return fr.map((J)=>oy(ry[J](j)))}function oy(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 iy}from"node:path";var sy=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}]),ay=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([])})})}),ly={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 dc(P){let O=iy(P.tmpdir,"cc-safety-net-self-test"),D=sy.map((j)=>{let J=G(P,u("self-test",{command:j.command},{kind:"command",shell:"auto"},{configCwd:O,executionCwd:O},j.command),{guard:{dependencies:{loadPolicySnapshot:()=>ay,getModes:()=>ly,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),Y=j.expectBlocked?"blocked":"allowed",re=J.decision.kind==="deny"?"blocked":"allowed";return{command:j.command,description:j.description,expected:Y,actual:re,passed:Y===re,reason:J.decision.kind==="deny"?J.decision.reason:void 0,ruleId:J.decision.kind==="deny"?J.decision.ruleId:void 0}});return{passed:D.filter((j)=>j.passed).length,failed:D.filter((j)=>!j.passed).length,total:D.length,results:D}}function Ei(P){let O=gn({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Dn(O.errors))return null;return{json:O.flags.json,skipUpdateCheck:O.flags.skipUpdateCheck}}async function uc(P,O={}){let D=await Ut(!O.json,()=>{let j=dy(P,O);return{ready:j,finish:()=>j}},()=>Mt(),{loadingMessage:"Checking system status…"});if(O.json)console.log(JSON.stringify(D,null,2));else uy(D);return D.engineSelfTest.failed>0||D.findings.some((j)=>j.severity==="error")?1:0}async function dy(P,O){let D=O.cwd??process.cwd(),j=await xr((Me)=>Ot({environment:P,cwd:D,openCodeVersion:Me}).status!=="n/a",void 0,D),J=Dt(P,D,{ampPluginListOutput:j.ampPluginListOutput,codexPluginListOutput:j.codexPluginListOutput,copilotCliVersion:j.versions["copilot-cli"],openCodeVersion:j.versions.opencode,openCodePluginListOutput:j.openCodePluginListOutput}),Y=js(P,D),re=Fs(P),ie=k(P,{cwd:D}),de=ie.policy,fe=T(de,P.env),we=q(de,fe.capabilities),Ce=hr(P,7),Se=ya(P,D),Ie=[vr(D),Ct(P)].filter((Me)=>cy(Me)),Ee=O.skipUpdateCheck?{currentVersion:pn(),latestVersion:null,updateAvailable:!1}:await Gn(),Ve={hooks:J,engineSelfTest:dc(P),userConfig:Y.userConfig,projectConfig:Y.projectConfig,configState:je(ie),effectiveRules:Y.effectiveRules,environment:re,effectiveSafety:{selectedPreset:de.safety.level??"standard",level:fe.effectiveLevel,capabilities:fe.capabilities,ruleOverrides:de.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(we).filter(([,Me])=>Me.source==="rule_override"&&Me.override==="off"&&Me.inheritedEnabled&&Me.changesInherited).map(([Me])=>Me),ruleCounts:{stored:Object.keys(de.destructiveCommandRuleOverrides).length,effective:Object.values(we).filter((Me)=>Me.changesInherited).length},...ie.policyScopes?{policyScopes:ie.policyScopes}:{}},...Se.length>0?{v2Leftovers:Se}:{},...Ie.length>0?{legacyConfigs:Ie}:{},posture:Xs(P,Y.userConfig.path),activity:Ce,update:Ee,system:j};return{...Ve,findings:Ms(Ve)}}function uy(P){console.log(),console.log(Gs(P.hooks)),console.log(),console.log(qs(P.engineSelfTest)),console.log(),console.log(Bs(P)),console.log(),console.log(Vs(P.environment)),console.log(),console.log(Js(P)),console.log(),console.log(Ws(P.findings)),console.log(),console.log(Ks(P.activity)),console.log(),console.log(Ys(P.system)),console.log(),console.log(zs(P.update)),console.log(Zs(P))}import{existsSync as py}from"node:fs";var fy=/^[A-Za-z0-9_@%+=:,./-]+$/,pc="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function _i(P){let O=gn({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Dn(O.errors))return console.error(pc),console.error("Pass -- before a command that starts with dashes."),null;if(O.values.cwd!==void 0&&!py(O.values.cwd))return console.error(`Error: --cwd path does not exist: ${O.values.cwd}`),null;let D=O.positionals.length===1?O.positionals[0]:O.positionals.map((j)=>fy.test(j)?j:`'${j.replaceAll("'","'\\''")}'`).join(" ");if(!D)return console.error("Error: No command provided"),console.error(pc),null;return{json:O.flags.json,cwd:O.values.cwd,command:D}}function fc(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 mc(P,O){let j=O-18;return[`${P.dtl}${P.dh.repeat(O)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(j)}${P.dv}`,`${P.dbl}${P.dh.repeat(O)}${P.dbr}`]}function Ti(P){return JSON.stringify(P)}function gc(P,O=0){return`[${P.map((j,J)=>Us(j,J,O)).join(",")}]`}function Xt(P,O,D=70){let j=P.split(" "),J=[],Y="";for(let re of j)if(Y&&Y.length+re.length+1>D)J.push(Y),Y=re;else Y=Y?`${Y} ${re}`:re;if(Y)J.push(Y);return J.map((re,ie)=>ie===0?re:`${O}${re}`)}function yc(P,O,D){let j=[];switch(P.type){case"parse":return null;case"env-strip":return j.push(""),j.push(`STEP ${O} ${D.h} Strip environment variables`),j.push(` Removed: ${P.envVars.map((J)=>`${J}=<redacted>`).join(", ")}`),j.push(` Tokens: ${Ti(P.output)}`),{lines:j,incrementStep:!0};case"leading-tokens-stripped":return j.push(""),j.push(`STEP ${O} ${D.h} Strip wrappers`),j.push(` Removed: ${P.removed.join(", ")}`),j.push(` Tokens: ${Ti(P.output)}`),{lines:j,incrementStep:!0};case"shell-wrapper":return j.push(""),j.push(`STEP ${O} ${D.h} Detect shell wrapper`),j.push(` Wrapper: ${P.wrapper} -c`),j.push(` Inner: ${P.innerCommand}`),{lines:j,incrementStep:!0};case"interpreter":{if(j.push(""),j.push(`STEP ${O} ${D.h} Detect interpreter`),j.push(` Interpreter: ${P.interpreter}`),j.push(` Code: ${P.codeArg}`),P.paranoidBlocked)j.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:j,incrementStep:!0}}case"busybox":return j.push(""),j.push(`STEP ${O} ${D.h} Busybox wrapper`),j.push(` Subcommand: ${P.subcommand}`),{lines:j,incrementStep:!0};case"transparent-wrapper":return j.push(""),j.push(`STEP ${O} ${D.h} Transparent wrapper`),j.push(` Wrapper: ${P.wrapper}`),j.push(` Tokens: ${Ti(P.output)}`),{lines:j,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(j.push(""),j.push(`STEP ${O} ${D.h} Match rules`),j.push(` Rule: ${P.rule}()`),P.matched)j.push(" Result: MATCHED");else j.push(" Result: No match");return{lines:j,incrementStep:!0}}case"worktree-relaxation":return j.push(""),j.push(`STEP ${O} ${D.h} Worktree relaxation`),j.push(` Mode: ${n.worktree.name}`),j.push(` Git cwd: ${P.gitCwd}`),j.push(" Result: Allowed local discard in linked worktree"),{lines:j,incrementStep:!0};case"temp-root-relaxation":return j.push(""),j.push(`STEP ${O} ${D.h} Temp-root relaxation`),j.push(` Git cwd: ${P.gitCwd}`),j.push(" Result: Allowed git discard in a temp-root repository"),{lines:j,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return j.push(""),j.push(`STEP ${O} ${D.h} Fallback scan`),j.push(` Found: ${P.embeddedCommandFound}`),{lines:j,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(j.push(""),j.push(`STEP ${O} ${D.h} Custom rules`),P.matched)j.push(" Result: MATCHED");else j.push(" Result: No match");return{lines:j,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return j.push(""),j.push(`STEP ${O} ${D.h} Dangerous text check`),j.push(` Token: ${P.token}`),j.push(" Result: MATCHED"),{lines:j,incrementStep:!0};return null}case"strict-unparseable":return j.push(""),j.push(`STEP ${O} ${D.h} Strict mode check`),j.push(` Command: ${P.rawCommand}`),j.push(" Result: ✗ UNPARSEABLE"),{lines:j,incrementStep:!0};case"segment-skipped":return null;case"error":return j.push(""),j.push(`ERROR: ${P.message}`),{lines:j,incrementStep:!1};default:return P}}function Ai(P,O){let D=fc(O?.asciiOnly??!1),j=58,J=[],Y=1;J.push(...mc(D,58)),J.push("");let re=P.trace.steps.find((Ee)=>Ee.type==="error");if(re&&re.type==="error"){J.push("ERROR"),J.push(` ${re.message}`),J.push(""),J.push("RESULT"),J.push(` Status: ${P.result==="blocked"?nn.red("BLOCKED"):nn.green("ALLOWED")}`),J.push(""),J.push("CONFIG");let Ee=P.configSource??"none";return J.push(` Path: ${Ee}`),J.join(`
|
|
223
|
-
`)}let ie=P.trace.steps.find((Ee)=>Ee.type==="parse");if(ie&&ie.type==="parse"){J.push("INPUT"),J.push(` ${ie.input}`),J.push(""),J.push(`STEP ${Y} ${D.h} Split shell commands`),Y++;for(let Ee=0;Ee<ie.segments.length;Ee++){let Ve=ie.segments[Ee];if(Ve){let Me=Math.random();J.push(` Segment ${Ee+1}: ${
|
|
224
|
-
`)}function
|
|
225
|
-
`,()=>O())})}async function
|
|
226
|
-
`))}function
|
|
227
|
-
`))}function
|
|
228
|
-
`)}var
|
|
229
|
-
`)}function
|
|
216
|
+
`}function Wt(P){return[{name:"__init__.py",content:gg(P)},{name:"plugin.yaml",content:mg(P)}]}import{mkdirSync as yg,readdirSync as hg,readFileSync as Tl,rmSync as si}from"node:fs";import{basename as vg,dirname as bg,join as Fn}from"node:path";var wg="__pycache__",kg=/^[a-z0-9][a-z0-9_-]{0,63}$/;function xg(P){try{return Tl(P,"utf-8").trim()}catch{return}}function ai(P){let O=P.env.get("HERMES_HOME")?.trim();if(O&&vg(bg(O))==="profiles")return O;let D=O||Fn(P.home,".hermes"),j=Fn(D,"active_profile"),J=xg(j),Y=J?.toLowerCase();if(!Y||Y==="default")return D;if(!kg.test(Y))throw Error(`Invalid Hermes profile name "${J}" in ${j}; run \`hermes profile use <name>\` with a valid profile.`);return Fn(D,"profiles",Y)}function li(P){return Fn(ai(P),"plugins",Rn)}function ci(P){return P.startsWith(ii)}function di(P,O){let D=li(P),j=dn(D);if(j&&(j.isSymbolicLink()||!j.isDirectory()))throw Error(`Refusing to ${O} ${D}: not a regular directory. Move or remove it and rerun ${O==="install"?"install":"uninstall"} --hermes-agent.`);return D}function Il(P,O){let D=dn(P);if(!D)return;if(D.isSymbolicLink()||!D.isFile())throw Error(`Refusing to ${O} ${P}: not a regular file. Move or remove it.`);let j=Tl(P,"utf-8");if(!ci(j))throw Error(`Refusing to ${O} unmanaged file at ${P}. Move or remove it.`);return j}function $l(P){let O=di(P,"install"),D=Wt(pn());if(D.map((J)=>Il(Fn(O,J.name),"overwrite")).every((J,Y)=>J===D[Y]?.content))return{path:O,alreadyInstalled:!0};return yg(O,{recursive:!0}),D.forEach((J)=>{cn(Fn(O,J.name),J.content)}),{path:O,alreadyInstalled:!1}}function ui(P){let O=di(P,"remove");if(!dn(O))return[];return Wt(pn()).filter((D)=>Il(Fn(O,D.name),"remove")!==void 0)}function Ol(P){let O=di(P,"remove");if(!dn(O))return{path:O,alreadyInstalled:!1};let D=ui(P);if(D.forEach((j)=>{si(Fn(O,j.name))}),si(Fn(O,wg),{recursive:!0,force:!0}),hg(O).length===0)si(O,{recursive:!0});return{path:O,alreadyInstalled:D.length>0}}var Kt="hermes-agent",Dl=/^([^\s#][^:]*):/,Cg=/^\s+([A-Za-z_][\w-]*):/,Ll=/^\s+-\s*(.*)$/;function Sg(P){return P.trim().replace(/^(["'])(.*)\1$/,"$2")}function Pg(P){let O=P.split(/\r?\n/),D=O.findIndex((Y)=>Dl.exec(Y)?.[1]?.trim()==="plugins");if(D===-1)return[];let j=O.slice(D+1),J=j.findIndex((Y)=>Dl.test(Y));return J===-1?j:j.slice(0,J)}function Nl(P,O){let D=Pg(P),j=D.findIndex((re)=>Cg.exec(re)?.[1]===O);if(j===-1)return[];let J=D.slice(j+1),Y=J.findIndex((re)=>!Ll.test(re));return(Y===-1?J:J.slice(0,Y)).map((re)=>Sg(Ll.exec(re)?.[1]??""))}function Rg(P){try{return jl(Fl(ai(P),"config.yaml"),"utf-8")}catch{return}}function pi(P){let O=Rg(P)??"";return Nl(O,"enabled").includes(Rn)&&!Nl(O,"disabled").includes(Rn)}function Hl(P){return/^# version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function Eg(P,O){let D=dn(P);if(!D)return{error:`${O.name} is missing from ${P}; run install --hermes-agent`};if(D.isSymbolicLink()||!D.isFile())return{error:`${P} is a symlink or not a regular file; move or remove it`};try{let j=jl(P,"utf-8");if(!ci(j))return{error:`Unmanaged ${O.name} occupies ${P}; move or remove it`};if(Hl(j)===pn()&&j!==O.content)return{error:`Modified ${O.name} occupies ${P}; run install --hermes-agent to restore it`};return{content:j}}catch(j){return{error:`Failed to read ${P}: ${j instanceof Error?j.message:String(j)}`}}}function _g(P){try{return{path:li(P)}}catch(O){return{error:O instanceof Error?O.message:String(O)}}}function Ml(P){let O=_g(P.environment);if("error"in O)return{platform:Kt,status:"n/a",errors:[O.error]};let D=O.path,j=_r(Kt,D);if(j)return j;let J=Wt(pn()).map((de)=>Eg(Fl(D,de.name),de)),Y=J.flatMap((de)=>("error"in de)?[de.error]:[]);if(Y.length>0)return{platform:Kt,status:"n/a",configPath:D,errors:Y};let re=J.some((de)=>("content"in de)&&Hl(de.content)!==pn()),ie=re?["Installed Hermes Agent plugin is outdated; run install --hermes-agent to update"]:[];if(!pi(P.environment))return{platform:Kt,status:"disabled",method:"plugin directory",configPath:D,errors:[`${Rn} is not enabled in Hermes; run \`hermes plugins enable ${Rn}\``,...ie]};return{platform:Kt,status:"configured",method:"plugin directory",configPath:D,errors:re?ie:void 0}}import{existsSync as Ag,readFileSync as Tg}from"node:fs";import{join as Ul}from"node:path";var Ig=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-code(\s|["']|$)/;function $g(P){return Ul(P.env.get("KIMI_CODE_HOME")||Ul(P.home,".kimi-code"),"config.toml")}function zt(P){let O=$g(P.environment);if(!Ag(O))return{platform:"kimi-code",status:"n/a",configPath:O};try{if(!Ig.test(Tg(O,"utf-8")))return{platform:"kimi-code",status:"n/a",configPath:O}}catch(D){return{platform:"kimi-code",status:"n/a",configPath:O,errors:[`Failed to read ${O}: ${D instanceof Error?D.message:String(D)}`]}}return{platform:"kimi-code",status:"configured",method:"hook config",configPath:O}}import{readFileSync as Zl}from"node:fs";import{join as Zt}from"node:path";var un="cc-safety-net",Pn="index.js",Tt="openclaw.plugin.json",It="package.json";var zr="// cc-safety-net managed OpenClaw plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --openclaw";import{existsSync as Lg,lstatSync as Ng,readdirSync as jg,readFileSync as Fg}from"node:fs";import{dirname as ql,join as qn}from"node:path";import{fileURLToPath as Hg}from"node:url";import{spawn as Og}from"node:child_process";function Dg(P){return P.join(" ")}function fi(P,O,D){return[`Failed to run ${Dg(P)}${O===null?"":` (exit ${O})`}.`,D.trim()].filter(Boolean).join(`
|
|
217
|
+
`)}function mi(P){let O={stdout:"",stderr:""};return P.stdout.setEncoding("utf-8"),P.stderr.setEncoding("utf-8"),P.stdout.on("data",(D)=>{O.stdout+=D}),P.stderr.on("data",(D)=>{O.stderr+=D}),O}function hn(P,O){return new Promise((D,j)=>{let J=zn([...P],process.env),Y=Og(J.cmd,J.args,{stdio:["ignore","pipe","pipe"]}),re=mi(Y),ie=()=>[re.stdout,re.stderr].filter(Boolean).join(`
|
|
218
|
+
`),de=O?.timeoutMs??120000,fe=setTimeout(()=>{Y.kill(),j(Error(fi(P,null,`Timed out after ${de}ms.
|
|
219
|
+
${ie()}`.trim())))},de);Y.on("error",(we)=>{clearTimeout(fe),j(Error(fi(P,null,`${we.message}
|
|
220
|
+
${ie()}`.trim())))}),Y.on("close",(we)=>{if(clearTimeout(fe),we!==0){j(Error(fi(P,we,ie())));return}D(O?.stdoutOnly?re.stdout:ie())})})}async function gi(P){for(let O of P)await hn(O)}async function Gl(P){for(let O of P)try{await hn(O)}catch(D){console.warn(D instanceof Error?D.message:String(D))}}var yi=qn("openclaw",un),it=`run \`openclaw plugins enable ${un}\``,Mg="config reload superseded by a newer runtime config source",Ug=[Pn,Tt,It];function Bl(P){let O=P.env.get("OPENCLAW_HOME")?.trim();return O?Nn(O,P.home):P.home}function Vl(P){let O=Bl(P),D=P.env.get("OPENCLAW_STATE_DIR")?.trim();if(D)return Nn(D,O);let j=P.env.get("OPENCLAW_CONFIG_PATH")?.trim();return j?ql(Nn(j,O)):qn(O,".openclaw")}function Jl(P){let O=P.env.get("OPENCLAW_CONFIG_PATH")?.trim();return O?Nn(O,Bl(P)):qn(Vl(P),"openclaw.json")}function Yt(P){return qn(Vl(P),"extensions",un)}function Gg(P){let O=jg(P);if(O.length===0)return!0;if(O.some((J)=>!Ug.includes(J)))return!1;let D=qn(P,Pn),j=dn(D);return j!==void 0&&!j.isSymbolicLink()&&j.isFile()&&Fg(D,"utf-8").startsWith(zr)}function hi(P){let O=Yt(P),D=dn(O);if(!D)return;if(!D.isSymbolicLink()&&D.isDirectory()&&Gg(O))return;throw Error(`Refusing to modify ${O}: it does not hold a cc-safety-net managed OpenClaw plugin. Move or remove it, then run the command again.`)}function Wl(){let P=ql(Hg(import.meta.url));return[qn(P,yi),qn(P,"..",yi),qn(P,"..","..","..","dist",yi)]}function vi(P=Wl()){return P.find((O)=>Lg(O)&&Ng(O).isDirectory())}function qg(P=Wl()){let O=vi(P);if(!O)throw Error("Packaged OpenClaw plugin directory not found. Reinstall cc-safety-net and try again.");return O}function Kl(P=qg()){return[["openclaw","plugins","install",P,"--force","--accept-capabilities"]]}function Bg(P){let O=(()=>{try{return JSON.parse(P)}catch{return}})(),D=tn(tn(O,"plugin"),"status");return typeof D==="string"?D:void 0}async function Vg(){await hn(["openclaw","plugins","enable",un]).catch((P)=>{if(!(P instanceof Error&&P.message.includes(Mg)))throw P})}async function zl(P){let O=async()=>Bg(await hn(["openclaw","plugins","inspect",un,"--runtime","--json"],{stdoutOnly:!0})),D=await O(),j=D==="disabled"&&P;if(j)await Vg();let J=j?await O():D;if(J==="loaded")return;throw Error(`${J===void 0?`The ${un} plugin's load state could not be verified: OpenClaw's runtime inspect report was unreadable.`:J==="disabled"?`OpenClaw reports the ${un} plugin with status "disabled"; ${it}.`:`OpenClaw reports the ${un} plugin with status "${J}".`} Run \`openclaw plugins inspect ${un} --runtime\` for details.`)}var Yr="openclaw";function $t(P,O){let D=Zt(P,O),j=dn(D);if(!j)return{error:`${O} is missing from ${D}; run install --openclaw`};if(j.isSymbolicLink()||!j.isFile())return{error:`${D} is a symlink or not a regular file; move or remove it`};try{return{content:Zl(D,"utf-8")}}catch(J){return{error:`Failed to read ${D}: ${J instanceof Error?J.message:String(J)}`}}}function Xl(P){try{return JSON.parse(vn(P))}catch{return}}function Jg(P){let O=$t(P,Tt);if("error"in O)return O.error;if(tn(Xl(O.content),"id")===un)return;return`${Zt(P,Tt)} is not a valid ${un} manifest; run install --openclaw`}function Wg(P){let O=$t(P,It);if("error"in O)return O.error;let D=tn(tn(Xl(O.content),"openclaw"),"extensions");if(Array.isArray(D)&&D.includes(`./${Pn}`))return;return`${Zt(P,It)} does not point OpenClaw at ${Pn}; run install --openclaw`}function Yl(P){return Array.isArray(P)?P.filter((O)=>typeof O==="string"):[]}function Kg(P){let O=Jl(P);if(!dn(O))return`${un} is not enabled; ${it}`;let D=(()=>{try{return JSON.parse(vn(Zl(O,"utf-8")))}catch{return}})();if(D===void 0)return`Failed to read ${O}; fix it, then ${it}`;let j=tn(D,"plugins");if(tn(j,"enabled")===!1)return`plugins.enabled is false in ${O}; no OpenClaw plugin loads`;let J=tn(tn(tn(j,"entries"),un),"enabled");if(Yl(tn(j,"deny")).includes(un)||J===!1)return`${un} is disabled in ${O}; ${it}`;let Y=Yl(tn(j,"allow"));if(Y.length>0&&!Y.includes(un))return`plugins.allow in ${O} does not list ${un}; add it, then ${it}`;if(Y.includes(un)||J===!0)return;return`${un} is not enabled; ${it}`}function Ql(P){return/^\/\/ version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function zg(P,O,D){if(D===void 0)return[];let j=$t(D,Pn);if(!(("content"in j)&&Ql(j.content)===O))return[];return[Pn,Tt,It].flatMap((Y)=>{let re=$t(P,Y),ie=$t(D,Y);if("error"in re||"error"in ie||re.content===ie.content)return[];return[`Modified ${Y} occupies ${Zt(P,Y)}; run install --openclaw to restore it`]})}function ec(P){let O=Yt(P.environment),D=_r(Yr,O);if(D)return D;let j=$t(O,Pn),Y=["error"in j?j.error:j.content.startsWith(zr)?void 0:`Unmanaged ${Pn} occupies ${Zt(O,Pn)}; move or remove it`,Jg(O),Wg(O)].filter((we)=>we!==void 0),re="content"in j?Ql(j.content):void 0,ie=Y.length>0?Y:zg(O,re,vi());if(ie.length>0)return{platform:Yr,status:"n/a",configPath:O,errors:ie};let de=re===pn()?[]:["Installed OpenClaw plugin is outdated; run install --openclaw to update"],fe=Kg(P.environment);if(fe)return{platform:Yr,status:"disabled",method:"plugin directory",configPath:O,errors:[fe,...de]};return{platform:Yr,status:"configured",method:"plugin directory",configPath:O,errors:de.length>0?de:void 0}}import{existsSync as oy,readFileSync as iy}from"node:fs";import{basename as sy}from"node:path";import{existsSync as Zr,readFileSync as bi,rmSync as Yg}from"node:fs";import{join as Tn}from"node:path";import{pathToFileURL as Zg}from"node:url";var Xt="cc-safety-net",pt=`${Xt}@latest`,wi=["opencode.json","opencode.jsonc"],Xg=60,Qg=250,nc="CCSafetyNetPlugin",ey={stringError:"Unterminated string in OpenCode config",bracketError:"Unmatched plugin array in OpenCode config"};function tc(P){return Tn(P.env.get("XDG_CONFIG_HOME")||Tn(P.home,".config"),"opencode")}function ki(P){return P.env.get("OPENCODE_CONFIG_DIR")||tc(P)}function xi(P){return wi.map((O)=>Tn(ki(P),O))}function Ci(P){return[...new Set([ki(P),tc(P)])].flatMap((O)=>wi.map((D)=>Tn(O,D)))}function rc(P){return Tn(P.env.get("XDG_CACHE_HOME")||Tn(P.home,".cache"),"opencode","packages",pt)}function oc(P){Yg(rc(P),{recursive:!0,force:!0})}async function ic(P){let O=(await hn(["opencode","--version"],{stdoutOnly:!0})).trim(),D=/^(?:opencode\s+)?v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(O),j=Number(D?.[1]),J=Number(D?.[2]),Y=Number(D?.[3]);if(!D||j!==1&&j!==2||j===1&&(J<18||J===18&&Y<29)||j===2&&J===0&&Y<6)throw Error(`OpenCode 1.18.29+ or 2.0.6+ is required; found ${O||"an unknown version"}.`);if(j===2){for(let re of xi(P)){if(!Zr(re))continue;let ie=Pi(bi(re,"utf-8"),re);if(["plugin","plugins"].some((fe)=>{let we=tn(ie,fe);return Array.isArray(we)&&we.some((Ce)=>Xr(Ce)&&(typeof Ce==="string"?Ce:tn(Ce,"package"))!==pt)}))throw Error(`Change the cc-safety-net package spec in ${re} to ${pt}, preserving its options, then retry. Adding another spec would create a duplicate plugin ID.`)}return{commands:[],afterInstall:async()=>{if((await hn(["opencode","plugin","add",pt],{stdoutOnly:!0})).includes("is already configured in"))await hn(["opencode","plugin","update",pt]);let ie=await lc();if(!/^cc-safety-net\s+\S+\s+cc-safety-net@latest\s*$/m.test(ie))throw Error("OpenCode did not load cc-safety-net from cc-safety-net@latest. Run `opencode plugin list` for details.");let de=["--param",`location[directory]=${process.cwd()}`];await hn(["opencode","api","integration.list",...de]);let fe=await hn(["opencode","api","plugin.list",...de],{stdoutOnly:!0}),we=Si(fe);if(we)throw Error(we);if(!sc(fe).some(ac))throw Error("OpenCode lists no active cc-safety-net for this directory. Run `opencode api plugin.list` for details.")}}}return oc(P),{commands:[["opencode","plugin","-g","-f",pt]],afterInstall:()=>ty(P)}}function sc(P){return ny(P).filter((O)=>tn(O,"id")===Xt||Xr(tn(tn(O,"source"),"target"))).map((O)=>tn(O,"state"))}function ac(P){return tn(P,"status")==="active"}function Si(P){let O=sc(P);if(O.some(ac))return;let D=O.find((j)=>tn(j,"status")==="failed");if(!D)return;return`OpenCode reports cc-safety-net failed: ${String(tn(D,"error")).split(`
|
|
221
|
+
`)[0]}`}function ny(P){if(!P)return[];try{let O=tn(JSON.parse(P),"data");return Array.isArray(O)?O:[]}catch{return[]}}async function lc(P=1){let O=await hn(["opencode","plugin","list"],{stdoutOnly:!0});if(/^cc-safety-net\s|\scc-safety-net@latest\s*$/m.test(O)||P===Xg)return O;return await new Promise((D)=>setTimeout(D,Qg)),lc(P+1)}async function ty(P){let O=Tn(rc(P),"node_modules",Xt),D=Tn(O,"package.json");if(!Zr(D))throw Error(`The OpenCode plugin cache at ${O} is missing its package, so OpenCode would load nothing and fail open. Run \`opencode plugin -g -f ${pt}\` for details.`);let j=tn(JSON.parse(bi(D,"utf-8")),"main");if(typeof j!=="string")throw Error(`The cached OpenCode plugin at ${O} declares no "main" entry.`);let J=Tn(O,j);if(typeof(await import(Zg(J).href))[nc]==="function")return;throw Error(`The cached OpenCode plugin at ${J} does not export a callable ${nc}, so OpenCode would load nothing and fail open.`)}function Pi(P,O){try{return JSON.parse(vn(P))}catch(D){if(D instanceof SyntaxError)throw Error(`Failed to parse OpenCode config ${O}: ${D.message}`);throw D}}function Xr(P){let O=typeof P==="string"?P:tn(P,"package");return typeof O==="string"&&(O===Xt||O.startsWith(`${Xt}@`))}function Ri(P){return["plugin","plugins"].some((O)=>{let D=tn(P,O);return Array.isArray(D)&&D.some(Xr)})}function ry(P,O){let j=["plugin","plugins"].flatMap((J)=>{let Y=Da(P,J,ey);if(!Y)return[];let re=[],ie=0,de=Y.start+1,fe=P.slice(Y.start+1,Y.end).matchAll(/\/\/[^\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|[^"\s{}[\],/]+|[{}[\],]/g);for(let we of fe){if(we[0].startsWith("//")||we[0].startsWith("/*"))continue;let Ce=Y.start+1+we.index;if(ie===0)de=Ce;if(we[0]==="{"||we[0]==="[")ie++;if(we[0]==="}"||we[0]==="]")ie--;if(ie!==0||we[0]===",")continue;let Se=Ce+we[0].length;if(Xr(JSON.parse(vn(P.slice(de,Se)))))re.push({start:de,end:Se})}return re}).sort((J,Y)=>J.start-Y.start).reverse().reduce((J,Y)=>{let re=/^(?:\s|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*,/.exec(J.slice(Y.end));if(re?.[0].includes("/")){let ie=Y.end+re[0].length-1;return J.slice(0,Y.start)+J.slice(Y.end,ie)+J.slice(ie+1)}return Oa(J,Y)},P);return Pi(j,O),j}function cc(P){oc(P);let O=Ci(P),D=O.find((Y)=>Zr(Y)),j=[],J=[];for(let Y of O){if(!Zr(Y))continue;try{let re=bi(Y,"utf-8");if(!Ri(Pi(re,Y)))continue;cn(Y,ry(re,Y)),J.push(Y)}catch(re){j.push(re instanceof Error?re.message:String(re))}}if(j.length>0)throw Error(j.join(`
|
|
222
|
+
`));return{path:J[0]??D??Tn(ki(P),wi[0]),alreadyInstalled:J.length>0}}function Ot(P){let O=[];for(let D of P.openCodeVersion?.startsWith("2.")?xi(P.environment):Ci(P.environment))if(oy(D))try{let j=iy(D,"utf-8"),J=vn(j),Y=JSON.parse(J);if(Ri(Y)){let re=Si(P.openCodePluginListOutput);if(re)return{platform:"opencode",status:"disabled",method:"opencode api plugin.list",configPath:D,errors:[...O,re]};return{platform:"opencode",status:"configured",method:"plugin array",configPath:D,errors:O.length>0?O:void 0}}}catch(j){O.push(`Failed to parse ${sy(D)}: ${j instanceof Error?j.message:String(j)}`)}return{platform:"opencode",status:"n/a",errors:O.length>0?O:void 0}}import{join as dc}from"node:path";function Ei(P){let O=P.env.get("PI_CODING_AGENT_DIR");return dc(O?Nn(O,P.home):dc(P.home,".pi","agent"),"settings.json")}function _i(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function uc(P){let O=Ei(P.environment),D=xn(O);if(D.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(D.kind==="missing")return{platform:"pi",status:"n/a"};let j=tn(D.value,"packages");if(!Array.isArray(j))return{platform:"pi",status:"n/a"};let J=j.find((ie)=>_i(typeof ie==="string"?ie:tn(ie,"source")));if(J===void 0)return{platform:"pi",status:"n/a"};let Y=tn(J,"extensions");if(Array.isArray(Y)&&Y.some((ie)=>typeof ie==="string"&&ie.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:O,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:O}}var ay={amp:Ca,"antigravity-cli":Sa,"claude-code":Ea,codex:Aa,"copilot-cli":Va,cursor:Qa,"deepseek-harness":nl,devin:cl,droid:vl,"gemini-cli":bl,"grok-build":_l,"hermes-agent":Ml,"kimi-code":zt,openclaw:ec,opencode:Ot,pi:uc};function Dt(P,O,D){let j={...D,cwd:O,environment:P};return mr.map((J)=>ly(ay[J](j)))}function ly(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 cy}from"node:path";var dy=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}]),uy=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([])})})}),py={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 pc(P){let O=cy(P.tmpdir,"cc-safety-net-self-test"),D=dy.map((j)=>{let J=G(P,u("self-test",{command:j.command},{kind:"command",shell:"auto"},{configCwd:O,executionCwd:O},j.command),{guard:{dependencies:{loadPolicySnapshot:()=>uy,getModes:()=>py,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),Y=j.expectBlocked?"blocked":"allowed",re=J.decision.kind==="deny"?"blocked":"allowed";return{command:j.command,description:j.description,expected:Y,actual:re,passed:Y===re,reason:J.decision.kind==="deny"?J.decision.reason:void 0,ruleId:J.decision.kind==="deny"?J.decision.ruleId:void 0}});return{passed:D.filter((j)=>j.passed).length,failed:D.filter((j)=>!j.passed).length,total:D.length,results:D}}function Ai(P){let O=gn({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Dn(O.errors))return null;return{json:O.flags.json,skipUpdateCheck:O.flags.skipUpdateCheck}}async function fc(P,O={}){let D=await Ut(!O.json,()=>{let j=my(P,O);return{ready:j,finish:()=>j}},()=>Mt(),{loadingMessage:"Checking system status…"});if(O.json)console.log(JSON.stringify(D,null,2));else gy(D);return D.engineSelfTest.failed>0||D.findings.some((j)=>j.severity==="error")?1:0}async function my(P,O){let D=O.cwd??process.cwd(),j=await Cr((Me)=>Ot({environment:P,cwd:D,openCodeVersion:Me}).status!=="n/a",void 0,D),J=Dt(P,D,{ampPluginListOutput:j.ampPluginListOutput,codexPluginListOutput:j.codexPluginListOutput,copilotCliVersion:j.versions["copilot-cli"],openCodeVersion:j.versions.opencode,openCodePluginListOutput:j.openCodePluginListOutput}),Y=Hs(P,D),re=Ms(P),ie=k(P,{cwd:D}),de=ie.policy,fe=T(de,P.env),we=q(de,fe.capabilities),Ce=vr(P,7),Se=va(P,D),Ie=[br(D),Ct(P)].filter((Me)=>fy(Me)),Ee=O.skipUpdateCheck?{currentVersion:pn(),latestVersion:null,updateAvailable:!1}:await Gn(),Ve={hooks:J,engineSelfTest:pc(P),userConfig:Y.userConfig,projectConfig:Y.projectConfig,configState:je(ie),effectiveRules:Y.effectiveRules,environment:re,effectiveSafety:{selectedPreset:de.safety.level??"standard",level:fe.effectiveLevel,capabilities:fe.capabilities,ruleOverrides:de.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(we).filter(([,Me])=>Me.source==="rule_override"&&Me.override==="off"&&Me.inheritedEnabled&&Me.changesInherited).map(([Me])=>Me),ruleCounts:{stored:Object.keys(de.destructiveCommandRuleOverrides).length,effective:Object.values(we).filter((Me)=>Me.changesInherited).length},...ie.policyScopes?{policyScopes:ie.policyScopes}:{}},...Se.length>0?{v2Leftovers:Se}:{},...Ie.length>0?{legacyConfigs:Ie}:{},posture:ea(P,Y.userConfig.path),activity:Ce,update:Ee,system:j};return{...Ve,findings:Gs(Ve)}}function gy(P){console.log(),console.log(Bs(P.hooks)),console.log(),console.log(Vs(P.engineSelfTest)),console.log(),console.log(Js(P)),console.log(),console.log(Ws(P.environment)),console.log(),console.log(Ks(P)),console.log(),console.log(zs(P.findings)),console.log(),console.log(Ys(P.activity)),console.log(),console.log(Xs(P.system)),console.log(),console.log(Zs(P.update)),console.log(Qs(P))}import{existsSync as yy}from"node:fs";var hy=/^[A-Za-z0-9_@%+=:,./-]+$/,mc="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function Ti(P){let O=gn({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Dn(O.errors))return console.error(mc),console.error("Pass -- before a command that starts with dashes."),null;if(O.values.cwd!==void 0&&!yy(O.values.cwd))return console.error(`Error: --cwd path does not exist: ${O.values.cwd}`),null;let D=O.positionals.length===1?O.positionals[0]:O.positionals.map((j)=>hy.test(j)?j:`'${j.replaceAll("'","'\\''")}'`).join(" ");if(!D)return console.error("Error: No command provided"),console.error(mc),null;return{json:O.flags.json,cwd:O.values.cwd,command:D}}function gc(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 yc(P,O){let j=O-18;return[`${P.dtl}${P.dh.repeat(O)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(j)}${P.dv}`,`${P.dbl}${P.dh.repeat(O)}${P.dbr}`]}function Ii(P){return JSON.stringify(P)}function hc(P,O=0){return`[${P.map((j,J)=>qs(j,J,O)).join(",")}]`}function Qt(P,O,D=70){let j=P.split(" "),J=[],Y="";for(let re of j)if(Y&&Y.length+re.length+1>D)J.push(Y),Y=re;else Y=Y?`${Y} ${re}`:re;if(Y)J.push(Y);return J.map((re,ie)=>ie===0?re:`${O}${re}`)}function vc(P,O,D){let j=[];switch(P.type){case"parse":return null;case"env-strip":return j.push(""),j.push(`STEP ${O} ${D.h} Strip environment variables`),j.push(` Removed: ${P.envVars.map((J)=>`${J}=<redacted>`).join(", ")}`),j.push(` Tokens: ${Ii(P.output)}`),{lines:j,incrementStep:!0};case"leading-tokens-stripped":return j.push(""),j.push(`STEP ${O} ${D.h} Strip wrappers`),j.push(` Removed: ${P.removed.join(", ")}`),j.push(` Tokens: ${Ii(P.output)}`),{lines:j,incrementStep:!0};case"shell-wrapper":return j.push(""),j.push(`STEP ${O} ${D.h} Detect shell wrapper`),j.push(` Wrapper: ${P.wrapper} -c`),j.push(` Inner: ${P.innerCommand}`),{lines:j,incrementStep:!0};case"interpreter":{if(j.push(""),j.push(`STEP ${O} ${D.h} Detect interpreter`),j.push(` Interpreter: ${P.interpreter}`),j.push(` Code: ${P.codeArg}`),P.paranoidBlocked)j.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:j,incrementStep:!0}}case"busybox":return j.push(""),j.push(`STEP ${O} ${D.h} Busybox wrapper`),j.push(` Subcommand: ${P.subcommand}`),{lines:j,incrementStep:!0};case"transparent-wrapper":return j.push(""),j.push(`STEP ${O} ${D.h} Transparent wrapper`),j.push(` Wrapper: ${P.wrapper}`),j.push(` Tokens: ${Ii(P.output)}`),{lines:j,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(j.push(""),j.push(`STEP ${O} ${D.h} Match rules`),j.push(` Rule: ${P.rule}()`),P.matched)j.push(" Result: MATCHED");else j.push(" Result: No match");return{lines:j,incrementStep:!0}}case"worktree-relaxation":return j.push(""),j.push(`STEP ${O} ${D.h} Worktree relaxation`),j.push(` Mode: ${n.worktree.name}`),j.push(` Git cwd: ${P.gitCwd}`),j.push(" Result: Allowed local discard in linked worktree"),{lines:j,incrementStep:!0};case"temp-root-relaxation":return j.push(""),j.push(`STEP ${O} ${D.h} Temp-root relaxation`),j.push(` Git cwd: ${P.gitCwd}`),j.push(" Result: Allowed git discard in a temp-root repository"),{lines:j,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return j.push(""),j.push(`STEP ${O} ${D.h} Fallback scan`),j.push(` Found: ${P.embeddedCommandFound}`),{lines:j,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(j.push(""),j.push(`STEP ${O} ${D.h} Custom rules`),P.matched)j.push(" Result: MATCHED");else j.push(" Result: No match");return{lines:j,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return j.push(""),j.push(`STEP ${O} ${D.h} Dangerous text check`),j.push(` Token: ${P.token}`),j.push(" Result: MATCHED"),{lines:j,incrementStep:!0};return null}case"strict-unparseable":return j.push(""),j.push(`STEP ${O} ${D.h} Strict mode check`),j.push(` Command: ${P.rawCommand}`),j.push(" Result: ✗ UNPARSEABLE"),{lines:j,incrementStep:!0};case"segment-skipped":return null;case"error":return j.push(""),j.push(`ERROR: ${P.message}`),{lines:j,incrementStep:!1};default:return P}}function $i(P,O){let D=gc(O?.asciiOnly??!1),j=58,J=[],Y=1;J.push(...yc(D,58)),J.push("");let re=P.trace.steps.find((Ee)=>Ee.type==="error");if(re&&re.type==="error"){J.push("ERROR"),J.push(` ${re.message}`),J.push(""),J.push("RESULT"),J.push(` Status: ${P.result==="blocked"?nn.red("BLOCKED"):nn.green("ALLOWED")}`),J.push(""),J.push("CONFIG");let Ee=P.configSource??"none";return J.push(` Path: ${Ee}`),J.join(`
|
|
223
|
+
`)}let ie=P.trace.steps.find((Ee)=>Ee.type==="parse");if(ie&&ie.type==="parse"){J.push("INPUT"),J.push(` ${ie.input}`),J.push(""),J.push(`STEP ${Y} ${D.h} Split shell commands`),Y++;for(let Ee=0;Ee<ie.segments.length;Ee++){let Ve=ie.segments[Ee];if(Ve){let Me=Math.random();J.push(` Segment ${Ee+1}: ${hc(Ve,Me)}`)}}}let de=P.trace.segments,fe=de.length>1;for(let Ee of de){if(fe){J.push("");let on="";if(ie&&ie.type==="parse"){let So=ie.segments[Ee.index];if(So)on=So.join(" ")}let sn=54,an=on,rn=` Segment ${Ee.index+1}: `,fn=" ";if(on){if(rn.length+on.length+fn.length>sn){let np=sn-rn.length-fn.length;an=`${on.substring(0,np-1)}…`}}let On=on?`${rn}${an}${fn}`:` Segment ${Ee.index+1} `,Qu=on?`${rn}${nn.cyan(an)}${fn}`:On,gs=58-On.length,ys=Math.floor(gs/2),ep=gs-ys;J.push(`${D.sh.repeat(ys)}${Qu}${D.sh.repeat(ep)}`)}if(Ee.steps.find((on)=>on.type==="segment-skipped")){J.push(""),J.push(" (skipped — prior segment blocked)");continue}let Me=!1,en=!1;for(let on of Ee.steps){let sn=vc(on,Y,D);if(sn){if(en=!0,on.type==="recurse"){J.push("");let an=" RECURSING ",rn=58-an.length-4;J.push(` ${D.tl}${D.h}${an}${D.h.repeat(rn)}`),J.push(` ${D.v}`),Me=!0;continue}for(let an of sn.lines)if(Me)J.push(` ${D.v} ${an}`);else J.push(an);if(sn.incrementStep)Y++}}if(Me)J.push(` ${D.v}`),J.push(` ${D.bl}${D.h.repeat(56)}`);if(!en)J.push(""),J.push(` ${nn.green("✓")} Allowed (no matching rules)`)}if(J.push(""),J.push("RESULT"),P.result==="blocked"){if(J.push(` Status: ${nn.red("BLOCKED")}`),P.customRule){if(J.push(` Rule: ${P.customRule.id}`),P.customRule.rulebook)J.push(` Rulebook: ${P.customRule.rulebook.name} ${P.customRule.rulebook.version}`);if(P.customRule.source)J.push(` Source: ${P.customRule.source}`);if(P.customRule.override)J.push(` Override: reason ${P.customRule.override.reason}`)}if(P.reason){let Ee=Qt(P.reason," ");J.push(` Reason: ${Ee[0]}`);for(let Ve=1;Ve<Ee.length;Ve++)J.push(Ee[Ve]??"")}}else J.push(` Status: ${nn.green("ALLOWED")}`);J.push(""),J.push("CONFIG");let we=P.configSource??"none",Ce=P.configValid?"":" (invalid)";J.push(` Path: ${we}${Ce}`);let Se=P.safetyPresetScope;J.push(` Safety preset: ${P.selectedPreset??"standard"}${Se?` (${wr(Se)})`:""}`),J.push(` Effective capabilities: ${P.effectiveLevel}`);let Ie=Object.entries(P.destructiveCommandRuleOverrides??{});if(J.push(` Rule customizations: ${Ie.length}`),P.ruleActivation)J.push(` Rule activation: ${P.ruleActivation.id} — ${P.ruleActivation.enabled?"on":"off"} via ${P.ruleActivation.source}`);return J.join(`
|
|
224
|
+
`)}function Oi(P){return JSON.stringify(P,null,2)}import{resolve as xy}from"node:path";var vy=["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 bc(P){let O=0,D={allocateSegment(){return O++},getNextSegmentIndex(){return O},recordGlobal(j){P.record({kind:"step",scope:"global",step:j})},recordSegment(j,J=D.currentSegmentIndex){if(J===void 0)return;P.record({kind:"step",scope:"segment",segmentIndex:J,step:j})}};return D}function wc(P={}){let O=[],D=P.maxEvents??512,j={maxTextLength:P.maxTextLength??2048,maxListLength:P.maxListLength??128,maxObjectProperties:P.maxObjectProperties??P.maxListLength??128,maxDepth:P.maxDepth??16},J,Y=new Set;return{record(re){if(J)return;if(!re||O.length>=D)return;try{O.push(Ni(by(re,j,Y)))}catch{}},finish(){if(J)return J;return J=Ni({events:Object.freeze(O)}),J}}}function by(P,O,D){if(P.kind!=="step")throw TypeError("invalid trace event");let{scope:j,step:J}=P;Qr(J,D,O);let Y=Di(J,O,D);if(j==="global")return{kind:"step",scope:"global",step:Y};if(j!=="segment")throw TypeError("invalid trace event scope");return{kind:"step",scope:"segment",segmentIndex:P.segmentIndex,step:Y}}function Qr(P,O,D,j=0,J=new WeakSet){if(typeof P==="string"){let ie=P.slice(0,D.maxTextLength);if(!ze(ie))return;for(let de of nt(ie))for(let fe of de.match(/[^\s"'()$]+/g)??[])O.add(kc(fe));return}if(!P||typeof P!=="object"||j>=D.maxDepth||J.has(P))return;if(J.add(P),Array.isArray(P)){let ie=Math.min(P.length,D.maxListLength);for(let de=0;de<ie;de++)Qr(P[de],O,D,j+1,J);return}let Y=0,re=new Set;for(let ie in P){if(!Object.hasOwn(P,ie))continue;if(Y>=D.maxObjectProperties)break;Y++,Qr(ie,O,D);let de=Li(ie,D,O);if(re.has(de))continue;re.add(de),Qr(P[ie],O,D,j+1,J)}}function Di(P,O,D,j=0,J=new WeakSet){if(typeof P==="string")return Li(P,O,D);if(!P||typeof P!=="object")return P;if(j>=O.maxDepth)return;if(J.has(P))return;if(J.add(P),Array.isArray(P)){let ie=[],de=Math.min(P.length,O.maxListLength);for(let fe=0;fe<de;fe++)ie.push(Di(P[fe],O,D,j+1,J));return ie}let Y={},re=0;for(let ie in P){if(!Object.hasOwn(P,ie))continue;if(re>=O.maxObjectProperties)break;re++;let de=Li(ie,O,D);if(Object.hasOwn(Y,de))continue;Object.defineProperty(Y,de,{value:Di(P[ie],O,D,j+1,J),enumerable:!0,configurable:!0,writable:!0})}return Y}function Li(P,O,D){let j=P.slice(0,O.maxTextLength),J=ze(j)?Be(j):j,Y=D.size>0?ky(J,D):J;return(wy(Y)?Pe(Y):Y).slice(0,O.maxTextLength)}function wy(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&&vy.some((O)=>P.includes(O))||P.length>=49&&/\b[a-f0-9]{32}\.[A-Za-z0-9]{16}\b/.test(P)}function ky(P,O){return P.replace(/[^\s"'()$]+/g,(D)=>O.has(kc(D))?"<redacted>":D)}function kc(P){let O=2166136261,D=2166136261;for(let j=0;j<P.length;j++)O=Math.imul(O^P.charCodeAt(j),16777619),D=Math.imul(D^P.charCodeAt(P.length-j-1),16777619);return`${O>>>0}:${D>>>0}:${P.length}`}function Ni(P){if(P&&typeof P==="object"&&!Object.isFrozen(P)){for(let O of Object.values(P))Ni(O);Object.freeze(P)}return P}function er(P,O={},D){let j=xy(O.cwd??process.cwd()),J=O.policySnapshot??k(D,{cwd:j,userConfigDir:O.userConfigDir}),Y=T(J.policy,D.env),re=Ue({policySnapshot:J,effectiveCapabilities:Y.capabilities,strict:Y.strict,paranoidRm:Y.paranoidRm,paranoidInterpreters:Y.paranoidInterpreters,worktreeMode:Y.worktreeMode}),ie={effectiveLevel:re.effectiveLevel,selectedPreset:J.policy.safety.level??"standard",...J.policyScopes?{safetyPresetScope:J.policyScopes.levelScope}:{},effectiveCapabilities:re.effectiveCapabilities,destructiveCommandRuleOverrides:J.policy.destructiveCommandRuleOverrides},{configSource:de,configValid:fe}=Sy(D,{cwd:j,userConfigDir:O.userConfigDir});if(!P||!P.trim())return{trace:{steps:[{type:"error",message:"No command provided"}],segments:[]},result:"allowed",configSource:de,configValid:fe,...ie};let we=y(P,"auto");if(we.status==="limited")throw new b;let Ce=we.dialect==="powershell"?y(P,"posix"):we,Se=dt(Ce),Ie=wc(),Ee=bc(Ie);Ee.recordGlobal({type:"parse",input:P,segments:Se.map((On)=>[...On])});let Ve=u("Bash",{command:P},{kind:"command",shell:"auto"},{configCwd:j,executionCwd:j},P),Me=V(Ve,{environment:D,trace:Ee,dependencies:{loadPolicySnapshot:()=>J}}),en=Me.decision.kind==="deny"?Me.decision:null;if(en&&(Me.stage==="policy-protection"||Me.stage==="secret-protection"))return{trace:{steps:[],segments:[{index:0,steps:[{type:"rule-check",rule:Cy(en),matched:!0,reason:en.reason}]}]},result:"blocked",reason:R(en.reason),segment:R(xc(en,P)),ruleId:R(en.ruleId),configSource:de,configValid:fe,...ie};let on=Ee.getNextSegmentIndex();if(en&&on>0&&on<Se.length)Ee.recordSegment({type:"segment-skipped",index:on,reason:"prior-segment-blocked"},on);let sn=Ie.finish(),an=en?.ruleId??Py(Ve,J,Y,D),rn=z.find((On)=>On.id===an&&On.activationCapability),fn=rn?re.policy.effectiveDestructiveCommandRules[rn.id]:void 0;return{trace:Ey(sn),result:en?"blocked":"allowed",reason:en?R(en.reason):void 0,segment:en?R(xc(en,P)):void 0,ruleId:en?R(en.ruleId):void 0,customRule:Ry(_y(en?.ruleId,J)),configSource:de,configValid:fe,...ie,...rn&&fn?{ruleActivation:{id:rn.id,...fn}}:{}}}function xc(P,O){return P.evidence?.segment??O}function Cy(P){if(P.reason===He)return"policy-protection:findPolicyConfigMutationTargetInSemanticFacts";if(P.reason===Ge)return"policy-apply-protection:findPolicyApplyInvocationInSemanticFacts";if(P.reason===x)return"git-metadata-protection:findGitMetadataMutationTargetInSemanticFacts";return"secret-protection:findSensitiveTargetInSemanticFacts"}function Sy(P,O){let D=_(O.cwd),j=W(P,O),J=X(P,{cwd:O.cwd,userConfigDir:O.userConfigDir});try{if(r(J.projectConfigTarget)!==null){if(Un(J.projectConfigTarget).errors.length===0)return{configSource:D,configValid:!0};return{configSource:D,configValid:!1}}}catch(Y){if(Y instanceof o)return{configSource:D,configValid:!1};throw Y}try{if(r(J.userConfigTarget)!==null){let Y=Un(J.userConfigTarget);return{configSource:j,configValid:Y.errors.length===0}}return{configSource:null,configValid:!0}}catch(Y){if(Y instanceof o)return{configSource:j,configValid:!1};throw Y}}function Py(P,O,D,j){let J=O.policy,Y=_e({...J,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:{...J.destructiveCommandRuleOverrides,...Object.fromEntries(z.flatMap((ie)=>ie.activationCapability?[[ie.id,"on"]]:[]))}},O.state==="degraded"?{diagnostics:O.diagnostics,reason:O.reason}:void 0),re=V(P,{environment:j,dependencies:{loadPolicySnapshot:()=>Y,getModes:()=>({...D,strict:!0,paranoidRm:!0,paranoidInterpreters:!0}),findSensitiveTarget:()=>null}});return re.decision.kind==="deny"?re.decision.ruleId:void 0}function Ry(P){if(!P)return;return{id:R(P.id),...P.rulebook?{rulebook:{name:R(P.rulebook.name),version:R(P.rulebook.version)}}:{},...P.source?{source:R(P.source)}:{},...P.override?{override:{type:"reason",reason:R(P.override.reason)}}:{}}}function Ey(P){let O=P.events.flatMap((j)=>j.kind==="step"&&j.scope==="global"?[j.step]:[]),D=new Map;for(let j of P.events){if(j.kind!=="step"||j.scope!=="segment")continue;let J=D.get(j.segmentIndex)??{index:j.segmentIndex,steps:[]};J.steps.push(j.step),D.set(j.segmentIndex,J)}return{steps:O,segments:[...D.values()]}}function _y(P,O){let D=P?.replace(/^custom\./,"");if(!D||!O.policy.rules.some((j)=>j.name===D))return;return O.ruleMetadata[D]??Object.freeze({id:D})}function Cc(P){return new Promise((O)=>{process.stdout.write(`${P}
|
|
225
|
+
`,()=>O())})}async function Sc(P,O){let D=Ti(O);if(!D)return 1;try{let j=er(D.command,{cwd:D.cwd},P),J=!!process.env.NO_COLOR||!process.stdout.isTTY;return await Cc(D.json?Oi(j):$i(j,{asciiOnly:J})),0}catch(j){let J=Ay(j instanceof p?j.cause:j);if(J===void 0)throw j;if(D.json)return await Cc(JSON.stringify({error:J})),1;return console.error(J),1}}function Ay(P){if(P instanceof b)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 Pc="2.6.2",_n=" ",ft="cc-safety-net";function Rc(P){return P.argument?`${P.flags} ${P.argument}`:P.flags}function Ty(P){return Math.max(...P.map((O)=>Rc(O).length))}function Iy(P){return Math.max(...P.map((O)=>O.usage.length))}function $y(P){return Math.max(...P.map((O)=>`${ft} ${O.usage}`.length))}function Oy(P,O){let D=`${ft} ${P.usage}`;return`${_n}${D.padEnd(O+2)}${P.description}`}function In(P,O){return`${_n}${P.padEnd(Math.max(40,P.length+2))}${O}`}function Lt(P,O=console.log){let D=[];if(D.push(`${ft} ${P.name}`),D.push(""),D.push(`${_n}${P.description}`),D.push(""),D.push("USAGE:"),D.push(`${_n}${ft} ${P.usage}`),D.push(""),P.subcommands&&P.subcommands.length>0){D.push("SUBCOMMANDS:");let j=Iy(P.subcommands);for(let J of P.subcommands)D.push(`${_n}${J.usage.padEnd(j+2)}${J.description}`);D.push("")}if(P.options.length>0){D.push("OPTIONS:");let j=Ty(P.options);for(let J of P.options){let Y=Rc(J),re=J.default?`${J.description} (default: ${J.default})`:J.description;D.push(`${_n}${Y.padEnd(j+2)}${re}`)}D.push("")}if(P.examples&&P.examples.length>0){D.push("EXAMPLES:");for(let j of P.examples)D.push(`${_n}${j}`)}O(D.join(`
|
|
226
|
+
`))}function ji(){let P=$y(yr),O=[];O.push(`${ft} v${Pc}`),O.push(""),O.push("Blocks destructive commands and secret access."),O.push(""),O.push("COMMANDS:");for(let D of yr)O.push(Oy(D,P));O.push(""),O.push("GLOBAL OPTIONS:"),O.push(`${_n}-h, --help Show help (use with command for command-specific help)`),O.push(`${_n}-V, --version Show version`),O.push(""),O.push("HELP:"),O.push(`${_n}${ft} help <command> Show help for a specific command`),O.push(`${_n}${ft} <command> --help Show help for a specific command`),O.push(""),O.push("ENVIRONMENT VARIABLES:"),O.push(In(`${n.level.name}=standard|strict|paranoid`,"Set session safety level")),O.push(In(`${n.worktree.name}=1`,"Allow local git discards in linked worktrees")),O.push(In(`${n.debug.name}=1`,"Print diagnostic messages to stderr")),O.push(In(`${n.auditScope.name}=all|blocked`,"Record all command decisions, or denials only")),O.push(In(`${n.projectTightenOnly.name}=1`,"Ignore project policy settings that weaken the user policy")),O.push(In("CC_SAFETY_NET_HOME","Override rule config home directory")),O.push(""),O.push("LEGACY ENVIRONMENT VARIABLES (STILL SUPPORTED):"),O.push(In(`${n.strict.name}=1`,"Force safety.overrides.fail_closed on")),O.push(In(`${n.paranoid.name}=1`,"Force paranoid_rm and paranoid_interpreters on")),O.push(In(`${n.paranoidRm.name}=1`,"Force safety.overrides.paranoid_rm on")),O.push(In(`${n.paranoidInterpreters.name}=1`,"Force safety.overrides.paranoid_interpreters on")),O.push(""),O.push("Documentation: https://ccsafetynet.com/docs"),console.log(O.join(`
|
|
227
|
+
`))}function Ec(){console.log(Pc)}function nr(P,O=console.log){let D=hr(P);if(!D)return!1;if(D.name.toLowerCase()!==P.toLowerCase())return!1;return Lt(D,O),!0}import{existsSync as ao,readFileSync as Od}from"node:fs";import{join as so}from"node:path";import*as Bn from"node:readline";function Dy(P){return P==="install"?"Install":"Uninstall"}function Ly(P){return P==="install"?"Installing":"Uninstalling"}function Ny(P){return P==="install"?"into":"from"}function Tc(P){return P?.available===!0}function jy(P,O){let D=new Set(O);return P.filter((j)=>D.has(j.target)).map((j)=>j.target)}function _c(P,O,D){if(P.every((j)=>!j.available))return O;return Array.from({length:P.length},(j,J)=>J+1).map((j)=>(O+j*D+P.length)%P.length).find((j)=>Tc(P[j]))}function Fy(P,O,D){if(D.ctrl&&D.name==="c")return"interrupt";if(D.name==="escape"||O==="q")return"abort";if(P==="install"&&(O==="u"||O==="U"))return"update";if(D.name==="up"||O==="k")return"up";if(D.name==="down"||O==="j")return"down";if(D.name==="space"||O===" ")return"toggle";if(D.name==="return"||D.name==="enter")return"confirm";return null}function Hy(P){return{cursor:P.findIndex((O)=>O.available),selected:[]}}function My(P,O,D){if(D==="confirm"||D==="update"||D==="abort"||D==="interrupt")return{state:P,done:D};if(D==="up")return{state:{...P,cursor:_c(O,P.cursor,-1)}};if(D==="down")return{state:{...P,cursor:_c(O,P.cursor,1)}};let j=O[P.cursor];if(!Tc(j))return{state:P};let J=P.selected.includes(j.target)?P.selected.filter((Y)=>Y!==j.target):jy(O,[...P.selected,j.target]);return{state:{...P,selected:J}}}var Ic="◉",$c="◯",Oc=">",Dc=" ";function Uy(P,O,D){return["",`${Dy(P)} CC Safety Net ${Ny(P)}:`,"",...O.map((j,J)=>{let Y=D.selected.includes(j.target),re=J===D.cursor,ie=Y?Ic:$c,de=re?Oc:Dc,fe=j.available?"":` (${j.unavailableReason??"not installed"})`,we=`${ie} ${j.label}${fe}`,Ce=!j.available?nn.dim(we):Y?nn.green(we):re?nn.bold(we):we;return`${de} ${Ce}`}),"",P==="install"?"Space: select Enter: confirm u: update installed Up/Down: move q/Esc: cancel":O.some((j)=>j.available)?"Space: select Enter: confirm Up/Down: move q/Esc: cancel":`No selectable integrations found for ${P}. q/Esc: close`].join(`
|
|
228
|
+
`)}var Ac=["global-hook","plugin"];function Gy(P,O,D={}){let j=D.color!==!1?nn.bold:(Y)=>Y;return["","Install the Kimi Code integration as:","",...[`Global hook — ${O?"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((Y,re)=>{let ie=re===P,de=`${ie?Ic:$c} ${Y}`;return`${ie?Oc:Dc} ${ie?j(de):de}`}),"","Enter: confirm Up/Down: move q/Esc: cancel"].join(`
|
|
229
|
+
`)}function Lc(P){let{input:O,output:D}=P;Bn.emitKeypressEvents(O);let j=O.isRaw===!0;O.setRawMode(!0),O.resume();let J=0,Y=()=>{if(J===0)return;Bn.moveCursor(D,0,-J),Bn.cursorTo(D,0),Bn.clearScreenDown(D)},re=()=>{Y();let ie=P.render();D.write(`${ie}
|
|
230
230
|
`),J=ie.split(`
|
|
231
|
-
`).length};return new Promise((ie)=>{let de=(we)=>{O.off("keypress",fe),O.setRawMode(j),O.pause(),Y(),ie(we)};function fe(we,Ce){P.onKey(we,Ce,{finish:de,draw:re})}O.on("keypress",fe),re()})}function
|
|
232
|
-
`);return}ie.draw()}})}import{existsSync as
|
|
233
|
-
`)})}),J.on("close",(de)=>{clearTimeout(ie),j({status:re?null:de,errorCode:re?"ETIMEDOUT":void 0,stdout:Y.stdout,stderr:Y.stderr})})})};var Nt="cc-safety-net.ts",
|
|
231
|
+
`).length};return new Promise((ie)=>{let de=(we)=>{O.off("keypress",fe),O.setRawMode(j),O.pause(),Y(),ie(we)};function fe(we,Ce){P.onKey(we,Ce,{finish:de,draw:re})}O.on("keypress",fe),re()})}function Nc(P={}){let O=0;return Lc({input:P.input??process.stdin,output:P.output??process.stdout,render:()=>Gy(O,P.globalHookInstalled===!0),onKey:(D,j,J)=>{if(j.ctrl&&j.name==="c"){J.finish(null),(P.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(j.name==="escape"||D==="q")return J.finish(null);if(j.name==="return"||j.name==="enter")return J.finish(Ac[O]);if(j.name==="up"||j.name==="down"||D==="k"||D==="j")O=(O+1)%Ac.length,J.draw()}})}function Fi(P=process.stdin,O=process.stdout){return Boolean(P.isTTY&&O.isTTY&&typeof P.setRawMode==="function")}function jc(P,O,D={}){let j=D.output??process.stdout,J=Hy(O);return Lc({input:D.input??process.stdin,output:j,render:()=>Uy(P,O,J),onKey:(Y,re,ie)=>{let de=Fy(P,Y,re);if(!de)return;let fe=My(J,O,de);if(J=fe.state,fe.done==="interrupt"){ie.finish(null),(D.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(fe.done==="abort")return ie.finish(null);if(fe.done==="update")return ie.finish("update");if(fe.done==="confirm"){if(J.selected.length===0){j.write("\x07"),ie.draw();return}ie.finish([...J.selected]),j.write(`${Ly(P)} selected integrations...
|
|
232
|
+
`);return}ie.draw()}})}import{existsSync as Fc,lstatSync as By,mkdirSync as Vy,mkdtempSync as Jy,readdirSync as Wy,readFileSync as jt,rmSync as no}from"node:fs";import{basename as Ky,dirname as zy,join as Sn}from"node:path";import{fileURLToPath as Yy}from"node:url";var Hi="// cc-safety-net managed Amp plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --amp",mt="cc-safety-net",gt="cc-safety-net/index.ts";import{spawn as qy}from"node:child_process";var Mi=(P,O)=>{let D=zn([...P],process.env);return new Promise((j)=>{let J=qy(D.cmd,D.args,{cwd:O,stdio:["ignore","pipe","pipe"]}),Y=mi(J),re=!1,ie=setTimeout(()=>{re=!0,J.kill()},120000);J.on("error",(de)=>{clearTimeout(ie),j({status:null,errorCode:de.code,stdout:Y.stdout,stderr:[de.message,Y.stderr].filter(Boolean).join(`
|
|
233
|
+
`)})}),J.on("close",(de)=>{clearTimeout(ie),j({status:re?null:de,errorCode:re?"ETIMEDOUT":void 0,stdout:Y.stdout,stderr:Y.stderr})})})};var Nt="cc-safety-net.ts",Ui=Sn("amp",gt);function Zy(P){return Sn(P.home,".config","amp","plugins","cc-safety-net.ts")}function Xy(){let P=zy(Yy(import.meta.url)),O=Sn(P,Ui),D=Sn(P,"..",Ui),j=Sn(P,"..","..","..","dist",Ui);return[O,D,j]}function Qy(P=Xy()){let O=P.find((D)=>Fc(D)&&By(D).isFile());if(!O)throw Error("Packaged Amp plugin artifact not found. Reinstall cc-safety-net and try again.");return O}function Hc(P){try{return JSON.parse(P)}catch{return}}function to(P){return P.subarray(0,Buffer.byteLength(Hi)).toString("utf-8")===Hi}async function tr(P,O,D){let j=await P(O,D);if(j.status===0)return j;throw Error([`Failed to run ${O.join(" ")}${j.status===null?"":` (exit ${j.status})`}.`,[j.stdout,j.stderr].filter(Boolean).join(`
|
|
234
234
|
`).trim()].filter(Boolean).join(`
|
|
235
|
-
`))}async function
|
|
235
|
+
`))}async function Mc(P){let O=await P(["amp","plugins","repositories","--json"]);if(O.status===null)throw Error(`${O.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 (${O.errorCode??"terminated"}). Check that the amp CLI responds and rerun install --amp.`}
|
|
236
236
|
${O.stderr}`.trim());if(O.status!==0)throw Error(`Failed to run amp plugins repositories --json (exit ${O.status}). Sign in with "amp login" and rerun install --amp.
|
|
237
237
|
${[O.stdout,O.stderr].filter(Boolean).join(`
|
|
238
|
-
`)}`.trim());let D=
|
|
239
|
-
`}async function
|
|
240
|
-
`)}function
|
|
241
|
-
`)}}function
|
|
242
|
-
`,O+1);return D===-1?P.length:D+1}function
|
|
243
|
-
`)){if(/^\s*\[/.test(Y))return;let re=j.exec(Y);if(re){let ie=J+re[0].lastIndexOf("[");return{start:ie,end:
|
|
244
|
-
${Y}${D}${P.slice(O.end)}`}function
|
|
238
|
+
`)}`.trim());let D=Hc(O.stdout),j=(Array.isArray(D)?D:[]).filter((J)=>tn(J,"scope")==="user"&&tn(J,"exists")===!0&&tn(J,"viewerCanWrite")===!0).map((J)=>tn(J,"cloneRef")).find((J)=>typeof J==="string"&&J.length>0);if(!j)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 j}async function Uc(P,O,D){let j=Jy(Sn(O.tmpdir,"cc-safety-net-amp-"));try{return await tr(P,["amp","clone","user-plugins",j]),await D(j)}finally{no(j,{recursive:!0,force:!0})}}function Gi(P){return`rerun ${P==="overwrite"?"install":"uninstall"} --amp`}function Gc(P,O,D){let j=Sn(P,O),J=dn(j);if(!J)return;if(J.isSymbolicLink()||!J.isFile())throw Error(`Refusing to ${D} ${O} in your Amp personal plugins repository: not a regular file. Remove it there and ${Gi(D)}.`);let Y=jt(j);if(to(Y))return Y;throw Error(`Refusing to ${D} unmanaged file ${O} in your Amp personal plugins repository. Remove it there and ${Gi(D)}.`)}function qc(P,O){let D=Sn(P,mt),j=dn(D);if(!j)return;if(j.isSymbolicLink()||!j.isDirectory())throw Error(`Refusing to ${O} ${mt} in your Amp personal plugins repository: not a regular directory. Remove it there and ${Gi(O)}.`);return Gc(P,gt,O)}function eh(P){let O=Sn(P,Nt),D=dn(O);if(!D||D.isSymbolicLink()||!D.isFile())return;let j=jt(O);return to(j)?j:void 0}async function Bc(P,O,D,j){if(await tr(P,D,O),(await tr(P,["git","status","--porcelain"],O)).stdout.trim()==="")return!1;return await tr(P,["git","-c","commit.gpgsign=false","-c","user.name=cc-safety-net","-c","user.email=cc-safety-net@localhost","commit","-m",j],O),await tr(P,["git","push","origin","HEAD"],O),!0}function eo(P,O){nh(P,O),th(P,O)}function Vc(P,O){if(O==="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 nh(P,O){let D=Zy(P),j=dn(D);if(!j)return;if(!j.isSymbolicLink()&&j.isFile()&&to(jt(D))){no(D);return}Vc(D,O)}function th(P,O){let D=Sn(P.home,".config","amp","plugins",mt),j=dn(D);if(!j)return;if(!j.isSymbolicLink()&&j.isDirectory()&&rh(D)){no(D,{recursive:!0});return}Vc(D,O)}function rh(P){let O=Ky(gt);if(Wy(P).join("\x00")!==O)return!1;let D=Sn(P,O),j=dn(D);return!!j&&!j.isSymbolicLink()&&j.isFile()&&to(jt(D))}function oh(P){let O=l(P);if(!Fc(O))return"";let D=Hc(jt(O,"utf-8"));if(!D||typeof D!=="object"||Array.isArray(D))return"";return`;globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__ = ${JSON.stringify(E(D,P.home))};
|
|
239
|
+
`}async function Jc(P,O=Qy(),D=Mi){let j=Buffer.concat([jt(O),Buffer.from(oh(P),"utf-8")]),J=await Mc(D);return Uc(D,P,async(Y)=>{let re=`${J}/${mt}`,ie=qc(Y,"overwrite"),de=Gc(Y,Nt,"overwrite");if(ie?.equals(j)&&!de)return eo(P,"fail"),{path:re,alreadyInstalled:!0};if(Vy(Sn(Y,mt),{recursive:!0}),cn(Sn(Y,gt),j),de)no(Sn(Y,Nt));let fe=await Bc(D,Y,["git","add","--",gt,...de?[Nt]:[]],`chore: update cc-safety-net plugin to v${pn()}`);return eo(P,"fail"),{path:re,alreadyInstalled:!fe}})}async function Wc(P,O=Mi){let D=await Mc(O);return Uc(O,P,async(j)=>{let J=qc(j,"remove"),Y=eh(j),re=`${D}/${Y&&!J?Nt:mt}`;if(!J&&!Y)return eo(P,"keep"),{path:re,alreadyInstalled:!1};return await Bc(O,j,["git","rm","--",...J?[gt]:[],...Y?[Nt]:[]],`chore: remove cc-safety-net plugin v${pn()}`),eo(P,"keep"),{path:re,alreadyInstalled:!0}})}import{existsSync as Kc,mkdirSync as ih,readFileSync as sh}from"node:fs";import{dirname as ah}from"node:path";var qi=Cn["antigravity-cli"],yt="cc-safety-net";function ht(P){return Boolean(P)&&typeof P==="object"&&!Array.isArray(P)}function oo(){return{PreToolUse:[{hooks:[{type:"command",command:qi,timeout:30}]}]}}function zc(P){try{let O=JSON.parse(sh(P,"utf-8"));if(!O||typeof O!=="object"||Array.isArray(O))throw Error("Antigravity hooks config must be a JSON object");return O}catch(O){if(O instanceof SyntaxError)throw Error(`Failed to parse Antigravity hooks config ${P}: ${O.message}`);throw O}}function Yc(P){let O=P[yt];if(O===void 0){let j=oo();return P[yt]=j,{definition:j,preToolUse:j.PreToolUse??[]}}if(!ht(O))throw Error(`Antigravity hooks config entry "${yt}" must be an object`);let D=Array.isArray(O.PreToolUse)?O.PreToolUse:[];return O.PreToolUse=D,{definition:O,preToolUse:D}}function Zc(P){if(!Array.isArray(P.PreToolUse))return!1;return P.PreToolUse.some((O)=>ht(O)&&Array.isArray(O.hooks)&&O.hooks.some((D)=>ht(D)&&D.command===qi))}function lh(P){return Object.values(P).some((O)=>ht(O)&&O.enabled!==!1&&Zc(O))}function ch(P){if(P[yt]===void 0)return!1;let O=Yc(P);if(O.definition.enabled!==!1||!Zc(O.definition))return!1;return O.definition.enabled=!0,!0}function dh(P){if(P[yt]===void 0){P[yt]=oo();return}let O=Yc(P);O.definition.enabled=!0,O.preToolUse.push(oo().PreToolUse?.[0]??{hooks:[]})}function uh(P){let O=!1;for(let D of Object.values(P)){if(!ht(D)||!Array.isArray(D.PreToolUse))continue;D.PreToolUse=D.PreToolUse.flatMap((j)=>{if(!ht(j)||!Array.isArray(j.hooks))return[j];let J=j.hooks.filter((Y)=>!ht(Y)||Y.command!==qi);if(J.length!==j.hooks.length)O=!0;return J.length===0?[]:[{...j,hooks:J}]})}return O}function ro(P,O){cn(P,`${JSON.stringify(O,null,2)}
|
|
240
|
+
`)}function Xc(P){let O=Gt(P.home);if(ih(ah(O),{recursive:!0}),!Kc(O))return ro(O,{[yt]:oo()}),{path:O,alreadyInstalled:!1};let D=zc(O);if(lh(D))return{path:O,alreadyInstalled:!0};if(ch(D))return ro(O,D),{path:O,alreadyInstalled:!1};return dh(D),ro(O,D),{path:O,alreadyInstalled:!1}}function Qc(P){let O=Gt(P.home);if(!Kc(O))return{path:O,alreadyInstalled:!1};let D=zc(O);if(!uh(D))return{path:O,alreadyInstalled:!1};return ro(O,D),{path:O,alreadyInstalled:!0}}import{existsSync as Ji,readlinkSync as mh}from"node:fs";import{join as Vn}from"node:path";import{spawn as ph}from"node:child_process";var $n=En.map((P)=>({target:P.id,flag:P.flag,label:mn(P.id),probeCommand:P.probeCommand}));function Bi(P){let O=new Set(P);return $n.map((D)=>D.target).filter((D)=>O.has(D))}async function ed(P,O){for(let D of P)await O(D)}var fh=5000;function rr(P,O=fh){return new Promise((D)=>{let j=zn([...P],process.env),J=ph(j.cmd,j.args,{env:process.env,stdio:"ignore"}),Y=!1,re=(de)=>{if(Y)return;Y=!0,clearTimeout(ie),D(de)},ie=setTimeout(()=>{J.kill(),re(!1)},O);J.on("error",()=>re(!1)),J.on("close",(de)=>re(de===0))})}function nd(P=rr,O={}){let D=new Set(O.configuredTargets??[]);return Promise.all($n.map(async(j)=>({target:j.target,flag:j.flag,label:j.label,...rd(O.action,await P(j.probeCommand),D.has(j.target))})))}function td(P,O){let D=new Set(O.configuredTargets??[]);return P.map((j)=>({...j,...rd(O.action,j.available,D.has(j.target))}))}function rd(P,O,D){if(P==="uninstall")return D?{available:!0}:{available:!1,unavailableReason:"not installed"};if(P==="install"&&D)return{available:!1,unavailableReason:"already installed"};if(!O)return{available:!1,unavailableReason:"CLI not installed"};return{available:!0}}var Jn="cc-safety-net",ad=["npx","-y","@deepseek-ai/dsh"],od="DeepSeek Harness",id=["@deepseek-ai","dsh-desktop"],ld="Quit DeepSeek Harness Desktop, then run this command again: Desktop plugins can only change while it is closed.",Vi=(P)=>`DeepSeek Harness Desktop is not in its default location, so ${P} ${Jn} from its Plugins page.`,cd=(P,O)=>Ji(Vn(Ko(P),O,"package.json"));function Wi(P,O){let D=O.platform??process.platform,j=cd(P,"desktop"),J=D==="darwin"?[Vn(P.home,"Applications"),O.systemApplications??"/Applications"].map((Y)=>Vn(Y,`${od}.app`,"Contents","Resources","runtime","cli","bin","dsh")):D==="win32"?[Vn(P.env.get("LOCALAPPDATA")||Vn(P.home,"AppData","Local"),"Programs",od,"resources","runtime","cli","bin","dsh.cmd")]:[];return{profile:j,cli:j?J.find((Y)=>Ji(Y)):void 0}}function dd(P,O=process.platform){if(O==="win32")return Ji(Vn(P.env.get("APPDATA")||Vn(P.home,"AppData","Roaming"),...id,"lockfile"));let D=gh(Vn(P.home,"Library","Application Support",...id,"SingletonLock")),j=Number(/-(\d+)$/.exec(D??"")?.[1]);return Number.isInteger(j)&&j>0&&yh(j)}function gh(P){try{return mh(P)}catch{return}}function yh(P){try{return process.kill(P,0),!0}catch(O){return O.code==="EPERM"}}async function ud(P,O={}){let D=Wi(P,O);if(D.cli&&dd(P,O.platform))throw Error(ld);let j=!D.cli||cd(P,"web")||await(O.probe??rr)(Ro),J=[...D.cli?[{profile:"desktop",label:"Desktop",dsh:[D.cli]}]:[],...j?[{profile:"web",label:"web",dsh:ad}]:[]];return{commands:J.map((Y)=>[...Y.dsh,"plugin","--profile",Y.profile,"add",`${Jn}@${pn()}`]),afterInstall:async()=>{let Y=new Set(zo(P).filter((ie)=>ie.enabled).map((ie)=>ie.name)),re=J.filter((ie)=>!Y.has(ie.profile));if(re.length>0)throw Error(`DeepSeek Harness installed ${Jn} in the ${sd(re)} but did not enable it. Enable it from the Plugins page.`)},message:[`Added ${Jn} to the DeepSeek Harness ${sd(J)}.`,...D.profile&&!D.cli?[Vi("add")]:[]].join(`
|
|
241
|
+
`)}}function sd(P){return`${P.map((O)=>O.label).join(" and ")} profile${P.length>1?"s":""}`}function pd(P,O={}){let D=new Set(zo(P).map((Y)=>Y.name));if(!D.has("desktop")&&!D.has("web"))throw Error(`${Jn} is not installed in the DeepSeek Harness web or Desktop profile`);let j=D.has("desktop")?Wi(P,O).cli:void 0,J=D.has("desktop")&&!j;if(J&&!D.has("web"))throw Error(Vi("remove"));if(j&&dd(P,O.platform))throw Error(ld);return{commands:[...j?[[j,"plugin","--profile","desktop","remove",Jn]]:[],...D.has("web")?[[...ad,"plugin","--profile","web","remove",Jn]]:[]],...J?{afterUninstall:()=>{throw Error(`Removed ${Jn} from the DeepSeek Harness web profile, but ${Vi("remove")}`)}}:{}}}function fd(P,O,D={}){let j=Wi(O,D).cli!==void 0;return P.map((J)=>J.target==="deepseek-harness"&&j?{...J,available:!0,unavailableReason:void 0}:J)}import{existsSync as hh,readdirSync as vh,rmSync as bh}from"node:fs";import{join as wh}from"node:path";function md(P,O=process.platform,D){if(!hh(P))return;let j=O==="win32"?/^bunx-\d+-cc-safety-net@/:new RegExp(`^bunx-${process.getuid?.()??0}-cc-safety-net@`);vh(P).filter((J)=>J!==D&&j.test(J)).forEach((J)=>{bh(wh(P,J),{recursive:!0,force:!0})})}import{existsSync as gd,readdirSync as kh,rmSync as xh}from"node:fs";import{join as Ft}from"node:path";function io(P,O=process.platform){let D=Ft(P.env.get("npm_config_cache")||(O==="win32"?Ft(P.env.get("LOCALAPPDATA")||Ft(P.home,"AppData","Local"),"npm-cache"):Ft(P.home,".npm")),"_npx");if(!gd(D))return;kh(D).filter((j)=>gd(Ft(D,j,"node_modules","cc-safety-net"))).forEach((j)=>{xh(Ft(D,j),{recursive:!0,force:!0})})}import{existsSync as kd,mkdirSync as Sh,readFileSync as xd}from"node:fs";import{dirname as Ph,join as wd}from"node:path";function Ch(P,O){if(P[O]!=="#")return O;let D=P.indexOf(`
|
|
242
|
+
`,O+1);return D===-1?P.length:D+1}function Ki(P,O,D){let j=new RegExp(`^(\\s*)${O}\\s*=\\s*\\[`),J=0;for(let Y of P.split(`
|
|
243
|
+
`)){if(/^\s*\[/.test(Y))return;let re=j.exec(Y);if(re){let ie=J+re[0].lastIndexOf("[");return{start:ie,end:Go(P,ie,{skipComment:Ch,...D})}}J+=Y.length+1}return}function yd(P,O,D){let j=P.slice(0,O.end).trimEnd(),J=$a(P,O.end),Y=J===""?" ":`${J} `,re=!j.endsWith("[")&&!j.endsWith(",");return`${j}${re?",":""}
|
|
244
|
+
${Y}${D}${P.slice(O.end)}`}function hd(P,O,D){let j=P.indexOf(D,O.start);if(j===-1||j>O.end)return P;return`${P.slice(0,j)}${P.slice(j+D.length).replace(/^\s*,/,"")}`}function vd(P,O){let D=new RegExp(`^\\s*${O}\\s*=\\s*\\[\\s*]\\s*(?:#.*)?$`),j=P.split(`
|
|
245
245
|
`),J=j.findIndex((ie)=>/^\s*\[/.test(ie)),Y=J===-1?j:j.slice(0,J),re=J===-1?[]:j.slice(J);return[...Y.filter((ie)=>!D.test(ie)),...re].join(`
|
|
246
|
-
`)}function
|
|
246
|
+
`)}function bd(P,O,D){let j=new RegExp(`^\\s*\\[\\[${O}]]\\s*$`,"m");return P.split(/(?=^\s*\[)/m).filter((J)=>!j.test(J)||!J.includes(D)).join("").trimEnd()}var or=Cn["kimi-code"],zi=`[[hooks]]
|
|
247
247
|
event = "PreToolUse"
|
|
248
|
-
command = "${
|
|
248
|
+
command = "${or}"`,Cd=`{ event = "PreToolUse", command = "${or}" }`,Sd={stringError:"Unterminated string in Kimi Code config",bracketError:"Unmatched hooks array in Kimi Code config"};function Pd(P){return wd(P.env.get("KIMI_CODE_HOME")??wd(P.home,".kimi-code"),"config.toml")}function Rh(P){let O=Ki(P,"hooks",Sd);if(O&&P.slice(O.start+1,O.end).trim())return yd(P,O,Cd);let D=vd(P,"hooks").trimEnd();if(D==="")return`${zi}
|
|
249
249
|
`;return`${D}
|
|
250
250
|
|
|
251
|
-
${
|
|
252
|
-
`}function
|
|
253
|
-
`),{path:O,alreadyInstalled:!1};let D=
|
|
254
|
-
`;return cn(O,J),{path:O,alreadyInstalled:!0}}var
|
|
255
|
-
`)},opencode:{installCommands:
|
|
256
|
-
`),`Enabled ${
|
|
257
|
-
`),`Enabled npm:cc-safety-net extensions in ${O}`}function
|
|
258
|
-
`)}async function
|
|
259
|
-
Removing the plugin files anyway; ${Rn} may still be listed in the Hermes config.`)}},restartNote:"Restart Hermes to apply the change."}};async function
|
|
260
|
-
`)}var
|
|
261
|
-
`);function
|
|
251
|
+
${zi}
|
|
252
|
+
`}function Rd(P){let O=Pd(P);if(Sh(Ph(O),{recursive:!0}),!kd(O))return cn(O,`${zi}
|
|
253
|
+
`),{path:O,alreadyInstalled:!1};let D=xd(O,"utf-8");if(D.includes(or))return{path:O,alreadyInstalled:!0};return cn(O,Rh(D)),{path:O,alreadyInstalled:!1}}function Ed(P){let O=Pd(P);if(!kd(O))return{path:O,alreadyInstalled:!1};let D=xd(O,"utf-8");if(!D.includes(or))return{path:O,alreadyInstalled:!1};let j=Ki(D,"hooks",Sd),J=j?hd(D,j,Cd):`${bd(D,"hooks",or)}
|
|
254
|
+
`;return cn(O,J),{path:O,alreadyInstalled:!0}}var Yi="safety-net@cc-marketplace",_d=new Set(["claude-code","codex","copilot-cli","gemini-cli","hermes-agent","openclaw","opencode","pi"]),Ad=new Set(["antigravity-cli","cursor","devin","droid","grok-build","hermes-agent","kimi-code"]);function Zi(P){return/^\s*safety-net@cc-marketplace[^a-z0-9-][^\n]*installed,/m.test(P??"")}function Dd(P){return/^\s*cc-safety-net[^a-z0-9-][^\n]*installed,/m.test(P??"")}function Eh(P){return/^Marketplace `cc-marketplace`\s*$/m.test(P??"")}var Ld={"claude-code":{installCommands:(P)=>{let O=Ir(P,"cc-safety-net@cc-marketplace");return{commands:[...O?[["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"]],...$r(P).status==="disabled"?[["claude","plugin","enable","cc-safety-net@cc-marketplace"]]:[]],cleanupCommands:Ir(P,Yi)?[["claude","plugin","uninstall",Yi]]:[],update:O}},uninstallCommands:[["claude","plugin","uninstall","cc-safety-net@cc-marketplace"],["claude","plugin","marketplace","remove","cc-marketplace"]]},codex:{installCommands:async(P,O)=>{let D=O??await hn(["codex","plugin","list"]),j=Dd(D);return{commands:[j||Eh(D)?["codex","plugin","marketplace","upgrade","cc-marketplace"]:["codex","plugin","marketplace","add","kenryu42/cc-marketplace"],["codex","plugin","add","cc-safety-net@cc-marketplace"]],cleanupCommands:Zi(D)?[["codex","plugin","remove","safety-net@cc-marketplace"]]:[],update:j}},uninstallCommands:[["codex","plugin","remove","cc-safety-net@cc-marketplace"],["codex","plugin","marketplace","remove","cc-marketplace"]],postInstallMessage:_a},"copilot-cli":{installCommands:async()=>{let P=await hn(["copilot","plugin","list"]),O=[...Ma(P)?[["copilot","plugin","uninstall","copilot-safety-net"]]:[],...Ua(P)?[["copilot","plugin","uninstall",ja]]:[]];if(Fa(P))return{commands:[["copilot","plugin","marketplace","update","cc-marketplace"],["copilot","plugin","update",An]],cleanupCommands:O,update:!0};return{commands:[Ha(await hn(["copilot","plugin","marketplace","list"]))?["copilot","plugin","marketplace","update","cc-marketplace"]:["copilot","plugin","marketplace","add","kenryu42/cc-marketplace"],["copilot","plugin","install",An]],cleanupCommands:O}},uninstallCommands:[["copilot","plugin","uninstall","cc-safety-net@cc-marketplace"],["copilot","plugin","marketplace","remove","cc-marketplace"]]},"gemini-cli":{installCommands:(P)=>{let O=ri(P);if(O.status==="configured")return{commands:[["gemini","extensions","update","gemini-safety-net"]],update:!0};if(O.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:hi,installCommands:(P)=>{let O=!ao(so(Yt(P),Pn));return{commands:Kl(),afterInstall:()=>zl(O)}},uninstallCommands:[["openclaw","plugins","uninstall",un,"--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(`
|
|
255
|
+
`)},opencode:{installCommands:ic},pi:{installCommands:[["pi","install","npm:cc-safety-net"]],uninstallCommands:[["pi","uninstall","npm:cc-safety-net"]]},"deepseek-harness":{installCommands:(P)=>ud(P),uninstallCommands:(P)=>pd(P)}};function Nd(P,O=(D)=>D){try{let D=JSON.parse(O(Od(P,"utf-8")));if(!D||typeof D!=="object"||Array.isArray(D))throw Error(`Settings file ${P} must be a JSON object`);return D}catch(D){if(D instanceof SyntaxError)throw Error(`Failed to parse ${P}: ${D.message}`);throw D}}function _h(P){let O=so(Vt(P),"settings.json");if(!ao(O))return;let D=Nd(O,vn),j=D.enabledPlugins;if(!j||typeof j!=="object"||Array.isArray(j))return;if(j[An]!==!1)return;let J=Od(O,"utf-8"),Y=J.replace(new RegExp(`("${An}"\\s*:\\s*)false`),"$1true");return j[An]=!0,cn(O,Y!==J?Y:`${JSON.stringify(D,null,2)}
|
|
256
|
+
`),`Enabled ${An} plugin in ${O}`}function Ah(P){let O=Ei(P);if(!ao(O))return;let D=Nd(O);if(!Array.isArray(D.packages))return;let j=D.packages.find((J)=>!!J&&typeof J==="object"&&!Array.isArray(J)&&_i(J.source)&&("extensions"in J));if(!j)return;return delete j.extensions,cn(O,`${JSON.stringify(D,null,2)}
|
|
257
|
+
`),`Enabled npm:cc-safety-net extensions in ${O}`}function Td(P,O){let D=gn({label:O,booleans:Object.fromEntries($n.map((Y)=>[Y.target,[Y.flag]]))},P),j=D.errors[0];if(j)throw Error(j);let J=$n.filter((Y)=>D.flags[Y.target]).map((Y)=>Y.target);if(J.length!==1)throw Error(`Choose exactly one ${O} target: ${$n.map((Y)=>Y.flag).join(", ")}`);return J[0]}async function jd(P,O=St){let[D,j,J]=await Promise.all([O(["amp","plugins","list"],30000),O(["codex","plugin","list"],30000),O(["copilot","--binary-version"])]);return{codexPluginListOutput:j,hooks:Dt(P,process.cwd(),{ampPluginListOutput:D,codexPluginListOutput:j,copilotCliVersion:J})}}async function Th(P,O,D=St){let j=await jd(P,D);return j.hooks.filter((J)=>O==="install"?J.configured:J.detected&&J.method!==Hr||J.inspectionStatus==="not-inspected").filter((J)=>J.platform!=="codex"||!Zi(j.codexPluginListOutput)||Dd(j.codexPluginListOutput)).map((J)=>J.platform)}function Ih(P,O,D,j){if(D.length>0)return{finish:async()=>[Td(D,O)]};if(!j.selectTargets&&!Fi(j.input,j.output))return{finish:async()=>[Td(D,O)]};let J=j.detectConfiguredTargets??(()=>Th(P,O,j.fetchVersion)),Y=Promise.all([nd(j.probeTargets).then((re)=>fd(re,P)),J()]);return{ready:Y,finish:async()=>{let[re,ie]=await Y,de=td(re,{action:O,configuredTargets:ie}),fe=j.selectTargets?await j.selectTargets(O,$d(O,de)):await jc(O,$d(O,de),{input:j.input,output:j.output});if(fe==="update")return fe;if(!fe||fe.length===0)return null;return Bi(fe)}}}async function $h(P,O,D=!1,j){let J=Ld[P];J.beforeInstall?.(O);let Y=typeof J.installCommands==="function"?await J.installCommands(O,j):{commands:J.installCommands};return await gi(Y.commands),await Gl(Y.cleanupCommands??[]),await Y.afterInstall?.(),[`${Y.update||D?"Updated":"Installed"} ${mn(P)} integration`,Y.message??J.postInstallMessage].filter(Boolean).join(`
|
|
258
|
+
`)}async function Oh(P,O){let D=Ld[P];if(!D.uninstallCommands)throw Error(`${mn(P)} uninstall is not supported`);let j=typeof D.uninstallCommands==="function"?D.uninstallCommands(O):{commands:D.uninstallCommands};return await gi(j.commands),j.afterUninstall?.(),`Uninstalled ${mn(P)} integration`}function Dh(P){let O=cc(P);return O.alreadyInstalled?`Uninstalled OpenCode plugin from ${O.path}`:`OpenCode plugin not installed in ${O.path}`}var Fd={"antigravity-cli":{install:Xc,uninstall:Qc},cursor:{install:Za,uninstall:Xa},devin:{install:al,uninstall:ll},droid:{install:yl,uninstall:hl},"grok-build":{install:Pl,uninstall:Rl},"kimi-code":{install:Rd,uninstall:Ed}};function Lh(P,O,D,j=!1){if(P==="install"&&!j)io(D);let J=Fd[O][P](D),Y=mn(O),re=P!=="install"?"Uninstalled":j?"Updated":"Installed";return P==="install"&&J.alreadyInstalled?j?`${Y} hook up to date in ${J.path}`:`${Y} hook already installed in ${J.path}`:P==="uninstall"&&!J.alreadyInstalled?`${Y} hook not installed in ${J.path}`:`${re} ${Y} hook ${P==="install"?"in":"from"} ${J.path}`}var Hd={amp:{install:Jc,uninstall:Wc,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:$l,uninstall:Ol,afterInstall:async(P)=>{let O=pi(P);return await hn(["hermes","plugins","enable",Rn,"--no-allow-tool-override"]),!O},beforeUninstall:async(P)=>{ui(P);try{await hn(["hermes","plugins","disable",Rn])}catch(O){console.warn(`${O instanceof Error?O.message:String(O)}
|
|
259
|
+
Removing the plugin files anyway; ${Rn} may still be listed in the Hermes config.`)}},restartNote:"Restart Hermes to apply the change."}};async function Nh(P,O,D,j=!1){let J=Hd[O];if(P==="uninstall")await J.beforeUninstall?.(D);let Y=P==="install"?await J.install(D):await J.uninstall(D),re=P==="install"&&await J.afterInstall?.(D),ie=mn(O),de=!re&&(P==="install"&&Y.alreadyInstalled||P==="uninstall"&&!Y.alreadyInstalled);return[de?P==="install"?`${ie} plugin ${j?"up to date":"already installed"} at ${Y.path}`:`${ie} plugin not installed at ${Y.path}`:`${P!=="install"?"Uninstalled":j?"Updated":"Installed"} ${ie} plugin ${P==="install"?"at":"from"} ${Y.path}`,de?void 0:J.restartNote].filter(Boolean).join(`
|
|
260
|
+
`)}var jh={"copilot-cli":{afterInstall:_h},"hermes-agent":{beforeInstall:(P,O)=>{if(!O)io(P)}},openclaw:{beforeUninstall:hi},pi:{afterInstall:Ah}};function Fh(P){return P in Fd}function Hh(P){return P in Hd}var Id=["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(`
|
|
261
|
+
`);function Mh(P){if(zt({environment:P,cwd:process.cwd()}).status!=="configured")return Id;return[Id,"",nn.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(`
|
|
262
262
|
`))].join(`
|
|
263
|
-
`)}function
|
|
264
|
-
`)}function
|
|
263
|
+
`)}function $d(P,O){return O.map((D)=>P==="install"&&D.target==="kimi-code"&&D.unavailableReason==="already installed"?{...D,available:!0,unavailableReason:void 0,label:`${D.label} (global hook installed)`}:D)}function Uh(P,O){if(P.selectKimiInstallMethod)return P.selectKimiInstallMethod();if(!Fi(P.input,P.output))return Promise.resolve("global-hook");return Nc({input:P.input,output:P.output,globalHookInstalled:zt({environment:O,cwd:process.cwd()}).status==="configured"})}async function Md(P,O,D,j=!1,J){let Y=jh[O];if(P==="install")Y?.beforeInstall?.(D,j);if(P==="uninstall")Y?.beforeUninstall?.(D);if(Fh(O))return Lh(P,O,D,j);if(Hh(O))return Nh(P,O,D,j);if(P==="uninstall")return O==="opencode"?Dh(D):Oh(O,D);return[await $h(O,D,j,J),await Y?.afterInstall?.(D)].filter(Boolean).join(`
|
|
264
|
+
`)}function Gh(P){let O=gn({label:"update"},P).errors[0];if(O)throw Error(O)}async function qh(P,O=St){let D=await jd(P,O),j=so(Vt(P),"installed-plugins");return{targets:Bi([...D.hooks.filter((Y)=>Y.platform!=="copilot-cli"&&Y.detected&&Y.method!==Hr).map((Y)=>Y.platform),...[Or,Na,La].flatMap((Y)=>ao(so(j,...Y))?["copilot-cli"]:[]),...Ir(P,Yi)?["claude-code"]:[],...Zi(D.codexPluginListOutput)?["codex"]:[]]),codexPluginListOutput:D.codexPluginListOutput}}async function Bh(P){let O=c(),D=P.output??process.stdout,j=(P.scriptPath??process.argv[1]??"").split(/[\\/]/),J=j.find((Ie)=>/^bunx-\d+-/.test(Ie)),Y=J!==void 0||j.includes("_npx")?null:(P.checkLatestVersion??Gn)(),re=async()=>{let Ie=Y&&await Y;if(Ie?.updateAvailable)D.write(`
|
|
265
265
|
Update available: cc-safety-net ${Ie.currentVersion} → ${Ie.latestVersion}. Update this CLI with your package manager, e.g. \`npm i -g cc-safety-net@latest\` for a global install.
|
|
266
|
-
`)},ie=
|
|
267
|
-
`)),await re(),Se.some((Ie)=>Ie.failed)?1:0}function
|
|
268
|
-
`),0;if(J==="update")return(D.runUpdate??(()=>
|
|
269
|
-
`);return}if(de==="plugin"){Y.write(`${
|
|
270
|
-
`);return}}let ie=await
|
|
271
|
-
`)}),0}catch(j){return console.error(
|
|
266
|
+
`)},ie=qh(O,P.fetchVersion??St).then(async(Ie)=>{let Ee=new Set(Ie.targets);return{targets:Ie.targets,codexPluginListOutput:Ie.codexPluginListOutput,available:new Map(await Promise.all($n.filter((Ve)=>Ee.has(Ve.target)&&_d.has(Ve.target)).map(async(Ve)=>[Ve.target,await rr(Ve.probeCommand)])))}}),de=await Ut(P.showBanner??!0,()=>({ready:ie,finish:()=>ie}),()=>Mt({input:P.input??process.stdin,output:D}),{loadingMessage:"Checking installed integrations…",output:D}),fe=await Promise.resolve().then(()=>(md(O.tmpdir,process.platform,J),null)).catch((Ie)=>ir(Ie));if(de.targets.length===0){if(D.write("No installed integrations found. Run `cc-safety-net install` to set one up.\n"),fe!==null)console.error(fe);return await re(),fe===null?0:1}let we=de.targets.some((Ie)=>Ad.has(Ie))?await Promise.resolve().then(()=>(io(O),null)).catch((Ie)=>ir(Ie)):null,Ce=await Rr(Promise.all(de.targets.map((Ie)=>{if(_d.has(Ie)&&!de.available.get(Ie))return Promise.resolve({message:`${mn(Ie)} not found; skipped`,failed:!1});if(we!==null&&Ad.has(Ie))return Promise.resolve({message:we,failed:!0});return Md("install",Ie,O,!0,de.codexPluginListOutput).then((Ee)=>({message:Ee,failed:!1}),(Ee)=>({message:ir(Ee),failed:!0}))})),{loadingMessage:`Updating ${de.targets.length} integration${de.targets.length===1?"":"s"}…`,output:D}),Se=fe===null?Ce:[...Ce,{message:fe,failed:!0}];return Se.forEach((Ie)=>Ie.failed?console.error(Ie.message):D.write(`${Ie.message}
|
|
267
|
+
`)),await re(),Se.some((Ie)=>Ie.failed)?1:0}function Xi(P,O={}){return Promise.resolve().then(()=>Gh(P)).then(()=>Bh(O)).catch((D)=>(console.error(ir(D)),1))}async function sr(P,O,D={}){try{let j=c(),J=await Ut(!0,()=>Ih(j,P,O,D),()=>Mt({input:D.input??process.stdin,output:D.output??process.stdout}),{loadingMessage:P==="install"?"Checking available integrations…":"Checking installed integrations…",output:D.output??process.stdout});if(!J)return(D.output??process.stdout).write(`Cancelled: nothing was ${P}ed.
|
|
268
|
+
`),0;if(J==="update")return(D.runUpdate??(()=>Xi([],{fetchVersion:D.fetchVersion,input:D.input,output:D.output,showBanner:!1})))();let Y=D.output??process.stdout;return await ed(J,async(re)=>{if(re==="kimi-code"&&P==="install"){let de=await Uh(D,j);if(de===null){Y.write(`Cancelled: Kimi Code integration was not installed.
|
|
269
|
+
`);return}if(de==="plugin"){Y.write(`${Mh(j)}
|
|
270
|
+
`);return}}let ie=await Rr(Md(P,re,j),{loadingMessage:`${P==="install"?"Installing":"Uninstalling"} ${mn(re)} integration…`,output:Y});Y.write(`${ie}
|
|
271
|
+
`)}),0}catch(j){return console.error(ir(j)),1}}function ir(P){let O=P instanceof Error?P.message:String(P),D=typeof P==="object"&&P!==null&&"code"in P?P.code:null;if(D==="EACCES"||D==="EPERM")return`${O}
|
|
272
272
|
Check file permissions for the target config file and parent directory.`;if(D==="ENOENT")return`${O}
|
|
273
273
|
Check that the target config path and parent directory exist.`;if(D==="ENOTDIR")return`${O}
|
|
274
|
-
Check that every parent path component is a directory.`;return O}import{mkdirSync as
|
|
275
|
-
`,384),qh(j,384),{path:j,policy:Y,errors:[]}}function Bd(P,O){let D=vt(O,P.home);if(D.errors.length>0)return{errors:D.errors};return{preview:Le(D.policy,P.env),errors:[]}}function Vd(P,O={}){let D=l(P,O);if(!Ud(D))return Hn(P,te,O);let j=Gd(D,"utf-8");if(!j.trim())return Hn(P,te,O);try{return Hn(P,E(JSON.parse(j),P.home),O)}catch{return Hn(P,te,O)}}var Jd=new Set(["check","apply"]),Wd="(unset)";async function zd(P,O,D={}){let j=gn({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},O),J=j.positionals[0],Y=[...j.errors,...J&&!Jd.has(J)?[`Unknown policy subcommand: ${J}`]:[],...J&&Jd.has(J)&&!j.positionals[1]?[`policy ${J} requires a file`]:[],...j.positionals.slice(2).map((Ve)=>`Unexpected policy argument: ${Ve}`)];if(Y.length>0){for(let Ve of Y)console.error(Ve);return 1}let re=j.positionals[1];if(!J||!re)return Lt(mr,console.error),1;let ie=D.cwd??process.cwd(),de=j.flags.global?l(P):h(ie);if(!j.flags.global&&L(P,{cwd:ie}))return console.error(`${de} is the user policy, not a project policy; use --global for the user scope, or run from a project directory`),1;let fe=bt(re),we=[...fe.errors,...sr(fe.value,P.home).map((Ve)=>`${re}: ${Ve}`),...!j.flags.global&&Zh(fe.value)&&fe.value.audit!==void 0?[`${re}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(we.length>0){for(let Ve of we)console.error(Ve);return 1}let Ce=E(fe.value,P.home);if(console.log(`Scope: ${j.flags.global?"user":"project"} (${de})`),console.log(`Proposal: ${re}`),j.flags.global)Kd(E(bt(de).value,P.home),Ce,!0);if(!j.flags.global){let Ve=ar(P).baseline;console.log("Effective policy (user + project merged):"),Kd(ee(Ve,ue(bt(de).value,P.home).policy).policy,ee(Ve,ue(fe.value,P.home).policy).policy,!1)}if(J==="check")return 0;let Se=D.input??process.stdin,Ie=D.output??process.stdout;if(!Se.isTTY||!Ie.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${re}${j.flags.global?" --global":""}`),1;if(!await zh(`Apply this policy to ${de}? [y/N] `,Se,Ie))return console.log("Cancelled; nothing was written."),0;return Yh(P,de,fe.value,Ce,j.flags.global),console.log(`Policy applied: ${de}`),0}function zh(P,O,D){let j=Kh({input:O,output:D,terminal:!1});return new Promise((J)=>{j.once("close",()=>J(!1)),j.question(P,(Y)=>{J(/^y(es)?$/i.test(Y.trim())),j.close()})})}function Yh(P,O,D,j,J){if(J){Hn(P,j);return}Jh(Wh(O),{recursive:!0}),wn(O,io(D,j))}function Kd(P,O,D){let j=oo(P,O,D);if(j.length===0){console.log("No changes.");return}console.log(`Changes (${j.length}):`);for(let J of j)console.log(` ${J.field}: ${J.before??Wd} -> ${J.after??Wd}`)}function Zh(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as db}from"node:path";var Yd="# 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- `caffeinate`, `exec`, `nice`, `nohup`, `setsid`, `stdbuf`, `time`, and `timeout` are built-in transparent wrappers and need no configuration. Configure only other 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 so(P,O){if(!P.ok){nu(P);return}Qd(P,O)}function Xd(P,O,D){if(P.ok)console.log(D);if(!P.add){so(P,`Added rulebook source: ${O}`);return}if(!P.ok){nu(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((j)=>{console.log(` - ${j}`)});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((j)=>j.slice(0,7)).join(", ")}.`);Qd(P,"Rule config updated.")}function Qd(P,O){for(let D of P.changes??[])console.log(D);console.log(O),console.log(""),Xh(P.entries)}function Xh(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let O of P)console.log(` - ${O.name} ${O.version} (${Qh(O.ruleCount)})`),console.log(` Source: ${O.spec}`)}function Qh(P){return`${P} ${P===1?"rule":"rules"}`}function eu(P){wt("Active sources",P.rulebooks,(O)=>[`[${O.source}] ${O.name} ${O.version}`,` Source: ${O.spec}`]),wt("Active rules",P.rules,(O)=>[`[${nv(P,O.name)}] ${O.name}`,...ev(O),` Reason: ${O.reason}`]),wt("Disabled rules",Zd(P,"off"),(O)=>[O.key]),wt("Reason overrides",Zd(P,"reason"),(O)=>[O.key,` Reason: ${O.value.reason}`]),wt("Transparent wrappers",P.transparent_wrappers,(O)=>[O]),wt("Issues",P.errors,(O)=>[O]),wt("Warnings",P.warnings,(O)=>[O])}function wt(P,O,D){if(O.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${O.length}):`);for(let j of O){let[J,...Y]=D(j);console.log(` - ${J}`);for(let re of Y)console.log(` ${re}`)}}function ev(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 nv(P,O){return P.rulebooks.find((D)=>D.rules.includes(O))?.source??"project"}function Zd(P,O){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((D)=>{if(O==="off")return D[1]==="off";return!!D[1]&&typeof D[1]==="object"}).map(([D,j])=>({key:D,value:j}))}function nu(P){for(let O of P.errors)console.error(O)}import{dirname as Su,join as go}from"node:path";import{join as rs,resolve as fv}from"node:path";function Qi(P){let O=m(P);if(O.errors.length>0)return{ok:!1,result:{ok:!1,errors:O.errors,entries:[]}};return{ok:!0,config:O.config??ut}}function tu(P){wn(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function ru(P){wn(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 uo}from"node:path";var tv="custom.";function ao(P){if(P.rulebook_version!==2)return[];let O=P.rules.map((D)=>({name:D.name,command:D.command,block_args:[],match:D.match,reason:D.reason,intent:D.intent}));return(P.tests??[]).flatMap((D,j)=>{let J=es(y(D.command));if(J.length===0)return[`tests[${j}]: could not parse fixture command: ${D.command}`];let Y=J.reduce((re,ie)=>re??A(ie,O)?.id.slice(tv.length),void 0);if(D.expect==="blocked"){if(Y===D.rule)return[];let re=Y?`"${Y}" matched first`:"no rule matched";return[`tests[${j}]: expected "${D.rule}" to block "${D.command}" but ${re}`]}return Y?[`tests[${j}]: expected "${D.command}" to be allowed but "${Y}" matched`]:[]})}function es(P){return P.nodes.flatMap((O)=>{if(O.kind==="group"||O.kind==="function")return es(O.body);if(O.kind!=="command")return[];let D=ve(ne(O.dialect,O.words)).words.map(t);return[...D.length>0?[D]:[],...O.nested.flatMap((j)=>es(j))]})}var lo=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function co(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??lo.maxRequests,maxResponseBytes:P.maxResponseBytes??lo.maxResponseBytes}}function Mn(P){return{controller:new AbortController,budget:co(),resolveUrl:P}}function ou(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function iu(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function su(P,O){if(O>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=O,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=O}var cu=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function au(P,O,D=S(uo(uo(O)),"rules policy"),j=Mn()){if(w(P))return sv(P,j);return iv(P,O,D)}async function du(P,O,D,j,J,Y){if(!w(P))return au(P,O,D,j);let re=J?null:rv(P,O,D);if(re)return re;if(!J&&!Y)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return au(P,O,D,j)}function rv(P,O,D=S(uo(uo(O)),"rules policy")){let j=N(P),J=M(O,j.name),Y=r(i(D,J));if(Y===null)return null;let re=le(ns(Y,`Invalid rulebook ${J}.`));if(re.name!==j.name)throw Error(`rulebook name "${re.name}" in ${J} must match "${j.name}"`);return{spec:P,rulebook:re,content:Y}}async function uu(P,O={}){if(!Z(P))throw Error(`Invalid GitHub repository source: ${P}`);let[D,j]=P.split("/");if(!D||!j)throw Error(`Invalid GitHub repository source: ${P}`);if(O.ref!==void 0&&!ae(O.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${O.ref}`);let J=O.operation??Mn(),Y=O.ref??await ov(D,j,P,J),re=await fu(D,j,Y,P,J),ie=await po(`https://api.github.com/repos/${D}/${j}/git/trees/${re}?recursive=1`,"tree",J),de=ie.response;if(!de.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${de.status}`);let fe=JSON.parse(ie.content);if(!Array.isArray(fe?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let we=fe.tree,Ce=[...new Set(we.flatMap((Se)=>{if(!Se||typeof Se!=="object")return[];let Ie=Se;if(Ie.type!=="blob"||typeof Ie.path!=="string")return[];let Ee=Ie.path.match(ct);return Ee?.[1]?[Ee[1]]:[]}))].sort();if(Ce.length===0)throw Error(`No rulebooks found in ${P} under ${he}/`);return{source:P,owner:D,repo:j,ref:Y,commit:re,names:Ce}}async function ov(P,O,D,j){let J=await po(`https://api.github.com/repos/${P}/${O}`,"metadata",j),Y=J.response;if(!Y.ok)throw Error(`Failed to inspect ${D}: GitHub returned ${Y.status}`);let ie=JSON.parse(J.content)?.default_branch;if(typeof ie!=="string"||ie==="")throw Error(`Failed to inspect ${D}: missing default branch`);if(!ae(ie))throw Error(`GitHub returned an invalid default branch: ${ie}`);return ie}function iv(P,O,D){lt(P);let j=M(O,P),J=r(i(D,j));if(J===null)throw Error(`Rulebook source not found: ${P}`);let Y=pu(ns(J,"Invalid local rulebook source."));if(Y.name!==P)throw Error(`rulebook name "${Y.name}" must match local source "${P}"`);return{spec:P,rulebook:Y,content:J}}async function sv(P,O){let D=N(P),j=await fu(D.owner,D.repo,D.ref,P,O),J=await po(`https://raw.githubusercontent.com/${D.owner}/${D.repo}/${j}/${D.path}`,"raw",O),Y=J.response;if(!Y.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${Y.status}`);let re=J.content,ie=pu(ns(re,"Invalid GitHub rulebook response."));if(ie.name!==D.name)throw Error(`rulebook name "${ie.name}" must match GitHub source "${D.name}"`);return{spec:P,rulebook:ie,content:re}}function pu(P){let O=le(P),D=ao(O);if(D.length>0)throw Error(D.join("; "));return O}function ns(P,O){try{return JSON.parse(P)}catch{throw Error(O)}}async function fu(P,O,D,j,J){let Y=await po(`https://api.github.com/repos/${P}/${O}/commits/${encodeURIComponent(D)}`,"commit",J),re=Y.response;if(!re.ok)throw Error(`Failed to resolve ${j}: GitHub returned ${re.status}`);let ie=JSON.parse(Y.content);if(typeof ie?.sha!=="string"||ie.sha==="")throw Error(`Failed to resolve commit for ${j}`);return ie.sha}async function av(P,O,D={}){if(D.signal?.aborted)throw D.signal.reason;let j=D.budget??co(),J=new AbortController,Y=()=>J.abort(D.signal?.reason);D.signal?.addEventListener("abort",Y,{once:!0});let re=!1,ie=setTimeout(()=>{if(J.signal.aborted)return;re=!0,J.abort()},D.timeoutMs??cu.timeoutMs);try{if(D.signal?.aborted)throw D.signal.reason;iu(j);let de=await fetch(P,{signal:J.signal,redirect:"error"});if(!de.ok)return mu(de),{response:de,content:""};return{response:de,content:await lv(de,O,j,()=>J.abort())}}catch(de){if(re)throw Error("GitHub request timed out",{cause:de});if(D.signal?.aborted)throw D.signal.reason;throw de}finally{clearTimeout(ie),D.signal?.removeEventListener("abort",Y)}}function po(P,O,D){return av(D.resolveUrl?.(P)??P,O,{budget:D.budget,signal:D.controller.signal})}async function lv(P,O,D=co(),j){let J=cu[`${O}Bytes`],Y=Number(P.headers.get("content-length"));if(Number.isFinite(Y)&&Y>J)throw mu(P),Error(`GitHub ${O} response exceeds ${J} bytes`);if(!P.body)return"";let re=P.body.getReader(),ie=[],de=0;while(!0){let fe=await re.read();if(fe.done)break;try{su(D,fe.value.byteLength)}catch(we){throw j?.(),lu(re),we}if(de+=fe.value.byteLength,de>J)throw j?.(),lu(re),Error(`GitHub ${O} response exceeds ${J} bytes`);ie.push(Buffer.from(fe.value))}return Buffer.concat(ie,de).toString("utf-8")}function mu(P){if(!P.body)return;gu(()=>P.body?.cancel())}function lu(P){gu(()=>P.cancel())}function gu(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var cv=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function yu(P,O){let D=bu(P.rules,O);if(D.length>0)return{ok:!0,specs:D};return vu(P.rules,O)}function hu(P,O){let D=bu(P,O);if(D.length>0)return{ok:!0,specs:D};let j=uv(P,O);if(j.length>0)return{ok:!0,specs:j};let J=pv(P,O);if(!J.ok)return J;if(J.specs.length>0)return{ok:!0,specs:J.specs};return vu(P,O)}function vu(P,O){let D=P.filter((j)=>ts(j)?.name===O);if(D.length===1)return{ok:!0,specs:D};return dv(O,D)}function dv(P,O){return{ok:!1,result:{ok:!1,errors:O.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${O.join(", ")}`],entries:[]}}}function bu(P,O){return P.filter((D)=>D===O)}function uv(P,O){let D=O.match(cv),j=D?.[1],J=D?.[2],Y=D?.[3];if(!j||!J||!Y||!ae(Y))return[];return wu(P,(re)=>re.owner===j&&re.repo===J&&re.ref===Y)}function pv(P,O){if(!Z(O))return{ok:!0,specs:[]};let[D,j]=O.split("/"),J=wu(P,(re)=>re.owner===D&&re.repo===j);if(new Set(J.map((re)=>ts(re)?.ref).filter((re)=>!!re)).size<2)return{ok:!0,specs:J};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${O}. Use an explicit ref:`,` cc-safety-net rule remove ${O}#<ref>`],entries:[]}}}function ts(P){try{return N(P)}catch{return null}}function wu(P,O){return P.filter((D)=>{let j=ts(D);return j?O(j):!1})}async function mo(P,O={}){let D=os(O);return mv(P,D,await fo(P,D,Mn()))}function mv(P,O,D){if(!D.ok)return D;let j=kn(P,O),J=[...new Set(B(j.configPath,j.filesystemScope))];if(J.length===0)return D;return{ok:!1,errors:J,entries:D.entries}}async function fo(P,O,D,j={},J=new Set,Y=new Set){try{let re=kn(P,O),ie=Qi(re.configTarget);if(!ie.ok)return ie.result;let de=ie.config,fe=O.only?yu(de,O.only):{ok:!0,specs:de.rules};if(!fe.ok)return fe.result;let we=new Set([...O.refresh?fe.specs:[],...J]),Ce=(rn)=>du(rn,re.configDir,re.filesystemScope,D,we.has(rn),!O.refresh||we.has(rn)),Se=await Pv(de.rules,O.refresh?(rn)=>Ce(rn).then((fn)=>({ok:!0,item:fn})).catch((fn)=>{if(ou(fn))throw fn;return{ok:!1,spec:rn,message:fn instanceof Error?fn.message:String(fn)}}):async(rn)=>({ok:!0,item:await Ce(rn)}),D),Ie=Se.filter((rn)=>!rn.ok),Ee=Se.filter((rn)=>rn.ok).map((rn)=>rn.item),Ve=Ee.flatMap((rn)=>gv(rn,de.rules)),Me=Ee.flatMap((rn)=>yv(rn,Y,re)),en=new Set([...Ve,...Me].map((rn)=>rn.spec)),on=[...Ie,...Ve,...Me],sn=[],an=vv(sn,()=>Ee.flatMap((rn)=>en.has(rn.spec)||on.length>0&&Y.has(rn.spec)?[]:hv(rn,re,j,sn)));return{ok:on.length===0,errors:on.map((rn)=>`Failed to update ${rn.spec}: ${rn.message}`),entries:Ee.map(wv),changes:an}}catch(re){return cr(re)}}function gv(P,O){if(!w(P.spec))return[];let D=Ne(P.spec),j=O.filter((J)=>J!==P.spec&&Ne(J).toLowerCase()===D.toLowerCase());if(j.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${D}" is also claimed by ${j.join(", ")}; rename one of them`}]}function yv(P,O,D){if(!O.has(P.spec)||!w(P.spec))return[];let j=M(D.configDir,P.rulebook.name),J=r(i(D.filesystemScope,j));if(J===null||J===P.content)return[];return[{ok:!1,spec:P.spec,message:`${j} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function hv(P,O,D,j){if(!w(P.spec))return[];let J=M(O.configDir,P.rulebook.name),Y=i(O.filesystemScope,J),re=r(Y);if(re===P.content)return[];return j?.push({target:Y,previous:re}),g(Y,P.content,void 0,D._testAfterPolicyRename),bv(P,re)}function vv(P,O){try{return O()}catch(D){for(let j of[...P].reverse()){if(j.previous===null){H(j.target);continue}g(j.target,j.previous)}throw D}}function bv(P,O){if(O===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let D=xe(O),j="problem"in D?null:D.rulebook,J=new Map(j?.rules.map((re)=>[re.name,JSON.stringify(re)])??[]),Y=new Set(P.rulebook.rules.map((re)=>re.name));return[`Updated ${P.spec} (${j?.version??"unreadable"} -> ${P.rulebook.version})`,...[...Y].filter((re)=>!J.has(re)).map((re)=>` + ${re}`),...[...J.keys()].filter((re)=>!Y.has(re)).map((re)=>` - ${re}`),...P.rulebook.rules.filter((re)=>{let ie=J.get(re.name);return ie!==void 0&&ie!==JSON.stringify(re)}).map((re)=>` ~ ${re.name}`)]}function wv(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function ku(P,O,D={}){return kv(P,O,Ev(D),Mn())}async function kv(P,O,D,j,J={}){let Y=null,re=!1;try{let ie=kn(P,D),de=r(ie.configTarget);Y={target:ie.configTarget,content:de};let fe=Qi(ie.configTarget);if(!fe.ok)return fe.result;let we=fe.config,Ce=Z(O);xv(O,D,Ce);let Se=Ce?await uu(O,{ref:D.ref,operation:j}):null,Ie=Se?Cv(Se,D.rulebooks):[],Ee=Se?Ie.map((sn)=>Sv(we.rules,Se,sn)??`${O}#${Se.ref}/${sn}`):[O],Ve=Ee.filter((sn)=>!we.rules.includes(sn)),Me=[...we.rules,...Ve];if(Me.length>ye)return Rv();if(Me.length!==we.rules.length)re=!0,wn(ie.configTarget,{version:1,rules:Me,overrides:we.overrides??{},transparent_wrappers:we.transparent_wrappers??[]},void 0,J._testAfterPolicyRename);let en=await fo(P,D,j,J,new Set(Ve),new Set(Ve));if(!en.ok)lr(ie.configTarget,de);if(!en.ok||!Se)return en;let on=Ie.filter((sn,an)=>Ve.includes(Ee[an]??""));return{...en,add:{source:O,ref:Se.ref,selected:Ie,added:on,alreadyConfigured:Ie.filter((sn)=>!on.includes(sn)),commits:Ve.length>0?[Se.commit]:[]}}}catch(ie){if(re&&Y)try{lr(Y.target,Y.content)}catch(de){return cr(de)}return cr(ie)}}function xv(P,O,D){if(!D&&O.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!D&&O.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(O.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let j=O.rulebooks?.filter((J)=>!d.test(J))??[];if(j.length>0)throw Error(`Invalid rulebook names: ${j.join(", ")}`)}function Cv(P,O){let D=O?[...new Set(O)]:P.names,j=D.filter((J)=>!P.names.includes(J));if(j.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${j.join(", ")}
|
|
276
|
-
Available rulebooks: ${P.names.join(", ")}`);return D}function
|
|
277
|
-
No config files found. Using built-in rules only.`),0;for(let Ee of Ce)if(Ee.inactive)
|
|
278
|
-
Added $schema to ${Ee.scope.toLowerCase()} config.`);
|
|
279
|
-
${nn.red(Ee)}`);if(Ie)if(Ie.result.errors.length>0)
|
|
274
|
+
Check that every parent path component is a directory.`;return O}import{mkdirSync as Yh}from"node:fs";import{dirname as Zh}from"node:path";import{createInterface as Xh}from"node:readline";import{existsSync as Gd,readFileSync as Vh}from"node:fs";function vt(P,O){let D=tt(P,O);return{policy:D.policy,errors:ce(Xe(D.issues,et,(j)=>j.kind==="custom")," "," ")}}function ar(P,O){return vt(P,O).errors}function Ud(P,O){return{"safety.level":P.safety.level,...Qi("safety.overrides",P.safety.overrides),"workflow.worktree_mode":String(P.workflow.worktree_mode),"destructive_command_protection.enabled":String(P.destructive_command_protection.enabled),...Qi("destructive_command_protection.overrides",P.destructive_command_protection.overrides),"destructive_command_protection.allow_paths":es(P.destructive_command_protection.allow_paths),"secret_protection.enabled":String(P.secret_protection.enabled),...Qi("secret_protection.overrides",P.secret_protection.overrides),"secret_protection.deny_paths":es(P.secret_protection.deny_paths),"secret_protection.allow_paths":es(P.secret_protection.allow_paths),...O?{"audit.retention_days":String(P.audit.retention_days)}:{}}}function lo(P,O,D){let j=Ud(P,D),J=Ud(O,D);return[...new Set([...Object.keys(j),...Object.keys(J)])].flatMap((Y)=>j[Y]===J[Y]?[]:[{field:Y,before:j[Y],after:J[Y]}])}function lr(P,O){let D=l(P,O);if(!Gd(D))return{baseline:E(globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__,P.home),diagnostics:[]};let j=bt(D),J=vt(j.value,P.home);return{baseline:J.policy,diagnostics:j.errors.length>0?j.errors:J.errors}}function bt(P){if(!Gd(P))return{errors:[`${P}: file not found`]};try{return{value:JSON.parse(Vh(P,"utf-8")),errors:[]}}catch(O){let D=O instanceof Error?O.message:String(O);return{errors:[`${P}: ${O instanceof SyntaxError?`Invalid JSON: ${D}`:D}`]}}}function co(P,O){let D=Jh(P)?P:{};return{version:O.version,...Object.fromEntries(["safety","workflow","destructive_command_protection","secret_protection"].filter((j)=>D[j]!==void 0).map((j)=>[j,D[j]]))}}function Qi(P,O){return Object.fromEntries(Object.entries(O).flatMap(([D,j])=>j===void 0?[]:[[`${P}.${D}`,String(j)]]))}function es(P){return P.length===0?"(none)":P.join(", ")}function Jh(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{chmodSync as Wh,existsSync as qd,mkdirSync as Kh,readFileSync as Bd}from"node:fs";import{dirname as zh}from"node:path";function Vd(P,O={}){let D=l(P,O);if(!qd(D))return{path:D,exists:!1,raw:"",policy:F(),errors:[]};let j=Bd(D,"utf-8");if(!j.trim())return{path:D,exists:!0,raw:j,policy:F(),errors:["Config file is empty"]};try{let J=vt(JSON.parse(j),P.home);return{path:D,exists:!0,raw:j,policy:J.policy,errors:J.errors}}catch(J){return{path:D,exists:!0,raw:j,policy:F(),errors:[`Invalid JSON: ${J instanceof Error?J.message:String(J)}`]}}}function Hn(P,O,D={}){let j=l(P,D),J=vt(O,P.home);if(J.errors.length>0)return{path:j,policy:F(),errors:J.errors};let Y=J.policy;return Kh(zh(j),{recursive:!0,mode:448}),g(se(j),`${JSON.stringify(Y,null,2)}
|
|
275
|
+
`,384),Wh(j,384),{path:j,policy:Y,errors:[]}}function Jd(P,O){let D=vt(O,P.home);if(D.errors.length>0)return{errors:D.errors};return{preview:Le(D.policy,P.env),errors:[]}}function Wd(P,O={}){let D=l(P,O);if(!qd(D))return Hn(P,te,O);let j=Bd(D,"utf-8");if(!j.trim())return Hn(P,te,O);try{return Hn(P,E(JSON.parse(j),P.home),O)}catch{return Hn(P,te,O)}}var Kd=new Set(["check","apply"]),zd="(unset)";async function Zd(P,O,D={}){let j=gn({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},O),J=j.positionals[0],Y=[...j.errors,...J&&!Kd.has(J)?[`Unknown policy subcommand: ${J}`]:[],...J&&Kd.has(J)&&!j.positionals[1]?[`policy ${J} requires a file`]:[],...j.positionals.slice(2).map((Ve)=>`Unexpected policy argument: ${Ve}`)];if(Y.length>0){for(let Ve of Y)console.error(Ve);return 1}let re=j.positionals[1];if(!J||!re)return Lt(gr,console.error),1;let ie=D.cwd??process.cwd(),de=j.flags.global?l(P):h(ie);if(!j.flags.global&&L(P,{cwd:ie}))return console.error(`${de} is the user policy, not a project policy; use --global for the user scope, or run from a project directory`),1;let fe=bt(re),we=[...fe.errors,...ar(fe.value,P.home).map((Ve)=>`${re}: ${Ve}`),...!j.flags.global&&nv(fe.value)&&fe.value.audit!==void 0?[`${re}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(we.length>0){for(let Ve of we)console.error(Ve);return 1}let Ce=E(fe.value,P.home);if(console.log(`Scope: ${j.flags.global?"user":"project"} (${de})`),console.log(`Proposal: ${re}`),j.flags.global)Yd(E(bt(de).value,P.home),Ce,!0);if(!j.flags.global){let Ve=lr(P).baseline;console.log("Effective policy (user + project merged):"),Yd(ee(Ve,ue(bt(de).value,P.home).policy).policy,ee(Ve,ue(fe.value,P.home).policy).policy,!1)}if(J==="check")return 0;let Se=D.input??process.stdin,Ie=D.output??process.stdout;if(!Se.isTTY||!Ie.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${re}${j.flags.global?" --global":""}`),1;if(!await Qh(`Apply this policy to ${de}? [y/N] `,Se,Ie))return console.log("Cancelled; nothing was written."),0;return ev(P,de,fe.value,Ce,j.flags.global),console.log(`Policy applied: ${de}`),0}function Qh(P,O,D){let j=Xh({input:O,output:D,terminal:!1});return new Promise((J)=>{j.once("close",()=>J(!1)),j.question(P,(Y)=>{J(/^y(es)?$/i.test(Y.trim())),j.close()})})}function ev(P,O,D,j,J){if(J){Hn(P,j);return}Yh(Zh(O),{recursive:!0}),wn(O,co(D,j))}function Yd(P,O,D){let j=lo(P,O,D);if(j.length===0){console.log("No changes.");return}console.log(`Changes (${j.length}):`);for(let J of j)console.log(` ${J.field}: ${J.before??zd} -> ${J.after??zd}`)}function nv(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as mb}from"node:path";var Xd="# 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- `caffeinate`, `exec`, `nice`, `nohup`, `setsid`, `stdbuf`, `time`, and `timeout` are built-in transparent wrappers and need no configuration. Configure only other 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 uo(P,O){if(!P.ok){ru(P);return}nu(P,O)}function eu(P,O,D){if(P.ok)console.log(D);if(!P.add){uo(P,`Added rulebook source: ${O}`);return}if(!P.ok){ru(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((j)=>{console.log(` - ${j}`)});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((j)=>j.slice(0,7)).join(", ")}.`);nu(P,"Rule config updated.")}function nu(P,O){for(let D of P.changes??[])console.log(D);console.log(O),console.log(""),tv(P.entries)}function tv(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let O of P)console.log(` - ${O.name} ${O.version} (${rv(O.ruleCount)})`),console.log(` Source: ${O.spec}`)}function rv(P){return`${P} ${P===1?"rule":"rules"}`}function tu(P){wt("Active sources",P.rulebooks,(O)=>[`[${O.source}] ${O.name} ${O.version}`,` Source: ${O.spec}`]),wt("Active rules",P.rules,(O)=>[`[${iv(P,O.name)}] ${O.name}`,...ov(O),` Reason: ${O.reason}`]),wt("Disabled rules",Qd(P,"off"),(O)=>[O.key]),wt("Reason overrides",Qd(P,"reason"),(O)=>[O.key,` Reason: ${O.value.reason}`]),wt("Transparent wrappers",P.transparent_wrappers,(O)=>[O]),wt("Issues",P.errors,(O)=>[O]),wt("Warnings",P.warnings,(O)=>[O])}function wt(P,O,D){if(O.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${O.length}):`);for(let j of O){let[J,...Y]=D(j);console.log(` - ${J}`);for(let re of Y)console.log(` ${re}`)}}function ov(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 iv(P,O){return P.rulebooks.find((D)=>D.rules.includes(O))?.source??"project"}function Qd(P,O){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((D)=>{if(O==="off")return D[1]==="off";return!!D[1]&&typeof D[1]==="object"}).map(([D,j])=>({key:D,value:j}))}function ru(P){for(let O of P.errors)console.error(O)}import{dirname as Ru,join as bo}from"node:path";import{join as is,resolve as hv}from"node:path";function ns(P){let O=m(P);if(O.errors.length>0)return{ok:!1,result:{ok:!1,errors:O.errors,entries:[]}};return{ok:!0,config:O.config??ut}}function ou(P){wn(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function iu(P){wn(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 go}from"node:path";var sv="custom.";function po(P){if(P.rulebook_version!==2)return[];let O=P.rules.map((D)=>({name:D.name,command:D.command,block_args:[],match:D.match,reason:D.reason,intent:D.intent}));return(P.tests??[]).flatMap((D,j)=>{let J=ts(y(D.command));if(J.length===0)return[`tests[${j}]: could not parse fixture command: ${D.command}`];let Y=J.reduce((re,ie)=>re??A(ie,O)?.id.slice(sv.length),void 0);if(D.expect==="blocked"){if(Y===D.rule)return[];let re=Y?`"${Y}" matched first`:"no rule matched";return[`tests[${j}]: expected "${D.rule}" to block "${D.command}" but ${re}`]}return Y?[`tests[${j}]: expected "${D.command}" to be allowed but "${Y}" matched`]:[]})}function ts(P){return P.nodes.flatMap((O)=>{if(O.kind==="group"||O.kind==="function")return ts(O.body);if(O.kind!=="command")return[];let D=ve(ne(O.dialect,O.words)).words.map(t);return[...D.length>0?[D]:[],...O.nested.flatMap((j)=>ts(j))]})}var fo=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function mo(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??fo.maxRequests,maxResponseBytes:P.maxResponseBytes??fo.maxResponseBytes}}function Mn(P){return{controller:new AbortController,budget:mo(),resolveUrl:P}}function su(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function au(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function lu(P,O){if(O>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=O,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=O}var uu=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function cu(P,O,D=S(go(go(O)),"rules policy"),j=Mn()){if(w(P))return dv(P,j);return cv(P,O,D)}async function pu(P,O,D,j,J,Y){if(!w(P))return cu(P,O,D,j);let re=J?null:av(P,O,D);if(re)return re;if(!J&&!Y)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return cu(P,O,D,j)}function av(P,O,D=S(go(go(O)),"rules policy")){let j=N(P),J=M(O,j.name),Y=r(i(D,J));if(Y===null)return null;let re=le(rs(Y,`Invalid rulebook ${J}.`));if(re.name!==j.name)throw Error(`rulebook name "${re.name}" in ${J} must match "${j.name}"`);return{spec:P,rulebook:re,content:Y}}async function fu(P,O={}){if(!Z(P))throw Error(`Invalid GitHub repository source: ${P}`);let[D,j]=P.split("/");if(!D||!j)throw Error(`Invalid GitHub repository source: ${P}`);if(O.ref!==void 0&&!ae(O.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${O.ref}`);let J=O.operation??Mn(),Y=O.ref??await lv(D,j,P,J),re=await gu(D,j,Y,P,J),ie=await yo(`https://api.github.com/repos/${D}/${j}/git/trees/${re}?recursive=1`,"tree",J),de=ie.response;if(!de.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${de.status}`);let fe=JSON.parse(ie.content);if(!Array.isArray(fe?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let we=fe.tree,Ce=[...new Set(we.flatMap((Se)=>{if(!Se||typeof Se!=="object")return[];let Ie=Se;if(Ie.type!=="blob"||typeof Ie.path!=="string")return[];let Ee=Ie.path.match(ct);return Ee?.[1]?[Ee[1]]:[]}))].sort();if(Ce.length===0)throw Error(`No rulebooks found in ${P} under ${he}/`);return{source:P,owner:D,repo:j,ref:Y,commit:re,names:Ce}}async function lv(P,O,D,j){let J=await yo(`https://api.github.com/repos/${P}/${O}`,"metadata",j),Y=J.response;if(!Y.ok)throw Error(`Failed to inspect ${D}: GitHub returned ${Y.status}`);let ie=JSON.parse(J.content)?.default_branch;if(typeof ie!=="string"||ie==="")throw Error(`Failed to inspect ${D}: missing default branch`);if(!ae(ie))throw Error(`GitHub returned an invalid default branch: ${ie}`);return ie}function cv(P,O,D){lt(P);let j=M(O,P),J=r(i(D,j));if(J===null)throw Error(`Rulebook source not found: ${P}`);let Y=mu(rs(J,"Invalid local rulebook source."));if(Y.name!==P)throw Error(`rulebook name "${Y.name}" must match local source "${P}"`);return{spec:P,rulebook:Y,content:J}}async function dv(P,O){let D=N(P),j=await gu(D.owner,D.repo,D.ref,P,O),J=await yo(`https://raw.githubusercontent.com/${D.owner}/${D.repo}/${j}/${D.path}`,"raw",O),Y=J.response;if(!Y.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${Y.status}`);let re=J.content,ie=mu(rs(re,"Invalid GitHub rulebook response."));if(ie.name!==D.name)throw Error(`rulebook name "${ie.name}" must match GitHub source "${D.name}"`);return{spec:P,rulebook:ie,content:re}}function mu(P){let O=le(P),D=po(O);if(D.length>0)throw Error(D.join("; "));return O}function rs(P,O){try{return JSON.parse(P)}catch{throw Error(O)}}async function gu(P,O,D,j,J){let Y=await yo(`https://api.github.com/repos/${P}/${O}/commits/${encodeURIComponent(D)}`,"commit",J),re=Y.response;if(!re.ok)throw Error(`Failed to resolve ${j}: GitHub returned ${re.status}`);let ie=JSON.parse(Y.content);if(typeof ie?.sha!=="string"||ie.sha==="")throw Error(`Failed to resolve commit for ${j}`);return ie.sha}async function uv(P,O,D={}){if(D.signal?.aborted)throw D.signal.reason;let j=D.budget??mo(),J=new AbortController,Y=()=>J.abort(D.signal?.reason);D.signal?.addEventListener("abort",Y,{once:!0});let re=!1,ie=setTimeout(()=>{if(J.signal.aborted)return;re=!0,J.abort()},D.timeoutMs??uu.timeoutMs);try{if(D.signal?.aborted)throw D.signal.reason;au(j);let de=await fetch(P,{signal:J.signal,redirect:"error"});if(!de.ok)return yu(de),{response:de,content:""};return{response:de,content:await pv(de,O,j,()=>J.abort())}}catch(de){if(re)throw Error("GitHub request timed out",{cause:de});if(D.signal?.aborted)throw D.signal.reason;throw de}finally{clearTimeout(ie),D.signal?.removeEventListener("abort",Y)}}function yo(P,O,D){return uv(D.resolveUrl?.(P)??P,O,{budget:D.budget,signal:D.controller.signal})}async function pv(P,O,D=mo(),j){let J=uu[`${O}Bytes`],Y=Number(P.headers.get("content-length"));if(Number.isFinite(Y)&&Y>J)throw yu(P),Error(`GitHub ${O} response exceeds ${J} bytes`);if(!P.body)return"";let re=P.body.getReader(),ie=[],de=0;while(!0){let fe=await re.read();if(fe.done)break;try{lu(D,fe.value.byteLength)}catch(we){throw j?.(),du(re),we}if(de+=fe.value.byteLength,de>J)throw j?.(),du(re),Error(`GitHub ${O} response exceeds ${J} bytes`);ie.push(Buffer.from(fe.value))}return Buffer.concat(ie,de).toString("utf-8")}function yu(P){if(!P.body)return;hu(()=>P.body?.cancel())}function du(P){hu(()=>P.cancel())}function hu(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var fv=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function vu(P,O){let D=ku(P.rules,O);if(D.length>0)return{ok:!0,specs:D};return wu(P.rules,O)}function bu(P,O){let D=ku(P,O);if(D.length>0)return{ok:!0,specs:D};let j=gv(P,O);if(j.length>0)return{ok:!0,specs:j};let J=yv(P,O);if(!J.ok)return J;if(J.specs.length>0)return{ok:!0,specs:J.specs};return wu(P,O)}function wu(P,O){let D=P.filter((j)=>os(j)?.name===O);if(D.length===1)return{ok:!0,specs:D};return mv(O,D)}function mv(P,O){return{ok:!1,result:{ok:!1,errors:O.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${O.join(", ")}`],entries:[]}}}function ku(P,O){return P.filter((D)=>D===O)}function gv(P,O){let D=O.match(fv),j=D?.[1],J=D?.[2],Y=D?.[3];if(!j||!J||!Y||!ae(Y))return[];return xu(P,(re)=>re.owner===j&&re.repo===J&&re.ref===Y)}function yv(P,O){if(!Z(O))return{ok:!0,specs:[]};let[D,j]=O.split("/"),J=xu(P,(re)=>re.owner===D&&re.repo===j);if(new Set(J.map((re)=>os(re)?.ref).filter((re)=>!!re)).size<2)return{ok:!0,specs:J};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${O}. Use an explicit ref:`,` cc-safety-net rule remove ${O}#<ref>`],entries:[]}}}function os(P){try{return N(P)}catch{return null}}function xu(P,O){return P.filter((D)=>{let j=os(D);return j?O(j):!1})}async function vo(P,O={}){let D=ss(O);return vv(P,D,await ho(P,D,Mn()))}function vv(P,O,D){if(!D.ok)return D;let j=kn(P,O),J=[...new Set(B(j.configPath,j.filesystemScope))];if(J.length===0)return D;return{ok:!1,errors:J,entries:D.entries}}async function ho(P,O,D,j={},J=new Set,Y=new Set){try{let re=kn(P,O),ie=ns(re.configTarget);if(!ie.ok)return ie.result;let de=ie.config,fe=O.only?vu(de,O.only):{ok:!0,specs:de.rules};if(!fe.ok)return fe.result;let we=new Set([...O.refresh?fe.specs:[],...J]),Ce=(rn)=>pu(rn,re.configDir,re.filesystemScope,D,we.has(rn),!O.refresh||we.has(rn)),Se=await Av(de.rules,O.refresh?(rn)=>Ce(rn).then((fn)=>({ok:!0,item:fn})).catch((fn)=>{if(su(fn))throw fn;return{ok:!1,spec:rn,message:fn instanceof Error?fn.message:String(fn)}}):async(rn)=>({ok:!0,item:await Ce(rn)}),D),Ie=Se.filter((rn)=>!rn.ok),Ee=Se.filter((rn)=>rn.ok).map((rn)=>rn.item),Ve=Ee.flatMap((rn)=>bv(rn,de.rules)),Me=Ee.flatMap((rn)=>wv(rn,Y,re)),en=new Set([...Ve,...Me].map((rn)=>rn.spec)),on=[...Ie,...Ve,...Me],sn=[],an=xv(sn,()=>Ee.flatMap((rn)=>en.has(rn.spec)||on.length>0&&Y.has(rn.spec)?[]:kv(rn,re,j,sn)));return{ok:on.length===0,errors:on.map((rn)=>`Failed to update ${rn.spec}: ${rn.message}`),entries:Ee.map(Sv),changes:an}}catch(re){return dr(re)}}function bv(P,O){if(!w(P.spec))return[];let D=Ne(P.spec),j=O.filter((J)=>J!==P.spec&&Ne(J).toLowerCase()===D.toLowerCase());if(j.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${D}" is also claimed by ${j.join(", ")}; rename one of them`}]}function wv(P,O,D){if(!O.has(P.spec)||!w(P.spec))return[];let j=M(D.configDir,P.rulebook.name),J=r(i(D.filesystemScope,j));if(J===null||J===P.content)return[];return[{ok:!1,spec:P.spec,message:`${j} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function kv(P,O,D,j){if(!w(P.spec))return[];let J=M(O.configDir,P.rulebook.name),Y=i(O.filesystemScope,J),re=r(Y);if(re===P.content)return[];return j?.push({target:Y,previous:re}),g(Y,P.content,void 0,D._testAfterPolicyRename),Cv(P,re)}function xv(P,O){try{return O()}catch(D){for(let j of[...P].reverse()){if(j.previous===null){H(j.target);continue}g(j.target,j.previous)}throw D}}function Cv(P,O){if(O===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let D=xe(O),j="problem"in D?null:D.rulebook,J=new Map(j?.rules.map((re)=>[re.name,JSON.stringify(re)])??[]),Y=new Set(P.rulebook.rules.map((re)=>re.name));return[`Updated ${P.spec} (${j?.version??"unreadable"} -> ${P.rulebook.version})`,...[...Y].filter((re)=>!J.has(re)).map((re)=>` + ${re}`),...[...J.keys()].filter((re)=>!Y.has(re)).map((re)=>` - ${re}`),...P.rulebook.rules.filter((re)=>{let ie=J.get(re.name);return ie!==void 0&&ie!==JSON.stringify(re)}).map((re)=>` ~ ${re.name}`)]}function Sv(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function Cu(P,O,D={}){return Pv(P,O,Iv(D),Mn())}async function Pv(P,O,D,j,J={}){let Y=null,re=!1;try{let ie=kn(P,D),de=r(ie.configTarget);Y={target:ie.configTarget,content:de};let fe=ns(ie.configTarget);if(!fe.ok)return fe.result;let we=fe.config,Ce=Z(O);Rv(O,D,Ce);let Se=Ce?await fu(O,{ref:D.ref,operation:j}):null,Ie=Se?Ev(Se,D.rulebooks):[],Ee=Se?Ie.map((sn)=>_v(we.rules,Se,sn)??`${O}#${Se.ref}/${sn}`):[O],Ve=Ee.filter((sn)=>!we.rules.includes(sn)),Me=[...we.rules,...Ve];if(Me.length>ye)return Tv();if(Me.length!==we.rules.length)re=!0,wn(ie.configTarget,{version:1,rules:Me,overrides:we.overrides??{},transparent_wrappers:we.transparent_wrappers??[]},void 0,J._testAfterPolicyRename);let en=await ho(P,D,j,J,new Set(Ve),new Set(Ve));if(!en.ok)cr(ie.configTarget,de);if(!en.ok||!Se)return en;let on=Ie.filter((sn,an)=>Ve.includes(Ee[an]??""));return{...en,add:{source:O,ref:Se.ref,selected:Ie,added:on,alreadyConfigured:Ie.filter((sn)=>!on.includes(sn)),commits:Ve.length>0?[Se.commit]:[]}}}catch(ie){if(re&&Y)try{cr(Y.target,Y.content)}catch(de){return dr(de)}return dr(ie)}}function Rv(P,O,D){if(!D&&O.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!D&&O.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(O.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let j=O.rulebooks?.filter((J)=>!d.test(J))??[];if(j.length>0)throw Error(`Invalid rulebook names: ${j.join(", ")}`)}function Ev(P,O){let D=O?[...new Set(O)]:P.names,j=D.filter((J)=>!P.names.includes(J));if(j.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${j.join(", ")}
|
|
276
|
+
Available rulebooks: ${P.names.join(", ")}`);return D}function _v(P,O,D){let j=`${O.source}#${O.ref}/${D}`;if(P.includes(j))return j;let J=`${O.source}#${O.commit}/${D}`;return P.find((Y)=>Y===J)}async function Av(P,O,D=Mn()){if(P.length>ye)throw Error(be);let j=[],J=0,Y,re=Array.from({length:Math.min(P.length,fo.concurrency)},async()=>{while(!Y){let ie=J;if(ie>=P.length)return;J++;try{j[ie]=await O(P[ie],ie,D.controller.signal)}catch(de){if(!Y)Y={value:de},J=P.length,D.controller.abort(de);return}}});if(await Promise.all(re),Y)throw Y.value;return j}function Tv(){return{ok:!1,errors:[be],entries:[]}}function ss(P){return{cwd:P.cwd,userConfigDir:P.userConfigDir,userConfigPath:P.userConfigPath,projectConfigPath:P.projectConfigPath,global:P.global,only:P.only,refresh:P.refresh}}function Iv(P){return{...ss(P),ref:P.ref,rulebooks:P.rulebooks}}function $v(P){return{...ss(P),deleteSource:P.deleteSource}}async function Su(P,O,D={}){try{return await Ov(P,O,$v(D),{})}catch(j){return dr(j)}}async function Ov(P,O,D,j){let J=kn(P,D),Y=m(J.configTarget);if(Y.errors.length>0)return{ok:!1,errors:Y.errors,entries:[]};if(!Y.config)return{ok:!1,errors:[`No config found at ${J.configPath}`],entries:[]};let re=bu(Y.config.rules,O);if(!re.ok)return re.result;let ie=D.deleteSource?Dv(J.configDir,re.specs,J.filesystemScope):{ok:!0,dirs:[]};if(!ie.ok)return ie.result;let de=r(J.configTarget);if(de===null)return dr(Error("Rules config is unavailable."));try{wn(J.configTarget,{version:1,rules:Y.config.rules.filter((Ce)=>!re.specs.includes(Ce)),overrides:Y.config.overrides??{},transparent_wrappers:Y.config.transparent_wrappers??[]},void 0,j._testAfterPolicyRename)}catch(Ce){throw cr(J.configTarget,de),Ce}let fe=await ho(P,D,Mn(),j);if(!fe.ok)return cr(J.configTarget,de),fe;let we=Lv(ie.dirs,j,J.filesystemScope);if(!we.ok){cr(J.configTarget,de);let Ce=await ho(P,D,Mn(),j);if(!Ce.ok)return{ok:!1,errors:[...we.result.errors,...Ce.errors],entries:Ce.entries};return we.result}return fe}function Dv(P,O,D){let j=O.flatMap((ie)=>d.test(ie)?[]:["--delete-source can only delete local rulebook sources"]),J=O.map((ie)=>is(P,ie)),Y=j.length>0?[]:J.flatMap((ie)=>Pu(ie,D)),re=[...j,...Y];return re.length>0?{ok:!1,result:{ok:!1,errors:re,entries:[]}}:{ok:!0,dirs:J}}function Pu(P,O){let D=hv(P),j=i(O,D),J=me(j);if(!J)return[`Local rulebook source directory not found: ${P}`];let Y=J.find((re)=>re.name==="rulebook.json");if(!Y)return[`Local rulebook source directory is missing rulebook.json: ${P}`];if(Y.kind!=="file")throw new o(O.label);if(r(i(O,is(D,"rulebook.json"))),J.length>1)return[`Local rulebook source directory contains extra files: ${P}. delete manually if you really want to remove the directory.`];return[]}function Lv(P,O,D){let j=P.flatMap((J)=>{try{if(!me(i(D,J)))return[];let Y=Pu(J,D);if(Y.length>0)return Y;return Nv(J,O,D),[]}catch(Y){return[`Failed to delete local rulebook source ${J}: ${Y instanceof Error?Y.message:String(Y)}`]}});return j.length>0?{ok:!1,result:{ok:!1,errors:j,entries:[]}}:{ok:!0}}function Nv(P,O,D){if(O._testDeleteLocalSourceDir){O._testDeleteLocalSourceDir(P);return}H(i(D,is(P,ge))),at(i(D,P))}function cr(P,O){if(O===null){H(P);return}g(P,O)}function dr(P){return{ok:!1,errors:[P instanceof Error?P.message:String(P)],entries:[]}}var jv=".safety-net.json",Fv="~/.cc-safety-net/config.json";async function Au(P,O){let D=L(P,{cwd:O.cwd});if(D)console.log(`Skipped the project scope: ${_(O.cwd)} is the user rule config, not a project rule config`);return[D||await Eu(P,{legacyPath:ma({cwd:O.cwd}),configPath:_(O.cwd),defaultRulebookName:"project-rules",migratedFrom:jv,cleanup:O.cleanup,syncOptions:{cwd:O.cwd}}),await Eu(P,{legacyPath:Ct(P),configPath:W(P),defaultRulebookName:"user-rules",migratedFrom:Fv,cleanup:O.cleanup,syncOptions:{cwd:O.cwd,global:!0}})].every((J)=>J)?0:1}async function Eu(P,O){let D=kn(P,O.syncOptions),j=i(D.filesystemScope,O.legacyPath),J=r(j);if(J===null)return console.log(`No legacy config found at ${O.legacyPath}`),!0;let Y=Mv(J);if(!Y.ok){for(let Ie of Y.errors)console.error(Ie);return!1}let re=m(D.configTarget);if(re.errors.length>0){for(let Ie of re.errors)console.error(Ie);return!1}let ie=re.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},de=Uv(Ru(O.configPath),ie.rules,O.defaultRulebookName,O.migratedFrom,D.filesystemScope),fe=bo(Ru(O.configPath),de,"rulebook.json"),we=i(D.filesystemScope,fe),Ce=[_u(D.configTarget),_u(we)],Se=await Hv(P,O,D.configTarget,we,de,Y.config.rules,ie.rules.includes(de)?ie.rules:[...ie.rules,de],ie.overrides??{},ie.transparent_wrappers??[]);if(!Se.ok){Bv(Ce);for(let Ie of Se.errors)console.error(Ie);return!1}if(!O.cleanup)return console.log(`Migrated legacy config at ${O.legacyPath}. Legacy file is no longer used.`),!0;if(!qv(D.configTarget,we,de,O.migratedFrom,Y.config.rules))return console.error(`Migration cleanup verification failed for ${O.legacyPath}`),!1;return H(j),console.log(`Deleted legacy config at ${O.legacyPath}`),!0}async function Hv(P,O,D,j,J,Y,re,ie,de){try{return wn(D,{version:1,rules:re,overrides:ie,transparent_wrappers:de}),wn(j,Gv(J,O.migratedFrom,Y)),await vo(P,O.syncOptions)}catch(fe){return{ok:!1,errors:[fe instanceof Error?fe.message:String(fe)]}}}function Mv(P){try{let O=JSON.parse(P),D=To(O);if(D.errors.length>0)return{ok:!1,errors:D.errors};return{ok:!0,config:{version:1,rules:O.rules??[]}}}catch{return{ok:!1,errors:["Invalid JSON"]}}}function Uv(P,O,D,j,J){let Y=O.find((re)=>Vv(i(J,bo(P,re,"rulebook.json")))===j);if(Y)return Y;if(r(i(J,bo(P,D,"rulebook.json")))===null)return D;for(let re=2;;re++){let ie=`${D}-${re}`;if(r(i(J,bo(P,ie,"rulebook.json")))===null)return ie}}function Gv(P,O,D){return{rulebook_version:1,name:P,version:"1.0.0",description:"Migrated CC Safety Net rules.",author:"project",migrated_from:O,allowed_commands:[...new Set(D.map((j)=>j.command))],rules:D,tests:D.map((j)=>({command:[j.command,j.subcommand,j.block_args[0]].filter(Boolean).join(" "),expect:"blocked",rule:j.name}))}}function qv(P,O,D,j,J){if(!m(P).config?.rules.includes(D))return!1;try{let re=r(O);if(re===null)return!1;let ie=JSON.parse(re);return ie.migrated_from===j&&JSON.stringify(ie.rules)===JSON.stringify(J)}catch{return!1}}function _u(P){return{target:P,content:r(P)}}function Bv(P){for(let O of P){if(O.content===null){H(O.target);continue}g(O.target,O.content)}}function Vv(P){let O=r(P);if(O===null)return null;try{let D=JSON.parse(O);return typeof D.migrated_from==="string"?D.migrated_from:null}catch{return null}}import{mkdir as Jv,readFile as Wv,writeFile as Kv}from"node:fs/promises";import{dirname as zv,join as Yv}from"node:path";var Zv=86400000,Xv=604800000;async function Iu(P,O=Date.now()){if(P.env.get("CC_SAFETY_NET_NO_UPDATE_CHECK"))return null;let D=We(P);if(!D)return null;let j=Yv(D,".cc-safety-net","update-check.json"),J=await Qv(j,O);if(!J.lastCheck||O-J.lastCheck>Zv){let ie=await Gn();if(J.lastCheck=O,ie.latestVersion)J.latestVersion=ie.latestVersion;if(!await Tu(j,J))return null;if(ie.error)return null}let Y=J.latestVersion,re=pn();if(!Y||!No(Y,re))return null;if(J.notifiedVersion===Y&&J.notifiedAt!==void 0&&O-J.notifiedAt<Xv)return null;if(J.notifiedVersion=Y,J.notifiedAt=O,!await Tu(j,J))return null;return`UPDATE_AVAILABLE: cc-safety-net v${Y} is available (running v${re}). 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 Qv(P,O){let D=await Wv(P,"utf8").then((Y)=>JSON.parse(Y)).catch(()=>{return});if(!D||typeof D!=="object"||Array.isArray(D))return{};let j=D,J=(Y)=>typeof Y==="number"&&Number.isFinite(Y)&&Y<=O?Y:void 0;return{lastCheck:J(j.lastCheck),latestVersion:typeof j.latestVersion==="string"?j.latestVersion:void 0,notifiedVersion:typeof j.notifiedVersion==="string"?j.notifiedVersion:void 0,notifiedAt:J(j.notifiedAt)}}async function Tu(P,O){return Jv(zv(P),{recursive:!0,mode:448}).then(()=>Kv(P,JSON.stringify(O),{mode:384})).then(()=>!0).catch(()=>!1)}import{join as eb,resolve as as}from"node:path";var $u="CC Safety Net Config",nb="═".repeat($u.length),tb="https://raw.githubusercontent.com/kenryu42/cc-safety-net/main/assets/cc-safety-net.schema.json",rb=new Set(["rule.json","rule.lock","cache"]);function Ou(P,O={}){try{return ob(P,O)}catch(D){if(D instanceof o)return console.error(D.message),1;throw D}}function ob(P,O){let D=O.cwd??process.cwd(),j=X(P,{cwd:D}),J=Ct(P),Y=br(D),re=as(D,he),ie=i(j.userScope,J),de=i(j.projectScope,Y),fe=!1,we=!1,Ce=[],Se=[],Ie=ib(i(j.projectScope,re));if(ab(),r(j.userConfigTarget)!==null){let Ee=Un(j.userConfigTarget);if(Ee.errors.push(...B(j.userConfigPath,j.userScope)),Ce.push({scope:"User",path:j.userConfigPath,result:Ee,schema:"rules",target:j.userConfigTarget}),Ee.errors.length>0)fe=!0}if(r(ie)!==null)if(we=!0,r(j.userConfigTarget)!==null)Se.push(wo("user","cleanup"));else{let Ee=Io(ie);if(Ce.push({scope:"User",path:J,result:Ee,schema:"legacy",inactive:!0,target:ie}),Se.push(wo("user",Ee.errors.length>0?"fix-or-delete":"migrate")),Ee.errors.length>0)fe=!0}if(r(j.projectConfigTarget)!==null){let Ee=Un(j.projectConfigTarget);if(Ee.errors.push(...B(j.projectConfigPath,j.projectScope)),Ce.push({scope:"Project",path:as(j.projectConfigPath),result:Ee,schema:"rules",target:j.projectConfigTarget}),Ee.errors.length>0)fe=!0;if(r(de)!==null)we=!0,Se.push(wo("project","cleanup"))}else if(r(de)!==null){we=!0,fe=!0;let Ee=Io(de);Ce.push({scope:"Project",path:as(Y),result:Ee,schema:"legacy",inactive:!0,target:de}),Se.push(wo("project",Ee.errors.length>0?"fix-or-delete":"migrate"))}if(Ie?.result.errors.length)fe=!0;if(Ce.length===0&&!Ie)return console.log(`
|
|
277
|
+
No config files found. Using built-in rules only.`),0;for(let Ee of Ce)if(Ee.inactive)cb(Ee.scope,Ee.path,Ee.result);else if(Ee.result.errors.length>0)db(Ee.scope,Ee.path,Ee.result.errors);else{if(Ee.schema==="rules"&&fb(Ee.target))console.log(`
|
|
278
|
+
Added $schema to ${Ee.scope.toLowerCase()} config.`);lb(Ee.scope,Ee.path,Ee.result,Ee.schema)}for(let Ee of Se)console.error(`
|
|
279
|
+
${nn.red(Ee)}`);if(Ie)if(Ie.result.errors.length>0)pb(Ie.path,Ie.result.errors);else ub(Ie.path,Ie.result);if(fe)return console.error(`
|
|
280
280
|
Config validation failed.`),1;return console.log(we?`
|
|
281
281
|
Configs valid with warnings.`:`
|
|
282
|
-
All configs valid.`),0}function
|
|
283
|
-
✓ ${P} config: ${O}`),console.log(` Schema: ${j==="rules"?"rulebook sources":"legacy inline rules"}`),D.ruleNames.size>0){console.log(` ${j==="rules"?"Sources":"Rules"}:`);let J=1;for(let Y of D.ruleNames)console.log(` ${J}. ${Y}`),J++}else console.log(` ${j==="rules"?"Sources":"Rules"}: (none)`)}function
|
|
284
|
-
✗ Legacy ${P.toLowerCase()} config: ${O}`),console.error(" Schema: legacy inline rules"),console.error(" Status: ignored by CC Safety Net"),D.errors.length>0){console.error(" Errors:");let j=1;for(let J of D.errors)for(let Y of J.split("; "))console.error(` ${j}. ${Y}`),j++;return}if(D.ruleNames.size>0){console.error(" Rules:");let j=1;for(let J of D.ruleNames)console.error(` ${j}. ${J}`),j++;return}console.error(" Rules: (none)")}function
|
|
285
|
-
✓ GitHub source rules: ${P}`),console.log(" Rulebooks:");let D=1;for(let j of O.ruleNames)console.log(` ${D}. ${j}`),D++}function
|
|
286
|
-
✗ ${P}: ${O}`),console.error(" Errors:");let j=1;for(let J of D)for(let Y of J.split("; "))console.error(` ${j}. ${Y}`),j++}function
|
|
287
|
-
`))}import{spawn as
|
|
288
|
-
return POSIX path of (choose folder with prompt "${
|
|
282
|
+
All configs valid.`),0}function wo(P,O){let D=`legacy ${P} config`;if(O==="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(O==="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 ${D}, then run \`npx -y cc-safety-net rule migrate\`.`}function ib(P){if(me(P)===null)return null;let O=sb(P);if(O.ruleNames.size===0&&O.errors.length===0)return null;return{path:P.path,result:O}}function sb(P){let O=[],D=new Set,j=(me(P)??[]).filter((J)=>!rb.has(J.name)).sort((J,Y)=>J.name.localeCompare(Y.name));if(j.length===0)return{errors:O,ruleNames:D};for(let J of j){if(!d.test(J.name)){O.push(`rulebook directory names must match ${d}: ${J.name}`);continue}if(J.kind!=="directory"){O.push(`${J.name} must be a rulebook directory`);continue}let Y=i(P.scope,eb(P.path,J.name,"rulebook.json")),re=r(Y);if(re===null){O.push(`${J.name}/rulebook.json is required`);continue}try{let ie;try{ie=JSON.parse(re)}catch{O.push(`${J.name}/rulebook.json: invalid JSON`);continue}let de=le(ie);if(de.name!==J.name){O.push(`rulebook name "${de.name}" must match folder "${J.name}"`);continue}let fe=po(de);if(fe.length>0){O.push(...fe.map((we)=>`${J.name}/rulebook.json: ${we}`));continue}D.add(J.name)}catch(ie){O.push(ie instanceof Error?`${J.name}/rulebook.json: ${ie.message}`:`${J.name}/rulebook.json: ${String(ie)}`)}}return{errors:O,ruleNames:D}}function ab(){console.log($u),console.log(nb)}function lb(P,O,D,j){if(console.log(`
|
|
283
|
+
✓ ${P} config: ${O}`),console.log(` Schema: ${j==="rules"?"rulebook sources":"legacy inline rules"}`),D.ruleNames.size>0){console.log(` ${j==="rules"?"Sources":"Rules"}:`);let J=1;for(let Y of D.ruleNames)console.log(` ${J}. ${Y}`),J++}else console.log(` ${j==="rules"?"Sources":"Rules"}: (none)`)}function cb(P,O,D){if(console.error(`
|
|
284
|
+
✗ Legacy ${P.toLowerCase()} config: ${O}`),console.error(" Schema: legacy inline rules"),console.error(" Status: ignored by CC Safety Net"),D.errors.length>0){console.error(" Errors:");let j=1;for(let J of D.errors)for(let Y of J.split("; "))console.error(` ${j}. ${Y}`),j++;return}if(D.ruleNames.size>0){console.error(" Rules:");let j=1;for(let J of D.ruleNames)console.error(` ${j}. ${J}`),j++;return}console.error(" Rules: (none)")}function db(P,O,D){Du(`${P} config`,O,D)}function ub(P,O){console.log(`
|
|
285
|
+
✓ GitHub source rules: ${P}`),console.log(" Rulebooks:");let D=1;for(let j of O.ruleNames)console.log(` ${D}. ${j}`),D++}function pb(P,O){Du("GitHub source rules",P,O)}function Du(P,O,D){console.error(`
|
|
286
|
+
✗ ${P}: ${O}`),console.error(" Errors:");let j=1;for(let J of D)for(let Y of J.split("; "))console.error(` ${j}. ${Y}`),j++}function fb(P){try{let O=r(P);if(O===null)return!1;let D=JSON.parse(O);if(D.$schema)return!1;return g(P,JSON.stringify({$schema:tb,...D},null,2)),!0}catch(O){if(O instanceof o)throw O;return!1}}var Lu=new Set(["init","add","remove","update","sync","list","wrapper","migrate","doc","verify"]),gb=new Set(["init","add","remove","update","sync","wrapper"]),yb=new Set(["add","remove","list"]),hb="cc-safety-net/rulebooks";async function Nu(P,O){try{return await vb(P,O)}catch(D){if(D instanceof o)return console.error(D.message),1;throw D}}async function vb(P,O){let D=wb(O),j=D.help?bb(D.positionals):null;if(j)return Lt(j),0;if(D.errors.length>0){for(let fe of D.errors)console.error(fe);return 1}let J=D.positionals[0];if(!J)return Lt(xt,console.error),1;let Y=D.positionals[1],re={global:D.global},ie=process.cwd();if(!D.global&&gb.has(J)&&!(J==="wrapper"&&Y==="list")&&L(P,{cwd:ie}))return console.error(`${_(ie)} is the user rule config, not a project rule config; use --global for the user scope, or run from a project directory`),1;if(J==="init"){let fe=kn(P,re);Sb(fe.configTarget);let we=mb(fe.configDir,"example-rules","rulebook.json"),Ce=i(fe.filesystemScope,we);if(D.example&&r(Ce)===null)iu(Ce);let Se=B(fe.configPath,fe.filesystemScope);for(let Ie of Se)console.error(Ie);if(Se.length>0)return 1;return console.log("Rule config initialized."),0}if(J==="add"){let fe=ju(D);if(!fe)return console.error("rule add requires a source (pass --only <rulebook...> to select from cc-safety-net/rulebooks)"),1;let we=kn(P,re),Ce=await Cu(P,fe,{...re,ref:D.ref,rulebooks:D.only.length>0?D.only:void 0});return eu(Ce,fe,`Scope: ${D.global?"user":"project"} (${we.configDir})`),Ce.ok?0:1}if(J==="remove"){if(!Y)return console.error("rule remove requires a source"),1;let fe=await Su(P,Y,{...re,deleteSource:D.deleteSource});return uo(fe,`Removed rulebook source: ${Y}`),fe.ok?0:1}if(J==="update"){let fe=await vo(P,{...re,only:Y,refresh:!0});return uo(fe,"Rule config updated."),fe.ok?0:1}if(J==="sync")return ha(P,{global:D.global});if(J==="list"){let fe=Q(P,{cwd:ie});return tu(fe),fe.errors.length>0?1:0}if(J==="wrapper")return Pb(P,D);if(J==="migrate")return Au(P,{cleanup:D.cleanup,cwd:ie});if(J==="doc"){console.log(Xd);let fe=await Iu(P);if(fe)console.error(fe);return 0}if(J==="verify")return Ou(P);return 1}function bb(P){if(P.length===0)return xt;let O=xt.subcommands.filter((j)=>j.usage.split(" ")[0]===P[0]);if(O.length===0)return null;if(P.length===1&&O.length>1)return{name:`rule ${P[0]}`,description:`Subcommands of rule ${P[0]}`,usage:`rule ${P[0]} <subcommand>`,subcommands:O,options:[]};let D=P.length===1?O[0]:O.find((j)=>j.usage.split(" ")[1]===P[1]);if(!D)return null;return{name:`rule ${P[0]}`,description:D.description,usage:`rule ${D.usage}`,options:P[0]==="add"?Eo:[],examples:P[0]==="add"?_o:void 0}}function wb(P){let O=gn({label:"rule",booleans:{global:["-g","--global"],cleanup:["--cleanup"],deleteSource:["--delete-source"],example:["--example"]},values:{ref:["--ref"]},lists:{only:["--only"]},positionals:"list"},P),D={...O.flags,ref:O.values.ref,only:O.lists.only??[],help:O.help,positionals:O.positionals,errors:O.errors};return kb(D),D}function kb(P){let[O]=P.positionals;if(O&&!Lu.has(O))P.errors.push(`Unknown rule subcommand: ${O}`);if(P.deleteSource&&O!=="remove")if(O&&Lu.has(O))P.errors.push(`Unknown option for rule ${O}: --delete-source`);else P.errors.push("--delete-source is only valid with 'rule remove'");if(P.cleanup&&O!=="migrate")P.errors.push(ur(O,"--cleanup"));if(P.example&&O!=="init")P.errors.push(ur(O,"--example"));if(P.ref&&O!=="add")P.errors.push(ur(O,"--ref"));if(P.only.length>0&&O!=="add")P.errors.push(ur(O,"--only"));if(O==="add")xb(P);if(O==="migrate"){if(P.global)P.errors.push(ur(O,"--global"));if(P.positionals.length>1)P.errors.push(`Unexpected rule migrate argument: ${P.positionals[1]}`)}else if(O==="wrapper")Cb(P);else if(P.positionals.length>2)P.errors.push(`Unexpected rule argument: ${P.positionals[2]}`);if(O==="list"&&P.global)P.errors.push("Unknown option for rule list: --global")}function ju(P){if(P.positionals[1])return P.positionals[1];if(P.ref||P.only.length>0)return hb;return}function xb(P){let O=ju(P);if(!O)return;if((P.ref||P.only.length>0)&&!Z(O)){if(P.ref)P.errors.push(`--ref can only select a ref for an owner/repo source: ${O}`);if(P.only.length>0)P.errors.push("--only can only select rulebooks from an owner/repo source");return}if(P.ref&&!ae(P.ref))P.errors.push(`--ref must use valid path segments: ${P.ref}`);let D=P.only.filter((j)=>!d.test(j));if(D.length>0)P.errors.push(`Invalid rulebook names: ${D.join(", ")}`)}function ur(P,O){return P?`Unknown option for rule ${P}: ${O}`:`Unknown option for rule: ${O}`}function Cb(P){let O=P.positionals[1],D=P.positionals[2];if(!O){P.errors.push("rule wrapper requires add, remove, or list");return}if(!yb.has(O)){P.errors.push(`Unknown rule wrapper action: ${O}`);return}if(O==="list"){if(D)P.errors.push(`Unexpected rule wrapper argument: ${D}`);return}if(!D){P.errors.push(`rule wrapper ${O} requires a command`);return}if(P.positionals.length>3)P.errors.push(`Unexpected rule wrapper argument: ${P.positionals[3]}`)}function Sb(P){if(r(P)===null){ou(P);return}let O=m(P);if(!O.config)return;wn(P,{version:1,rules:O.config.rules,overrides:O.config.overrides??{},transparent_wrappers:O.config.transparent_wrappers??[]})}async function Pb(P,O){let D=O.positionals[1],j=O.positionals[2],J=kn(P,{global:O.global}).configTarget;if(D==="list"){let de=m(J);if(de.errors.length>0){for(let fe of de.errors)console.error(fe);return 1}return Rb(de.config?.transparent_wrappers??[]),0}if(!j||!C.test(j))return console.error("transparent wrapper must match command pattern"),1;if(Ae(j))return console.error(`reserved command "${j}" cannot be a wrapper`),1;let Y=m(J);if(Y.errors.length>0){for(let de of Y.errors)console.error(de);return 1}let re=Y.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},ie=D==="add"?[...new Set([...re.transparent_wrappers??[],j])]:(re.transparent_wrappers??[]).filter((de)=>de!==j);return wn(J,{version:1,rules:re.rules,overrides:re.overrides??{},transparent_wrappers:ie}),console.log(D==="add"?`Added transparent wrapper: ${j}`:`Removed transparent wrapper: ${j}`),0}function Rb(P){if(P.length===0){console.log("Transparent wrappers: (none)");return}console.log(`Transparent wrappers (${P.length}):`);for(let O of P)console.log(` - ${O}`)}import{sep as $b}from"node:path";import{existsSync as Eb,readFileSync as _b}from"node:fs";import{join as Ab}from"node:path";async function Tb(P){if(P.isTTY)return null;return(await Ke(P).catch(()=>null))?.trim()||null}function Ib(P){let O=P.env.get("CLAUDE_SETTINGS_PATH");if(O)return O;return Ab(Tr(P),"settings.json")}function ls(P){let O=Ib(P);if(!Eb(O))return!1;try{let D=_b(O,"utf-8"),j=JSON.parse(D);if(!j.enabledPlugins)return!1;let J="cc-safety-net@cc-marketplace";if(!(J in j.enabledPlugins))return!1;return j.enabledPlugins[J]===!0}catch(D){if(v(n.debug,P.env))console.error(`CC Safety Net debug: failed to read Claude settings: ${O}: ${D instanceof Error?D.message:String(D)}`);return!1}}async function cs(P,O=process.stdin){let D=ls(P),j;if(!D)j="\uD83D\uDEE1️ CC Safety Net ❌";else{let Y=k(P,{cwd:process.cwd()}),re=Y.policy,ie=T(re,P.env),de=Object.values(q(re,ie.capabilities)).some((Ce)=>Ce.changesInherited),fe={standard:"✅",strict:"\uD83D\uDD12",paranoid:"\uD83D\uDC41️",custom:"\uD83D\uDD27"}[de?"custom":ie.effectiveLevel],we=Y.policyScopes&&!Y.policyScopes.weakeningsIgnored&&Y.policyScopes.weakenings.length>0?"\uD83D\uDD3B":"";j=`\uD83D\uDEE1️ CC Safety Net ${fe}${ie.worktreeMode?"\uD83C\uDF33":""}${we}${Y.state==="degraded"?"⚠️":""}`}let J=await Tb(O);if(J&&!J.startsWith("{"))console.log(`${J} | ${j}`);else console.log(j)}function Fu(P){let O=k(P,{cwd:process.cwd()}),D=O.policy,j=T(D,P.env),J=!!process.env.NO_COLOR||!process.stdout.isTTY,Y=Math.min(process.stdout.columns||80,100),re=J?"ok":"✔",ie=J?"OFF":"✘",de=(Ve,Me)=>{let en=` ${Ve.padEnd(13)}${Me}`;return(en.length>Y?`${en.slice(0,Y-1)}…`:en).replaceAll(ie,nn.red(ie))},fe=Object.values(q(D,j.capabilities)).some((Ve)=>Ve.changesInherited),we=(Ve)=>Ve===P.home||Ve.startsWith(`${P.home}${$b}`)?`~${Ve.slice(P.home.length)}`:Ve,Ce={ready:nn.green,degraded:nn.yellow}[O.state],Se=O.policyScopes?.weakenings??[],Ie=[...ls(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."],...O.diagnostics],Ee=J?"-":"·";console.log([`${J?"":"\uD83D\uDEE1️ "}CC Safety Net — ${Ce(O.state)}`,"",de("Protection",`destructive ${D.destructiveCommandProtectionEnabled?re:ie} secrets ${D.secretProtection.enabled?re:ie}`),de("Level",fe?`${j.effectiveLevel} (customised)`:j.effectiveLevel),de("Rules",D.rules.length===0?"none active":`${D.rules.length} active`),de("Policy",we(l(P))),...O.policyScopes?[de("Project",we(h(process.cwd())))]:[],...j.worktreeMode?[de("Worktree","relaxations active")]:[],"",...Se.length===0?[]:[O.policyScopes?.weakeningsIgnored?" Project policy (ignored)":" Project policy",...Se.flatMap((Ve)=>Qt(Ve," ",Y-6).map((Me,en)=>en===0?` ${Me}`:Me)),""],...Ie.length===0?[" Everything configured is active."]:[" Not active",...Ie.flatMap((Ve)=>Qt(Ve," ",Y-6).map((Me,en)=>en===0?` ${Ee} ${Me}`:Me)),""," Full report: cc-safety-net doctor"]].join(`
|
|
287
|
+
`))}import{spawn as zu}from"node:child_process";import{randomBytes as Gb}from"node:crypto";import{existsSync as qb}from"node:fs";import{createServer as Bb}from"node:http";import{Writable as Vb}from"node:stream";var ko=500;function Ob(P){let O=P.filter((J)=>J.decision!=="allow"),D=P.filter((J)=>J.decision==="allow"),j=Math.min(O.length,Math.max(ko-D.length,Math.ceil(ko/2)));return[...O.slice(0,j),...D.slice(0,ko-j)]}function Hu(P,O,D=U(P)){if(D)K(P,D);let j=(Me)=>new Date(Me.getFullYear(),Me.getMonth(),Me.getDate()).getTime(),J=j(new Date),Y=new Date(J);Y.setDate(Y.getDate()-(O-1));let re=Y.getTime(),ie=[],de={count:0};for(let Me of D?Wn(D,de):[])for(let en of kt(Me,de)){let on=new Date(en.ts).getTime();if(!Number.isFinite(on))continue;if(on>=re)ie.push(en)}ie.sort((Me,en)=>new Date(en.ts).getTime()-new Date(Me.ts).getTime());let fe=Array.from({length:O},()=>0),we=Array.from({length:O},()=>0),Ce={},Se={},Ie={},Ee=0,Ve=0;for(let Me of ie){let en=Me.agent||"unknown";Ce[en]=(Ce[en]??0)+1;let on=Math.round((J-j(new Date(Me.ts)))/86400000),sn=O-1-on,an=on>=0&&on<O;if(an)we[sn]=(we[sn]??0)+1;if(Me.decision!=="allow"){if(Ee++,Me.ruleId)Se[Me.ruleId]=(Se[Me.ruleId]??0)+1;let rn=Po(Me.segment||Me.command);if(rn)Ie[rn]=(Ie[rn]??0)+1;if(Me.failureStage)Ve++;if(an)fe[sn]=(fe[sn]??0)+1}}return{days:O,logsDir:D,homeDir:P.home,totalInWindow:ie.length,truncated:ie.length>ko,unreadable:de.count,counts:{blocked:Ee,allowed:ie.length-Ee,agents:Ce,blockedByDay:fe,analyzedByDay:we,rules:Se,commands:Ie,errors:Ve},entries:Ob(ie).sort((Me,en)=>new Date(en.ts).getTime()-new Date(Me.ts).getTime())}}import{spawn as Db}from"node:child_process";import{existsSync as Lb,statSync as Mu}from"node:fs";import{delimiter as Nb,join as jb}from"node:path";var Fb=120000,xo="Choose the project folder",Hb=`try
|
|
288
|
+
return POSIX path of (choose folder with prompt "${xo}")
|
|
289
289
|
on error number -128
|
|
290
290
|
return ""
|
|
291
|
-
end try`,
|
|
291
|
+
end try`,Mb=`Add-Type -AssemblyName System.Windows.Forms
|
|
292
292
|
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
293
|
-
$dialog.Description = '${
|
|
294
|
-
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,
|
|
293
|
+
$dialog.Description = '${xo}'
|
|
294
|
+
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,Uu=[{binary:"zenity",args:["--file-selection","--directory",`--title=${xo}`]},{binary:"kdialog",args:["--getexistingdirectory",".","--title",xo]}],Gu=(P,O)=>(O.PATH??"").split(Nb).some((D)=>{if(D.length===0)return!1;try{let j=Mu(jb(D,P));return j.isFile()&&(j.mode&73)!==0}catch{return!1}});function ds(P,O){if(P==="darwin"||P==="win32")return!0;if(P!=="linux")return!1;if(!O.DISPLAY&&!O.WAYLAND_DISPLAY)return!1;return Uu.some((D)=>Gu(D.binary,O))}function Ub(P,O){if(P==="darwin")return{cmd:"osascript",args:["-e",Hb]};if(P==="win32")return{cmd:"powershell.exe",args:["-NoProfile","-STA","-Command",Mb]};let D=Uu.find((j)=>Gu(j.binary,O));return D?{cmd:D.binary,args:D.args}:null}function us(P=process.platform,O=process.env){let D=Ub(P,O);if(!D)return Promise.resolve({error:"No folder dialog is available on this system"});return new Promise((j)=>{let J=Db(D.cmd,D.args,{env:O,stdio:["ignore","pipe","pipe"]}),Y="",re=!1,ie=(fe)=>{if(re)return;re=!0,clearTimeout(de),j(fe)},de=setTimeout(()=>{J.kill(),ie({error:"The folder dialog timed out"})},Fb);J.stdout.on("data",(fe)=>{Y+=fe.toString()}),J.on("error",()=>ie({error:`Could not open the folder dialog (${D.cmd})`})),J.on("close",()=>{let fe=Y.trim().replace(/\/+$/,"");if(!fe)return ie({cancelled:!0});if(!Lb(fe)||!Mu(fe).isDirectory())return ie({error:"That selection is not a folder on disk"});ie({path:fe})})})}var qu=`<!doctype html>
|
|
295
295
|
<html lang="en">
|
|
296
296
|
<head>
|
|
297
297
|
<meta charset="utf-8">
|
|
@@ -5766,6 +5766,6 @@ loadPolicy().then((loaded) => {
|
|
|
5766
5766
|
</script>
|
|
5767
5767
|
</body>
|
|
5768
5768
|
</html>
|
|
5769
|
-
`;var
|
|
5770
|
-
`),{path:j,errors:[]}}catch(Y){return{path:j,errors:[Y instanceof Error?Y.message:String(Y)]}}}function
|
|
5771
|
-
`)}}finally{console.log=re,console.error=ie}},J=
|
|
5769
|
+
`;var Bu='<script id="ccsn-data" type="application/json">';function Vu(P,O=qu){return O.replace(Bu,()=>Bu+JSON.stringify({token:P}).replaceAll("<","\\u003c"))}var Co="kenryu42/cc-safety-net",Jb=`https://github.com/${Co}`,ms=1e4,Wb=7,Kb="The project draft directory changed; reload the draft before applying.",zb="audit settings are user scope only; remove the audit section from a project proposal";async function Yu(P,O={}){let D=gn({label:"gui",booleans:{noOpen:["--no-open"]}},P),j=O.log??console.log,J=O.error??console.error;if(D.errors.length>0){for(let re of D.errors)J(re);return J("Usage: cc-safety-net gui [--no-open]"),1}let Y=await Yb(c,O);if(j(`CC Safety Net policy GUI: ${Y.url}`),!D.flags.noOpen)try{await(O.openBrowser??aw)(Y.url)}catch(re){J(`Failed to open browser: ${re instanceof Error?re.message:String(re)}`),J(`Open this URL manually: ${Y.url}`)}if(O.keepAlive===!1)return await Y.close(),0;return await sw(Y),0}async function Yb(P,O={}){let D=Gb(24).toString("base64url"),j={dir:null,revision:0},J=Bb((ie,de)=>{Zb(P,ie,de,D,O,j)});await new Promise((ie,de)=>{J.once("error",de),J.listen(0,"127.0.0.1",()=>{J.off("error",de),ie()})});let re=`http://127.0.0.1:${J.address().port}`;return{origin:re,token:D,url:`${re}/?token=${encodeURIComponent(D)}`,close:()=>iw(J)}}async function Zb(P,O,D,j,J,Y){let re=P(),ie=new URL(O.url??"/","http://127.0.0.1");if(O.method==="GET"&&ie.pathname==="/favicon.ico"){D.writeHead(204,{"cache-control":"no-store"}),D.end();return}if(!tw(O,ie,j)){ln(D,403,{error:"Forbidden"});return}if(O.method==="GET"&&ie.pathname==="/"){await Promise.resolve(J.buildDocument?.()).then((de)=>ow(D,Vu(j,de)),(de)=>ln(D,500,{error:de instanceof Error?de.message:String(de)}));return}if(O.method==="GET"&&ie.pathname==="/api/policy"){let de=Vd(re,J),fe=k(re,ps(J));ln(D,200,{...de,configState:je(fe),...fe.policyScopes?{projectPolicy:{path:h(J.cwd??process.cwd()),weakenings:fe.policyScopes.weakeningsIgnored?[]:fe.policyScopes.weakenings}}:{},destructiveCommandRules:z,secretPatterns:Qe,version:pn(),preview:de.errors.length>0?null:Le(de.policy,re.env)});return}if(O.method==="POST"&&ie.pathname==="/api/policy/preview"){let de=await pr(O);if(!de.ok){ln(D,de.status,{errors:[de.error]});return}let fe=Jd(re,de.value);ln(D,fe.errors.length>0?400:200,fe);return}if(O.method==="POST"&&ie.pathname==="/api/policy/explain"){let de=await pr(O);if(!de.ok){ln(D,de.status,{errors:[de.error]});return}let fe=de.value;if(fe===null||typeof fe.command!=="string"){ln(D,400,{errors:["command must be a string"]});return}let we=ar(fe.policy,re.home);if(we.length>0){ln(D,400,{errors:we});return}ln(D,200,ew(re,fe.command,fe.policy,J));return}if(O.method==="POST"&&ie.pathname==="/api/policy"){let de=await pr(O);if(!de.ok){ln(D,de.status,{errors:[de.error]});return}let fe=Hn(re,de.value,J);ln(D,fe.errors.length>0?400:200,fe);return}if(O.method==="POST"&&ie.pathname==="/api/reset"){ln(D,200,Hn(re,te,J));return}if(O.method==="POST"&&ie.pathname==="/api/repair"){ln(D,200,Wd(re,J));return}if(O.method==="POST"&&ie.pathname==="/api/policy/project/choose-directory"){let de=await(J.chooseDirectory??us)();if("path"in de)Y.dir=de.path,Y.revision+=1;ln(D,200,{cancelled:"cancelled"in de,..."error"in de?{error:de.error}:{}});return}if(O.method==="GET"&&ie.pathname==="/api/policy/project"){let de=Zu(Y,J),fe=Xu(re,de,J),we=fe?{projection:{},diagnostics:[fe]}:Ju(de,re.home),Ce=lr(re,J);ln(D,200,{path:h(de),revision:Y.revision,baseline:Ce.baseline,userPolicyDiagnostics:Ce.diagnostics,projection:we.projection,projectionDiagnostics:we.diagnostics,canPickDirectory:ds(process.platform,process.env)});return}if(O.method==="POST"&&ie.pathname==="/api/policy/project/diff"){let de=await Wu(re,O,D,Y,J);if(!de)return;let fe=Ju(de.dir,re.home),we=lr(re,J).baseline,Ce=ee(we,ue(de.proposal,re.home).policy);ln(D,200,{rows:lo(ee(we,fe.projection).policy,Ce.policy,!1),weakenings:Ce.weakenings,existingFileDiagnostics:fe.diagnostics});return}if(O.method==="POST"&&ie.pathname==="/api/policy/project/apply"){let de=await Wu(re,O,D,Y,J);if(!de)return;let fe=Qb(de.dir,de.proposal,re.home);ln(D,fe.errors.length>0?500:200,fe);return}if(O.method==="GET"&&ie.pathname==="/api/activity"){let de=oe(re,J),fe=nw(ie.searchParams.get("days"),de);if(fe===null){ln(D,400,{error:`days must be an integer between 1 and ${de}`});return}ln(D,200,Hu(re,fe,J.activityLogsDir));return}if(O.method==="POST"&&ie.pathname==="/api/rules/choose-directory"){ln(D,200,await us());return}if(O.method==="GET"&&ie.pathname==="/api/rules"){let de=Q(re,ps(J)),fe=new Map(de.rules.map((we)=>[we.name,we]));ln(D,200,{projectPath:J.cwd??process.cwd(),canPickDirectory:ds(process.platform,process.env),rulebooks:de.rulebooks.map((we)=>({source:we.source,spec:we.spec,name:we.name,version:we.version,rules:we.rules.flatMap((Ce)=>{let Se=fe.get(Ce);if(!Se)return[];return[{name:Se.name,command:Se.command,subcommand:Se.subcommand,block_args:Se.block_args,reason:Se.reason}]})})),errors:de.errors,warnings:de.warnings});return}if(O.method==="GET"&&ie.pathname==="/api/star/context"){ln(D,200,await(J.fetchStarContext??(()=>fw(re,{logsDir:J.activityLogsDir})))());return}if(O.method==="POST"&&ie.pathname==="/api/star"){let de=await(J.starRepo??lw)();ln(D,200,de.ok?{ok:!0}:{ok:!1,fallbackUrl:Jb});return}if(O.method==="GET"&&ie.pathname==="/api/integrations"){ln(D,200,await(J.fetchIntegrations??(()=>cw(re)))());return}if(O.method==="GET"&&ie.pathname==="/api/health"){ln(D,200,await(J.fetchHealth??uw)());return}if(O.method==="POST"&&(ie.pathname==="/api/install"||ie.pathname==="/api/uninstall")){let de=await pr(O);if(!de.ok){ln(D,de.status,{errors:[de.error]});return}let fe=de.value?.target;if(typeof fe!=="string"||!$n.some((Ce)=>Ce.target===fe)){ln(D,400,{error:"unknown target"});return}let we=ie.pathname==="/api/install"?"install":"uninstall";ln(D,200,await(J.runIntegration??pw)(we,fe));return}ln(D,404,{error:"Not found"})}function ps(P){return{...P,cwd:P.cwd??process.cwd()}}function Zu(P,O){return P.dir??O.cwd??process.cwd()}function Xu(P,O,D){if(!L(P,{...D,cwd:O}))return null;return`${h(O)} is the user policy, not a project policy; choose a project directory, or run the GUI from one`}function Ju(P,O){let D=h(P),j=qb(D)?bt(D):{value:void 0,errors:[]},J=ue(j.value,O);return{projection:J.policy,diagnostics:[...j.errors,...J.diagnostics]}}async function Wu(P,O,D,j,J){let Y=Zu(j,J),re=j.revision,ie=await pr(O);if(!ie.ok)return ln(D,ie.status,{errors:[ie.error]}),null;let de=ie.value;if(typeof de?.revision!=="number")return ln(D,400,{errors:["revision must be a number"]}),null;if(de.revision!==re)return ln(D,409,{errors:[Kb]}),null;let fe=Xu(P,Y,J);if(fe)return ln(D,400,{errors:[fe]}),null;let we=Xb(de.proposal,P.home);if(we.length>0)return ln(D,400,{errors:we}),null;return{dir:Y,proposal:de.proposal}}function Xb(P,O){let D=ar(P,O);if(D.length>0)return D;return P?.audit===void 0?[]:[zb]}function Qb(P,O,D){let j=h(P),J=co(O,E(O,D));try{return g(i(S(P,"project policy"),j),`${JSON.stringify(J,null,2)}
|
|
5770
|
+
`),{path:j,errors:[]}}catch(Y){return{path:j,errors:[Y instanceof Error?Y.message:String(Y)]}}}function ew(P,O,D,j){let J=E(D,P.home),Y=k(P,ps(j)),re=_e({rules:Y.policy.rules,transparentWrappers:Y.policy.transparentWrappers,safety:$e(J.safety),worktreeMode:J.workflow.worktree_mode,destructiveCommandProtectionEnabled:J.destructive_command_protection.enabled,destructiveCommandRuleOverrides:J.destructive_command_protection.overrides,destructiveCommandAllowPaths:J.destructive_command_protection.allow_paths,secretProtection:{enabled:J.secret_protection.enabled,disabledRules:Fe(J.secret_protection.overrides),denyPaths:J.secret_protection.deny_paths,allowPaths:J.secret_protection.allow_paths}});return er(O,{policySnapshot:re,cwd:j.cwd,userConfigDir:j.userConfigDir},P)}function nw(P,O){if(P===null)return Math.min(Wb,O);let D=Number(P);if(!Number.isInteger(D)||D<1||D>O)return null;return D}function tw(P,O,D){if(O.searchParams.get("token")!==D)return!1;if(P.method!=="POST")return!0;return P.headers["x-cc-safety-net-token"]===D}var rw=1048576;async function pr(P){let O=[],D=0;for await(let j of P){let J=j;if(D+=J.byteLength,D>rw)return{ok:!1,status:413,error:"Request body is too large"};O.push(J)}try{return{ok:!0,value:JSON.parse(Buffer.concat(O).toString("utf-8")||"{}")}}catch(j){return{ok:!1,status:400,error:`Invalid JSON: ${j instanceof Error?j.message:String(j)}`}}}function ow(P,O){P.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),P.end(O)}function ln(P,O,D){P.writeHead(O,{"content-type":"application/json; charset=utf-8","cache-control":"no-store"}),P.end(JSON.stringify(D))}function iw(P){return new Promise((O,D)=>{P.close((j)=>j?D(j):O())})}function sw(P){return new Promise((O)=>{let D=()=>{process.off("SIGINT",j),process.off("SIGTERM",j)},j=()=>{D(),P.close().then(O)};process.once("SIGINT",j),process.once("SIGTERM",j)})}function aw(P){let O=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",D=process.platform==="win32"?["/c","start","",P]:[P];return new Promise((j,J)=>{let Y=zu(O,D,{detached:!0,stdio:"ignore"}),re=(de)=>{Y.off("spawn",ie),J(de)},ie=()=>{Y.off("error",re),Y.unref(),j()};Y.once("error",re),Y.once("spawn",ie)})}async function lw(P="gh",O=ms){return{ok:await fs(P,["api","-X","PUT",`/user/starred/${Co}`],O)===0}}async function cw(P,O={}){let D=await Cr((J)=>Ot({environment:P,cwd:process.cwd(),openCodeVersion:J}).status!=="n/a",O.fetcher),j=dw(P,D);return{targets:En.map((J)=>{let Y=j.find((re)=>re.platform===J.id);return{target:J.id,label:mn(J.id),version:D.versions[J.id]??null,status:Y?.configured?"active":Y?.detected?"disabled":Y?.inspectionStatus==="not-inspected"?"not-inspected":"not-installed"}}),system:{version:D.version,nodeVersion:D.nodeVersion,platform:D.platform}}}function dw(P,O){return Dt(P,process.cwd(),{ampPluginListOutput:O.ampPluginListOutput,codexPluginListOutput:O.codexPluginListOutput,copilotCliVersion:O.versions["copilot-cli"],openCodeVersion:O.versions.opencode,openCodePluginListOutput:O.openCodePluginListOutput})}async function uw(P={}){let O=await(P.checkUpdates??Gn)();return{update:{latestVersion:O.latestVersion??null,updateAvailable:O.updateAvailable}}}var Ku=Promise.resolve();function pw(P,O,D={}){let j=async()=>{let Y=[],{log:re,error:ie}=console;console.log=(...de)=>Y.push(de.map(String).join(" ")),console.error=console.log;try{return{ok:await sr(P,[],{selectTargets:async()=>[O],output:new Vb({write(fe,we,Ce){Y.push(String(fe).replace(/\n$/,"")),Ce()}}),...D})===0,output:Y.join(`
|
|
5771
|
+
`)}}finally{console.log=re,console.error=ie}},J=Ku.then(j);return Ku=J.then(()=>{return},()=>{return}),J}async function fw(P,O={}){let[D,j,J]=await Promise.all([mw(O.command),gw(O.fetchRepo),Promise.resolve(vr(P,oe(P),O.logsDir).totalBlocked)]);return{starred:D,starCount:j,blockedTotal:J}}async function mw(P="gh",O=ms){if(await fs(P,["auth","status"],O)!==0)return null;let D=await fs(P,["api",`/user/starred/${Co}`],O);if(D===0)return!0;if(D===null)return null;return!1}function fs(P,O,D){return new Promise((j)=>{let J=zu(P,O,{stdio:"ignore",windowsHide:!0}),Y=!1,re=setTimeout(()=>{J.kill(),ie(null)},D),ie=(de)=>{if(Y)return;Y=!0,clearTimeout(re),j(de)};J.once("error",()=>ie(null)),J.once("close",ie)})}async function gw(P=fetch){try{let O=await P(`https://api.github.com/repos/${Co}`,{headers:{accept:"application/vnd.github+json"},signal:AbortSignal.timeout(ms)});if(!O.ok)return null;let D=await O.json();return typeof D.stargazers_count==="number"?D.stargazers_count:null}catch{return null}}function yw(P){if(P[0]!=="help")return!1;let O=P[1];if(!O)ji(),process.exit(0);if(nr(O))process.exit(0);console.error(`Unknown command: ${O}`),console.error("Run 'cc-safety-net --help' for available commands."),process.exit(1)}var hw={hook:async()=>{console.error("hook requires exactly one integration flag. Try: cc-safety-net hook --kimi-code"),nr("hook",console.error),process.exit(1)},install:async(P)=>{process.exit(await sr("install",P))},update:async(P)=>{process.exit(await Xi(P))},uninstall:async(P)=>{process.exit(await sr("uninstall",P))},rule:async(P)=>{process.exit(await Nu(c(),P))},policy:async(P)=>{process.exit(await Zd(c(),P))},status:async(P)=>{if(Dn(gn({label:"status"},P).errors))process.exit(1);Fu(c())},statusline:async(P)=>{let O=gn({label:"statusline",booleans:{claudeCode:["-cc","--claude-code"]}},P);if(O.errors.length===0&&O.flags.claudeCode){await cs(c());return}if(Dn(O.errors),!O.flags.claudeCode)console.error("statusline requires --claude-code (-cc)");nr("statusline",console.error),process.exit(1)},doctor:async(P)=>{let O=Ai(P);if(!O)process.exit(1);let D=await fc(c(),{json:O.json,skipUpdateCheck:O.skipUpdateCheck});process.exit(D)},logs:async(P)=>{process.exit(await xs(c(),P))},gui:async(P)=>{process.exit(await Yu(P))},explain:async(P)=>{process.exit(await Sc(c(),P))}};async function vw(P){let O=gn({label:"cc-safety-net",booleans:{version:["-V","--version"]},positionals:"list"},P);if(yw(P))return;let D=P[0],j=D?hr(D):void 0;if(O.help&&j&&j.name!=="rule")nr(j.name),process.exit(0);if(!D||O.help&&!j)ji(),process.exit(0);if(O.flags.version)Ec(),process.exit(0);if(j){await hw[j.name](P.slice(1));return}if(D==="--statusline"){await cs(c());return}console.error(D.startsWith("-")?`Unknown option: ${D}`:`Unknown command: ${D}`),console.error("Run 'cc-safety-net --help' for usage."),process.exit(1)}export{vw as runCli};
|