cc-safety-net 2.4.13 → 2.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/amp/cc-safety-net/index.ts +40 -40
- package/dist/api.js +1 -1
- package/dist/bin/hook.js +38 -38
- package/dist/chunks/{index-fjkkpma1.js → index-96rww7g0.js} +2 -2
- package/dist/chunks/index-gj6afr0n.js +45 -0
- package/dist/cli.js +4 -4
- package/dist/index.js +1 -1
- package/dist/openclaw/cc-safety-net/index.js +40 -40
- package/dist/openclaw/cc-safety-net/openclaw.plugin.json +1 -1
- package/dist/openclaw/cc-safety-net/package.json +1 -1
- package/dist/pi/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunks/index-av9c301x.js +0 -45
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a,s,ke,Ue,k,Ze,Ge,c,o,He,R,L,ve,Qe,f,We,d,r,v,i,re,n,g,le,F,tt,nt,l,ue,de,rt,I,K,oe,S,ot,M,b,Se,j,u,J,Be,ze,O,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,Y,U,Pe,me,G,Ke,H,Z,X,ae,Je,Ye,C,W,Ae,Oe,De,E,Ne,Ee,Fe,h,t,Q,ge,y,x,_,st,Me,$e,p,B}from"./chunks/index-
|
|
1
|
+
import{a,s,ke,Ue,k,Ze,Ge,c,o,He,R,L,ve,Qe,f,We,d,r,v,i,re,n,g,le,F,tt,nt,l,ue,de,rt,I,K,oe,S,ot,M,b,Se,j,u,J,Be,ze,O,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,Y,U,Pe,me,G,Ke,H,Z,X,ae,Je,Ye,C,W,Ae,Oe,De,E,Ne,Ee,Fe,h,t,Q,ge,y,x,_,st,Me,$e,p,B}from"./chunks/index-gj6afr0n.js";import{te,z,je,D,N}from"./chunks/index-96rww7g0.js";var Zd=["-h","--help"];function xt(P,A){let q=Object.entries(P.booleans??{}),V=Object.entries(P.values??{}),ee=Object.entries(P.lists??{}),ne=Object.fromEntries(q.map(([Xe])=>[Xe,!1])),ce={},he=Object.fromEntries(ee.map(([Xe])=>[Xe,[]])),ye=[],be=[],xe=!1,Ie=-1;for(let[Xe,et]of A.entries()){if(Xe<=Ie)continue;if(et==="--"){ye.push(...A.slice(Xe+1));break}if(Zd.includes(et)){xe=!0;continue}let Re=q.find(([,lt])=>lt.includes(et));if(Re){ne[Re[0]]=!0;continue}let at=V.find(([,lt])=>lt.includes(et));if(at){let lt=A[Xe+1];if(lt===void 0||lt.startsWith("-")){be.push(`${et} requires a value`);continue}ce[at[0]]=lt,Ie=Xe+1;continue}let ct=ee.find(([,lt])=>lt.includes(et));if(ct){let lt=A.slice(Xe+1),ft=lt.findIndex((gt)=>gt.startsWith("-")),mt=lt.slice(0,ft===-1?lt.length:ft);if(mt.length===0){be.push(`${et} requires at least one value`);continue}he[ct[0]]=[...he[ct[0]]??[],...mt],Ie=Xe+mt.length;continue}if(et.startsWith("-")){be.push(`Unknown option for ${P.label}: ${et}`);continue}if(P.positionals==="tail"){ye.push(...A.slice(Xe));break}ye.push(et)}if(P.positionals!=="list"&&P.positionals!=="tail")be.push(...ye.map((Xe)=>`Unexpected argument for ${P.label}: ${Xe}`));return{flags:ne,values:ce,lists:he,positionals:ye,help:xe,errors:be}}function Ut(P){for(let A of P)console.error(A);return P.length>0}import{readdirSync as iu,statSync as Vi,unlinkSync as su}from"node:fs";import{basename as zi,dirname as au,isAbsolute as lu,join as cu,relative as du,resolve as uu,sep as pu}from"node:path";var Gi=(P)=>{let A=Date.now()-new Date(P).getTime();if(!Number.isFinite(A))return"";let q=Math.floor(A/60000),V=Math.floor(q/60),ee=Math.floor(V/24);if(ee>0)return`${ee}d ago`;if(V>0)return`${V}h ago`;if(q>0)return`${q}m ago`;return"just now"},so=(P)=>{let A=(P??"").trim().split(/\s+/).filter((ee)=>ee&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(ee)),q=A[0]?.split("/").pop();if(!q)return null;let V=A[1];return V&&/^[a-z][a-z0-9-]*$/.test(V)?`${q} ${V}`:q};function Bi(P){let A=(ee)=>`${ee.sessionId}
|
|
2
2
|
${so(ee.segment||ee.command)}`,q=P.filter((ee)=>ee.decision!=="allow"),V=q.filter((ee)=>ee.sessionId).reduce((ee,ne)=>ee.set(A(ne),(ee.get(A(ne))??0)+1),new Map);return new Set(q.filter((ee)=>ee.failureStage||(V.get(A(ee))??0)>=2))}import{existsSync as Qd,readdirSync as eu,readFileSync as tu}from"node:fs";import{join as nu}from"node:path";function Xt(P,A){try{return eu(P,{withFileTypes:!0,encoding:"utf8"}).flatMap((q)=>{let V=nu(P,q.name);if(q.isDirectory())return Xt(V,A);if(q.name.endsWith(".jsonl"))return[V];return[]})}catch{if(A&&Qd(P))A.count++;return[]}}var ru=["segment","reason","sessionId","decision","agent","ruleId","failureStage"];function ou(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let A=P;if(typeof A.ts!=="string"||typeof A.command!=="string")return!1;return ru.every((q)=>A[q]===void 0||typeof A[q]==="string")}function mn(P,A){try{return tu(P,"utf-8").split(`
|
|
3
3
|
`).filter(Boolean).flatMap((q)=>{try{let V=JSON.parse(q);if(!ou(V)){if(A)A.count++;return[]}return[V]}catch{if(A)A.count++;return[]}})}catch{if(A)A.count++;return[]}}function Ct(P){return Array.from(P,(A)=>{let q=A.charCodeAt(0);if(q<=31||q>=127&&q<=159)return`\\x${q.toString(16).padStart(2,"0")}`;return A}).join("")}function fu(P,A){let q=te(P),V=xt({label:"logs",booleans:{all:["--all"],suspect:["--suspect"],json:["--json"],pruneLegacy:["--prune-legacy"],dryRun:["--dry-run"]},values:{id:["--id"],limit:["--limit"],since:["--since"],agent:["--agent"],rule:["--rule"],session:["--session"],project:["--project"]}},A);if(Ut(V.errors))return null;if(V.values.id!==void 0&&!/^[a-f0-9]{16}$/.test(V.values.id))return console.error("--id must be 16 hexadecimal characters"),null;let ee=V.values.limit===void 0?20:qi(V.values.limit);if(ee===null)return console.error("--limit must be a positive number"),null;let ne=V.values.since===void 0?Math.min(30,q):qi(V.values.since);if(ne===null||ne>q)return console.error(`--since must be a positive number of days no greater than ${q}`),null;let ce={limit:ee,limitExplicit:V.values.limit!==void 0,since:ne,sinceExplicit:V.values.since!==void 0,all:V.flags.all,json:V.flags.json,suspect:V.flags.suspect,pruneLegacy:V.flags.pruneLegacy,dryRun:V.flags.dryRun,id:V.values.id,agent:V.values.agent,rule:V.values.rule,session:V.values.session,project:V.values.project===void 0?void 0:uu(V.values.project)};if(ce.id&&(ce.agent!==void 0||ce.rule!==void 0||ce.session!==void 0||ce.project!==void 0||ce.suspect||ce.sinceExplicit||ce.limitExplicit))return console.error("--id cannot be combined with --agent, --rule, --session, --project, --suspect, --since, or --limit"),null;if(ce.pruneLegacy&&(ce.id!==void 0||ce.agent!==void 0||ce.rule!==void 0||ce.session!==void 0||ce.project!==void 0||ce.suspect||ce.all||ce.sinceExplicit||ce.limitExplicit))return console.error("--prune-legacy cannot be combined with --id, --agent, --rule, --session, --project, --suspect, --all, --since, or --limit"),null;if(ce.dryRun&&!ce.pruneLegacy)return console.error("--dry-run requires --prune-legacy"),null;return ce}async function Ji(P,A,q={}){let V=fu(P,A);if(!V)return 1;let ee=q.logsDir??D(P);if(V.pruneLegacy)return mu(ee,V.json,V.dryRun);if(!ee)return console.log(V.json?"[]":V.id?`No retained audit log entry found for id ${Ct(V.id)}.`:"No audit log entries found."),0;z(P,ee);let ne={count:0},ce=Xt(ee,ne).flatMap((Ie)=>mn(Ie,ne).map((Xe)=>({entry:Xe,file:Ie})));if(ne.count>0)console.error(`warning: ${ne.count} audit log ${ne.count===1?"source":"sources"} could not be read; these results are incomplete`);if(V.id)return vu(ce,V,q.timeZone);let he=Date.now()-V.since*24*60*60*1000,ye=ce.filter((Ie)=>bu(Ie,V,ee,he)),be=V.suspect?Bi(ye.map((Ie)=>Ie.entry)):null,xe=(be?ye.filter((Ie)=>be.has(Ie.entry)):ye).sort((Ie,Xe)=>Date.parse(Xe.entry.ts)-Date.parse(Ie.entry.ts)).slice(0,V.limit);if(V.json)return console.log(JSON.stringify(xe.map((Ie)=>Ie.entry),null,2)),0;if(xe.length===0)return console.log("No audit log entries found."),0;for(let Ie of xe)console.log(ku(Ie.entry,q.timeZone));return 0}function mu(P,A,q){let V=P?hu(P).map((he)=>cu(P,he)):[];if(q)return gu(V,A);let ee=[],ne=0,ce=0;for(let he of V){let ye=Vi(he,{throwIfNoEntry:!1})?.size??0,be=yu(he);if(be){ee.push(`${zi(he)}: ${be}`);continue}ne++,ce+=ye}if(A)return console.log(JSON.stringify({removedFiles:ne,removedBytes:ce,failedFiles:ee.length})),ee.length===0?0:1;console.log(ne===0&&ee.length===0?"No legacy audit log files found.":`Removed ${ne} legacy audit log ${ne===1?"file":"files"} (${Wi(ce)}).`);for(let he of ee)console.error(`Could not remove ${Ct(he)}`);if(console.log("Nested v2 audit logs were not changed."),ne>0)console.log("This deletion cannot be undone.");return ee.length===0?0:1}function gu(P,A){let q=P.reduce((V,ee)=>V+(Vi(ee,{throwIfNoEntry:!1})?.size??0),0);if(A)return console.log(JSON.stringify({dryRun:!0,files:P.length,bytes:q})),0;if(console.log(P.length===0?"No legacy audit log files found.":`Would remove ${P.length} legacy audit log ${P.length===1?"file":"files"} (${Wi(q)}).`),console.log("Nested v2 audit logs are not included."),P.length>0)console.log("Run the same command without --dry-run to delete them.");return 0}function hu(P){try{return iu(P,{withFileTypes:!0}).filter((A)=>A.isFile()&&A.name.endsWith(".jsonl")).map((A)=>A.name)}catch{return[]}}function yu(P){try{return su(P),null}catch(A){return A instanceof Error?A.message:String(A)}}function Wi(P){let A=["B","KiB","MiB","GiB"],q=Math.min(Math.floor(Math.log2(Math.max(P,1))/10),A.length-1);return`${Math.round(P/1024**q*10)/10} ${A[q]}`}function vu(P,A,q){let V=P.filter((ne)=>ne.entry.id===A.id);if(V.length>1)return console.error(`Multiple audit log entries found for id ${Ct(A.id??"")}.`),1;if(A.json)return console.log(JSON.stringify(V.map((ne)=>ne.entry),null,2)),0;let ee=V[0];if(!ee)return console.log(`No retained audit log entry found for id ${Ct(A.id??"")}.`),0;return console.log(xu(ee.entry,q)),0}function bu(P,A,q,V){if(!A.all&&P.entry.decision==="allow")return!1;if(Date.parse(P.entry.ts)<V)return!1;if(A.agent!==void 0&&P.entry.agent!==A.agent)return!1;if(A.rule!==void 0&&P.entry.ruleId!==A.rule)return!1;if(A.session!==void 0&&!Lu(P,q,A.session))return!1;if(A.project!==void 0&&!wu(P.entry.cwd,A.project))return!1;return!0}function Lu(P,A,q){if(P.entry.sessionId===q)return!0;return au(P.file)===A&&zi(P.file,".jsonl")===q}function wu(P,A){if(!P)return!1;let q=du(A,P);return q!==".."&&!q.startsWith(`..${pu}`)&&!lu(q)}function ku(P,A){let q=Ct(P.id??"-"),V=Ct(P.decision??"deny"),ee=P.cwd?` [${Ct(P.cwd)}]`:"",ne=P.segment||P.command,ce=ne===P.command?"":"↳ ",he=ne.length>50?`${ne.slice(0,50)}…`:ne;return`${q.padEnd(16)} ${Ct(Ki(P.ts,A))} ${V.padEnd(5)} ${Ct(P.agent??"-").padEnd(15)} ${Ct(P.ruleId??"-").padEnd(20)} ${ce}${Ct(he)}${ee}`}function xu(P,A){let q=(ee)=>Ct(ee===void 0||ee===null||ee===""?"-":ee),V=P.shape?`${P.agent??"-"} (shape: ${P.shape})`:P.agent??"-";return[`id: ${q(P.id)}`,`ts: ${q(Ki(P.ts,A))}`,`decision: ${q(P.decision)}`,`agent: ${q(V)}`,`level: ${q(P.level)}`,`tool: ${q(P.toolName)}`,`rule: ${q(P.ruleId)}`,`intent: ${q(P.intent)}`,`stage: ${q(P.failureStage)}`,`error: ${q(P.errorCode)}`,`session: ${q(P.sessionId)}`,`cwd: ${q(P.cwd)}`,`version: ${q(P.v)}`,`truncated: ${q(P.truncated===!0?"yes":void 0)}`,`reason: ${q(P.reason)}`,`command: ${q(P.command)}`,`segment: ${q(P.segment)}`].join(`
|
|
4
4
|
`)}function Ki(P,A){let q=new Date(P);if(Number.isNaN(q.getTime()))return P;return new Intl.DateTimeFormat("sv-SE",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23",timeZone:A}).format(q)}function qi(P){let A=Number(P);return Number.isFinite(A)&&A>0?A:null}var Yi={name:"doctor",aliases:["--doctor"],description:"Run diagnostic checks to verify installation and configuration",usage:"doctor [options]",options:[{flags:"--json",description:"Output diagnostics as JSON"},{flags:"--skip-update-check",description:"Skip npm registry version check"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net doctor","cc-safety-net doctor --json","cc-safety-net doctor --skip-update-check"]};var Xi={name:"explain",description:"Show step-by-step analysis trace of how a command would be analyzed",usage:"explain [options] <command>",argument:"<command>",options:[{flags:"--json",description:"Output analysis as JSON"},{flags:"--cwd",argument:"<path>",description:"Use custom working directory"},{flags:"-h, --help",description:"Show this help"}],examples:['cc-safety-net explain "git reset --hard"','cc-safety-net explain --json "rm -rf /"','cc-safety-net explain --cwd /tmp "git status"']};var Zi={name:"gui",description:"Open the local policy editor GUI",usage:"gui [options]",options:[{flags:"--no-open",description:"Print the URL without opening a browser"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net gui","cc-safety-net gui --no-open"]};var nr=[{id:"antigravity-cli",displayName:"Antigravity CLI",doctorOrder:3,runtime:{order:1,flags:["-ac","--agy-cli"],description:"Run as Antigravity CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:2,flag:"--agy-cli",artifactKind:"hook config",probeCommand:["agy","--version"]}},{id:"claude-code",displayName:"Claude Code",doctorOrder:1,runtime:{order:2,displayName:"Coding CLI",flags:["-cc","--coding-cli"],legacyFlags:["--claude-code"],description:"Run as Coding CLI PreToolUse hook",legacyTopLevelFlags:["-cc","--claude-code"]},install:{order:3,flag:"--claude-code",artifactKind:"plugin",probeCommand:["claude","--version"]}},{id:"codex",displayName:"Codex",doctorOrder:4,runtime:{order:3,flags:["-cx","--codex"],description:"Run as a Codex PreToolUse hook",legacyTopLevelFlags:[]},install:{order:4,flag:"--codex",artifactKind:"plugin",probeCommand:["codex","--version"]}},{id:"copilot-cli",displayName:"GitHub Copilot CLI",doctorOrder:7,runtime:{order:6,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:7,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:6,runtime:{order:5,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:6,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:8,runtime:{order:7,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:9,runtime:{order:8,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:9,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:10,runtime:{order:9,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:10,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:11,install:{order:11,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:12,install:{order:12,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:13,install:{order:13,flag:"--pi",artifactKind:"package",probeCommand:["pi","--version"]}},{id:"cursor",displayName:"Cursor",doctorOrder:5,runtime:{order:4,flags:["-cu","--cursor"],description:"Run as Cursor preToolUse hook",legacyTopLevelFlags:[]},install:{order:5,flag:"--cursor",artifactKind:"hook config",probeCommand:["cursor","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],rr=nr.slice().sort((P,A)=>P.doctorOrder-A.doctorOrder).map((P)=>P.id),En=nr.filter((P)=>("runtime"in P)).slice().sort((P,A)=>P.runtime.order-A.runtime.order).map((P)=>({id:P.id,displayName:"displayName"in P.runtime?P.runtime.displayName:P.displayName,flags:P.runtime.flags,legacyFlags:"legacyFlags"in P.runtime?P.runtime.legacyFlags:[],description:P.runtime.description,legacyTopLevelFlags:P.runtime.legacyTopLevelFlags})),$t=nr.slice().sort((P,A)=>P.install.order-A.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...A})=>A),Cu=Object.fromEntries(nr.map((P)=>[P.id,P.displayName]));function bt(P){return Cu[P]}var Su=En.map((P)=>({flags:P.flags.join(", "),description:P.description})),Ru=En.flatMap((P)=>P.flags.map((A)=>`cc-safety-net hook ${A}`)),Qi={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...Su,{flags:"-h, --help",description:"Show this help"}],examples:Ru};var es={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Install ${bt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net install",...$t.map((P)=>`cc-safety-net install ${P.flag}`)]},ts={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Uninstall ${bt(P.id)} ${P.artifactKind}`})),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net uninstall",...$t.map((P)=>`cc-safety-net uninstall ${P.flag}`)]},ns={name:"update",description:"Update every installed CC Safety Net integration to the latest version",usage:"update",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net update"]};var rs={name:"logs",description:"Browse audit log entries recorded by hooks",usage:"logs [options]",options:[{flags:"--id",argument:"<id>",description:"Show one entry from retained history by its 16-character id (not guaranteed once it is older than the configured retention)"},{flags:"--limit",argument:"<n>",description:"Maximum entries to print",default:"20"},{flags:"--since",argument:"<days>",description:"Only include entries newer than this many days (max: the configured audit retention, 1-365)",default:"30"},{flags:"--agent",argument:"<name>",description:"Filter by agent name"},{flags:"--rule",argument:"<ruleId>",description:"Filter by rule id"},{flags:"--session",argument:"<id>",description:"Filter by session id"},{flags:"--project",argument:"<path>",description:"Filter by project path"},{flags:"--suspect",description:"Only denials that look like false positives"},{flags:"--all",description:"Include allow entries"},{flags:"--prune-legacy",description:"Permanently delete all legacy root-level logs; nested logs are untouched"},{flags:"--dry-run",description:"With --prune-legacy, report what would be deleted and delete nothing"},{flags:"--json",description:"Output entries as JSON"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net logs --id 3fa9c2d1a70e8b42","cc-safety-net logs --agent claude-code","cc-safety-net logs --project . --since 7","cc-safety-net logs --suspect --since 7","cc-safety-net logs --json","cc-safety-net logs --prune-legacy --dry-run","cc-safety-net logs --prune-legacy"]};var or={name:"policy",description:"Check and apply project or user policy proposals",usage:"policy <subcommand>",subcommands:[{usage:"check <file>",description:"Validate a policy proposal and print its diff"},{usage:"apply <file>",description:"Apply a proposal after confirming in a terminal"}],options:[{flags:"-g, --global",description:"Use the user-scope policy instead of the project one"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net policy check proposal.json","cc-safety-net policy apply proposal.json","cc-safety-net policy apply proposal.json --global"]};var ao=[{flags:"--ref",argument:"<ref>",description:"Use a branch, tag, or commit"},{flags:"--only",argument:"<rulebook...>",description:"Add only these repository rulebooks"},{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"-h, --help",description:"Show this help"}],lo=["cc-safety-net rule add project-rules","cc-safety-net rule add acme/safety-rules","cc-safety-net rule add acme/safety-rules --only aws gcloud","cc-safety-net rule add acme/safety-rules --ref v2 --only aws","cc-safety-net rule add --only terraform aws"],gn={name:"rule",description:"Manage CC Safety Net rule config and rulebook sources",usage:"rule <subcommand>",subcommands:[{usage:"init [--example]",description:"Create inert rule config"},{usage:"add [source] [--ref <ref>] [--only <rulebook...>]",description:"Add rulebook sources and sync"},{usage:"remove <source>",description:"Remove a rulebook source and sync"},{usage:"update [source]",description:"Re-fetch and vendor remote rulebooks"},{usage:"sync",description:"Deprecated: migrate lock and cache leftovers"},{usage:"list",description:"List active rulebooks"},{usage:"wrapper add <command>",description:"Trust a transparent command wrapper"},{usage:"wrapper remove <command>",description:"Remove a transparent command wrapper"},{usage:"wrapper list",description:"List transparent command wrappers"},{usage:"migrate [--cleanup]",description:"Migrate legacy inline rules"},{usage:"doc",description:"Print the rulebook authoring guide"},{usage:"verify",description:"Validate rule config files"}],options:[{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"--cleanup",description:"Delete legacy files after rule migrate verifies them"},{flags:"--delete-source",description:"Delete clean local source directory on remove"},{flags:"--example",description:"Create an inactive example rulebook with rule init"},...ao.slice(0,2),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net rule init","cc-safety-net rule init --example","cc-safety-net rule wrapper add rtk",...lo,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var os={name:"status",description:"Show what the runtime is enforcing right now",usage:"status",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net status"]};var is={name:"statusline",description:"Print status line with mode indicators for shell integration",usage:"statusline --claude-code",options:[{flags:"-cc, --claude-code",description:"Print status line for Claude Code"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net statusline -cc","cc-safety-net statusline --claude-code"]};var ir=[os,Yi,rs,Xi,gn,or,es,ns,ts,Qi,Zi,is];function Pu(P){return P.aliases??[]}function sr(P){let A=P.toLowerCase();return ir.find((q)=>q.name.toLowerCase()===A||Pu(q).some((V)=>V.toLowerCase()===A))}import{basename as Eu}from"node:path";function ar(P,A=7,q=D(P)){let V=Date.now()-A*24*60*60*1000,ee=[],ne=new Set,ce=0,he,ye,be,xe;if(q)z(P,q);let Ie={count:0},Xe=q?Xt(q,Ie):[];for(let Re of Xe)for(let at of mn(Re,Ie)){if(at.decision==="allow")continue;let ct=new Date(at.ts).getTime();if(ct>=V){if(ce++,ne.add(at.sessionId??Eu(Re,".jsonl")),ye===void 0||ct<=ye)he=at.ts,ye=ct;if(xe===void 0||ct>xe)be=at.ts,xe=ct;Du(ee,at,ct)}}let et=ee.map((Re)=>({timestamp:Re.ts,command:Re.command,reason:Re.reason,relativeTime:Gi(new Date(Re.ts))}));return{totalBlocked:ce,sessionCount:ne.size,recentEntries:et,oldestEntry:he,newestEntry:be,unreadable:Ie.count}}function Du(P,A,q){let V=P.findIndex((ee)=>q>new Date(ee.ts).getTime());if(V===-1){if(P.length<3)P.push(A);return}if(P.splice(V,0,A),P.length>3)P.pop()}import{dirname as Uu}from"node:path";import{dirname as Au,join as _u,resolve as Tu}from"node:path";var Iu="config.json";function Pt(P,A,q,V){g($u(P),`${JSON.stringify(A,null,2)}
|
|
@@ -18,7 +18,7 @@ ${so(ee.segment||ee.command)}`,q=P.filter((ee)=>ee.decision!=="allow"),V=q.filte
|
|
|
18
18
|
`)}function dr(P){return Zt({rows:P})}function Cs(P){let A=[];return A.push("System Info"),A.push(up(P)),A.join(`
|
|
19
19
|
`)}function up(P){let A=["Component","Version"],q=(ne)=>{if(ne===null)return dt.dim("not found");return ne},ee=[{label:"cc-safety-net",value:P.version},...rr.map((ne)=>({label:bt(ne),value:P.versions[ne]??null})),{label:"Node.js",value:P.nodeVersion},{label:"npm",value:P.npmVersion},{label:"Bun",value:P.bunVersion},{label:"Platform",value:P.platform}].map((ne)=>[ne.label,q(ne.value)]);return Zt({headers:A,rows:ee})}function Ss(P){if(P.findings.length===0)return dt.green(`
|
|
20
20
|
No findings from inspected doctor facts.`);let A={error:P.findings.filter((ne)=>ne.severity==="error").length,warning:P.findings.filter((ne)=>ne.severity==="warning").length,info:P.findings.filter((ne)=>ne.severity==="info").length},q=["error","warning","info"].filter((ne)=>A[ne]>0).map((ne)=>`${A[ne]} ${ne}`),V=P.findings.length===1?"finding":"findings",ee=`
|
|
21
|
-
${P.findings.length} ${V}: ${q.join(", ")}.`;if(A.error>0)return dt.red(ee);if(A.warning>0)return dt.yellow(ee);return dt.blue(ee)}import{lstatSync as pp}from"node:fs";import{dirname as mo}from"node:path";function go(P,A){try{let q=pp(A);if(q.isSymbolicLink())return{kind:P,path:A,status:"unsafe",issues:["symlink"]};if(!q.isDirectory())return{kind:P,path:A,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:A,status:"unknown",issues:[]};let V=[...q.uid!==process.getuid()?["ownership"]:[],...(q.mode&18)!==0?["permissions"]:[]];return{kind:P,path:A,status:V.length>0?"unsafe":"safe",issues:V}}catch(q){if(typeof q==="object"&&q!==null&&"code"in q&&q.code==="ENOENT")return{kind:P,path:A,status:"not-applicable",issues:[]};return{kind:P,path:A,status:"unknown",issues:[]}}}function Rs(P,A){let q=D(P);return{directories:[go("policy",mo(mo(A))),go("config",mo(A)),...q?[go("audit",q)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as fp}from"node:child_process";import{existsSync as Ps}from"node:fs";import{delimiter as mp,extname as gp,join as hp}from"node:path";import{stripVTControlCharacters as Es}from"node:util";var As="2.4.
|
|
21
|
+
${P.findings.length} ${V}: ${q.join(", ")}.`;if(A.error>0)return dt.red(ee);if(A.warning>0)return dt.yellow(ee);return dt.blue(ee)}import{lstatSync as pp}from"node:fs";import{dirname as mo}from"node:path";function go(P,A){try{let q=pp(A);if(q.isSymbolicLink())return{kind:P,path:A,status:"unsafe",issues:["symlink"]};if(!q.isDirectory())return{kind:P,path:A,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:A,status:"unknown",issues:[]};let V=[...q.uid!==process.getuid()?["ownership"]:[],...(q.mode&18)!==0?["permissions"]:[]];return{kind:P,path:A,status:V.length>0?"unsafe":"safe",issues:V}}catch(q){if(typeof q==="object"&&q!==null&&"code"in q&&q.code==="ENOENT")return{kind:P,path:A,status:"not-applicable",issues:[]};return{kind:P,path:A,status:"unknown",issues:[]}}}function Rs(P,A){let q=D(P);return{directories:[go("policy",mo(mo(A))),go("config",mo(A)),...q?[go("audit",q)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as fp}from"node:child_process";import{existsSync as Ps}from"node:fs";import{delimiter as mp,extname as gp,join as hp}from"node:path";import{stripVTControlCharacters as Es}from"node:util";var As="2.4.15",yp=5000,vp="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function wt(){return As}function ho(P,A){let q=P[A];if(q)return q;let V=Object.keys(P).find((ee)=>ee.toLowerCase()===A.toLowerCase()&&!!P[ee]);return V?P[V]:q}function bp(P){return(ho(P,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((A)=>A.length>0)}function Lp(P,A){let q=gp(P)?[P]:[...bp(A).map((V)=>`${P}${V}`),P];if(P.includes("/")||P.includes("\\"))return q.find((V)=>Ps(V))??P;return(ho(A,"PATH")??"").split(mp).flatMap((V)=>q.map((ee)=>hp(V,ee))).find((V)=>Ps(V))??P}function Ds(P){if(!/[\s"&|<>^]/.test(P))return P;return`"${P.replace(/"/g,'""')}"`}function Qt(P,A){let[q,...V]=P,ee=A[vp]==="win32"?"win32":process.platform;if(!q||ee!=="win32")return{cmd:q??"",args:V};let ne=Lp(q,A);if(!/\.(?:bat|cmd)$/i.test(ne))return{cmd:ne,args:V};return{cmd:ho(A,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",Ds(ne),...V.map(Ds)].join(" ")]}}var hn=async(P,A=yp)=>{let q=await wp(P,{timeoutMs:A});if(q.code!==0)return null;return Es(q.stdout).trim()||Es(q.stderr).trim()||null};function wp(P,A){let[q,...V]=P;if(!q)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((ee)=>{try{let ne=Qt([q,...V],process.env),ce=fp(ne.cmd,ne.args,{stdio:["ignore","pipe","pipe"]}),he=!1,ye="",be="";ce.stdout.on("data",(Xe)=>{ye+=Xe.toString()}),ce.stderr.on("data",(Xe)=>{be+=Xe.toString()});let xe=(Xe)=>{if(he)return;he=!0,clearTimeout(Ie),ee(Xe)},Ie=setTimeout(()=>{ce.kill(),xe({code:null,stdout:ye,stderr:be})},A.timeoutMs);ce.on("close",(Xe)=>{xe({code:Xe,stdout:ye,stderr:be})}),ce.on("error",()=>{xe({code:null,stdout:ye,stderr:be})})}catch{ee({code:null,stdout:"",stderr:""})}})}function ur(P){if(!P)return null;let A=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(P);if(A)return A[1]??null;let q=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(P);if(q)return q[1]??null;return P.split(`
|
|
22
22
|
`)[0]?.trim()||null}async function pr(P,A=hn,q=process.cwd()){let V=Promise.all($t.map(async(Ie)=>[Ie.id,ur(await A([...Ie.probeCommand]))])),[ee,ne,ce,he,ye,be,xe]=await Promise.all([V,V.then(async(Ie)=>{let Xe=Ie.find(([at])=>at==="opencode")?.[1];if(!Xe?.startsWith("2.")||!P(Xe))return null;let et=["--param",`location[directory]=${q}`],Re=["opencode","api","integration.list",...et];return await A(Re,30000),A(["opencode","api","plugin.list",...et],30000)}),A(["codex","plugin","list"],30000),A(["amp","plugins","list"],30000),A(["node","--version"]),A(["npm","--version"]),A(["bun","--version"])]);return{version:As,versions:Object.fromEntries(ee),codexPluginListOutput:ce,ampPluginListOutput:he,openCodePluginListOutput:ne,nodeVersion:ur(ye),npmVersion:ur(be),bunVersion:ur(xe),platform:`${process.platform} ${process.arch}`}}function yo(P,A){if(A==="dev")return!1;let q=P.split(".").map(Number),V=A.split(".").map(Number),[ee=0,ne=0,ce=0]=q,[he=0,ye=0,be=0]=V;if(ee!==he)return ee>he;if(ne!==ye)return ne>ye;return ce>be}async function Wt(){let P=wt(),A=new AbortController,q=setTimeout(()=>A.abort(),3000);try{let V=await fetch("https://registry.npmjs.org/cc-safety-net/latest",{signal:A.signal});if(!V.ok)return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:`npm registry returned ${V.status}`};let ee=await V.json(),ne=yo(ee.version,P);return{currentVersion:P,latestVersion:ee.version,updateAvailable:ne}}catch(V){return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:V instanceof Error?V.message:"Network error"}}finally{clearTimeout(q)}}import*as js from"node:readline";var $s=(P)=>`\x1B[${P}B`,kp=(P)=>`\x1B[${P}A`;var _s=["░","▒","▓","╱","╲","┃","━","┏","┓","┗","┛","╋"];function xp(P){return new Promise((A)=>setTimeout(A,P))}function Cp(P,A,q){if(!q)return A(P);if(q.aborted)return Promise.resolve();return new Promise((V,ee)=>{let ne=()=>q.removeEventListener("abort",ce),ce=()=>{ne(),V()};q.addEventListener("abort",ce,{once:!0}),A(P).then(()=>{ne(),V()},(he)=>{ne(),ee(he)})})}function fr(P){return Math.max(0,Math.min(1,P))}function yn(P){return Math.max(0,Math.min(255,Math.round(P)))}function vo(P){return P<=0.0031308?12.92*P:1.055*P**0.4166666666666667-0.055}function Sp(P,A,q){let V=q*Math.PI/180,ee=A*Math.cos(V),ne=A*Math.sin(V),ce=(P+0.3963377774*ee+0.2158037573*ne)**3,he=(P-0.1055613458*ee-0.0638541728*ne)**3,ye=(P-0.0894841775*ee-1.291485548*ne)**3;return{blue:yn(vo(fr(-0.0041960863*ce-0.7034186147*he+1.707614701*ye))*255),green:yn(vo(fr(-1.2684380046*ce+2.6097574011*he-0.3413193965*ye))*255),red:yn(vo(fr(4.0767416621*ce-3.3077115913*he+0.2309699292*ye))*255)}}function bo(P,A){let q=(A*P*180/Math.PI%360+360)%360;return Sp(0.72,0.15,q)}function Os(P,A=0.1){let q=bo(A,P);return`\x1B[38;2;${q.red};${q.green};${q.blue}m`}function Rp(P,A){return{blue:yn(P.blue+(255-P.blue)*A),green:yn(P.green+(255-P.green)*A),red:yn(P.red+(255-P.red)*A)}}function Fs(P,A,q){let V=Math.imul(P+2654435769,2246822507)^Math.imul(A+3266489909,668265263)^Math.imul(q+374761393,2654435761),ee=V^V>>>15,ne=Math.imul(ee,739982445),ce=ne^ne>>>12,he=Math.imul(ce,695872825);return((he^he>>>15)>>>0)/4294967296}function Pp(P,A,q){let V=Math.floor(Fs(P,A,q)*_s.length);return _s[V]??"░"}function Ts(P){let A=fr(P);return A*A*A*(A*(A*6-15)+10)}function Ep(P){if(P.length===0)return"";let A=[],q=!1,V="";for(let ee of P){let ne=`${ee.red};${ee.green};${ee.blue}`;if(ee.bold!==q)A.push(ee.bold?"\x1B[1m":"\x1B[22m"),q=ee.bold;if(ne!==V)A.push(`\x1B[38;2;${ne}m`),V=ne;A.push(ee.character)}return`${A.join("")}\x1B[22m\x1B[39m`}function Dp(P,A,q,V,ee){return P.map((ne,ce)=>({...bo(q,V+A+ce/ee),bold:!1,character:ne}))}function Ap(P,A,q,V,ee,ne,ce,he){let ye=Math.max(1,V*0.75),be=Math.min(1,q/ye),xe=ee*Ts(be),Ie=Math.max(0,(q-ye)/Math.max(1,V-ye)),Xe=(1-Ts(q/V))*he*2,et=0.35*Math.max(0,1-Ie*2),Re=be>=1,at=Math.min(P.length,Math.ceil(xe+2+1));return P.slice(0,at).map((ct,lt)=>{let ft=bo(ne,ce+A+lt/he+Xe),mt=lt+Fs(A,lt,7919)*2-1;if(mt>xe+2)return{...ft,bold:!1,character:" "};let gt=xe-mt,ut=0.8*Math.exp(-(gt*gt)/12.5),vt=Math.min(0.9,ut+et),At=!Re&&mt>xe-4;return{...Rp(ft,vt),bold:vt>0.3,character:At?Pp(A,lt,q):ct}})}function Is(P){return`\x1B[?2026h${P.map((A,q)=>`\x1B8${q>0?$s(q):""}${Ep(A)}`).join("")}\x1B[?2026l`}async function Lo(P,A={}){if(!P)return;let q=A.output??process.stdout,V=A.sleep??xp,ee=A.seed??0,ne=P.split(`
|
|
23
23
|
`).map((xe)=>Array.from(xe)),ce=Math.max(...ne.map((xe)=>xe.length)),he=12000*ne.filter((xe)=>xe.length>0).length/40,ye=ce>0?Math.max(1,Math.ceil(he/16.666666666666668)):0,be=ye>0?he/ye:0;q.write(`\x1B[?25l${ne.length>1?`${`
|
|
24
24
|
`.repeat(ne.length-1)}${kp(ne.length-1)}`:""}\x1B7`);try{for(let xe=1;xe<=ye;xe+=1){if(A.signal?.aborted)break;q.write(Is(ne.map((Ie,Xe)=>Ap(Ie,Xe,xe,ye,ce,0.1,ee,3)))),await Cp(be,V,A.signal)}}finally{if(q.write(Is(ne.map((xe,Ie)=>Dp(xe,Ie,0.1,ee,3)))),q.write("\x1B8"),ne.length>1)q.write($s(ne.length-1));q.write(`
|
|
@@ -220,7 +220,7 @@ ${he()}`.trim())))}),ne.on("close",(xe)=>{if(clearTimeout(be),xe!==0){V(Error(Ho
|
|
|
220
220
|
`));return{path:ee[0]??q??Mt(Wo(P),Jo[0]),alreadyInstalled:ee.length>0}}function kn(P){let A=[];for(let q of P.openCodeVersion?.startsWith("2.")?Ko(P.environment):Yo(P.environment))if(Nf(q))try{let V=jf(q,"utf-8"),ee=_t(V),ne=JSON.parse(ee);if(Qo(ne)){let ce=Xo(P.openCodePluginListOutput);if(ce)return{platform:"opencode",status:"disabled",method:"opencode api plugin.list",configPath:q,errors:[...A,ce]};return{platform:"opencode",status:"configured",method:"plugin array",configPath:q,errors:A.length>0?A:void 0}}}catch(V){A.push(`Failed to parse ${Mf(q)}: ${V instanceof Error?V.message:String(V)}`)}return{platform:"opencode",status:"n/a",errors:A.length>0?A:void 0}}import{join as yl}from"node:path";function ei(P){let A=P.env.get("PI_CODING_AGENT_DIR");return yl(A?en(A,P.home):yl(P.home,".pi","agent"),"settings.json")}function ti(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function vl(P){let A=ei(P.environment),q=Ft(A);if(q.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(q.kind==="missing")return{platform:"pi",status:"n/a"};let V=pt(q.value,"packages");if(!Array.isArray(V))return{platform:"pi",status:"n/a"};let ee=V.find((he)=>ti(typeof he==="string"?he:pt(he,"source")));if(ee===void 0)return{platform:"pi",status:"n/a"};let ne=pt(ee,"extensions");if(Array.isArray(ne)&&ne.some((he)=>typeof he==="string"&&he.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:A,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:A}}var Hf={amp:Ks,"antigravity-cli":Ys,"claude-code":Qs,codex:ta,"copilot-cli":ha,cursor:Ca,"gemini-cli":Sa,"grok-build":Oa,"hermes-agent":Ja,"kimi-code":Mn,openclaw:al,opencode:kn,pi:vl};function xn(P,A,q){let V={...q,cwd:A,environment:P};return rr.map((ee)=>Uf(Hf[ee](V)))}function Uf(P){if(P.status==="not-inspected")return{platform:P.platform,detected:!1,configured:!1,inspectionStatus:"not-inspected"};return{platform:P.platform,detected:P.status!=="n/a",configured:P.status==="configured",inspectionStatus:P.status!=="n/a"?"verified":P.errors&&P.errors.length>0?"failed":"not-applicable",method:P.method,configPath:P.configPath,configPaths:P.configPaths,errors:P.errors}}import{join as Gf}from"node:path";var Bf=Object.freeze([{command:"git reset --hard",description:"git reset --hard",expectBlocked:!0},{command:"rm -rf /",description:"rm -rf /",expectBlocked:!0},{command:"rm -rf ./node_modules",description:"rm in cwd (safe)",expectBlocked:!1}]),qf=Object.freeze({state:"ready",diagnostics:Object.freeze([]),ruleMetadata:Object.freeze({}),policy:Object.freeze({rules:Object.freeze([]),transparentWrappers:Object.freeze([]),safety:Object.freeze({}),worktreeMode:!1,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:Object.freeze({}),destructiveCommandAllowPaths:Object.freeze([]),secretProtection:Object.freeze({enabled:!0,disabledRules:Object.freeze([]),denyPaths:Object.freeze([]),allowPaths:Object.freeze([])})})}),Vf={strict:!1,paranoidRm:!1,paranoidInterpreters:!1,worktreeMode:!1,effectiveLevel:"standard",capabilities:{fail_closed:{enabled:!1,source:"preset",sources:[]},paranoid_rm:{enabled:!1,source:"preset",sources:[]},paranoid_interpreters:{enabled:!1,source:"preset",sources:[]}}};function bl(P){let A=Gf(P.tmpdir,"cc-safety-net-self-test"),q=Bf.map((V)=>{let ee=N(P,d("self-test",{command:V.command},{kind:"command",shell:"auto"},{configCwd:A,executionCwd:A},V.command),{guard:{dependencies:{loadPolicySnapshot:()=>qf,getModes:()=>Vf,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),ne=V.expectBlocked?"blocked":"allowed",ce=ee.decision.kind==="deny"?"blocked":"allowed";return{command:V.command,description:V.description,expected:ne,actual:ce,passed:ne===ce,reason:ee.decision.kind==="deny"?ee.decision.reason:void 0,ruleId:ee.decision.kind==="deny"?ee.decision.ruleId:void 0}});return{passed:q.filter((V)=>V.passed).length,failed:q.filter((V)=>!V.passed).length,total:q.length,results:q}}function ni(P){let A=xt({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Ut(A.errors))return null;return{json:A.flags.json,skipUpdateCheck:A.flags.skipUpdateCheck}}async function Ll(P,A={}){let q=await An(!A.json,()=>{let V=zf(P,A);return{ready:V,finish:()=>V}},()=>Dn(),{loadingMessage:"Checking system status…"});if(A.json)console.log(JSON.stringify(q,null,2));else Jf(q);return q.engineSelfTest.failed>0||q.findings.some((V)=>V.severity==="error")?1:0}async function zf(P,A){let q=A.cwd??process.cwd(),V=await pr((at)=>kn({environment:P,cwd:q,openCodeVersion:at}).status!=="n/a",void 0,q),ee=xn(P,q,{ampPluginListOutput:V.ampPluginListOutput,codexPluginListOutput:V.codexPluginListOutput,copilotCliVersion:V.versions["copilot-cli"],openCodeVersion:V.versions.opencode,openCodePluginListOutput:V.openCodePluginListOutput}),ne=us(P,q),ce=ps(P),he=E(P,{cwd:q}),ye=he.policy,be=R(ye,P.env),xe=H(ye,be.capabilities),Ie=ar(P,7),Xe=qs(P,q),et=A.skipUpdateCheck?{currentVersion:wt(),latestVersion:null,updateAvailable:!1}:await Wt(),Re={hooks:ee,engineSelfTest:bl(P),userConfig:ne.userConfig,projectConfig:ne.projectConfig,configState:Ne(he),effectiveRules:ne.effectiveRules,environment:ce,effectiveSafety:{selectedPreset:ye.safety.level??"standard",level:be.effectiveLevel,capabilities:be.capabilities,ruleOverrides:ye.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(xe).filter(([,at])=>at.source==="rule_override"&&at.override==="off"&&at.inheritedEnabled&&at.changesInherited).map(([at])=>at),ruleCounts:{stored:Object.keys(ye.destructiveCommandRuleOverrides).length,effective:Object.values(xe).filter((at)=>at.changesInherited).length},...he.policyScopes?{policyScopes:he.policyScopes}:{}},...Xe.length>0?{v2Leftovers:Xe}:{},posture:Rs(P,ne.userConfig.path),activity:Ie,update:et,system:V};return{...Re,findings:ms(Re)}}function Jf(P){console.log(),console.log(hs(P.hooks)),console.log(),console.log(ys(P.engineSelfTest)),console.log(),console.log(vs(P)),console.log(),console.log(bs(P.environment)),console.log(),console.log(Ls(P)),console.log(),console.log(ws(P.findings)),console.log(),console.log(ks(P.activity)),console.log(),console.log(Cs(P.system)),console.log(),console.log(xs(P.update)),console.log(Ss(P))}import{existsSync as Wf}from"node:fs";var Kf=/^[A-Za-z0-9_@%+=:,./-]+$/,wl="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function ri(P){let A=xt({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Ut(A.errors))return console.error(wl),console.error("Pass -- before a command that starts with dashes."),null;if(A.values.cwd!==void 0&&!Wf(A.values.cwd))return console.error(`Error: --cwd path does not exist: ${A.values.cwd}`),null;let q=A.positionals.length===1?A.positionals[0]:A.positionals.map((V)=>Kf.test(V)?V:`'${V.replaceAll("'","'\\''")}'`).join(" ");if(!q)return console.error("Error: No command provided"),console.error(wl),null;return{json:A.flags.json,cwd:A.values.cwd,command:q}}function kl(P){if(P)return{dh:"=",dv:"|",dtl:"+",dtr:"+",dbl:"+",dbr:"+",h:"-",v:"|",tl:"+",tr:"+",bl:"+",br:"+",sh:"="};return{dh:"═",dv:"║",dtl:"╔",dtr:"╗",dbl:"╚",dbr:"╝",h:"─",v:"│",tl:"┌",tr:"┐",bl:"└",br:"┘",sh:"━"}}function xl(P,A){let V=A-18;return[`${P.dtl}${P.dh.repeat(A)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(V)}${P.dv}`,`${P.dbl}${P.dh.repeat(A)}${P.dbr}`]}function oi(P){return JSON.stringify(P)}function Cl(P,A=0){return`[${P.map((V,ee)=>gs(V,ee,A)).join(",")}]`}function Bn(P,A,q=70){let V=P.split(" "),ee=[],ne="";for(let ce of V)if(ne&&ne.length+ce.length+1>q)ee.push(ne),ne=ce;else ne=ne?`${ne} ${ce}`:ce;if(ne)ee.push(ne);return ee.map((ce,he)=>he===0?ce:`${A}${ce}`)}function Sl(P,A,q){let V=[];switch(P.type){case"parse":return null;case"env-strip":return V.push(""),V.push(`STEP ${A} ${q.h} Strip environment variables`),V.push(` Removed: ${P.envVars.map((ee)=>`${ee}=<redacted>`).join(", ")}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"leading-tokens-stripped":return V.push(""),V.push(`STEP ${A} ${q.h} Strip wrappers`),V.push(` Removed: ${P.removed.join(", ")}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"shell-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Detect shell wrapper`),V.push(` Wrapper: ${P.wrapper} -c`),V.push(` Inner: ${P.innerCommand}`),{lines:V,incrementStep:!0};case"interpreter":{if(V.push(""),V.push(`STEP ${A} ${q.h} Detect interpreter`),V.push(` Interpreter: ${P.interpreter}`),V.push(` Code: ${P.codeArg}`),P.paranoidBlocked)V.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:V,incrementStep:!0}}case"busybox":return V.push(""),V.push(`STEP ${A} ${q.h} Busybox wrapper`),V.push(` Subcommand: ${P.subcommand}`),{lines:V,incrementStep:!0};case"transparent-wrapper":return V.push(""),V.push(`STEP ${A} ${q.h} Transparent wrapper`),V.push(` Wrapper: ${P.wrapper}`),V.push(` Tokens: ${oi(P.output)}`),{lines:V,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(V.push(""),V.push(`STEP ${A} ${q.h} Match rules`),V.push(` Rule: ${P.rule}()`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}case"worktree-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Worktree relaxation`),V.push(` Mode: ${o.worktree.name}`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed local discard in linked worktree"),{lines:V,incrementStep:!0};case"temp-root-relaxation":return V.push(""),V.push(`STEP ${A} ${q.h} Temp-root relaxation`),V.push(` Git cwd: ${P.gitCwd}`),V.push(" Result: Allowed git discard in a temp-root repository"),{lines:V,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return V.push(""),V.push(`STEP ${A} ${q.h} Fallback scan`),V.push(` Found: ${P.embeddedCommandFound}`),{lines:V,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(V.push(""),V.push(`STEP ${A} ${q.h} Custom rules`),P.matched)V.push(" Result: MATCHED");else V.push(" Result: No match");return{lines:V,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return V.push(""),V.push(`STEP ${A} ${q.h} Dangerous text check`),V.push(` Token: ${P.token}`),V.push(" Result: MATCHED"),{lines:V,incrementStep:!0};return null}case"strict-unparseable":return V.push(""),V.push(`STEP ${A} ${q.h} Strict mode check`),V.push(` Command: ${P.rawCommand}`),V.push(" Result: ✗ UNPARSEABLE"),{lines:V,incrementStep:!0};case"segment-skipped":return null;case"error":return V.push(""),V.push(`ERROR: ${P.message}`),{lines:V,incrementStep:!1};default:return P}}function ii(P,A){let q=kl(A?.asciiOnly??!1),V=58,ee=[],ne=1;ee.push(...xl(q,58)),ee.push("");let ce=P.trace.steps.find((Re)=>Re.type==="error");if(ce&&ce.type==="error"){ee.push("ERROR"),ee.push(` ${ce.message}`),ee.push(""),ee.push("RESULT"),ee.push(` Status: ${P.result==="blocked"?dt.red("BLOCKED"):dt.green("ALLOWED")}`),ee.push(""),ee.push("CONFIG");let Re=P.configSource??"none";return ee.push(` Path: ${Re}`),ee.join(`
|
|
221
221
|
`)}let he=P.trace.steps.find((Re)=>Re.type==="parse");if(he&&he.type==="parse"){ee.push("INPUT"),ee.push(` ${he.input}`),ee.push(""),ee.push(`STEP ${ne} ${q.h} Split shell commands`),ne++;for(let Re=0;Re<he.segments.length;Re++){let at=he.segments[Re];if(at){let ct=Math.random();ee.push(` Segment ${Re+1}: ${Cl(at,ct)}`)}}}let ye=P.trace.segments,be=ye.length>1;for(let Re of ye){if(be){ee.push("");let ft="";if(he&&he.type==="parse"){let io=he.segments[Re.index];if(io)ft=io.join(" ")}let mt=54,gt=ft,ut=` Segment ${Re.index+1}: `,vt=" ";if(ft){if(ut.length+ft.length+vt.length>mt){let Xd=mt-ut.length-vt.length;gt=`${ft.substring(0,Xd-1)}…`}}let At=ft?`${ut}${gt}${vt}`:` Segment ${Re.index+1} `,Kd=ft?`${ut}${dt.cyan(gt)}${vt}`:At,Hi=58-At.length,Ui=Math.floor(Hi/2),Yd=Hi-Ui;ee.push(`${q.sh.repeat(Ui)}${Kd}${q.sh.repeat(Yd)}`)}if(Re.steps.find((ft)=>ft.type==="segment-skipped")){ee.push(""),ee.push(" (skipped — prior segment blocked)");continue}let ct=!1,lt=!1;for(let ft of Re.steps){let mt=Sl(ft,ne,q);if(mt){if(lt=!0,ft.type==="recurse"){ee.push("");let gt=" RECURSING ",ut=58-gt.length-4;ee.push(` ${q.tl}${q.h}${gt}${q.h.repeat(ut)}`),ee.push(` ${q.v}`),ct=!0;continue}for(let gt of mt.lines)if(ct)ee.push(` ${q.v} ${gt}`);else ee.push(gt);if(mt.incrementStep)ne++}}if(ct)ee.push(` ${q.v}`),ee.push(` ${q.bl}${q.h.repeat(56)}`);if(!lt)ee.push(""),ee.push(` ${dt.green("✓")} Allowed (no matching rules)`)}if(ee.push(""),ee.push("RESULT"),P.result==="blocked"){if(ee.push(` Status: ${dt.red("BLOCKED")}`),P.customRule){if(ee.push(` Rule: ${P.customRule.id}`),P.customRule.rulebook)ee.push(` Rulebook: ${P.customRule.rulebook.name} ${P.customRule.rulebook.version}`);if(P.customRule.source)ee.push(` Source: ${P.customRule.source}`);if(P.customRule.override)ee.push(` Override: reason ${P.customRule.override.reason}`)}if(P.reason){let Re=Bn(P.reason," ");ee.push(` Reason: ${Re[0]}`);for(let at=1;at<Re.length;at++)ee.push(Re[at]??"")}}else ee.push(` Status: ${dt.green("ALLOWED")}`);ee.push(""),ee.push("CONFIG");let xe=P.configSource??"none",Ie=P.configValid?"":" (invalid)";ee.push(` Path: ${xe}${Ie}`);let Xe=P.safetyPresetScope;ee.push(` Safety preset: ${P.selectedPreset??"standard"}${Xe?` (${cr(Xe)})`:""}`),ee.push(` Effective capabilities: ${P.effectiveLevel}`);let et=Object.entries(P.destructiveCommandRuleOverrides??{});if(ee.push(` Rule customizations: ${et.length}`),P.ruleActivation)ee.push(` Rule activation: ${P.ruleActivation.id} — ${P.ruleActivation.enabled?"on":"off"} via ${P.ruleActivation.source}`);return ee.join(`
|
|
222
222
|
`)}function si(P){return JSON.stringify(P,null,2)}import{resolve as em}from"node:path";var Yf=["AKIA","ASIA","ghp_","gho_","ghu_","ghs_","ghr_","github_pat_","glpat-","xox","npm_","pypi-","rk_","sk-","sk_","gsk_","xai-","pplx-","bastn_","tgp_v1_","flp_","wfr_","fw_","fwp_","tp-","psk-"];function Rl(P){let A=0,q={allocateSegment(){return A++},getNextSegmentIndex(){return A},recordGlobal(V){P.record({kind:"step",scope:"global",step:V})},recordSegment(V,ee=q.currentSegmentIndex){if(ee===void 0)return;P.record({kind:"step",scope:"segment",segmentIndex:ee,step:V})}};return q}function Pl(P={}){let A=[],q=P.maxEvents??512,V={maxTextLength:P.maxTextLength??2048,maxListLength:P.maxListLength??128,maxObjectProperties:P.maxObjectProperties??P.maxListLength??128,maxDepth:P.maxDepth??16},ee,ne=new Set;return{record(ce){if(ee)return;if(!ce||A.length>=q)return;try{A.push(ci(Xf(ce,V,ne)))}catch{}},finish(){if(ee)return ee;return ee=ci({events:Object.freeze(A)}),ee}}}function Xf(P,A,q){if(P.kind!=="step")throw TypeError("invalid trace event");let{scope:V,step:ee}=P;Or(ee,q,A);let ne=ai(ee,A,q);if(V==="global")return{kind:"step",scope:"global",step:ne};if(V!=="segment")throw TypeError("invalid trace event scope");return{kind:"step",scope:"segment",segmentIndex:P.segmentIndex,step:ne}}function Or(P,A,q,V=0,ee=new WeakSet){if(typeof P==="string"){let he=P.slice(0,q.maxTextLength);if(!Ge(he))return;for(let ye of Ze(he))for(let be of ye.match(/[^\s"'()$]+/g)??[])A.add(El(be));return}if(!P||typeof P!=="object"||V>=q.maxDepth||ee.has(P))return;if(ee.add(P),Array.isArray(P)){let he=Math.min(P.length,q.maxListLength);for(let ye=0;ye<he;ye++)Or(P[ye],A,q,V+1,ee);return}let ne=0,ce=new Set;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ne>=q.maxObjectProperties)break;ne++,Or(he,A,q);let ye=li(he,q,A);if(ce.has(ye))continue;ce.add(ye),Or(P[he],A,q,V+1,ee)}}function ai(P,A,q,V=0,ee=new WeakSet){if(typeof P==="string")return li(P,A,q);if(!P||typeof P!=="object")return P;if(V>=A.maxDepth)return;if(ee.has(P))return;if(ee.add(P),Array.isArray(P)){let he=[],ye=Math.min(P.length,A.maxListLength);for(let be=0;be<ye;be++)he.push(ai(P[be],A,q,V+1,ee));return he}let ne={},ce=0;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ce>=A.maxObjectProperties)break;ce++;let ye=li(he,A,q);if(Object.hasOwn(ne,ye))continue;Object.defineProperty(ne,ye,{value:ai(P[he],A,q,V+1,ee),enumerable:!0,configurable:!0,writable:!0})}return ne}function li(P,A,q){let V=P.slice(0,A.maxTextLength),ee=Ge(V)?Ue(V):V,ne=q.size>0?Qf(ee,q):ee;return(Zf(ne)?ke(ne):ne).slice(0,A.maxTextLength)}function Zf(P){return P.includes("PRIVATE KEY")||P.includes("://")||P.includes("eyJ")||P.includes(":")&&/(?:authorization|cookie|x-api-key|api-key|(?:^|\s)(?:-u|--user)(?:\s|=))/i.test(P)||P.length>=14&&Yf.some((A)=>P.includes(A))||P.length>=49&&/\b[a-f0-9]{32}\.[A-Za-z0-9]{16}\b/.test(P)}function Qf(P,A){return P.replace(/[^\s"'()$]+/g,(q)=>A.has(El(q))?"<redacted>":q)}function El(P){let A=2166136261,q=2166136261;for(let V=0;V<P.length;V++)A=Math.imul(A^P.charCodeAt(V),16777619),q=Math.imul(q^P.charCodeAt(P.length-V-1),16777619);return`${A>>>0}:${q>>>0}:${P.length}`}function ci(P){if(P&&typeof P==="object"&&!Object.isFrozen(P)){for(let A of Object.values(P))ci(A);Object.freeze(P)}return P}function qn(P,A={},q){let V=em(A.cwd??process.cwd()),ee=A.policySnapshot??E(q,{cwd:V,userConfigDir:A.userConfigDir}),ne=R(ee.policy,q.env),ce=Fe({policySnapshot:ee,effectiveCapabilities:ne.capabilities,strict:ne.strict,paranoidRm:ne.paranoidRm,paranoidInterpreters:ne.paranoidInterpreters,worktreeMode:ne.worktreeMode}),he={effectiveLevel:ce.effectiveLevel,selectedPreset:ee.policy.safety.level??"standard",...ee.policyScopes?{safetyPresetScope:ee.policyScopes.levelScope}:{},effectiveCapabilities:ce.effectiveCapabilities,destructiveCommandRuleOverrides:ee.policy.destructiveCommandRuleOverrides},{configSource:ye,configValid:be}=nm(q,{cwd:V,userConfigDir:A.userConfigDir});if(!P||!P.trim())return{trace:{steps:[{type:"error",message:"No command provided"}],segments:[]},result:"allowed",configSource:ye,configValid:be,...he};let xe=h(P,"auto");if(xe.status==="limited")throw new y;let Ie=xe.dialect==="powershell"?h(P,"posix"):xe,Xe=st(Ie),et=Pl(),Re=Rl(et);Re.recordGlobal({type:"parse",input:P,segments:Xe.map((At)=>[...At])});let at=d("Bash",{command:P},{kind:"command",shell:"auto"},{configCwd:V,executionCwd:V},P),ct=B(at,{environment:q,trace:Re,dependencies:{loadPolicySnapshot:()=>ee}}),lt=ct.decision.kind==="deny"?ct.decision:null;if(lt&&(ct.stage==="policy-protection"||ct.stage==="secret-protection")){let At=tm(lt);return{trace:{steps:[],segments:[{index:0,steps:[{type:"rule-check",rule:At.rule,matched:!0,reason:lt.reason}]}]},result:"blocked",reason:k(lt.reason),segment:k(Dl(lt,P)),...At.ruleId?{ruleId:k(At.ruleId)}:{},configSource:ye,configValid:be,...he}}let ft=Re.getNextSegmentIndex();if(lt&&ft>0&&ft<Xe.length)Re.recordSegment({type:"segment-skipped",index:ft,reason:"prior-segment-blocked"},ft);let mt=et.finish(),gt=lt?.ruleId??rm(at,ee,ne,q),ut=G.find((At)=>At.id===gt&&At.activationCapability),vt=ut?ce.policy.effectiveDestructiveCommandRules[ut.id]:void 0;return{trace:im(mt),result:lt?"blocked":"allowed",reason:lt?k(lt.reason):void 0,segment:lt?k(Dl(lt,P)):void 0,ruleId:lt?.ruleId?k(lt.ruleId):void 0,customRule:om(sm(lt?.ruleId,ee)),configSource:ye,configValid:be,...he,...ut&&vt?{ruleActivation:{id:ut.id,...vt}}:{}}}function Dl(P,A){return P.evidence?.segment??A}function tm(P){if(P.reason===$e)return{ruleId:"policy-protection",rule:"policy-protection:findPolicyConfigMutationTargetInSemanticFacts"};if(P.reason===Me)return{ruleId:"policy-apply-protection",rule:"policy-apply-protection:findPolicyApplyInvocationInSemanticFacts"};if(P.reason===x)return{ruleId:"git-metadata-protection",rule:"git-metadata-protection:findGitMetadataMutationTargetInSemanticFacts"};return{ruleId:P.ruleId,rule:"secret-protection:findSensitiveTargetInSemanticFacts"}}function nm(P,A){let q=M(A.cwd),V=j(P,A),ee=J(P,{cwd:A.cwd,userConfigDir:A.userConfigDir});try{if(n(ee.projectConfigTarget)!==null){if(Jt(ee.projectConfigTarget).errors.length===0)return{configSource:q,configValid:!0};return{configSource:q,configValid:!1}}}catch(ne){if(ne instanceof r)return{configSource:q,configValid:!1};throw ne}try{if(n(ee.userConfigTarget)!==null){let ne=Jt(ee.userConfigTarget);return{configSource:V,configValid:ne.errors.length===0}}return{configSource:null,configValid:!0}}catch(ne){if(ne instanceof r)return{configSource:V,configValid:!1};throw ne}}function rm(P,A,q,V){let ee=A.policy,ne=Ee({...ee,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:{...ee.destructiveCommandRuleOverrides,...Object.fromEntries(G.flatMap((he)=>he.activationCapability?[[he.id,"on"]]:[]))}},A.state==="degraded"?{diagnostics:A.diagnostics,reason:A.reason}:void 0),ce=B(P,{environment:V,dependencies:{loadPolicySnapshot:()=>ne,getModes:()=>({...q,strict:!0,paranoidRm:!0,paranoidInterpreters:!0}),findSensitiveTarget:()=>null}});return ce.decision.kind==="deny"?ce.decision.ruleId:void 0}function om(P){if(!P)return;return{id:k(P.id),...P.rulebook?{rulebook:{name:k(P.rulebook.name),version:k(P.rulebook.version)}}:{},...P.source?{source:k(P.source)}:{},...P.override?{override:{type:"reason",reason:k(P.override.reason)}}:{}}}function im(P){let A=P.events.flatMap((V)=>V.kind==="step"&&V.scope==="global"?[V.step]:[]),q=new Map;for(let V of P.events){if(V.kind!=="step"||V.scope!=="segment")continue;let ee=q.get(V.segmentIndex)??{index:V.segmentIndex,steps:[]};ee.steps.push(V.step),q.set(V.segmentIndex,ee)}return{steps:A,segments:[...q.values()]}}function sm(P,A){let q=P?.replace(/^custom\./,"");if(!q||!A.policy.rules.some((V)=>V.name===q))return;return A.ruleMetadata[q]??Object.freeze({id:q})}function Al(P){return new Promise((A)=>{process.stdout.write(`${P}
|
|
223
|
-
`,()=>A())})}async function _l(P,A){let q=ri(A);if(!q)return 1;try{let V=qn(q.command,{cwd:q.cwd},P),ee=!!process.env.NO_COLOR||!process.stdout.isTTY;return await Al(q.json?si(V):ii(V,{asciiOnly:ee})),0}catch(V){let ee=am(V instanceof p?V.cause:V);if(ee===void 0)throw V;if(q.json)return await Al(JSON.stringify({error:ee})),1;return console.error(ee),1}}function am(P){if(P instanceof y)return P.message;if(P instanceof f)return P.message;if(P instanceof s&&a[P.kind].errorCode==="path-canonicalization-limit")return"Path canonicalization work limit exceeded.";return}var Tl="2.4.
|
|
223
|
+
`,()=>A())})}async function _l(P,A){let q=ri(A);if(!q)return 1;try{let V=qn(q.command,{cwd:q.cwd},P),ee=!!process.env.NO_COLOR||!process.stdout.isTTY;return await Al(q.json?si(V):ii(V,{asciiOnly:ee})),0}catch(V){let ee=am(V instanceof p?V.cause:V);if(ee===void 0)throw V;if(q.json)return await Al(JSON.stringify({error:ee})),1;return console.error(ee),1}}function am(P){if(P instanceof y)return P.message;if(P instanceof f)return P.message;if(P instanceof s&&a[P.kind].errorCode==="path-canonicalization-limit")return"Path canonicalization work limit exceeded.";return}var Tl="2.4.15",Ot=" ",sn="cc-safety-net";function Il(P){return P.argument?`${P.flags} ${P.argument}`:P.flags}function lm(P){return Math.max(...P.map((A)=>Il(A).length))}function cm(P){return Math.max(...P.map((A)=>A.usage.length))}function dm(P){return Math.max(...P.map((A)=>`${sn} ${A.usage}`.length))}function um(P,A){let q=`${sn} ${P.usage}`;return`${Ot}${q.padEnd(A+2)}${P.description}`}function qt(P,A){return`${Ot}${P.padEnd(Math.max(40,P.length+2))}${A}`}function Cn(P,A=console.log){let q=[];if(q.push(`${sn} ${P.name}`),q.push(""),q.push(`${Ot}${P.description}`),q.push(""),q.push("USAGE:"),q.push(`${Ot}${sn} ${P.usage}`),q.push(""),P.subcommands&&P.subcommands.length>0){q.push("SUBCOMMANDS:");let V=cm(P.subcommands);for(let ee of P.subcommands)q.push(`${Ot}${ee.usage.padEnd(V+2)}${ee.description}`);q.push("")}if(P.options.length>0){q.push("OPTIONS:");let V=lm(P.options);for(let ee of P.options){let ne=Il(ee),ce=ee.default?`${ee.description} (default: ${ee.default})`:ee.description;q.push(`${Ot}${ne.padEnd(V+2)}${ce}`)}q.push("")}if(P.examples&&P.examples.length>0){q.push("EXAMPLES:");for(let V of P.examples)q.push(`${Ot}${V}`)}A(q.join(`
|
|
224
224
|
`))}function di(){let P=dm(ir),A=[];A.push(`${sn} v${Tl}`),A.push(""),A.push("Blocks destructive commands and secret access."),A.push(""),A.push("COMMANDS:");for(let q of ir)A.push(um(q,P));A.push(""),A.push("GLOBAL OPTIONS:"),A.push(`${Ot}-h, --help Show help (use with command for command-specific help)`),A.push(`${Ot}-V, --version Show version`),A.push(""),A.push("HELP:"),A.push(`${Ot}${sn} help <command> Show help for a specific command`),A.push(`${Ot}${sn} <command> --help Show help for a specific command`),A.push(""),A.push("ENVIRONMENT VARIABLES:"),A.push(qt(`${o.level.name}=standard|strict|paranoid`,"Set session safety level")),A.push(qt(`${o.worktree.name}=1`,"Allow local git discards in linked worktrees")),A.push(qt(`${o.debug.name}=1`,"Print diagnostic messages to stderr")),A.push(qt(`${o.auditScope.name}=all|blocked`,"Record all command decisions, or denials only")),A.push(qt("CC_SAFETY_NET_HOME","Override rule config home directory")),A.push(""),A.push("LEGACY ENVIRONMENT VARIABLES (STILL SUPPORTED):"),A.push(qt(`${o.strict.name}=1`,"Force safety.overrides.fail_closed on")),A.push(qt(`${o.paranoid.name}=1`,"Force paranoid_rm and paranoid_interpreters on")),A.push(qt(`${o.paranoidRm.name}=1`,"Force safety.overrides.paranoid_rm on")),A.push(qt(`${o.paranoidInterpreters.name}=1`,"Force safety.overrides.paranoid_interpreters on")),A.push(""),A.push("Documentation: https://ccsafetynet.com/docs"),console.log(A.join(`
|
|
225
225
|
`))}function $l(){console.log(Tl)}function Vn(P,A=console.log){let q=sr(P);if(!q)return!1;if(q.name.toLowerCase()!==P.toLowerCase())return!1;return Cn(q,A),!0}import{existsSync as Br,readFileSync as Ac}from"node:fs";import{join as Gr}from"node:path";import*as Yt from"node:readline";function pm(P){return P==="install"?"Install":"Uninstall"}function fm(P){return P==="install"?"Installing":"Uninstalling"}function mm(P){return P==="install"?"into":"from"}function Nl(P){return P?.available===!0}function gm(P,A){let q=new Set(A);return P.filter((V)=>q.has(V.target)).map((V)=>V.target)}function Ol(P,A,q){if(P.every((V)=>!V.available))return A;return Array.from({length:P.length},(V,ee)=>ee+1).map((V)=>(A+V*q+P.length)%P.length).find((V)=>Nl(P[V]))}function hm(P,A,q){if(q.ctrl&&q.name==="c")return"interrupt";if(q.name==="escape"||A==="q")return"abort";if(P==="install"&&(A==="u"||A==="U"))return"update";if(q.name==="up"||A==="k")return"up";if(q.name==="down"||A==="j")return"down";if(q.name==="space"||A===" ")return"toggle";if(q.name==="return"||q.name==="enter")return"confirm";return null}function ym(P){return{cursor:P.findIndex((A)=>A.available),selected:[]}}function vm(P,A,q){if(q==="confirm"||q==="update"||q==="abort"||q==="interrupt")return{state:P,done:q};if(q==="up")return{state:{...P,cursor:Ol(A,P.cursor,-1)}};if(q==="down")return{state:{...P,cursor:Ol(A,P.cursor,1)}};let V=A[P.cursor];if(!Nl(V))return{state:P};let ee=P.selected.includes(V.target)?P.selected.filter((ne)=>ne!==V.target):gm(A,[...P.selected,V.target]);return{state:{...P,selected:ee}}}var jl="◉",Ml="◯",Hl=">",Ul=" ";function bm(P,A,q,V={}){let ee=V.color!==!1,ne=ee?dt.dim:(ye)=>ye,ce=ee?dt.green:(ye)=>ye,he=ee?dt.bold:(ye)=>ye;return["",`${pm(P)} CC Safety Net ${mm(P)}:`,"",...A.map((ye,be)=>{let xe=q.selected.includes(ye.target),Ie=be===q.cursor,Xe=xe?jl:Ml,et=Ie?Hl:Ul,Re=ye.available?"":` (${ye.unavailableReason??"not installed"})`,at=`${Xe} ${ye.label}${Re}`,ct=!ye.available?ne(at):xe?ce(at):Ie?he(at):at;return`${et} ${ct}`}),"",P==="install"?"Space: select Enter: confirm u: update installed Up/Down: move q/Esc: cancel":A.some((ye)=>ye.available)?"Space: select Enter: confirm Up/Down: move q/Esc: cancel":`No selectable integrations found for ${P}. q/Esc: close`].join(`
|
|
226
226
|
`)}var Fl=["global-hook","plugin"];function Lm(P,A,q={}){let V=q.color!==!1?dt.bold:(ne)=>ne;return["","Install the Kimi Code integration as:","",...[`Global hook — ${A?"already installed; selecting it reports the current state":"write the hook into ~/.kimi-code/config.toml now"}`,"Native Kimi plugin — print the steps to run inside Kimi Code"].map((ne,ce)=>{let he=ce===P,ye=`${he?jl:Ml} ${ne}`;return`${he?Hl:Ul} ${he?V(ye):ye}`}),"","Enter: confirm Up/Down: move q/Esc: cancel"].join(`
|
|
@@ -269,7 +269,7 @@ Update available: cc-safety-net ${et.currentVersion} → ${et.latestVersion}. Up
|
|
|
269
269
|
Check file permissions for the target config file and parent directory.`;if(q==="ENOENT")return`${A}
|
|
270
270
|
Check that the target config path and parent directory exist.`;if(q==="ENOTDIR")return`${A}
|
|
271
271
|
Check that every parent path component is a directory.`;return A}import{mkdirSync as Sg}from"node:fs";import{dirname as Rg}from"node:path";import{createInterface as Pg}from"node:readline";import{existsSync as Mc,readFileSync as Lg}from"node:fs";function un(P,A){let q=Ye(P,A);return{policy:q.policy,errors:ie(qe(q.issues,Je,(V)=>V.kind==="custom")," "," ")}}function Yn(P,A){return un(P,A).errors}function jc(P,A){return{"safety.level":P.safety.level,...Ci("safety.overrides",P.safety.overrides),"workflow.worktree_mode":String(P.workflow.worktree_mode),"destructive_command_protection.enabled":String(P.destructive_command_protection.enabled),...Ci("destructive_command_protection.overrides",P.destructive_command_protection.overrides),"destructive_command_protection.allow_paths":Si(P.destructive_command_protection.allow_paths),"secret_protection.enabled":String(P.secret_protection.enabled),...Ci("secret_protection.overrides",P.secret_protection.overrides),"secret_protection.deny_paths":Si(P.secret_protection.deny_paths),"secret_protection.allow_paths":Si(P.secret_protection.allow_paths),...A?{"audit.retention_days":String(P.audit.retention_days)}:{}}}function qr(P,A,q){let V=jc(P,q),ee=jc(A,q);return[...new Set([...Object.keys(V),...Object.keys(ee)])].flatMap((ne)=>V[ne]===ee[ne]?[]:[{field:ne,before:V[ne],after:ee[ne]}])}function Xn(P,A){let q=u(P,A);if(!Mc(q))return{baseline:C(globalThis.__CC_SAFETY_NET_EMBEDDED_POLICY__,P.home),diagnostics:[]};let V=pn(q),ee=un(V.value,P.home);return{baseline:ee.policy,diagnostics:V.errors.length>0?V.errors:ee.errors}}function pn(P){if(!Mc(P))return{errors:[`${P}: file not found`]};try{return{value:JSON.parse(Lg(P,"utf-8")),errors:[]}}catch(A){let q=A instanceof Error?A.message:String(A);return{errors:[`${P}: ${A instanceof SyntaxError?`Invalid JSON: ${q}`:q}`]}}}function Vr(P,A){let q=wg(P)?P:{};return{version:A.version,...Object.fromEntries(["safety","workflow","destructive_command_protection","secret_protection"].filter((V)=>q[V]!==void 0).map((V)=>[V,q[V]]))}}function Ci(P,A){return Object.fromEntries(Object.entries(A).flatMap(([q,V])=>V===void 0?[]:[[`${P}.${q}`,String(V)]]))}function Si(P){return P.length===0?"(none)":P.join(", ")}function wg(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{chmodSync as kg,existsSync as Hc,mkdirSync as xg,readFileSync as Uc}from"node:fs";import{dirname as Cg}from"node:path";function Gc(P,A={}){let q=u(P,A);if(!Hc(q))return{path:q,exists:!1,raw:"",policy:W(),errors:[]};let V=Uc(q,"utf-8");if(!V.trim())return{path:q,exists:!0,raw:V,policy:W(),errors:["Config file is empty"]};try{let ee=un(JSON.parse(V),P.home);return{path:q,exists:!0,raw:V,policy:ee.policy,errors:ee.errors}}catch(ee){return{path:q,exists:!0,raw:V,policy:W(),errors:[`Invalid JSON: ${ee instanceof Error?ee.message:String(ee)}`]}}}function Vt(P,A,q={}){let V=u(P,q),ee=un(A,P.home);if(ee.errors.length>0)return{path:V,policy:W(),errors:ee.errors};let ne=ee.policy;return xg(Cg(V),{recursive:!0,mode:448}),g(re(V),`${JSON.stringify(ne,null,2)}
|
|
272
|
-
`,384),kg(V,384),{path:V,policy:ne,errors:[]}}function Bc(P,A){let q=un(A,P.home);if(q.errors.length>0)return{errors:q.errors};return{preview:Ae(q.policy,P.env),errors:[]}}function qc(P,A={}){let q=u(P,A);if(!Hc(q))return Vt(P,X,A);let V=Uc(q,"utf-8");if(!V.trim())return Vt(P,X,A);try{return Vt(P,C(JSON.parse(V),P.home),A)}catch{return Vt(P,X,A)}}var Vc=new Set(["check","apply"]),zc="(unset)";async function Wc(P,A,q={}){let V=xt({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},A),ee=V.positionals[0],ne=[...V.errors,...ee&&!Vc.has(ee)?[`Unknown policy subcommand: ${ee}`]:[],...ee&&Vc.has(ee)&&!V.positionals[1]?[`policy ${ee} requires a file`]:[],...V.positionals.slice(2).map((Re)=>`Unexpected policy argument: ${Re}`)];if(ne.length>0){for(let Re of ne)console.error(Re);return 1}let ce=V.positionals[1];if(!ee||!ce)return Cn(or,console.error),1;let he=V.flags.global?u(P):b(q.cwd??process.cwd()),ye=pn(ce),be=[...ye.errors,...Yn(ye.value,P.home).map((Re)=>`${ce}: ${Re}`),...!V.flags.global&&Ag(ye.value)&&ye.value.audit!==void 0?[`${ce}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(be.length>0){for(let Re of be)console.error(Re);return 1}let xe=C(ye.value,P.home);if(console.log(`Scope: ${V.flags.global?"user":"project"} (${he})`),console.log(`Proposal: ${ce}`),V.flags.global)Jc(C(pn(he).value,P.home),xe,!0);if(!V.flags.global){let Re=Xn(P).baseline;console.log("Effective policy (user + project merged):"),Jc(Z(Re,ae(pn(he).value,P.home).policy).policy,Z(Re,ae(ye.value,P.home).policy).policy,!1)}if(ee==="check")return 0;let Ie=q.input??process.stdin,Xe=q.output??process.stdout;if(!Ie.isTTY||!Xe.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${ce}${V.flags.global?" --global":""}`),1;if(!await Eg(`Apply this policy to ${he}? [y/N] `,Ie,Xe))return console.log("Cancelled; nothing was written."),0;return Dg(P,he,ye.value,xe,V.flags.global),console.log(`Policy applied: ${he}`),0}function Eg(P,A,q){let V=Pg({input:A,output:q,terminal:!1});return new Promise((ee)=>{V.once("close",()=>ee(!1)),V.question(P,(ne)=>{ee(/^y(es)?$/i.test(ne.trim())),V.close()})})}function Dg(P,A,q,V,ee){if(ee){Vt(P,V);return}Sg(Rg(A),{recursive:!0}),Pt(A,Vr(q,V))}function Jc(P,A,q){let V=qr(P,A,q);if(V.length===0){console.log("No changes.");return}console.log(`Changes (${V.length}):`);for(let ee of V)console.log(` ${ee.field}: ${ee.before??zc} -> ${ee.after??zc}`)}function Ag(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as Bh}from"node:path";var Kc="# Custom Rules Reference\n\nAgent reference for generating CC Safety Net rulebook configuration.\n\n## Config Locations\n\n| Scope | Config path | Rulebook path | Priority |\n|-------|-------------|---------------|----------|\n| User | `~/.cc-safety-net/rules/rule.json` | `~/.cc-safety-net/rules/<rulebook-name>/rulebook.json` | First |\n| Project | `.cc-safety-net/rules/rule.json` | `.cc-safety-net/rules/<rulebook-name>/rulebook.json` | Second |\n| GitHub source | Listed in a local `rule.json` | Vendored into the consumer's `<rulebook-name>/rulebook.json` by `rule add` | Source order |\n\nEvery rulebook is a live file: the runtime reads it on each tool call, so an edit applies to the next command with no publishing step.\n\nUser scope is evaluated before project scope; within a scope, sources apply in `rules` array order. A duplicate active rulebook name keeps the first claim and ignores the later rulebook with a warning, so a user-scoped name shadows a project-scoped one.\n\nUse `cc-safety-net rule init` to create an inert local config. Use `--global` for user scope. Use `cc-safety-net rule init --example` to also create an inactive example rulebook. `CC_SAFETY_NET_HOME` overrides the `~/.cc-safety-net` user root.\n\nLegacy inline `.safety-net.json` and `~/.cc-safety-net/config.json` files are not loaded at runtime. Convert them with `cc-safety-net rule migrate`.\n\n## rule.json Schema\n\n```json\n{\n \"version\": 1,\n \"rules\": [\"project-rules\", \"owner/repo#main/team-rules\"],\n \"overrides\": {\n \"project-rules/block-docker-system-prune\": {\n \"reason\": \"Use targeted Docker cleanup commands.\"\n },\n \"team-rules/block-npm-global\": \"off\"\n },\n \"transparent_wrappers\": [\"rtk\"]\n}\n```\n\n- `version`: Required. Must be `1`.\n- `$schema`: Optional. `cc-safety-net rule verify` inserts it into a valid `rule.json` that lacks it.\n- `rules`: Optional array of rulebook source strings. Missing `rules` is treated as `[]`.\n- `overrides`: Optional object keyed by `<rulebook-name>/<rule-name>`.\n- `overrides` values are either `\"off\"` to disable a rule or an object with a required `reason` (replacement block reason) and an optional `intent` (one of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain`).\n- A project override cannot target a user-scoped rule: only that override is ignored, the user rule keeps its configured state, and `rule verify` reports the diagnostic as a failure.\n- `transparent_wrappers`: Optional array of command names that transparently execute a visible child command.\n- Transparent wrappers have no built-in defaults. Configure only wrappers you intentionally trust, such as `\"rtk\"`.\n- Use `cc-safety-net rule wrapper add rtk` to configure RTK without manually editing `rule.json`.\n\n## Rulebook Sources\n\n- Local sources are bare rulebook names such as `project-rules`; the rulebook file is `.cc-safety-net/rules/project-rules/rulebook.json`.\n- Run `cc-safety-net rule add owner/repo` to add every rulebook currently present on the repository's default branch.\n- Use `--only` to select one or more rulebooks while preserving their order: `cc-safety-net rule add owner/repo --only aws gcloud`.\n- Use `--ref` to select a branch, tag, or commit instead of the default branch: `cc-safety-net rule add owner/repo --ref v2 --only aws`.\n- GitHub sources are stored in canonical form as `owner/repo#ref/<rulebook-name>`. That form remains valid in `rule.json` and as direct CLI input.\n- GitHub refs may contain `/`-separated path segments, such as `feature/rulebook-v2`.\n- The GitHub source name, the repository directory name, and the rulebook `name` must match exactly.\n- Rulebook source strings must be unique in a config.\n\n## rulebook.json Schema\n\n```json\n{\n \"rulebook_version\": 1,\n \"name\": \"project-rules\",\n \"version\": \"1.0.0\",\n \"description\": \"Project-specific CC Safety Net rules.\",\n \"author\": \"project\",\n \"allowed_commands\": [\"docker\"],\n \"rules\": [\n {\n \"name\": \"block-docker-system-prune\",\n \"command\": \"docker\",\n \"subcommand\": \"system\",\n \"block_args\": [\"prune\"],\n \"reason\": \"Use targeted cleanup instead.\"\n }\n ],\n \"tests\": [\n {\n \"command\": \"docker system prune\",\n \"expect\": \"blocked\",\n \"rule\": \"block-docker-system-prune\"\n },\n {\n \"command\": \"docker ps\",\n \"expect\": \"allowed\"\n }\n ]\n}\n```\n\n### Rulebook Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `rulebook_version` | Yes | Must be `1` or `2` |\n| `name` | Yes | `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$` |\n| `version` | Yes | Non-empty string |\n| `description` | No | Free text; not type-checked at runtime |\n| `author` | No | Free text; not type-checked at runtime |\n| `allowed_commands` | Yes | Unique command names matching `^[a-zA-Z][a-zA-Z0-9_-]*$` |\n| `rules` | Yes | Array of rule objects |\n| `tests` | No | Array of fixtures |\n\n### Rule Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Unique within the rulebook (case-insensitive); same pattern as rulebook `name` |\n| `command` | Yes | Must be listed in `allowed_commands`; basename only, not path |\n| `subcommand` | No | Same pattern as `command`; omit to match any subcommand |\n| `intent` | No | One of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain` |\n| `block_args` | Yes | Non-empty array of non-empty strings |\n| `reason` | Yes | Non-empty string, max 256 chars |\n\n### Rule Fields (`rulebook_version` 2)\n\nVersion 2 replaces `subcommand` and `block_args` with an exact-token `match` object. Version 1 rulebooks keep their fields and their behavior; a client that does not support version 2 rejects the rulebook instead of applying broader version 1 semantics.\n\n```json\n{\n \"name\": \"block-terraform-apply-destroy\",\n \"command\": \"terraform\",\n \"match\": {\n \"command_path\": [\"apply\"],\n \"any_args\": [\"-destroy\", \"--destroy\"]\n },\n \"reason\": \"Review a destroy plan first with 'terraform plan -destroy'.\",\n \"intent\": \"use_alternative\"\n}\n```\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `name` | Yes | Same as version 1 |\n| `command` | Yes | Same as version 1 |\n| `match.command_path` | Yes | Non-empty array of non-empty command words |\n| `match.any_args` | No | Non-empty array of unique non-empty argument tokens |\n| `match.exclude_args` | No | Non-empty array of unique non-empty argument tokens |\n| `intent` | No | Same as version 1 |\n| `reason` | Yes | Same as version 1 |\n\n### Matching Behavior (`rulebook_version` 2)\n\n- **Command**: Normalized to lowercase basename, as in version 1.\n- **Command path**: After recognized global options and their values are skipped, the next command words must equal `command_path` exactly. AWS, gcloud, and Azure CLI value-taking global options are built in; Terraform's `-chdir=dir` is `=`-joined and is skipped with its own token.\n- **Unrecognized options**: A token starting with `-` that is not a recognized global option is skipped without consuming a value, so an unlisted value-taking option with a separate value (`--newflag value`) makes the rule miss. This fails open deliberately; document such gaps in the rulebook.\n- **`any_args`**: At least one listed token must appear literally among the arguments.\n- **`exclude_args`**: Any listed token appearing literally among the arguments prevents the match, which is how a safe preview such as `aws s3 rm --dryrun` stays allowed.\n- **No short-option expansion**: Arguments compare as exact tokens, so list every accepted spelling (`\"-destroy\"` and `\"--destroy\"`).\n- **Literal and case-sensitive**: No regex, glob, or substring matching. The first matching rule wins.\n- Release channels are separate rules: `gcloud beta compute instances delete` needs its own `command_path`.\n\n### Test Fixture Fields\n\n| Field | Required | Constraints |\n|-------|----------|-------------|\n| `command` | Yes | Non-empty shell command string |\n| `expect` | Yes | `\"blocked\"` or `\"allowed\"` |\n| `rule` | Required for blocked fixtures | Rule name expected to block the command |\n\nFixtures are optional documentation of intended behavior. Version 1 fixtures are shape-validated only. Version 2 fixtures are evaluated against the rulebook's own rules when a source is fetched by `rule add` or `rule update`, and by `rule verify`; a failing fixture rejects that source before it is written. Loading a rulebook does not re-evaluate fixtures. CC Safety Net never executes fixture commands; they are analyzer inputs only.\n\n## Matching Behavior\n\nThe subcommand, argument, and option rules below describe `rulebook_version` 1 rules; version 2 rules match as described in Matching Behavior (`rulebook_version` 2). Execution order and transparent wrappers apply to both.\n\n- **Command**: Normalized to lowercase basename with any trailing `.exe` removed (`/usr/bin/git` → `git`).\n- **Subcommand**: The first command token after recognized Git and Docker global options and their values; `--` ends option parsing. An unrecognized option without `=` may consume the following token as its value.\n- **Arguments**: Each `block_args` value is compared literally against every command token, including expanded short options. The command is blocked if **any** item matches.\n- **Short options**: Expanded (`-Ap` matches `-A`).\n- **Long options**: Exact match (`--all-files` does not match `--all`).\n- **Execution order**: Built-in rules first, then custom rulebooks. Custom rules only add restrictions.\n- **Transparent wrappers**: A configured wrapper such as `rtk` lets `rtk git commit` be analyzed as `git commit` only when `git` is protected by built-in analyzers or active custom rules. `rtk -- git commit` is also supported.\n\n## Workflow\n\n1. Run `cc-safety-net rule init` or create `rule.json` manually.\n2. Optionally run `cc-safety-net rule init --example` to create an inactive example rulebook.\n3. Use `cc-safety-net rule wrapper add rtk` for trusted transparent wrappers.\n4. Run `cc-safety-net rule add <source>` after creating or choosing a rulebook source; add `--only <rulebook...>` or `--ref <ref>` for repository selection. The command adds the selected sources and syncs them.\n5. Edit a local rulebook whenever you like: the edit is enforced on the next command, so there is nothing to run afterwards.\n6. Run `cc-safety-net rule update [source]` to re-fetch remote sources and rewrite the vendored copies; the command prints what changed. A source with an ordinary update failure keeps its vendored copy while the other selected sources still update. Resource-limit failures remain fatal for the whole update.\n7. Run `cc-safety-net rule verify` to validate config, local rulebooks, and shareable GitHub-source rulebook directories in the current repository (it does not fetch remote content).\n8. Run `cc-safety-net rule list` to inspect active rulebooks and transparent wrappers.\n\nA missing or invalid rulebook file makes that source inactive, and an unreadable or invalid `rule.json` makes every source in its scope inactive. Inactive sources stop applying their rules while other custom rules and all built-in protections stay active. Fix the file named in the diagnostic, or run `cc-safety-net rule update` when a remote source has not been vendored yet. Run `cc-safety-net status` to see degraded sources.\n";function zr(P,A){if(!P.ok){ed(P);return}Zc(P,A)}function Xc(P,A,q){if(P.ok)console.log(q);if(!P.add){zr(P,`Added rulebook source: ${A}`);return}if(!P.ok){ed(P);return}if(P.add.added.length>0)console.log(`Added ${P.add.added.length} ${P.add.added.length===1?"rulebook":"rulebooks"} from ${P.add.source} at ${P.add.ref}:`),P.add.added.forEach((V)=>{console.log(` - ${V}`)});if(P.add.alreadyConfigured.length>0)console.log(`Rulebooks already configured from ${P.add.source} at ${P.add.ref}: ${P.add.alreadyConfigured.join(", ")}`);if(P.add.commits.length>0)console.log(`Vendored at ${P.add.commits.map((V)=>V.slice(0,7)).join(", ")}.`);Zc(P,"Rule config updated.")}function Zc(P,A){for(let q of P.changes??[])console.log(q);console.log(A),console.log(""),_g(P.entries)}function _g(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let A of P)console.log(` - ${A.name} ${A.version} (${Tg(A.ruleCount)})`),console.log(` Source: ${A.spec}`)}function Tg(P){return`${P} ${P===1?"rule":"rules"}`}function Qc(P){fn("Active sources",P.rulebooks,(A)=>[`[${A.source}] ${A.name} ${A.version}`,` Source: ${A.spec}`]),fn("Active rules",P.rules,(A)=>[`[${$g(P,A.name)}] ${A.name}`,...Ig(A),` Reason: ${A.reason}`]),fn("Disabled rules",Yc(P,"off"),(A)=>[A.key]),fn("Reason overrides",Yc(P,"reason"),(A)=>[A.key,` Reason: ${A.value.reason}`]),fn("Transparent wrappers",P.transparent_wrappers,(A)=>[A]),fn("Issues",P.errors,(A)=>[A]),fn("Warnings",P.warnings,(A)=>[A])}function fn(P,A,q){if(A.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${A.length}):`);for(let V of A){let[ee,...ne]=q(V);console.log(` - ${ee}`);for(let ce of ne)console.log(` ${ce}`)}}function Ig(P){if(!P.match)return[` Command: ${P.subcommand?`${P.command} ${P.subcommand}`:P.command}`,` Block args: ${P.block_args.join(", ")}`];return[` Command: ${[P.command,...P.match.command_path].join(" ")}`,...P.match.any_args?[` Any args: ${P.match.any_args.join(", ")}`]:[],...P.match.exclude_args?[` Exclude args: ${P.match.exclude_args.join(", ")}`]:[]]}function $g(P,A){return P.rulebooks.find((q)=>q.rules.includes(A))?.source??"project"}function Yc(P,A){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((q)=>{if(A==="off")return q[1]==="off";return!!q[1]&&typeof q[1]==="object"}).map(([q,V])=>({key:q,value:V}))}function ed(P){for(let A of P.errors)console.error(A)}import{dirname as xd,join as eo}from"node:path";import{join as Ai,resolve as zg}from"node:path";function Ri(P){let A=m(P);if(A.errors.length>0)return{ok:!1,result:{ok:!1,errors:A.errors,entries:[]}};return{ok:!0,config:A.config??it}}function td(P){Pt(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function nd(P){Pt(P,{rulebook_version:1,name:"example-rules",version:"1.0.0",description:"Project-specific CC Safety Net rules.",author:"project",allowed_commands:["docker"],rules:[{name:"block-docker-system-prune",command:"docker",subcommand:"system",block_args:["prune"],reason:"Use targeted cleanup instead."}],tests:[{command:"docker system prune",expect:"blocked",rule:"block-docker-system-prune"}]})}import{dirname as Yr}from"node:path";var Og="custom.";function Jr(P){if(P.rulebook_version!==2)return[];let A=P.rules.map((q)=>({name:q.name,command:q.command,block_args:[],match:q.match,reason:q.reason,intent:q.intent}));return(P.tests??[]).flatMap((q,V)=>{let ee=Pi(h(q.command));if(ee.length===0)return[`tests[${V}]: could not parse fixture command: ${q.command}`];let ne=ee.reduce((ce,he)=>ce??_(he,A)?.id.slice(Og.length),void 0);if(q.expect==="blocked"){if(ne===q.rule)return[];let ce=ne?`"${ne}" matched first`:"no rule matched";return[`tests[${V}]: expected "${q.rule}" to block "${q.command}" but ${ce}`]}return ne?[`tests[${V}]: expected "${q.command}" to be allowed but "${ne}" matched`]:[]})}function Pi(P){return P.nodes.flatMap((A)=>{if(A.kind==="group"||A.kind==="function")return Pi(A.body);if(A.kind!=="command")return[];let q=ge(Q(A.dialect,A.words)).words.map(t);return[...q.length>0?[q]:[],...A.nested.flatMap((V)=>Pi(V))]})}var Wr=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function Kr(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??Wr.maxRequests,maxResponseBytes:P.maxResponseBytes??Wr.maxResponseBytes}}function zt(P){return{controller:new AbortController,budget:Kr(),resolveUrl:P}}function rd(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function od(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function id(P,A){if(A>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=A,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=A}var ld=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function sd(P,A,q=v(Yr(Yr(A)),"rules policy"),V=zt()){if(S(P))return Mg(P,V);return jg(P,A,q)}async function cd(P,A,q,V,ee,ne){if(!S(P))return sd(P,A,q,V);let ce=ee?null:Fg(P,A,q);if(ce)return ce;if(!ee&&!ne)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return sd(P,A,q,V)}function Fg(P,A,q=v(Yr(Yr(A)),"rules policy")){let V=I(P),ee=O(A,V.name),ne=n(i(q,ee));if(ne===null)return null;let ce=se(Ei(ne,`Invalid rulebook ${ee}.`));if(ce.name!==V.name)throw Error(`rulebook name "${ce.name}" in ${ee} must match "${V.name}"`);return{spec:P,rulebook:ce,content:ne}}async function dd(P,A={}){if(!K(P))throw Error(`Invalid GitHub repository source: ${P}`);let[q,V]=P.split("/");if(!q||!V)throw Error(`Invalid GitHub repository source: ${P}`);if(A.ref!==void 0&&!oe(A.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${A.ref}`);let ee=A.operation??zt(),ne=A.ref??await Ng(q,V,P,ee),ce=await pd(q,V,ne,P,ee),he=await Xr(`https://api.github.com/repos/${q}/${V}/git/trees/${ce}?recursive=1`,"tree",ee),ye=he.response;if(!ye.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${ye.status}`);let be=JSON.parse(he.content);if(!Array.isArray(be?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let xe=be.tree,Ie=[...new Set(xe.flatMap((Xe)=>{if(!Xe||typeof Xe!=="object")return[];let et=Xe;if(et.type!=="blob"||typeof et.path!=="string")return[];let Re=et.path.match(rt);return Re?.[1]?[Re[1]]:[]}))].sort();if(Ie.length===0)throw Error(`No rulebooks found in ${P} under ${de}/`);return{source:P,owner:q,repo:V,ref:ne,commit:ce,names:Ie}}async function Ng(P,A,q,V){let ee=await Xr(`https://api.github.com/repos/${P}/${A}`,"metadata",V),ne=ee.response;if(!ne.ok)throw Error(`Failed to inspect ${q}: GitHub returned ${ne.status}`);let he=JSON.parse(ee.content)?.default_branch;if(typeof he!=="string"||he==="")throw Error(`Failed to inspect ${q}: missing default branch`);if(!oe(he))throw Error(`GitHub returned an invalid default branch: ${he}`);return he}function jg(P,A,q){ot(P);let V=O(A,P),ee=n(i(q,V));if(ee===null)throw Error(`Rulebook source not found: ${P}`);let ne=ud(Ei(ee,"Invalid local rulebook source."));if(ne.name!==P)throw Error(`rulebook name "${ne.name}" must match local source "${P}"`);return{spec:P,rulebook:ne,content:ee}}async function Mg(P,A){let q=I(P),V=await pd(q.owner,q.repo,q.ref,P,A),ee=await Xr(`https://raw.githubusercontent.com/${q.owner}/${q.repo}/${V}/${q.path}`,"raw",A),ne=ee.response;if(!ne.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${ne.status}`);let ce=ee.content,he=ud(Ei(ce,"Invalid GitHub rulebook response."));if(he.name!==q.name)throw Error(`rulebook name "${he.name}" must match GitHub source "${q.name}"`);return{spec:P,rulebook:he,content:ce}}function ud(P){let A=se(P),q=Jr(A);if(q.length>0)throw Error(q.join("; "));return A}function Ei(P,A){try{return JSON.parse(P)}catch{throw Error(A)}}async function pd(P,A,q,V,ee){let ne=await Xr(`https://api.github.com/repos/${P}/${A}/commits/${encodeURIComponent(q)}`,"commit",ee),ce=ne.response;if(!ce.ok)throw Error(`Failed to resolve ${V}: GitHub returned ${ce.status}`);let he=JSON.parse(ne.content);if(typeof he?.sha!=="string"||he.sha==="")throw Error(`Failed to resolve commit for ${V}`);return he.sha}async function Hg(P,A,q={}){if(q.signal?.aborted)throw q.signal.reason;let V=q.budget??Kr(),ee=new AbortController,ne=()=>ee.abort(q.signal?.reason);q.signal?.addEventListener("abort",ne,{once:!0});let ce=!1,he=setTimeout(()=>{if(ee.signal.aborted)return;ce=!0,ee.abort()},q.timeoutMs??ld.timeoutMs);try{if(q.signal?.aborted)throw q.signal.reason;od(V);let ye=await fetch(P,{signal:ee.signal,redirect:"error"});if(!ye.ok)return fd(ye),{response:ye,content:""};return{response:ye,content:await Ug(ye,A,V,()=>ee.abort())}}catch(ye){if(ce)throw Error("GitHub request timed out",{cause:ye});if(q.signal?.aborted)throw q.signal.reason;throw ye}finally{clearTimeout(he),q.signal?.removeEventListener("abort",ne)}}function Xr(P,A,q){return Hg(q.resolveUrl?.(P)??P,A,{budget:q.budget,signal:q.controller.signal})}async function Ug(P,A,q=Kr(),V){let ee=ld[`${A}Bytes`],ne=Number(P.headers.get("content-length"));if(Number.isFinite(ne)&&ne>ee)throw fd(P),Error(`GitHub ${A} response exceeds ${ee} bytes`);if(!P.body)return"";let ce=P.body.getReader(),he=[],ye=0;while(!0){let be=await ce.read();if(be.done)break;try{id(q,be.value.byteLength)}catch(xe){throw V?.(),ad(ce),xe}if(ye+=be.value.byteLength,ye>ee)throw V?.(),ad(ce),Error(`GitHub ${A} response exceeds ${ee} bytes`);he.push(Buffer.from(be.value))}return Buffer.concat(he,ye).toString("utf-8")}function fd(P){if(!P.body)return;md(()=>P.body?.cancel())}function ad(P){md(()=>P.cancel())}function md(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var Gg=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function gd(P,A){let q=vd(P.rules,A);if(q.length>0)return{ok:!0,specs:q};return yd(P.rules,A)}function hd(P,A){let q=vd(P,A);if(q.length>0)return{ok:!0,specs:q};let V=qg(P,A);if(V.length>0)return{ok:!0,specs:V};let ee=Vg(P,A);if(!ee.ok)return ee;if(ee.specs.length>0)return{ok:!0,specs:ee.specs};return yd(P,A)}function yd(P,A){let q=P.filter((V)=>Di(V)?.name===A);if(q.length===1)return{ok:!0,specs:q};return Bg(A,q)}function Bg(P,A){return{ok:!1,result:{ok:!1,errors:A.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${A.join(", ")}`],entries:[]}}}function vd(P,A){return P.filter((q)=>q===A)}function qg(P,A){let q=A.match(Gg),V=q?.[1],ee=q?.[2],ne=q?.[3];if(!V||!ee||!ne||!oe(ne))return[];return bd(P,(ce)=>ce.owner===V&&ce.repo===ee&&ce.ref===ne)}function Vg(P,A){if(!K(A))return{ok:!0,specs:[]};let[q,V]=A.split("/"),ee=bd(P,(ce)=>ce.owner===q&&ce.repo===V);if(new Set(ee.map((ce)=>Di(ce)?.ref).filter((ce)=>!!ce)).size<2)return{ok:!0,specs:ee};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${A}. Use an explicit ref:`,` cc-safety-net rule remove ${A}#<ref>`],entries:[]}}}function Di(P){try{return I(P)}catch{return null}}function bd(P,A){return P.filter((q)=>{let V=Di(q);return V?A(V):!1})}async function Qr(P,A={}){let q=_i(A);return Jg(P,q,await Zr(P,q,zt()))}function Jg(P,A,q){if(!q.ok)return q;let V=Et(P,A),ee=[...new Set(U(V.configPath,V.filesystemScope))];if(ee.length===0)return q;return{ok:!1,errors:ee,entries:q.entries}}async function Zr(P,A,q,V={},ee=new Set,ne=new Set){try{let ce=Et(P,A),he=Ri(ce.configTarget);if(!he.ok)return he.result;let ye=he.config,be=A.only?gd(ye,A.only):{ok:!0,specs:ye.rules};if(!be.ok)return be.result;let xe=new Set([...A.refresh?be.specs:[],...ee]),Ie=(ut)=>cd(ut,ce.configDir,ce.filesystemScope,q,xe.has(ut),!A.refresh||xe.has(ut)),Xe=await oh(ye.rules,A.refresh?(ut)=>Ie(ut).then((vt)=>({ok:!0,item:vt})).catch((vt)=>{if(rd(vt))throw vt;return{ok:!1,spec:ut,message:vt instanceof Error?vt.message:String(vt)}}):async(ut)=>({ok:!0,item:await Ie(ut)}),q),et=Xe.filter((ut)=>!ut.ok),Re=Xe.filter((ut)=>ut.ok).map((ut)=>ut.item),at=Re.flatMap((ut)=>Wg(ut,ye.rules)),ct=Re.flatMap((ut)=>Kg(ut,ne,ce)),lt=new Set([...at,...ct].map((ut)=>ut.spec)),ft=[...et,...at,...ct],mt=[],gt=Xg(mt,()=>Re.flatMap((ut)=>lt.has(ut.spec)||ft.length>0&&ne.has(ut.spec)?[]:Yg(ut,ce,V,mt)));return{ok:ft.length===0,errors:ft.map((ut)=>`Failed to update ${ut.spec}: ${ut.message}`),entries:Re.map(Qg),changes:gt}}catch(ce){return Qn(ce)}}function Wg(P,A){if(!S(P.spec))return[];let q=Pe(P.spec),V=A.filter((ee)=>ee!==P.spec&&Pe(ee).toLowerCase()===q.toLowerCase());if(V.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${q}" is also claimed by ${V.join(", ")}; rename one of them`}]}function Kg(P,A,q){if(!A.has(P.spec)||!S(P.spec))return[];let V=O(q.configDir,P.rulebook.name),ee=n(i(q.filesystemScope,V));if(ee===null||ee===P.content)return[];return[{ok:!1,spec:P.spec,message:`${V} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function Yg(P,A,q,V){if(!S(P.spec))return[];let ee=O(A.configDir,P.rulebook.name),ne=i(A.filesystemScope,ee),ce=n(ne);if(ce===P.content)return[];return V?.push({target:ne,previous:ce}),g(ne,P.content,void 0,q._testAfterPolicyRename),Zg(P,ce)}function Xg(P,A){try{return A()}catch(q){for(let V of[...P].reverse()){if(V.previous===null){F(V.target);continue}g(V.target,V.previous)}throw q}}function Zg(P,A){if(A===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let q=me(A),V="problem"in q?null:q.rulebook,ee=new Map(V?.rules.map((ce)=>[ce.name,JSON.stringify(ce)])??[]),ne=new Set(P.rulebook.rules.map((ce)=>ce.name));return[`Updated ${P.spec} (${V?.version??"unreadable"} -> ${P.rulebook.version})`,...[...ne].filter((ce)=>!ee.has(ce)).map((ce)=>` + ${ce}`),...[...ee.keys()].filter((ce)=>!ne.has(ce)).map((ce)=>` - ${ce}`),...P.rulebook.rules.filter((ce)=>{let he=ee.get(ce.name);return he!==void 0&&he!==JSON.stringify(ce)}).map((ce)=>` ~ ${ce.name}`)]}function Qg(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function Ld(P,A,q={}){return eh(P,A,sh(q),zt())}async function eh(P,A,q,V,ee={}){let ne=null,ce=!1;try{let he=Et(P,q),ye=n(he.configTarget);ne={target:he.configTarget,content:ye};let be=Ri(he.configTarget);if(!be.ok)return be.result;let xe=be.config,Ie=K(A);th(A,q,Ie);let Xe=Ie?await dd(A,{ref:q.ref,operation:V}):null,et=Xe?nh(Xe,q.rulebooks):[],Re=Xe?et.map((mt)=>rh(xe.rules,Xe,mt)??`${A}#${Xe.ref}/${mt}`):[A],at=Re.filter((mt)=>!xe.rules.includes(mt)),ct=[...xe.rules,...at];if(ct.length>pe)return ih();if(ct.length!==xe.rules.length)ce=!0,Pt(he.configTarget,{version:1,rules:ct,overrides:xe.overrides??{},transparent_wrappers:xe.transparent_wrappers??[]},void 0,ee._testAfterPolicyRename);let lt=await Zr(P,q,V,ee,new Set(at),new Set(at));if(!lt.ok)Zn(he.configTarget,ye);if(!lt.ok||!Xe)return lt;let ft=et.filter((mt,gt)=>at.includes(Re[gt]??""));return{...lt,add:{source:A,ref:Xe.ref,selected:et,added:ft,alreadyConfigured:et.filter((mt)=>!ft.includes(mt)),commits:at.length>0?[Xe.commit]:[]}}}catch(he){if(ce&&ne)try{Zn(ne.target,ne.content)}catch(ye){return Qn(ye)}return Qn(he)}}function th(P,A,q){if(!q&&A.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!q&&A.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(A.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let V=A.rulebooks?.filter((ee)=>!l.test(ee))??[];if(V.length>0)throw Error(`Invalid rulebook names: ${V.join(", ")}`)}function nh(P,A){let q=A?[...new Set(A)]:P.names,V=q.filter((ee)=>!P.names.includes(ee));if(V.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${V.join(", ")}
|
|
272
|
+
`,384),kg(V,384),{path:V,policy:ne,errors:[]}}function Bc(P,A){let q=un(A,P.home);if(q.errors.length>0)return{errors:q.errors};return{preview:Ae(q.policy,P.env),errors:[]}}function qc(P,A={}){let q=u(P,A);if(!Hc(q))return Vt(P,X,A);let V=Uc(q,"utf-8");if(!V.trim())return Vt(P,X,A);try{return Vt(P,C(JSON.parse(V),P.home),A)}catch{return Vt(P,X,A)}}var Vc=new Set(["check","apply"]),zc="(unset)";async function Wc(P,A,q={}){let V=xt({label:"policy",booleans:{global:["-g","--global"]},positionals:"list"},A),ee=V.positionals[0],ne=[...V.errors,...ee&&!Vc.has(ee)?[`Unknown policy subcommand: ${ee}`]:[],...ee&&Vc.has(ee)&&!V.positionals[1]?[`policy ${ee} requires a file`]:[],...V.positionals.slice(2).map((Re)=>`Unexpected policy argument: ${Re}`)];if(ne.length>0){for(let Re of ne)console.error(Re);return 1}let ce=V.positionals[1];if(!ee||!ce)return Cn(or,console.error),1;let he=V.flags.global?u(P):b(q.cwd??process.cwd()),ye=pn(ce),be=[...ye.errors,...Yn(ye.value,P.home).map((Re)=>`${ce}: ${Re}`),...!V.flags.global&&Ag(ye.value)&&ye.value.audit!==void 0?[`${ce}: audit settings are user scope only; remove the audit section from a project proposal`]:[]];if(be.length>0){for(let Re of be)console.error(Re);return 1}let xe=C(ye.value,P.home);if(console.log(`Scope: ${V.flags.global?"user":"project"} (${he})`),console.log(`Proposal: ${ce}`),V.flags.global)Jc(C(pn(he).value,P.home),xe,!0);if(!V.flags.global){let Re=Xn(P).baseline;console.log("Effective policy (user + project merged):"),Jc(Z(Re,ae(pn(he).value,P.home).policy).policy,Z(Re,ae(ye.value,P.home).policy).policy,!1)}if(ee==="check")return 0;let Ie=q.input??process.stdin,Xe=q.output??process.stdout;if(!Ie.isTTY||!Xe.isTTY)return console.error("policy apply confirms interactively; run this yourself in a terminal:"),console.error(` cc-safety-net policy apply ${ce}${V.flags.global?" --global":""}`),1;if(!await Eg(`Apply this policy to ${he}? [y/N] `,Ie,Xe))return console.log("Cancelled; nothing was written."),0;return Dg(P,he,ye.value,xe,V.flags.global),console.log(`Policy applied: ${he}`),0}function Eg(P,A,q){let V=Pg({input:A,output:q,terminal:!1});return new Promise((ee)=>{V.once("close",()=>ee(!1)),V.question(P,(ne)=>{ee(/^y(es)?$/i.test(ne.trim())),V.close()})})}function Dg(P,A,q,V,ee){if(ee){Vt(P,V);return}Sg(Rg(A),{recursive:!0}),Pt(A,Vr(q,V))}function Jc(P,A,q){let V=qr(P,A,q);if(V.length===0){console.log("No changes.");return}console.log(`Changes (${V.length}):`);for(let ee of V)console.log(` ${ee.field}: ${ee.before??zc} -> ${ee.after??zc}`)}function Ag(P){return!!P&&typeof P==="object"&&!Array.isArray(P)}import{join as Bh}from"node:path";var Kc="# Custom Rules Reference\n\nAgent reference for generating CC Safety Net rulebook configuration.\n\n## Config Locations\n\n| Scope | Config path | Rulebook path | Priority |\n|-------|-------------|---------------|----------|\n| User | `~/.cc-safety-net/rules/rule.json` | `~/.cc-safety-net/rules/<rulebook-name>/rulebook.json` | First |\n| Project | `.cc-safety-net/rules/rule.json` | `.cc-safety-net/rules/<rulebook-name>/rulebook.json` | Second |\n| GitHub source | Listed in a local `rule.json` | Vendored into the consumer's `<rulebook-name>/rulebook.json` by `rule add` | Source order |\n\nEvery rulebook is a live file: the runtime reads it on each tool call, so an edit applies to the next command with no publishing step.\n\nUser scope is evaluated before project scope; within a scope, sources apply in `rules` array order. A duplicate active rulebook name keeps the first claim and ignores the later rulebook with a warning, so a user-scoped name shadows a project-scoped one.\n\nUse `cc-safety-net rule init` to create an inert local config. Use `--global` for user scope. Use `cc-safety-net rule init --example` to also create an inactive example rulebook. `CC_SAFETY_NET_HOME` overrides the `~/.cc-safety-net` user root.\n\nLegacy inline `.safety-net.json` and `~/.cc-safety-net/config.json` files are not loaded at runtime. Convert them with `cc-safety-net rule migrate`.\n\n## rule.json Schema\n\n```json\n{\n \"version\": 1,\n \"rules\": [\"project-rules\", \"owner/repo#main/team-rules\"],\n \"overrides\": {\n \"project-rules/block-docker-system-prune\": {\n \"reason\": \"Use targeted Docker cleanup commands.\"\n },\n \"team-rules/block-npm-global\": \"off\"\n },\n \"transparent_wrappers\": [\"rtk\"]\n}\n```\n\n- `version`: Required. Must be `1`.\n- `$schema`: Optional. `cc-safety-net rule verify` inserts it into a valid `rule.json` that lacks it.\n- `rules`: Optional array of rulebook source strings. Missing `rules` is treated as `[]`.\n- `overrides`: Optional object keyed by `<rulebook-name>/<rule-name>`.\n- `overrides` values are either `\"off\"` to disable a rule or an object with a required `reason` (replacement block reason) and an optional `intent` (one of `hard_stop`, `use_alternative`, `scope_down`, `manual_only`, `stop_and_explain`).\n- A project override cannot target a user-scoped rule: only that override is ignored, the user rule keeps its configured state, and `rule verify` reports the diagnostic as a failure.\n- `transparent_wrappers`: Optional array of command names that transparently execute a visible child command.\n- `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 zr(P,A){if(!P.ok){ed(P);return}Zc(P,A)}function Xc(P,A,q){if(P.ok)console.log(q);if(!P.add){zr(P,`Added rulebook source: ${A}`);return}if(!P.ok){ed(P);return}if(P.add.added.length>0)console.log(`Added ${P.add.added.length} ${P.add.added.length===1?"rulebook":"rulebooks"} from ${P.add.source} at ${P.add.ref}:`),P.add.added.forEach((V)=>{console.log(` - ${V}`)});if(P.add.alreadyConfigured.length>0)console.log(`Rulebooks already configured from ${P.add.source} at ${P.add.ref}: ${P.add.alreadyConfigured.join(", ")}`);if(P.add.commits.length>0)console.log(`Vendored at ${P.add.commits.map((V)=>V.slice(0,7)).join(", ")}.`);Zc(P,"Rule config updated.")}function Zc(P,A){for(let q of P.changes??[])console.log(q);console.log(A),console.log(""),_g(P.entries)}function _g(P){if(P.length===0){console.log("Active rulebooks: (none)");return}console.log(`Active rulebooks (${P.length}):`);for(let A of P)console.log(` - ${A.name} ${A.version} (${Tg(A.ruleCount)})`),console.log(` Source: ${A.spec}`)}function Tg(P){return`${P} ${P===1?"rule":"rules"}`}function Qc(P){fn("Active sources",P.rulebooks,(A)=>[`[${A.source}] ${A.name} ${A.version}`,` Source: ${A.spec}`]),fn("Active rules",P.rules,(A)=>[`[${$g(P,A.name)}] ${A.name}`,...Ig(A),` Reason: ${A.reason}`]),fn("Disabled rules",Yc(P,"off"),(A)=>[A.key]),fn("Reason overrides",Yc(P,"reason"),(A)=>[A.key,` Reason: ${A.value.reason}`]),fn("Transparent wrappers",P.transparent_wrappers,(A)=>[A]),fn("Issues",P.errors,(A)=>[A]),fn("Warnings",P.warnings,(A)=>[A])}function fn(P,A,q){if(A.length===0){console.log(`${P}: (none)`);return}console.log(`${P} (${A.length}):`);for(let V of A){let[ee,...ne]=q(V);console.log(` - ${ee}`);for(let ce of ne)console.log(` ${ce}`)}}function Ig(P){if(!P.match)return[` Command: ${P.subcommand?`${P.command} ${P.subcommand}`:P.command}`,` Block args: ${P.block_args.join(", ")}`];return[` Command: ${[P.command,...P.match.command_path].join(" ")}`,...P.match.any_args?[` Any args: ${P.match.any_args.join(", ")}`]:[],...P.match.exclude_args?[` Exclude args: ${P.match.exclude_args.join(", ")}`]:[]]}function $g(P,A){return P.rulebooks.find((q)=>q.rules.includes(A))?.source??"project"}function Yc(P,A){return Object.entries({...P.userConfig?.overrides,...P.projectConfig?.overrides}).filter((q)=>{if(A==="off")return q[1]==="off";return!!q[1]&&typeof q[1]==="object"}).map(([q,V])=>({key:q,value:V}))}function ed(P){for(let A of P.errors)console.error(A)}import{dirname as xd,join as eo}from"node:path";import{join as Ai,resolve as zg}from"node:path";function Ri(P){let A=m(P);if(A.errors.length>0)return{ok:!1,result:{ok:!1,errors:A.errors,entries:[]}};return{ok:!0,config:A.config??it}}function td(P){Pt(P,{version:1,rules:[],overrides:{},transparent_wrappers:[]})}function nd(P){Pt(P,{rulebook_version:1,name:"example-rules",version:"1.0.0",description:"Project-specific CC Safety Net rules.",author:"project",allowed_commands:["docker"],rules:[{name:"block-docker-system-prune",command:"docker",subcommand:"system",block_args:["prune"],reason:"Use targeted cleanup instead."}],tests:[{command:"docker system prune",expect:"blocked",rule:"block-docker-system-prune"}]})}import{dirname as Yr}from"node:path";var Og="custom.";function Jr(P){if(P.rulebook_version!==2)return[];let A=P.rules.map((q)=>({name:q.name,command:q.command,block_args:[],match:q.match,reason:q.reason,intent:q.intent}));return(P.tests??[]).flatMap((q,V)=>{let ee=Pi(h(q.command));if(ee.length===0)return[`tests[${V}]: could not parse fixture command: ${q.command}`];let ne=ee.reduce((ce,he)=>ce??_(he,A)?.id.slice(Og.length),void 0);if(q.expect==="blocked"){if(ne===q.rule)return[];let ce=ne?`"${ne}" matched first`:"no rule matched";return[`tests[${V}]: expected "${q.rule}" to block "${q.command}" but ${ce}`]}return ne?[`tests[${V}]: expected "${q.command}" to be allowed but "${ne}" matched`]:[]})}function Pi(P){return P.nodes.flatMap((A)=>{if(A.kind==="group"||A.kind==="function")return Pi(A.body);if(A.kind!=="command")return[];let q=ge(Q(A.dialect,A.words)).words.map(t);return[...q.length>0?[q]:[],...A.nested.flatMap((V)=>Pi(V))]})}var Wr=Object.freeze({concurrency:4,maxRequests:131,maxResponseBytes:67108864});function Kr(P={}){return{requests:0,responseBytes:0,maxRequests:P.maxRequests??Wr.maxRequests,maxResponseBytes:P.maxResponseBytes??Wr.maxResponseBytes}}function zt(P){return{controller:new AbortController,budget:Kr(),resolveUrl:P}}function rd(P){return P instanceof Error&&P.message==="Rule synchronization exceeds CC Safety Net's safe resource limits."}function od(P){if(P.requests>=P.maxRequests)throw Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.requests++}function id(P,A){if(A>P.maxResponseBytes-P.responseBytes)throw P.responseBytes+=A,Error("Rule synchronization exceeds CC Safety Net's safe resource limits.");P.responseBytes+=A}var ld=Object.freeze({timeoutMs:15000,metadataBytes:524288,commitBytes:262144,treeBytes:16777216,rawBytes:4194304});async function sd(P,A,q=v(Yr(Yr(A)),"rules policy"),V=zt()){if(S(P))return Mg(P,V);return jg(P,A,q)}async function cd(P,A,q,V,ee,ne){if(!S(P))return sd(P,A,q,V);let ce=ee?null:Fg(P,A,q);if(ce)return ce;if(!ee&&!ne)throw Error(`${P} is not vendored; run rule update ${P} to vendor it`);return sd(P,A,q,V)}function Fg(P,A,q=v(Yr(Yr(A)),"rules policy")){let V=I(P),ee=O(A,V.name),ne=n(i(q,ee));if(ne===null)return null;let ce=se(Ei(ne,`Invalid rulebook ${ee}.`));if(ce.name!==V.name)throw Error(`rulebook name "${ce.name}" in ${ee} must match "${V.name}"`);return{spec:P,rulebook:ce,content:ne}}async function dd(P,A={}){if(!K(P))throw Error(`Invalid GitHub repository source: ${P}`);let[q,V]=P.split("/");if(!q||!V)throw Error(`Invalid GitHub repository source: ${P}`);if(A.ref!==void 0&&!oe(A.ref))throw Error(`GitHub rulebook refs must use valid path segments: ${A.ref}`);let ee=A.operation??zt(),ne=A.ref??await Ng(q,V,P,ee),ce=await pd(q,V,ne,P,ee),he=await Xr(`https://api.github.com/repos/${q}/${V}/git/trees/${ce}?recursive=1`,"tree",ee),ye=he.response;if(!ye.ok)throw Error(`Failed to inspect ${P}: GitHub tree returned ${ye.status}`);let be=JSON.parse(he.content);if(!Array.isArray(be?.tree))throw Error(`Failed to inspect ${P}: unexpected GitHub tree response`);let xe=be.tree,Ie=[...new Set(xe.flatMap((Xe)=>{if(!Xe||typeof Xe!=="object")return[];let et=Xe;if(et.type!=="blob"||typeof et.path!=="string")return[];let Re=et.path.match(rt);return Re?.[1]?[Re[1]]:[]}))].sort();if(Ie.length===0)throw Error(`No rulebooks found in ${P} under ${de}/`);return{source:P,owner:q,repo:V,ref:ne,commit:ce,names:Ie}}async function Ng(P,A,q,V){let ee=await Xr(`https://api.github.com/repos/${P}/${A}`,"metadata",V),ne=ee.response;if(!ne.ok)throw Error(`Failed to inspect ${q}: GitHub returned ${ne.status}`);let he=JSON.parse(ee.content)?.default_branch;if(typeof he!=="string"||he==="")throw Error(`Failed to inspect ${q}: missing default branch`);if(!oe(he))throw Error(`GitHub returned an invalid default branch: ${he}`);return he}function jg(P,A,q){ot(P);let V=O(A,P),ee=n(i(q,V));if(ee===null)throw Error(`Rulebook source not found: ${P}`);let ne=ud(Ei(ee,"Invalid local rulebook source."));if(ne.name!==P)throw Error(`rulebook name "${ne.name}" must match local source "${P}"`);return{spec:P,rulebook:ne,content:ee}}async function Mg(P,A){let q=I(P),V=await pd(q.owner,q.repo,q.ref,P,A),ee=await Xr(`https://raw.githubusercontent.com/${q.owner}/${q.repo}/${V}/${q.path}`,"raw",A),ne=ee.response;if(!ne.ok)throw Error(`Failed to fetch ${P}: GitHub raw returned ${ne.status}`);let ce=ee.content,he=ud(Ei(ce,"Invalid GitHub rulebook response."));if(he.name!==q.name)throw Error(`rulebook name "${he.name}" must match GitHub source "${q.name}"`);return{spec:P,rulebook:he,content:ce}}function ud(P){let A=se(P),q=Jr(A);if(q.length>0)throw Error(q.join("; "));return A}function Ei(P,A){try{return JSON.parse(P)}catch{throw Error(A)}}async function pd(P,A,q,V,ee){let ne=await Xr(`https://api.github.com/repos/${P}/${A}/commits/${encodeURIComponent(q)}`,"commit",ee),ce=ne.response;if(!ce.ok)throw Error(`Failed to resolve ${V}: GitHub returned ${ce.status}`);let he=JSON.parse(ne.content);if(typeof he?.sha!=="string"||he.sha==="")throw Error(`Failed to resolve commit for ${V}`);return he.sha}async function Hg(P,A,q={}){if(q.signal?.aborted)throw q.signal.reason;let V=q.budget??Kr(),ee=new AbortController,ne=()=>ee.abort(q.signal?.reason);q.signal?.addEventListener("abort",ne,{once:!0});let ce=!1,he=setTimeout(()=>{if(ee.signal.aborted)return;ce=!0,ee.abort()},q.timeoutMs??ld.timeoutMs);try{if(q.signal?.aborted)throw q.signal.reason;od(V);let ye=await fetch(P,{signal:ee.signal,redirect:"error"});if(!ye.ok)return fd(ye),{response:ye,content:""};return{response:ye,content:await Ug(ye,A,V,()=>ee.abort())}}catch(ye){if(ce)throw Error("GitHub request timed out",{cause:ye});if(q.signal?.aborted)throw q.signal.reason;throw ye}finally{clearTimeout(he),q.signal?.removeEventListener("abort",ne)}}function Xr(P,A,q){return Hg(q.resolveUrl?.(P)??P,A,{budget:q.budget,signal:q.controller.signal})}async function Ug(P,A,q=Kr(),V){let ee=ld[`${A}Bytes`],ne=Number(P.headers.get("content-length"));if(Number.isFinite(ne)&&ne>ee)throw fd(P),Error(`GitHub ${A} response exceeds ${ee} bytes`);if(!P.body)return"";let ce=P.body.getReader(),he=[],ye=0;while(!0){let be=await ce.read();if(be.done)break;try{id(q,be.value.byteLength)}catch(xe){throw V?.(),ad(ce),xe}if(ye+=be.value.byteLength,ye>ee)throw V?.(),ad(ce),Error(`GitHub ${A} response exceeds ${ee} bytes`);he.push(Buffer.from(be.value))}return Buffer.concat(he,ye).toString("utf-8")}function fd(P){if(!P.body)return;md(()=>P.body?.cancel())}function ad(P){md(()=>P.cancel())}function md(P){try{Promise.resolve(P()).catch(()=>{})}catch{}}var Gg=/^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)#(.+)$/;function gd(P,A){let q=vd(P.rules,A);if(q.length>0)return{ok:!0,specs:q};return yd(P.rules,A)}function hd(P,A){let q=vd(P,A);if(q.length>0)return{ok:!0,specs:q};let V=qg(P,A);if(V.length>0)return{ok:!0,specs:V};let ee=Vg(P,A);if(!ee.ok)return ee;if(ee.specs.length>0)return{ok:!0,specs:ee.specs};return yd(P,A)}function yd(P,A){let q=P.filter((V)=>Di(V)?.name===A);if(q.length===1)return{ok:!0,specs:q};return Bg(A,q)}function Bg(P,A){return{ok:!1,result:{ok:!1,errors:A.length===0?[`No configured rulebook matches ${P}`]:[`Ambiguous rulebook match ${P}: ${A.join(", ")}`],entries:[]}}}function vd(P,A){return P.filter((q)=>q===A)}function qg(P,A){let q=A.match(Gg),V=q?.[1],ee=q?.[2],ne=q?.[3];if(!V||!ee||!ne||!oe(ne))return[];return bd(P,(ce)=>ce.owner===V&&ce.repo===ee&&ce.ref===ne)}function Vg(P,A){if(!K(A))return{ok:!0,specs:[]};let[q,V]=A.split("/"),ee=bd(P,(ce)=>ce.owner===q&&ce.repo===V);if(new Set(ee.map((ce)=>Di(ce)?.ref).filter((ce)=>!!ce)).size<2)return{ok:!0,specs:ee};return{ok:!1,result:{ok:!1,errors:[`Multiple refs are configured for ${A}. Use an explicit ref:`,` cc-safety-net rule remove ${A}#<ref>`],entries:[]}}}function Di(P){try{return I(P)}catch{return null}}function bd(P,A){return P.filter((q)=>{let V=Di(q);return V?A(V):!1})}async function Qr(P,A={}){let q=_i(A);return Jg(P,q,await Zr(P,q,zt()))}function Jg(P,A,q){if(!q.ok)return q;let V=Et(P,A),ee=[...new Set(U(V.configPath,V.filesystemScope))];if(ee.length===0)return q;return{ok:!1,errors:ee,entries:q.entries}}async function Zr(P,A,q,V={},ee=new Set,ne=new Set){try{let ce=Et(P,A),he=Ri(ce.configTarget);if(!he.ok)return he.result;let ye=he.config,be=A.only?gd(ye,A.only):{ok:!0,specs:ye.rules};if(!be.ok)return be.result;let xe=new Set([...A.refresh?be.specs:[],...ee]),Ie=(ut)=>cd(ut,ce.configDir,ce.filesystemScope,q,xe.has(ut),!A.refresh||xe.has(ut)),Xe=await oh(ye.rules,A.refresh?(ut)=>Ie(ut).then((vt)=>({ok:!0,item:vt})).catch((vt)=>{if(rd(vt))throw vt;return{ok:!1,spec:ut,message:vt instanceof Error?vt.message:String(vt)}}):async(ut)=>({ok:!0,item:await Ie(ut)}),q),et=Xe.filter((ut)=>!ut.ok),Re=Xe.filter((ut)=>ut.ok).map((ut)=>ut.item),at=Re.flatMap((ut)=>Wg(ut,ye.rules)),ct=Re.flatMap((ut)=>Kg(ut,ne,ce)),lt=new Set([...at,...ct].map((ut)=>ut.spec)),ft=[...et,...at,...ct],mt=[],gt=Xg(mt,()=>Re.flatMap((ut)=>lt.has(ut.spec)||ft.length>0&&ne.has(ut.spec)?[]:Yg(ut,ce,V,mt)));return{ok:ft.length===0,errors:ft.map((ut)=>`Failed to update ${ut.spec}: ${ut.message}`),entries:Re.map(Qg),changes:gt}}catch(ce){return Qn(ce)}}function Wg(P,A){if(!S(P.spec))return[];let q=Pe(P.spec),V=A.filter((ee)=>ee!==P.spec&&Pe(ee).toLowerCase()===q.toLowerCase());if(V.length===0)return[];return[{ok:!1,spec:P.spec,message:`rulebook name "${q}" is also claimed by ${V.join(", ")}; rename one of them`}]}function Kg(P,A,q){if(!A.has(P.spec)||!S(P.spec))return[];let V=O(q.configDir,P.rulebook.name),ee=n(i(q.filesystemScope,V));if(ee===null||ee===P.content)return[];return[{ok:!1,spec:P.spec,message:`${V} already exists and no configured source claims it; remove or rename the file, then re-run rule add`}]}function Yg(P,A,q,V){if(!S(P.spec))return[];let ee=O(A.configDir,P.rulebook.name),ne=i(A.filesystemScope,ee),ce=n(ne);if(ce===P.content)return[];return V?.push({target:ne,previous:ce}),g(ne,P.content,void 0,q._testAfterPolicyRename),Zg(P,ce)}function Xg(P,A){try{return A()}catch(q){for(let V of[...P].reverse()){if(V.previous===null){F(V.target);continue}g(V.target,V.previous)}throw q}}function Zg(P,A){if(A===null)return[`Vendored ${P.spec} (${P.rulebook.version})`];let q=me(A),V="problem"in q?null:q.rulebook,ee=new Map(V?.rules.map((ce)=>[ce.name,JSON.stringify(ce)])??[]),ne=new Set(P.rulebook.rules.map((ce)=>ce.name));return[`Updated ${P.spec} (${V?.version??"unreadable"} -> ${P.rulebook.version})`,...[...ne].filter((ce)=>!ee.has(ce)).map((ce)=>` + ${ce}`),...[...ee.keys()].filter((ce)=>!ne.has(ce)).map((ce)=>` - ${ce}`),...P.rulebook.rules.filter((ce)=>{let he=ee.get(ce.name);return he!==void 0&&he!==JSON.stringify(ce)}).map((ce)=>` ~ ${ce.name}`)]}function Qg(P){return{spec:P.spec,name:P.rulebook.name,version:P.rulebook.version,ruleCount:P.rulebook.rules.length}}async function Ld(P,A,q={}){return eh(P,A,sh(q),zt())}async function eh(P,A,q,V,ee={}){let ne=null,ce=!1;try{let he=Et(P,q),ye=n(he.configTarget);ne={target:he.configTarget,content:ye};let be=Ri(he.configTarget);if(!be.ok)return be.result;let xe=be.config,Ie=K(A);th(A,q,Ie);let Xe=Ie?await dd(A,{ref:q.ref,operation:V}):null,et=Xe?nh(Xe,q.rulebooks):[],Re=Xe?et.map((mt)=>rh(xe.rules,Xe,mt)??`${A}#${Xe.ref}/${mt}`):[A],at=Re.filter((mt)=>!xe.rules.includes(mt)),ct=[...xe.rules,...at];if(ct.length>pe)return ih();if(ct.length!==xe.rules.length)ce=!0,Pt(he.configTarget,{version:1,rules:ct,overrides:xe.overrides??{},transparent_wrappers:xe.transparent_wrappers??[]},void 0,ee._testAfterPolicyRename);let lt=await Zr(P,q,V,ee,new Set(at),new Set(at));if(!lt.ok)Zn(he.configTarget,ye);if(!lt.ok||!Xe)return lt;let ft=et.filter((mt,gt)=>at.includes(Re[gt]??""));return{...lt,add:{source:A,ref:Xe.ref,selected:et,added:ft,alreadyConfigured:et.filter((mt)=>!ft.includes(mt)),commits:at.length>0?[Xe.commit]:[]}}}catch(he){if(ce&&ne)try{Zn(ne.target,ne.content)}catch(ye){return Qn(ye)}return Qn(he)}}function th(P,A,q){if(!q&&A.rulebooks!==void 0)throw Error("--only can only select rulebooks from an owner/repo source");if(!q&&A.ref)throw Error(`--ref can only select a ref for an owner/repo source: ${P}`);if(A.rulebooks?.length===0)throw Error("--only requires at least one rulebook name");let V=A.rulebooks?.filter((ee)=>!l.test(ee))??[];if(V.length>0)throw Error(`Invalid rulebook names: ${V.join(", ")}`)}function nh(P,A){let q=A?[...new Set(A)]:P.names,V=q.filter((ee)=>!P.names.includes(ee));if(V.length>0)throw Error(`Rulebooks not found in ${P.source} at ${P.ref}: ${V.join(", ")}
|
|
273
273
|
Available rulebooks: ${P.names.join(", ")}`);return q}function rh(P,A,q){let V=`${A.source}#${A.ref}/${q}`;if(P.includes(V))return V;let ee=`${A.source}#${A.commit}/${q}`;return P.find((ne)=>ne===ee)}async function oh(P,A,q=zt()){if(P.length>pe)throw Error(fe);let V=[],ee=0,ne,ce=Array.from({length:Math.min(P.length,Wr.concurrency)},async()=>{while(!ne){let he=ee;if(he>=P.length)return;ee++;try{V[he]=await A(P[he],he,q.controller.signal)}catch(ye){if(!ne)ne={value:ye},ee=P.length,q.controller.abort(ye);return}}});if(await Promise.all(ce),ne)throw ne.value;return V}function ih(){return{ok:!1,errors:[fe],entries:[]}}function _i(P){return{cwd:P.cwd,userConfigDir:P.userConfigDir,userConfigPath:P.userConfigPath,projectConfigPath:P.projectConfigPath,global:P.global,only:P.only,refresh:P.refresh}}function sh(P){return{..._i(P),ref:P.ref,rulebooks:P.rulebooks}}function ah(P){return{..._i(P),deleteSource:P.deleteSource}}async function wd(P,A,q={}){try{return await lh(P,A,ah(q),{})}catch(V){return Qn(V)}}async function lh(P,A,q,V){let ee=Et(P,q),ne=m(ee.configTarget);if(ne.errors.length>0)return{ok:!1,errors:ne.errors,entries:[]};if(!ne.config)return{ok:!1,errors:[`No config found at ${ee.configPath}`],entries:[]};let ce=hd(ne.config.rules,A);if(!ce.ok)return ce.result;let he=q.deleteSource?ch(ee.configDir,ce.specs,ee.filesystemScope):{ok:!0,dirs:[]};if(!he.ok)return he.result;let ye=n(ee.configTarget);if(ye===null)return Qn(Error("Rules config is unavailable."));try{Pt(ee.configTarget,{version:1,rules:ne.config.rules.filter((Ie)=>!ce.specs.includes(Ie)),overrides:ne.config.overrides??{},transparent_wrappers:ne.config.transparent_wrappers??[]},void 0,V._testAfterPolicyRename)}catch(Ie){throw Zn(ee.configTarget,ye),Ie}let be=await Zr(P,q,zt(),V);if(!be.ok)return Zn(ee.configTarget,ye),be;let xe=dh(he.dirs,V,ee.filesystemScope);if(!xe.ok){Zn(ee.configTarget,ye);let Ie=await Zr(P,q,zt(),V);if(!Ie.ok)return{ok:!1,errors:[...xe.result.errors,...Ie.errors],entries:Ie.entries};return xe.result}return be}function ch(P,A,q){let V=A.flatMap((he)=>l.test(he)?[]:["--delete-source can only delete local rulebook sources"]),ee=A.map((he)=>Ai(P,he)),ne=V.length>0?[]:ee.flatMap((he)=>kd(he,q)),ce=[...V,...ne];return ce.length>0?{ok:!1,result:{ok:!1,errors:ce,entries:[]}}:{ok:!0,dirs:ee}}function kd(P,A){let q=zg(P),V=i(A,q),ee=le(V);if(!ee)return[`Local rulebook source directory not found: ${P}`];let ne=ee.find((ce)=>ce.name==="rulebook.json");if(!ne)return[`Local rulebook source directory is missing rulebook.json: ${P}`];if(ne.kind!=="file")throw new r(A.label);if(n(i(A,Ai(q,"rulebook.json"))),ee.length>1)return[`Local rulebook source directory contains extra files: ${P}. delete manually if you really want to remove the directory.`];return[]}function dh(P,A,q){let V=P.flatMap((ee)=>{try{if(!le(i(q,ee)))return[];let ne=kd(ee,q);if(ne.length>0)return ne;return uh(ee,A,q),[]}catch(ne){return[`Failed to delete local rulebook source ${ee}: ${ne instanceof Error?ne.message:String(ne)}`]}});return V.length>0?{ok:!1,result:{ok:!1,errors:V,entries:[]}}:{ok:!0}}function uh(P,A,q){if(A._testDeleteLocalSourceDir){A._testDeleteLocalSourceDir(P);return}F(i(q,Ai(P,ue))),nt(i(q,P))}function Zn(P,A){if(A===null){F(P);return}g(P,A)}function Qn(P){return{ok:!1,errors:[P instanceof Error?P.message:String(P)],entries:[]}}var ph=".safety-net.json",fh="~/.cc-safety-net/config.json";async function Rd(P,A){return[await Cd(P,{legacyPath:Hs({cwd:A.cwd}),configPath:M(A.cwd),defaultRulebookName:"project-rules",migratedFrom:ph,cleanup:A.cleanup,syncOptions:{cwd:A.cwd}}),await Cd(P,{legacyPath:lr(P),configPath:j(P),defaultRulebookName:"user-rules",migratedFrom:fh,cleanup:A.cleanup,syncOptions:{cwd:A.cwd,global:!0}})].every((V)=>V)?0:1}async function Cd(P,A){let q=Et(P,A.syncOptions),V=i(q.filesystemScope,A.legacyPath),ee=n(V);if(ee===null)return console.log(`No legacy config found at ${A.legacyPath}`),!0;let ne=gh(ee);if(!ne.ok){for(let et of ne.errors)console.error(et);return!1}let ce=m(q.configTarget);if(ce.errors.length>0){for(let et of ce.errors)console.error(et);return!1}let he=ce.config??{version:1,rules:[],overrides:{},transparent_wrappers:[]},ye=hh(xd(A.configPath),he.rules,A.defaultRulebookName,A.migratedFrom,q.filesystemScope),be=eo(xd(A.configPath),ye,"rulebook.json"),xe=i(q.filesystemScope,be),Ie=[Sd(q.configTarget),Sd(xe)],Xe=await mh(P,A,q.configTarget,xe,ye,ne.config.rules,he.rules.includes(ye)?he.rules:[...he.rules,ye],he.overrides??{},he.transparent_wrappers??[]);if(!Xe.ok){bh(Ie);for(let et of Xe.errors)console.error(et);return!1}if(!A.cleanup)return console.log(`Migrated legacy config at ${A.legacyPath}. Legacy file is no longer used.`),!0;if(!vh(q.configTarget,xe,ye,A.migratedFrom,ne.config.rules))return console.error(`Migration cleanup verification failed for ${A.legacyPath}`),!1;return F(V),console.log(`Deleted legacy config at ${A.legacyPath}`),!0}async function mh(P,A,q,V,ee,ne,ce,he,ye){try{return Pt(q,{version:1,rules:ce,overrides:he,transparent_wrappers:ye}),Pt(V,yh(ee,A.migratedFrom,ne)),await Qr(P,A.syncOptions)}catch(be){return{ok:!1,errors:[be instanceof Error?be.message:String(be)]}}}function gh(P){try{let A=JSON.parse(P),q=uo(A);if(q.errors.length>0)return{ok:!1,errors:q.errors};return{ok:!0,config:{version:1,rules:A.rules??[]}}}catch{return{ok:!1,errors:["Invalid JSON"]}}}function hh(P,A,q,V,ee){let ne=A.find((ce)=>Lh(i(ee,eo(P,ce,"rulebook.json")))===V);if(ne)return ne;if(n(i(ee,eo(P,q,"rulebook.json")))===null)return q;for(let ce=2;;ce++){let he=`${q}-${ce}`;if(n(i(ee,eo(P,he,"rulebook.json")))===null)return he}}function yh(P,A,q){return{rulebook_version:1,name:P,version:"1.0.0",description:"Migrated CC Safety Net rules.",author:"project",migrated_from:A,allowed_commands:[...new Set(q.map((V)=>V.command))],rules:q,tests:q.map((V)=>({command:[V.command,V.subcommand,V.block_args[0]].filter(Boolean).join(" "),expect:"blocked",rule:V.name}))}}function vh(P,A,q,V,ee){if(!m(P).config?.rules.includes(q))return!1;try{let ce=n(A);if(ce===null)return!1;let he=JSON.parse(ce);return he.migrated_from===V&&JSON.stringify(he.rules)===JSON.stringify(ee)}catch{return!1}}function Sd(P){return{target:P,content:n(P)}}function bh(P){for(let A of P){if(A.content===null){F(A.target);continue}g(A.target,A.content)}}function Lh(P){let A=n(P);if(A===null)return null;try{let q=JSON.parse(A);return typeof q.migrated_from==="string"?q.migrated_from:null}catch{return null}}import{mkdir as wh,readFile as kh,writeFile as xh}from"node:fs/promises";import{dirname as Ch,join as Sh}from"node:path";var Rh=86400000,Ph=604800000;async function Ed(P,A=Date.now()){if(P.env.get("CC_SAFETY_NET_NO_UPDATE_CHECK"))return null;let q=je(P);if(!q)return null;let V=Sh(q,".cc-safety-net","update-check.json"),ee=await Eh(V,A);if(!ee.lastCheck||A-ee.lastCheck>Rh){let he=await Wt();if(ee.lastCheck=A,he.latestVersion)ee.latestVersion=he.latestVersion;if(!await Pd(V,ee))return null;if(he.error)return null}let ne=ee.latestVersion,ce=wt();if(!ne||!yo(ne,ce))return null;if(ee.notifiedVersion===ne&&ee.notifiedAt!==void 0&&A-ee.notifiedAt<Ph)return null;if(ee.notifiedVersion=ne,ee.notifiedAt=A,!await Pd(V,ee))return null;return`UPDATE_AVAILABLE: cc-safety-net v${ne} is available (running v${ce}). Ask the user once whether to run \`npx -y cc-safety-net@latest update\`; continue the current task either way and do not raise this again.`}async function Eh(P,A){let q=await kh(P,"utf8").then((ne)=>JSON.parse(ne)).catch(()=>{return});if(!q||typeof q!=="object"||Array.isArray(q))return{};let V=q,ee=(ne)=>typeof ne==="number"&&Number.isFinite(ne)&&ne<=A?ne:void 0;return{lastCheck:ee(V.lastCheck),latestVersion:typeof V.latestVersion==="string"?V.latestVersion:void 0,notifiedVersion:typeof V.notifiedVersion==="string"?V.notifiedVersion:void 0,notifiedAt:ee(V.notifiedAt)}}async function Pd(P,A){return wh(Ch(P),{recursive:!0,mode:448}).then(()=>xh(P,JSON.stringify(A),{mode:384})).then(()=>!0).catch(()=>!1)}import{join as Dh,resolve as Ti}from"node:path";var Dd="CC Safety Net Config",Ah="═".repeat(Dd.length),_h="https://raw.githubusercontent.com/kenryu42/cc-safety-net/main/assets/cc-safety-net.schema.json",Th=new Set(["rule.json","rule.lock","cache"]);function Ad(P,A={}){try{return Ih(P,A)}catch(q){if(q instanceof r)return console.error(q.message),1;throw q}}function Ih(P,A){let q=A.cwd??process.cwd(),V=J(P,{cwd:q}),ee=lr(P),ne=cs(q),ce=Ti(q,de),he=i(V.userScope,ee),ye=i(V.projectScope,ne),be=!1,xe=!1,Ie=[],Xe=[],et=$h(i(V.projectScope,ce));if(Fh(),n(V.userConfigTarget)!==null){let Re=Jt(V.userConfigTarget);if(Re.errors.push(...U(V.userConfigPath,V.userScope)),Ie.push({scope:"User",path:V.userConfigPath,result:Re,schema:"rules",target:V.userConfigTarget}),Re.errors.length>0)be=!0}if(n(he)!==null)if(xe=!0,n(V.userConfigTarget)!==null)Xe.push(to("user","cleanup"));else{let Re=po(he);if(Ie.push({scope:"User",path:ee,result:Re,schema:"legacy",inactive:!0,target:he}),Xe.push(to("user",Re.errors.length>0?"fix-or-delete":"migrate")),Re.errors.length>0)be=!0}if(n(V.projectConfigTarget)!==null){let Re=Jt(V.projectConfigTarget);if(Re.errors.push(...U(V.projectConfigPath,V.projectScope)),Ie.push({scope:"Project",path:Ti(V.projectConfigPath),result:Re,schema:"rules",target:V.projectConfigTarget}),Re.errors.length>0)be=!0;if(n(ye)!==null)xe=!0,Xe.push(to("project","cleanup"))}else if(n(ye)!==null){xe=!0,be=!0;let Re=po(ye);Ie.push({scope:"Project",path:Ti(ne),result:Re,schema:"legacy",inactive:!0,target:ye}),Xe.push(to("project",Re.errors.length>0?"fix-or-delete":"migrate"))}if(et?.result.errors.length)be=!0;if(Ie.length===0&&!et)return console.log(`
|
|
274
274
|
No config files found. Using built-in rules only.`),0;for(let Re of Ie)if(Re.inactive)jh(Re.scope,Re.path,Re.result);else if(Re.result.errors.length>0)Mh(Re.scope,Re.path,Re.result.errors);else{if(Re.schema==="rules"&&Gh(Re.target))console.log(`
|
|
275
275
|
Added $schema to ${Re.scope.toLowerCase()} config.`);Nh(Re.scope,Re.path,Re.result,Re.schema)}for(let Re of Xe)console.error(`
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{ye,P,A,be,c,xe,f,q,ne,ce,d,p}from"./chunks/index-av9c301x.js";import{ee}from"./chunks/index-pm924a1e.js";import{he,N}from"./chunks/index-fjkkpma1.js";import{join as Bc,resolve as ks}from"node:path";function vr(){return{"cc-safety-net":{description:"Operate CC Safety Net: explain blocks, rules, integrations, diagnostics",template:ee.slice(ee.indexOf("# CC Safety Net"))}}}var jc=new Set(["powershell","pwsh"]),qc=new Set(["bash","dash","ksh","sh","zsh"]);function Rs(e={}){return async({directory:t,homeDir:r})=>{let n=ks(t),o;return{config:async(s)=>{o=s;let a=vr(),u=s.command??{};s.command={...a,...u}},"tool.execute.before":async(s,a)=>{_n({configCwd:n,homeDir:r,tool:s.tool,sessionID:s.sessionID,toolInput:a.args,route:Jc(s.tool,An(o?.shell)),guardDependencies:e})}}}}function _n({configCwd:e,homeDir:t,expandHomeWorkdir:r=!1,tool:n,sessionID:o,toolInput:s,route:a,guardDependencies:u={}}){let l=t===void 0?c():{...c(),home:t},i=(h,I,m=e)=>{he(l,h,()=>o,{agent:"opencode",toolName:I,cwd:m}),_s(h)};if(typeof n!=="string"||n.trim()==="")i(P());let x;try{x=ne(s)}catch(h){if(!(h instanceof f))throw h;i(P({toolName:n}),n)}if(!ce(e)){i(A({directory:"session",problem:"unusable",cwd:e},{command:x,toolName:n}),n);return}let S=Gc(e,s,r?l.home:void 0);if(S===null){i(P({command:x,toolName:n}),n);return}if(typeof S!=="string"){i(A(S,{command:x,toolName:n}),n,S.cwd);return}let g=d(n,s,a,{configCwd:e,executionCwd:S},x??null);try{let h=N(l,g,{guard:{auditAllowed:xe(l.env),dependencies:u},audit:{agent:"opencode",getSessionId:()=>o}});Os(h)}catch(h){if(!(h instanceof p))throw h;if(h.stage==="policy-protection"||h.stage==="config-load"||h.stage==="secret-protection")throw h.cause;Os(h.evaluation);return}}function An(e,t=process.platform,r=process.env.SHELL){if(typeof e!=="string"&&t==="win32")return"powershell";let n=typeof e==="string"?e:r;if(typeof n!=="string")return"auto";let o=n.trim().split(/[\\/]/).at(-1)?.toLowerCase().replace(/\.exe$/,"");if(!o)return"auto";if(jc.has(o))return"powershell";if(qc.has(o))return"posix";return"auto"}function Jc(e,t){if(e==="bash")return{kind:"command",shell:t};return{kind:q(e)}}function Gc(e,t,r){if(!t||typeof t!=="object"||Array.isArray(t))return e;if(!Object.hasOwn(t,"workdir"))return e;let n=t.workdir;if(typeof n!=="string"||n.trim()==="")return null;let o=process.platform==="win32"?zc(n):n,s=o.startsWith("~/")||process.platform==="win32"&&o.startsWith("~\\"),a=r===void 0?o:o==="~"?r:s?Bc(r,o.slice(2)):o,u=ks(e,a);return ce(u)?u:{directory:"requested",problem:"unusable",cwd:n}}function zc(e){return e.replace(/^\/([a-zA-Z]):(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/cygdrive\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/mnt\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`)}function Os(e){let t=ye(e,{includeEvidence:!0});if(t)_s(t)}function _s(e){throw Error(be(e))}var pe=(e,t)=>{switch(t.length){case 0:return e;case 1:return t[0](e);case 2:return t[1](t[0](e));case 3:return t[2](t[1](t[0](e)));case 4:return t[3](t[2](t[1](t[0](e))));case 5:return t[4](t[3](t[2](t[1](t[0](e)))));case 6:return t[5](t[4](t[3](t[2](t[1](t[0](e))))));case 7:return t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))));case 8:return t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e))))))));case 9:return t[8](t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))))));default:{let r=e;for(let n=0,o=t.length;n<o;n++)r=t[n](r);return r}}},Kc={pipe(){return pe(this,arguments)}},jt=function(){function e(){}return e.prototype=Kc,e}();var C=function(e,t){if(typeof e==="function")return function(){return e(arguments)?t.apply(this,arguments):(r)=>t(r,...arguments)};switch(e){case 0:case 1:throw RangeError(`Invalid arity ${e}`);case 2:return function(r,n){if(arguments.length>=2)return t(r,n);return function(o){return t(o,r)}};case 3:return function(r,n,o){if(arguments.length>=3)return t(r,n,o);return function(s){return t(s,r,n)}};default:return function(){if(arguments.length>=e)return t.apply(this,arguments);let r=arguments;return function(n){return t(n,...r)}}}};var ue=(e)=>e;var As=(e)=>()=>e;var yt=As(void 0),Fs=yt;function nt(e){let t=new WeakMap;return(r)=>{let n=t.get(r);if(n!==void 0)return n;let o=e(r);return t.set(r,o),o}}function Tt(e){let t=new WeakMap;return(r)=>{let n=t.get(r);if(n!==void 0)return n;let o=e(r);return t.set(r,o),t.set(o,o),o}}var qt=(e)=>{let t=new Set(Reflect.ownKeys(e));if(e.constructor===Object)return t;if(e instanceof Error)t.delete("stack");let r=Object.getPrototypeOf(e),n=r;while(n!==null&&n!==Object.prototype){let o=Reflect.ownKeys(n);for(let s=0;s<o.length;s++)t.add(o[s]);n=Object.getPrototypeOf(n)}if(t.has("constructor")&&typeof e.constructor==="function"&&r===e.constructor.prototype)t.delete("constructor");return t},Jt=new WeakSet;function Gt(e){return typeof e==="string"}function Fn(e){return typeof e==="number"}function Ps(e){return typeof e==="symbol"}function Or(e){return Gt(e)||Fn(e)||Ps(e)}function Et(e){return typeof e==="function"}function Pn(e){return e!==void 0}function Dn(e){return e!=null}function Ds(e){return!0}function Un(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function kr(e){return typeof e==="object"&&e!==null||Et(e)}var _=C(2,(e,t)=>kr(e)&&(t in e));function Us(e){return e instanceof Error}var X="~effect/interfaces/Hash",D=(e)=>{switch(typeof e){case"number":return Fr(e);case"bigint":return K(e.toString(10));case"boolean":return K(String(e));case"symbol":return K(String(e));case"string":return K(e);case"undefined":return K("undefined");case"function":case"object":if(e===null)return K("null");else if(e instanceof Date){if(Number.isNaN(e.getTime()))return K("Invalid Date");return K(e.toISOString())}else if(e instanceof RegExp)return K(e.toString());else{if(Jt.has(e))return Rr(e);if(Mn.has(e))return Mn.get(e);let t=Yc(e,()=>{if(Wc(e))return e[X]();else if(typeof e==="function")return Rr(e);else if(e instanceof DataView)return _r(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));else if(Array.isArray(e)||ArrayBuffer.isView(e))return _r(e);else if(e instanceof Map)return Vc(e);else if(e instanceof Set)return Zc(e);return Hc(e)});return Mn.set(e,t),t}default:throw Error(`BUG: unhandled typeof ${typeof e} - please report an issue at https://github.com/Effect-TS/effect/issues`)}},Rr=(e)=>{if(!Nn.has(e))Nn.set(e,Fr(Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)));return Nn.get(e)},le=C(2,(e,t)=>e*53^t),Ar=(e)=>e&3221225471|e>>>1&1073741824,Wc=(e)=>_(e,X),Fr=(e)=>{if(e!==e)return K("NaN");if(e===1/0)return K("Infinity");if(e===-1/0)return K("-Infinity");let t=e|0;if(t!==e)t^=e*4294967295;while(e>4294967295)t^=e/=4294967295;return Ar(t)},K=(e)=>{let t=5381,r=e.length;while(r)t=t*33^e.charCodeAt(--r);return Ar(t)},$n=(e,t)=>{let r=12289;for(let n of t)r^=le(D(n),D(e[n]));return Ar(r)},Hc=(e)=>$n(e,qt(e)),Bn=(e,t)=>(r)=>{let n=e;for(let o of r)n^=t(o);return Ar(n)},_r=Bn(6151,D),Vc=Bn(K("Map"),([e,t])=>le(D(e),D(t))),Zc=Bn(K("Set"),D),Nn=new WeakMap,Mn=new WeakMap,Ln=new WeakSet;function Yc(e,t){if(Ln.has(e))return K("[Circular]");Ln.add(e);let r=t();return Ln.delete(e),r}var Q="~effect/interfaces/Equal";function V(){if(arguments.length===1)return(e)=>st(e,arguments[0]);return st(arguments[0],arguments[1])}function st(e,t){if(e===t)return!0;if(e==null||t==null)return!1;let r=typeof e;if(r!==typeof t)return!1;if(r==="number"&&e!==e&&t!==t)return!0;if(r!=="object"&&r!=="function")return!1;if(Jt.has(e)||Jt.has(t))return!1;return eu(e,t,Qc)}function Xc(e,t,r){let n=jn.has(e),o=qn.has(t);if(n&&o)return!0;if(n||o)return!1;jn.add(e),qn.add(t);let s=r();return jn.delete(e),qn.delete(t),s}var jn=new WeakSet,qn=new WeakSet;function Qc(e,t){if(D(e)!==D(t))return!1;else if(e instanceof Date){if(!(t instanceof Date))return!1;let s=e.getTime(),a=t.getTime();return s===a||Number.isNaN(s)&&Number.isNaN(a)}else if(e instanceof RegExp){if(!(t instanceof RegExp))return!1;return e.toString()===t.toString()}let r=Ms(e),n=Ms(t);if(r!==n)return!1;let o=r&&n;if(typeof e==="function"&&!o)return!1;return Xc(e,t,()=>{if(o)return e[Q](t);else if(Array.isArray(e)){if(!Array.isArray(t)||e.length!==t.length)return!1;return tu(e,t)}else if(ArrayBuffer.isView(e)){let s=e instanceof DataView;if(!ArrayBuffer.isView(t)||e.byteLength!==t.byteLength||s!==t instanceof DataView)return!1;if(s){let a=t;return Ns(new Uint8Array(e.buffer,e.byteOffset,e.byteLength),new Uint8Array(a.buffer,a.byteOffset,a.byteLength))}return Ns(e,t)}else if(e instanceof Map){if(!(t instanceof Map)||e.size!==t.size)return!1;return ou(e,t)}else if(e instanceof Set){if(!(t instanceof Set)||e.size!==t.size)return!1;return au(e,t)}return ru(e,t)})}function eu(e,t,r){let n=Pr.get(e);if(!n)n=new WeakMap,Pr.set(e,n);else if(n.has(t))return n.get(t);let o=r(e,t);n.set(t,o);let s=Pr.get(t);if(!s)s=new WeakMap,Pr.set(t,s);return s.set(e,o),o}var Pr=new WeakMap;function tu(e,t){for(let r=0;r<e.length;r++)if(!st(e[r],t[r]))return!1;return!0}function Ns(e,t){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0}function ru(e,t){let r=qt(e),n=qt(t);if(r.size!==n.size)return!1;for(let o of r)if(!n.has(o)||!st(e[o],t[o]))return!1;return!0}function nu(e,t){return function(n,o){let s=Array.from(o);for(let[a,u]of n){let l=!1;for(let i=0;i<s.length;i++){let[x,S]=s[i];if(e(a,x)&&t(u,S)){s[i]=s[s.length-1],s.pop(),l=!0;break}}if(!l)return!1}return!0}}var ou=nu(st,st);function su(e){return function(r,n){let o=Array.from(n);for(let s of r){let a=!1;for(let u=0;u<o.length;u++){let l=o[u];if(e(s,l)){o[u]=o[o.length-1],o.pop(),a=!0;break}}if(!a)return!1}return!0}}var au=su(st),Ms=(e)=>_(e,Q);var zt=Symbol.for("~effect/Redactable"),iu=(e)=>_(e,zt);function It(e){if(iu(e))return Jn(e);return e}function Jn(e){return e[zt](globalThis[vt]?.context??cu)}var vt="~effect/Fiber/currentFiber",Ls=new Map,cu={"~effect/Context":{},base:Ls,depth:0,mapUnsafe:Ls,pipe(){return pe(this,arguments)}};function M(e,t){let r=t?.space??0,n=new WeakSet,o=!r?"":typeof r==="number"?" ".repeat(r):r,s=(i)=>o.repeat(i),a=(i,x)=>{let S=i?.constructor;return S&&S!==Object.prototype.constructor&&S.name?`${S.name}(${x})`:x},u=(i)=>{try{return Reflect.ownKeys(i)}catch{return["[ownKeys threw]"]}};function l(i,x=0){if(typeof i==="string")return JSON.stringify(i);if(typeof i==="number"||i==null||typeof i==="boolean"||typeof i==="symbol")return String(i);if(typeof i==="bigint")return String(i)+"n";if(typeof i==="object"||typeof i==="function"){if(n.has(i))return uu;n.add(i);let S;if(zt in i)S=l(Jn(i),x);else if(Array.isArray(i))S=!o||i.length<=1?`[${i.map((y)=>l(y,x)).join(",")}]`:`[
|
|
1
|
+
import{ye,P,A,be,c,xe,f,q,ne,ce,d,p}from"./chunks/index-gj6afr0n.js";import{ee}from"./chunks/index-pm924a1e.js";import{he,N}from"./chunks/index-96rww7g0.js";import{join as Bc,resolve as ks}from"node:path";function vr(){return{"cc-safety-net":{description:"Operate CC Safety Net: explain blocks, rules, integrations, diagnostics",template:ee.slice(ee.indexOf("# CC Safety Net"))}}}var jc=new Set(["powershell","pwsh"]),qc=new Set(["bash","dash","ksh","sh","zsh"]);function Rs(e={}){return async({directory:t,homeDir:r})=>{let n=ks(t),o;return{config:async(s)=>{o=s;let a=vr(),u=s.command??{};s.command={...a,...u}},"tool.execute.before":async(s,a)=>{_n({configCwd:n,homeDir:r,tool:s.tool,sessionID:s.sessionID,toolInput:a.args,route:Jc(s.tool,An(o?.shell)),guardDependencies:e})}}}}function _n({configCwd:e,homeDir:t,expandHomeWorkdir:r=!1,tool:n,sessionID:o,toolInput:s,route:a,guardDependencies:u={}}){let l=t===void 0?c():{...c(),home:t},i=(h,I,m=e)=>{he(l,h,()=>o,{agent:"opencode",toolName:I,cwd:m}),_s(h)};if(typeof n!=="string"||n.trim()==="")i(P());let x;try{x=ne(s)}catch(h){if(!(h instanceof f))throw h;i(P({toolName:n}),n)}if(!ce(e)){i(A({directory:"session",problem:"unusable",cwd:e},{command:x,toolName:n}),n);return}let S=Gc(e,s,r?l.home:void 0);if(S===null){i(P({command:x,toolName:n}),n);return}if(typeof S!=="string"){i(A(S,{command:x,toolName:n}),n,S.cwd);return}let g=d(n,s,a,{configCwd:e,executionCwd:S},x??null);try{let h=N(l,g,{guard:{auditAllowed:xe(l.env),dependencies:u},audit:{agent:"opencode",getSessionId:()=>o}});Os(h)}catch(h){if(!(h instanceof p))throw h;if(h.stage==="policy-protection"||h.stage==="config-load"||h.stage==="secret-protection")throw h.cause;Os(h.evaluation);return}}function An(e,t=process.platform,r=process.env.SHELL){if(typeof e!=="string"&&t==="win32")return"powershell";let n=typeof e==="string"?e:r;if(typeof n!=="string")return"auto";let o=n.trim().split(/[\\/]/).at(-1)?.toLowerCase().replace(/\.exe$/,"");if(!o)return"auto";if(jc.has(o))return"powershell";if(qc.has(o))return"posix";return"auto"}function Jc(e,t){if(e==="bash")return{kind:"command",shell:t};return{kind:q(e)}}function Gc(e,t,r){if(!t||typeof t!=="object"||Array.isArray(t))return e;if(!Object.hasOwn(t,"workdir"))return e;let n=t.workdir;if(typeof n!=="string"||n.trim()==="")return null;let o=process.platform==="win32"?zc(n):n,s=o.startsWith("~/")||process.platform==="win32"&&o.startsWith("~\\"),a=r===void 0?o:o==="~"?r:s?Bc(r,o.slice(2)):o,u=ks(e,a);return ce(u)?u:{directory:"requested",problem:"unusable",cwd:n}}function zc(e){return e.replace(/^\/([a-zA-Z]):(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/cygdrive\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`).replace(/^\/mnt\/([a-zA-Z])(?:[\\/]|$)/,(r,n)=>`${n.toUpperCase()}:/`)}function Os(e){let t=ye(e,{includeEvidence:!0});if(t)_s(t)}function _s(e){throw Error(be(e))}var pe=(e,t)=>{switch(t.length){case 0:return e;case 1:return t[0](e);case 2:return t[1](t[0](e));case 3:return t[2](t[1](t[0](e)));case 4:return t[3](t[2](t[1](t[0](e))));case 5:return t[4](t[3](t[2](t[1](t[0](e)))));case 6:return t[5](t[4](t[3](t[2](t[1](t[0](e))))));case 7:return t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))));case 8:return t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e))))))));case 9:return t[8](t[7](t[6](t[5](t[4](t[3](t[2](t[1](t[0](e)))))))));default:{let r=e;for(let n=0,o=t.length;n<o;n++)r=t[n](r);return r}}},Kc={pipe(){return pe(this,arguments)}},jt=function(){function e(){}return e.prototype=Kc,e}();var C=function(e,t){if(typeof e==="function")return function(){return e(arguments)?t.apply(this,arguments):(r)=>t(r,...arguments)};switch(e){case 0:case 1:throw RangeError(`Invalid arity ${e}`);case 2:return function(r,n){if(arguments.length>=2)return t(r,n);return function(o){return t(o,r)}};case 3:return function(r,n,o){if(arguments.length>=3)return t(r,n,o);return function(s){return t(s,r,n)}};default:return function(){if(arguments.length>=e)return t.apply(this,arguments);let r=arguments;return function(n){return t(n,...r)}}}};var ue=(e)=>e;var As=(e)=>()=>e;var yt=As(void 0),Fs=yt;function nt(e){let t=new WeakMap;return(r)=>{let n=t.get(r);if(n!==void 0)return n;let o=e(r);return t.set(r,o),o}}function Tt(e){let t=new WeakMap;return(r)=>{let n=t.get(r);if(n!==void 0)return n;let o=e(r);return t.set(r,o),t.set(o,o),o}}var qt=(e)=>{let t=new Set(Reflect.ownKeys(e));if(e.constructor===Object)return t;if(e instanceof Error)t.delete("stack");let r=Object.getPrototypeOf(e),n=r;while(n!==null&&n!==Object.prototype){let o=Reflect.ownKeys(n);for(let s=0;s<o.length;s++)t.add(o[s]);n=Object.getPrototypeOf(n)}if(t.has("constructor")&&typeof e.constructor==="function"&&r===e.constructor.prototype)t.delete("constructor");return t},Jt=new WeakSet;function Gt(e){return typeof e==="string"}function Fn(e){return typeof e==="number"}function Ps(e){return typeof e==="symbol"}function Or(e){return Gt(e)||Fn(e)||Ps(e)}function Et(e){return typeof e==="function"}function Pn(e){return e!==void 0}function Dn(e){return e!=null}function Ds(e){return!0}function Un(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function kr(e){return typeof e==="object"&&e!==null||Et(e)}var _=C(2,(e,t)=>kr(e)&&(t in e));function Us(e){return e instanceof Error}var X="~effect/interfaces/Hash",D=(e)=>{switch(typeof e){case"number":return Fr(e);case"bigint":return K(e.toString(10));case"boolean":return K(String(e));case"symbol":return K(String(e));case"string":return K(e);case"undefined":return K("undefined");case"function":case"object":if(e===null)return K("null");else if(e instanceof Date){if(Number.isNaN(e.getTime()))return K("Invalid Date");return K(e.toISOString())}else if(e instanceof RegExp)return K(e.toString());else{if(Jt.has(e))return Rr(e);if(Mn.has(e))return Mn.get(e);let t=Yc(e,()=>{if(Wc(e))return e[X]();else if(typeof e==="function")return Rr(e);else if(e instanceof DataView)return _r(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));else if(Array.isArray(e)||ArrayBuffer.isView(e))return _r(e);else if(e instanceof Map)return Vc(e);else if(e instanceof Set)return Zc(e);return Hc(e)});return Mn.set(e,t),t}default:throw Error(`BUG: unhandled typeof ${typeof e} - please report an issue at https://github.com/Effect-TS/effect/issues`)}},Rr=(e)=>{if(!Nn.has(e))Nn.set(e,Fr(Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)));return Nn.get(e)},le=C(2,(e,t)=>e*53^t),Ar=(e)=>e&3221225471|e>>>1&1073741824,Wc=(e)=>_(e,X),Fr=(e)=>{if(e!==e)return K("NaN");if(e===1/0)return K("Infinity");if(e===-1/0)return K("-Infinity");let t=e|0;if(t!==e)t^=e*4294967295;while(e>4294967295)t^=e/=4294967295;return Ar(t)},K=(e)=>{let t=5381,r=e.length;while(r)t=t*33^e.charCodeAt(--r);return Ar(t)},$n=(e,t)=>{let r=12289;for(let n of t)r^=le(D(n),D(e[n]));return Ar(r)},Hc=(e)=>$n(e,qt(e)),Bn=(e,t)=>(r)=>{let n=e;for(let o of r)n^=t(o);return Ar(n)},_r=Bn(6151,D),Vc=Bn(K("Map"),([e,t])=>le(D(e),D(t))),Zc=Bn(K("Set"),D),Nn=new WeakMap,Mn=new WeakMap,Ln=new WeakSet;function Yc(e,t){if(Ln.has(e))return K("[Circular]");Ln.add(e);let r=t();return Ln.delete(e),r}var Q="~effect/interfaces/Equal";function V(){if(arguments.length===1)return(e)=>st(e,arguments[0]);return st(arguments[0],arguments[1])}function st(e,t){if(e===t)return!0;if(e==null||t==null)return!1;let r=typeof e;if(r!==typeof t)return!1;if(r==="number"&&e!==e&&t!==t)return!0;if(r!=="object"&&r!=="function")return!1;if(Jt.has(e)||Jt.has(t))return!1;return eu(e,t,Qc)}function Xc(e,t,r){let n=jn.has(e),o=qn.has(t);if(n&&o)return!0;if(n||o)return!1;jn.add(e),qn.add(t);let s=r();return jn.delete(e),qn.delete(t),s}var jn=new WeakSet,qn=new WeakSet;function Qc(e,t){if(D(e)!==D(t))return!1;else if(e instanceof Date){if(!(t instanceof Date))return!1;let s=e.getTime(),a=t.getTime();return s===a||Number.isNaN(s)&&Number.isNaN(a)}else if(e instanceof RegExp){if(!(t instanceof RegExp))return!1;return e.toString()===t.toString()}let r=Ms(e),n=Ms(t);if(r!==n)return!1;let o=r&&n;if(typeof e==="function"&&!o)return!1;return Xc(e,t,()=>{if(o)return e[Q](t);else if(Array.isArray(e)){if(!Array.isArray(t)||e.length!==t.length)return!1;return tu(e,t)}else if(ArrayBuffer.isView(e)){let s=e instanceof DataView;if(!ArrayBuffer.isView(t)||e.byteLength!==t.byteLength||s!==t instanceof DataView)return!1;if(s){let a=t;return Ns(new Uint8Array(e.buffer,e.byteOffset,e.byteLength),new Uint8Array(a.buffer,a.byteOffset,a.byteLength))}return Ns(e,t)}else if(e instanceof Map){if(!(t instanceof Map)||e.size!==t.size)return!1;return ou(e,t)}else if(e instanceof Set){if(!(t instanceof Set)||e.size!==t.size)return!1;return au(e,t)}return ru(e,t)})}function eu(e,t,r){let n=Pr.get(e);if(!n)n=new WeakMap,Pr.set(e,n);else if(n.has(t))return n.get(t);let o=r(e,t);n.set(t,o);let s=Pr.get(t);if(!s)s=new WeakMap,Pr.set(t,s);return s.set(e,o),o}var Pr=new WeakMap;function tu(e,t){for(let r=0;r<e.length;r++)if(!st(e[r],t[r]))return!1;return!0}function Ns(e,t){if(e.length!==t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0}function ru(e,t){let r=qt(e),n=qt(t);if(r.size!==n.size)return!1;for(let o of r)if(!n.has(o)||!st(e[o],t[o]))return!1;return!0}function nu(e,t){return function(n,o){let s=Array.from(o);for(let[a,u]of n){let l=!1;for(let i=0;i<s.length;i++){let[x,S]=s[i];if(e(a,x)&&t(u,S)){s[i]=s[s.length-1],s.pop(),l=!0;break}}if(!l)return!1}return!0}}var ou=nu(st,st);function su(e){return function(r,n){let o=Array.from(n);for(let s of r){let a=!1;for(let u=0;u<o.length;u++){let l=o[u];if(e(s,l)){o[u]=o[o.length-1],o.pop(),a=!0;break}}if(!a)return!1}return!0}}var au=su(st),Ms=(e)=>_(e,Q);var zt=Symbol.for("~effect/Redactable"),iu=(e)=>_(e,zt);function It(e){if(iu(e))return Jn(e);return e}function Jn(e){return e[zt](globalThis[vt]?.context??cu)}var vt="~effect/Fiber/currentFiber",Ls=new Map,cu={"~effect/Context":{},base:Ls,depth:0,mapUnsafe:Ls,pipe(){return pe(this,arguments)}};function M(e,t){let r=t?.space??0,n=new WeakSet,o=!r?"":typeof r==="number"?" ".repeat(r):r,s=(i)=>o.repeat(i),a=(i,x)=>{let S=i?.constructor;return S&&S!==Object.prototype.constructor&&S.name?`${S.name}(${x})`:x},u=(i)=>{try{return Reflect.ownKeys(i)}catch{return["[ownKeys threw]"]}};function l(i,x=0){if(typeof i==="string")return JSON.stringify(i);if(typeof i==="number"||i==null||typeof i==="boolean"||typeof i==="symbol")return String(i);if(typeof i==="bigint")return String(i)+"n";if(typeof i==="object"||typeof i==="function"){if(n.has(i))return uu;n.add(i);let S;if(zt in i)S=l(Jn(i),x);else if(Array.isArray(i))S=!o||i.length<=1?`[${i.map((y)=>l(y,x)).join(",")}]`:`[
|
|
2
2
|
${s(x+1)}${i.map((y)=>l(y,x+1)).join(`,
|
|
3
3
|
`+s(x+1))}
|
|
4
4
|
${s(x)}]`;else if(i instanceof Date)S=$s(i);else if(!t?.ignoreToString&&_(i,"toString")&&typeof i.toString==="function"&&i.toString!==Object.prototype.toString&&i.toString!==Array.prototype.toString){let y=lu(i);S=i instanceof Error&&i.cause?`${y} (cause: ${l(i.cause,x)})`:y}else if(Symbol.iterator in i)S=`${i.constructor.name}(${l(Array.from(i),x)})`;else{let y=u(i);if(!o||y.length<=1){let g=`{${y.map((h)=>`${Kt(h)}:${l(i[h],x)}`).join(",")}}`;S=a(i,g)}else{let g=`{
|