cc-safety-net 2.6.8 → 2.6.10
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 +44 -43
- package/dist/api.js +1 -1
- package/dist/bin/hook.js +43 -42
- package/dist/chunks/index-0m5hdtv4.js +2 -0
- package/dist/chunks/index-ba8pkj4y.js +2 -0
- package/dist/chunks/index-ezd6m8rs.js +2 -0
- package/dist/chunks/index-g4mrvgys.js +2 -0
- package/dist/chunks/{index-0d9zqgyg.js → index-nkbb32a2.js} +4 -4
- package/dist/chunks/index-szahvnh8.js +50 -0
- package/dist/cli.js +121 -121
- package/dist/deepseek-harness/index.js +1 -1
- package/dist/index.js +8 -8
- package/dist/openclaw/cc-safety-net/index.js +44 -43
- package/dist/openclaw/cc-safety-net/openclaw.plugin.json +1 -1
- package/dist/openclaw/cc-safety-net/package.json +1 -1
- package/dist/pi/index.js +2 -2
- package/package.json +2 -2
- package/dist/chunks/index-j7c6979z.js +0 -49
- package/dist/chunks/index-zwavdh1s.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import{a,s,
|
|
2
|
-
${
|
|
3
|
-
`).filter(Boolean).flatMap((
|
|
4
|
-
`)}function As(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 Ss(P){let O=Number(P);return Number.isFinite(O)&&O>0?O:null}var Ts={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 Is={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 $s={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 Eo=["npx","--offline","--no-install","@deepseek-ai/dsh","--version"],Os=["cursor-agent","--version"],mr=[{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:Eo}},{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"]}}],gr=mr.slice().sort((P,O)=>P.doctorOrder-O.doctorOrder).map((P)=>P.id),Ht=mr.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=mr.slice().sort((P,O)=>P.install.order-O.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...O})=>O),Lp=Object.fromEntries(mr.map((P)=>[P.id,P.displayName]));function mn(P){return Lp[P]}var Np=Ht.map((P)=>({flags:P.flags.join(", "),description:P.description})),jp=Ht.flatMap((P)=>P.flags.map((O)=>`cc-safety-net hook ${O}`)),Ds={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...Np,{flags:"-h, --help",description:"Show this help"}],examples:jp};var Ls={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}`)]},Ns={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}`)]},js={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 Fs={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 yr={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 _o=[{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"}],Ao=["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"},..._o.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",...Ao,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var Hs={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 Ms={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 hr=[Hs,Ts,Fs,Is,xt,yr,Ls,js,Ns,Ds,$s,Ms];function Fp(P){return P.aliases??[]}function vr(P){let O=P.toLowerCase();return hr.find((D)=>D.name.toLowerCase()===O||Fp(D).some((j)=>j.toLowerCase()===O))}import{existsSync as xy}from"node:fs";import{basename as Hp}from"node:path";function br(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?Kn(D,Ce):[];for(let Ee of Se)for(let qe of kt(Ee,Ce)){if(qe.decision==="allow")continue;let Me=new Date(qe.ts).getTime();if(Me>=j){if(re++,Y.add(qe.sessionId??Hp(Ee,".jsonl")),de===void 0||Me<=de)ie=qe.ts,de=Me;if(we===void 0||Me>we)fe=qe.ts,we=Me;Mp(J,qe,Me)}}let Ie=J.map((Ee)=>({timestamp:Ee.ts,command:Ee.command,reason:Ee.reason,relativeTime:xs(new Date(Ee.ts))}));return{totalBlocked:re,sessionCount:Y.size,recentEntries:Ie,oldestEntry:ie,newestEntry:fe,unreadable:Ce.count}}function Mp(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 Xp}from"node:path";import{dirname as Up,join as Gp,resolve as qp}from"node:path";var Bp="config.json";function wn(P,O,D,j){g(Vp(P),`${JSON.stringify(O,null,2)}
|
|
5
|
-
`,D,j)}function Vp(P){return typeof P==="string"?se(P):P}function Io(P){return{errors:ce(Kp(P),": "," "),ruleNames:new Set(De(P).map((O)=>O.toLowerCase()))}}var Jp="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",Us="must match pattern (letters, numbers, hyphens, underscores)";function Kp(P){if(!Gs(P))return[e([],"Config must be an object")];return[...P.version===1?[]:[e(["version"],"must be 1")],...Wp(P.rules)]}function Wp(P){if(P===void 0)return[];if(!Array.isArray(P))return[e(["rules"],"must be an array")];return[...P.flatMap((O,D)=>Gs(O)?zp(O,["rules",D]):[e(["rules",D],"must be an object")]),...Oe(P)]}function zp(P,O){return[...To(P.name,[...O,"name"],"required string",d,Jp),...To(P.command,[...O,"command"],"required string",C,Us),...P.subcommand===void 0?[]:To(P.subcommand,[...O,"subcommand"],"must be a string if provided",C,Us),...Yp(P.block_args,[...O,"block_args"]),...Zp(P.reason,[...O,"reason"]),...P.intent===void 0||Te(P.intent)?[]:[e([...O,"intent"],Re)]]}function To(P,O,D,j,J){if(typeof P!=="string")return[e(O,D)];return j.test(P)?[]:[e(O,J)]}function Yp(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 Zp(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 Gs(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}function $o(P){let O=qs(P);if(!O.ok)return O.result;return Io(O.parsed)}function qs(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 wr(P){return qp(P,".safety-net.json")}function Un(P){let O=qs(P);if(!O.ok)return O.result;let D=Ze(O.parsed);return{errors:D.errors,ruleNames:D.sources}}function Ct(P,O={}){return Gp(Up(ke(P,O)),Bp)}function Bs(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 Qp(P,O){return{source:O,name:P.name,command:P.command,subcommand:P.subcommand,blockArgs:[...P.block_args],reason:P.reason}}function Vs(P,O){let D=W(P),j=_(O),J=Xp(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:Bs(D,re.userConfigTarget,re.userScope),projectConfig:Bs(j,re.projectConfigTarget,re.projectScope),effectiveRules:Y.rules.map((de)=>Qp(de,ie.get(de.name)??"project"))}}var ef=[{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 Js(P){return[...ef.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 Ks={error:0,warning:1,info:2},nf=["policy","config","audit"];function tf(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 rf=[{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 Ve(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."}]:[]}},...nf.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 ${tf(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 Ws(P){return rf.flatMap((O,D)=>O.derive(P).map((j,J)=>({finding:j,catalogOrder:D,occurrence:J}))).sort((O,D)=>Ks[O.finding.severity]-Ks[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 of=(P)=>Ln()?`\x1B[32m${P}\x1B[0m`:P,sf=(P)=>Ln()?`\x1B[33m${P}\x1B[0m`:P,af=(P)=>Ln()?`\x1B[34m${P}\x1B[0m`:P,lf=(P)=>Ln()?`\x1B[36m${P}\x1B[0m`:P,cf=(P)=>Ln()?`\x1B[31m${P}\x1B[0m`:P,df=(P)=>Ln()?`\x1B[2m${P}\x1B[0m`:P,uf=(P)=>Ln()?`\x1B[1m${P}\x1B[0m`:P,nn={green:of,yellow:sf,blue:af,cyan:lf,red:cf,dim:df,bold:uf},pf="\x1B[0m",ff=[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 mf(P){let O=P;return()=>(O=(O*1664525+1013904223)%4294967296,O/4294967296)}function gf(P){let O=[...ff],D=mf(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 yf(P,O=0){if(!Ln())return"";let D=gf(O);return`\x1B[38;5;${D[P%D.length]}m`}function zs(P,O,D=0){if(!Ln())return`"${P}"`;return`${yf(O,D)}"${P}"${pf}`}function kr(P){return P==="default"?"built-in default":`${P} policy`}var hf=new RegExp("\x1B\\[[0-9;]*m","g"),Oo=(P)=>P.replace(hf,"").length;function Wn(P){let O=(P.headers??P.rows[0]??[]).map((re,ie)=>{let de=Math.max(...P.rows.map((fe)=>Oo(fe[ie]??"")));return Math.max(Oo(re),de)}),D=(re,ie)=>re+" ".repeat(Math.max(0,ie-Oo(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
|
|
12
|
-
`);for(let
|
|
13
|
-
`)}function
|
|
14
|
-
`)}function
|
|
15
|
-
`);if(
|
|
16
|
-
`);if(
|
|
17
|
-
`);return
|
|
18
|
-
`)}function
|
|
19
|
-
`)}function
|
|
20
|
-
No findings from inspected doctor facts.`);let
|
|
21
|
-
${
|
|
22
|
-
`)[0]?.trim()||null}async function
|
|
23
|
-
`).map((
|
|
24
|
-
`.repeat(
|
|
25
|
-
\x1B[0m\x1B[?25h`)}}var
|
|
26
|
-
`);function
|
|
27
|
-
`).map((
|
|
28
|
-
`).find((
|
|
29
|
-
`)
|
|
30
|
-
`,
|
|
31
|
-
`?
|
|
32
|
-
`,
|
|
33
|
-
`,
|
|
34
|
-
`)}function
|
|
35
|
-
`),
|
|
36
|
-
${
|
|
37
|
-
${
|
|
38
|
-
`)}function
|
|
39
|
-
`)}function
|
|
40
|
-
`),{path:
|
|
41
|
-
`)}function
|
|
42
|
-
`)}function
|
|
43
|
-
`)}function
|
|
44
|
-
# version: ${
|
|
45
|
-
`}function
|
|
46
|
-
version: "${
|
|
1
|
+
import{a,s,De,et,T,gt,tt,c,n,nt,P,S,pe,yt,m,Te,x,ot,l,o,w,i,se,r,g,me,H,xt,vt,p,ge,he,St,L,Z,ae,C,wt,I,h,_e,W,u,A,X,it,st,M,E,O,ye,be,Le,Ct,d,e,Me,Pe,Fe,xe,at,ct,ce,Ie,le,$e,Q,B,je,ve,z,lt,V,ee,te,ue,ut,dt,k,F,Ue,He,We,R,Be,Ae,ze,y,t,ne,Se,b,v,D,Et,Ve,qe,f,q}from"./chunks/index-szahvnh8.js";import{Ze}from"./chunks/index-ba8pkj4y.js";import{Ke,we}from"./chunks/index-0m5hdtv4.js";import{Xe}from"./chunks/index-g4mrvgys.js";import"./chunks/index-ezd6m8rs.js";import{oe,K,Qe,U,G}from"./chunks/index-nkbb32a2.js";var Vp=["-h","--help"];function gn(_,N){let j=Object.entries(_.booleans??{}),J=Object.entries(_.values??{}),Y=Object.entries(_.lists??{}),re=Object.fromEntries(j.map(([Oe])=>[Oe,!1])),ie={},de=Object.fromEntries(Y.map(([Oe])=>[Oe,[]])),fe=[],ke=[],Ce=!1,Re=-1;for(let[Oe,Ee]of N.entries()){if(Oe<=Re)continue;if(Ee==="--"){fe.push(...N.slice(Oe+1));break}if(Vp.includes(Ee)){Ce=!0;continue}let Ne=j.find(([,Ye])=>Ye.includes(Ee));if(Ne){re[Ne[0]]=!0;continue}let Je=J.find(([,Ye])=>Ye.includes(Ee));if(Je){let Ye=N[Oe+1];if(Ye===void 0||Ye.startsWith("-")){ke.push(`${Ee} requires a value`);continue}ie[Je[0]]=Ye,Re=Oe+1;continue}let Ge=Y.find(([,Ye])=>Ye.includes(Ee));if(Ge){let Ye=N.slice(Oe+1),rn=Ye.findIndex((sn)=>sn.startsWith("-")),on=Ye.slice(0,rn===-1?Ye.length:rn);if(on.length===0){ke.push(`${Ee} requires at least one value`);continue}de[Ge[0]]=[...de[Ge[0]]??[],...on],Re=Oe+on.length;continue}if(Ee.startsWith("-")){ke.push(`Unknown option for ${_.label}: ${Ee}`);continue}if(_.positionals==="tail"){fe.push(...N.slice(Oe));break}fe.push(Ee)}if(_.positionals!=="list"&&_.positionals!=="tail")ke.push(...fe.map((Oe)=>`Unexpected argument for ${_.label}: ${Oe}`));return{flags:re,values:ie,lists:de,positionals:fe,help:Ce,errors:ke}}function Dn(_){for(let N of _)console.error(N);return _.length>0}import{readdirSync as Xp,statSync as Bs,unlinkSync as Qp}from"node:fs";import{basename as qs,dirname as ef,isAbsolute as nf,join as tf,relative as rf,resolve as of,sep as sf}from"node:path";var Ms=(_)=>{let N=Date.now()-new Date(_).getTime();if(!Number.isFinite(N))return"";let j=Math.floor(N/60000),J=Math.floor(j/60),Y=Math.floor(J/24);if(Y>0)return`${Y}d ago`;if(J>0)return`${J}h ago`;if(j>0)return`${j}m ago`;return"just now"},Mo=(_)=>{let N=(_??"").trim().split(/\s+/).filter((Y)=>Y&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(Y)),j=N[0]?.split("/").pop();if(!j)return null;let J=N[1];return J&&/^[a-z][a-z0-9-]*$/.test(J)?`${j} ${J}`:j};function Us(_){let N=(Y)=>`${Y.sessionId}
|
|
2
|
+
${Mo(Y.segment||Y.command)}`,j=_.filter((Y)=>Y.decision!=="allow"),J=j.filter((Y)=>Y.sessionId).reduce((Y,re)=>Y.set(N(re),(Y.get(N(re))??0)+1),new Map);return new Set(j.filter((Y)=>Y.failureStage||(J.get(N(Y))??0)>=2))}import{existsSync as Kp,readdirSync as Jp,readFileSync as Wp}from"node:fs";import{join as zp}from"node:path";function zn(_,N){try{return Jp(_,{withFileTypes:!0,encoding:"utf8"}).flatMap((j)=>{let J=zp(_,j.name);if(j.isDirectory())return zn(J,N);if(j.name.endsWith(".jsonl"))return[J];return[]})}catch{if(N&&Kp(_))N.count++;return[]}}var Yp=["segment","reason","sessionId","decision","agent","ruleId","failureStage"];function Zp(_){if(!_||typeof _!=="object"||Array.isArray(_))return!1;let N=_;if(typeof N.ts!=="string"||typeof N.command!=="string")return!1;return Yp.every((j)=>N[j]===void 0||typeof N[j]==="string")}function It(_,N){try{return Wp(_,"utf-8").split(`
|
|
3
|
+
`).filter(Boolean).flatMap((j)=>{try{let J=JSON.parse(j);if(!Zp(J)){if(N)N.count++;return[]}return[J]}catch{if(N)N.count++;return[]}})}catch{if(N)N.count++;return[]}}function yn(_){return Array.from(_,(N)=>{let j=N.charCodeAt(0);if(j<=31||j>=127&&j<=159)return`\\x${j.toString(16).padStart(2,"0")}`;return N}).join("")}function af(_,N){let j=oe(_),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"]}},N);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 Y=J.values.limit===void 0?20:Gs(J.values.limit);if(Y===null)return console.error("--limit must be a positive number"),null;let re=J.values.since===void 0?Math.min(30,j):Gs(J.values.since);if(re===null||re>j)return console.error(`--since must be a positive number of days no greater than ${j}`),null;let ie={limit:Y,limitExplicit:J.values.limit!==void 0,since:re,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:of(J.values.project)};if(ie.id&&(ie.agent!==void 0||ie.rule!==void 0||ie.session!==void 0||ie.project!==void 0||ie.suspect||ie.sinceExplicit||ie.limitExplicit))return console.error("--id cannot be combined with --agent, --rule, --session, --project, --suspect, --since, or --limit"),null;if(ie.pruneLegacy&&(ie.id!==void 0||ie.agent!==void 0||ie.rule!==void 0||ie.session!==void 0||ie.project!==void 0||ie.suspect||ie.all||ie.sinceExplicit||ie.limitExplicit))return console.error("--prune-legacy cannot be combined with --id, --agent, --rule, --session, --project, --suspect, --all, --since, or --limit"),null;if(ie.dryRun&&!ie.pruneLegacy)return console.error("--dry-run requires --prune-legacy"),null;return ie}async function Vs(_,N,j={}){let J=af(_,N);if(!J)return 1;let Y=j.logsDir??U(_);if(J.pruneLegacy)return lf(Y,J.json,J.dryRun);if(!Y)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(_,Y);let re={count:0},ie=zn(Y,re).flatMap((Re)=>It(Re,re).map((Oe)=>({entry:Oe,file:Re})));if(re.count>0)console.error(`warning: ${re.count} audit log ${re.count===1?"source":"sources"} could not be read; these results are incomplete`);if(J.id)return pf(ie,J,j.timeZone);let de=Date.now()-J.since*24*60*60*1000,fe=ie.filter((Re)=>ff(Re,J,Y,de)),ke=J.suspect?Us(fe.map((Re)=>Re.entry)):null,Ce=(ke?fe.filter((Re)=>ke.has(Re.entry)):fe).sort((Re,Oe)=>Date.parse(Oe.entry.ts)-Date.parse(Re.entry.ts)).slice(0,J.limit);if(J.json)return console.log(JSON.stringify(Ce.map((Re)=>Re.entry),null,2)),0;if(Ce.length===0)return console.log("No audit log entries found."),0;for(let Re of Ce)console.log(yf(Re.entry,j.timeZone));return 0}function lf(_,N,j){let J=_?df(_).map((de)=>tf(_,de)):[];if(j)return cf(J,N);let Y=[],re=0,ie=0;for(let de of J){let fe=Bs(de,{throwIfNoEntry:!1})?.size??0,ke=uf(de);if(ke){Y.push(`${qs(de)}: ${ke}`);continue}re++,ie+=fe}if(N)return console.log(JSON.stringify({removedFiles:re,removedBytes:ie,failedFiles:Y.length})),Y.length===0?0:1;console.log(re===0&&Y.length===0?"No legacy audit log files found.":`Removed ${re} legacy audit log ${re===1?"file":"files"} (${Ks(ie)}).`);for(let de of Y)console.error(`Could not remove ${yn(de)}`);if(console.log("Nested v2 audit logs were not changed."),re>0)console.log("This deletion cannot be undone.");return Y.length===0?0:1}function cf(_,N){let j=_.reduce((J,Y)=>J+(Bs(Y,{throwIfNoEntry:!1})?.size??0),0);if(N)return console.log(JSON.stringify({dryRun:!0,files:_.length,bytes:j})),0;if(console.log(_.length===0?"No legacy audit log files found.":`Would remove ${_.length} legacy audit log ${_.length===1?"file":"files"} (${Ks(j)}).`),console.log("Nested v2 audit logs are not included."),_.length>0)console.log("Run the same command without --dry-run to delete them.");return 0}function df(_){try{return Xp(_,{withFileTypes:!0}).filter((N)=>N.isFile()&&N.name.endsWith(".jsonl")).map((N)=>N.name)}catch{return[]}}function uf(_){try{return Qp(_),null}catch(N){return N instanceof Error?N.message:String(N)}}function Ks(_){let N=["B","KiB","MiB","GiB"],j=Math.min(Math.floor(Math.log2(Math.max(_,1))/10),N.length-1);return`${Math.round(_/1024**j*10)/10} ${N[j]}`}function pf(_,N,j){let J=_.filter((re)=>re.entry.id===N.id);if(J.length>1)return console.error(`Multiple audit log entries found for id ${yn(N.id??"")}.`),1;if(N.json)return console.log(JSON.stringify(J.map((re)=>re.entry),null,2)),0;let Y=J[0];if(!Y)return console.log(`No retained audit log entry found for id ${yn(N.id??"")}.`),0;return console.log(hf(Y.entry,j)),0}function ff(_,N,j,J){if(!N.all&&_.entry.decision==="allow")return!1;if(Date.parse(_.entry.ts)<J)return!1;if(N.agent!==void 0&&_.entry.agent!==N.agent)return!1;if(N.rule!==void 0&&_.entry.ruleId!==N.rule)return!1;if(N.session!==void 0&&!mf(_,j,N.session))return!1;if(N.project!==void 0&&!gf(_.entry.cwd,N.project))return!1;return!0}function mf(_,N,j){if(_.entry.sessionId===j)return!0;return ef(_.file)===N&&qs(_.file,".jsonl")===j}function gf(_,N){if(!_)return!1;let j=rf(N,_);return j!==".."&&!j.startsWith(`..${sf}`)&&!nf(j)}function yf(_,N){let j=yn(_.id??"-"),J=yn(_.decision??"deny"),Y=_.cwd?` [${yn(_.cwd)}]`:"",re=_.segment||_.command,ie=re===_.command?"":"↳ ",de=re.length>50?`${re.slice(0,50)}…`:re;return`${j.padEnd(16)} ${yn(Js(_.ts,N))} ${J.padEnd(5)} ${yn(_.agent??"-").padEnd(15)} ${yn(_.ruleId??"-").padEnd(20)} ${ie}${yn(de)}${Y}`}function hf(_,N){let j=(Y)=>yn(Y===void 0||Y===null||Y===""?"-":Y),J=_.shape?`${_.agent??"-"} (shape: ${_.shape})`:_.agent??"-";return[`id: ${j(_.id)}`,`ts: ${j(Js(_.ts,N))}`,`decision: ${j(_.decision)}`,`agent: ${j(J)}`,`level: ${j(_.level)}`,`tool: ${j(_.toolName)}`,`rule: ${j(_.ruleId)}`,`intent: ${j(_.intent)}`,`stage: ${j(_.failureStage)}`,`error: ${j(_.errorCode)}`,`session: ${j(_.sessionId)}`,`cwd: ${j(_.cwd)}`,`version: ${j(_.v)}`,`truncated: ${j(_.truncated===!0?"yes":void 0)}`,`reason: ${j(_.reason)}`,`command: ${j(_.command)}`,`segment: ${j(_.segment)}`].join(`
|
|
4
|
+
`)}function Js(_,N){let j=new Date(_);if(Number.isNaN(j.getTime()))return _;return new Intl.DateTimeFormat("sv-SE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:N}).format(j)}function Gs(_){let N=Number(_);return Number.isFinite(N)&&N>0?N:null}var Ws={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 zs={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 Ys={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 Uo=["npx","--offline","--no-install","@deepseek-ai/dsh","--version"],Zs=["cursor-agent","--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:Uo}},{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"]}}],Rr=Pr.slice().sort((_,N)=>_.doctorOrder-N.doctorOrder).map((_)=>_.id),Yt=Pr.filter((_)=>("runtime"in _)).slice().sort((_,N)=>_.runtime.order-N.runtime.order).map((_)=>({id:_.id,displayName:"displayName"in _.runtime?_.runtime.displayName:_.displayName,flags:_.runtime.flags,legacyFlags:"legacyFlags"in _.runtime?_.runtime.legacyFlags:[],description:_.runtime.description,legacyTopLevelFlags:_.runtime.legacyTopLevelFlags})),Rn=Pr.slice().sort((_,N)=>_.install.order-N.install.order).map((_)=>({id:_.id,..._.install})).map(({order:_,...N})=>N),vf=Object.fromEntries(Pr.map((_)=>[_.id,_.displayName]));function cn(_){return vf[_]}var bf=Yt.map((_)=>({flags:_.flags.join(", "),description:_.description})),wf=Yt.flatMap((_)=>_.flags.map((N)=>`cc-safety-net hook ${N}`)),Xs={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...bf,{flags:"-h, --help",description:"Show this help"}],examples:wf};var Qs={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...Rn.map((_)=>({flags:_.flag,description:`Install ${cn(_.id)} ${_.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...Rn.map((_)=>`cc-safety-net install ${_.flag}`)]},ea={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...Rn.map((_)=>({flags:_.flag,description:`Uninstall ${cn(_.id)} ${_.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...Rn.map((_)=>`cc-safety-net uninstall ${_.flag}`)]},na={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 ta={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 Er={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 Go=[{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"}],Bo=["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"],$t={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:"tool add <name...> [--cwd-arg <name>] [--project]",description:"Analyze community shell tools (user scope by default)"},{usage:"tool remove <name...> [--project]",description:"Stop analyzing declared tools"},{usage:"tool list",description:"List declared command tools in both scopes"},{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"},{flags:"--project",description:"Use project-scope rule config with rule tool"},{flags:"--cwd-arg",argument:"<name>",description:"Name the working-directory argument with rule tool add"},...Go.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","cc-safety-net rule tool add ops_bash","cc-safety-net rule tool add ops_run_background --cwd-arg cwd --project",...Bo,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var ra={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 oa={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 _r=[ra,Ws,ta,zs,$t,Er,Qs,na,ea,Xs,Ys,oa];function kf(_){return _.aliases??[]}function Tr(_){let N=_.toLowerCase();return _r.find((j)=>j.name.toLowerCase()===N||kf(j).some((J)=>J.toLowerCase()===N))}import{existsSync as ah}from"node:fs";import{basename as Cf}from"node:path";function Ar(_,N=7,j=U(_)){let J=Date.now()-N*24*60*60*1000,Y=[],re=new Set,ie=0,de,fe,ke,Ce;if(j)K(_,j);let Re={count:0},Oe=j?zn(j,Re):[];for(let Ne of Oe)for(let Je of It(Ne,Re)){if(Je.decision==="allow")continue;let Ge=new Date(Je.ts).getTime();if(Ge>=J){if(ie++,re.add(Je.sessionId??Cf(Ne,".jsonl")),fe===void 0||Ge<=fe)de=Je.ts,fe=Ge;if(Ce===void 0||Ge>Ce)ke=Je.ts,Ce=Ge;xf(Y,Je,Ge)}}let Ee=Y.map((Ne)=>({timestamp:Ne.ts,command:Ne.command,reason:Ne.reason,relativeTime:Ms(new Date(Ne.ts))}));return{totalBlocked:ie,sessionCount:re.size,recentEntries:Ee,oldestEntry:de,newestEntry:ke,unreadable:Re.count}}function xf(_,N,j){let J=_.findIndex((Y)=>j>new Date(Y.ts).getTime());if(J===-1){if(_.length<3)_.push(N);return}if(_.splice(J,0,N),_.length>3)_.pop()}import{dirname as Lf}from"node:path";import{dirname as Sf,join as Pf,resolve as Rf}from"node:path";var Ef="config.json";function vn(_,N,j,J){g(_f(_),`${JSON.stringify(N,null,2)}
|
|
5
|
+
`,j,J)}function _f(_){return typeof _==="string"?se(_):_}function Vo(_){return{errors:ce(Af(_),": "," "),ruleNames:new Set($e(_).map((N)=>N.toLowerCase()))}}var Tf="must match pattern (letters, numbers, hyphens, underscores; max 64 chars)",ia="must match pattern (letters, numbers, hyphens, underscores)";function Af(_){if(!sa(_))return[e([],"Config must be an object")];return[..._.version===1?[]:[e(["version"],"must be 1")],...Of(_.rules)]}function Of(_){if(_===void 0)return[];if(!Array.isArray(_))return[e(["rules"],"must be an array")];return[..._.flatMap((N,j)=>sa(N)?If(N,["rules",j]):[e(["rules",j],"must be an object")]),...Me(_)]}function If(_,N){return[...qo(_.name,[...N,"name"],"required string",p,Tf),...qo(_.command,[...N,"command"],"required string",E,ia),..._.subcommand===void 0?[]:qo(_.subcommand,[...N,"subcommand"],"must be a string if provided",E,ia),...$f(_.block_args,[...N,"block_args"]),...Df(_.reason,[...N,"reason"]),..._.intent===void 0||Ie(_.intent)?[]:[e([...N,"intent"],Pe)]]}function qo(_,N,j,J,Y){if(typeof _!=="string")return[e(N,j)];return J.test(_)?[]:[e(N,Y)]}function $f(_,N){if(!Array.isArray(_))return[e(N,"required array")];if(_.length===0)return[e(N,"must have at least one element")];return _.flatMap((j,J)=>{if(typeof j!=="string")return[e([...N,J],"must be a string")];return j===""?[e([...N,J],"must not be empty")]:[]})}function Df(_,N){if(typeof _!=="string")return[e(N,"required string")];if(_==="")return[e(N,"must not be empty")];return _.length>O?[e(N,`must be at most ${O} characters`)]:[]}function sa(_){return!!_&&typeof _==="object"&&!Array.isArray(_)}function Ko(_){let N=aa(_);if(!N.ok)return N.result;return Vo(N.parsed)}function aa(_){let N=[],j=new Set;try{let J=typeof _==="string"?se(_):_,Y=r(J);if(Y===null)return N.push(`File not found: ${J.path}`),{ok:!1,result:{errors:N,ruleNames:j}};if(!Y.trim())return N.push("Config file is empty"),{ok:!1,result:{errors:N,ruleNames:j}};return{ok:!0,parsed:JSON.parse(Y)}}catch(J){if(J instanceof o)return N.push(J.message),{ok:!1,result:{errors:N,ruleNames:j}};let Y=J instanceof Error?J.message:String(J);return N.push(J instanceof SyntaxError?"Invalid JSON":Y),{ok:!1,result:{errors:N,ruleNames:j}}}}function Or(_){return Rf(_,".safety-net.json")}function Un(_){let N=aa(_);if(!N.ok)return N.result;let j=at(N.parsed);return{errors:j.errors,ruleNames:j.sources}}function Dt(_,N={}){return Pf(Sf(_e(_,N)),Ef)}function la(_,N,j){let J;try{if(r(N)===null)return{path:_,exists:!1,valid:!1,ruleCount:0};J=Un(N),J.errors.push(...B(_,j))}catch(Y){if(!(Y instanceof o))throw Y;J={errors:[Y.message],ruleNames:new Set}}return{path:_,exists:!0,valid:J.errors.length===0,ruleCount:J.ruleNames.size,...J.errors.length>0?{errors:J.errors}:{}}}function Nf(_,N){return{source:N,name:_.name,command:_.command,subcommand:_.subcommand,blockArgs:[..._.block_args],reason:_.reason}}function ca(_,N){let j=W(_),J=I(N),Y=Lf(j),re=Q(_,{cwd:N,userConfigPath:j,projectConfigPath:J,userConfigDir:Y}),ie=X(_,{cwd:N,userConfigPath:j,projectConfigPath:J,userConfigDir:Y}),de=new Map(re.rulebooks.flatMap((fe)=>fe.rules.map((ke)=>[ke,fe.source])));return{userConfig:la(j,ie.userConfigTarget,ie.userScope),projectConfig:la(J,ie.projectConfigTarget,ie.projectScope),effectiveRules:re.rules.map((fe)=>Nf(fe,de.get(fe.name)??"project"))}}var jf=[{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 da(_){return[...jf.map((N)=>({name:N.flag.name,value:pe(N.flag,_.env),isSet:yt(N.flag,_.env),legacyName:N.flag.legacyName,legacyValue:N.flag.legacyName?_.env.get(N.flag.legacyName):void 0,legacyIsSet:N.flag.legacyName?_.env.get(N.flag.legacyName)!==void 0:void 0,description:N.description,defaultBehavior:N.defaultBehavior})),{name:"CC_SAFETY_NET_HOME",value:_.env.get("CC_SAFETY_NET_HOME"),isSet:_.env.get("CC_SAFETY_NET_HOME")!==void 0,description:"Override user-scope config/cache directory",defaultBehavior:"~/.cc-safety-net"}]}var ua={error:0,warning:1,info:2},Hf=["policy","config","audit"];function Ff(_){return _.map((N)=>{if(N==="ownership")return"is not owned by the current user";if(N==="permissions")return"has unsafe permissions";if(N==="symlink")return"is a symbolic link";return"is not a directory"}).join(" and ")}var Mf=[{derive:(_)=>_.hooks.length>0&&_.hooks.every((N)=>!N.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:(_)=>_.hooks.filter((N)=>N.inspectionStatus==="failed").map((N)=>{let j=cn(N.platform);return{checkId:"integration.inspection-failed",severity:"error",title:`${j} inspection failed`,detail:`Doctor could not verify the ${j} integration configuration.`,fixHint:`Correct the reported ${j} configuration error, then run \`cc-safety-net doctor\` again.`,integration:N.platform}})},{derive:(_)=>_.userConfig.exists&&!_.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:_.userConfig.path}]:[]},{derive:(_)=>_.projectConfig.exists&&!_.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:_.projectConfig.path}]:[]},{derive:(_)=>_.configState.state==="degraded"?[{checkId:"config.runtime-degraded",severity:"warning",title:"Runtime is enforcing a fallback configuration",detail:`The rejected candidate configuration is not active: ${_.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:(_)=>_.v2Leftovers&&_.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: ${_.v2Leftovers.join(", ")}.`,fixHint:"Run `cc-safety-net rule sync` (add `--global` for user scope) to migrate them, then rerun doctor."}]:[]},{derive:(_)=>_.legacyConfigs&&_.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: ${_.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:(_)=>{let N=_.environment.find((j)=>j.name==="CC_SAFETY_NET_AUDIT_SCOPE");return nt(N?.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."}]:[]}},...Hf.map((_)=>({derive:(N)=>N.posture.directories.filter((j)=>j.kind===_&&j.status==="unsafe").map((j)=>({checkId:`posture.${_}-directory-unsafe`,severity:"error",title:`${_[0]?.toUpperCase()}${_.slice(1)} directory is unsafe`,detail:`The ${_} directory ${Ff(j.issues)}.`,fixHint:"Ensure this is a real directory owned by the current user with no group or other write access, then rerun doctor.",...j.path?{path:j.path}:{}}))})),{derive:(_)=>{let N=[..._.effectiveSafety.weakenedRuleOverrides].sort();return N.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: ${N.join(", ")}.`,fixHint:`Remove these \`off\` overrides or set them to \`on\`: ${N.join(", ")}.`}]:[]}}];function pa(_){return Mf.flatMap((N,j)=>N.derive(_).map((J,Y)=>({finding:J,catalogOrder:j,occurrence:Y}))).sort((N,j)=>ua[N.finding.severity]-ua[j.finding.severity]||N.catalogOrder-j.catalogOrder||N.occurrence-j.occurrence).map((N)=>N.finding)}function Ln(){return Boolean(process.stdout.isTTY&&!process.env.NO_COLOR)}var Uf=(_)=>Ln()?`\x1B[32m${_}\x1B[0m`:_,Gf=(_)=>Ln()?`\x1B[33m${_}\x1B[0m`:_,Bf=(_)=>Ln()?`\x1B[34m${_}\x1B[0m`:_,qf=(_)=>Ln()?`\x1B[36m${_}\x1B[0m`:_,Vf=(_)=>Ln()?`\x1B[31m${_}\x1B[0m`:_,Kf=(_)=>Ln()?`\x1B[2m${_}\x1B[0m`:_,Jf=(_)=>Ln()?`\x1B[1m${_}\x1B[0m`:_,en={green:Uf,yellow:Gf,blue:Bf,cyan:qf,red:Vf,dim:Kf,bold:Jf},Wf="\x1B[0m",zf=[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 Yf(_){let N=_;return()=>(N=(N*1664525+1013904223)%4294967296,N/4294967296)}function Zf(_){let N=[...zf],j=Yf(_);for(let J=N.length-1;J>0;J--){let Y=Math.floor(j()*(J+1)),re=N[J];N[J]=N[Y],N[Y]=re}return N}function Xf(_,N=0){if(!Ln())return"";let j=Zf(N);return`\x1B[38;5;${j[_%j.length]}m`}function fa(_,N,j=0){if(!Ln())return`"${_}"`;return`${Xf(N,j)}"${_}"${Wf}`}function Ir(_){return _==="default"?"built-in default":`${_} policy`}var Qf=new RegExp("\x1B\\[[0-9;]*m","g"),Jo=(_)=>_.replace(Qf,"").length;function Yn(_){let N=(_.headers??_.rows[0]??[]).map((ie,de)=>{let fe=Math.max(..._.rows.map((ke)=>Jo(ke[de]??"")));return Math.max(Jo(ie),fe)}),j=(ie,de)=>ie+" ".repeat(Math.max(0,de-Jo(ie))),J=(ie,de)=>de[0]+N.map((fe)=>ie.repeat(fe+2)).join(de[1])+de[2],Y=(ie)=>`│ ${ie.map((de,fe)=>j(de,N[fe]??0)).join(" │ ")} │`,re=_.headers?[` ${Y(_.headers)}`,` ${J("─",["├","┼","┤"])}`]:[];return[` ${J("─",["┌","┬","┐"])}`,...re,..._.rows.map((ie)=>` ${Y(ie)}`),` ${J("─",["└","┴","┘"])}`].join(`
|
|
6
|
+
`)}function ma(_){let N=[];N.push("Hook Integration"),N.push(em(_));let j=[],J=[];for(let Y of _){let re=cn(Y.platform);if(Y.errors&&Y.errors.length>0)for(let ie of Y.errors)if(Y.configured)j.push({platform:re,message:ie});else J.push({platform:re,message:ie})}for(let Y of j)N.push(` Warning (${Y.platform}): ${Y.message}`);for(let Y of J)N.push(en.red(` Error (${Y.platform}): ${Y.message}`));return N.join(`
|
|
7
|
+
`)}function em(_){let N=["Platform","Discovery","Configuration","Inspection"],j=_.map((J)=>{let Y=cn(J.platform);if(J.inspectionStatus==="not-inspected"){let fe=en.dim("Not inspected");return[Y,fe,fe,fe]}let re=J.detected?en.green("Detected"):J.inspectionStatus==="failed"?en.red("Unknown"):en.dim("Not detected"),ie=J.configured?en.green("Configured"):J.detected?en.yellow("Not configured"):J.inspectionStatus==="failed"?en.red("Unknown"):en.dim("Not applicable"),de=J.inspectionStatus==="verified"?en.green("Verified"):J.inspectionStatus==="failed"?en.red("Failed"):en.dim("Not applicable");return[Y,re,ie,de]});return Yn({headers:N,rows:j})}function ga(_){let j=["Guard Engine Verification",` Synthetic self-test: ${_.failed>0?en.red(`${_.passed}/${_.total} FAIL`):en.green(`${_.passed}/${_.total} passed`)}`],J=_.results.filter((Y)=>!Y.passed);if(J.length>0){j.push(""),j.push(en.red(" Failures:"));for(let Y of J)j.push(en.red(` • ${Y.description}`)),j.push(en.red(` expected ${Y.expected}, got ${Y.actual}`))}return j.join(`
|
|
8
|
+
`)}function nm(_){if(_.length===0)return" (no custom rules)";let N=["Source","Name","Command","Block Args"],j=_.map((J)=>[J.source,J.name,J.subcommand?`${J.command} ${J.subcommand}`:J.command,J.blockArgs.join(", ")]);return Yn({headers:N,rows:j})}function ya(_){let N=[];if(N.push("Configuration"),N.push(tm(_.userConfig,_.projectConfig)),N.push(""),_.effectiveRules.length>0)N.push(` Effective rules (${_.effectiveRules.length} total):`),N.push(nm(_.effectiveRules));else N.push(" Effective rules: (none - using built-in rules only)");return N.join(`
|
|
9
|
+
`)}function tm(_,N){let j=["Scope","Status"],J=(re)=>{if(!re.exists)return en.dim("N/A");if(!re.valid)return en.red(`Invalid (${re.errors?.[0]??"unknown error"})`);return en.green("Configured")},Y=[["User",J(_)],["Project",J(N)]];return Yn({headers:j,rows:Y})}function ha(_){let N=[];return N.push("Environment"),N.push(rm(_)),N.join(`
|
|
10
|
+
`)}function va(_){let N=_.effectiveSafety.policyScopes,j=["Effective Safety",` Selected preset: ${_.effectiveSafety.selectedPreset}${N?` (${Ir(N.levelScope)})`:""}`,` Effective: ${_.effectiveSafety.level}`],J=[["fail_closed","fail_closed"],["paranoid_rm","paranoid_rm"],["paranoid_interpreters","paranoid_interpreters"]];for(let[Y,re]of J){let ie=_.effectiveSafety.capabilities[Y],de=ie.enabled?en.green("ON"):en.dim("OFF"),fe=ie.sources.length>0?` (${ie.sources.join(", ")})`:"";j.push(` ${re}: ${de} via ${ie.source}${fe}`)}if(N&&N.weakenings.length>0){j.push(` Project policy deltas${N.weakeningsIgnored?" (ignored)":""}:`);for(let Y of N.weakenings)j.push(` ${Y}`)}j.push(` Stored rule customizations: ${_.effectiveSafety.ruleCounts.stored}`),j.push(` Effective rule customizations: ${_.effectiveSafety.ruleCounts.effective}`);for(let[Y,re]of Object.entries(_.effectiveSafety.ruleOverrides))j.push(` ${Y}: ${re}`);return j.join(`
|
|
11
|
+
`)}function ba(_){let N=["Findings"];if(_.length===0)return N.push(" No findings from inspected doctor facts."),N.join(`
|
|
12
|
+
`);for(let j of _){let J=`[${j.severity.toUpperCase()}] ${j.checkId}: ${yn(j.title)}`,Y=j.severity==="error"?en.red:j.severity==="warning"?en.yellow:en.blue;if(N.push(` ${Y(J)}`),N.push(` ${yn(j.detail)}`),j.path)N.push(` Path: ${yn(j.path)}`);if(j.fixHint)N.push(` Fix: ${yn(j.fixHint)}`)}return N.join(`
|
|
13
|
+
`)}function rm(_){let N=["Variable","Status","Legacy"],j=_.map((J)=>{let Y=J.isSet?en.green("✓"):en.dim("✗"),re=J.legacyName&&J.legacyIsSet?`${J.legacyName} ${en.green("✓")}`:J.legacyName??"";return[J.name,Y,re]});return Yn({headers:N,rows:j})}function wa(_){let N=[];if(_.totalBlocked===0)N.push("Recent Activity"),N.push(" No blocked commands in the last 7 days"),N.push(" Tip: This is normal for new installations");else N.push(`Recent Activity · last 7 days (${_.totalBlocked} blocked / ${_.sessionCount} sessions)`),N.push(om(_.recentEntries));if(_.unreadable>0)N.push(` Warning: ${_.unreadable} audit log ${_.unreadable===1?"source":"sources"} could not be read; this summary is incomplete`);return N.join(`
|
|
14
|
+
`)}function om(_){let N=["Time","Command"],j=_.map((J)=>{let Y=yn(J.command.replace(/\r\n|\r|\n/g," ↵ ").replace(/\t/g," ")),re=Y.length>40?`${Y.slice(0,37)}...`:Y;return[J.relativeTime,re]});return Yn({headers:N,rows:j})}function ka(_){let N=[];if(N.push("Update Check"),_.latestVersion===null&&!_.error)return N.push($r([["Status",en.dim("Skipped")],["Installed",_.currentVersion]])),N.join(`
|
|
15
|
+
`);if(_.error)return N.push($r([["Status",`${en.yellow("⚠")} Error`],["Installed",_.currentVersion],["Error",en.dim(_.error)]])),N.join(`
|
|
16
|
+
`);if(_.updateAvailable)return N.push($r([["Status",`${en.yellow("⚠")} Update Available`],["Current",_.currentVersion],["Latest",en.green(_.latestVersion??"")]])),N.push(""),N.push(" Run: bunx cc-safety-net@latest doctor"),N.push(" Or: npx cc-safety-net@latest doctor"),N.join(`
|
|
17
|
+
`);return N.push($r([["Status",`${en.green("✓")} Up to date`],["Version",_.currentVersion]])),N.join(`
|
|
18
|
+
`)}function $r(_){return Yn({rows:_})}function Ca(_){let N=[];return N.push("System Info"),N.push(im(_)),N.join(`
|
|
19
|
+
`)}function im(_){let N=["Component","Version"],j=(re)=>{if(re===null)return en.dim("not found");return re},Y=[{label:"cc-safety-net",value:_.version},...Rr.map((re)=>({label:cn(re),value:_.versions[re]??null})),{label:"Node.js",value:_.nodeVersion},{label:"npm",value:_.npmVersion},{label:"Bun",value:_.bunVersion},{label:"Platform",value:_.platform}].map((re)=>[re.label,j(re.value)]);return Yn({headers:N,rows:Y})}function xa(_){if(_.findings.length===0)return en.green(`
|
|
20
|
+
No findings from inspected doctor facts.`);let N={error:_.findings.filter((re)=>re.severity==="error").length,warning:_.findings.filter((re)=>re.severity==="warning").length,info:_.findings.filter((re)=>re.severity==="info").length},j=["error","warning","info"].filter((re)=>N[re]>0).map((re)=>`${N[re]} ${re}`),J=_.findings.length===1?"finding":"findings",Y=`
|
|
21
|
+
${_.findings.length} ${J}: ${j.join(", ")}.`;if(N.error>0)return en.red(Y);if(N.warning>0)return en.yellow(Y);return en.blue(Y)}import{lstatSync as sm}from"node:fs";import{dirname as Wo}from"node:path";function zo(_,N){try{let j=sm(N);if(j.isSymbolicLink())return{kind:_,path:N,status:"unsafe",issues:["symlink"]};if(!j.isDirectory())return{kind:_,path:N,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:_,path:N,status:"unknown",issues:[]};let J=[...j.uid!==process.getuid()?["ownership"]:[],...(j.mode&18)!==0?["permissions"]:[]];return{kind:_,path:N,status:J.length>0?"unsafe":"safe",issues:J}}catch(j){if(typeof j==="object"&&j!==null&&"code"in j&&j.code==="ENOENT")return{kind:_,path:N,status:"not-applicable",issues:[]};return{kind:_,path:N,status:"unknown",issues:[]}}}function Sa(_,N){let j=U(_);return{directories:[zo("policy",Wo(Wo(N))),zo("config",Wo(N)),...j?[zo("audit",j)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as am}from"node:child_process";import{existsSync as Pa}from"node:fs";import{delimiter as lm,extname as cm,join as dm}from"node:path";import{stripVTControlCharacters as Ra}from"node:util";var _a="2.6.10",um=5000,pm="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function pn(){return _a}function Yo(_,N){let j=_[N];if(j)return j;let J=Object.keys(_).find((Y)=>Y.toLowerCase()===N.toLowerCase()&&!!_[Y]);return J?_[J]:j}function fm(_){return(Yo(_,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((N)=>N.length>0)}function mm(_,N){let j=cm(_)?[_]:[...fm(N).map((J)=>`${_}${J}`),_];if(_.includes("/")||_.includes("\\"))return j.find((J)=>Pa(J))??_;return(Yo(N,"PATH")??"").split(lm).flatMap((J)=>j.map((Y)=>dm(J,Y))).find((J)=>Pa(J))??_}function Ea(_){if(!/[\s"&|<>^]/.test(_))return _;return`"${_.replace(/"/g,'""')}"`}function Zn(_,N){let[j,...J]=_,Y=N[pm]==="win32"?"win32":process.platform;if(!j||Y!=="win32")return{cmd:j??"",args:J};let re=mm(j,N);if(!/\.(?:bat|cmd)$/i.test(re))return{cmd:re,args:J};return{cmd:Yo(N,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",Ea(re),...J.map(Ea)].join(" ")]}}var Lt=async(_,N=um)=>{let j=await gm(_,{timeoutMs:N});if(j.code!==0)return null;return Ra(j.stdout).trim()||Ra(j.stderr).trim()||null};function gm(_,N){let[j,...J]=_;if(!j)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((Y)=>{try{let re=Zn([j,...J],process.env),ie=am(re.cmd,re.args,{stdio:["ignore","pipe","pipe"]}),de=!1,fe="",ke="";ie.stdout.on("data",(Oe)=>{fe+=Oe.toString()}),ie.stderr.on("data",(Oe)=>{ke+=Oe.toString()});let Ce=(Oe)=>{if(de)return;de=!0,clearTimeout(Re),Y(Oe)},Re=setTimeout(()=>{ie.kill(),Ce({code:null,stdout:fe,stderr:ke})},N.timeoutMs);ie.on("close",(Oe)=>{Ce({code:Oe,stdout:fe,stderr:ke})}),ie.on("error",()=>{Ce({code:null,stdout:fe,stderr:ke})})}catch{Y({code:null,stdout:"",stderr:""})}})}function Dr(_){if(!_)return null;let N=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(_);if(N)return N[1]??null;let j=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(_);if(j)return j[1]??null;return _.split(`
|
|
22
|
+
`)[0]?.trim()||null}async function Lr(_,N=Lt,j=process.cwd()){let J=Promise.all(Rn.map(async(Re)=>[Re.id,Dr(await N([...Re.probeCommand]))])),[Y,re,ie,de,fe,ke,Ce]=await Promise.all([J,J.then(async(Re)=>{let Oe=Re.find(([Je])=>Je==="opencode")?.[1];if(!Oe?.startsWith("2.")||!_(Oe))return null;let Ee=["--param",`location[directory]=${j}`],Ne=["opencode","api","integration.list",...Ee];return await N(Ne,30000),N(["opencode","api","plugin.list",...Ee],30000)}),N(["codex","plugin","list"],30000),N(["amp","plugins","list"],30000),N(["node","--version"]),N(["npm","--version"]),N(["bun","--version"])]);return{version:_a,versions:Object.fromEntries(Y),codexPluginListOutput:ie,ampPluginListOutput:de,openCodePluginListOutput:re,nodeVersion:Dr(fe),npmVersion:Dr(ke),bunVersion:Dr(Ce),platform:`${process.platform} ${process.arch}`}}function Zo(_,N){if(N==="dev")return!1;let j=_.split(".").map(Number),J=N.split(".").map(Number),[Y=0,re=0,ie=0]=j,[de=0,fe=0,ke=0]=J;if(Y!==de)return Y>de;if(re!==fe)return re>fe;return ie>ke}async function Gn(){let _=pn(),N=new AbortController,j=setTimeout(()=>N.abort(),3000);try{let J=await fetch("https://registry.npmjs.org/cc-safety-net/latest",{signal:N.signal});if(!J.ok)return{currentVersion:_,latestVersion:null,updateAvailable:!1,error:`npm registry returned ${J.status}`};let Y=await J.json(),re=Zo(Y.version,_);return{currentVersion:_,latestVersion:Y.version,updateAvailable:re}}catch(J){return{currentVersion:_,latestVersion:null,updateAvailable:!1,error:J instanceof Error?J.message:"Network error"}}finally{clearTimeout(j)}}import*as Na from"node:readline";var Ia=(_)=>`\x1B[${_}B`,ym=(_)=>`\x1B[${_}A`;var Ta=["░","▒","▓","╱","╲","┃","━","┏","┓","┗","┛","╋"];function hm(_){return new Promise((N)=>setTimeout(N,_))}function vm(_,N,j){if(!j)return N(_);if(j.aborted)return Promise.resolve();return new Promise((J,Y)=>{let re=()=>j.removeEventListener("abort",ie),ie=()=>{re(),J()};j.addEventListener("abort",ie,{once:!0}),N(_).then(()=>{re(),J()},(de)=>{re(),Y(de)})})}function Nr(_){return Math.max(0,Math.min(1,_))}function Nt(_){return Math.max(0,Math.min(255,Math.round(_)))}function Xo(_){return _<=0.0031308?12.92*_:1.055*_**0.4166666666666667-0.055}function bm(_,N,j){let J=j*Math.PI/180,Y=N*Math.cos(J),re=N*Math.sin(J),ie=(_+0.3963377774*Y+0.2158037573*re)**3,de=(_-0.1055613458*Y-0.0638541728*re)**3,fe=(_-0.0894841775*Y-1.291485548*re)**3;return{blue:Nt(Xo(Nr(-0.0041960863*ie-0.7034186147*de+1.707614701*fe))*255),green:Nt(Xo(Nr(-1.2684380046*ie+2.6097574011*de-0.3413193965*fe))*255),red:Nt(Xo(Nr(4.0767416621*ie-3.3077115913*de+0.2309699292*fe))*255)}}function Qo(_,N){let j=(N*_*180/Math.PI%360+360)%360;return bm(0.72,0.15,j)}function $a(_,N=0.1){let j=Qo(N,_);return`\x1B[38;2;${j.red};${j.green};${j.blue}m`}function wm(_,N){return{blue:Nt(_.blue+(255-_.blue)*N),green:Nt(_.green+(255-_.green)*N),red:Nt(_.red+(255-_.red)*N)}}function Da(_,N,j){let J=Math.imul(_+2654435769,2246822507)^Math.imul(N+3266489909,668265263)^Math.imul(j+374761393,2654435761),Y=J^J>>>15,re=Math.imul(Y,739982445),ie=re^re>>>12,de=Math.imul(ie,695872825);return((de^de>>>15)>>>0)/4294967296}function km(_,N,j){let J=Math.floor(Da(_,N,j)*Ta.length);return Ta[J]??"░"}function Aa(_){let N=Nr(_);return N*N*N*(N*(N*6-15)+10)}function Cm(_){if(_.length===0)return"";let N=[],j=!1,J="";for(let Y of _){let re=`${Y.red};${Y.green};${Y.blue}`;if(Y.bold!==j)N.push(Y.bold?"\x1B[1m":"\x1B[22m"),j=Y.bold;if(re!==J)N.push(`\x1B[38;2;${re}m`),J=re;N.push(Y.character)}return`${N.join("")}\x1B[22m\x1B[39m`}function xm(_,N,j,J,Y){return _.map((re,ie)=>({...Qo(j,J+N+ie/Y),bold:!1,character:re}))}function Sm(_,N,j,J,Y,re,ie,de){let fe=Math.max(1,J*0.75),ke=Math.min(1,j/fe),Ce=Y*Aa(ke),Re=Math.max(0,(j-fe)/Math.max(1,J-fe)),Oe=(1-Aa(j/J))*de*2,Ee=0.35*Math.max(0,1-Re*2),Ne=ke>=1,Je=Math.min(_.length,Math.ceil(Ce+2+1));return _.slice(0,Je).map((Ge,Ye)=>{let rn=Qo(re,ie+N+Ye/de+Oe),on=Ye+Da(N,Ye,7919)*2-1;if(on>Ce+2)return{...rn,bold:!1,character:" "};let sn=Ce-on,tn=0.8*Math.exp(-(sn*sn)/12.5),fn=Math.min(0.9,tn+Ee),$n=!Ne&&on>Ce-4;return{...wm(rn,fn),bold:fn>0.3,character:$n?km(N,Ye,j):Ge}})}function Oa(_){return`\x1B[?2026h${_.map((N,j)=>`\x1B8${j>0?Ia(j):""}${Cm(N)}`).join("")}\x1B[?2026l`}async function ei(_,N={}){if(!_)return;let j=N.output??process.stdout,J=N.sleep??hm,Y=N.seed??0,re=_.split(`
|
|
23
|
+
`).map((Ce)=>Array.from(Ce)),ie=Math.max(...re.map((Ce)=>Ce.length)),de=12000*re.filter((Ce)=>Ce.length>0).length/40,fe=ie>0?Math.max(1,Math.ceil(de/16.666666666666668)):0,ke=fe>0?de/fe:0;j.write(`\x1B[?25l${re.length>1?`${`
|
|
24
|
+
`.repeat(re.length-1)}${ym(re.length-1)}`:""}\x1B7`);try{for(let Ce=1;Ce<=fe;Ce+=1){if(N.signal?.aborted)break;j.write(Oa(re.map((Re,Oe)=>Sm(Re,Oe,Ce,fe,ie,0.1,Y,3)))),await vm(ke,J,N.signal)}}finally{if(j.write(Oa(re.map((Ce,Re)=>xm(Ce,Re,0.1,Y,3)))),j.write("\x1B8"),re.length>1)j.write(Ia(re.length-1));j.write(`
|
|
25
|
+
\x1B[0m\x1B[?25h`)}}var La=["┏━┛┏━┛ ┏━┛┏━┃┏━┛┏━┛━┏┛┃ ┃ ┏━ ┏━┛━┏┛","┃ ┃ ━━┃┏━┃┏━┛┏━┛ ┃ ━┏┛ ┃ ┃┏━┛ ┃ ","━━┛━━┛ ━━┛┛ ┛┛ ━━┛ ┛ ┛ ┛ ┛━━┛ ┛ "].join(`
|
|
26
|
+
`);function Pm(_){return Boolean(_.isTTY)}async function Zt(_={}){let N=_.output??process.stdout;if(!Pm(N))return;let j=_.input??process.stdin,J={output:N,seed:_.seed??Math.random()*8192,sleep:_.sleep};if(!j.isTTY||typeof j.setRawMode!=="function"){await ei(La,J);return}let Y=new AbortController,re=j.readableFlowing===!0,ie=j.isRaw===!0,de=!1,fe=(ke,Ce)=>{if(Ce.ctrl&&Ce.name==="c")de=!0;if(de||Ce.name==="return"||Ce.name==="enter")Y.abort()};Na.emitKeypressEvents(j),j.on("keypress",fe),j.setRawMode(!0),j.resume();try{await ei(La,{...J,signal:Y.signal})}finally{if(j.off("keypress",fe),j.setRawMode(ie),!re)j.pause()}if(!de)return;if(_.onInterrupt){_.onInterrupt();return}process.kill(process.pid,"SIGINT")}import{createHash as Am}from"node:crypto";import{existsSync as Fa}from"node:fs";import{dirname as jr,join as Ma}from"node:path";import{dirname as ja,join as Rm,resolve as Em}from"node:path";var _m="rule.lock";function Tm(_){return Rm(ja(_),_m)}function Ha(_={}){return Em(_.cwd??process.cwd(),".safety-net.json")}function hn(_,N){let j=N.global?N.userConfigPath??W(_,N):N.projectConfigPath??I(N.cwd??process.cwd()),J=N.global?it(_,N):st(j,N.cwd??process.cwd()),Y=Tm(j);return{configDir:ja(j),configPath:j,lockPath:Y,filesystemScope:J,configTarget:i(J,j),lockTarget:i(J,Y)}}var Om="`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.",Im="cache",$m="rulebooks";function Ua(_,N={}){let j=hn(_,N),J=i(j.filesystemScope,Ba(j.configDir)),Y=r(j.lockTarget);if(console.log(Om),Y===null&&!Fa(J.path))return console.log(`No v2 lock or cache leftovers found in ${jr(j.configDir)}; nothing to migrate.`),0;let re=Hm(Y),ie=d(j.configTarget);if(!ie.config&&(r(j.configTarget)!==null||re.size>0))return console.error(`Cannot migrate: the rules config in ${jr(j.configDir)} is missing or unreadable while v2 leftovers remain. Restore rule.json, then re-run rule sync.`),1;let de=ie.config?.rules??[];for(let fe of de.flatMap((ke)=>Dm(ke,re,j,J,N.global===!0)))console.log(fe);return H(j.lockTarget),xt(J),console.log(`Removed the v2 lock and cache under ${jr(j.configDir)}.`),0}function Ga(_,N){return[...new Set([{cwd:N},{cwd:N,global:!0}].flatMap((j)=>{let J=hn(_,j);return[J.lockPath,Ba(J.configDir)]}))].filter((j)=>Fa(j))}function Dm(_,N,j,J,Y){if(!C(_))return[];let re=L(_).name,ie=i(j.filesystemScope,M(j.configDir,re)),de=r(ie);if(de!==null&&Lm(de,re))return[];let fe=N.get(_),ke=fe?Nm(fe,re,J.path,j.filesystemScope):null;if(ke===null)return[`Could not migrate ${_} from the v2 cache. Run \`cc-safety-net rule update ${_}${Y?" --global":""}\` to vendor it.`];if(g(ie,ke),de!==null)return[`Restored ${_} from the v2 cache over an invalid file.`];return[`Vendored ${_} from the v2 cache.`]}function Lm(_,N){let j=ve(_);return!("problem"in j)&&j.rulebook.name===N}function Nm(_,N,j,J){let Y=Ma(j,$m,`${jm(_)}--${_.digest.replace("sha256:","").slice(0,12)}`,ge),re=r(i(J,Y));if(re===null||Um(re)!==_.digest)return null;let ie=ve(re);if("problem"in ie||ie.rulebook.name!==N)return null;return re}function Ba(_){return Ma(jr(_),Im)}function jm(_){return([_.owner,_.repo,_.display_ref,_.name].every((J)=>typeof J==="string"&&J!=="")?`${_.owner}/${_.repo}#${_.display_ref}/${_.name}`:_.spec).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"rulebook"}function Hm(_){let N=_===null?null:Mm(_),j=qa(N)&&Array.isArray(N.rulebooks)?N.rulebooks:[];return new Map(j.filter(Fm).map((J)=>[J.spec,J]))}function Fm(_){return qa(_)&&typeof _.spec==="string"&&typeof _.digest==="string"}function qa(_){return!!_&&typeof _==="object"}function Mm(_){try{return JSON.parse(_)}catch{return null}}function Um(_){return`sha256:${Am("sha256").update(_).digest("hex")}`}var Va="\r\x1B[2K",Gm="\x1B[?25l",Bm="\x1B[39m",qm="\x1B[?25h",Vm=100,Km=0.55,Jm=80,Ka=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];function Wm(_){return new Promise((N)=>setTimeout(N,_))}async function Hr(_,N={}){let j=N.output??process.stdout;if(!j.isTTY)return _;let J=N.sleep??Wm,Y=!1,re=_.then((de)=>(Y=!0,de),(de)=>{throw Y=!0,de});if(await Promise.race([re.then(()=>!0),J(Vm).then(()=>!1)]))return re;j.write(Gm);try{for(let de=0;!Y;de+=1)j.write(`${Va}${$a(de*Km)}${Ka[de%Ka.length]}${Bm} ${N.loadingMessage??"Loading…"}`),await Promise.race([re,J(Jm)]);return await re}finally{j.write(`${Va}${qm}`)}}async function Xt(_,N,j,J={}){let Y=N();if(_)await j();if(_&&Y.ready)await Hr(Y.ready,J);return Y.finish()}import{stripVTControlCharacters as zm}from"node:util";var Fr="amp plugins list",Ym=/^\s*[✓✗]\s+cc-safety-net(?:\.ts)?\s+\(User Plugins\)\s+(\S+)\s*$/;function Ja(_){if(!_.ampPluginListOutput)return{platform:"amp",status:"n/a"};let N=zm(_.ampPluginListOutput).split(`
|
|
27
|
+
`).map((j)=>Ym.exec(j)?.[1]).find((j)=>j!==void 0);if(!N)return{platform:"amp",status:"n/a"};if(N!=="active")return{platform:"amp",status:"disabled",method:Fr,configPath:Fr,errors:[`Amp personal plugin cc-safety-net is ${N}; run "plugins: reload" in Amp or reinstall with install --amp`]};return{platform:"amp",status:"configured",method:Fr,configPath:Fr}}import{existsSync as og,readFileSync as ig}from"node:fs";import{isAbsolute as Nx,join as rg}from"node:path";function Qt(_){return rg(_,".gemini","config","hooks.json")}var Wa=new Map([["run_command","auto"]]);var sg=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*(?:--agy-cli|-ac)(\s|["']|$)/;function ag(_){if(!_||typeof _!=="object"||Array.isArray(_))return[];return Object.values(_).flatMap((N)=>{if(!N||typeof N!=="object"||Array.isArray(N))return[];let j=N,J=j.PreToolUse;if(!Array.isArray(J))return[];return J.flatMap((Y)=>{if(!Y||typeof Y!=="object"||Array.isArray(Y))return[];let re=Y.hooks;if(!Array.isArray(re))return[];return re.flatMap((ie)=>{if(!ie||typeof ie!=="object"||Array.isArray(ie))return[];let de=ie.command;if(typeof de!=="string"||!sg.test(de))return[];return[{command:de,enabled:j.enabled!==!1}]})})})}function za(_){let N=Qt(_.environment.home);if(!og(N))return{platform:"antigravity-cli",status:"n/a",configPath:N};let j;try{j=ag(JSON.parse(ig(N,"utf-8")))}catch(J){return{platform:"antigravity-cli",status:"n/a",configPath:N,errors:[`Failed to parse Antigravity hooks config ${N}: ${J instanceof Error?J.message:String(J)}`]}}if(j.some((J)=>J.enabled))return{platform:"antigravity-cli",status:"configured",method:"hook config",configPath:N};if(j.length>0)return{platform:"antigravity-cli",status:"disabled",method:"hook config",configPath:N};return{platform:"antigravity-cli",status:"n/a",configPath:N}}import{existsSync as pg}from"node:fs";import{join as er}from"node:path";import{existsSync as lg,lstatSync as cg,readFileSync as dg}from"node:fs";import{join as ug}from"node:path";function kn(_,N=(j)=>j){if(!lg(_))return{kind:"missing"};try{return{kind:"ok",value:JSON.parse(N(dg(_,"utf-8")))}}catch{return{kind:"unreadable"}}}function Nn(_,N){if(_==="~")return N;if(_.startsWith("~/")||_.startsWith("~\\"))return ug(N,_.slice(2));return _}function dn(_){try{return cg(_)}catch{return}}function Gr(_,N){let j=dn(N);if(!j)return{platform:_,status:"n/a",configPath:N};if(!j.isSymbolicLink()&&j.isDirectory())return;return{platform:_,status:"n/a",configPath:N,errors:[`${N} is a symlink or not a directory; move or remove it before installing`]}}function nn(_,N){return typeof _==="object"&&_!==null?_[N]:void 0}var Br="cc-safety-net@cc-marketplace";function qr(_){return _.env.get("CLAUDE_CONFIG_DIR")||er(_.home,".claude")}function oi(_){return er(qr(_),"plugins","installed_plugins.json")}function Ya(_,N){let j=nn(nn(_,"plugins"),N);return Array.isArray(j)&&j.length>0}function Vr(_,N){let j=kn(oi(_));return j.kind==="ok"&&Ya(j.value,N)}function Kr(_){let N=oi(_),j=kn(N);if(j.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(j.kind==="missing")return{platform:"claude-code",status:"n/a"};if(!Ya(j.value,Br))return{platform:"claude-code",status:"n/a"};let J=er(qr(_),"settings.json"),Y=kn(J);if(Y.kind==="unreadable")return{platform:"claude-code",status:"not-inspected"};if(!(Y.kind==="ok"&&nn(nn(Y.value,"enabledPlugins"),Br)===!0))return{platform:"claude-code",status:"disabled",method:"plugin config",configPath:J,errors:[`${Br} is installed but not enabled in Claude Code`]};return{platform:"claude-code",status:"configured",method:"plugin config",configPath:N}}function Za(_){if(Kr(_).status!=="configured")return;let N=kn(oi(_)),j=N.kind==="ok"?nn(nn(N.value,"plugins"),Br):void 0,J=(Array.isArray(j)?j:[]).filter((de)=>nn(de,"scope")==="user").map((de)=>nn(de,"installPath")).find((de)=>typeof de==="string");if(!J)return;let Y=er(J,".cursor-plugin","plugin.json"),re=kn(Y),ie=re.kind==="ok"?nn(re.value,"hooks"):void 0;return typeof ie==="string"&&pg(er(J,ie))?Y:void 0}function Xa(_){return Kr(_.environment)}var Qa="Start Codex, open `/hooks`, select the cc-safety-net PreToolUse hook, and press `t` to trust it.";function el(_){if(!_.codexPluginListOutput)return{platform:"codex",status:"n/a"};let N=_.codexPluginListOutput.split(`
|
|
28
|
+
`).find((j)=>j.includes("https://github.com/kenryu42/cc-safety-net.git"));if(!N)return{platform:"codex",status:"n/a"};if(!N.includes("installed,"))return{platform:"codex",status:"n/a"};if(!N.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 Yr,readdirSync as fg,readFileSync as mg}from"node:fs";import{join as wn}from"node:path";function bn(_){let N="",j=0,J=!1,Y=!1,re=-1;while(j<_.length){let ie=_[j],de=_[j+1];if(Y){N+=ie,Y=!1,j++;continue}if(ie==='"'&&!J){J=!0,re=-1,N+=ie,j++;continue}if(ie==='"'&&J){J=!1,N+=ie,j++;continue}if(ie==="\\"&&J){Y=!0,N+=ie,j++;continue}if(J){N+=ie,j++;continue}if(ie==="/"&&de==="/"){while(j<_.length&&_[j]!==`
|
|
29
|
+
`)j++;continue}if(ie==="/"&&de==="*"){j+=2;while(j<_.length-1){if(_[j]==="*"&&_[j+1]==="/"){j+=2;break}j++}continue}if(ie===","){re=N.length,N+=ie,j++;continue}if(ie==="}"||ie==="]"){if(re!==-1){let fe=N.slice(re+1);if(/^\s*$/.test(fe))N=N.slice(0,re)+fe}re=-1,N+=ie,j++;continue}if(!/\s/.test(ie))re=-1;N+=ie,j++}return N}function tl(_,N,j){let J=N+1,Y=!1;while(J<_.length){if(Y){Y=!1,J++;continue}if(_[J]==="\\"){Y=!0,J++;continue}if(_[J]==='"')return J+1;J++}throw Error(j)}function si(_,N,j){let J=_[N],Y=J==="["?"]":"}",re=0,ie=N;while(ie<_.length){let de=j.skipComment?.(_,ie)??ie;if(de!==ie){ie=de;continue}if(_[ie]==='"'){ie=tl(_,ie,j.stringError);continue}if(_[ie]===J)re++;if(_[ie]===Y){if(re--,re===0)return ie}ie++}throw Error(j.bracketError)}function rl(_,N){let j=_.lastIndexOf(`
|
|
30
|
+
`,N)+1;return/^[ \t]*/.exec(_.slice(j))?.[0]??""}function ol(_,N){let j=N.end+(/^\s*/.exec(_.slice(N.end))?.[0].length??0);if(_[j]===","){let ie=_[j+1]===`
|
|
31
|
+
`?j+2:j+1;return`${_.slice(0,N.start)}${_.slice(ie)}`}let J=_.slice(0,N.start).search(/\s*$/)-1;if(_[J]!==",")return`${_.slice(0,N.start)}${_.slice(N.end)}`;let Y=_.lastIndexOf(`
|
|
32
|
+
`,J-1),re=Y!==-1&&/^\s*$/.test(_.slice(Y+1,J))?Y:J;return`${_.slice(0,re)}${_.slice(N.end)}`}function ii(_,N){if(_.startsWith("//",N)){let j=_.indexOf(`
|
|
33
|
+
`,N+2);return j===-1?_.length:j+1}if(_.startsWith("/*",N)){let j=_.indexOf("*/",N+2);return j===-1?_.length:j+2}return N}function nl(_,N){let j=N;while(j<_.length){if(/\s/.test(_[j]??"")){j++;continue}let J=ii(_,j);if(J===j)return j;j=J}return j}function il(_,N,j){let J=0,Y=0;while(Y<_.length){let re=ii(_,Y);if(re!==Y){Y=re;continue}if(_[Y]==='"'){let ie=tl(_,Y,j.stringError);if(J===1&&JSON.parse(_.slice(Y,ie))===N){let de=nl(_,ie),fe=nl(_,de+1);if(_[de]===":"&&_[fe]==="[")return{start:fe,end:si(_,fe,{skipComment:ii,...j})}}Y=ie;continue}if(_[Y]==="{"||_[Y]==="[")J++;if(_[Y]==="}"||_[Y]==="]")J--;Y++}return}var Tn="cc-safety-net@cc-marketplace",Jr=["cc-marketplace","cc-safety-net"],sl=["_direct","copilot-safety-net"],al=["cc-marketplace","safety-net"],ll="safety-net@cc-marketplace";function Wr(_,N){let j=N.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`(^|[^a-z0-9-])${j}([^a-z0-9-]|$)`,"m").test(_??"")}function cl(_){return Wr(_,"cc-safety-net@cc-marketplace")}function dl(_){return Wr(_,"cc-marketplace")}function ul(_){return Wr(_,"copilot-safety-net")}function pl(_){return Wr(_,"safety-net@cc-marketplace")}function zr(_){if(!_?.includes("cc-safety-net"))return!1;return/(^|\s)hook\s+(?:[^\s]+\s+)*(--copilot-cli|-cp)(\s|$)/.test(_)}function ml(_,N){if(!_)return null;let j=_.match(/(\d+)\.(\d+)\.(\d+)/);if(!j)return null;let J=[Number(j[1]),Number(j[2]),Number(j[3])];for(let Y=0;Y<N.length;Y++){let re=J[Y]??0,ie=N[Y]??0;if(re!==ie)return re>ie}return!0}function gg(_){return ml(_,[0,0,422])}function yg(_){return ml(_,[1,0,8])}function tr(_){return _.env.get("COPILOT_HOME")||wn(_.home,".copilot")}function ai(_){return(_.hooks?.preToolUse??[]).some((j)=>{if(j.type!==void 0&&j.type!=="command")return!1;return zr(j.command)||zr(j.bash)||zr(j.powershell)||zr(j.exec&&[j.exec,...j.args??[]].join(" "))})}function nr(_){return _===void 0||typeof _==="string"}function hg(_){return _===void 0||Array.isArray(_)&&_.every((N)=>typeof N==="string")}function vg(_){if(!_||typeof _!=="object"||Array.isArray(_))return!1;let N=_;if(N.disableAllHooks!==void 0&&typeof N.disableAllHooks!=="boolean")return!1;if(N.hooks===void 0)return!0;if(!N.hooks||typeof N.hooks!=="object"||Array.isArray(N.hooks))return!1;let j=N.hooks.preToolUse;if(j===void 0)return!0;return Array.isArray(j)&&j.every((J)=>J!==null&&typeof J==="object"&&!Array.isArray(J)&&nr(J.type)&&nr(J.command)&&nr(J.bash)&&nr(J.powershell)&&nr(J.exec)&&hg(J.args))}function li(_,N){try{let j=JSON.parse(bn(mg(_,"utf-8")));if(!vg(j)){N?.push(`Invalid hook config ${_}: hooks.preToolUse must be an array of hook objects`);return}return j}catch(j){N?.push(`Failed to parse ${_}: ${j instanceof Error?j.message:String(j)}`);return}}function gl(_,N){try{return fg(_).filter((j)=>j.endsWith(".json")).sort((j,J)=>j.localeCompare(J))}catch(j){return N?.push(`Failed to read ${_}: ${j instanceof Error?j.message:String(j)}`),[]}}function bg(_,N){if(!Yr(_))return[];let j=[];for(let J of gl(_,N)){let Y=wn(_,J),re=li(Y,N);if(re&&ai(re))j.push(Y)}return j}function jt(_,N){if(!Yr(_))return;let j=li(_,N);if(!j)return;return{path:_,config:j}}function fl(_,N,j,J){if(N){_.push(`GitHub Copilot CLI ${N} does not support ${j}; requires ${J}+`);return}_.push(`GitHub Copilot CLI version unavailable; skipping ${j} because it requires ${J}+`)}function wg(_){for(let N of _){if(N?.config.disableAllHooks===!0)return N.path;if(N?.config.disableAllHooks===!1)return}return}function kg(_,N,j,J){let Y=tr(_),re=wn(N,".github","hooks"),ie=wn(Y,"hooks"),de=wn(N,".github","copilot"),fe=wn(N,".claude"),ke=yg(j),Ce=ke===!0?J:void 0,Re=[jt(wn(de,"settings.local.json"),Ce),jt(wn(de,"settings.json"),Ce),jt(wn(fe,"settings.local.json"),Ce),jt(wn(fe,"settings.json"),Ce)],Oe=[jt(wn(Y,"settings.json"),Ce),jt(wn(Y,"config.json"),Ce)];if(ke!==!1){let sn=wg([...Re,...Oe]);if(sn){if(ke===null)J.push(`GitHub Copilot CLI version unavailable; treating disableAllHooks in ${sn} as active`);return{activeConfigPaths:[],repoInlineSources:Re,disabledBy:sn}}}let Ee=bg(re,J),Ne=gg(j),Je=Ne===!0?J:void 0,Ge=Yr(ie)?gl(ie,Je):[],Ye=[];for(let sn of Ge){let tn=wn(ie,sn),fn=li(tn,Je);if(fn&&ai(fn))Ye.push(tn)}if(Ne!==!0&&Ye.length>0)fl(J,j,`user hook files in ${ie}`,"0.0.422"),Ye.length=0;let rn=[];for(let sn of[...Re,...Oe]){if(!sn)continue;if(!ai(sn.config))continue;if(ke===!0){rn.push(sn);continue}fl(J,j,"inline hook definitions in Copilot config files","1.0.8");break}let on=(sn)=>sn.filter((tn)=>!!tn&&rn.includes(tn)).map((tn)=>tn.path);return{activeConfigPaths:[...on(Re),...Ee,...on(Oe),...Ye],repoInlineSources:Re}}function yl(_){let N=[],j=kg(_.environment,_.cwd,_.copilotCliVersion,N);if(j.disabledBy)return{platform:"copilot-cli",status:"disabled",method:"hook config",configPath:j.disabledBy,configPaths:[j.disabledBy],errors:N.length>0?N:void 0};let J=tr(_.environment),Y=wn(J,"installed-plugins",...Jr),re=Yr(Y),ie=wn(J,"settings.json"),de=kn(ie,bn),fe=(Ee)=>nn(nn(Ee,"enabledPlugins"),Tn),ke=j.repoInlineSources.find((Ee)=>typeof fe(Ee?.config)==="boolean"),Ce=ke??(de.kind==="ok"?{path:ie,config:de.value}:void 0);if(re&&!ke&&de.kind==="unreadable")return{platform:"copilot-cli",status:"not-inspected"};let Re=re&&Ce!==void 0&&fe(Ce.config)===!1;if(Re&&j.activeConfigPaths.length===0)return{platform:"copilot-cli",status:"disabled",method:"plugin config",configPath:Ce.path,errors:[`${Tn} is installed but not enabled in Copilot CLI`]};let Oe=re&&!Re;if(Oe||j.activeConfigPaths.length>0){let Ee=j.activeConfigPaths[0];return{platform:"copilot-cli",status:"configured",method:Oe?"plugin config":"hook config",configPath:Ee??(Oe?Y:void 0),configPaths:j.activeConfigPaths.length>0?j.activeConfigPaths:void 0,errors:N.length>0?N:void 0}}return{platform:"copilot-cli",status:"n/a",errors:N.length>0?N:void 0}}import{existsSync as yi,readdirSync as Lg,readFileSync as Ng}from"node:fs";import{join as gi}from"node:path";import{existsSync as fi,mkdirSync as _g,readFileSync as Tg,rmSync as Ag}from"node:fs";import{dirname as Og,join as Zr}from"node:path";import{existsSync as Cg,renameSync as xg,statSync as Sg,writeFileSync as Pg}from"node:fs";function ln(_,N){let j=`${_}.${process.pid}.tmp`;Pg(j,N,Cg(_)?{mode:Sg(_).mode&511}:{}),xg(j,_)}import{spawn as Rg}from"node:child_process";function Eg(_){return _.join(" ")}function ci(_,N,j){return[`Failed to run ${Eg(_)}${N===null?"":` (exit ${N})`}.`,j.trim()].filter(Boolean).join(`
|
|
34
|
+
`)}function di(_){let N={stdout:"",stderr:""};return _.stdout.setEncoding("utf-8"),_.stderr.setEncoding("utf-8"),_.stdout.on("data",(j)=>{N.stdout+=j}),_.stderr.on("data",(j)=>{N.stderr+=j}),N}function mn(_,N){return new Promise((j,J)=>{let Y=Zn([..._],process.env),re=Rg(Y.cmd,Y.args,{stdio:["ignore","pipe","pipe"]}),ie=di(re),de=()=>[ie.stdout,ie.stderr].filter(Boolean).join(`
|
|
35
|
+
`),fe=N?.timeoutMs??120000,ke=setTimeout(()=>{re.kill(),J(Error(ci(_,null,`Timed out after ${fe}ms.
|
|
36
|
+
${de()}`.trim())))},fe);re.on("error",(Ce)=>{clearTimeout(ke),J(Error(ci(_,null,`${Ce.message}
|
|
37
|
+
${de()}`.trim())))}),re.on("close",(Ce)=>{if(clearTimeout(ke),Ce!==0){J(Error(ci(_,Ce,de())));return}j(N?.stdoutOnly?ie.stdout:de())})})}async function ui(_){for(let N of _)await mn(N)}async function hl(_){for(let N of _)try{await mn(N)}catch(j){console.warn(j instanceof Error?j.message:String(j))}}var Cn=Object.fromEntries(Yt.map((_)=>[_.id,`npx -y cc-safety-net hook ${_.flags[1]}`]));var or=Cn.cursor,vl=30;function Qr(_){return Zr(_.home,".cursor","hooks.json")}var rr="cc-safety-net";function mi(_){let N=Zr(_.home,".cursor","plugins");return{cache:Zr(N,"cache",rr),marketplaceClone:Zr(N,"marketplaces","github.com","kenryu42","cc-safety-net")}}async function bl(_){let N=Object.values(mi(_));if(!N.some((J)=>fi(J)))return;let j="No marketplace matches";return await mn(["cursor-agent","plugin","marketplace","remove",rr]).catch((J)=>{if(!(J instanceof Error&&J.message.includes(j)))throw J}),N.forEach((J)=>Ag(J,{recursive:!0,force:!0})),[`Removed the ${rr} marketplace from your Cursor account.`,"If /plugins in cursor-agent still lists CC Safety Net as installed, uninstall it there."].join(`
|
|
38
|
+
`)}function Qn(_){return typeof _==="object"&&_!==null&&!Array.isArray(_)}function pi(){return{command:or,timeout:vl,failClosed:!0}}function Xr(_){return Qn(_)&&_.command===or}function Ig(_){return Object.keys(_).length===3&&_.command===or&&_.timeout===vl&&_.failClosed===!0}function $g(_){try{return JSON.parse(Tg(_,"utf-8"))}catch(N){if(N instanceof SyntaxError)throw Error(`Failed to parse Cursor hooks config ${_}: ${N.message}`);throw N}}function wl(_){let N=$g(_);if(!Qn(N))throw Error(`Cursor hooks config ${_} must be a JSON object`);if(N.version!==1)throw Error(`Cursor hooks config ${_} must set "version": 1`);if(N.hooks!==void 0&&!Qn(N.hooks))throw Error(`Cursor hooks config ${_} "hooks" must be an object`);let j=Qn(N.hooks)?N.hooks.preToolUse:void 0;if(j!==void 0&&!Array.isArray(j))throw Error(`Cursor hooks config ${_} "hooks.preToolUse" must be an array`);return N}function kl(_){let N=Qn(_.hooks)?_.hooks.preToolUse:void 0;return Array.isArray(N)?N:[]}function Dg(_){if(!_.some(Xr))return[..._,pi()];return _.reduce((N,j)=>{if(!Xr(j))return N.result.push(j),N;if(!N.inserted)N.result.push(pi()),N.inserted=!0;return N},{result:[],inserted:!1}).result}function Cl(_,N,j){let J=Qn(N.hooks)?N.hooks:{},Y={...N,hooks:{...J,preToolUse:j}};ln(_,`${JSON.stringify(Y,null,2)}
|
|
39
|
+
`)}function xl(_){let N=Qr(_);if(!fi(N))return _g(Og(N),{recursive:!0}),ln(N,`${JSON.stringify({version:1,hooks:{preToolUse:[pi()]}},null,2)}
|
|
40
|
+
`),{path:N,alreadyInstalled:!1};let j=wl(N),J=kl(j),Y=J.filter(Xr);if(Qn(j.hooks)&&Array.isArray(j.hooks.preToolUse)&&Y.length===1&&Y[0]!==void 0&&Ig(Y[0]))return{path:N,alreadyInstalled:!0};return Cl(N,j,Dg(J)),{path:N,alreadyInstalled:!1}}function Sl(_){let N=Qr(_);if(!fi(N))return{path:N,alreadyInstalled:!1};let j=wl(N),J=kl(j),Y=J.filter((re)=>!Xr(re));if(Y.length===J.length)return{path:N,alreadyInstalled:!1};return Cl(N,j,Y),{path:N,alreadyInstalled:!0}}function jg(_){if(!_||typeof _!=="object"||Array.isArray(_))return[];let N=_.hooks;if(!N||typeof N!=="object"||Array.isArray(N))return[];let j=N.preToolUse;if(!Array.isArray(j))return[];return j.filter((J)=>!!J&&typeof J==="object"&&!Array.isArray(J)&&J.command===or)}function Hg(_){let N=[];if(_.length>1)N.push("Multiple managed cc-safety-net hooks found; reinstall to collapse duplicates");let j=_[0];if(j&&j.failClosed!==!0)N.push('Managed hook is missing "failClosed": true; reinstall to repair');if(j&&j.timeout!==30)N.push('Managed hook "timeout" is not 30; reinstall to repair');return N}var eo="Claude Code plugin",hi="Cursor plugin";function Fg(_){let N=gi(mi(_).cache,rr);if(!yi(N))return{};try{return{version:Lg(N).map((j)=>gi(N,j)).find((j)=>yi(gi(j,".cache-complete")))}}catch{return{unreadable:N}}}function Mg(_,N){if(N){let Y=["Cursor records enabled plugins on your Cursor account, which doctor cannot check, and keeps a plugin's files after it is uninstalled.","Cursor pins marketplace plugins to the commit they were added at, so `cc-safety-net update` cannot update this plugin, while the npx hook (`cc-safety-net install --cursor`) stays current."].join(" ");return{method:hi,configPath:N,duplicate:`The Cursor plugin also runs this check, so every tool call is checked twice. ${Y} If the plugin is installed, run \`cc-safety-net uninstall --cursor\`, uninstall CC Safety Net from /plugins in cursor-agent, then run \`cc-safety-net install --cursor\`.`,alone:`${Y} If /plugins in cursor-agent does not list CC Safety Net as installed, run \`cc-safety-net install --cursor\`.`}}let j=Za(_);if(!j)return;let J="Cursor runs the Claude Code plugin only while it imports Claude Code plugins, which doctor cannot check.";return{method:eo,configPath:j,duplicate:`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.`,alone:`${J} If it stops, run \`cc-safety-net install --cursor\`.`}}function Pl(_){let N=Ug(_),j=Fg(_.environment);if(j.unreadable)return N.status==="configured"?{...N,errors:[...N.errors??[],`Cannot read ${j.unreadable}, so doctor cannot tell whether the Cursor plugin also runs this check.`]}:{platform:"cursor",status:"not-inspected"};let J=Mg(_.environment,j.version);if(!J)return N;if(N.status==="configured")return{...N,errors:[...N.errors??[],J.duplicate]};return{platform:"cursor",status:"configured",method:J.method,configPath:J.configPath,errors:[...N.errors??[],J.alone]}}function Ug(_){let N=Qr(_.environment);if(!yi(N))return{platform:"cursor",status:"n/a",configPath:N};let j;try{j=JSON.parse(Ng(N,"utf-8"))}catch(re){return{platform:"cursor",status:"n/a",configPath:N,errors:[`Failed to parse Cursor hooks config ${N}: ${re instanceof Error?re.message:String(re)}`]}}let J=jg(j);if(J.length===0)return{platform:"cursor",status:"n/a",configPath:N};let Y=Hg(J);return{platform:"cursor",status:"configured",method:"hook config",configPath:N,errors:Y.length>0?Y:void 0}}import{readdirSync as Gg}from"node:fs";import{join as vi,resolve as Bg}from"node:path";var bi="cc-safety-net";function wi(_){let N=_.env.get("DSH_HOME");return vi(N?.trim()?Bg(Nn(N,_.home)):vi(_.home,".dsh"),"profiles")}function Rl(_){let N=wi(_),j=dn(N)?.isDirectory()?Gg(N,{withFileTypes:!0}).filter((Y)=>Y.isDirectory()&&Y.name!=="node_modules").map((Y)=>{let re=vi(N,Y.name,"package.json");return{name:Y.name,configPath:re,manifest:kn(re)}}):[];return{installed:j.flatMap((Y)=>{if(Y.manifest.kind!=="ok")return[];let re=Y.manifest.value;if(nn(nn(re,"dependencies"),bi)===void 0)return[];let ie=nn(nn(nn(re,"dsh"),"profile"),"bundles");return[{name:Y.name,configPath:Y.configPath,enabled:Array.isArray(ie)&&ie.includes(bi)}]}),unreadable:j.some((Y)=>Y.manifest.kind==="unreadable")}}function ki(_){return Rl(_).installed}function El(_){let N=Rl(_.environment),j=N.installed.filter((re)=>re.enabled),J=N.installed.filter((re)=>!re.enabled),Y=J.map((re)=>`${bi} is installed in the ${re.name} profile but its bundle is disabled`);if(j.length>0)return{platform:"deepseek-harness",status:"configured",method:"dsh bundle",configPaths:j.map((re)=>re.configPath),...Y.length>0?{errors:Y}:{}};if(J.length>0)return{platform:"deepseek-harness",status:"disabled",method:"dsh bundle",configPaths:J.map((re)=>re.configPath),errors:Y};return N.unreadable?{platform:"deepseek-harness",status:"not-inspected"}:{platform:"deepseek-harness",status:"n/a"}}import{existsSync as zg}from"node:fs";import{existsSync as Al,mkdirSync as qg,readFileSync as Vg}from"node:fs";import{dirname as Kg,join as Si}from"node:path";function Ci(_){return typeof _==="object"&&_!==null&&!Array.isArray(_)}function xi(_,N){return Ci(_)&&_.command===N}function _l(_,N){return Ci(_)&&Array.isArray(_.hooks)&&_.hooks.some((j)=>xi(j,N))}function rt(_,N){return{hooks:[{type:"command",command:_,timeout:N}]}}function jn(_,N){return _.flatMap((j)=>{if(!Ci(j)||!Array.isArray(j.hooks))return[j];let J=j.hooks.filter((Y)=>!xi(Y,N));if(J.length===j.hooks.length)return[j];return J.length===0?[]:[{...j,hooks:J}]})}function Ht(_,N,j){let J=_.filter((Y)=>_l(Y,N));return J.length===1&&JSON.stringify(J[0])===JSON.stringify(rt(N,j))}function Ft(_,N){return _.find((j)=>_l(j,N))}function Mt(_,N,j){let J=Array.isArray(_.hooks)?_.hooks.find((Y)=>xi(Y,N)):void 0;return[..._.matcher===void 0||_.matcher===""||_.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===j?[]:[`Managed hook "timeout" is not ${j}; reinstall to repair`]]}var pt=Cn.devin,no=30,Jg={config:{},hooks:{},preToolUse:[]};function to(_,N=process.platform){let j=N==="win32"?_.env.get("APPDATA")||Si(_.home,"AppData","Roaming"):_.env.get("XDG_CONFIG_HOME")||Si(_.home,".config");return Si(j,"devin","config.json")}function Tl(_){return typeof _==="object"&&_!==null&&!Array.isArray(_)}function Wg(_){try{return{ok:!0,value:JSON.parse(bn(Vg(_,"utf-8")))}}catch(N){return{ok:!1,message:N instanceof Error?N.message:String(N)}}}function Pi(_){let N=Wg(_);if(!N.ok)return`Failed to parse Devin CLI config ${_}: ${N.message}`;let j=N.value;if(!Tl(j))return`Devin CLI config ${_} must be a JSON object`;let J=j.hooks===void 0?{}:j.hooks;if(!Tl(J))return`Devin CLI config ${_} "hooks" must be an object`;let Y=J.PreToolUse===void 0?[]:J.PreToolUse;if(!Array.isArray(Y))return`Devin CLI config ${_} "hooks.PreToolUse" must be an array`;return{config:j,hooks:J,preToolUse:Y}}function Ol(_){let N=Pi(_);if(typeof N==="string")throw Error(N);return N}function Il(_,N,j){let J={...N.config,hooks:{...N.hooks,PreToolUse:j}};ln(_,`${JSON.stringify(J,null,2)}
|
|
41
|
+
`)}function $l(_){let N=to(_),j=Al(N)?Ol(N):Jg;if(Ht(j.preToolUse,pt,no))return{path:N,alreadyInstalled:!0};return qg(Kg(N),{recursive:!0}),Il(N,j,[...jn(j.preToolUse,pt),rt(pt,no)]),{path:N,alreadyInstalled:!1}}function Dl(_){let N=to(_);if(!Al(N))return{path:N,alreadyInstalled:!1};let j=Ol(N),J=jn(j.preToolUse,pt);if(JSON.stringify(J)===JSON.stringify(j.preToolUse))return{path:N,alreadyInstalled:!1};return Il(N,j,J),{path:N,alreadyInstalled:!0}}function Ll(_){let N=to(_.environment);if(!zg(N))return{platform:"devin",status:"n/a",configPath:N};let j=Pi(N);if(typeof j==="string")return{platform:"devin",status:"n/a",configPath:N,errors:[j]};let J=Ft(j.preToolUse,pt);if(!J)return{platform:"devin",status:"n/a",configPath:N};let Y=Mt(J,pt,no);return{platform:"devin",status:"configured",method:"hook config",configPath:N,errors:Y.length>0?Y:void 0}}import{existsSync as ny,readFileSync as ty}from"node:fs";import{existsSync as ro,mkdirSync as Yg,readFileSync as Zg,rmSync as Xg}from"node:fs";import{dirname as Qg,join as Nl}from"node:path";var ft=Cn.droid,oo=30;function io(_){return Nl(_.home,".factory","hooks.json")}function jl(_){return Nl(_.home,".factory","settings.json")}function Ri(_){return typeof _==="object"&&_!==null&&!Array.isArray(_)}function Hl(_){try{return JSON.parse(Zg(_,"utf-8"))}catch(N){if(N instanceof SyntaxError)throw Error(`Failed to parse Factory Droid hooks config ${_}: ${N.message}`);throw N}}function Fl(_){let N=Hl(_);if(!Ri(N))throw Error(`Factory Droid hooks config ${_} must be a JSON object`);if(N.PreToolUse===void 0||Array.isArray(N.PreToolUse))return N;throw Error(`Factory Droid hooks config ${_} "PreToolUse" must be an array`)}function ey(_){if(!ro(_))return{};let N=Hl(_);return Ri(N)&&Ri(N.hooks)?N.hooks:{}}function Ml(_){return Array.isArray(_.PreToolUse)?_.PreToolUse:[]}function Ul(_,N,j){ln(_,`${JSON.stringify({...N,PreToolUse:j},null,2)}
|
|
42
|
+
`)}function Gl(_){let N=io(_),j=ro(N),J=j?Fl(N):ey(jl(_)),Y=Ml(J);if(j&&Ht(Y,ft,oo))return{path:N,alreadyInstalled:!0};return Yg(Qg(N),{recursive:!0}),Ul(N,J,[...jn(Y,ft),rt(ft,oo)]),{path:N,alreadyInstalled:!1}}function Bl(_){let N=io(_);if(!ro(N))return{path:N,alreadyInstalled:!1};let j=Fl(N),J=Ml(j),Y=jn(J,ft);if(JSON.stringify(Y)===JSON.stringify(J))return{path:N,alreadyInstalled:!1};let re=ro(jl(_));if(Y.length===0&&Object.keys(j).length===1&&!re)return Xg(N),{path:N,alreadyInstalled:!0};return Ul(N,j,Y),{path:N,alreadyInstalled:!0}}function ql(_){let N=io(_.environment);if(!ny(N))return{platform:"droid",status:"n/a",configPath:N};let j;try{j=JSON.parse(ty(N,"utf-8"))}catch(ie){return{platform:"droid",status:"n/a",configPath:N,errors:[`Failed to parse Factory Droid hooks config ${N}: ${ie instanceof Error?ie.message:String(ie)}`]}}let J=typeof j==="object"&&j!==null&&"PreToolUse"in j?j.PreToolUse:void 0,Y=Ft(Array.isArray(J)?J:[],ft);if(!Y)return{platform:"droid",status:"n/a",configPath:N};let re=[...Y.commandRegex===void 0||Y.commandRegex===""?[]:['Managed hook has a "commandRegex" that narrows coverage; reinstall to repair'],...Mt(Y,ft,oo)];return{platform:"droid",status:"configured",method:"hook config",configPath:N,errors:re.length>0?re:void 0}}import{existsSync as ry}from"node:fs";import{join as Ei}from"node:path";var _i="gemini-safety-net";function Ti(_){let N=Ei(_.home,".gemini","extensions"),j=Ei(N,_i);if(!ry(j))return{platform:"gemini-cli",status:"n/a"};let J=Ei(N,"extension-enablement.json"),Y=kn(J);if(Y.kind==="unreadable")return{platform:"gemini-cli",status:"not-inspected"};let re=Y.kind==="ok"?nn(nn(Y.value,_i),"overrides"):void 0;if(Array.isArray(re)&&re.some((de)=>typeof de==="string"&&de.startsWith("!")))return{platform:"gemini-cli",status:"disabled",method:"extension config",configPath:J,errors:[`${_i} is disabled in Gemini CLI`]};return{platform:"gemini-cli",status:"configured",method:"extension config",configPath:j}}function Vl(_){return Ti(_.environment)}import{existsSync as ay,readFileSync as ly}from"node:fs";import{existsSync as Jl,mkdirSync as oy,readFileSync as Wl,rmSync as iy}from"node:fs";import{dirname as sy,join as Kl}from"node:path";var mt=Cn["grok-build"],ao=30,Ai=rt(mt,ao);function lo(_){return Kl(_.env.get("GROK_HOME")??Kl(_.home,".grok"),"hooks","cc-safety-net.json")}function co(_){return typeof _==="object"&&_!==null&&!Array.isArray(_)}function zl(_){try{let N=JSON.parse(_);return co(N)?N:null}catch{return null}}function Yl(_){let N=co(_.hooks)?_.hooks.PreToolUse:void 0;return Array.isArray(N)?N:[]}function so(_,N,j){let J=co(N.hooks)?N.hooks:{};ln(_,`${JSON.stringify({...N,hooks:{...J,PreToolUse:j}},null,2)}
|
|
43
|
+
`)}function Zl(_){let N=lo(_);if(!Jl(N))return oy(sy(N),{recursive:!0}),so(N,{},[Ai]),{path:N,alreadyInstalled:!1};let j=zl(Wl(N,"utf-8"));if(!j)return so(N,{},[Ai]),{path:N,alreadyInstalled:!1};let J=Yl(j);if(Ht(J,mt,ao))return{path:N,alreadyInstalled:!0};return so(N,j,[...jn(J,mt),Ai]),{path:N,alreadyInstalled:!1}}function Xl(_){let N=lo(_);if(!Jl(N))return{path:N,alreadyInstalled:!1};let j=zl(Wl(N,"utf-8"));if(!j)return{path:N,alreadyInstalled:!1};let J=Yl(j),Y=jn(J,mt);if(JSON.stringify(Y)===JSON.stringify(J))return{path:N,alreadyInstalled:!1};let re=co(j.hooks)?j.hooks:{};if(Y.length===0&&Object.keys(j).length===1&&Object.keys(re).length===1)return iy(N),{path:N,alreadyInstalled:!0};return so(N,j,Y),{path:N,alreadyInstalled:!0}}function Ql(_){return!!_&&typeof _==="object"&&!Array.isArray(_)}function ec(_){let N=lo(_.environment);if(!ay(N))return{platform:"grok-build",status:"n/a",configPath:N};let j;try{j=JSON.parse(ly(N,"utf-8"))}catch(ie){return{platform:"grok-build",status:"n/a",configPath:N,errors:[`Failed to parse Grok Build hooks config ${N}: ${ie instanceof Error?ie.message:String(ie)}`]}}let J=Ql(j)&&Ql(j.hooks)?j.hooks.PreToolUse:void 0,Y=Ft(Array.isArray(J)?J:[],mt);if(!Y)return{platform:"grok-build",status:"n/a",configPath:N};let re=Mt(Y,mt,ao);return{platform:"grok-build",status:"configured",method:"hook config",configPath:N,errors:re.length>0?re:void 0}}import{readFileSync as cc}from"node:fs";import{join as dc}from"node:path";var Pn="cc-safety-net",Oi="# cc-safety-net managed Hermes Agent plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --hermes-agent",cy=30;function nc(_){return`${Oi}
|
|
44
|
+
# version: ${_}
|
|
45
|
+
`}function dy(_){return`${nc(_)}name: ${Pn}
|
|
46
|
+
version: "${_}"
|
|
47
47
|
description: "Block destructive commands and secret-file access before Hermes runs a tool."
|
|
48
48
|
author: "cc-safety-net"
|
|
49
49
|
provides_hooks:
|
|
50
50
|
- pre_tool_call
|
|
51
|
-
`}function
|
|
51
|
+
`}function uy(_){return`${nc(_)}"""CC Safety Net guard for Hermes Agent.
|
|
52
52
|
|
|
53
53
|
Registers pre_tool_call and forwards the tool call to the packaged CC Safety Net
|
|
54
54
|
adapter (cc-safety-net hook --hermes-agent) over JSON stdin. The adapter prints nothing
|
|
@@ -64,8 +64,8 @@ import subprocess
|
|
|
64
64
|
|
|
65
65
|
HOOK_EVENT = "pre_tool_call"
|
|
66
66
|
SUPPORTED_TOOLS = ("patch", "read_file", "terminal", "write_file")
|
|
67
|
-
ANALYZER = [${Cn["hermes-agent"].split(" ").map((
|
|
68
|
-
TIMEOUT_SECONDS = ${
|
|
67
|
+
ANALYZER = [${Cn["hermes-agent"].split(" ").map((N)=>`"${N}"`).join(", ")}]
|
|
68
|
+
TIMEOUT_SECONDS = ${cy}
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def _block(detail):
|
|
@@ -218,82 +218,82 @@ def _pre_tool_call(tool_name="", args=None, session_id="", task_id="", **_):
|
|
|
218
218
|
|
|
219
219
|
def register(ctx):
|
|
220
220
|
ctx.register_hook("pre_tool_call", _pre_tool_call)
|
|
221
|
-
`}function Wt(P){return[{name:"__init__.py",content:Rg(P)},{name:"plugin.yaml",content:Pg(P)}]}import{mkdirSync as Eg,readdirSync as _g,readFileSync as jl,rmSync as gi}from"node:fs";import{basename as Ag,dirname as Tg,join as Fn}from"node:path";var Ig="__pycache__",$g=/^[a-z0-9][a-z0-9_-]{0,63}$/;function Og(P){try{return jl(P,"utf-8").trim()}catch{return}}function yi(P){let O=P.env.get("HERMES_HOME")?.trim();if(O&&Ag(Tg(O))==="profiles")return O;let D=O||Fn(P.home,".hermes"),j=Fn(D,"active_profile"),J=Og(j),Y=J?.toLowerCase();if(!Y||Y==="default")return D;if(!$g.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 hi(P){return Fn(yi(P),"plugins",Rn)}function vi(P){return P.startsWith(mi)}function bi(P,O){let D=hi(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 Fl(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=jl(P,"utf-8");if(!vi(j))throw Error(`Refusing to ${O} unmanaged file at ${P}. Move or remove it.`);return j}function Hl(P){let O=bi(P,"install"),D=Wt(pn());if(D.map((J)=>Fl(Fn(O,J.name),"overwrite")).every((J,Y)=>J===D[Y]?.content))return{path:O,alreadyInstalled:!0};return Eg(O,{recursive:!0}),D.forEach((J)=>{cn(Fn(O,J.name),J.content)}),{path:O,alreadyInstalled:!1}}function wi(P){let O=bi(P,"remove");if(!dn(O))return[];return Wt(pn()).filter((D)=>Fl(Fn(O,D.name),"remove")!==void 0)}function Ml(P){let O=bi(P,"remove");if(!dn(O))return{path:O,alreadyInstalled:!1};let D=wi(P);if(D.forEach((j)=>{gi(Fn(O,j.name))}),gi(Fn(O,Ig),{recursive:!0,force:!0}),_g(O).length===0)gi(O,{recursive:!0});return{path:O,alreadyInstalled:D.length>0}}var zt="hermes-agent",Ul=/^([^\s#][^:]*):/,Dg=/^\s+([A-Za-z_][\w-]*):/,Gl=/^\s+-\s*(.*)$/;function Lg(P){return P.trim().replace(/^(["'])(.*)\1$/,"$2")}function Ng(P){let O=P.split(/\r?\n/),D=O.findIndex((Y)=>Ul.exec(Y)?.[1]?.trim()==="plugins");if(D===-1)return[];let j=O.slice(D+1),J=j.findIndex((Y)=>Ul.test(Y));return J===-1?j:j.slice(0,J)}function ql(P,O){let D=Ng(P),j=D.findIndex((re)=>Dg.exec(re)?.[1]===O);if(j===-1)return[];let J=D.slice(j+1),Y=J.findIndex((re)=>!Gl.test(re));return(Y===-1?J:J.slice(0,Y)).map((re)=>Lg(Gl.exec(re)?.[1]??""))}function jg(P){try{return Bl(Vl(yi(P),"config.yaml"),"utf-8")}catch{return}}function ki(P){let O=jg(P)??"";return ql(O,"enabled").includes(Rn)&&!ql(O,"disabled").includes(Rn)}function Jl(P){return/^# version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function Fg(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=Bl(P,"utf-8");if(!vi(j))return{error:`Unmanaged ${O.name} occupies ${P}; move or remove it`};if(Jl(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 Hg(P){try{return{path:hi(P)}}catch(O){return{error:O instanceof Error?O.message:String(O)}}}function Kl(P){let O=Hg(P.environment);if("error"in O)return{platform:zt,status:"n/a",errors:[O.error]};let D=O.path,j=Ar(zt,D);if(j)return j;let J=Wt(pn()).map((de)=>Fg(Vl(D,de.name),de)),Y=J.flatMap((de)=>("error"in de)?[de.error]:[]);if(Y.length>0)return{platform:zt,status:"n/a",configPath:D,errors:Y};let re=J.some((de)=>("content"in de)&&Jl(de.content)!==pn()),ie=re?["Installed Hermes Agent plugin is outdated; run install --hermes-agent to update"]:[];if(!ki(P.environment))return{platform:zt,status:"disabled",method:"plugin directory",configPath:D,errors:[`${Rn} is not enabled in Hermes; run \`hermes plugins enable ${Rn}\``,...ie]};return{platform:zt,status:"configured",method:"plugin directory",configPath:D,errors:re?ie:void 0}}import{existsSync as Mg,readFileSync as Ug}from"node:fs";import{join as Wl}from"node:path";var Gg=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-code(\s|["']|$)/;function qg(P){return Wl(P.env.get("KIMI_CODE_HOME")||Wl(P.home,".kimi-code"),"config.toml")}function Yt(P){let O=qg(P.environment);if(!Mg(O))return{platform:"kimi-code",status:"n/a",configPath:O};try{if(!Gg.test(Ug(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 rc}from"node:fs";import{join as Xt}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 Bg,lstatSync as Vg,readdirSync as Jg,readFileSync as Kg}from"node:fs";import{dirname as zl,join as qn}from"node:path";import{fileURLToPath as Wg}from"node:url";var xi=qn("openclaw",un),it=`run \`openclaw plugins enable ${un}\``,zg="config reload superseded by a newer runtime config source",Yg=[Pn,Tt,It];function Yl(P){let O=P.env.get("OPENCLAW_HOME")?.trim();return O?Nn(O,P.home):P.home}function Zl(P){let O=Yl(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?zl(Nn(j,O)):qn(O,".openclaw")}function Xl(P){let O=P.env.get("OPENCLAW_CONFIG_PATH")?.trim();return O?Nn(O,Yl(P)):qn(Zl(P),"openclaw.json")}function Zt(P){return qn(Zl(P),"extensions",un)}function Zg(P){let O=Jg(P);if(O.length===0)return!0;if(O.some((J)=>!Yg.includes(J)))return!1;let D=qn(P,Pn),j=dn(D);return j!==void 0&&!j.isSymbolicLink()&&j.isFile()&&Kg(D,"utf-8").startsWith(Zr)}function Ci(P){let O=Zt(P),D=dn(O);if(!D)return;if(!D.isSymbolicLink()&&D.isDirectory()&&Zg(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 Ql(){let P=zl(Wg(import.meta.url));return[qn(P,xi),qn(P,"..",xi),qn(P,"..","..","..","dist",xi)]}function Si(P=Ql()){return P.find((O)=>Bg(O)&&Vg(O).isDirectory())}function Xg(P=Ql()){let O=Si(P);if(!O)throw Error("Packaged OpenClaw plugin directory not found. Reinstall cc-safety-net and try again.");return O}function ec(P=Xg()){return[["openclaw","plugins","install",P,"--force","--accept-capabilities"]]}function Qg(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 ey(){await gn(["openclaw","plugins","enable",un]).catch((P)=>{if(!(P instanceof Error&&P.message.includes(zg)))throw P})}async function nc(P){let O=async()=>Qg(await gn(["openclaw","plugins","inspect",un,"--runtime","--json"],{stdoutOnly:!0})),D=await O(),j=D==="disabled"&&P;if(j)await ey();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 Xr="openclaw";function $t(P,O){let D=Xt(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:rc(D,"utf-8")}}catch(J){return{error:`Failed to read ${D}: ${J instanceof Error?J.message:String(J)}`}}}function oc(P){try{return JSON.parse(vn(P))}catch{return}}function ny(P){let O=$t(P,Tt);if("error"in O)return O.error;if(tn(oc(O.content),"id")===un)return;return`${Xt(P,Tt)} is not a valid ${un} manifest; run install --openclaw`}function ty(P){let O=$t(P,It);if("error"in O)return O.error;let D=tn(tn(oc(O.content),"openclaw"),"extensions");if(Array.isArray(D)&&D.includes(`./${Pn}`))return;return`${Xt(P,It)} does not point OpenClaw at ${Pn}; run install --openclaw`}function tc(P){return Array.isArray(P)?P.filter((O)=>typeof O==="string"):[]}function ry(P){let O=Xl(P);if(!dn(O))return`${un} is not enabled; ${it}`;let D=(()=>{try{return JSON.parse(vn(rc(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(tc(tn(j,"deny")).includes(un)||J===!1)return`${un} is disabled in ${O}; ${it}`;let Y=tc(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 ic(P){return/^\/\/ version:\s*(.+)$/m.exec(P)?.[1]?.trim()}function oy(P,O,D){if(D===void 0)return[];let j=$t(D,Pn);if(!(("content"in j)&&ic(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 ${Xt(P,Y)}; run install --openclaw to restore it`]})}function sc(P){let O=Zt(P.environment),D=Ar(Xr,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 ${Xt(O,Pn)}; move or remove it`,ny(O),ty(O)].filter((we)=>we!==void 0),re="content"in j?ic(j.content):void 0,ie=Y.length>0?Y:oy(O,re,Si());if(ie.length>0)return{platform:Xr,status:"n/a",configPath:O,errors:ie};let de=re===pn()?[]:["Installed OpenClaw plugin is outdated; run install --openclaw to update"],fe=ry(P.environment);if(fe)return{platform:Xr,status:"disabled",method:"plugin directory",configPath:O,errors:[fe,...de]};return{platform:Xr,status:"configured",method:"plugin directory",configPath:O,errors:de.length>0?de:void 0}}import{existsSync as fy,readFileSync as my}from"node:fs";import{basename as gy}from"node:path";import{existsSync as Qr,readFileSync as Pi,rmSync as iy}from"node:fs";import{join as Tn}from"node:path";import{pathToFileURL as sy}from"node:url";var Qt="cc-safety-net",pt=`${Qt}@latest`,Ri=["opencode.json","opencode.jsonc"],ay=60,ly=250,ac="CCSafetyNetPlugin",cy={stringError:"Unterminated string in OpenCode config",bracketError:"Unmatched plugin array in OpenCode config"};function lc(P){return Tn(P.env.get("XDG_CONFIG_HOME")||Tn(P.home,".config"),"opencode")}function Ei(P){return P.env.get("OPENCODE_CONFIG_DIR")||lc(P)}function _i(P){return Ri.map((O)=>Tn(Ei(P),O))}function Ai(P){return[...new Set([Ei(P),lc(P)])].flatMap((O)=>Ri.map((D)=>Tn(O,D)))}function cc(P){return Tn(P.env.get("XDG_CACHE_HOME")||Tn(P.home,".cache"),"opencode","packages",pt)}function dc(P){iy(cc(P),{recursive:!0,force:!0})}async function uc(P){let O=(await gn(["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 _i(P)){if(!Qr(re))continue;let ie=Ii(Pi(re,"utf-8"),re);if(["plugin","plugins"].some((fe)=>{let we=tn(ie,fe);return Array.isArray(we)&&we.some((Ce)=>eo(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 gn(["opencode","plugin","add",pt],{stdoutOnly:!0})).includes("is already configured in"))await gn(["opencode","plugin","update",pt]);let ie=await mc();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 gn(["opencode","api","integration.list",...de]);let fe=await gn(["opencode","api","plugin.list",...de],{stdoutOnly:!0}),we=Ti(fe);if(we)throw Error(we);if(!pc(fe).some(fc))throw Error("OpenCode lists no active cc-safety-net for this directory. Run `opencode api plugin.list` for details.")}}}return dc(P),{commands:[["opencode","plugin","-g","-f",pt]],afterInstall:()=>uy(P)}}function pc(P){return dy(P).filter((O)=>tn(O,"id")===Qt||eo(tn(tn(O,"source"),"target"))).map((O)=>tn(O,"state"))}function fc(P){return tn(P,"status")==="active"}function Ti(P){let O=pc(P);if(O.some(fc))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(`
|
|
222
|
-
`)[0]}`}function
|
|
223
|
-
`));return{path:J[0]??D??Tn(Ei(P),Ri[0]),alreadyInstalled:J.length>0}}function Ot(P){let O=[];for(let D of P.openCodeVersion?.startsWith("2.")?_i(P.environment):Ai(P.environment))if(fy(D))try{let j=my(D,"utf-8"),J=vn(j),Y=JSON.parse(J);if($i(Y)){let re=Ti(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 ${gy(D)}: ${j instanceof Error?j.message:String(j)}`)}return{platform:"opencode",status:"n/a",errors:O.length>0?O:void 0}}import{join as yc}from"node:path";function Oi(P){let O=P.env.get("PI_CODING_AGENT_DIR");return yc(O?Nn(O,P.home):yc(P.home,".pi","agent"),"settings.json")}function Di(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function hc(P){let O=Oi(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)=>Di(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 yy={amp:Aa,"antigravity-cli":Ta,"claude-code":Oa,codex:La,"copilot-cli":Za,cursor:sl,"deepseek-harness":ll,devin:yl,droid:Pl,"gemini-cli":Rl,"grok-build":Ll,"hermes-agent":Kl,"kimi-code":Yt,openclaw:sc,opencode:Ot,pi:hc};function Dt(P,O,D){let j={...D,cwd:O,environment:P};return gr.map((J)=>hy(yy[J](j)))}function hy(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 vy}from"node:path";var by=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}]),wy=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([])})})}),ky={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 vc(P){let O=vy(P.tmpdir,"cc-safety-net-self-test"),D=by.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:()=>wy,getModes:()=>ky,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 Li(P){let O=yn({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 bc(P,O={}){let D=await Ut(!O.json,()=>{let j=Cy(P,O);return{ready:j,finish:()=>j}},()=>Mt(),{loadingMessage:"Checking system status…"});if(O.json)console.log(JSON.stringify(D,null,2));else Sy(D);return D.engineSelfTest.failed>0||D.findings.some((j)=>j.severity==="error")?1:0}async function Cy(P,O){let D=O.cwd??process.cwd(),j=await Sr((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=Vs(P,D),re=Js(P),ie=k(P,{cwd:D}),de=ie.policy,fe=T(de,P.env),we=V(de,fe.capabilities),Ce=br(P,7),Se=Sa(P,D),Ie=[wr(D),Ct(P)].filter((Me)=>xy(Me)),Ee=O.skipUpdateCheck?{currentVersion:pn(),latestVersion:null,updateAvailable:!1}:await Gn(),qe={hooks:J,engineSelfTest:vc(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:sa(P,Y.userConfig.path),activity:Ce,update:Ee,system:j};return{...qe,findings:Ws(qe)}}function Sy(P){console.log(),console.log(Ys(P.hooks)),console.log(),console.log(Zs(P.engineSelfTest)),console.log(),console.log(Xs(P)),console.log(),console.log(Qs(P.environment)),console.log(),console.log(ea(P)),console.log(),console.log(na(P.findings)),console.log(),console.log(ta(P.activity)),console.log(),console.log(oa(P.system)),console.log(),console.log(ra(P.update)),console.log(ia(P))}import{existsSync as Py}from"node:fs";var Ry=/^[A-Za-z0-9_@%+=:,./-]+$/,wc="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function Ni(P){let O=yn({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Dn(O.errors))return console.error(wc),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)=>Ry.test(j)?j:`'${j.replaceAll("'","'\\''")}'`).join(" ");if(!D)return console.error("Error: No command provided"),console.error(wc),null;return{json:O.flags.json,cwd:O.values.cwd,command:D}}function kc(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 xc(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 ji(P){return JSON.stringify(P)}function Cc(P,O=0){return`[${P.map((j,J)=>zs(j,J,O)).join(",")}]`}function er(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 Sc(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: ${ji(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: ${ji(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: ${ji(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 Fi(P,O){let D=kc(O?.asciiOnly??!1),j=58,J=[],Y=1;J.push(...xc(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(`
|
|
224
|
-
`)}let
|
|
225
|
-
`)}function
|
|
226
|
-
`,()=>
|
|
227
|
-
`))}function
|
|
228
|
-
`))}function
|
|
229
|
-
`)}var
|
|
230
|
-
`)}function
|
|
231
|
-
`),
|
|
232
|
-
`).length};return new Promise((
|
|
233
|
-
`);return}
|
|
234
|
-
`)})}),
|
|
221
|
+
`}function ir(_){return[{name:"__init__.py",content:uy(_)},{name:"plugin.yaml",content:dy(_)}]}import{mkdirSync as py,readdirSync as fy,readFileSync as tc,rmSync as Ii}from"node:fs";import{basename as my,dirname as gy,join as Hn}from"node:path";var yy="__pycache__",hy=/^[a-z0-9][a-z0-9_-]{0,63}$/;function vy(_){try{return tc(_,"utf-8").trim()}catch{return}}function $i(_){let N=_.env.get("HERMES_HOME")?.trim();if(N&&my(gy(N))==="profiles")return N;let j=N||Hn(_.home,".hermes"),J=Hn(j,"active_profile"),Y=vy(J),re=Y?.toLowerCase();if(!re||re==="default")return j;if(!hy.test(re))throw Error(`Invalid Hermes profile name "${Y}" in ${J}; run \`hermes profile use <name>\` with a valid profile.`);return Hn(j,"profiles",re)}function Di(_){return Hn($i(_),"plugins",Pn)}function Li(_){return _.startsWith(Oi)}function Ni(_,N){let j=Di(_),J=dn(j);if(J&&(J.isSymbolicLink()||!J.isDirectory()))throw Error(`Refusing to ${N} ${j}: not a regular directory. Move or remove it and rerun ${N==="install"?"install":"uninstall"} --hermes-agent.`);return j}function rc(_,N){let j=dn(_);if(!j)return;if(j.isSymbolicLink()||!j.isFile())throw Error(`Refusing to ${N} ${_}: not a regular file. Move or remove it.`);let J=tc(_,"utf-8");if(!Li(J))throw Error(`Refusing to ${N} unmanaged file at ${_}. Move or remove it.`);return J}function oc(_){let N=Ni(_,"install"),j=ir(pn());if(j.map((Y)=>rc(Hn(N,Y.name),"overwrite")).every((Y,re)=>Y===j[re]?.content))return{path:N,alreadyInstalled:!0};return py(N,{recursive:!0}),j.forEach((Y)=>{ln(Hn(N,Y.name),Y.content)}),{path:N,alreadyInstalled:!1}}function ji(_){let N=Ni(_,"remove");if(!dn(N))return[];return ir(pn()).filter((j)=>rc(Hn(N,j.name),"remove")!==void 0)}function ic(_){let N=Ni(_,"remove");if(!dn(N))return{path:N,alreadyInstalled:!1};let j=ji(_);if(j.forEach((J)=>{Ii(Hn(N,J.name))}),Ii(Hn(N,yy),{recursive:!0,force:!0}),fy(N).length===0)Ii(N,{recursive:!0});return{path:N,alreadyInstalled:j.length>0}}var sr="hermes-agent",sc=/^([^\s#][^:]*):/,by=/^\s+([A-Za-z_][\w-]*):/,ac=/^\s+-\s*(.*)$/;function wy(_){return _.trim().replace(/^(["'])(.*)\1$/,"$2")}function ky(_){let N=_.split(/\r?\n/),j=N.findIndex((re)=>sc.exec(re)?.[1]?.trim()==="plugins");if(j===-1)return[];let J=N.slice(j+1),Y=J.findIndex((re)=>sc.test(re));return Y===-1?J:J.slice(0,Y)}function lc(_,N){let j=ky(_),J=j.findIndex((ie)=>by.exec(ie)?.[1]===N);if(J===-1)return[];let Y=j.slice(J+1),re=Y.findIndex((ie)=>!ac.test(ie));return(re===-1?Y:Y.slice(0,re)).map((ie)=>wy(ac.exec(ie)?.[1]??""))}function Cy(_){try{return cc(dc($i(_),"config.yaml"),"utf-8")}catch{return}}function Hi(_){let N=Cy(_)??"";return lc(N,"enabled").includes(Pn)&&!lc(N,"disabled").includes(Pn)}function uc(_){return/^# version:\s*(.+)$/m.exec(_)?.[1]?.trim()}function xy(_,N){let j=dn(_);if(!j)return{error:`${N.name} is missing from ${_}; run install --hermes-agent`};if(j.isSymbolicLink()||!j.isFile())return{error:`${_} is a symlink or not a regular file; move or remove it`};try{let J=cc(_,"utf-8");if(!Li(J))return{error:`Unmanaged ${N.name} occupies ${_}; move or remove it`};if(uc(J)===pn()&&J!==N.content)return{error:`Modified ${N.name} occupies ${_}; run install --hermes-agent to restore it`};return{content:J}}catch(J){return{error:`Failed to read ${_}: ${J instanceof Error?J.message:String(J)}`}}}function Sy(_){try{return{path:Di(_)}}catch(N){return{error:N instanceof Error?N.message:String(N)}}}function pc(_){let N=Sy(_.environment);if("error"in N)return{platform:sr,status:"n/a",errors:[N.error]};let j=N.path,J=Gr(sr,j);if(J)return J;let Y=ir(pn()).map((fe)=>xy(dc(j,fe.name),fe)),re=Y.flatMap((fe)=>("error"in fe)?[fe.error]:[]);if(re.length>0)return{platform:sr,status:"n/a",configPath:j,errors:re};let ie=Y.some((fe)=>("content"in fe)&&uc(fe.content)!==pn()),de=ie?["Installed Hermes Agent plugin is outdated; run install --hermes-agent to update"]:[];if(!Hi(_.environment))return{platform:sr,status:"disabled",method:"plugin directory",configPath:j,errors:[`${Pn} is not enabled in Hermes; run \`hermes plugins enable ${Pn}\``,...de]};return{platform:sr,status:"configured",method:"plugin directory",configPath:j,errors:ie?de:void 0}}import{existsSync as Py,readFileSync as Ry}from"node:fs";import{join as fc}from"node:path";var Ey=/cc-safety-net\s+hook\s+(?:[^\s]+\s+)*--kimi-code(\s|["']|$)/;function _y(_){return fc(_.env.get("KIMI_CODE_HOME")||fc(_.home,".kimi-code"),"config.toml")}function ar(_){let N=_y(_.environment);if(!Py(N))return{platform:"kimi-code",status:"n/a",configPath:N};try{if(!Ey.test(Ry(N,"utf-8")))return{platform:"kimi-code",status:"n/a",configPath:N}}catch(j){return{platform:"kimi-code",status:"n/a",configPath:N,errors:[`Failed to read ${N}: ${j instanceof Error?j.message:String(j)}`]}}return{platform:"kimi-code",status:"configured",method:"hook config",configPath:N}}import{readFileSync as Cc}from"node:fs";import{join as cr}from"node:path";var un="cc-safety-net",Sn="index.js",Ut="openclaw.plugin.json",Gt="package.json";var uo="// cc-safety-net managed OpenClaw plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --openclaw";import{existsSync as Ty,lstatSync as Ay,readdirSync as Oy,readFileSync as Iy}from"node:fs";import{dirname as mc,join as Bn}from"node:path";import{fileURLToPath as $y}from"node:url";var Fi=Bn("openclaw",un),ht=`run \`openclaw plugins enable ${un}\``,Dy="config reload superseded by a newer runtime config source",Ly=[Sn,Ut,Gt];function gc(_){let N=_.env.get("OPENCLAW_HOME")?.trim();return N?Nn(N,_.home):_.home}function yc(_){let N=gc(_),j=_.env.get("OPENCLAW_STATE_DIR")?.trim();if(j)return Nn(j,N);let J=_.env.get("OPENCLAW_CONFIG_PATH")?.trim();return J?mc(Nn(J,N)):Bn(N,".openclaw")}function hc(_){let N=_.env.get("OPENCLAW_CONFIG_PATH")?.trim();return N?Nn(N,gc(_)):Bn(yc(_),"openclaw.json")}function lr(_){return Bn(yc(_),"extensions",un)}function Ny(_){let N=Oy(_);if(N.length===0)return!0;if(N.some((Y)=>!Ly.includes(Y)))return!1;let j=Bn(_,Sn),J=dn(j);return J!==void 0&&!J.isSymbolicLink()&&J.isFile()&&Iy(j,"utf-8").startsWith(uo)}function Mi(_){let N=lr(_),j=dn(N);if(!j)return;if(!j.isSymbolicLink()&&j.isDirectory()&&Ny(N))return;throw Error(`Refusing to modify ${N}: it does not hold a cc-safety-net managed OpenClaw plugin. Move or remove it, then run the command again.`)}function vc(){let _=mc($y(import.meta.url));return[Bn(_,Fi),Bn(_,"..",Fi),Bn(_,"..","..","..","dist",Fi)]}function Ui(_=vc()){return _.find((N)=>Ty(N)&&Ay(N).isDirectory())}function jy(_=vc()){let N=Ui(_);if(!N)throw Error("Packaged OpenClaw plugin directory not found. Reinstall cc-safety-net and try again.");return N}function bc(_=jy()){return[["openclaw","plugins","install",_,"--force","--accept-capabilities"]]}function Hy(_){let N=(()=>{try{return JSON.parse(_)}catch{return}})(),j=nn(nn(N,"plugin"),"status");return typeof j==="string"?j:void 0}async function Fy(){await mn(["openclaw","plugins","enable",un]).catch((_)=>{if(!(_ instanceof Error&&_.message.includes(Dy)))throw _})}async function wc(_){let N=async()=>Hy(await mn(["openclaw","plugins","inspect",un,"--runtime","--json"],{stdoutOnly:!0})),j=await N(),J=j==="disabled"&&_;if(J)await Fy();let Y=J?await N():j;if(Y==="loaded")return;throw Error(`${Y===void 0?`The ${un} plugin's load state could not be verified: OpenClaw's runtime inspect report was unreadable.`:Y==="disabled"?`OpenClaw reports the ${un} plugin with status "disabled"; ${ht}.`:`OpenClaw reports the ${un} plugin with status "${Y}".`} Run \`openclaw plugins inspect ${un} --runtime\` for details.`)}var po="openclaw";function Bt(_,N){let j=cr(_,N),J=dn(j);if(!J)return{error:`${N} is missing from ${j}; run install --openclaw`};if(J.isSymbolicLink()||!J.isFile())return{error:`${j} is a symlink or not a regular file; move or remove it`};try{return{content:Cc(j,"utf-8")}}catch(Y){return{error:`Failed to read ${j}: ${Y instanceof Error?Y.message:String(Y)}`}}}function xc(_){try{return JSON.parse(bn(_))}catch{return}}function My(_){let N=Bt(_,Ut);if("error"in N)return N.error;if(nn(xc(N.content),"id")===un)return;return`${cr(_,Ut)} is not a valid ${un} manifest; run install --openclaw`}function Uy(_){let N=Bt(_,Gt);if("error"in N)return N.error;let j=nn(nn(xc(N.content),"openclaw"),"extensions");if(Array.isArray(j)&&j.includes(`./${Sn}`))return;return`${cr(_,Gt)} does not point OpenClaw at ${Sn}; run install --openclaw`}function kc(_){return Array.isArray(_)?_.filter((N)=>typeof N==="string"):[]}function Gy(_){let N=hc(_);if(!dn(N))return`${un} is not enabled; ${ht}`;let j=(()=>{try{return JSON.parse(bn(Cc(N,"utf-8")))}catch{return}})();if(j===void 0)return`Failed to read ${N}; fix it, then ${ht}`;let J=nn(j,"plugins");if(nn(J,"enabled")===!1)return`plugins.enabled is false in ${N}; no OpenClaw plugin loads`;let Y=nn(nn(nn(J,"entries"),un),"enabled");if(kc(nn(J,"deny")).includes(un)||Y===!1)return`${un} is disabled in ${N}; ${ht}`;let re=kc(nn(J,"allow"));if(re.length>0&&!re.includes(un))return`plugins.allow in ${N} does not list ${un}; add it, then ${ht}`;if(re.includes(un)||Y===!0)return;return`${un} is not enabled; ${ht}`}function Sc(_){return/^\/\/ version:\s*(.+)$/m.exec(_)?.[1]?.trim()}function By(_,N,j){if(j===void 0)return[];let J=Bt(j,Sn);if(!(("content"in J)&&Sc(J.content)===N))return[];return[Sn,Ut,Gt].flatMap((re)=>{let ie=Bt(_,re),de=Bt(j,re);if("error"in ie||"error"in de||ie.content===de.content)return[];return[`Modified ${re} occupies ${cr(_,re)}; run install --openclaw to restore it`]})}function Pc(_){let N=lr(_.environment),j=Gr(po,N);if(j)return j;let J=Bt(N,Sn),re=["error"in J?J.error:J.content.startsWith(uo)?void 0:`Unmanaged ${Sn} occupies ${cr(N,Sn)}; move or remove it`,My(N),Uy(N)].filter((Ce)=>Ce!==void 0),ie="content"in J?Sc(J.content):void 0,de=re.length>0?re:By(N,ie,Ui());if(de.length>0)return{platform:po,status:"n/a",configPath:N,errors:de};let fe=ie===pn()?[]:["Installed OpenClaw plugin is outdated; run install --openclaw to update"],ke=Gy(_.environment);if(ke)return{platform:po,status:"disabled",method:"plugin directory",configPath:N,errors:[ke,...fe]};return{platform:po,status:"configured",method:"plugin directory",configPath:N,errors:fe.length>0?fe:void 0}}import{existsSync as Xy,readFileSync as Qy}from"node:fs";import{basename as eh}from"node:path";import{existsSync as fo,readFileSync as Gi,rmSync as qy}from"node:fs";import{join as An}from"node:path";import{pathToFileURL as Vy}from"node:url";var dr="cc-safety-net",bt=`${dr}@latest`,Bi=["opencode.json","opencode.jsonc"],Ky=60,Jy=250,Rc="CCSafetyNetPlugin",Wy={stringError:"Unterminated string in OpenCode config",bracketError:"Unmatched plugin array in OpenCode config"};function Ec(_){return An(_.env.get("XDG_CONFIG_HOME")||An(_.home,".config"),"opencode")}function qi(_){return _.env.get("OPENCODE_CONFIG_DIR")||Ec(_)}function Vi(_){return Bi.map((N)=>An(qi(_),N))}function Ki(_){return[...new Set([qi(_),Ec(_)])].flatMap((N)=>Bi.map((j)=>An(N,j)))}function _c(_){return An(_.env.get("XDG_CACHE_HOME")||An(_.home,".cache"),"opencode","packages",bt)}function Tc(_){qy(_c(_),{recursive:!0,force:!0})}async function Ac(_){let N=(await mn(["opencode","--version"],{stdoutOnly:!0})).trim(),j=/^(?:opencode\s+)?v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(N),J=Number(j?.[1]),Y=Number(j?.[2]),re=Number(j?.[3]);if(!j||J!==1&&J!==2||J===1&&(Y<18||Y===18&&re<29)||J===2&&Y===0&&re<6)throw Error(`OpenCode 1.18.29+ or 2.0.6+ is required; found ${N||"an unknown version"}.`);if(J===2){for(let ie of Vi(_)){if(!fo(ie))continue;let de=Wi(Gi(ie,"utf-8"),ie);if(["plugin","plugins"].some((ke)=>{let Ce=nn(de,ke);return Array.isArray(Ce)&&Ce.some((Re)=>mo(Re)&&(typeof Re==="string"?Re:nn(Re,"package"))!==bt)}))throw Error(`Change the cc-safety-net package spec in ${ie} to ${bt}, preserving its options, then retry. Adding another spec would create a duplicate plugin ID.`)}return{commands:[],afterInstall:async()=>{if((await mn(["opencode","plugin","add",bt],{stdoutOnly:!0})).includes("is already configured in"))await mn(["opencode","plugin","update",bt]);let de=await $c();if(!/^cc-safety-net\s+\S+\s+cc-safety-net@latest\s*$/m.test(de))throw Error("OpenCode did not load cc-safety-net from cc-safety-net@latest. Run `opencode plugin list` for details.");let fe=["--param",`location[directory]=${process.cwd()}`];await mn(["opencode","api","integration.list",...fe]);let ke=await mn(["opencode","api","plugin.list",...fe],{stdoutOnly:!0}),Ce=Ji(ke);if(Ce)throw Error(Ce);if(!Oc(ke).some(Ic))throw Error("OpenCode lists no active cc-safety-net for this directory. Run `opencode api plugin.list` for details.")}}}return Tc(_),{commands:[["opencode","plugin","-g","-f",bt]],afterInstall:()=>Yy(_)}}function Oc(_){return zy(_).filter((N)=>nn(N,"id")===dr||mo(nn(nn(N,"source"),"target"))).map((N)=>nn(N,"state"))}function Ic(_){return nn(_,"status")==="active"}function Ji(_){let N=Oc(_);if(N.some(Ic))return;let j=N.find((J)=>nn(J,"status")==="failed");if(!j)return;return`OpenCode reports cc-safety-net failed: ${String(nn(j,"error")).split(`
|
|
222
|
+
`)[0]}`}function zy(_){if(!_)return[];try{let N=nn(JSON.parse(_),"data");return Array.isArray(N)?N:[]}catch{return[]}}async function $c(_=1){let N=await mn(["opencode","plugin","list"],{stdoutOnly:!0});if(/^cc-safety-net\s|\scc-safety-net@latest\s*$/m.test(N)||_===Ky)return N;return await new Promise((j)=>setTimeout(j,Jy)),$c(_+1)}async function Yy(_){let N=An(_c(_),"node_modules",dr),j=An(N,"package.json");if(!fo(j))throw Error(`The OpenCode plugin cache at ${N} is missing its package, so OpenCode would load nothing and fail open. Run \`opencode plugin -g -f ${bt}\` for details.`);let J=nn(JSON.parse(Gi(j,"utf-8")),"main");if(typeof J!=="string")throw Error(`The cached OpenCode plugin at ${N} declares no "main" entry.`);let Y=An(N,J);if(typeof(await import(Vy(Y).href))[Rc]==="function")return;throw Error(`The cached OpenCode plugin at ${Y} does not export a callable ${Rc}, so OpenCode would load nothing and fail open.`)}function Wi(_,N){try{return JSON.parse(bn(_))}catch(j){if(j instanceof SyntaxError)throw Error(`Failed to parse OpenCode config ${N}: ${j.message}`);throw j}}function mo(_){let N=typeof _==="string"?_:nn(_,"package");return typeof N==="string"&&(N===dr||N.startsWith(`${dr}@`))}function zi(_){return["plugin","plugins"].some((N)=>{let j=nn(_,N);return Array.isArray(j)&&j.some(mo)})}function Zy(_,N){let J=["plugin","plugins"].flatMap((Y)=>{let re=il(_,Y,Wy);if(!re)return[];let ie=[],de=0,fe=re.start+1,ke=_.slice(re.start+1,re.end).matchAll(/\/\/[^\n]*|\/\*[\s\S]*?\*\/|"(?:\\.|[^"\\])*"|[^"\s{}[\],/]+|[{}[\],]/g);for(let Ce of ke){if(Ce[0].startsWith("//")||Ce[0].startsWith("/*"))continue;let Re=re.start+1+Ce.index;if(de===0)fe=Re;if(Ce[0]==="{"||Ce[0]==="[")de++;if(Ce[0]==="}"||Ce[0]==="]")de--;if(de!==0||Ce[0]===",")continue;let Oe=Re+Ce[0].length;if(mo(JSON.parse(bn(_.slice(fe,Oe)))))ie.push({start:fe,end:Oe})}return ie}).sort((Y,re)=>Y.start-re.start).reverse().reduce((Y,re)=>{let ie=/^(?:\s|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*,/.exec(Y.slice(re.end));if(ie?.[0].includes("/")){let de=re.end+ie[0].length-1;return Y.slice(0,re.start)+Y.slice(re.end,de)+Y.slice(de+1)}return ol(Y,re)},_);return Wi(J,N),J}function Dc(_){Tc(_);let N=Ki(_),j=N.find((re)=>fo(re)),J=[],Y=[];for(let re of N){if(!fo(re))continue;try{let ie=Gi(re,"utf-8");if(!zi(Wi(ie,re)))continue;ln(re,Zy(ie,re)),Y.push(re)}catch(ie){J.push(ie instanceof Error?ie.message:String(ie))}}if(J.length>0)throw Error(J.join(`
|
|
223
|
+
`));return{path:Y[0]??j??An(qi(_),Bi[0]),alreadyInstalled:Y.length>0}}function qt(_){let N=[];for(let j of _.openCodeVersion?.startsWith("2.")?Vi(_.environment):Ki(_.environment))if(Xy(j))try{let J=Qy(j,"utf-8"),Y=bn(J),re=JSON.parse(Y);if(zi(re)){let ie=Ji(_.openCodePluginListOutput);if(ie)return{platform:"opencode",status:"disabled",method:"opencode api plugin.list",configPath:j,errors:[...N,ie]};return{platform:"opencode",status:"configured",method:"plugin array",configPath:j,errors:N.length>0?N:void 0}}}catch(J){N.push(`Failed to parse ${eh(j)}: ${J instanceof Error?J.message:String(J)}`)}return{platform:"opencode",status:"n/a",errors:N.length>0?N:void 0}}import{join as Lc}from"node:path";function Yi(_){let N=_.env.get("PI_CODING_AGENT_DIR");return Lc(N?Nn(N,_.home):Lc(_.home,".pi","agent"),"settings.json")}function Zi(_){if(typeof _!=="string")return!1;return _==="npm:cc-safety-net"||_.startsWith("npm:cc-safety-net@")}function Nc(_){let N=Yi(_.environment),j=kn(N);if(j.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(j.kind==="missing")return{platform:"pi",status:"n/a"};let J=nn(j.value,"packages");if(!Array.isArray(J))return{platform:"pi",status:"n/a"};let Y=J.find((de)=>Zi(typeof de==="string"?de:nn(de,"source")));if(Y===void 0)return{platform:"pi",status:"n/a"};let re=nn(Y,"extensions");if(Array.isArray(re)&&re.some((de)=>typeof de==="string"&&de.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:N,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:N}}var nh={amp:Ja,"antigravity-cli":za,"claude-code":Xa,codex:el,"copilot-cli":yl,cursor:Pl,"deepseek-harness":El,devin:Ll,droid:ql,"gemini-cli":Vl,"grok-build":ec,"hermes-agent":pc,"kimi-code":ar,openclaw:Pc,opencode:qt,pi:Nc};function Vt(_,N,j){let J={...j,cwd:N,environment:_};return Rr.map((Y)=>th(nh[Y](J)))}function th(_){if(_.status==="not-inspected")return{platform:_.platform,detected:!1,configured:!1,inspectionStatus:"not-inspected"};return{platform:_.platform,detected:_.status!=="n/a",configured:_.status==="configured",inspectionStatus:_.status!=="n/a"?"verified":_.errors&&_.errors.length>0?"failed":"not-applicable",method:_.method,configPath:_.configPath,configPaths:_.configPaths,errors:_.errors}}import{join as rh}from"node:path";var oh=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}]),ih=Object.freeze({state:"ready",diagnostics:Object.freeze([]),ruleMetadata:Object.freeze({}),policy:Object.freeze({rules:Object.freeze([]),transparentWrappers:Object.freeze([]),commandTools: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([])})})}),sh={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 jc(_){let N=rh(_.tmpdir,"cc-safety-net-self-test"),j=oh.map((J)=>{let Y=G(_,l("self-test",{command:J.command},{kind:"command",shell:"auto"},{configCwd:N,executionCwd:N},J.command),{guard:{dependencies:{loadPolicySnapshot:()=>ih,getModes:()=>sh,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),re=J.expectBlocked?"blocked":"allowed",ie=Y.decision.kind==="deny"?"blocked":"allowed";return{command:J.command,description:J.description,expected:re,actual:ie,passed:re===ie,reason:Y.decision.kind==="deny"?Y.decision.reason:void 0,ruleId:Y.decision.kind==="deny"?Y.decision.ruleId:void 0}});return{passed:j.filter((J)=>J.passed).length,failed:j.filter((J)=>!J.passed).length,total:j.length,results:j}}function Xi(_){let N=gn({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},_);if(Dn(N.errors))return null;return{json:N.flags.json,skipUpdateCheck:N.flags.skipUpdateCheck}}async function Hc(_,N={}){let j=await Xt(!N.json,()=>{let J=lh(_,N);return{ready:J,finish:()=>J}},()=>Zt(),{loadingMessage:"Checking system status…"});if(N.json)console.log(JSON.stringify(j,null,2));else ch(j);return j.engineSelfTest.failed>0||j.findings.some((J)=>J.severity==="error")?1:0}async function lh(_,N){let j=N.cwd??process.cwd(),J=await Lr((Ge)=>qt({environment:_,cwd:j,openCodeVersion:Ge}).status!=="n/a",void 0,j),Y=Vt(_,j,{ampPluginListOutput:J.ampPluginListOutput,codexPluginListOutput:J.codexPluginListOutput,copilotCliVersion:J.versions["copilot-cli"],openCodeVersion:J.versions.opencode,openCodePluginListOutput:J.openCodePluginListOutput}),re=ca(_,j),ie=da(_),de=R(_,{cwd:j}),fe=de.policy,ke=P(fe,_.env),Ce=V(fe,ke.capabilities),Re=Ar(_,7),Oe=Ga(_,j),Ee=[Or(j),Dt(_)].filter((Ge)=>ah(Ge)),Ne=N.skipUpdateCheck?{currentVersion:pn(),latestVersion:null,updateAvailable:!1}:await Gn(),Je={hooks:Y,engineSelfTest:jc(_),userConfig:re.userConfig,projectConfig:re.projectConfig,configState:Be(de),effectiveRules:re.effectiveRules,environment:ie,effectiveSafety:{selectedPreset:fe.safety.level??"standard",level:ke.effectiveLevel,capabilities:ke.capabilities,ruleOverrides:fe.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(Ce).filter(([,Ge])=>Ge.source==="rule_override"&&Ge.override==="off"&&Ge.inheritedEnabled&&Ge.changesInherited).map(([Ge])=>Ge),ruleCounts:{stored:Object.keys(fe.destructiveCommandRuleOverrides).length,effective:Object.values(Ce).filter((Ge)=>Ge.changesInherited).length},...de.policyScopes?{policyScopes:de.policyScopes}:{}},...Oe.length>0?{v2Leftovers:Oe}:{},...Ee.length>0?{legacyConfigs:Ee}:{},posture:Sa(_,re.userConfig.path),activity:Re,update:Ne,system:J};return{...Je,findings:pa(Je)}}function ch(_){console.log(),console.log(ma(_.hooks)),console.log(),console.log(ga(_.engineSelfTest)),console.log(),console.log(ya(_)),console.log(),console.log(ha(_.environment)),console.log(),console.log(va(_)),console.log(),console.log(ba(_.findings)),console.log(),console.log(wa(_.activity)),console.log(),console.log(Ca(_.system)),console.log(),console.log(ka(_.update)),console.log(xa(_))}import{existsSync as dh}from"node:fs";var uh=/^[A-Za-z0-9_@%+=:,./-]+$/,Fc="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function Qi(_){let N=gn({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},_);if(Dn(N.errors))return console.error(Fc),console.error("Pass -- before a command that starts with dashes."),null;if(N.values.cwd!==void 0&&!dh(N.values.cwd))return console.error(`Error: --cwd path does not exist: ${N.values.cwd}`),null;let j=N.positionals.length===1?N.positionals[0]:N.positionals.map((J)=>uh.test(J)?J:`'${J.replaceAll("'","'\\''")}'`).join(" ");if(!j)return console.error("Error: No command provided"),console.error(Fc),null;return{json:N.flags.json,cwd:N.values.cwd,command:j}}function Mc(_){if(_)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 Uc(_,N){let J=N-18;return[`${_.dtl}${_.dh.repeat(N)}${_.dtr}`,`${_.dv} Command Analysis${" ".repeat(J)}${_.dv}`,`${_.dbl}${_.dh.repeat(N)}${_.dbr}`]}function es(_){return JSON.stringify(_)}function Gc(_,N=0){return`[${_.map((J,Y)=>fa(J,Y,N)).join(",")}]`}function ur(_,N,j=70){let J=_.split(" "),Y=[],re="";for(let ie of J)if(re&&re.length+ie.length+1>j)Y.push(re),re=ie;else re=re?`${re} ${ie}`:ie;if(re)Y.push(re);return Y.map((ie,de)=>de===0?ie:`${N}${ie}`)}function Bc(_,N,j){let J=[];switch(_.type){case"parse":return null;case"env-strip":return J.push(""),J.push(`STEP ${N} ${j.h} Strip environment variables`),J.push(` Removed: ${_.envVars.map((Y)=>`${Y}=<redacted>`).join(", ")}`),J.push(` Tokens: ${es(_.output)}`),{lines:J,incrementStep:!0};case"leading-tokens-stripped":return J.push(""),J.push(`STEP ${N} ${j.h} Strip wrappers`),J.push(` Removed: ${_.removed.join(", ")}`),J.push(` Tokens: ${es(_.output)}`),{lines:J,incrementStep:!0};case"shell-wrapper":return J.push(""),J.push(`STEP ${N} ${j.h} Detect shell wrapper`),J.push(` Wrapper: ${_.wrapper} -c`),J.push(` Inner: ${_.innerCommand}`),{lines:J,incrementStep:!0};case"interpreter":{if(J.push(""),J.push(`STEP ${N} ${j.h} Detect interpreter`),J.push(` Interpreter: ${_.interpreter}`),J.push(` Code: ${_.codeArg}`),_.paranoidBlocked)J.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:J,incrementStep:!0}}case"busybox":return J.push(""),J.push(`STEP ${N} ${j.h} Busybox wrapper`),J.push(` Subcommand: ${_.subcommand}`),{lines:J,incrementStep:!0};case"transparent-wrapper":return J.push(""),J.push(`STEP ${N} ${j.h} Transparent wrapper`),J.push(` Wrapper: ${_.wrapper}`),J.push(` Tokens: ${es(_.output)}`),{lines:J,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(J.push(""),J.push(`STEP ${N} ${j.h} Match rules`),J.push(` Rule: ${_.rule}()`),_.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 ${N} ${j.h} Worktree relaxation`),J.push(` Mode: ${n.worktree.name}`),J.push(` Git cwd: ${_.gitCwd}`),J.push(" Result: Allowed local discard in linked worktree"),{lines:J,incrementStep:!0};case"temp-root-relaxation":return J.push(""),J.push(`STEP ${N} ${j.h} Temp-root relaxation`),J.push(` Git cwd: ${_.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(_.embeddedCommandFound)return J.push(""),J.push(`STEP ${N} ${j.h} Fallback scan`),J.push(` Found: ${_.embeddedCommandFound}`),{lines:J,incrementStep:!0};return null}case"custom-rules-check":{if(_.rulesChecked){if(J.push(""),J.push(`STEP ${N} ${j.h} Custom rules`),_.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(_.matched)return J.push(""),J.push(`STEP ${N} ${j.h} Dangerous text check`),J.push(` Token: ${_.token}`),J.push(" Result: MATCHED"),{lines:J,incrementStep:!0};return null}case"strict-unparseable":return J.push(""),J.push(`STEP ${N} ${j.h} Strict mode check`),J.push(` Command: ${_.rawCommand}`),J.push(" Result: ✗ UNPARSEABLE"),{lines:J,incrementStep:!0};case"segment-skipped":return null;case"error":return J.push(""),J.push(`ERROR: ${_.message}`),{lines:J,incrementStep:!1};default:return _}}function ns(_,N){let j=Mc(N?.asciiOnly??!1),J=58,Y=[],re=1;Y.push(...Uc(j,58)),Y.push("");let ie=_.trace.steps.find((Ne)=>Ne.type==="error");if(ie&&ie.type==="error"){Y.push("ERROR"),Y.push(` ${ie.message}`),Y.push(""),Y.push("RESULT"),Y.push(` Status: ${_.result==="blocked"?en.red("BLOCKED"):en.green("ALLOWED")}`),Y.push(""),Y.push("CONFIG");let Ne=_.configSource??"none";return Y.push(` Path: ${Ne}`),Y.join(`
|
|
224
|
+
`)}let de=_.trace.steps.find((Ne)=>Ne.type==="parse");if(de&&de.type==="parse"){Y.push("INPUT"),Y.push(` ${de.input}`),Y.push(""),Y.push(`STEP ${re} ${j.h} Split shell commands`),re++;for(let Ne=0;Ne<de.segments.length;Ne++){let Je=de.segments[Ne];if(Je){let Ge=Math.random();Y.push(` Segment ${Ne+1}: ${Gc(Je,Ge)}`)}}}let fe=_.trace.segments,ke=fe.length>1;for(let Ne of fe){if(ke){Y.push("");let rn="";if(de&&de.type==="parse"){let Fo=de.segments[Ne.index];if(Fo)rn=Fo.join(" ")}let on=54,sn=rn,tn=` Segment ${Ne.index+1}: `,fn=" ";if(rn){if(tn.length+rn.length+fn.length>on){let qp=on-tn.length-fn.length;sn=`${rn.substring(0,qp-1)}…`}}let $n=rn?`${tn}${sn}${fn}`:` Segment ${Ne.index+1} `,Gp=rn?`${tn}${en.cyan(sn)}${fn}`:$n,Hs=58-$n.length,Fs=Math.floor(Hs/2),Bp=Hs-Fs;Y.push(`${j.sh.repeat(Fs)}${Gp}${j.sh.repeat(Bp)}`)}if(Ne.steps.find((rn)=>rn.type==="segment-skipped")){Y.push(""),Y.push(" (skipped — prior segment blocked)");continue}let Ge=!1,Ye=!1;for(let rn of Ne.steps){let on=Bc(rn,re,j);if(on){if(Ye=!0,rn.type==="recurse"){Y.push("");let sn=" RECURSING ",tn=58-sn.length-4;Y.push(` ${j.tl}${j.h}${sn}${j.h.repeat(tn)}`),Y.push(` ${j.v}`),Ge=!0;continue}for(let sn of on.lines)if(Ge)Y.push(` ${j.v} ${sn}`);else Y.push(sn);if(on.incrementStep)re++}}if(Ge)Y.push(` ${j.v}`),Y.push(` ${j.bl}${j.h.repeat(56)}`);if(!Ye)Y.push(""),Y.push(` ${en.green("✓")} Allowed (no matching rules)`)}if(Y.push(""),Y.push("RESULT"),_.result==="blocked"){if(Y.push(` Status: ${en.red("BLOCKED")}`),_.customRule){if(Y.push(` Rule: ${_.customRule.id}`),_.customRule.rulebook)Y.push(` Rulebook: ${_.customRule.rulebook.name} ${_.customRule.rulebook.version}`);if(_.customRule.source)Y.push(` Source: ${_.customRule.source}`);if(_.customRule.override)Y.push(` Override: reason ${_.customRule.override.reason}`)}if(_.reason){let Ne=ur(_.reason," ");Y.push(` Reason: ${Ne[0]}`);for(let Je=1;Je<Ne.length;Je++)Y.push(Ne[Je]??"")}}else Y.push(` Status: ${en.green("ALLOWED")}`);Y.push(""),Y.push("CONFIG");let Ce=_.configSource??"none",Re=_.configValid?"":" (invalid)";Y.push(` Path: ${Ce}${Re}`);let Oe=_.safetyPresetScope;Y.push(` Safety preset: ${_.selectedPreset??"standard"}${Oe?` (${Ir(Oe)})`:""}`),Y.push(` Effective capabilities: ${_.effectiveLevel}`);let Ee=Object.entries(_.destructiveCommandRuleOverrides??{});if(Y.push(` Rule customizations: ${Ee.length}`),_.ruleActivation)Y.push(` Rule activation: ${_.ruleActivation.id} — ${_.ruleActivation.enabled?"on":"off"} via ${_.ruleActivation.source}`);return Y.join(`
|
|
225
|
+
`)}function ts(_){return JSON.stringify(_,null,2)}import{resolve as yh}from"node:path";var ph=["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 qc(_){let N=0,j={allocateSegment(){return N++},getNextSegmentIndex(){return N},recordGlobal(J){_.record({kind:"step",scope:"global",step:J})},recordSegment(J,Y=j.currentSegmentIndex){if(Y===void 0)return;_.record({kind:"step",scope:"segment",segmentIndex:Y,step:J})}};return j}function Vc(_={}){let N=[],j=_.maxEvents??512,J={maxTextLength:_.maxTextLength??2048,maxListLength:_.maxListLength??128,maxObjectProperties:_.maxObjectProperties??_.maxListLength??128,maxDepth:_.maxDepth??16},Y,re=new Set;return{record(ie){if(Y)return;if(!ie||N.length>=j)return;try{N.push(is(fh(ie,J,re)))}catch{}},finish(){if(Y)return Y;return Y=is({events:Object.freeze(N)}),Y}}}function fh(_,N,j){if(_.kind!=="step")throw TypeError("invalid trace event");let{scope:J,step:Y}=_;go(Y,j,N);let re=rs(Y,N,j);if(J==="global")return{kind:"step",scope:"global",step:re};if(J!=="segment")throw TypeError("invalid trace event scope");return{kind:"step",scope:"segment",segmentIndex:_.segmentIndex,step:re}}function go(_,N,j,J=0,Y=new WeakSet){if(typeof _==="string"){let de=_.slice(0,j.maxTextLength);if(!tt(de))return;for(let fe of gt(de))for(let ke of fe.match(/[^\s"'()$]+/g)??[])N.add(Kc(ke));return}if(!_||typeof _!=="object"||J>=j.maxDepth||Y.has(_))return;if(Y.add(_),Array.isArray(_)){let de=Math.min(_.length,j.maxListLength);for(let fe=0;fe<de;fe++)go(_[fe],N,j,J+1,Y);return}let re=0,ie=new Set;for(let de in _){if(!Object.hasOwn(_,de))continue;if(re>=j.maxObjectProperties)break;re++,go(de,N,j);let fe=os(de,j,N);if(ie.has(fe))continue;ie.add(fe),go(_[de],N,j,J+1,Y)}}function rs(_,N,j,J=0,Y=new WeakSet){if(typeof _==="string")return os(_,N,j);if(!_||typeof _!=="object")return _;if(J>=N.maxDepth)return;if(Y.has(_))return;if(Y.add(_),Array.isArray(_)){let de=[],fe=Math.min(_.length,N.maxListLength);for(let ke=0;ke<fe;ke++)de.push(rs(_[ke],N,j,J+1,Y));return de}let re={},ie=0;for(let de in _){if(!Object.hasOwn(_,de))continue;if(ie>=N.maxObjectProperties)break;ie++;let fe=os(de,N,j);if(Object.hasOwn(re,fe))continue;Object.defineProperty(re,fe,{value:rs(_[de],N,j,J+1,Y),enumerable:!0,configurable:!0,writable:!0})}return re}function os(_,N,j){let J=_.slice(0,N.maxTextLength),Y=tt(J)?et(J):J,re=j.size>0?gh(Y,j):Y;return(mh(re)?De(re):re).slice(0,N.maxTextLength)}function mh(_){return _.includes("PRIVATE KEY")||_.includes("://")||_.includes("eyJ")||_.includes(":")&&/(?:authorization|cookie|x-api-key|api-key|(?:^|\s)(?:-u|--user)(?:\s|=))/i.test(_)||_.length>=14&&ph.some((N)=>_.includes(N))||_.length>=49&&/\b[a-f0-9]{32}\.[A-Za-z0-9]{16}\b/.test(_)}function gh(_,N){return _.replace(/[^\s"'()$]+/g,(j)=>N.has(Kc(j))?"<redacted>":j)}function Kc(_){let N=2166136261,j=2166136261;for(let J=0;J<_.length;J++)N=Math.imul(N^_.charCodeAt(J),16777619),j=Math.imul(j^_.charCodeAt(_.length-J-1),16777619);return`${N>>>0}:${j>>>0}:${_.length}`}function is(_){if(_&&typeof _==="object"&&!Object.isFrozen(_)){for(let N of Object.values(_))is(N);Object.freeze(_)}return _}function pr(_,N={},j){let J=yh(N.cwd??process.cwd()),Y=N.policySnapshot??R(j,{cwd:J,userConfigDir:N.userConfigDir}),re=P(Y.policy,j.env),ie=ze({policySnapshot:Y,effectiveCapabilities:re.capabilities,strict:re.strict,paranoidRm:re.paranoidRm,paranoidInterpreters:re.paranoidInterpreters,worktreeMode:re.worktreeMode}),de={effectiveLevel:ie.effectiveLevel,selectedPreset:Y.policy.safety.level??"standard",...Y.policyScopes?{safetyPresetScope:Y.policyScopes.levelScope}:{},effectiveCapabilities:ie.effectiveCapabilities,destructiveCommandRuleOverrides:Y.policy.destructiveCommandRuleOverrides},{configSource:fe,configValid:ke}=vh(j,{cwd:J,userConfigDir:N.userConfigDir});if(!_||!_.trim())return{trace:{steps:[{type:"error",message:"No command provided"}],segments:[]},result:"allowed",configSource:fe,configValid:ke,...de};let Ce=y(_,"auto");if(Ce.status==="limited")throw new b;let Re=Ce.dialect==="powershell"?y(_,"posix"):Ce,Oe=Et(Re),Ee=Vc(),Ne=qc(Ee);Ne.recordGlobal({type:"parse",input:_,segments:Oe.map(($n)=>[...$n])});let Je=l("Bash",{command:_},{kind:"command",shell:"auto"},{configCwd:J,executionCwd:J},_),Ge=q(Je,{environment:j,trace:Ne,dependencies:{loadPolicySnapshot:()=>Y}}),Ye=Ge.decision.kind==="deny"?Ge.decision:null;if(Ye&&(Ge.stage==="policy-protection"||Ge.stage==="secret-protection"))return{trace:{steps:[],segments:[{index:0,steps:[{type:"rule-check",rule:hh(Ye),matched:!0,reason:Ye.reason}]}]},result:"blocked",reason:T(Ye.reason),segment:T(Jc(Ye,_)),ruleId:T(Ye.ruleId),configSource:fe,configValid:ke,...de};let rn=Ne.getNextSegmentIndex();if(Ye&&rn>0&&rn<Oe.length)Ne.recordSegment({type:"segment-skipped",index:rn,reason:"prior-segment-blocked"},rn);let on=Ee.finish(),sn=Ye?.ruleId??bh(Je,Y,re,j),tn=z.find(($n)=>$n.id===sn&&$n.activationCapability),fn=tn?ie.policy.effectiveDestructiveCommandRules[tn.id]:void 0;return{trace:kh(on),result:Ye?"blocked":"allowed",reason:Ye?T(Ye.reason):void 0,segment:Ye?T(Jc(Ye,_)):void 0,ruleId:Ye?T(Ye.ruleId):void 0,customRule:wh(Ch(Ye?.ruleId,Y)),configSource:fe,configValid:ke,...de,...tn&&fn?{ruleActivation:{id:tn.id,...fn}}:{}}}function Jc(_,N){return _.evidence?.segment??N}function hh(_){if(_.reason===qe)return"policy-protection:findPolicyConfigMutationTargetInSemanticFacts";if(_.reason===Ve)return"policy-apply-protection:findPolicyApplyInvocationInSemanticFacts";if(_.reason===v)return"git-metadata-protection:findGitMetadataMutationTargetInSemanticFacts";return"secret-protection:findSensitiveTargetInSemanticFacts"}function vh(_,N){let j=I(N.cwd),J=W(_,N),Y=X(_,{cwd:N.cwd,userConfigDir:N.userConfigDir});try{if(r(Y.projectConfigTarget)!==null){if(Un(Y.projectConfigTarget).errors.length===0)return{configSource:j,configValid:!0};return{configSource:j,configValid:!1}}}catch(re){if(re instanceof o)return{configSource:j,configValid:!1};throw re}try{if(r(Y.userConfigTarget)!==null){let re=Un(Y.userConfigTarget);return{configSource:J,configValid:re.errors.length===0}}return{configSource:null,configValid:!0}}catch(re){if(re instanceof o)return{configSource:J,configValid:!1};throw re}}function bh(_,N,j,J){let Y=N.policy,re=Ae({...Y,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:{...Y.destructiveCommandRuleOverrides,...Object.fromEntries(z.flatMap((de)=>de.activationCapability?[[de.id,"on"]]:[]))}},N.state==="degraded"?{diagnostics:N.diagnostics,reason:N.reason}:void 0),ie=q(_,{environment:J,dependencies:{loadPolicySnapshot:()=>re,getModes:()=>({...j,strict:!0,paranoidRm:!0,paranoidInterpreters:!0}),findSensitiveTarget:()=>null}});return ie.decision.kind==="deny"?ie.decision.ruleId:void 0}function wh(_){if(!_)return;return{id:T(_.id),..._.rulebook?{rulebook:{name:T(_.rulebook.name),version:T(_.rulebook.version)}}:{},..._.source?{source:T(_.source)}:{},..._.override?{override:{type:"reason",reason:T(_.override.reason)}}:{}}}function kh(_){let N=_.events.flatMap((J)=>J.kind==="step"&&J.scope==="global"?[J.step]:[]),j=new Map;for(let J of _.events){if(J.kind!=="step"||J.scope!=="segment")continue;let Y=j.get(J.segmentIndex)??{index:J.segmentIndex,steps:[]};Y.steps.push(J.step),j.set(J.segmentIndex,Y)}return{steps:N,segments:[...j.values()]}}function Ch(_,N){let j=_?.replace(/^custom\./,"");if(!j||!N.policy.rules.some((J)=>J.name===j))return;return N.ruleMetadata[j]??Object.freeze({id:j})}function Wc(_){return new Promise((N)=>{process.stdout.write(`${_}
|
|
226
|
+
`,()=>N())})}async function zc(_,N){let j=Qi(N);if(!j)return 1;try{let J=pr(j.command,{cwd:j.cwd},_),Y=!!process.env.NO_COLOR||!process.stdout.isTTY;return await Wc(j.json?ts(J):ns(J,{asciiOnly:Y})),0}catch(J){let Y=xh(J instanceof f?J.cause:J);if(Y===void 0)throw J;if(j.json)return await Wc(JSON.stringify({error:Y})),1;return console.error(Y),1}}function xh(_){if(_ instanceof b)return _.message;if(_ instanceof m)return _.message;if(_ instanceof s&&a[_.kind].errorCode==="path-canonicalization-limit")return"Path canonicalization work limit exceeded.";return}var Yc="2.6.10",En=" ",kt="cc-safety-net";function Zc(_){return _.argument?`${_.flags} ${_.argument}`:_.flags}function Sh(_){return Math.max(..._.map((N)=>Zc(N).length))}function Ph(_){return Math.max(..._.map((N)=>N.usage.length))}function Rh(_){return Math.max(..._.map((N)=>`${kt} ${N.usage}`.length))}function Eh(_,N){let j=`${kt} ${_.usage}`;return`${En}${j.padEnd(N+2)}${_.description}`}function On(_,N){return`${En}${_.padEnd(Math.max(40,_.length+2))}${N}`}function Kt(_,N=console.log){let j=[];if(j.push(`${kt} ${_.name}`),j.push(""),j.push(`${En}${_.description}`),j.push(""),j.push("USAGE:"),j.push(`${En}${kt} ${_.usage}`),j.push(""),_.subcommands&&_.subcommands.length>0){j.push("SUBCOMMANDS:");let J=Ph(_.subcommands);for(let Y of _.subcommands)j.push(`${En}${Y.usage.padEnd(J+2)}${Y.description}`);j.push("")}if(_.options.length>0){j.push("OPTIONS:");let J=Sh(_.options);for(let Y of _.options){let re=Zc(Y),ie=Y.default?`${Y.description} (default: ${Y.default})`:Y.description;j.push(`${En}${re.padEnd(J+2)}${ie}`)}j.push("")}if(_.examples&&_.examples.length>0){j.push("EXAMPLES:");for(let J of _.examples)j.push(`${En}${J}`)}N(j.join(`
|
|
227
|
+
`))}function ss(){let _=Rh(_r),N=[];N.push(`${kt} v${Yc}`),N.push(""),N.push("Blocks destructive commands and secret access."),N.push(""),N.push("COMMANDS:");for(let j of _r)N.push(Eh(j,_));N.push(""),N.push("GLOBAL OPTIONS:"),N.push(`${En}-h, --help Show help (use with command for command-specific help)`),N.push(`${En}-V, --version Show version`),N.push(""),N.push("HELP:"),N.push(`${En}${kt} help <command> Show help for a specific command`),N.push(`${En}${kt} <command> --help Show help for a specific command`),N.push(""),N.push("ENVIRONMENT VARIABLES:"),N.push(On(`${n.level.name}=standard|strict|paranoid`,"Set session safety level")),N.push(On(`${n.worktree.name}=1`,"Allow local git discards in linked worktrees")),N.push(On(`${n.debug.name}=1`,"Print diagnostic messages to stderr")),N.push(On(`${n.auditScope.name}=all|blocked`,"Record all command decisions, or denials only")),N.push(On(`${n.projectTightenOnly.name}=1`,"Ignore project policy settings that weaken the user policy")),N.push(On("CC_SAFETY_NET_HOME","Override rule config home directory")),N.push(""),N.push("LEGACY ENVIRONMENT VARIABLES (STILL SUPPORTED):"),N.push(On(`${n.strict.name}=1`,"Force safety.overrides.fail_closed on")),N.push(On(`${n.paranoid.name}=1`,"Force paranoid_rm and paranoid_interpreters on")),N.push(On(`${n.paranoidRm.name}=1`,"Force safety.overrides.paranoid_rm on")),N.push(On(`${n.paranoidInterpreters.name}=1`,"Force safety.overrides.paranoid_interpreters on")),N.push(""),N.push("Documentation: https://ccsafetynet.com/docs"),console.log(N.join(`
|
|
228
|
+
`))}function Xc(){console.log(Yc)}function fr(_,N=console.log){let j=Tr(_);if(!j)return!1;if(j.name.toLowerCase()!==_.toLowerCase())return!1;return Kt(j,N),!0}import{existsSync as xo,readFileSync as ou}from"node:fs";import{join as Co}from"node:path";import*as qn from"node:readline";function _h(_){return _==="install"?"Install":"Uninstall"}function Th(_){return _==="install"?"Installing":"Uninstalling"}function Ah(_){return _==="install"?"into":"from"}function nd(_){return _?.available===!0}function Oh(_,N){let j=new Set(N);return _.filter((J)=>j.has(J.target)).map((J)=>J.target)}function Qc(_,N,j){if(_.every((J)=>!J.available))return N;return Array.from({length:_.length},(J,Y)=>Y+1).map((J)=>(N+J*j+_.length)%_.length).find((J)=>nd(_[J]))}function Ih(_,N,j){if(j.ctrl&&j.name==="c")return"interrupt";if(j.name==="escape"||N==="q")return"abort";if(_==="install"&&(N==="u"||N==="U"))return"update";if(j.name==="up"||N==="k")return"up";if(j.name==="down"||N==="j")return"down";if(j.name==="space"||N===" ")return"toggle";if(j.name==="return"||j.name==="enter")return"confirm";return null}function $h(_){return{cursor:_.findIndex((N)=>N.available),selected:[]}}function Dh(_,N,j){if(j==="confirm"||j==="update"||j==="abort"||j==="interrupt")return{state:_,done:j};if(j==="up")return{state:{..._,cursor:Qc(N,_.cursor,-1)}};if(j==="down")return{state:{..._,cursor:Qc(N,_.cursor,1)}};let J=N[_.cursor];if(!nd(J))return{state:_};let Y=_.selected.includes(J.target)?_.selected.filter((re)=>re!==J.target):Oh(N,[..._.selected,J.target]);return{state:{..._,selected:Y}}}var td="◉",rd="◯",od=">",id=" ";function Lh(_,N,j){return["",`${_h(_)} CC Safety Net ${Ah(_)}:`,"",...N.map((J,Y)=>{let re=j.selected.includes(J.target),ie=Y===j.cursor,de=re?td:rd,fe=ie?od:id,ke=J.available?"":` (${J.unavailableReason??"not installed"})`,Ce=`${de} ${J.label}${ke}`,Re=!J.available?en.dim(Ce):re?en.green(Ce):ie?en.bold(Ce):Ce;return`${fe} ${Re}`}),"",_==="install"?"Space: select Enter: confirm u: update installed Up/Down: move q/Esc: cancel":N.some((J)=>J.available)?"Space: select Enter: confirm Up/Down: move q/Esc: cancel":`No selectable integrations found for ${_}. q/Esc: close`].join(`
|
|
229
|
+
`)}var ed=["global-hook","plugin"];function Nh(_,N,j={}){let J=j.color!==!1?en.bold:(re)=>re;return["","Install the Kimi Code integration as:","",...[`Global hook — ${N?"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((re,ie)=>{let de=ie===_,fe=`${de?td:rd} ${re}`;return`${de?od:id} ${de?J(fe):fe}`}),"","Enter: confirm Up/Down: move q/Esc: cancel"].join(`
|
|
230
|
+
`)}function sd(_){let{input:N,output:j}=_;qn.emitKeypressEvents(N);let J=N.isRaw===!0;N.setRawMode(!0),N.resume();let Y=0,re=()=>{if(Y===0)return;qn.moveCursor(j,0,-Y),qn.cursorTo(j,0),qn.clearScreenDown(j)},ie=()=>{re();let de=_.render();j.write(`${de}
|
|
231
|
+
`),Y=de.split(`
|
|
232
|
+
`).length};return new Promise((de)=>{let fe=(Ce)=>{N.off("keypress",ke),N.setRawMode(J),N.pause(),re(),de(Ce)};function ke(Ce,Re){_.onKey(Ce,Re,{finish:fe,draw:ie})}N.on("keypress",ke),ie()})}function ad(_={}){let N=0;return sd({input:_.input??process.stdin,output:_.output??process.stdout,render:()=>Nh(N,_.globalHookInstalled===!0),onKey:(j,J,Y)=>{if(J.ctrl&&J.name==="c"){Y.finish(null),(_.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(J.name==="escape"||j==="q")return Y.finish(null);if(J.name==="return"||J.name==="enter")return Y.finish(ed[N]);if(J.name==="up"||J.name==="down"||j==="k"||j==="j")N=(N+1)%ed.length,Y.draw()}})}function as(_=process.stdin,N=process.stdout){return Boolean(_.isTTY&&N.isTTY&&typeof _.setRawMode==="function")}function ld(_,N,j={}){let J=j.output??process.stdout,Y=$h(N);return sd({input:j.input??process.stdin,output:J,render:()=>Lh(_,N,Y),onKey:(re,ie,de)=>{let fe=Ih(_,re,ie);if(!fe)return;let ke=Dh(Y,N,fe);if(Y=ke.state,ke.done==="interrupt"){de.finish(null),(j.onInterrupt??(()=>process.kill(process.pid,"SIGINT")))();return}if(ke.done==="abort")return de.finish(null);if(ke.done==="update")return de.finish("update");if(ke.done==="confirm"){if(Y.selected.length===0){J.write("\x07"),de.draw();return}de.finish([...Y.selected]),J.write(`${Th(_)} selected integrations...
|
|
233
|
+
`);return}de.draw()}})}import{existsSync as cd,lstatSync as Hh,mkdirSync as Fh,mkdtempSync as Mh,readdirSync as Uh,readFileSync as Wt,rmSync as ho}from"node:fs";import{basename as Gh,dirname as Bh,join as xn}from"node:path";import{fileURLToPath as qh}from"node:url";var ls="// cc-safety-net managed Amp plugin. Do not edit. Reinstall with: npx -y cc-safety-net install --amp",Pt="cc-safety-net",Rt="cc-safety-net/index.ts";import{spawn as jh}from"node:child_process";var cs=(_,N)=>{let j=Zn([..._],process.env);return new Promise((J)=>{let Y=jh(j.cmd,j.args,{cwd:N,stdio:["ignore","pipe","pipe"]}),re=di(Y),ie=!1,de=setTimeout(()=>{ie=!0,Y.kill()},120000);Y.on("error",(fe)=>{clearTimeout(de),J({status:null,errorCode:fe.code,stdout:re.stdout,stderr:[fe.message,re.stderr].filter(Boolean).join(`
|
|
234
|
+
`)})}),Y.on("close",(fe)=>{clearTimeout(de),J({status:ie?null:fe,errorCode:ie?"ETIMEDOUT":void 0,stdout:re.stdout,stderr:re.stderr})})})};var Jt="cc-safety-net.ts",ds=xn("amp",Rt);function Vh(_){return xn(_.home,".config","amp","plugins","cc-safety-net.ts")}function Kh(){let _=Bh(qh(import.meta.url)),N=xn(_,ds),j=xn(_,"..",ds),J=xn(_,"..","..","..","dist",ds);return[N,j,J]}function Jh(_=Kh()){let N=_.find((j)=>cd(j)&&Hh(j).isFile());if(!N)throw Error("Packaged Amp plugin artifact not found. Reinstall cc-safety-net and try again.");return N}function dd(_){try{return JSON.parse(_)}catch{return}}function vo(_){return _.subarray(0,Buffer.byteLength(ls)).toString("utf-8")===ls}async function mr(_,N,j){let J=await _(N,j);if(J.status===0)return J;throw Error([`Failed to run ${N.join(" ")}${J.status===null?"":` (exit ${J.status})`}.`,[J.stdout,J.stderr].filter(Boolean).join(`
|
|
235
235
|
`).trim()].filter(Boolean).join(`
|
|
236
|
-
`))}async function
|
|
237
|
-
${
|
|
238
|
-
${[
|
|
239
|
-
`)}`.trim());let
|
|
240
|
-
`}async function
|
|
241
|
-
`)}function
|
|
242
|
-
`)}}function
|
|
243
|
-
`,
|
|
244
|
-
`)){if(/^\s*\[/.test(
|
|
245
|
-
${
|
|
246
|
-
`),J
|
|
247
|
-
`)}function
|
|
236
|
+
`))}async function ud(_){let N=await _(["amp","plugins","repositories","--json"]);if(N.status===null)throw Error(`${N.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 (${N.errorCode??"terminated"}). Check that the amp CLI responds and rerun install --amp.`}
|
|
237
|
+
${N.stderr}`.trim());if(N.status!==0)throw Error(`Failed to run amp plugins repositories --json (exit ${N.status}). Sign in with "amp login" and rerun install --amp.
|
|
238
|
+
${[N.stdout,N.stderr].filter(Boolean).join(`
|
|
239
|
+
`)}`.trim());let j=dd(N.stdout),J=(Array.isArray(j)?j:[]).filter((Y)=>nn(Y,"scope")==="user"&&nn(Y,"exists")===!0&&nn(Y,"viewerCanWrite")===!0).map((Y)=>nn(Y,"cloneRef")).find((Y)=>typeof Y==="string"&&Y.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 pd(_,N,j){let J=Mh(xn(N.tmpdir,"cc-safety-net-amp-"));try{return await mr(_,["amp","clone","user-plugins",J]),await j(J)}finally{ho(J,{recursive:!0,force:!0})}}function us(_){return`rerun ${_==="overwrite"?"install":"uninstall"} --amp`}function fd(_,N,j){let J=xn(_,N),Y=dn(J);if(!Y)return;if(Y.isSymbolicLink()||!Y.isFile())throw Error(`Refusing to ${j} ${N} in your Amp personal plugins repository: not a regular file. Remove it there and ${us(j)}.`);let re=Wt(J);if(vo(re))return re;throw Error(`Refusing to ${j} unmanaged file ${N} in your Amp personal plugins repository. Remove it there and ${us(j)}.`)}function md(_,N){let j=xn(_,Pt),J=dn(j);if(!J)return;if(J.isSymbolicLink()||!J.isDirectory())throw Error(`Refusing to ${N} ${Pt} in your Amp personal plugins repository: not a regular directory. Remove it there and ${us(N)}.`);return fd(_,Rt,N)}function Wh(_){let N=xn(_,Jt),j=dn(N);if(!j||j.isSymbolicLink()||!j.isFile())return;let J=Wt(N);return vo(J)?J:void 0}async function gd(_,N,j,J){if(await mr(_,j,N),(await mr(_,["git","status","--porcelain"],N)).stdout.trim()==="")return!1;return await mr(_,["git","-c","commit.gpgsign=false","-c","user.name=cc-safety-net","-c","user.email=cc-safety-net@localhost","commit","-m",J],N),await mr(_,["git","push","origin","HEAD"],N),!0}function yo(_,N){zh(_,N),Yh(_,N)}function yd(_,N){if(N==="keep")return;throw Error(`Local Amp plugin ${_} is not a managed copy and masks the personal plugin. Remove it and rerun install --amp.`)}function zh(_,N){let j=Vh(_),J=dn(j);if(!J)return;if(!J.isSymbolicLink()&&J.isFile()&&vo(Wt(j))){ho(j);return}yd(j,N)}function Yh(_,N){let j=xn(_.home,".config","amp","plugins",Pt),J=dn(j);if(!J)return;if(!J.isSymbolicLink()&&J.isDirectory()&&Zh(j)){ho(j,{recursive:!0});return}yd(j,N)}function Zh(_){let N=Gh(Rt);if(Uh(_).join("\x00")!==N)return!1;let j=xn(_,N),J=dn(j);return!!J&&!J.isSymbolicLink()&&J.isFile()&&vo(Wt(j))}function Xh(_){let N=u(_);if(!cd(N))return"";let j=dd(Wt(N,"utf-8"));if(!j||typeof j!=="object"||Array.isArray(j))return"";return`;globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__ = ${JSON.stringify(k(j,_.home))};
|
|
240
|
+
`}async function hd(_,N=Jh(),j=cs){let J=Buffer.concat([Wt(N),Buffer.from(Xh(_),"utf-8")]),Y=await ud(j);return pd(j,_,async(re)=>{let ie=`${Y}/${Pt}`,de=md(re,"overwrite"),fe=fd(re,Jt,"overwrite");if(de?.equals(J)&&!fe)return yo(_,"fail"),{path:ie,alreadyInstalled:!0};if(Fh(xn(re,Pt),{recursive:!0}),ln(xn(re,Rt),J),fe)ho(xn(re,Jt));let ke=await gd(j,re,["git","add","--",Rt,...fe?[Jt]:[]],`chore: update cc-safety-net plugin to v${pn()}`);return yo(_,"fail"),{path:ie,alreadyInstalled:!ke}})}async function vd(_,N=cs){let j=await ud(N);return pd(N,_,async(J)=>{let Y=md(J,"remove"),re=Wh(J),ie=`${j}/${re&&!Y?Jt:Pt}`;if(!Y&&!re)return yo(_,"keep"),{path:ie,alreadyInstalled:!1};return await gd(N,J,["git","rm","--",...Y?[Rt]:[],...re?[Jt]:[]],`chore: remove cc-safety-net plugin v${pn()}`),yo(_,"keep"),{path:ie,alreadyInstalled:!0}})}import{existsSync as bd,mkdirSync as Qh,readFileSync as ev}from"node:fs";import{dirname as nv}from"node:path";var ps=Cn["antigravity-cli"],_t="cc-safety-net";function Tt(_){return Boolean(_)&&typeof _==="object"&&!Array.isArray(_)}function wo(){return{PreToolUse:[{hooks:[{type:"command",command:ps,timeout:30}]}]}}function wd(_){try{let N=JSON.parse(ev(_,"utf-8"));if(!N||typeof N!=="object"||Array.isArray(N))throw Error("Antigravity hooks config must be a JSON object");return N}catch(N){if(N instanceof SyntaxError)throw Error(`Failed to parse Antigravity hooks config ${_}: ${N.message}`);throw N}}function kd(_){let N=_[_t];if(N===void 0){let J=wo();return _[_t]=J,{definition:J,preToolUse:J.PreToolUse??[]}}if(!Tt(N))throw Error(`Antigravity hooks config entry "${_t}" must be an object`);let j=Array.isArray(N.PreToolUse)?N.PreToolUse:[];return N.PreToolUse=j,{definition:N,preToolUse:j}}function Cd(_){if(!Array.isArray(_.PreToolUse))return!1;return _.PreToolUse.some((N)=>Tt(N)&&Array.isArray(N.hooks)&&N.hooks.some((j)=>Tt(j)&&j.command===ps))}function tv(_){return Object.values(_).some((N)=>Tt(N)&&N.enabled!==!1&&Cd(N))}function rv(_){if(_[_t]===void 0)return!1;let N=kd(_);if(N.definition.enabled!==!1||!Cd(N.definition))return!1;return N.definition.enabled=!0,!0}function ov(_){if(_[_t]===void 0){_[_t]=wo();return}let N=kd(_);N.definition.enabled=!0,N.preToolUse.push(wo().PreToolUse?.[0]??{hooks:[]})}function iv(_){let N=!1;for(let j of Object.values(_)){if(!Tt(j)||!Array.isArray(j.PreToolUse))continue;j.PreToolUse=j.PreToolUse.flatMap((J)=>{if(!Tt(J)||!Array.isArray(J.hooks))return[J];let Y=J.hooks.filter((re)=>!Tt(re)||re.command!==ps);if(Y.length!==J.hooks.length)N=!0;return Y.length===0?[]:[{...J,hooks:Y}]})}return N}function bo(_,N){ln(_,`${JSON.stringify(N,null,2)}
|
|
241
|
+
`)}function xd(_){let N=Qt(_.home);if(Qh(nv(N),{recursive:!0}),!bd(N))return bo(N,{[_t]:wo()}),{path:N,alreadyInstalled:!1};let j=wd(N);if(tv(j))return{path:N,alreadyInstalled:!0};if(rv(j))return bo(N,j),{path:N,alreadyInstalled:!1};return ov(j),bo(N,j),{path:N,alreadyInstalled:!1}}function Sd(_){let N=Qt(_.home);if(!bd(N))return{path:N,alreadyInstalled:!1};let j=wd(N);if(!iv(j))return{path:N,alreadyInstalled:!1};return bo(N,j),{path:N,alreadyInstalled:!0}}import{existsSync as gs,readlinkSync as lv}from"node:fs";import{join as Vn}from"node:path";import{spawn as sv}from"node:child_process";var In=Rn.map((_)=>({target:_.id,flag:_.flag,label:cn(_.id),probeCommand:_.probeCommand}));function fs(_){let N=new Set(_);return In.map((j)=>j.target).filter((j)=>N.has(j))}async function Pd(_,N){for(let j of _)await N(j)}var av=5000;function gr(_,N=av){return new Promise((j)=>{let J=Zn([..._],process.env),Y=sv(J.cmd,J.args,{env:process.env,stdio:"ignore"}),re=!1,ie=(fe)=>{if(re)return;re=!0,clearTimeout(de),j(fe)},de=setTimeout(()=>{Y.kill(),ie(!1)},N);Y.on("error",()=>ie(!1)),Y.on("close",(fe)=>ie(fe===0))})}function Rd(_=gr,N={}){let j=new Set(N.configuredTargets??[]);return Promise.all(In.map(async(J)=>({target:J.target,flag:J.flag,label:J.label,..._d(N.action,await _(J.probeCommand)||J.target==="cursor"&&await _(Zs),j.has(J.target))})))}function Ed(_,N){let j=new Set(N.configuredTargets??[]);return _.map((J)=>({...J,..._d(N.action,J.available,j.has(J.target))}))}function _d(_,N,j){if(_==="uninstall")return j?{available:!0}:{available:!1,unavailableReason:"not installed"};if(_==="install"&&j)return{available:!1,unavailableReason:"already installed"};if(!N)return{available:!1,unavailableReason:"CLI not installed"};return{available:!0}}var Kn="cc-safety-net",Id=["npx","-y","@deepseek-ai/dsh"],Td="DeepSeek Harness",Ad=["@deepseek-ai","dsh-desktop"],$d="Quit DeepSeek Harness Desktop, then run this command again: Desktop plugins can only change while it is closed.",ms=(_)=>`DeepSeek Harness Desktop is not in its default location, so ${_} ${Kn} from its Plugins page.`,Dd=(_,N)=>gs(Vn(wi(_),N,"package.json"));function ys(_,N){let j=N.platform??process.platform,J=Dd(_,"desktop"),Y=j==="darwin"?[Vn(_.home,"Applications"),N.systemApplications??"/Applications"].map((re)=>Vn(re,`${Td}.app`,"Contents","Resources","runtime","cli","bin","dsh")):j==="win32"?[Vn(_.env.get("LOCALAPPDATA")||Vn(_.home,"AppData","Local"),"Programs",Td,"resources","runtime","cli","bin","dsh.cmd")]:[];return{profile:J,cli:J?Y.find((re)=>gs(re)):void 0}}function Ld(_,N=process.platform){if(N==="win32")return gs(Vn(_.env.get("APPDATA")||Vn(_.home,"AppData","Roaming"),...Ad,"lockfile"));let j=cv(Vn(_.home,"Library","Application Support",...Ad,"SingletonLock")),J=Number(/-(\d+)$/.exec(j??"")?.[1]);return Number.isInteger(J)&&J>0&&dv(J)}function cv(_){try{return lv(_)}catch{return}}function dv(_){try{return process.kill(_,0),!0}catch(N){return N.code==="EPERM"}}async function Nd(_,N={}){let j=ys(_,N);if(j.cli&&Ld(_,N.platform))throw Error($d);let J=!j.cli||Dd(_,"web")||await(N.probe??gr)(Uo),Y=[...j.cli?[{profile:"desktop",label:"Desktop",dsh:[j.cli]}]:[],...J?[{profile:"web",label:"web",dsh:Id}]:[]];return{commands:Y.map((re)=>[...re.dsh,"plugin","--profile",re.profile,"add",`${Kn}@${pn()}`]),afterInstall:async()=>{let re=new Set(ki(_).filter((de)=>de.enabled).map((de)=>de.name)),ie=Y.filter((de)=>!re.has(de.profile));if(ie.length>0)throw Error(`DeepSeek Harness installed ${Kn} in the ${Od(ie)} but did not enable it. Enable it from the Plugins page.`)},message:[`Added ${Kn} to the DeepSeek Harness ${Od(Y)}.`,...j.profile&&!j.cli?[ms("add")]:[]].join(`
|
|
242
|
+
`)}}function Od(_){return`${_.map((N)=>N.label).join(" and ")} profile${_.length>1?"s":""}`}function jd(_,N={}){let j=new Set(ki(_).map((re)=>re.name));if(!j.has("desktop")&&!j.has("web"))throw Error(`${Kn} is not installed in the DeepSeek Harness web or Desktop profile`);let J=j.has("desktop")?ys(_,N).cli:void 0,Y=j.has("desktop")&&!J;if(Y&&!j.has("web"))throw Error(ms("remove"));if(J&&Ld(_,N.platform))throw Error($d);return{commands:[...J?[[J,"plugin","--profile","desktop","remove",Kn]]:[],...j.has("web")?[[...Id,"plugin","--profile","web","remove",Kn]]:[]],...Y?{afterUninstall:()=>{throw Error(`Removed ${Kn} from the DeepSeek Harness web profile, but ${ms("remove")}`)}}:{}}}function Hd(_,N,j={}){let J=ys(N,j).cli!==void 0;return _.map((Y)=>Y.target==="deepseek-harness"&&J?{...Y,available:!0,unavailableReason:void 0}:Y)}import{existsSync as uv,readdirSync as pv,rmSync as fv}from"node:fs";import{join as mv}from"node:path";function Fd(_,N=process.platform,j){if(!uv(_))return;let J=N==="win32"?/^bunx-\d+-cc-safety-net@/:new RegExp(`^bunx-${process.getuid?.()??0}-cc-safety-net@`);pv(_).filter((Y)=>Y!==j&&J.test(Y)).forEach((Y)=>{fv(mv(_,Y),{recursive:!0,force:!0})})}import{existsSync as Md,readdirSync as gv,rmSync as yv}from"node:fs";import{join as zt}from"node:path";function ko(_,N=process.platform){let j=zt(_.env.get("npm_config_cache")||(N==="win32"?zt(_.env.get("LOCALAPPDATA")||zt(_.home,"AppData","Local"),"npm-cache"):zt(_.home,".npm")),"_npx");if(!Md(j))return;gv(j).filter((J)=>Md(zt(j,J,"node_modules","cc-safety-net"))).forEach((J)=>{yv(zt(j,J),{recursive:!0,force:!0})})}import{existsSync as Kd,mkdirSync as vv,readFileSync as Jd}from"node:fs";import{dirname as bv,join as Vd}from"node:path";function hv(_,N){if(_[N]!=="#")return N;let j=_.indexOf(`
|
|
243
|
+
`,N+1);return j===-1?_.length:j+1}function hs(_,N,j){let J=new RegExp(`^(\\s*)${N}\\s*=\\s*\\[`),Y=0;for(let re of _.split(`
|
|
244
|
+
`)){if(/^\s*\[/.test(re))return;let ie=J.exec(re);if(ie){let de=Y+ie[0].lastIndexOf("[");return{start:de,end:si(_,de,{skipComment:hv,...j})}}Y+=re.length+1}return}function Ud(_,N,j){let J=_.slice(0,N.end).trimEnd(),Y=rl(_,N.end),re=Y===""?" ":`${Y} `,ie=!J.endsWith("[")&&!J.endsWith(",");return`${J}${ie?",":""}
|
|
245
|
+
${re}${j}${_.slice(N.end)}`}function Gd(_,N,j){let J=_.indexOf(j,N.start);if(J===-1||J>N.end)return _;return`${_.slice(0,J)}${_.slice(J+j.length).replace(/^\s*,/,"")}`}function Bd(_,N){let j=new RegExp(`^\\s*${N}\\s*=\\s*\\[\\s*]\\s*(?:#.*)?$`),J=_.split(`
|
|
246
|
+
`),Y=J.findIndex((de)=>/^\s*\[/.test(de)),re=Y===-1?J:J.slice(0,Y),ie=Y===-1?[]:J.slice(Y);return[...re.filter((de)=>!j.test(de)),...ie].join(`
|
|
247
|
+
`)}function qd(_,N,j){let J=new RegExp(`^\\s*\\[\\[${N}]]\\s*$`,"m");return _.split(/(?=^\s*\[)/m).filter((Y)=>!J.test(Y)||!Y.includes(j)).join("").trimEnd()}var yr=Cn["kimi-code"],vs=`[[hooks]]
|
|
248
248
|
event = "PreToolUse"
|
|
249
|
-
command = "${
|
|
250
|
-
`;return`${
|
|
251
|
-
|
|
252
|
-
${
|
|
253
|
-
`}function
|
|
254
|
-
`),{path:
|
|
255
|
-
`;return
|
|
256
|
-
`)},opencode:{installCommands:
|
|
257
|
-
`),`Enabled ${
|
|
258
|
-
`),`Enabled npm:cc-safety-net extensions in ${
|
|
259
|
-
`)}async function
|
|
260
|
-
Removing the plugin files anyway; ${
|
|
261
|
-
`)}var
|
|
262
|
-
`);function
|
|
249
|
+
command = "${yr}"`,Wd=`{ event = "PreToolUse", command = "${yr}" }`,zd={stringError:"Unterminated string in Kimi Code config",bracketError:"Unmatched hooks array in Kimi Code config"};function Yd(_){return Vd(_.env.get("KIMI_CODE_HOME")??Vd(_.home,".kimi-code"),"config.toml")}function wv(_){let N=hs(_,"hooks",zd);if(N&&_.slice(N.start+1,N.end).trim())return Ud(_,N,Wd);let j=Bd(_,"hooks").trimEnd();if(j==="")return`${vs}
|
|
250
|
+
`;return`${j}
|
|
251
|
+
|
|
252
|
+
${vs}
|
|
253
|
+
`}function Zd(_){let N=Yd(_);if(vv(bv(N),{recursive:!0}),!Kd(N))return ln(N,`${vs}
|
|
254
|
+
`),{path:N,alreadyInstalled:!1};let j=Jd(N,"utf-8");if(j.includes(yr))return{path:N,alreadyInstalled:!0};return ln(N,wv(j)),{path:N,alreadyInstalled:!1}}function Xd(_){let N=Yd(_);if(!Kd(N))return{path:N,alreadyInstalled:!1};let j=Jd(N,"utf-8");if(!j.includes(yr))return{path:N,alreadyInstalled:!1};let J=hs(j,"hooks",zd),Y=J?Gd(j,J,Wd):`${qd(j,"hooks",yr)}
|
|
255
|
+
`;return ln(N,Y),{path:N,alreadyInstalled:!0}}var bs="safety-net@cc-marketplace",Qd=new Set(["claude-code","codex","copilot-cli","gemini-cli","hermes-agent","openclaw","opencode","pi"]),eu=new Set(["antigravity-cli","cursor","devin","droid","grok-build","hermes-agent","kimi-code"]);function ws(_){return/^\s*safety-net@cc-marketplace[^a-z0-9-][^\n]*installed,/m.test(_??"")}function iu(_){return/^\s*cc-safety-net[^a-z0-9-][^\n]*installed,/m.test(_??"")}function kv(_){return/^Marketplace `cc-marketplace`\s*$/m.test(_??"")}var su={"claude-code":{installCommands:(_)=>{let N=Vr(_,"cc-safety-net@cc-marketplace");return{commands:[...N?[["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"]],...Kr(_).status==="disabled"?[["claude","plugin","enable","cc-safety-net@cc-marketplace"]]:[]],cleanupCommands:Vr(_,bs)?[["claude","plugin","uninstall",bs]]:[],update:N}},uninstallCommands:[["claude","plugin","uninstall","cc-safety-net@cc-marketplace"],["claude","plugin","marketplace","remove","cc-marketplace"]]},codex:{installCommands:async(_,N)=>{let j=N??await mn(["codex","plugin","list"]),J=iu(j);return{commands:[J||kv(j)?["codex","plugin","marketplace","upgrade","cc-marketplace"]:["codex","plugin","marketplace","add","kenryu42/cc-marketplace"],["codex","plugin","add","cc-safety-net@cc-marketplace"]],cleanupCommands:ws(j)?[["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:Qa},"copilot-cli":{installCommands:async()=>{let _=await mn(["copilot","plugin","list"]),N=[...ul(_)?[["copilot","plugin","uninstall","copilot-safety-net"]]:[],...pl(_)?[["copilot","plugin","uninstall",ll]]:[]];if(cl(_))return{commands:[["copilot","plugin","marketplace","update","cc-marketplace"],["copilot","plugin","update",Tn]],cleanupCommands:N,update:!0};return{commands:[dl(await mn(["copilot","plugin","marketplace","list"]))?["copilot","plugin","marketplace","update","cc-marketplace"]:["copilot","plugin","marketplace","add","kenryu42/cc-marketplace"],["copilot","plugin","install",Tn]],cleanupCommands:N}},uninstallCommands:[["copilot","plugin","uninstall","cc-safety-net@cc-marketplace"],["copilot","plugin","marketplace","remove","cc-marketplace"]]},"gemini-cli":{installCommands:(_)=>{let N=Ti(_);if(N.status==="configured")return{commands:[["gemini","extensions","update","gemini-safety-net"]],update:!0};if(N.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:Mi,installCommands:(_)=>{let N=!xo(Co(lr(_),Sn));return{commands:bc(),afterInstall:()=>wc(N)}},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(`
|
|
256
|
+
`)},opencode:{installCommands:Ac},pi:{installCommands:[["pi","install","npm:cc-safety-net"]],uninstallCommands:[["pi","uninstall","npm:cc-safety-net"]]},"deepseek-harness":{installCommands:(_)=>Nd(_),uninstallCommands:(_)=>jd(_)}};function au(_,N=(j)=>j){try{let j=JSON.parse(N(ou(_,"utf-8")));if(!j||typeof j!=="object"||Array.isArray(j))throw Error(`Settings file ${_} must be a JSON object`);return j}catch(j){if(j instanceof SyntaxError)throw Error(`Failed to parse ${_}: ${j.message}`);throw j}}function Cv(_){let N=Co(tr(_),"settings.json");if(!xo(N))return;let j=au(N,bn),J=j.enabledPlugins;if(!J||typeof J!=="object"||Array.isArray(J))return;if(J[Tn]!==!1)return;let Y=ou(N,"utf-8"),re=Y.replace(new RegExp(`("${Tn}"\\s*:\\s*)false`),"$1true");return J[Tn]=!0,ln(N,re!==Y?re:`${JSON.stringify(j,null,2)}
|
|
257
|
+
`),`Enabled ${Tn} plugin in ${N}`}function xv(_){let N=Yi(_);if(!xo(N))return;let j=au(N);if(!Array.isArray(j.packages))return;let J=j.packages.find((Y)=>!!Y&&typeof Y==="object"&&!Array.isArray(Y)&&Zi(Y.source)&&("extensions"in Y));if(!J)return;return delete J.extensions,ln(N,`${JSON.stringify(j,null,2)}
|
|
258
|
+
`),`Enabled npm:cc-safety-net extensions in ${N}`}function nu(_,N){let j=gn({label:N,booleans:Object.fromEntries(In.map((re)=>[re.target,[re.flag]]))},_),J=j.errors[0];if(J)throw Error(J);let Y=In.filter((re)=>j.flags[re.target]).map((re)=>re.target);if(Y.length!==1)throw Error(`Choose exactly one ${N} target: ${In.map((re)=>re.flag).join(", ")}`);return Y[0]}async function lu(_,N=Lt){let[j,J,Y]=await Promise.all([N(["amp","plugins","list"],30000),N(["codex","plugin","list"],30000),N(["copilot","--binary-version"])]);return{codexPluginListOutput:J,hooks:Vt(_,process.cwd(),{ampPluginListOutput:j,codexPluginListOutput:J,copilotCliVersion:Y})}}async function Sv(_,N,j=Lt){let J=await lu(_,j);return J.hooks.filter((Y)=>N==="install"?Y.configured:Y.detected&&Y.method!==eo||Y.inspectionStatus==="not-inspected").filter((Y)=>Y.platform!=="codex"||!ws(J.codexPluginListOutput)||iu(J.codexPluginListOutput)).map((Y)=>Y.platform)}function Pv(_,N,j,J){if(j.length>0)return{finish:async()=>[nu(j,N)]};if(!J.selectTargets&&!as(J.input,J.output))return{finish:async()=>[nu(j,N)]};let Y=J.detectConfiguredTargets??(()=>Sv(_,N,J.fetchVersion)),re=Promise.all([Rd(J.probeTargets).then((ie)=>Hd(ie,_)),Y()]);return{ready:re,finish:async()=>{let[ie,de]=await re,fe=Ed(ie,{action:N,configuredTargets:de}),ke=J.selectTargets?await J.selectTargets(N,ru(N,fe)):await ld(N,ru(N,fe),{input:J.input,output:J.output});if(ke==="update")return ke;if(!ke||ke.length===0)return null;return fs(ke)}}}async function Rv(_,N,j=!1,J){let Y=su[_];Y.beforeInstall?.(N);let re=typeof Y.installCommands==="function"?await Y.installCommands(N,J):{commands:Y.installCommands};return await ui(re.commands),await hl(re.cleanupCommands??[]),await re.afterInstall?.(),[`${re.update||j?"Updated":"Installed"} ${cn(_)} integration`,re.message??Y.postInstallMessage].filter(Boolean).join(`
|
|
259
|
+
`)}async function Ev(_,N){let j=su[_];if(!j.uninstallCommands)throw Error(`${cn(_)} uninstall is not supported`);let J=typeof j.uninstallCommands==="function"?j.uninstallCommands(N):{commands:j.uninstallCommands};return await ui(J.commands),J.afterUninstall?.(),`Uninstalled ${cn(_)} integration`}function _v(_){let N=Dc(_);return N.alreadyInstalled?`Uninstalled OpenCode plugin from ${N.path}`:`OpenCode plugin not installed in ${N.path}`}var cu={"antigravity-cli":{install:xd,uninstall:Sd},cursor:{install:xl,uninstall:Sl},devin:{install:$l,uninstall:Dl},droid:{install:Gl,uninstall:Bl},"grok-build":{install:Zl,uninstall:Xl},"kimi-code":{install:Zd,uninstall:Xd}};function Tv(_,N,j,J=!1){if(_==="install"&&!J)ko(j);let Y=cu[N][_](j),re=cn(N),ie=_!=="install"?"Uninstalled":J?"Updated":"Installed";return _==="install"&&Y.alreadyInstalled?J?`${re} hook up to date in ${Y.path}`:`${re} hook already installed in ${Y.path}`:_==="uninstall"&&!Y.alreadyInstalled?`${re} hook not installed in ${Y.path}`:`${ie} ${re} hook ${_==="install"?"in":"from"} ${Y.path}`}var du={amp:{install:hd,uninstall:vd,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:oc,uninstall:ic,afterInstall:async(_)=>{let N=Hi(_);return await mn(["hermes","plugins","enable",Pn,"--no-allow-tool-override"]),!N},beforeUninstall:async(_)=>{ji(_);try{await mn(["hermes","plugins","disable",Pn])}catch(N){console.warn(`${N instanceof Error?N.message:String(N)}
|
|
260
|
+
Removing the plugin files anyway; ${Pn} may still be listed in the Hermes config.`)}},restartNote:"Restart Hermes to apply the change."}};async function Av(_,N,j,J=!1){let Y=du[N];if(_==="uninstall")await Y.beforeUninstall?.(j);let re=_==="install"?await Y.install(j):await Y.uninstall(j),ie=_==="install"&&await Y.afterInstall?.(j),de=cn(N),fe=!ie&&(_==="install"&&re.alreadyInstalled||_==="uninstall"&&!re.alreadyInstalled);return[fe?_==="install"?`${de} plugin ${J?"up to date":"already installed"} at ${re.path}`:`${de} plugin not installed at ${re.path}`:`${_!=="install"?"Uninstalled":J?"Updated":"Installed"} ${de} plugin ${_==="install"?"at":"from"} ${re.path}`,fe?void 0:Y.restartNote].filter(Boolean).join(`
|
|
261
|
+
`)}var Ov={"copilot-cli":{afterInstall:Cv},cursor:{afterUninstall:bl},"hermes-agent":{beforeInstall:(_,N)=>{if(!N)ko(_)}},openclaw:{beforeUninstall:Mi},pi:{afterInstall:xv}};function Iv(_){return _ in cu}function $v(_){return _ in du}var tu=["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/tree/plugin"," 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(`
|
|
262
|
+
`);function Dv(_){if(ar({environment:_,cwd:process.cwd()}).status!=="configured")return tu;return[tu,"",en.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(`
|
|
263
263
|
`))].join(`
|
|
264
|
-
`)}function
|
|
265
|
-
`);if(
|
|
266
|
-
`)}function
|
|
267
|
-
Update available: cc-safety-net ${
|
|
268
|
-
`)},
|
|
269
|
-
`)),await
|
|
270
|
-
`),0;if(
|
|
271
|
-
`);return}if(
|
|
272
|
-
`);return}}let
|
|
273
|
-
`)}),0}catch(
|
|
274
|
-
Check file permissions for the target config file and parent directory.`;if(
|
|
275
|
-
Check that the target config path and parent directory exist.`;if(
|
|
276
|
-
Check that every parent path component is a directory.`;return
|
|
277
|
-
`,384),tv(j,384),{path:j,policy:Y,errors:[]}}function Xd(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 Qd(P,O={}){let D=l(P,O);if(!zd(D))return Hn(P,te,O);let j=Yd(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 eu=new Set(["check","apply"]),nu="(unset)";async function ru(P,O,D={}){let j=yn({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},O),J=j.positionals[0],Y=[...j.errors,...J&&!eu.has(J)?[`Unknown policy subcommand: ${J}`]:[],...J&&eu.has(J)&&!j.positionals[1]?[`policy ${J} requires a file`]:[],...j.positionals.slice(2).map((qe)=>`Unexpected policy argument: ${qe}`)];if(Y.length>0){for(let qe of Y)console.error(qe);return 1}let re=j.positionals[1];if(!J||!re)return Lt(yr,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,...lr(fe.value,P.home).map((qe)=>`${re}: ${qe}`),...!j.flags.global&&dv(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 qe of we)console.error(qe);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)tu(E(bt(de).value,P.home),Ce,!0);if(!j.flags.global){let qe=cr(P).baseline;console.log("Effective policy (user + project merged):"),tu(ee(qe,ue(bt(de).value,P.home).policy).policy,ee(qe,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 lv(`Apply this policy to ${de}? [y/N] `,Se,Ie))return console.log("Cancelled; nothing was written."),0;return cv(P,de,fe.value,Ce,j.flags.global),console.log(`Policy applied: ${de}`),0}function lv(P,O,D){let j=av({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 cv(P,O,D,j,J){if(J){Hn(P,j);return}iv(sv(O),{recursive:!0}),wn(O,po(D,j))}function tu(P,O,D){let j=uo(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??nu} -> ${J.after??nu}`)}function dv(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as Cb}from"node:path";var ou="# 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 fo(P,O){if(!P.ok){cu(P);return}au(P,O)}function su(P,O,D){if(P.ok)console.log(D);if(!P.add){fo(P,`Added rulebook source: ${O}`);return}if(!P.ok){cu(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(", ")}.`);au(P,"Rule config updated.")}function au(P,O){for(let D of P.changes??[])console.log(D);console.log(O),console.log(""),uv(P.entries)}function uv(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} (${pv(O.ruleCount)})`),console.log(` Source: ${O.spec}`)}function pv(P){return`${P} ${P===1?"rule":"rules"}`}function lu(P){wt("Active sources",P.rulebooks,(O)=>[`[${O.source}] ${O.name} ${O.version}`,` Source: ${O.spec}`]),wt("Active rules",P.rules,(O)=>[`[${mv(P,O.name)}] ${O.name}`,...fv(O),` Reason: ${O.reason}`]),wt("Disabled rules",iu(P,"off"),(O)=>[O.key]),wt("Reason overrides",iu(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 fv(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 mv(P,O){return P.rulebooks.find((D)=>D.rules.includes(O))?.source??"project"}function iu(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 cu(P){for(let O of P.errors)console.error(O)}import{dirname as $u,join as ko}from"node:path";import{join as us,resolve as Rv}from"node:path";function as(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 du(P){wn(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function uu(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 ho}from"node:path";var gv="custom.";function mo(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=ls(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(gv.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 ls(P){return P.nodes.flatMap((O)=>{if(O.kind==="group"||O.kind==="function")return ls(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)=>ls(j))]})}var go=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function yo(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??go.maxRequests,maxResponseBytes:P.maxResponseBytes??go.maxResponseBytes}}function Mn(P){return{controller:new AbortController,budget:yo(),resolveUrl:P}}function pu(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function fu(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function mu(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 hu=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function gu(P,O,D=S(ho(ho(O)),"rules policy"),j=Mn()){if(w(P))return bv(P,j);return vv(P,O,D)}async function vu(P,O,D,j,J,Y){if(!w(P))return gu(P,O,D,j);let re=J?null:yv(P,O,D);if(re)return re;if(!J&&!Y)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return gu(P,O,D,j)}function yv(P,O,D=S(ho(ho(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(cs(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 bu(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 hv(D,j,P,J),re=await ku(D,j,Y,P,J),ie=await vo(`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 hv(P,O,D,j){let J=await vo(`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 vv(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=wu(cs(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 bv(P,O){let D=N(P),j=await ku(D.owner,D.repo,D.ref,P,O),J=await vo(`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=wu(cs(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 wu(P){let O=le(P),D=mo(O);if(D.length>0)throw Error(D.join("; "));return O}function cs(P,O){try{return JSON.parse(P)}catch{throw Error(O)}}async function ku(P,O,D,j,J){let Y=await vo(`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 wv(P,O,D={}){if(D.signal?.aborted)throw D.signal.reason;let j=D.budget??yo(),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??hu.timeoutMs);try{if(D.signal?.aborted)throw D.signal.reason;fu(j);let de=await fetch(P,{signal:J.signal,redirect:"error"});if(!de.ok)return xu(de),{response:de,content:""};return{response:de,content:await kv(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 vo(P,O,D){return wv(D.resolveUrl?.(P)??P,O,{budget:D.budget,signal:D.controller.signal})}async function kv(P,O,D=yo(),j){let J=hu[`${O}Bytes`],Y=Number(P.headers.get("content-length"));if(Number.isFinite(Y)&&Y>J)throw xu(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{mu(D,fe.value.byteLength)}catch(we){throw j?.(),yu(re),we}if(de+=fe.value.byteLength,de>J)throw j?.(),yu(re),Error(`GitHub ${O} response exceeds ${J} bytes`);ie.push(Buffer.from(fe.value))}return Buffer.concat(ie,de).toString("utf-8")}function xu(P){if(!P.body)return;Cu(()=>P.body?.cancel())}function yu(P){Cu(()=>P.cancel())}function Cu(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var xv=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function Su(P,O){let D=Eu(P.rules,O);if(D.length>0)return{ok:!0,specs:D};return Ru(P.rules,O)}function Pu(P,O){let D=Eu(P,O);if(D.length>0)return{ok:!0,specs:D};let j=Sv(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 Ru(P,O)}function Ru(P,O){let D=P.filter((j)=>ds(j)?.name===O);if(D.length===1)return{ok:!0,specs:D};return Cv(O,D)}function Cv(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 Eu(P,O){return P.filter((D)=>D===O)}function Sv(P,O){let D=O.match(xv),j=D?.[1],J=D?.[2],Y=D?.[3];if(!j||!J||!Y||!ae(Y))return[];return _u(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=_u(P,(re)=>re.owner===D&&re.repo===j);if(new Set(J.map((re)=>ds(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 ds(P){try{return N(P)}catch{return null}}function _u(P,O){return P.filter((D)=>{let j=ds(D);return j?O(j):!1})}async function wo(P,O={}){let D=ps(O);return Ev(P,D,await bo(P,D,Mn()))}function Ev(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 bo(P,O,D,j={},J=new Set,Y=new Set){try{let re=kn(P,O),ie=as(re.configTarget);if(!ie.ok)return ie.result;let de=ie.config,fe=O.only?Su(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)=>vu(rn,re.configDir,re.filesystemScope,D,we.has(rn),!O.refresh||we.has(rn)),Se=await Fv(de.rules,O.refresh?(rn)=>Ce(rn).then((fn)=>({ok:!0,item:fn})).catch((fn)=>{if(pu(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),qe=Ee.flatMap((rn)=>_v(rn,de.rules)),Me=Ee.flatMap((rn)=>Av(rn,Y,re)),en=new Set([...qe,...Me].map((rn)=>rn.spec)),on=[...Ie,...qe,...Me],sn=[],an=Iv(sn,()=>Ee.flatMap((rn)=>en.has(rn.spec)||on.length>0&&Y.has(rn.spec)?[]:Tv(rn,re,j,sn)));return{ok:on.length===0,errors:on.map((rn)=>`Failed to update ${rn.spec}: ${rn.message}`),entries:Ee.map(Ov),changes:an}}catch(re){return ur(re)}}function _v(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 Av(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 Tv(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),$v(P,re)}function Iv(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 $v(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 Ov(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function Au(P,O,D={}){return Dv(P,O,Mv(D),Mn())}async function Dv(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=as(ie.configTarget);if(!fe.ok)return fe.result;let we=fe.config,Ce=Z(O);Lv(O,D,Ce);let Se=Ce?await bu(O,{ref:D.ref,operation:j}):null,Ie=Se?Nv(Se,D.rulebooks):[],Ee=Se?Ie.map((sn)=>jv(we.rules,Se,sn)??`${O}#${Se.ref}/${sn}`):[O],qe=Ee.filter((sn)=>!we.rules.includes(sn)),Me=[...we.rules,...qe];if(Me.length>ye)return Hv();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 bo(P,D,j,J,new Set(qe),new Set(qe));if(!en.ok)dr(ie.configTarget,de);if(!en.ok||!Se)return en;let on=Ie.filter((sn,an)=>qe.includes(Ee[an]??""));return{...en,add:{source:O,ref:Se.ref,selected:Ie,added:on,alreadyConfigured:Ie.filter((sn)=>!on.includes(sn)),commits:qe.length>0?[Se.commit]:[]}}}catch(ie){if(re&&Y)try{dr(Y.target,Y.content)}catch(de){return ur(de)}return ur(ie)}}function Lv(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 Nv(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(", ")}
|
|
278
|
-
Available rulebooks: ${P.names.join(", ")}`);return D}function jv(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 Fv(P,O,D=Mn()){if(P.length>ye)throw Error(be);let j=[],J=0,Y,re=Array.from({length:Math.min(P.length,go.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 Hv(){return{ok:!1,errors:[be],entries:[]}}function ps(P){return{cwd:P.cwd,userConfigDir:P.userConfigDir,userConfigPath:P.userConfigPath,projectConfigPath:P.projectConfigPath,global:P.global,only:P.only,refresh:P.refresh}}function Mv(P){return{...ps(P),ref:P.ref,rulebooks:P.rulebooks}}function Uv(P){return{...ps(P),deleteSource:P.deleteSource}}async function Tu(P,O,D={}){try{return await Gv(P,O,Uv(D),{})}catch(j){return ur(j)}}async function Gv(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=Pu(Y.config.rules,O);if(!re.ok)return re.result;let ie=D.deleteSource?qv(J.configDir,re.specs,J.filesystemScope):{ok:!0,dirs:[]};if(!ie.ok)return ie.result;let de=r(J.configTarget);if(de===null)return ur(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 dr(J.configTarget,de),Ce}let fe=await bo(P,D,Mn(),j);if(!fe.ok)return dr(J.configTarget,de),fe;let we=Bv(ie.dirs,j,J.filesystemScope);if(!we.ok){dr(J.configTarget,de);let Ce=await bo(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 qv(P,O,D){let j=O.flatMap((ie)=>d.test(ie)?[]:["--delete-source can only delete local rulebook sources"]),J=O.map((ie)=>us(P,ie)),Y=j.length>0?[]:J.flatMap((ie)=>Iu(ie,D)),re=[...j,...Y];return re.length>0?{ok:!1,result:{ok:!1,errors:re,entries:[]}}:{ok:!0,dirs:J}}function Iu(P,O){let D=Rv(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,us(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 Bv(P,O,D){let j=P.flatMap((J)=>{try{if(!me(i(D,J)))return[];let Y=Iu(J,D);if(Y.length>0)return Y;return Vv(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 Vv(P,O,D){if(O._testDeleteLocalSourceDir){O._testDeleteLocalSourceDir(P);return}H(i(D,us(P,ge))),at(i(D,P))}function dr(P,O){if(O===null){H(P);return}g(P,O)}function ur(P){return{ok:!1,errors:[P instanceof Error?P.message:String(P)],entries:[]}}var Jv=".safety-net.json",Kv="~/.cc-safety-net/config.json";async function Lu(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 Ou(P,{legacyPath:wa({cwd:O.cwd}),configPath:_(O.cwd),defaultRulebookName:"project-rules",migratedFrom:Jv,cleanup:O.cleanup,syncOptions:{cwd:O.cwd}}),await Ou(P,{legacyPath:Ct(P),configPath:W(P),defaultRulebookName:"user-rules",migratedFrom:Kv,cleanup:O.cleanup,syncOptions:{cwd:O.cwd,global:!0}})].every((J)=>J)?0:1}async function Ou(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=zv(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=Yv($u(O.configPath),ie.rules,O.defaultRulebookName,O.migratedFrom,D.filesystemScope),fe=ko($u(O.configPath),de,"rulebook.json"),we=i(D.filesystemScope,fe),Ce=[Du(D.configTarget),Du(we)],Se=await Wv(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){Qv(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(!Xv(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 Wv(P,O,D,j,J,Y,re,ie,de){try{return wn(D,{version:1,rules:re,overrides:ie,transparent_wrappers:de}),wn(j,Zv(J,O.migratedFrom,Y)),await wo(P,O.syncOptions)}catch(fe){return{ok:!1,errors:[fe instanceof Error?fe.message:String(fe)]}}}function zv(P){try{let O=JSON.parse(P),D=Io(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 Yv(P,O,D,j,J){let Y=O.find((re)=>eb(i(J,ko(P,re,"rulebook.json")))===j);if(Y)return Y;if(r(i(J,ko(P,D,"rulebook.json")))===null)return D;for(let re=2;;re++){let ie=`${D}-${re}`;if(r(i(J,ko(P,ie,"rulebook.json")))===null)return ie}}function Zv(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 Xv(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 Du(P){return{target:P,content:r(P)}}function Qv(P){for(let O of P){if(O.content===null){H(O.target);continue}g(O.target,O.content)}}function eb(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 nb,readFile as tb,writeFile as rb}from"node:fs/promises";import{dirname as ob,join as ib}from"node:path";var sb=86400000,ab=604800000;async function ju(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=ib(D,".cc-safety-net","update-check.json"),J=await lb(j,O);if(!J.lastCheck||O-J.lastCheck>sb){let ie=await Gn();if(J.lastCheck=O,ie.latestVersion)J.latestVersion=ie.latestVersion;if(!await Nu(j,J))return null;if(ie.error)return null}let Y=J.latestVersion,re=pn();if(!Y||!jo(Y,re))return null;if(J.notifiedVersion===Y&&J.notifiedAt!==void 0&&O-J.notifiedAt<ab)return null;if(J.notifiedVersion=Y,J.notifiedAt=O,!await Nu(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 lb(P,O){let D=await tb(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 Nu(P,O){return nb(ob(P),{recursive:!0,mode:448}).then(()=>rb(P,JSON.stringify(O),{mode:384})).then(()=>!0).catch(()=>!1)}import{join as cb,resolve as fs}from"node:path";var Fu="CC Safety Net Config",db="═".repeat(Fu.length),ub="https://raw.githubusercontent.com/kenryu42/cc-safety-net/main/assets/cc-safety-net.schema.json",pb=new Set(["rule.json","rule.lock","cache"]);function Hu(P,O={}){try{return fb(P,O)}catch(D){if(D instanceof o)return console.error(D.message),1;throw D}}function fb(P,O){let D=O.cwd??process.cwd(),j=X(P,{cwd:D}),J=Ct(P),Y=wr(D),re=fs(D,he),ie=i(j.userScope,J),de=i(j.projectScope,Y),fe=!1,we=!1,Ce=[],Se=[],Ie=mb(i(j.projectScope,re));if(yb(),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(xo("user","cleanup"));else{let Ee=$o(ie);if(Ce.push({scope:"User",path:J,result:Ee,schema:"legacy",inactive:!0,target:ie}),Se.push(xo("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:fs(j.projectConfigPath),result:Ee,schema:"rules",target:j.projectConfigTarget}),Ee.errors.length>0)fe=!0;if(r(de)!==null)we=!0,Se.push(xo("project","cleanup"))}else if(r(de)!==null){we=!0,fe=!0;let Ee=$o(de);Ce.push({scope:"Project",path:fs(Y),result:Ee,schema:"legacy",inactive:!0,target:de}),Se.push(xo("project",Ee.errors.length>0?"fix-or-delete":"migrate"))}if(Ie?.result.errors.length)fe=!0;if(Ce.length===0&&!Ie)return console.log(`
|
|
279
|
-
No config files found. Using built-in rules only.`),0;for(let
|
|
280
|
-
Added $schema to ${
|
|
281
|
-
${
|
|
282
|
-
Config validation failed.`),1;return console.log(
|
|
264
|
+
`)}function ru(_,N){return N.map((j)=>_==="install"&&j.target==="kimi-code"&&j.unavailableReason==="already installed"?{...j,available:!0,unavailableReason:void 0,label:`${j.label} (global hook installed)`}:j)}function Lv(_,N){if(_.selectKimiInstallMethod)return _.selectKimiInstallMethod();if(!as(_.input,_.output))return Promise.resolve("global-hook");return ad({input:_.input,output:_.output,globalHookInstalled:ar({environment:N,cwd:process.cwd()}).status==="configured"})}async function uu(_,N,j,J=!1,Y){let re=Ov[N];if(_==="install")re?.beforeInstall?.(j,J);if(_==="uninstall")re?.beforeUninstall?.(j);if(Iv(N))return[Tv(_,N,j,J),_==="uninstall"?await re?.afterUninstall?.(j):void 0].filter(Boolean).join(`
|
|
265
|
+
`);if($v(N))return Av(_,N,j,J);if(_==="uninstall")return N==="opencode"?_v(j):Ev(N,j);return[await Rv(N,j,J,Y),await re?.afterInstall?.(j)].filter(Boolean).join(`
|
|
266
|
+
`)}function Nv(_){let N=gn({label:"update"},_).errors[0];if(N)throw Error(N)}async function jv(_,N=Lt){let j=await lu(_,N),J=Co(tr(_),"installed-plugins");return{targets:fs([...j.hooks.filter((re)=>re.platform!=="copilot-cli"&&re.detected&&re.method!==eo&&re.method!==hi).map((re)=>re.platform),...[Jr,al,sl].flatMap((re)=>xo(Co(J,...re))?["copilot-cli"]:[]),...Vr(_,bs)?["claude-code"]:[],...ws(j.codexPluginListOutput)?["codex"]:[]]),codexPluginListOutput:j.codexPluginListOutput}}async function Hv(_){let N=c(),j=_.output??process.stdout,J=(_.scriptPath??process.argv[1]??"").split(/[\\/]/),Y=J.find((Ee)=>/^bunx-\d+-/.test(Ee)),re=Y!==void 0||J.includes("_npx")?null:(_.checkLatestVersion??Gn)(),ie=async()=>{let Ee=re&&await re;if(Ee?.updateAvailable)j.write(`
|
|
267
|
+
Update available: cc-safety-net ${Ee.currentVersion} → ${Ee.latestVersion}. Update this CLI with your package manager, e.g. \`npm i -g cc-safety-net@latest\` for a global install.
|
|
268
|
+
`)},de=jv(N,_.fetchVersion??Lt).then(async(Ee)=>{let Ne=new Set(Ee.targets);return{targets:Ee.targets,codexPluginListOutput:Ee.codexPluginListOutput,available:new Map(await Promise.all(In.filter((Je)=>Ne.has(Je.target)&&Qd.has(Je.target)).map(async(Je)=>[Je.target,await gr(Je.probeCommand)])))}}),fe=await Xt(_.showBanner??!0,()=>({ready:de,finish:()=>de}),()=>Zt({input:_.input??process.stdin,output:j}),{loadingMessage:"Checking installed integrations…",output:j}),ke=await Promise.resolve().then(()=>(Fd(N.tmpdir,process.platform,Y),null)).catch((Ee)=>hr(Ee));if(fe.targets.length===0){if(j.write("No installed integrations found. Run `cc-safety-net install` to set one up.\n"),ke!==null)console.error(ke);return await ie(),ke===null?0:1}let Ce=fe.targets.some((Ee)=>eu.has(Ee))?await Promise.resolve().then(()=>(ko(N),null)).catch((Ee)=>hr(Ee)):null,Re=await Hr(Promise.all(fe.targets.map((Ee)=>{if(Qd.has(Ee)&&!fe.available.get(Ee))return Promise.resolve({message:`${cn(Ee)} not found; skipped`,failed:!1});if(Ce!==null&&eu.has(Ee))return Promise.resolve({message:Ce,failed:!0});return uu("install",Ee,N,!0,fe.codexPluginListOutput).then((Ne)=>({message:Ne,failed:!1}),(Ne)=>({message:hr(Ne),failed:!0}))})),{loadingMessage:`Updating ${fe.targets.length} integration${fe.targets.length===1?"":"s"}…`,output:j}),Oe=ke===null?Re:[...Re,{message:ke,failed:!0}];return Oe.forEach((Ee)=>Ee.failed?console.error(Ee.message):j.write(`${Ee.message}
|
|
269
|
+
`)),await ie(),Oe.some((Ee)=>Ee.failed)?1:0}function ks(_,N={}){return Promise.resolve().then(()=>Nv(_)).then(()=>Hv(N)).catch((j)=>(console.error(hr(j)),1))}async function vr(_,N,j={}){try{let J=c(),Y=await Xt(!0,()=>Pv(J,_,N,j),()=>Zt({input:j.input??process.stdin,output:j.output??process.stdout}),{loadingMessage:_==="install"?"Checking available integrations…":"Checking installed integrations…",output:j.output??process.stdout});if(!Y)return(j.output??process.stdout).write(`Cancelled: nothing was ${_}ed.
|
|
270
|
+
`),0;if(Y==="update")return(j.runUpdate??(()=>ks([],{fetchVersion:j.fetchVersion,input:j.input,output:j.output,showBanner:!1})))();let re=j.output??process.stdout;return await Pd(Y,async(ie)=>{if(ie==="kimi-code"&&_==="install"){let fe=await Lv(j,J);if(fe===null){re.write(`Cancelled: Kimi Code integration was not installed.
|
|
271
|
+
`);return}if(fe==="plugin"){re.write(`${Dv(J)}
|
|
272
|
+
`);return}}let de=await Hr(uu(_,ie,J),{loadingMessage:`${_==="install"?"Installing":"Uninstalling"} ${cn(ie)} integration…`,output:re});re.write(`${de}
|
|
273
|
+
`)}),0}catch(J){return console.error(hr(J)),1}}function hr(_){let N=_ instanceof Error?_.message:String(_),j=typeof _==="object"&&_!==null&&"code"in _?_.code:null;if(j==="EACCES"||j==="EPERM")return`${N}
|
|
274
|
+
Check file permissions for the target config file and parent directory.`;if(j==="ENOENT")return`${N}
|
|
275
|
+
Check that the target config path and parent directory exist.`;if(j==="ENOTDIR")return`${N}
|
|
276
|
+
Check that every parent path component is a directory.`;return N}import{mkdirSync as qv}from"node:fs";import{dirname as Vv}from"node:path";import{createInterface as Kv}from"node:readline";import{existsSync as fu,readFileSync as Fv}from"node:fs";function At(_,N){let j=dt(_,N);return{policy:j.policy,errors:ce(ct(j.issues,ut,(J)=>J.kind==="custom")," "," ")}}function br(_,N){return At(_,N).errors}function pu(_,N){return{"safety.level":_.safety.level,...Cs("safety.overrides",_.safety.overrides),"workflow.worktree_mode":String(_.workflow.worktree_mode),"destructive_command_protection.enabled":String(_.destructive_command_protection.enabled),...Cs("destructive_command_protection.overrides",_.destructive_command_protection.overrides),"destructive_command_protection.allow_paths":xs(_.destructive_command_protection.allow_paths),"secret_protection.enabled":String(_.secret_protection.enabled),...Cs("secret_protection.overrides",_.secret_protection.overrides),"secret_protection.deny_paths":xs(_.secret_protection.deny_paths),"secret_protection.allow_paths":xs(_.secret_protection.allow_paths),...N?{"audit.retention_days":String(_.audit.retention_days)}:{}}}function So(_,N,j){let J=pu(_,j),Y=pu(N,j);return[...new Set([...Object.keys(J),...Object.keys(Y)])].flatMap((re)=>J[re]===Y[re]?[]:[{field:re,before:J[re],after:Y[re]}])}function wr(_,N){let j=u(_,N);if(!fu(j))return{baseline:k(globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__,_.home),diagnostics:[]};let J=Ot(j),Y=At(J.value,_.home);return{baseline:Y.policy,diagnostics:J.errors.length>0?J.errors:Y.errors}}function Ot(_){if(!fu(_))return{errors:[`${_}: file not found`]};try{return{value:JSON.parse(Fv(_,"utf-8")),errors:[]}}catch(N){let j=N instanceof Error?N.message:String(N);return{errors:[`${_}: ${N instanceof SyntaxError?`Invalid JSON: ${j}`:j}`]}}}function Po(_,N){let j=Mv(_)?_:{};return{version:N.version,...Object.fromEntries(["safety","workflow","destructive_command_protection","secret_protection"].filter((J)=>j[J]!==void 0).map((J)=>[J,j[J]]))}}function Cs(_,N){return Object.fromEntries(Object.entries(N).flatMap(([j,J])=>J===void 0?[]:[[`${_}.${j}`,String(J)]]))}function xs(_){return _.length===0?"(none)":_.join(", ")}function Mv(_){return!!_&&typeof _==="object"&&!Array.isArray(_)}import{chmodSync as Uv,existsSync as mu,mkdirSync as Gv,readFileSync as gu}from"node:fs";import{dirname as Bv}from"node:path";function yu(_,N={}){let j=u(_,N);if(!mu(j))return{path:j,exists:!1,raw:"",policy:F(),errors:[]};let J=gu(j,"utf-8");if(!J.trim())return{path:j,exists:!0,raw:J,policy:F(),errors:["Config file is empty"]};try{let Y=At(JSON.parse(J),_.home);return{path:j,exists:!0,raw:J,policy:Y.policy,errors:Y.errors}}catch(Y){return{path:j,exists:!0,raw:J,policy:F(),errors:[`Invalid JSON: ${Y instanceof Error?Y.message:String(Y)}`]}}}function Fn(_,N,j={}){let J=u(_,j),Y=At(N,_.home);if(Y.errors.length>0)return{path:J,policy:F(),errors:Y.errors};let re=Y.policy;return Gv(Bv(J),{recursive:!0,mode:448}),g(se(J),`${JSON.stringify(re,null,2)}
|
|
277
|
+
`,384),Uv(J,384),{path:J,policy:re,errors:[]}}function hu(_,N){let j=At(N,_.home);if(j.errors.length>0)return{errors:j.errors};return{preview:Ue(j.policy,_.env),errors:[]}}function vu(_,N={}){let j=u(_,N);if(!mu(j))return Fn(_,te,N);let J=gu(j,"utf-8");if(!J.trim())return Fn(_,te,N);try{return Fn(_,k(JSON.parse(J),_.home),N)}catch{return Fn(_,te,N)}}var bu=new Set(["check","apply"]),wu="(unset)";async function Cu(_,N,j={}){let J=gn({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},N),Y=J.positionals[0],re=[...J.errors,...Y&&!bu.has(Y)?[`Unknown policy subcommand: ${Y}`]:[],...Y&&bu.has(Y)&&!J.positionals[1]?[`policy ${Y} requires a file`]:[],...J.positionals.slice(2).map((Je)=>`Unexpected policy argument: ${Je}`)];if(re.length>0){for(let Je of re)console.error(Je);return 1}let ie=J.positionals[1];if(!Y||!ie)return Kt(Er,console.error),1;let de=j.cwd??process.cwd(),fe=J.flags.global?u(_):h(de);if(!J.flags.global&&A(_,{cwd:de}))return console.error(`${fe} is the user policy, not a project policy; use --global for the user scope, or run from a project directory`),1;let ke=Ot(ie),Ce=[...ke.errors,...br(ke.value,_.home).map((Je)=>`${ie}: ${Je}`),...!J.flags.global&&zv(ke.value)&&ke.value.audit!==void 0?[`${ie}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(Ce.length>0){for(let Je of Ce)console.error(Je);return 1}let Re=k(ke.value,_.home);if(console.log(`Scope: ${J.flags.global?"user":"project"} (${fe})`),console.log(`Proposal: ${ie}`),J.flags.global)ku(k(Ot(fe).value,_.home),Re,!0);if(!J.flags.global){let Je=wr(_).baseline;console.log("Effective policy (user + project merged):"),ku(ee(Je,ue(Ot(fe).value,_.home).policy).policy,ee(Je,ue(ke.value,_.home).policy).policy,!1)}if(Y==="check")return 0;let Oe=j.input??process.stdin,Ee=j.output??process.stdout;if(!Oe.isTTY||!Ee.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${ie}${J.flags.global?" --global":""}`),1;if(!await Jv(`Apply this policy to ${fe}? [y/N] `,Oe,Ee))return console.log("Cancelled; nothing was written."),0;return Wv(_,fe,ke.value,Re,J.flags.global),console.log(`Policy applied: ${fe}`),0}function Jv(_,N,j){let J=Kv({input:N,output:j,terminal:!1});return new Promise((Y)=>{J.once("close",()=>Y(!1)),J.question(_,(re)=>{Y(/^y(es)?$/i.test(re.trim())),J.close()})})}function Wv(_,N,j,J,Y){if(Y){Fn(_,J);return}qv(Vv(N),{recursive:!0}),vn(N,Po(j,J))}function ku(_,N,j){let J=So(_,N,j);if(J.length===0){console.log("No changes.");return}console.log(`Changes (${J.length}):`);for(let Y of J)console.log(` ${Y.field}: ${Y.before??wu} -> ${Y.after??wu}`)}function zv(_){return!!_&&typeof _==="object"&&!Array.isArray(_)}import{join as pw}from"node:path";var xu="# 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- `command_tools`: Optional array of shell tools that a plugin adds to an agent and that CC Safety Net does not know. Each entry is a tool name such as `\"ops_bash\"`, or `{ \"name\": \"ops_run_background\", \"cwd\": \"cwd\" }` when the tool takes its working directory in a named argument (otherwise `workdir`, `cwd`, or `working_directory` is read). A call to a declared tool with a non-empty string `command` is analyzed like a built-in shell command. User and project lists merge; the user entry wins a name declared in both.\n- Built-in shell and file tools are already analyzed and cannot be declared; `rule verify` reports one declared by hand.\n- Use `cc-safety-net rule tool add ops_bash ops_run_background` to declare tools in the user config, or add `--project` for the project config.\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 Ro(_,N){if(!_.ok){Tu(_);return}Ru(_,N)}function Pu(_,N,j){if(_.ok)console.log(j);if(!_.add){Ro(_,`Added rulebook source: ${N}`);return}if(!_.ok){Tu(_);return}if(_.add.added.length>0)console.log(`Added ${_.add.added.length} ${_.add.added.length===1?"rulebook":"rulebooks"} from ${_.add.source} at ${_.add.ref}:`),_.add.added.forEach((J)=>{console.log(` - ${J}`)});if(_.add.alreadyConfigured.length>0)console.log(`Rulebooks already configured from ${_.add.source} at ${_.add.ref}: ${_.add.alreadyConfigured.join(", ")}`);if(_.add.commits.length>0)console.log(`Vendored at ${_.add.commits.map((J)=>J.slice(0,7)).join(", ")}.`);Ru(_,"Rule config updated.")}function Ru(_,N){for(let j of _.changes??[])console.log(j);console.log(N),console.log(""),Yv(_.entries)}function Yv(_){if(_.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${_.length}):`);for(let N of _)console.log(` - ${N.name} ${N.version} (${Zv(N.ruleCount)})`),console.log(` Source: ${N.spec}`)}function Zv(_){return`${_} ${_===1?"rule":"rules"}`}function Eu(_){Jn("Active sources",_.rulebooks,(N)=>[`[${N.source}] ${N.name} ${N.version}`,` Source: ${N.spec}`]),Jn("Active rules",_.rules,(N)=>[`[${Qv(_,N.name)}] ${N.name}`,...Xv(N),` Reason: ${N.reason}`]),Jn("Disabled rules",Su(_,"off"),(N)=>[N.key]),Jn("Reason overrides",Su(_,"reason"),(N)=>[N.key,` Reason: ${N.value.reason}`]),Jn("Transparent wrappers",_.transparent_wrappers,(N)=>[N]),Jn("Issues",_.errors,(N)=>[N]),Jn("Warnings",_.warnings,(N)=>[N])}function _u(_){Jn("Command tools",_,(N)=>typeof N.tool==="string"?[`[${N.scope}] ${N.tool}`]:[`[${N.scope}] ${N.tool.name}`,...N.tool.cwd===void 0?[]:[` Cwd argument: ${N.tool.cwd}`]])}function Jn(_,N,j){if(N.length===0){console.log(`${_}: (none)`);return}console.log(`${_} (${N.length}):`);for(let J of N){let[Y,...re]=j(J);console.log(` - ${Y}`);for(let ie of re)console.log(` ${ie}`)}}function Xv(_){if(!_.match)return[` Command: ${_.subcommand?`${_.command} ${_.subcommand}`:_.command}`,` Block args: ${_.block_args.join(", ")}`];return[` Command: ${[_.command,..._.match.command_path].join(" ")}`,..._.match.any_args?[` Any args: ${_.match.any_args.join(", ")}`]:[],..._.match.exclude_args?[` Exclude args: ${_.match.exclude_args.join(", ")}`]:[]]}function Qv(_,N){return _.rulebooks.find((j)=>j.rules.includes(N))?.source??"project"}function Su(_,N){return Object.entries({..._.userConfig?.overrides,..._.projectConfig?.overrides}).filter((j)=>{if(N==="off")return j[1]==="off";return!!j[1]&&typeof j[1]==="object"}).map(([j,J])=>({key:j,value:J}))}function Tu(_){for(let N of _.errors)console.error(N)}import{dirname as Xu,join as Do}from"node:path";import{join as Es,resolve as ub}from"node:path";function kr(_){let N=d(_);if(N.errors.length>0)return{ok:!1,result:{ok:!1,errors:N.errors,entries:[]}};return{ok:!0,config:N.config??Ct}}function Au(_){vn(_,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function Ou(_){vn(_,{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 Ao}from"node:path";var eb="custom.";function Eo(_){if(_.rulebook_version!==2)return[];let N=_.rules.map((j)=>({name:j.name,command:j.command,block_args:[],match:j.match,reason:j.reason,intent:j.intent}));return(_.tests??[]).flatMap((j,J)=>{let Y=Ss(y(j.command));if(Y.length===0)return[`tests[${J}]: could not parse fixture command: ${j.command}`];let re=Y.reduce((ie,de)=>ie??D(de,N)?.id.slice(eb.length),void 0);if(j.expect==="blocked"){if(re===j.rule)return[];let ie=re?`"${re}" matched first`:"no rule matched";return[`tests[${J}]: expected "${j.rule}" to block "${j.command}" but ${ie}`]}return re?[`tests[${J}]: expected "${j.command}" to be allowed but "${re}" matched`]:[]})}function Ss(_){return _.nodes.flatMap((N)=>{if(N.kind==="group"||N.kind==="function")return Ss(N.body);if(N.kind!=="command")return[];let j=Se(ne(N.dialect,N.words)).words.map(t);return[...j.length>0?[j]:[],...N.nested.flatMap((J)=>Ss(J))]})}var _o=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function To(_={}){return{requests:0,responseBytes:0,maxRequests:_.maxRequests??_o.maxRequests,maxResponseBytes:_.maxResponseBytes??_o.maxResponseBytes}}function Mn(_){return{controller:new AbortController,budget:To(),resolveUrl:_}}function Iu(_){return _ instanceof Error&&_.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function $u(_){if(_.requests>=_.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");_.requests++}function Du(_,N){if(N>_.maxResponseBytes-_.responseBytes)throw _.responseBytes+=N,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");_.responseBytes+=N}var ju=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function Lu(_,N,j=w(Ao(Ao(N)),"rules policy"),J=Mn()){if(C(_))return ob(_,J);return rb(_,N,j)}async function Hu(_,N,j,J,Y,re){if(!C(_))return Lu(_,N,j,J);let ie=Y?null:nb(_,N,j);if(ie)return ie;if(!Y&&!re)throw Error(`${_} is not vendored; run rule update ${_} to vendor it`);return Lu(_,N,j,J)}function nb(_,N,j=w(Ao(Ao(N)),"rules policy")){let J=L(_),Y=M(N,J.name),re=r(i(j,Y));if(re===null)return null;let ie=le(Ps(re,`Invalid rulebook ${Y}.`));if(ie.name!==J.name)throw Error(`rulebook name "${ie.name}" in ${Y} must match "${J.name}"`);return{spec:_,rulebook:ie,content:re}}async function Fu(_,N={}){if(!Z(_))throw Error(`Invalid GitHub repository source: ${_}`);let[j,J]=_.split("/");if(!j||!J)throw Error(`Invalid GitHub repository source: ${_}`);if(N.ref!==void 0&&!ae(N.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${N.ref}`);let Y=N.operation??Mn(),re=N.ref??await tb(j,J,_,Y),ie=await Uu(j,J,re,_,Y),de=await Oo(`https://api.github.com/repos/${j}/${J}/git/trees/${ie}?recursive=1`,"tree",Y),fe=de.response;if(!fe.ok)throw Error(`Failed to inspect ${_}: GitHub tree returned ${fe.status}`);let ke=JSON.parse(de.content);if(!Array.isArray(ke?.tree))throw Error(`Failed to inspect ${_}: unexpected GitHub tree response`);let Ce=ke.tree,Re=[...new Set(Ce.flatMap((Oe)=>{if(!Oe||typeof Oe!=="object")return[];let Ee=Oe;if(Ee.type!=="blob"||typeof Ee.path!=="string")return[];let Ne=Ee.path.match(St);return Ne?.[1]?[Ne[1]]:[]}))].sort();if(Re.length===0)throw Error(`No rulebooks found in ${_} under ${he}/`);return{source:_,owner:j,repo:J,ref:re,commit:ie,names:Re}}async function tb(_,N,j,J){let Y=await Oo(`https://api.github.com/repos/${_}/${N}`,"metadata",J),re=Y.response;if(!re.ok)throw Error(`Failed to inspect ${j}: GitHub returned ${re.status}`);let de=JSON.parse(Y.content)?.default_branch;if(typeof de!=="string"||de==="")throw Error(`Failed to inspect ${j}: missing default branch`);if(!ae(de))throw Error(`GitHub returned an invalid default branch: ${de}`);return de}function rb(_,N,j){wt(_);let J=M(N,_),Y=r(i(j,J));if(Y===null)throw Error(`Rulebook source not found: ${_}`);let re=Mu(Ps(Y,"Invalid local rulebook source."));if(re.name!==_)throw Error(`rulebook name "${re.name}" must match local source "${_}"`);return{spec:_,rulebook:re,content:Y}}async function ob(_,N){let j=L(_),J=await Uu(j.owner,j.repo,j.ref,_,N),Y=await Oo(`https://raw.githubusercontent.com/${j.owner}/${j.repo}/${J}/${j.path}`,"raw",N),re=Y.response;if(!re.ok)throw Error(`Failed to fetch ${_}: GitHub raw returned ${re.status}`);let ie=Y.content,de=Mu(Ps(ie,"Invalid GitHub rulebook response."));if(de.name!==j.name)throw Error(`rulebook name "${de.name}" must match GitHub source "${j.name}"`);return{spec:_,rulebook:de,content:ie}}function Mu(_){let N=le(_),j=Eo(N);if(j.length>0)throw Error(j.join("; "));return N}function Ps(_,N){try{return JSON.parse(_)}catch{throw Error(N)}}async function Uu(_,N,j,J,Y){let re=await Oo(`https://api.github.com/repos/${_}/${N}/commits/${encodeURIComponent(j)}`,"commit",Y),ie=re.response;if(!ie.ok)throw Error(`Failed to resolve ${J}: GitHub returned ${ie.status}`);let de=JSON.parse(re.content);if(typeof de?.sha!=="string"||de.sha==="")throw Error(`Failed to resolve commit for ${J}`);return de.sha}async function ib(_,N,j={}){if(j.signal?.aborted)throw j.signal.reason;let J=j.budget??To(),Y=new AbortController,re=()=>Y.abort(j.signal?.reason);j.signal?.addEventListener("abort",re,{once:!0});let ie=!1,de=setTimeout(()=>{if(Y.signal.aborted)return;ie=!0,Y.abort()},j.timeoutMs??ju.timeoutMs);try{if(j.signal?.aborted)throw j.signal.reason;$u(J);let fe=await fetch(_,{signal:Y.signal,redirect:"error"});if(!fe.ok)return Gu(fe),{response:fe,content:""};return{response:fe,content:await sb(fe,N,J,()=>Y.abort())}}catch(fe){if(ie)throw Error("GitHub request timed out",{cause:fe});if(j.signal?.aborted)throw j.signal.reason;throw fe}finally{clearTimeout(de),j.signal?.removeEventListener("abort",re)}}function Oo(_,N,j){return ib(j.resolveUrl?.(_)??_,N,{budget:j.budget,signal:j.controller.signal})}async function sb(_,N,j=To(),J){let Y=ju[`${N}Bytes`],re=Number(_.headers.get("content-length"));if(Number.isFinite(re)&&re>Y)throw Gu(_),Error(`GitHub ${N} response exceeds ${Y} bytes`);if(!_.body)return"";let ie=_.body.getReader(),de=[],fe=0;while(!0){let ke=await ie.read();if(ke.done)break;try{Du(j,ke.value.byteLength)}catch(Ce){throw J?.(),Nu(ie),Ce}if(fe+=ke.value.byteLength,fe>Y)throw J?.(),Nu(ie),Error(`GitHub ${N} response exceeds ${Y} bytes`);de.push(Buffer.from(ke.value))}return Buffer.concat(de,fe).toString("utf-8")}function Gu(_){if(!_.body)return;Bu(()=>_.body?.cancel())}function Nu(_){Bu(()=>_.cancel())}function Bu(_){try{Promise.resolve(_()).catch(()=>{})}catch{}}var ab=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function qu(_,N){let j=Ju(_.rules,N);if(j.length>0)return{ok:!0,specs:j};return Ku(_.rules,N)}function Vu(_,N){let j=Ju(_,N);if(j.length>0)return{ok:!0,specs:j};let J=cb(_,N);if(J.length>0)return{ok:!0,specs:J};let Y=db(_,N);if(!Y.ok)return Y;if(Y.specs.length>0)return{ok:!0,specs:Y.specs};return Ku(_,N)}function Ku(_,N){let j=_.filter((J)=>Rs(J)?.name===N);if(j.length===1)return{ok:!0,specs:j};return lb(N,j)}function lb(_,N){return{ok:!1,result:{ok:!1,errors:N.length===0?[`No configured rulebook matches ${_}`]:[`Ambiguous rulebook match ${_}: ${N.join(", ")}`],entries:[]}}}function Ju(_,N){return _.filter((j)=>j===N)}function cb(_,N){let j=N.match(ab),J=j?.[1],Y=j?.[2],re=j?.[3];if(!J||!Y||!re||!ae(re))return[];return Wu(_,(ie)=>ie.owner===J&&ie.repo===Y&&ie.ref===re)}function db(_,N){if(!Z(N))return{ok:!0,specs:[]};let[j,J]=N.split("/"),Y=Wu(_,(ie)=>ie.owner===j&&ie.repo===J);if(new Set(Y.map((ie)=>Rs(ie)?.ref).filter((ie)=>!!ie)).size<2)return{ok:!0,specs:Y};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${N}. Use an explicit ref:`,` cc-safety-net rule remove ${N}#<ref>`],entries:[]}}}function Rs(_){try{return L(_)}catch{return null}}function Wu(_,N){return _.filter((j)=>{let J=Rs(j);return J?N(J):!1})}async function $o(_,N={}){let j=_s(N);return pb(_,j,await Io(_,j,Mn()))}function pb(_,N,j){if(!j.ok)return j;let J=hn(_,N),Y=[...new Set(B(J.configPath,J.filesystemScope))];if(Y.length===0)return j;return{ok:!1,errors:Y,entries:j.entries}}async function Io(_,N,j,J={},Y=new Set,re=new Set){try{let ie=hn(_,N),de=kr(ie.configTarget);if(!de.ok)return de.result;let fe=de.config,ke=N.only?qu(fe,N.only):{ok:!0,specs:fe.rules};if(!ke.ok)return ke.result;let Ce=new Set([...N.refresh?ke.specs:[],...Y]),Re=(tn)=>Hu(tn,ie.configDir,ie.filesystemScope,j,Ce.has(tn),!N.refresh||Ce.has(tn)),Oe=await xb(fe.rules,N.refresh?(tn)=>Re(tn).then((fn)=>({ok:!0,item:fn})).catch((fn)=>{if(Iu(fn))throw fn;return{ok:!1,spec:tn,message:fn instanceof Error?fn.message:String(fn)}}):async(tn)=>({ok:!0,item:await Re(tn)}),j),Ee=Oe.filter((tn)=>!tn.ok),Ne=Oe.filter((tn)=>tn.ok).map((tn)=>tn.item),Je=Ne.flatMap((tn)=>fb(tn,fe.rules)),Ge=Ne.flatMap((tn)=>mb(tn,re,ie)),Ye=new Set([...Je,...Ge].map((tn)=>tn.spec)),rn=[...Ee,...Je,...Ge],on=[],sn=yb(on,()=>Ne.flatMap((tn)=>Ye.has(tn.spec)||rn.length>0&&re.has(tn.spec)?[]:gb(tn,ie,J,on)));return{ok:rn.length===0,errors:rn.map((tn)=>`Failed to update ${tn.spec}: ${tn.message}`),entries:Ne.map(vb),changes:sn}}catch(ie){return xr(ie)}}function fb(_,N){if(!C(_.spec))return[];let j=je(_.spec),J=N.filter((Y)=>Y!==_.spec&&je(Y).toLowerCase()===j.toLowerCase());if(J.length===0)return[];return[{ok:!1,spec:_.spec,message:`rulebook name "${j}" is also claimed by ${J.join(", ")}; rename one of them`}]}function mb(_,N,j){if(!N.has(_.spec)||!C(_.spec))return[];let J=M(j.configDir,_.rulebook.name),Y=r(i(j.filesystemScope,J));if(Y===null||Y===_.content)return[];return[{ok:!1,spec:_.spec,message:`${J} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function gb(_,N,j,J){if(!C(_.spec))return[];let Y=M(N.configDir,_.rulebook.name),re=i(N.filesystemScope,Y),ie=r(re);if(ie===_.content)return[];return J?.push({target:re,previous:ie}),g(re,_.content,void 0,j._testAfterPolicyRename),hb(_,ie)}function yb(_,N){try{return N()}catch(j){for(let J of[..._].reverse()){if(J.previous===null){H(J.target);continue}g(J.target,J.previous)}throw j}}function hb(_,N){if(N===null)return[`Vendored ${_.spec} (${_.rulebook.version})`];let j=ve(N),J="problem"in j?null:j.rulebook,Y=new Map(J?.rules.map((ie)=>[ie.name,JSON.stringify(ie)])??[]),re=new Set(_.rulebook.rules.map((ie)=>ie.name));return[`Updated ${_.spec} (${J?.version??"unreadable"} -> ${_.rulebook.version})`,...[...re].filter((ie)=>!Y.has(ie)).map((ie)=>` + ${ie}`),...[...Y.keys()].filter((ie)=>!re.has(ie)).map((ie)=>` - ${ie}`),..._.rulebook.rules.filter((ie)=>{let de=Y.get(ie.name);return de!==void 0&&de!==JSON.stringify(ie)}).map((ie)=>` ~ ${ie.name}`)]}function vb(_){return{spec:_.spec,name:_.rulebook.name,version:_.rulebook.version,ruleCount:_.rulebook.rules.length}}async function zu(_,N,j={}){return bb(_,N,Pb(j),Mn())}async function bb(_,N,j,J,Y={}){let re=null,ie=!1;try{let de=hn(_,j),fe=r(de.configTarget);re={target:de.configTarget,content:fe};let ke=kr(de.configTarget);if(!ke.ok)return ke.result;let Ce=ke.config,Re=Z(N);wb(N,j,Re);let Oe=Re?await Fu(N,{ref:j.ref,operation:J}):null,Ee=Oe?kb(Oe,j.rulebooks):[],Ne=Oe?Ee.map((on)=>Cb(Ce.rules,Oe,on)??`${N}#${Oe.ref}/${on}`):[N],Je=Ne.filter((on)=>!Ce.rules.includes(on)),Ge=[...Ce.rules,...Je];if(Ge.length>ye)return Sb();if(Ge.length!==Ce.rules.length)ie=!0,vn(de.configTarget,{...Ce,rules:Ge},void 0,Y._testAfterPolicyRename);let Ye=await Io(_,j,J,Y,new Set(Je),new Set(Je));if(!Ye.ok)Cr(de.configTarget,fe);if(!Ye.ok||!Oe)return Ye;let rn=Ee.filter((on,sn)=>Je.includes(Ne[sn]??""));return{...Ye,add:{source:N,ref:Oe.ref,selected:Ee,added:rn,alreadyConfigured:Ee.filter((on)=>!rn.includes(on)),commits:Je.length>0?[Oe.commit]:[]}}}catch(de){if(ie&&re)try{Cr(re.target,re.content)}catch(fe){return xr(fe)}return xr(de)}}function wb(_,N,j){if(!j&&N.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!j&&N.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${_}`);if(N.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let J=N.rulebooks?.filter((Y)=>!p.test(Y))??[];if(J.length>0)throw Error(`Invalid rulebook names: ${J.join(", ")}`)}function kb(_,N){let j=N?[...new Set(N)]:_.names,J=j.filter((Y)=>!_.names.includes(Y));if(J.length>0)throw Error(`Rulebooks not found in ${_.source} at ${_.ref}: ${J.join(", ")}
|
|
278
|
+
Available rulebooks: ${_.names.join(", ")}`);return j}function Cb(_,N,j){let J=`${N.source}#${N.ref}/${j}`;if(_.includes(J))return J;let Y=`${N.source}#${N.commit}/${j}`;return _.find((re)=>re===Y)}async function xb(_,N,j=Mn()){if(_.length>ye)throw Error(be);let J=[],Y=0,re,ie=Array.from({length:Math.min(_.length,_o.concurrency)},async()=>{while(!re){let de=Y;if(de>=_.length)return;Y++;try{J[de]=await N(_[de],de,j.controller.signal)}catch(fe){if(!re)re={value:fe},Y=_.length,j.controller.abort(fe);return}}});if(await Promise.all(ie),re)throw re.value;return J}function Sb(){return{ok:!1,errors:[be],entries:[]}}function _s(_){return{cwd:_.cwd,userConfigDir:_.userConfigDir,userConfigPath:_.userConfigPath,projectConfigPath:_.projectConfigPath,global:_.global,only:_.only,refresh:_.refresh}}function Pb(_){return{..._s(_),ref:_.ref,rulebooks:_.rulebooks}}function Rb(_){return{..._s(_),deleteSource:_.deleteSource}}async function Yu(_,N,j={}){try{return await Eb(_,N,Rb(j),{})}catch(J){return xr(J)}}async function Eb(_,N,j,J){let Y=hn(_,j),re=d(Y.configTarget);if(re.errors.length>0)return{ok:!1,errors:re.errors,entries:[]};if(!re.config)return{ok:!1,errors:[`No config found at ${Y.configPath}`],entries:[]};let ie=Vu(re.config.rules,N);if(!ie.ok)return ie.result;let de=j.deleteSource?_b(Y.configDir,ie.specs,Y.filesystemScope):{ok:!0,dirs:[]};if(!de.ok)return de.result;let fe=r(Y.configTarget);if(fe===null)return xr(Error("Rules config is unavailable."));try{vn(Y.configTarget,{...re.config,rules:re.config.rules.filter((Re)=>!ie.specs.includes(Re))},void 0,J._testAfterPolicyRename)}catch(Re){throw Cr(Y.configTarget,fe),Re}let ke=await Io(_,j,Mn(),J);if(!ke.ok)return Cr(Y.configTarget,fe),ke;let Ce=Tb(de.dirs,J,Y.filesystemScope);if(!Ce.ok){Cr(Y.configTarget,fe);let Re=await Io(_,j,Mn(),J);if(!Re.ok)return{ok:!1,errors:[...Ce.result.errors,...Re.errors],entries:Re.entries};return Ce.result}return ke}function _b(_,N,j){let J=N.flatMap((de)=>p.test(de)?[]:["--delete-source can only delete local rulebook sources"]),Y=N.map((de)=>Es(_,de)),re=J.length>0?[]:Y.flatMap((de)=>Zu(de,j)),ie=[...J,...re];return ie.length>0?{ok:!1,result:{ok:!1,errors:ie,entries:[]}}:{ok:!0,dirs:Y}}function Zu(_,N){let j=ub(_),J=i(N,j),Y=me(J);if(!Y)return[`Local rulebook source directory not found: ${_}`];let re=Y.find((ie)=>ie.name==="rulebook.json");if(!re)return[`Local rulebook source directory is missing rulebook.json: ${_}`];if(re.kind!=="file")throw new o(N.label);if(r(i(N,Es(j,"rulebook.json"))),Y.length>1)return[`Local rulebook source directory contains extra files: ${_}. delete manually if you really want to remove the directory.`];return[]}function Tb(_,N,j){let J=_.flatMap((Y)=>{try{if(!me(i(j,Y)))return[];let re=Zu(Y,j);if(re.length>0)return re;return Ab(Y,N,j),[]}catch(re){return[`Failed to delete local rulebook source ${Y}: ${re instanceof Error?re.message:String(re)}`]}});return J.length>0?{ok:!1,result:{ok:!1,errors:J,entries:[]}}:{ok:!0}}function Ab(_,N,j){if(N._testDeleteLocalSourceDir){N._testDeleteLocalSourceDir(_);return}H(i(j,Es(_,ge))),vt(i(j,_))}function Cr(_,N){if(N===null){H(_);return}g(_,N)}function xr(_){return{ok:!1,errors:[_ instanceof Error?_.message:String(_)],entries:[]}}var Ob=".safety-net.json",Ib="~/.cc-safety-net/config.json";async function np(_,N){let j=A(_,{cwd:N.cwd});if(j)console.log(`Skipped the project scope: ${I(N.cwd)} is the user rule config, not a project rule config`);return[j||await Qu(_,{legacyPath:Ha({cwd:N.cwd}),configPath:I(N.cwd),defaultRulebookName:"project-rules",migratedFrom:Ob,cleanup:N.cleanup,syncOptions:{cwd:N.cwd}}),await Qu(_,{legacyPath:Dt(_),configPath:W(_),defaultRulebookName:"user-rules",migratedFrom:Ib,cleanup:N.cleanup,syncOptions:{cwd:N.cwd,global:!0}})].every((Y)=>Y)?0:1}async function Qu(_,N){let j=hn(_,N.syncOptions),J=i(j.filesystemScope,N.legacyPath),Y=r(J);if(Y===null)return console.log(`No legacy config found at ${N.legacyPath}`),!0;let re=Db(Y);if(!re.ok){for(let Ee of re.errors)console.error(Ee);return!1}let ie=d(j.configTarget);if(ie.errors.length>0){for(let Ee of ie.errors)console.error(Ee);return!1}let de=ie.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},fe=Lb(Xu(N.configPath),de.rules,N.defaultRulebookName,N.migratedFrom,j.filesystemScope),ke=Do(Xu(N.configPath),fe,"rulebook.json"),Ce=i(j.filesystemScope,ke),Re=[ep(j.configTarget),ep(Ce)],Oe=await $b(_,N,j.configTarget,Ce,fe,re.config.rules,{...de,rules:de.rules.includes(fe)?de.rules:[...de.rules,fe]});if(!Oe.ok){Hb(Re);for(let Ee of Oe.errors)console.error(Ee);return!1}if(!N.cleanup)return console.log(`Migrated legacy config at ${N.legacyPath}. Legacy file is no longer used.`),!0;if(!jb(j.configTarget,Ce,fe,N.migratedFrom,re.config.rules))return console.error(`Migration cleanup verification failed for ${N.legacyPath}`),!1;return H(J),console.log(`Deleted legacy config at ${N.legacyPath}`),!0}async function $b(_,N,j,J,Y,re,ie){try{return vn(j,ie),vn(J,Nb(Y,N.migratedFrom,re)),await $o(_,N.syncOptions)}catch(de){return{ok:!1,errors:[de instanceof Error?de.message:String(de)]}}}function Db(_){try{let N=JSON.parse(_),j=Vo(N);if(j.errors.length>0)return{ok:!1,errors:j.errors};return{ok:!0,config:{version:1,rules:N.rules??[]}}}catch{return{ok:!1,errors:["Invalid JSON"]}}}function Lb(_,N,j,J,Y){let re=N.find((ie)=>Fb(i(Y,Do(_,ie,"rulebook.json")))===J);if(re)return re;if(r(i(Y,Do(_,j,"rulebook.json")))===null)return j;for(let ie=2;;ie++){let de=`${j}-${ie}`;if(r(i(Y,Do(_,de,"rulebook.json")))===null)return de}}function Nb(_,N,j){return{rulebook_version:1,name:_,version:"1.0.0",description:"Migrated CC Safety Net rules.",author:"project",migrated_from:N,allowed_commands:[...new Set(j.map((J)=>J.command))],rules:j,tests:j.map((J)=>({command:[J.command,J.subcommand,J.block_args[0]].filter(Boolean).join(" "),expect:"blocked",rule:J.name}))}}function jb(_,N,j,J,Y){if(!d(_).config?.rules.includes(j))return!1;try{let ie=r(N);if(ie===null)return!1;let de=JSON.parse(ie);return de.migrated_from===J&&JSON.stringify(de.rules)===JSON.stringify(Y)}catch{return!1}}function ep(_){return{target:_,content:r(_)}}function Hb(_){for(let N of _){if(N.content===null){H(N.target);continue}g(N.target,N.content)}}function Fb(_){let N=r(_);if(N===null)return null;try{let j=JSON.parse(N);return typeof j.migrated_from==="string"?j.migrated_from:null}catch{return null}}import{mkdir as Mb,readFile as Ub,writeFile as Gb}from"node:fs/promises";import{dirname as Bb,join as qb}from"node:path";var Vb=86400000,Kb=604800000;async function rp(_,N=Date.now()){if(_.env.get("CC_SAFETY_NET_NO_UPDATE_CHECK"))return null;let j=Qe(_);if(!j)return null;let J=qb(j,".cc-safety-net","update-check.json"),Y=await Jb(J,N);if(!Y.lastCheck||N-Y.lastCheck>Vb){let de=await Gn();if(Y.lastCheck=N,de.latestVersion)Y.latestVersion=de.latestVersion;if(!await tp(J,Y))return null;if(de.error)return null}let re=Y.latestVersion,ie=pn();if(!re||!Zo(re,ie))return null;if(Y.notifiedVersion===re&&Y.notifiedAt!==void 0&&N-Y.notifiedAt<Kb)return null;if(Y.notifiedVersion=re,Y.notifiedAt=N,!await tp(J,Y))return null;return`UPDATE_AVAILABLE: cc-safety-net v${re} is available (running v${ie}). 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 Jb(_,N){let j=await Ub(_,"utf8").then((re)=>JSON.parse(re)).catch(()=>{return});if(!j||typeof j!=="object"||Array.isArray(j))return{};let J=j,Y=(re)=>typeof re==="number"&&Number.isFinite(re)&&re<=N?re:void 0;return{lastCheck:Y(J.lastCheck),latestVersion:typeof J.latestVersion==="string"?J.latestVersion:void 0,notifiedVersion:typeof J.notifiedVersion==="string"?J.notifiedVersion:void 0,notifiedAt:Y(J.notifiedAt)}}async function tp(_,N){return Mb(Bb(_),{recursive:!0,mode:448}).then(()=>Gb(_,JSON.stringify(N),{mode:384})).then(()=>!0).catch(()=>!1)}import{join as Xb,resolve as Os}from"node:path";var op=new Map([["Bash","posix"],["PowerShell","powershell"],["Monitor","posix"]]);var ip=new Map([["Bash","auto"]]);var sp=new Map([["bash","auto"],["Bash","auto"],["powershell","powershell"],["PowerShell","powershell"]]);var ap=new Map([["Shell","auto"]]);var lp=new Map([["exec","auto"]]),cp="write_to_process";var dp=new Map([["Execute","auto"]]);var up=new Map([["run_shell_command","auto"]]);var pp=new Map([["run_terminal_command","auto"],["monitor","auto"]]);var fp=new Map([["terminal","posix"]]);var mp=new Map([["Bash","posix"]]);var gp="exec";var Yb=[["antigravity-cli",[...Wa.keys()]],["claude-code",[...op.keys()]],["codex",[...ip.keys()]],["copilot-cli",[...sp.keys()]],["cursor",[...ap.keys()]],["deepseek-harness",[...Xe.keys()]],["devin",[...lp.keys(),cp]],["droid",[...dp.keys()]],["gemini-cli",[...up.keys()]],["grok-build",[...pp.keys()]],["hermes-agent",[...fp.keys()]],["kimi-code",[...mp.keys()]],["openclaw",[gp]],["opencode",[Ke,we]],["pi",[...Ze.keys()]]].flatMap(([_,N])=>N.map((j)=>({name:j,integration:cn(_)}))),yp="and is already analyzed. Built-in tools cannot be declared.";function Lo(_){let N=Te(_),j=[...new Set(Yb.filter((J)=>Te(J.name)===N).map((J)=>J.integration))].sort((J,Y)=>J.localeCompare(Y));if(j.length>0)return`"${_}" is a built-in shell tool of ${Zb(j)}, ${yp}`;if(x(_)!=="unknown")return`"${_}" is a built-in file tool, ${yp}`;return}function Zb(_){return _.length===1?_[0]??"":`${_.slice(0,-1).join(", ")} and ${_.at(-1)}`}var vp="CC Safety Net Config",Qb="═".repeat(vp.length),ew="https://raw.githubusercontent.com/kenryu42/cc-safety-net/main/assets/cc-safety-net.schema.json",nw=new Set(["rule.json","rule.lock","cache"]);function bp(_,N={}){try{return tw(_,N)}catch(j){if(j instanceof o)return console.error(j.message),1;throw j}}function tw(_,N){let j=N.cwd??process.cwd(),J=X(_,{cwd:j}),Y=Dt(_),re=Or(j),ie=Os(j,he),de=i(J.userScope,Y),fe=i(J.projectScope,re),ke=!1,Ce=!1,Re=[],Oe=[],Ee=rw(i(J.projectScope,ie));if(iw(),r(J.userConfigTarget)!==null){let Ne=Un(J.userConfigTarget);if(Ne.errors.push(...B(J.userConfigPath,J.userScope),...hp(J.userConfigTarget)),Re.push({scope:"User",path:J.userConfigPath,result:Ne,schema:"rules",target:J.userConfigTarget}),Ne.errors.length>0)ke=!0}if(r(de)!==null)if(Ce=!0,r(J.userConfigTarget)!==null)Oe.push(No("user","cleanup"));else{let Ne=Ko(de);if(Re.push({scope:"User",path:Y,result:Ne,schema:"legacy",inactive:!0,target:de}),Oe.push(No("user",Ne.errors.length>0?"fix-or-delete":"migrate")),Ne.errors.length>0)ke=!0}if(r(J.projectConfigTarget)!==null){let Ne=Un(J.projectConfigTarget);if(Ne.errors.push(...B(J.projectConfigPath,J.projectScope),...hp(J.projectConfigTarget)),Re.push({scope:"Project",path:Os(J.projectConfigPath),result:Ne,schema:"rules",target:J.projectConfigTarget}),Ne.errors.length>0)ke=!0;if(r(fe)!==null)Ce=!0,Oe.push(No("project","cleanup"))}else if(r(fe)!==null){Ce=!0,ke=!0;let Ne=Ko(fe);Re.push({scope:"Project",path:Os(re),result:Ne,schema:"legacy",inactive:!0,target:fe}),Oe.push(No("project",Ne.errors.length>0?"fix-or-delete":"migrate"))}if(Ee?.result.errors.length)ke=!0;if(Re.length===0&&!Ee)return console.log(`
|
|
279
|
+
No config files found. Using built-in rules only.`),0;for(let Ne of Re)if(Ne.inactive)aw(Ne.scope,Ne.path,Ne.result);else if(Ne.result.errors.length>0)lw(Ne.scope,Ne.path,Ne.result.errors);else{if(Ne.schema==="rules"&&uw(Ne.target))console.log(`
|
|
280
|
+
Added $schema to ${Ne.scope.toLowerCase()} config.`);sw(Ne.scope,Ne.path,Ne.result,Ne.schema)}for(let Ne of Oe)console.error(`
|
|
281
|
+
${en.red(Ne)}`);if(Ee)if(Ee.result.errors.length>0)dw(Ee.path,Ee.result.errors);else cw(Ee.path,Ee.result);if(ke)return console.error(`
|
|
282
|
+
Config validation failed.`),1;return console.log(Ce?`
|
|
283
283
|
Configs valid with warnings.`:`
|
|
284
|
-
All configs valid.`),0}function
|
|
285
|
-
✓ ${
|
|
286
|
-
✗ Legacy ${
|
|
287
|
-
✓ GitHub source rules: ${
|
|
288
|
-
✗ ${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 xb(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:ub,...D},null,2)),!0}catch(O){if(O instanceof o)throw O;return!1}}var Uu=new Set(["init","add","remove","update","sync","list","wrapper","migrate","doc","verify"]),Sb=new Set(["init","add","remove","update","sync","wrapper"]),Pb=new Set(["add","remove","list"]),Rb="cc-safety-net/rulebooks";async function Gu(P,O){try{return await Eb(P,O)}catch(D){if(D instanceof o)return console.error(D.message),1;throw D}}async function Eb(P,O){let D=Ab(O),j=D.help?_b(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&&Sb.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);Ob(fe.configTarget);let we=Cb(fe.configDir,"example-rules","rulebook.json"),Ce=i(fe.filesystemScope,we);if(D.example&&r(Ce)===null)uu(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=qu(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 Au(P,fe,{...re,ref:D.ref,rulebooks:D.only.length>0?D.only:void 0});return su(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 Tu(P,Y,{...re,deleteSource:D.deleteSource});return fo(fe,`Removed rulebook source: ${Y}`),fe.ok?0:1}if(J==="update"){let fe=await wo(P,{...re,only:Y,refresh:!0});return fo(fe,"Rule config updated."),fe.ok?0:1}if(J==="sync")return Ca(P,{global:D.global});if(J==="list"){let fe=Q(P,{cwd:ie});return lu(fe),fe.errors.length>0?1:0}if(J==="wrapper")return Db(P,D);if(J==="migrate")return Lu(P,{cleanup:D.cleanup,cwd:ie});if(J==="doc"){console.log(ou);let fe=await ju(P);if(fe)console.error(fe);return 0}if(J==="verify")return Hu(P);return 1}function _b(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"?_o:[],examples:P[0]==="add"?Ao:void 0}}function Ab(P){let O=yn({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 Tb(D),D}function Tb(P){let[O]=P.positionals;if(O&&!Uu.has(O))P.errors.push(`Unknown rule subcommand: ${O}`);if(P.deleteSource&&O!=="remove")if(O&&Uu.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(pr(O,"--cleanup"));if(P.example&&O!=="init")P.errors.push(pr(O,"--example"));if(P.ref&&O!=="add")P.errors.push(pr(O,"--ref"));if(P.only.length>0&&O!=="add")P.errors.push(pr(O,"--only"));if(O==="add")Ib(P);if(O==="migrate"){if(P.global)P.errors.push(pr(O,"--global"));if(P.positionals.length>1)P.errors.push(`Unexpected rule migrate argument: ${P.positionals[1]}`)}else if(O==="wrapper")$b(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 qu(P){if(P.positionals[1])return P.positionals[1];if(P.ref||P.only.length>0)return Rb;return}function Ib(P){let O=qu(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 pr(P,O){return P?`Unknown option for rule ${P}: ${O}`:`Unknown option for rule: ${O}`}function $b(P){let O=P.positionals[1],D=P.positionals[2];if(!O){P.errors.push("rule wrapper requires add, remove, or list");return}if(!Pb.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 Ob(P){if(r(P)===null){du(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 Db(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 Lb(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 Lb(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 Ub}from"node:path";import{existsSync as Nb,readFileSync as jb}from"node:fs";import{join as Fb}from"node:path";async function Hb(P){if(P.isTTY)return null;return(await Ke(P).catch(()=>null))?.trim()||null}function Mb(P){let O=P.env.get("CLAUDE_SETTINGS_PATH");if(O)return O;return Fb(Ir(P),"settings.json")}function ms(P){let O=Mb(P);if(!Nb(O))return!1;try{let D=jb(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 gs(P,O=process.stdin){let D=ms(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(V(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 Hb(O);if(J&&!J.startsWith("{"))console.log(`${J} | ${j}`);else console.log(j)}function Bu(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=(qe,Me)=>{let en=` ${qe.padEnd(13)}${Me}`;return(en.length>Y?`${en.slice(0,Y-1)}…`:en).replaceAll(ie,nn.red(ie))},fe=Object.values(V(D,j.capabilities)).some((qe)=>qe.changesInherited),we=(qe)=>qe===P.home||qe.startsWith(`${P.home}${Ub}`)?`~${qe.slice(P.home.length)}`:qe,Ce={ready:nn.green,degraded:nn.yellow}[O.state],Se=O.policyScopes?.weakenings??[],Ie=[...ms(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((qe)=>er(qe," ",Y-6).map((Me,en)=>en===0?` ${Me}`:Me)),""],...Ie.length===0?[" Everything configured is active."]:[" Not active",...Ie.flatMap((qe)=>er(qe," ",Y-6).map((Me,en)=>en===0?` ${Ee} ${Me}`:Me)),""," Full report: cc-safety-net doctor"]].join(`
|
|
289
|
-
`))}import{spawn as
|
|
290
|
-
return POSIX path of (choose folder with prompt "${
|
|
284
|
+
All configs valid.`),0}function hp(_){return(d(_).config?.command_tools??[]).flatMap((N)=>Lo(typeof N==="string"?N:N.name)??[])}function No(_,N){let j=`legacy ${_} config`;if(N==="cleanup")return`Warning: Legacy ${_} config is no longer needed. Run \`npx -y cc-safety-net rule migrate --cleanup\` to clean it up safely.`;if(N==="migrate")return`Warning: Legacy ${_} config is ignored by CC Safety Net. Run \`npx -y cc-safety-net rule migrate\`.`;return`Warning: Legacy ${_} config is no longer supported. Fix or delete the ${j}, then run \`npx -y cc-safety-net rule migrate\`.`}function rw(_){if(me(_)===null)return null;let N=ow(_);if(N.ruleNames.size===0&&N.errors.length===0)return null;return{path:_.path,result:N}}function ow(_){let N=[],j=new Set,J=(me(_)??[]).filter((Y)=>!nw.has(Y.name)).sort((Y,re)=>Y.name.localeCompare(re.name));if(J.length===0)return{errors:N,ruleNames:j};for(let Y of J){if(!p.test(Y.name)){N.push(`rulebook directory names must match ${p}: ${Y.name}`);continue}if(Y.kind!=="directory"){N.push(`${Y.name} must be a rulebook directory`);continue}let re=i(_.scope,Xb(_.path,Y.name,"rulebook.json")),ie=r(re);if(ie===null){N.push(`${Y.name}/rulebook.json is required`);continue}try{let de;try{de=JSON.parse(ie)}catch{N.push(`${Y.name}/rulebook.json: invalid JSON`);continue}let fe=le(de);if(fe.name!==Y.name){N.push(`rulebook name "${fe.name}" must match folder "${Y.name}"`);continue}let ke=Eo(fe);if(ke.length>0){N.push(...ke.map((Ce)=>`${Y.name}/rulebook.json: ${Ce}`));continue}j.add(Y.name)}catch(de){N.push(de instanceof Error?`${Y.name}/rulebook.json: ${de.message}`:`${Y.name}/rulebook.json: ${String(de)}`)}}return{errors:N,ruleNames:j}}function iw(){console.log(vp),console.log(Qb)}function sw(_,N,j,J){if(console.log(`
|
|
285
|
+
✓ ${_} config: ${N}`),console.log(` Schema: ${J==="rules"?"rulebook sources":"legacy inline rules"}`),j.ruleNames.size>0){console.log(` ${J==="rules"?"Sources":"Rules"}:`);let Y=1;for(let re of j.ruleNames)console.log(` ${Y}. ${re}`),Y++}else console.log(` ${J==="rules"?"Sources":"Rules"}: (none)`)}function aw(_,N,j){if(console.error(`
|
|
286
|
+
✗ Legacy ${_.toLowerCase()} config: ${N}`),console.error(" Schema: legacy inline rules"),console.error(" Status: ignored by CC Safety Net"),j.errors.length>0){console.error(" Errors:");let J=1;for(let Y of j.errors)for(let re of Y.split("; "))console.error(` ${J}. ${re}`),J++;return}if(j.ruleNames.size>0){console.error(" Rules:");let J=1;for(let Y of j.ruleNames)console.error(` ${J}. ${Y}`),J++;return}console.error(" Rules: (none)")}function lw(_,N,j){wp(`${_} config`,N,j)}function cw(_,N){console.log(`
|
|
287
|
+
✓ GitHub source rules: ${_}`),console.log(" Rulebooks:");let j=1;for(let J of N.ruleNames)console.log(` ${j}. ${J}`),j++}function dw(_,N){wp("GitHub source rules",_,N)}function wp(_,N,j){console.error(`
|
|
288
|
+
✗ ${_}: ${N}`),console.error(" Errors:");let J=1;for(let Y of j)for(let re of Y.split("; "))console.error(` ${J}. ${re}`),J++}function uw(_){try{let N=r(_);if(N===null)return!1;let j=JSON.parse(N);if(j.$schema)return!1;return g(_,JSON.stringify({$schema:ew,...j},null,2)),!0}catch(N){if(N instanceof o)throw N;return!1}}var kp=new Set(["init","add","remove","update","sync","list","wrapper","tool","migrate","doc","verify"]),fw=new Set(["init","add","remove","update","sync","wrapper"]),xp=new Set(["add","remove","list"]),mw="cc-safety-net/rulebooks";async function Sp(_,N){try{return await gw(_,N)}catch(j){if(j instanceof o)return console.error(j.message),1;throw j}}async function gw(_,N){let j=hw(N),J=j.help?yw(j.positionals):null;if(J)return Kt(J),0;if(j.errors.length>0){for(let ke of j.errors)console.error(ke);return 1}let Y=j.positionals[0];if(!Y)return Kt($t,console.error),1;let re=j.positionals[1],ie={global:j.global},de=process.cwd();if((Y==="tool"?j.project:!j.global&&fw.has(Y)&&!(Y==="wrapper"&&re==="list"))&&A(_,{cwd:de}))return console.error(`${I(de)} is the user rule config, not a project rule config; ${Y==="tool"?"drop --project":"use --global"} for the user scope, or run from a project directory`),1;if(Y==="init"){let ke=hn(_,ie);ww(ke.configTarget);let Ce=pw(ke.configDir,"example-rules","rulebook.json"),Re=i(ke.filesystemScope,Ce);if(j.example&&r(Re)===null)Ou(Re);let Oe=B(ke.configPath,ke.filesystemScope);for(let Ee of Oe)console.error(Ee);if(Oe.length>0)return 1;return console.log("Rule config initialized."),0}if(Y==="add"){let ke=Pp(j);if(!ke)return console.error("rule add requires a source (pass --only <rulebook...> to select from cc-safety-net/rulebooks)"),1;let Ce=hn(_,ie),Re=await zu(_,ke,{...ie,ref:j.ref,rulebooks:j.only.length>0?j.only:void 0});return Pu(Re,ke,`Scope: ${j.global?"user":"project"} (${Ce.configDir})`),Re.ok?0:1}if(Y==="remove"){if(!re)return console.error("rule remove requires a source"),1;let ke=await Yu(_,re,{...ie,deleteSource:j.deleteSource});return Ro(ke,`Removed rulebook source: ${re}`),ke.ok?0:1}if(Y==="update"){let ke=await $o(_,{...ie,only:re,refresh:!0});return Ro(ke,"Rule config updated."),ke.ok?0:1}if(Y==="sync")return Ua(_,{global:j.global});if(Y==="list"){let ke=Q(_,{cwd:de});return Eu(ke),ke.errors.length>0?1:0}if(Y==="wrapper")return kw(_,j);if(Y==="tool")return xw(_,j,de);if(Y==="migrate")return np(_,{cleanup:j.cleanup,cwd:de});if(Y==="doc"){console.log(xu);let ke=await rp(_);if(ke)console.error(ke);return 0}if(Y==="verify")return bp(_);return 1}function yw(_){if(_.length===0)return $t;let N=$t.subcommands.filter((J)=>J.usage.split(" ")[0]===_[0]);if(N.length===0)return null;if(_.length===1&&N.length>1)return{name:`rule ${_[0]}`,description:`Subcommands of rule ${_[0]}`,usage:`rule ${_[0]} <subcommand>`,subcommands:N,options:[]};let j=_.length===1?N[0]:N.find((J)=>J.usage.split(" ")[1]===_[1]);if(!j)return null;return{name:`rule ${_[0]}`,description:j.description,usage:`rule ${j.usage}`,options:_[0]==="add"?Go:[],examples:_[0]==="add"?Bo:void 0}}function hw(_){let N=gn({label:"rule",booleans:{global:["-g","--global"],project:["--project"],cleanup:["--cleanup"],deleteSource:["--delete-source"],example:["--example"]},values:{ref:["--ref"],cwdArg:["--cwd-arg"]},lists:{only:["--only"]},positionals:"list"},_),j={...N.flags,ref:N.values.ref,cwdArg:N.values.cwdArg,only:N.lists.only??[],help:N.help,positionals:N.positionals,errors:N.errors};return vw(j),j}function vw(_){let[N]=_.positionals;if(N&&!kp.has(N))_.errors.push(`Unknown rule subcommand: ${N}`);if(_.deleteSource&&N!=="remove")if(N&&kp.has(N))_.errors.push(`Unknown option for rule ${N}: --delete-source`);else _.errors.push("--delete-source is only valid with 'rule remove'");if(_.cleanup&&N!=="migrate")_.errors.push(Wn(N,"--cleanup"));if(_.example&&N!=="init")_.errors.push(Wn(N,"--example"));if(_.ref&&N!=="add")_.errors.push(Wn(N,"--ref"));if(_.only.length>0&&N!=="add")_.errors.push(Wn(N,"--only"));let j=N==="tool"?_.positionals[1]:void 0,J=j&&xp.has(j)?`tool ${j}`:N;if(_.project&&j!=="add"&&j!=="remove")_.errors.push(Wn(J,"--project"));if(_.cwdArg!==void 0&&j!=="add")_.errors.push(Wn(J,"--cwd-arg"));if(N==="tool"&&_.global)_.errors.push(Wn(N,"--global"));if(N==="add")bw(_);if(N==="migrate"){if(_.global)_.errors.push(Wn(N,"--global"));if(_.positionals.length>1)_.errors.push(`Unexpected rule migrate argument: ${_.positionals[1]}`)}else if(N==="wrapper")Cp(_,"a command");else if(N==="tool")Cp(_,"a tool name");else if(_.positionals.length>2)_.errors.push(`Unexpected rule argument: ${_.positionals[2]}`);if(N==="list"&&_.global)_.errors.push("Unknown option for rule list: --global")}function Pp(_){if(_.positionals[1])return _.positionals[1];if(_.ref||_.only.length>0)return mw;return}function bw(_){let N=Pp(_);if(!N)return;if((_.ref||_.only.length>0)&&!Z(N)){if(_.ref)_.errors.push(`--ref can only select a ref for an owner/repo source: ${N}`);if(_.only.length>0)_.errors.push("--only can only select rulebooks from an owner/repo source");return}if(_.ref&&!ae(_.ref))_.errors.push(`--ref must use valid path segments: ${_.ref}`);let j=_.only.filter((J)=>!p.test(J));if(j.length>0)_.errors.push(`Invalid rulebook names: ${j.join(", ")}`)}function Wn(_,N){return _?`Unknown option for rule ${_}: ${N}`:`Unknown option for rule: ${N}`}function Cp(_,N){let j=_.positionals[0],J=_.positionals[1],Y=_.positionals[2];if(!J){_.errors.push(`rule ${j} requires add, remove, or list`);return}if(!xp.has(J)){_.errors.push(`Unknown rule ${j} action: ${J}`);return}if(J==="list"){if(Y)_.errors.push(`Unexpected rule ${j} argument: ${Y}`);return}if(!Y){_.errors.push(`rule ${j} ${J} requires ${N}`);return}if(j!=="tool"&&_.positionals.length>3)_.errors.push(`Unexpected rule ${j} argument: ${_.positionals[3]}`)}function ww(_){if(r(_)===null){Au(_);return}let N=d(_);if(!N.config)return;vn(_,N.config)}async function kw(_,N){let j=N.positionals[1],J=N.positionals[2],Y=hn(_,{global:N.global}).configTarget;if(j==="list"){let fe=d(Y);if(fe.errors.length>0){for(let ke of fe.errors)console.error(ke);return 1}return Cw(fe.config?.transparent_wrappers??[]),0}if(!J||!E.test(J))return console.error("transparent wrapper must match command pattern"),1;if(Le(J))return console.error(`reserved command "${J}" cannot be a wrapper`),1;let re=d(Y);if(re.errors.length>0){for(let fe of re.errors)console.error(fe);return 1}let ie=re.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},de=j==="add"?[...new Set([...ie.transparent_wrappers??[],J])]:(ie.transparent_wrappers??[]).filter((fe)=>fe!==J);return vn(Y,{...ie,transparent_wrappers:de}),console.log(j==="add"?`Added transparent wrapper: ${J}`:`Removed transparent wrapper: ${J}`),0}function Cw(_){if(_.length===0){console.log("Transparent wrappers: (none)");return}console.log(`Transparent wrappers (${_.length}):`);for(let N of _)console.log(` - ${N}`)}function xw(_,N,j){let J=N.positionals[1],Y=[...new Set(N.positionals.slice(2))];if(J==="list")return Sw(_,j);let re=[...Y.flatMap((ke)=>{if(!Fe.test(ke))return[`"${ke}" is not a tool name: ${xe}.`];let Ce=J==="add"?Lo(ke):void 0;return Ce?[Ce]:[]}),...N.cwdArg!==void 0&&!Fe.test(N.cwdArg)?[`"${N.cwdArg}" is not an argument name: ${xe}.`]:[]];if(re.length>0){for(let ke of re)console.error(en.red(`Error: ${ke}`));return 1}let ie=hn(_,{global:!N.project}),de=kr(ie.configTarget);if(!de.ok){for(let ke of de.result.errors)console.error(ke);return 1}let fe=Y.reduce((ke,Ce)=>{let Re=N.cwdArg===void 0?Ce:{name:Ce,cwd:N.cwdArg},Oe=(Ee)=>(typeof Ee==="string"?Ee:Ee.name)===Ce;if(J==="remove")return ke.filter((Ee)=>!Oe(Ee));return ke.some(Oe)?ke.map((Ee)=>Oe(Ee)?Re:Ee):[...ke,Re]},de.config.command_tools??[]);vn(ie.configTarget,{...de.config,command_tools:fe});for(let ke of Y)console.log(J==="add"?`Added command tool: ${ke}${N.cwdArg===void 0?"":` (cwd argument: ${N.cwdArg})`}`:`Removed command tool: ${ke}`);return console.log(`Scope: ${N.project?"project":"user"} (${ie.configPath})`),0}function Sw(_,N){let j=(A(_,{cwd:N})?["user"]:["user","project"]).map((Y)=>{let re=hn(_,{global:Y==="user"});return{scope:Y,path:re.configPath,...d(re.configTarget)}}),J=j.flatMap((Y)=>Y.errors.map((re)=>`${Y.path}: ${re}`));if(J.length>0){for(let Y of J)console.error(Y);return 1}return _u(j.flatMap((Y)=>(Y.config?.command_tools??[]).map((re)=>({scope:Y.scope,tool:re})))),0}import{sep as Aw}from"node:path";import{existsSync as Pw,readFileSync as Rw}from"node:fs";import{join as Ew}from"node:path";async function _w(_){if(_.isTTY)return null;return(await ot(_).catch(()=>null))?.trim()||null}function Tw(_){let N=_.env.get("CLAUDE_SETTINGS_PATH");if(N)return N;return Ew(qr(_),"settings.json")}function Is(_){let N=Tw(_);if(!Pw(N))return!1;try{let j=Rw(N,"utf-8"),J=JSON.parse(j);if(!J.enabledPlugins)return!1;let Y="cc-safety-net@cc-marketplace";if(!(Y in J.enabledPlugins))return!1;return J.enabledPlugins[Y]===!0}catch(j){if(S(n.debug,_.env))console.error(`CC Safety Net debug: failed to read Claude settings: ${N}: ${j instanceof Error?j.message:String(j)}`);return!1}}async function $s(_,N=process.stdin){let j=Is(_),J;if(!j)J="\uD83D\uDEE1️ CC Safety Net ❌";else{let re=R(_,{cwd:process.cwd()}),ie=re.policy,de=P(ie,_.env),fe=Object.values(V(ie,de.capabilities)).some((Re)=>Re.changesInherited),ke={standard:"✅",strict:"\uD83D\uDD12",paranoid:"\uD83D\uDC41️",custom:"\uD83D\uDD27"}[fe?"custom":de.effectiveLevel],Ce=re.policyScopes&&!re.policyScopes.weakeningsIgnored&&re.policyScopes.weakenings.length>0?"\uD83D\uDD3B":"";J=`\uD83D\uDEE1️ CC Safety Net ${ke}${de.worktreeMode?"\uD83C\uDF33":""}${Ce}${re.state==="degraded"?"⚠️":""}`}let Y=await _w(N);if(Y&&!Y.startsWith("{"))console.log(`${Y} | ${J}`);else console.log(J)}function Rp(_){let N=R(_,{cwd:process.cwd()}),j=N.policy,J=P(j,_.env),Y=!!process.env.NO_COLOR||!process.stdout.isTTY,re=Math.min(process.stdout.columns||80,100),ie=Y?"ok":"✔",de=Y?"OFF":"✘",fe=(Je,Ge)=>{let Ye=` ${Je.padEnd(13)}${Ge}`;return(Ye.length>re?`${Ye.slice(0,re-1)}…`:Ye).replaceAll(de,en.red(de))},ke=Object.values(V(j,J.capabilities)).some((Je)=>Je.changesInherited),Ce=(Je)=>Je===_.home||Je.startsWith(`${_.home}${Aw}`)?`~${Je.slice(_.home.length)}`:Je,Re={ready:en.green,degraded:en.yellow}[N.state],Oe=N.policyScopes?.weakenings??[],Ee=[...Is(_)?[]:["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."],...N.diagnostics],Ne=Y?"-":"·";console.log([`${Y?"":"\uD83D\uDEE1️ "}CC Safety Net — ${Re(N.state)}`,"",fe("Protection",`destructive ${j.destructiveCommandProtectionEnabled?ie:de} secrets ${j.secretProtection.enabled?ie:de}`),fe("Level",ke?`${J.effectiveLevel} (customised)`:J.effectiveLevel),fe("Rules",j.rules.length===0?"none active":`${j.rules.length} active`),fe("Policy",Ce(u(_))),...N.policyScopes?[fe("Project",Ce(h(process.cwd())))]:[],...J.worktreeMode?[fe("Worktree","relaxations active")]:[],"",...Oe.length===0?[]:[N.policyScopes?.weakeningsIgnored?" Project policy (ignored)":" Project policy",...Oe.flatMap((Je)=>ur(Je," ",re-6).map((Ge,Ye)=>Ye===0?` ${Ge}`:Ge)),""],...Ee.length===0?[" Everything configured is active."]:[" Not active",...Ee.flatMap((Je)=>ur(Je," ",re-6).map((Ge,Ye)=>Ye===0?` ${Ne} ${Ge}`:Ge)),""," Full report: cc-safety-net doctor"]].join(`
|
|
289
|
+
`))}import{spawn as jp}from"node:child_process";import{randomBytes as Mw}from"node:crypto";import{existsSync as Uw}from"node:fs";import{createServer as Gw}from"node:http";import{Writable as Bw}from"node:stream";var jo=500;function Ow(_){let N=_.filter((Y)=>Y.decision!=="allow"),j=_.filter((Y)=>Y.decision==="allow"),J=Math.min(N.length,Math.max(jo-j.length,Math.ceil(jo/2)));return[...N.slice(0,J),...j.slice(0,jo-J)]}function Ep(_,N,j=U(_)){if(j)K(_,j);let J=(Ge)=>new Date(Ge.getFullYear(),Ge.getMonth(),Ge.getDate()).getTime(),Y=J(new Date),re=new Date(Y);re.setDate(re.getDate()-(N-1));let ie=re.getTime(),de=[],fe={count:0};for(let Ge of j?zn(j,fe):[])for(let Ye of It(Ge,fe)){let rn=new Date(Ye.ts).getTime();if(!Number.isFinite(rn))continue;if(rn>=ie)de.push(Ye)}de.sort((Ge,Ye)=>new Date(Ye.ts).getTime()-new Date(Ge.ts).getTime());let ke=Array.from({length:N},()=>0),Ce=Array.from({length:N},()=>0),Re={},Oe={},Ee={},Ne=0,Je=0;for(let Ge of de){let Ye=Ge.agent||"unknown";Re[Ye]=(Re[Ye]??0)+1;let rn=Math.round((Y-J(new Date(Ge.ts)))/86400000),on=N-1-rn,sn=rn>=0&&rn<N;if(sn)Ce[on]=(Ce[on]??0)+1;if(Ge.decision!=="allow"){if(Ne++,Ge.ruleId)Oe[Ge.ruleId]=(Oe[Ge.ruleId]??0)+1;let tn=Mo(Ge.segment||Ge.command);if(tn)Ee[tn]=(Ee[tn]??0)+1;if(Ge.failureStage)Je++;if(sn)ke[on]=(ke[on]??0)+1}}return{days:N,logsDir:j,homeDir:_.home,totalInWindow:de.length,truncated:de.length>jo,unreadable:fe.count,counts:{blocked:Ne,allowed:de.length-Ne,agents:Re,blockedByDay:ke,analyzedByDay:Ce,rules:Oe,commands:Ee,errors:Je},entries:Ow(de).sort((Ge,Ye)=>new Date(Ye.ts).getTime()-new Date(Ge.ts).getTime())}}import{spawn as Iw}from"node:child_process";import{existsSync as $w,statSync as _p}from"node:fs";import{delimiter as Dw,join as Lw}from"node:path";var Nw=120000,Ho="Choose the project folder",jw=`try
|
|
290
|
+
return POSIX path of (choose folder with prompt "${Ho}")
|
|
291
291
|
on error number -128
|
|
292
292
|
return ""
|
|
293
|
-
end try`,
|
|
293
|
+
end try`,Hw=`Add-Type -AssemblyName System.Windows.Forms
|
|
294
294
|
$dialog = New-Object System.Windows.Forms.FolderBrowserDialog
|
|
295
|
-
$dialog.Description = '${
|
|
296
|
-
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,
|
|
295
|
+
$dialog.Description = '${Ho}'
|
|
296
|
+
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { [Console]::Out.Write($dialog.SelectedPath) }`,Tp=[{binary:"zenity",args:["--file-selection","--directory",`--title=${Ho}`]},{binary:"kdialog",args:["--getexistingdirectory",".","--title",Ho]}],Ap=(_,N)=>(N.PATH??"").split(Dw).some((j)=>{if(j.length===0)return!1;try{let J=_p(Lw(j,_));return J.isFile()&&(J.mode&73)!==0}catch{return!1}});function Ds(_,N){if(_==="darwin"||_==="win32")return!0;if(_!=="linux")return!1;if(!N.DISPLAY&&!N.WAYLAND_DISPLAY)return!1;return Tp.some((j)=>Ap(j.binary,N))}function Fw(_,N){if(_==="darwin")return{cmd:"osascript",args:["-e",jw]};if(_==="win32")return{cmd:"powershell.exe",args:["-NoProfile","-STA","-Command",Hw]};let j=Tp.find((J)=>Ap(J.binary,N));return j?{cmd:j.binary,args:j.args}:null}function Ls(_=process.platform,N=process.env){let j=Fw(_,N);if(!j)return Promise.resolve({error:"No folder dialog is available on this system"});return new Promise((J)=>{let Y=Iw(j.cmd,j.args,{env:N,stdio:["ignore","pipe","pipe"]}),re="",ie=!1,de=(ke)=>{if(ie)return;ie=!0,clearTimeout(fe),J(ke)},fe=setTimeout(()=>{Y.kill(),de({error:"The folder dialog timed out"})},Nw);Y.stdout.on("data",(ke)=>{re+=ke.toString()}),Y.on("error",()=>de({error:`Could not open the folder dialog (${j.cmd})`})),Y.on("close",()=>{let ke=re.trim().replace(/\/+$/,"");if(!ke)return de({cancelled:!0});if(!$w(ke)||!_p(ke).isDirectory())return de({error:"That selection is not a folder on disk"});de({path:ke})})})}var Op=`<!doctype html>
|
|
297
297
|
<html lang="en">
|
|
298
298
|
<head>
|
|
299
299
|
<meta charset="utf-8">
|
|
@@ -5755,6 +5755,6 @@ loadPolicy().then((loaded) => {
|
|
|
5755
5755
|
</script>
|
|
5756
5756
|
</body>
|
|
5757
5757
|
</html>
|
|
5758
|
-
`;var
|
|
5759
|
-
`),{path:
|
|
5760
|
-
`)}}finally{console.log=
|
|
5758
|
+
`;var Ip='<script id="ccsn-data" type="application/json">';function $p(_,N=Op){return N.replace(Ip,()=>Ip+JSON.stringify({token:_}).replaceAll("<","\\u003c"))}var js="kenryu42/cc-safety-net",qw=`https://github.com/${js}`,Hp=1e4,Vw=7,Kw="The project draft directory changed; reload the draft before applying.",Jw="audit settings are user scope only; remove the audit section from a project proposal";async function Fp(_,N={}){let j=gn({label:"gui",booleans:{noOpen:["--no-open"]}},_),J=N.log??console.log,Y=N.error??console.error;if(j.errors.length>0){for(let ie of j.errors)Y(ie);return Y("Usage: cc-safety-net gui [--no-open]"),1}let re=await Ww(c,N);if(J(`CC Safety Net policy GUI: ${re.url}`),!j.flags.noOpen)try{await(N.openBrowser??ik)(re.url)}catch(ie){Y(`Failed to open browser: ${ie instanceof Error?ie.message:String(ie)}`),Y(`Open this URL manually: ${re.url}`)}if(N.keepAlive===!1)return await re.close(),0;return await ok(re),0}async function Ww(_,N={}){let j=Mw(24).toString("base64url"),J={dir:null,revision:0},Y=Gw((de,fe)=>{zw(_,de,fe,j,N,J)});await new Promise((de,fe)=>{Y.once("error",fe),Y.listen(0,"127.0.0.1",()=>{Y.off("error",fe),de()})});let ie=`http://127.0.0.1:${Y.address().port}`;return{origin:ie,token:j,url:`${ie}/?token=${encodeURIComponent(j)}`,close:()=>rk(Y)}}async function zw(_,N,j,J,Y,re){let ie=_(),de=new URL(N.url??"/","http://127.0.0.1");if(N.method==="GET"&&de.pathname==="/favicon.ico"){j.writeHead(204,{"cache-control":"no-store"}),j.end();return}if(!ek(N,de,J)){an(j,403,{error:"Forbidden"});return}if(N.method==="GET"&&de.pathname==="/"){await Promise.resolve(Y.buildDocument?.()).then((fe)=>tk(j,$p(J,fe)),(fe)=>an(j,500,{error:fe instanceof Error?fe.message:String(fe)}));return}if(N.method==="GET"&&de.pathname==="/api/policy"){let fe=yu(ie,Y),ke=R(ie,Ns(Y));an(j,200,{...fe,configState:Be(ke),...ke.policyScopes?{projectPolicy:{path:h(Y.cwd??process.cwd()),weakenings:ke.policyScopes.weakeningsIgnored?[]:ke.policyScopes.weakenings}}:{},destructiveCommandRules:z,secretPatterns:lt,version:pn(),preview:fe.errors.length>0?null:Ue(fe.policy,ie.env)});return}if(N.method==="POST"&&de.pathname==="/api/policy/preview"){let fe=await Sr(N);if(!fe.ok){an(j,fe.status,{errors:[fe.error]});return}let ke=hu(ie,fe.value);an(j,ke.errors.length>0?400:200,ke);return}if(N.method==="POST"&&de.pathname==="/api/policy/explain"){let fe=await Sr(N);if(!fe.ok){an(j,fe.status,{errors:[fe.error]});return}let ke=fe.value;if(ke===null||typeof ke.command!=="string"){an(j,400,{errors:["command must be a string"]});return}let Ce=br(ke.policy,ie.home);if(Ce.length>0){an(j,400,{errors:Ce});return}an(j,200,Xw(ie,ke.command,ke.policy,Y));return}if(N.method==="POST"&&de.pathname==="/api/policy"){let fe=await Sr(N);if(!fe.ok){an(j,fe.status,{errors:[fe.error]});return}let ke=Fn(ie,fe.value,Y);an(j,ke.errors.length>0?400:200,ke);return}if(N.method==="POST"&&de.pathname==="/api/reset"){an(j,200,Fn(ie,te,Y));return}if(N.method==="POST"&&de.pathname==="/api/repair"){an(j,200,vu(ie,Y));return}if(N.method==="POST"&&de.pathname==="/api/policy/project/choose-directory"){let fe=await(Y.chooseDirectory??Ls)();if("path"in fe)re.dir=fe.path,re.revision+=1;an(j,200,{cancelled:"cancelled"in fe,..."error"in fe?{error:fe.error}:{}});return}if(N.method==="GET"&&de.pathname==="/api/policy/project"){let fe=Mp(re,Y),ke=Up(ie,fe,Y),Ce=ke?{projection:{},diagnostics:[ke]}:Dp(fe,ie.home),Re=wr(ie,Y);an(j,200,{path:h(fe),revision:re.revision,baseline:Re.baseline,userPolicyDiagnostics:Re.diagnostics,projection:Ce.projection,projectionDiagnostics:Ce.diagnostics,canPickDirectory:Ds(process.platform,process.env)});return}if(N.method==="POST"&&de.pathname==="/api/policy/project/diff"){let fe=await Lp(ie,N,j,re,Y);if(!fe)return;let ke=Dp(fe.dir,ie.home),Ce=wr(ie,Y).baseline,Re=ee(Ce,ue(fe.proposal,ie.home).policy);an(j,200,{rows:So(ee(Ce,ke.projection).policy,Re.policy,!1),weakenings:Re.weakenings,existingFileDiagnostics:ke.diagnostics});return}if(N.method==="POST"&&de.pathname==="/api/policy/project/apply"){let fe=await Lp(ie,N,j,re,Y);if(!fe)return;let ke=Zw(fe.dir,fe.proposal,ie.home);an(j,ke.errors.length>0?500:200,ke);return}if(N.method==="GET"&&de.pathname==="/api/activity"){let fe=oe(ie,Y),ke=Qw(de.searchParams.get("days"),fe);if(ke===null){an(j,400,{error:`days must be an integer between 1 and ${fe}`});return}an(j,200,Ep(ie,ke,Y.activityLogsDir));return}if(N.method==="POST"&&de.pathname==="/api/rules/choose-directory"){an(j,200,await Ls());return}if(N.method==="GET"&&de.pathname==="/api/rules"){let fe=Q(ie,Ns(Y)),ke=new Map(fe.rules.map((Ce)=>[Ce.name,Ce]));an(j,200,{projectPath:Y.cwd??process.cwd(),canPickDirectory:Ds(process.platform,process.env),rulebooks:fe.rulebooks.map((Ce)=>({source:Ce.source,spec:Ce.spec,name:Ce.name,version:Ce.version,rules:Ce.rules.flatMap((Re)=>{let Oe=ke.get(Re);if(!Oe)return[];return[{name:Oe.name,command:Oe.command,subcommand:Oe.subcommand,block_args:Oe.block_args,reason:Oe.reason}]})})),errors:fe.errors,warnings:fe.warnings});return}if(N.method==="GET"&&de.pathname==="/api/star/context"){an(j,200,await(Y.fetchStarContext??(()=>uk(ie,{logsDir:Y.activityLogsDir})))());return}if(N.method==="POST"&&de.pathname==="/api/star"){let fe=await(Y.starRepo??sk)();an(j,200,fe.ok?{ok:!0}:{ok:!1,fallbackUrl:qw});return}if(N.method==="GET"&&de.pathname==="/api/integrations"){an(j,200,await(Y.fetchIntegrations??(()=>ak(ie)))());return}if(N.method==="GET"&&de.pathname==="/api/health"){an(j,200,await(Y.fetchHealth??ck)());return}if(N.method==="POST"&&(de.pathname==="/api/install"||de.pathname==="/api/uninstall")){let fe=await Sr(N);if(!fe.ok){an(j,fe.status,{errors:[fe.error]});return}let ke=fe.value?.target;if(typeof ke!=="string"||!In.some((Re)=>Re.target===ke)){an(j,400,{error:"unknown target"});return}let Ce=de.pathname==="/api/install"?"install":"uninstall";an(j,200,await(Y.runIntegration??dk)(Ce,ke));return}an(j,404,{error:"Not found"})}function Ns(_){return{..._,cwd:_.cwd??process.cwd()}}function Mp(_,N){return _.dir??N.cwd??process.cwd()}function Up(_,N,j){if(!A(_,{...j,cwd:N}))return null;return`${h(N)} is the user policy, not a project policy; choose a project directory, or run the GUI from one`}function Dp(_,N){let j=h(_),J=Uw(j)?Ot(j):{value:void 0,errors:[]},Y=ue(J.value,N);return{projection:Y.policy,diagnostics:[...J.errors,...Y.diagnostics]}}async function Lp(_,N,j,J,Y){let re=Mp(J,Y),ie=J.revision,de=await Sr(N);if(!de.ok)return an(j,de.status,{errors:[de.error]}),null;let fe=de.value;if(typeof fe?.revision!=="number")return an(j,400,{errors:["revision must be a number"]}),null;if(fe.revision!==ie)return an(j,409,{errors:[Kw]}),null;let ke=Up(_,re,Y);if(ke)return an(j,400,{errors:[ke]}),null;let Ce=Yw(fe.proposal,_.home);if(Ce.length>0)return an(j,400,{errors:Ce}),null;return{dir:re,proposal:fe.proposal}}function Yw(_,N){let j=br(_,N);if(j.length>0)return j;return _?.audit===void 0?[]:[Jw]}function Zw(_,N,j){let J=h(_),Y=Po(N,k(N,j));try{return g(i(w(_,"project policy"),J),`${JSON.stringify(Y,null,2)}
|
|
5759
|
+
`),{path:J,errors:[]}}catch(re){return{path:J,errors:[re instanceof Error?re.message:String(re)]}}}function Xw(_,N,j,J){let Y=k(j,_.home),re=R(_,Ns(J)),ie=Ae({rules:re.policy.rules,transparentWrappers:re.policy.transparentWrappers,commandTools:re.policy.commandTools,safety:We(Y.safety),worktreeMode:Y.workflow.worktree_mode,destructiveCommandProtectionEnabled:Y.destructive_command_protection.enabled,destructiveCommandRuleOverrides:Y.destructive_command_protection.overrides,destructiveCommandAllowPaths:Y.destructive_command_protection.allow_paths,secretProtection:{enabled:Y.secret_protection.enabled,disabledRules:He(Y.secret_protection.overrides),denyPaths:Y.secret_protection.deny_paths,allowPaths:Y.secret_protection.allow_paths}});return pr(N,{policySnapshot:ie,cwd:J.cwd,userConfigDir:J.userConfigDir},_)}function Qw(_,N){if(_===null)return Math.min(Vw,N);let j=Number(_);if(!Number.isInteger(j)||j<1||j>N)return null;return j}function ek(_,N,j){if(N.searchParams.get("token")!==j)return!1;if(_.method!=="POST")return!0;return _.headers["x-cc-safety-net-token"]===j}var nk=1048576;async function Sr(_){let N=[],j=0;for await(let J of _){let Y=J;if(j+=Y.byteLength,j>nk)return{ok:!1,status:413,error:"Request body is too large"};N.push(Y)}try{return{ok:!0,value:JSON.parse(Buffer.concat(N).toString("utf-8")||"{}")}}catch(J){return{ok:!1,status:400,error:`Invalid JSON: ${J instanceof Error?J.message:String(J)}`}}}function tk(_,N){_.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),_.end(N)}function an(_,N,j){_.writeHead(N,{"content-type":"application/json; charset=utf-8","cache-control":"no-store"}),_.end(JSON.stringify(j))}function rk(_){return new Promise((N,j)=>{_.close((J)=>J?j(J):N())})}function ok(_){return new Promise((N)=>{let j=()=>{process.off("SIGINT",J),process.off("SIGTERM",J)},J=()=>{j(),_.close().then(N)};process.once("SIGINT",J),process.once("SIGTERM",J)})}function ik(_){let N=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",j=process.platform==="win32"?["/c","start","",_]:[_];return new Promise((J,Y)=>{let re=jp(N,j,{detached:!0,stdio:"ignore"}),ie=(fe)=>{re.off("spawn",de),Y(fe)},de=()=>{re.off("error",ie),re.unref(),J()};re.once("error",ie),re.once("spawn",de)})}async function sk(_="gh",N=Hp){return{ok:await pk(_,["api","-X","PUT",`/user/starred/${js}`],N)===0}}async function ak(_,N={}){let j=await Lr((Y)=>qt({environment:_,cwd:process.cwd(),openCodeVersion:Y}).status!=="n/a",N.fetcher),J=lk(_,j);return{targets:Rn.map((Y)=>{let re=J.find((ie)=>ie.platform===Y.id);return{target:Y.id,label:cn(Y.id),version:j.versions[Y.id]??null,status:re?.configured?"active":re?.detected?"disabled":re?.inspectionStatus==="not-inspected"?"not-inspected":"not-installed"}}),system:{version:j.version,nodeVersion:j.nodeVersion,platform:j.platform}}}function lk(_,N){return Vt(_,process.cwd(),{ampPluginListOutput:N.ampPluginListOutput,codexPluginListOutput:N.codexPluginListOutput,copilotCliVersion:N.versions["copilot-cli"],openCodeVersion:N.versions.opencode,openCodePluginListOutput:N.openCodePluginListOutput})}async function ck(_={}){let N=await(_.checkUpdates??Gn)();return{update:{latestVersion:N.latestVersion??null,updateAvailable:N.updateAvailable}}}var Np=Promise.resolve();function dk(_,N,j={}){let J=async()=>{let re=[],{log:ie,error:de}=console;console.log=(...fe)=>re.push(fe.map(String).join(" ")),console.error=console.log;try{return{ok:await vr(_,[],{selectTargets:async()=>[N],output:new Bw({write(ke,Ce,Re){re.push(String(ke).replace(/\n$/,"")),Re()}}),...j})===0,output:re.join(`
|
|
5760
|
+
`)}}finally{console.log=ie,console.error=de}},Y=Np.then(J);return Np=Y.then(()=>{return},()=>{return}),Y}async function uk(_,N={}){let[j,J]=await Promise.all([fk(N.fetchRepo),Promise.resolve(Ar(_,oe(_),N.logsDir).totalBlocked)]);return{starCount:j,blockedTotal:J}}function pk(_,N,j){return new Promise((J)=>{let Y=jp(_,N,{stdio:"ignore",windowsHide:!0}),re=!1,ie=setTimeout(()=>{Y.kill(),de(null)},j),de=(fe)=>{if(re)return;re=!0,clearTimeout(ie),J(fe)};Y.once("error",()=>de(null)),Y.once("close",de)})}async function fk(_=fetch){try{let N=await _(`https://api.github.com/repos/${js}`,{headers:{accept:"application/vnd.github+json"},signal:AbortSignal.timeout(Hp)});if(!N.ok)return null;let j=await N.json();return typeof j.stargazers_count==="number"?j.stargazers_count:null}catch{return null}}function mk(_){if(_[0]!=="help")return!1;let N=_[1];if(!N)ss(),process.exit(0);if(fr(N))process.exit(0);console.error(`Unknown command: ${N}`),console.error("Run 'cc-safety-net --help' for available commands."),process.exit(1)}var gk={hook:async()=>{console.error("hook requires exactly one integration flag. Try: cc-safety-net hook --kimi-code"),fr("hook",console.error),process.exit(1)},install:async(_)=>{process.exit(await vr("install",_))},update:async(_)=>{process.exit(await ks(_))},uninstall:async(_)=>{process.exit(await vr("uninstall",_))},rule:async(_)=>{process.exit(await Sp(c(),_))},policy:async(_)=>{process.exit(await Cu(c(),_))},status:async(_)=>{if(Dn(gn({label:"status"},_).errors))process.exit(1);Rp(c())},statusline:async(_)=>{let N=gn({label:"statusline",booleans:{claudeCode:["-cc","--claude-code"]}},_);if(N.errors.length===0&&N.flags.claudeCode){await $s(c());return}if(Dn(N.errors),!N.flags.claudeCode)console.error("statusline requires --claude-code (-cc)");fr("statusline",console.error),process.exit(1)},doctor:async(_)=>{let N=Xi(_);if(!N)process.exit(1);let j=await Hc(c(),{json:N.json,skipUpdateCheck:N.skipUpdateCheck});process.exit(j)},logs:async(_)=>{process.exit(await Vs(c(),_))},gui:async(_)=>{process.exit(await Fp(_))},explain:async(_)=>{process.exit(await zc(c(),_))}};async function yk(_){let N=gn({label:"cc-safety-net",booleans:{version:["-V","--version"]},positionals:"list"},_);if(mk(_))return;let j=_[0],J=j?Tr(j):void 0;if(N.help&&J&&J.name!=="rule")fr(J.name),process.exit(0);if(!j||N.help&&!J)ss(),process.exit(0);if(N.flags.version)Xc(),process.exit(0);if(J){await gk[J.name](_.slice(1));return}if(j==="--statusline"){await $s(c());return}console.error(j.startsWith("-")?`Unknown option: ${j}`:`Unknown command: ${j}`),console.error("Run 'cc-safety-net --help' for usage."),process.exit(1)}export{yk as runCli};
|