cc-safety-net 2.4.6 → 2.4.8

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.
@@ -1,2 +1,2 @@
1
- var Q="\n# CC Safety Net\n\nCC Safety Net hooks into coding agent CLIs (Claude Code, Codex, Cursor, Gemini CLI, and others)\nand blocks destructive commands and secret access before they run. The `cc-safety-net` CLI\ninspects and controls that protection. Run it as `npx -y cc-safety-net`.\n\n## Learn the current CLI\n\nThe installed CLI is the authority for command syntax. Do not guess flags.\n\n```bash\nnpx -y cc-safety-net --help\nnpx -y cc-safety-net help <command>\n```\n\nRun `npx -y cc-safety-net rule doc` and treat that output as the complete source of truth for\nrulebook schema, paths, GitHub sources, matching behavior, and validation.\n\nThese commands are read-only and safe to run for discovery: `--help`, `--version`, `status`,\n`doctor`, `logs` (without `--prune-legacy`), `explain`, `rule list`, `rule verify`, `rule doc`,\n`policy check`, `help`. Every other command mutates configuration or installed integrations; run\nthose only as part of a workflow below.\n\n## Core model\n\n- Built-in guards always apply. Custom rules only add restrictions; nothing in rule config can\n bypass built-in CC Safety Net protections.\n- Config files (`rule.json`) list rulebook sources. Rule definitions live in `rulebook.json`,\n not directly in `rule.json`.\n- Three scopes: user (all projects), project (current project only), and shareable GitHub\n rulebooks at `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in a repository.\n- Rulebooks are live files. The runtime reads each `rulebook.json` on every tool call, so a saved\n edit applies to the next command with no sync step.\n- `policy.json` sets the safety level, per-feature toggles, per-rule overrides, and path lists. It\n has two scopes: the project file `.cc-safety-net/policy.json`, committed and shared with the\n team, layered on the user file that applies to every project.\n- The session safety level is `standard`, `strict`, or `paranoid`, set per session with the\n `CC_SAFETY_NET_LEVEL` environment variable.\n\n## Choose the workflow\n\n- The user asks why a command was blocked, or shows a `BLOCKED by CC Safety Net` message:\n explain a decision.\n- The user thinks a block was wrong: triage a false positive.\n- The user wants to add, edit, disable, or migrate blocking rules: configure rules.\n- The user wants to change the safety level, toggle a protection, or adjust path lists: configure\n the policy.\n- The user wants CC Safety Net installed into or removed from an agent CLI: manage integrations.\n- A rule does not fire, or the user asks whether protection is working: diagnose.\n- The user asks how or why the analyzer behaves a certain way, beyond what `explain` and\n `rule doc` show: answer from the source.\n\n## Explain a decision\n\n1. Get the exact blocked command. If the user does not have it, find it with\n `npx -y cc-safety-net logs` (narrow with `--project .`, `--agent <name>`, or `--since <days>`).\n2. Pass the exact command to `npx -y cc-safety-net explain` as one literal argument. Prefer an\n argv-capable tool; when invoking through a shell, shell-escape the whole command as one\n argument. Never interpolate raw command text into double quotes: `$()`, backticks, and\n variables would expand before `explain` receives it. Add `--cwd <path>` when the decision\n depends on the working directory. Once received, `explain` analyzes the string and never\n executes it.\n3. Read the trace: how the command was split, which rule matched, and the RESULT status and\n reason. `explain` exits 0 for both allowed and blocked verdicts; read the verdict from the\n output, not the exit status.\n4. Report the reason in plain language. For a genuine hazard, suggest the safer alternative the\n reason names, such as `git stash` before `git reset --hard`.\n\n## Triage a false positive\n\n1. List recent suspect denials with `npx -y cc-safety-net logs --suspect --since 7`, or fetch\n one entry with `npx -y cc-safety-net logs --id <id>`.\n2. Reproduce the decision with `explain` and read which rule fired.\n3. If a custom rule fired, fix that rulebook: disable or reword it with an override, or edit the\n rule (see configure rules), then re-run `explain` to confirm the new verdict.\n4. If a built-in rule fired, no rule edit can relax it. Check the reason for a documented escape\n hatch, such as `CC_SAFETY_NET_WORKTREE=1` for local git discards in linked worktrees (git\n discards in a temp-root repository outside the workspace are already allowed), or\n `rule wrapper add` when a trusted transparent wrapper hid the real command from the analyzer.\n Pass the wrapper name as a separate argv value, or shell-escape it as one argument. If the\n user explicitly wants that built-in rule off, read its id from the `ruleId` field of\n `explain --json` and propose a per-rule policy override (see configure the policy). Otherwise\n explain the risk the rule guards against and suggest reporting the case at\n https://github.com/kenryu42/cc-safety-net/issues.\n\n## Configure rules\n\nUse information already provided in the user's prompt. Ask only when the scope, action, rule\nintent, merge behavior, or target command is unclear.\n\n1. Determine the requested scope from the prompt when possible:\n - User: applies to all projects.\n - Project: applies only to the current project.\n - GitHub: edits or creates a shareable rulebook structure in the current repository.\n2. Determine whether to add a rule, edit a rule, disable a rule, override a reason, trust a\n transparent wrapper, migrate legacy rules, or explain custom rules from the prompt when\n possible.\n3. Inspect existing configs before modifying installed local rules:\n - Run `npx -y cc-safety-net rule verify`\n - Run `npx -y cc-safety-net rule list`\n4. Inspect relevant project files only when the user asks for rule suggestions or the requested\n rule depends on project context. Look at manifests, scripts, task runners, CI, infrastructure,\n database, migration, and deployment files that explain risky commands.\n5. Convert the request into valid CC Safety Net JSON using `rule doc`.\n - For User or Project scope, add or edit the selected local `rule.json` and\n `<rulebook-name>/rulebook.json`.\n - For GitHub scope, add or edit `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in the\n current repository.\n - Do not offer to add a GitHub source with `owner/repo`; installing rules from a GitHub\n source is outside this workflow.\n - If the user explicitly asks to install existing GitHub rulebooks instead of authoring them,\n use `npx -y cc-safety-net rule add owner/repo --only <rulebook...>`; omit `--only` only\n when they want every rulebook, and add `--ref <ref>` only when they name a non-default ref.\n `rule add --only <rulebook...>` with no source selects from the official\n `cc-safety-net/rulebooks` repository, whose curated rulebooks block destructive Terraform,\n AWS, gcloud, and Azure CLI operations; prefer installing one of those over authoring when it\n already covers the request.\n - For transparent wrappers, prefer `npx -y cc-safety-net rule wrapper add` with the trusted\n wrapper name passed as a separate argv value, or shell-escaped as one argument, over editing\n `rule.json` by hand.\n6. Preserve unrelated existing rulebook sources, overrides, and rulebooks. Preview proposed JSON\n before writing when creating a new rulebook, merging with existing config, or resolving\n ambiguity.\n7. For GitHub rules, ensure the repository layout is\n `.cc-safety-net/rules/<rulebook-name>/rulebook.json`, and ensure the source name, directory\n name, and rulebook `name` match exactly.\n8. Validate after edits:\n - User or Project rules: run `npx -y cc-safety-net rule verify` and `npx -y cc-safety-net rule\n list`. Both commands cover every scope, so neither takes `--global`.\n - Shareable GitHub rulebook-only edits: run `npx -y cc-safety-net rule verify`. Run `list` only\n if the rulebook is also installed in local `rule.json`.\n9. If validation fails, show the exact errors and make the smallest fix.\n10. Confirm the saved paths or GitHub rulebook path and summarize the added or updated rules.\n\nRule invariants:\n\n- Do not use legacy inline `.safety-net.json` or `~/.cc-safety-net/config.json` rules. Convert\n existing legacy files with `npx -y cc-safety-net rule migrate`.\n- Every rule command must be listed in `allowed_commands`. The `tests` fixtures are optional;\n `rule verify` evaluates `rulebook_version` 2 fixtures against the rulebook's own rules, and\n fixture commands are analyzer input that CC Safety Net never executes.\n- A blocked fixture, when present, must specify the expected `rule`, and that rule must exist in\n the rulebook.\n- Local source names are bare names such as `project-rules`; do not put filesystem paths in\n `rules`.\n- A saved rulebook is live. There is no pending state and nothing to run afterwards, so verify the\n edit rather than activating it.\n- A missing or invalid rulebook file makes that source inactive, and an unreadable or invalid\n `rule.json` makes every source in its scope inactive: those rules stop applying while other\n custom rules and built-in protections stay active. Fix the file named in the diagnostic.\n- A duplicate rulebook name keeps the first claim, user scope before project scope, and ignores\n the later rulebook.\n- `npx -y cc-safety-net rule add owner/repo` fetches remote rulebooks, validates them, and vendors\n each one into `<rulebook-name>/rulebook.json`; `npx -y cc-safety-net rule update [source]`\n re-fetches and overwrites those copies and prints what changed. The runtime never fetches, and a\n remote source with no vendored file reports that `rule update` has to vendor it first.\n- `rule sync` is deprecated: it only migrates lock and cache leftovers from an earlier version.\n Never run it as a validation or activation step.\n\n## Configure the policy\n\nBoth `policy.json` files are protected: you propose the change, the user applies it. Reading them\nis allowed, writing them is not.\n\n`policy.json` fields, all optional except `version: 1` (`policy check` reports every schema\nerror, so validate against it rather than guessing further fields):\n\n- `safety.level`: `standard`, `strict`, or `paranoid`. `safety.overrides`: booleans for\n `fail_closed`, `paranoid_rm`, and `paranoid_interpreters` that pin one capability apart from\n the level.\n- `workflow.worktree_mode`: boolean, allows local git discards in linked worktrees; discards in a\n temp-root repository outside the workspace need no toggle.\n- `destructive_command_protection` and `secret_protection`: an `enabled` boolean, and per-rule\n `overrides` mapping a built-in rule id (`git.reset-hard`, `secret.basename.env`) to `\"on\"` or\n `\"off\"`. Get the id for a blocked command from the `ruleId` field of `explain --json`.\n- `destructive_command_protection.allow_paths`: absolute or `~/` paths where recursive delete\n targets are permitted. `secret_protection.allow_paths`: exact user-managed paths exempted from\n secret protection, globs rejected. `secret_protection.deny_paths`: extra paths protected like\n built-in secrets.\n- `audit.retention_days`: days of audit history to keep, user scope only.\n\n1. Inspect the current state: `npx -y cc-safety-net status` for the effective policy and the file\n paths it loaded, `npx -y cc-safety-net rule list` for custom rules, plus whatever project\n context the request depends on. Read an existing `policy.json` before proposing changes to it.\n2. Write the proposed policy JSON to an unprotected path such as `policy-proposal.json`. For\n project scope, set only the fields the team intends to control; an unset field inherits\n from the user policy, and `apply` writes only the fields the proposal sets.\n Applying replaces the target file, so the proposal is the complete policy, not a patch. Audit\n settings are user scope only; a project proposal cannot set them.\n3. Run `npx -y cc-safety-net policy check policy-proposal.json` and show the user the printed\n diff. Add `--global` to target the user policy instead of the project one. Fix every reported\n error and re-check until it passes.\n4. Ask the user to run the apply in their own terminal and quote the exact command:\n `npx -y cc-safety-net policy apply policy-proposal.json` (with `--global` when that is the\n scope). It confirms interactively, there is no `--yes` flag, and agent invocations of\n `policy apply` are blocked by design, so never run it, wrap it, or write the file yourself.\n5. Once the user confirms they applied it, run `npx -y cc-safety-net status` and report the\n effective policy, including any project scope deltas it prints.\n\n## Manage integrations\n\n1. Run `npx -y cc-safety-net doctor` first. It reports each supported platform as detected,\n configured, and verified, and names outdated installs with the exact repair command.\n2. Install with an explicit target flag, such as `npx -y cc-safety-net install --claude-code`.\n Run `npx -y cc-safety-net help install` for the full target list. Bare `install` opens an\n interactive picker; leave that for the user's own terminal.\n3. Run `npx -y cc-safety-net@latest update` to update every installed integration at once.\n4. Uninstall only when the user explicitly asks to remove protection, with the matching target\n flag.\n5. After any install, update, or uninstall, run `doctor` again and confirm the affected platform\n rows read as verified.\n\n## Diagnose\n\n1. `npx -y cc-safety-net status` shows what the runtime enforces right now, including a degraded\n `policy.json` that `rule list` does not report.\n2. `npx -y cc-safety-net doctor` verifies the installation: platform detection and hook config,\n a synthetic guard self-test, and configuration scopes. Use `--json` when parsing the result.\n3. When a custom rule does not fire, run in order: `rule verify`, `rule list`, then re-test the\n command with `explain`.\n\n## Answer from the source\n\nFor questions the CLI output cannot settle, such as why the analyzer treats a construct a\ncertain way or whether a gap is a known limitation, read the source code of the installed\nversion.\n\n1. Get `<version>` from `npx -y cc-safety-net --version`.\n2. Locate the repository. Plugin installs ship the full repository, and this skill file lives\n at `<repo>/skills/cc-safety-net/SKILL.md` inside it, so the repository root is two\n directories above the skill file. Use the candidate only if its `package.json` has\n `\"name\": \"cc-safety-net\"` and version `<version>`, and a `src/` directory exists next to it.\n If the package version differs, run `doctor` to report the outdated integration, then treat\n the candidate as unavailable and continue to the next step.\n3. If no matching local root exists (skill-only installs, a mismatched plugin, or guidance\n without a file path), resolve the immutable commit recorded with the published package using\n `npm view \"cc-safety-net@<version>\" gitHead`. Require a 40-character lowercase hexadecimal\n commit and fetch that exact commit into a fresh owner-only temporary directory:\n\n ```bash\n set -euo pipefail\n git_head=$(npm view \"cc-safety-net@<version>\" gitHead)\n [[ $git_head =~ ^[0-9a-f]{40}$ ]] || { echo \"Invalid published gitHead\" >&2; exit 1; }\n source_dir=$(mktemp -d \"${TMPDIR:-/tmp}/cc-safety-net-v<version>-XXXXXXXX\")\n trap 'rm -rf -- \"$source_dir\"' EXIT\n chmod 700 \"$source_dir\"\n git -c init.templateDir= init \"$source_dir\"\n git -c core.hooksPath=/dev/null -C \"$source_dir\" fetch --depth 1 https://github.com/kenryu42/cc-safety-net \"$git_head\"\n git -c core.hooksPath=/dev/null -C \"$source_dir\" checkout --detach \"$git_head\"\n [[ $(git -C \"$source_dir\" rev-parse HEAD) == \"$git_head\" ]] || { echo \"Source checkout mismatch\" >&2; exit 1; }\n printf 'Source checkout: %s\\n' \"$source_dir\"\n trap - EXIT\n ```\n\n Never answer from `main`; it can contain unreleased behavior the installed version does not\n have.\n4. Read `docs/` first; `residual-risk.md` and `secret-protection-known-limitations.md` exist to\n answer whether something is a known gap. For behavior questions, continue into\n `src/analyzer`, `src/guards`, and `src/rules`.\n5. State in the answer which version the source came from. Treat the located source as\n read-only reference; do not edit, build, or run it.\n6. Remove a temporary checkout after the source inspection: `rm -rf -- \"<source_dir>\"`.\n\n## Safety rules\n\n- Help the user operate CC Safety Net, never evade it. Do not change levels, uninstall, edit\n config, or propose a policy that weakens protection to get a blocked command through unless the\n user explicitly asks for that outcome and understands what the block guards against.\n- Never run `hook`; it is the integration entry point that reads hook JSON from stdin, not a\n user-facing command.\n- `logs --prune-legacy` permanently deletes legacy logs. Run it only on an explicit request, and\n run it with `--dry-run` first.\n- `rule remove --delete-source` deletes the local source directory. Ask before using it.\n- Prefer `gui --no-open` and give the user the URL instead of opening a browser from a session.\n- If a command prints an `UPDATE_AVAILABLE:` line, ask the user once whether to run\n `npx -y cc-safety-net@latest update`, continue the workflow without waiting either way, and do\n not raise it again.\n";
1
+ var Q="\n# CC Safety Net\n\nCC Safety Net hooks into coding agent CLIs (Claude Code, Codex, Cursor, Gemini CLI, and others)\nand blocks destructive commands and secret access before they run. The `cc-safety-net` CLI\ninspects and controls that protection. Run it as `npx -y cc-safety-net`.\n\n## Learn the current CLI\n\nThe installed CLI is the authority for command syntax. Do not guess flags.\n\n```bash\nnpx -y cc-safety-net --help\nnpx -y cc-safety-net help <command>\n```\n\nRun `npx -y cc-safety-net rule doc` and treat that output as the complete source of truth for\nrulebook schema, paths, GitHub sources, matching behavior, and validation.\n\nThese commands are read-only and safe to run for discovery: `--help`, `--version`, `status`,\n`doctor`, `logs` (without `--prune-legacy`), `explain`, `rule list`, `rule verify`, `rule doc`,\n`policy check`, `help`. Every other command mutates configuration or installed integrations; run\nthose only as part of a workflow below.\n\n## Core model\n\n- Built-in guards always apply. Custom rules only add restrictions; nothing in rule config can\n bypass built-in CC Safety Net protections.\n- Config files (`rule.json`) list rulebook sources. Rule definitions live in `rulebook.json`,\n not directly in `rule.json`.\n- Three scopes: user (all projects), project (current project only), and shareable GitHub\n rulebooks at `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in a repository.\n- Rulebooks are live files. The runtime reads each `rulebook.json` on every tool call, so a saved\n edit applies to the next command with no sync step.\n- `policy.json` sets the safety level, per-feature toggles, per-rule overrides, and path lists. It\n has two scopes: the project file `.cc-safety-net/policy.json`, committed and shared with the\n team, layered on the user file that applies to every project.\n- The session safety level is `standard`, `strict`, or `paranoid`, set per session with the\n `CC_SAFETY_NET_LEVEL` environment variable.\n\n## Choose the workflow\n\n- The user asks why a command was blocked, or shows a `BLOCKED by CC Safety Net` message:\n explain a decision.\n- The user thinks a block was wrong: triage a false positive.\n- The user wants to add, edit, disable, or migrate blocking rules: configure rules.\n- The user wants to change the safety level, toggle a protection, or adjust path lists: configure\n the policy.\n- The user wants CC Safety Net installed into or removed from an agent CLI: manage integrations.\n- A rule does not fire, or the user asks whether protection is working: diagnose.\n- The user asks how or why the analyzer behaves a certain way, beyond what `explain` and\n `rule doc` show: answer from the source.\n\n## Explain a decision\n\n1. Get the exact blocked command. If the user does not have it, find it with\n `npx -y cc-safety-net logs` (narrow with `--project .`, `--agent <name>`, or `--since <days>`).\n2. Pass the exact command to `npx -y cc-safety-net explain` as one literal argument. Prefer an\n argv-capable tool; when invoking through a shell, shell-escape the whole command as one\n argument. Never interpolate raw command text into double quotes: `$()`, backticks, and\n variables would expand before `explain` receives it. Add `--cwd <path>` when the decision\n depends on the working directory. Once received, `explain` analyzes the string and never\n executes it.\n3. Read the trace: how the command was split, which rule matched, and the RESULT status and\n reason. `explain` exits 0 for both allowed and blocked verdicts; read the verdict from the\n output, not the exit status.\n4. Report the reason in plain language. For a genuine hazard, suggest the safer alternative the\n reason names, such as `git stash` before `git reset --hard`.\n\n## Triage a false positive\n\n1. List recent suspect denials with `npx -y cc-safety-net logs --suspect --since 7`, or fetch\n one entry with `npx -y cc-safety-net logs --id <id>`.\n2. Reproduce the decision with `explain` and read which rule fired.\n3. If a custom rule fired, fix that rulebook: disable or reword it with an override, or edit the\n rule (see configure rules), then re-run `explain` to confirm the new verdict.\n4. If a built-in rule fired, no rule edit can relax it. Check the reason for a documented escape\n hatch, such as `CC_SAFETY_NET_WORKTREE=1` for local git discards in linked worktrees (git\n discards in a temp-root repository outside the workspace are already allowed), or\n `rule wrapper add` when a trusted transparent wrapper hid the real command from the analyzer.\n Pass the wrapper name as a separate argv value, or shell-escape it as one argument. If the\n user explicitly wants that built-in rule off, read its id from the `ruleId` field of\n `explain --json` and propose a per-rule policy override (see configure the policy). Otherwise\n explain the risk the rule guards against and suggest reporting the case at\n https://github.com/kenryu42/cc-safety-net/issues.\n\n## Configure rules\n\nUse information already provided in the user's prompt. Ask only when the scope, action, rule\nintent, merge behavior, or target command is unclear.\n\n1. Determine the requested scope from the prompt when possible:\n - User: applies to all projects.\n - Project: applies only to the current project.\n - GitHub: edits or creates a shareable rulebook structure in the current repository.\n2. Determine whether to add a rule, edit a rule, disable a rule, override a reason, trust a\n transparent wrapper, migrate legacy rules, or explain custom rules from the prompt when\n possible.\n3. Inspect existing configs before modifying installed local rules:\n - Run `npx -y cc-safety-net rule verify`\n - Run `npx -y cc-safety-net rule list`\n4. Inspect relevant project files only when the user asks for rule suggestions or the requested\n rule depends on project context. Look at manifests, scripts, task runners, CI, infrastructure,\n database, migration, and deployment files that explain risky commands.\n5. Convert the request into valid CC Safety Net JSON using `rule doc`.\n - For User or Project scope, add or edit the selected local `rule.json` and\n `<rulebook-name>/rulebook.json`.\n - For GitHub scope, add or edit `.cc-safety-net/rules/<rulebook-name>/rulebook.json` in the\n current repository.\n - Do not offer to add a GitHub source with `owner/repo`; installing rules from a GitHub\n source is outside this workflow.\n - If the user explicitly asks to install existing GitHub rulebooks instead of authoring them,\n use `npx -y cc-safety-net rule add owner/repo --only <rulebook...>`; omit `--only` only\n when they want every rulebook, and add `--ref <ref>` only when they name a non-default ref.\n `rule add --only <rulebook...>` with no source selects from the official\n `cc-safety-net/rulebooks` repository, whose curated rulebooks block destructive Terraform,\n AWS, gcloud, and Azure CLI operations; prefer installing one of those over authoring when it\n already covers the request.\n - For transparent wrappers, prefer `npx -y cc-safety-net rule wrapper add` with the trusted\n wrapper name passed as a separate argv value, or shell-escaped as one argument, over editing\n `rule.json` by hand.\n6. Preserve unrelated existing rulebook sources, overrides, and rulebooks. Preview proposed JSON\n before writing when creating a new rulebook, merging with existing config, or resolving\n ambiguity.\n7. For GitHub rules, ensure the repository layout is\n `.cc-safety-net/rules/<rulebook-name>/rulebook.json`, and ensure the source name, directory\n name, and rulebook `name` match exactly.\n8. Validate after edits:\n - User or Project rules: run `npx -y cc-safety-net rule verify` and `npx -y cc-safety-net rule\n list`. Both commands cover every scope, so neither takes `--global`.\n - Shareable GitHub rulebook-only edits: run `npx -y cc-safety-net rule verify`. Run `list` only\n if the rulebook is also installed in local `rule.json`.\n9. If validation fails, show the exact errors and make the smallest fix.\n10. Confirm the saved paths or GitHub rulebook path and summarize the added or updated rules.\n\nRule invariants:\n\n- Do not use legacy inline `.safety-net.json` or `~/.cc-safety-net/config.json` rules. Convert\n existing legacy files with `npx -y cc-safety-net rule migrate`.\n- Every rule command must be listed in `allowed_commands`. The `tests` fixtures are optional;\n `rule verify` evaluates `rulebook_version` 2 fixtures against the rulebook's own rules, and\n fixture commands are analyzer input that CC Safety Net never executes.\n- A blocked fixture, when present, must specify the expected `rule`, and that rule must exist in\n the rulebook.\n- Local source names are bare names such as `project-rules`; do not put filesystem paths in\n `rules`.\n- A saved rulebook is live. There is no pending state and nothing to run afterwards, so verify the\n edit rather than activating it.\n- A missing or invalid rulebook file makes that source inactive, and an unreadable or invalid\n `rule.json` makes every source in its scope inactive: those rules stop applying while other\n custom rules and built-in protections stay active. Fix the file named in the diagnostic.\n- A duplicate rulebook name keeps the first claim, user scope before project scope, and ignores\n the later rulebook.\n- `npx -y cc-safety-net rule add owner/repo` fetches remote rulebooks, validates them, and vendors\n each one into `<rulebook-name>/rulebook.json`; `npx -y cc-safety-net rule update [source]`\n re-fetches and overwrites those copies and prints what changed. The runtime never fetches, and a\n remote source with no vendored file reports that `rule update` has to vendor it first.\n- `rule sync` is deprecated: it only migrates lock and cache leftovers from an earlier version.\n Never run it as a validation or activation step.\n\n## Configure the policy\n\nBoth `policy.json` files are protected: you propose the change, the user applies it. Reading them\nis allowed, writing them is not.\n\n`policy.json` fields, all optional except `version: 1` (`policy check` reports every schema\nerror, so validate against it rather than guessing further fields):\n\n- `safety.level`: `standard`, `strict`, or `paranoid`. `safety.overrides`: booleans for\n `fail_closed`, `paranoid_rm`, and `paranoid_interpreters` that pin one capability apart from\n the level.\n- `workflow.worktree_mode`: boolean, allows local git discards in linked worktrees; discards in a\n temp-root repository outside the workspace need no toggle.\n- `destructive_command_protection` and `secret_protection`: an `enabled` boolean, and per-rule\n `overrides` mapping a built-in rule id (`git.reset-hard`, `secret.basename.env`) to `\"on\"` or\n `\"off\"`. Get the id for a blocked command from the `ruleId` field of `explain --json`.\n- `destructive_command_protection.allow_paths`: absolute or `~/` paths where recursive delete\n targets are permitted. `secret_protection.allow_paths`: exact user-managed files or directories,\n or a folder followed by `**/` and an exact file name (for example `~/code/**/.env.local`; the\n folder cannot be home or above it), exempted from built-in secret patterns. Deny paths and\n Coding CLI protections still win. Other glob forms are rejected.\n `secret_protection.deny_paths`: extra paths protected like built-in secrets.\n- `audit.retention_days`: days of audit history to keep, user scope only.\n\n1. Inspect the current state: `npx -y cc-safety-net status` for the effective policy and the file\n paths it loaded, `npx -y cc-safety-net rule list` for custom rules, plus whatever project\n context the request depends on. Read an existing `policy.json` before proposing changes to it.\n2. Write the proposed policy JSON to an unprotected path such as `policy-proposal.json`. For\n project scope, set only the fields the team intends to control; an unset field inherits\n from the user policy, and `apply` writes only the fields the proposal sets.\n Applying replaces the target file, so the proposal is the complete policy, not a patch. Audit\n settings are user scope only; a project proposal cannot set them.\n3. Run `npx -y cc-safety-net policy check policy-proposal.json` and show the user the printed\n diff. Add `--global` to target the user policy instead of the project one. Fix every reported\n error and re-check until it passes.\n4. Ask the user to run the apply in their own terminal and quote the exact command:\n `npx -y cc-safety-net policy apply policy-proposal.json` (with `--global` when that is the\n scope). It confirms interactively, there is no `--yes` flag, and agent invocations of\n `policy apply` are blocked by design, so never run it, wrap it, or write the file yourself.\n5. Once the user confirms they applied it, run `npx -y cc-safety-net status` and report the\n effective policy, including any project scope deltas it prints.\n\n## Manage integrations\n\n1. Run `npx -y cc-safety-net doctor` first. It reports each supported platform as detected,\n configured, and verified, and names outdated installs with the exact repair command.\n2. Install with an explicit target flag, such as `npx -y cc-safety-net install --claude-code`.\n Run `npx -y cc-safety-net help install` for the full target list. Bare `install` opens an\n interactive picker; leave that for the user's own terminal.\n3. Run `npx -y cc-safety-net@latest update` to update every installed integration at once.\n4. Uninstall only when the user explicitly asks to remove protection, with the matching target\n flag.\n5. After any install, update, or uninstall, run `doctor` again and confirm the affected platform\n rows read as verified.\n\n## Diagnose\n\n1. `npx -y cc-safety-net status` shows what the runtime enforces right now, including a degraded\n `policy.json` that `rule list` does not report.\n2. `npx -y cc-safety-net doctor` verifies the installation: platform detection and hook config,\n a synthetic guard self-test, and configuration scopes. Use `--json` when parsing the result.\n3. When a custom rule does not fire, run in order: `rule verify`, `rule list`, then re-test the\n command with `explain`.\n\n## Answer from the source\n\nFor questions the CLI output cannot settle, such as why the analyzer treats a construct a\ncertain way or whether a gap is a known limitation, read the source code of the installed\nversion.\n\n1. Get `<version>` from `npx -y cc-safety-net --version`.\n2. Locate the repository. Plugin installs ship the full repository, and this skill file lives\n at `<repo>/skills/cc-safety-net/SKILL.md` inside it, so the repository root is two\n directories above the skill file. Use the candidate only if its `package.json` has\n `\"name\": \"cc-safety-net\"` and version `<version>`, and a `src/` directory exists next to it.\n If the package version differs, run `doctor` to report the outdated integration, then treat\n the candidate as unavailable and continue to the next step.\n3. If no matching local root exists (skill-only installs, a mismatched plugin, or guidance\n without a file path), resolve the immutable commit recorded with the published package using\n `npm view \"cc-safety-net@<version>\" gitHead`. Require a 40-character lowercase hexadecimal\n commit and fetch that exact commit into a fresh owner-only temporary directory:\n\n ```bash\n set -euo pipefail\n git_head=$(npm view \"cc-safety-net@<version>\" gitHead)\n [[ $git_head =~ ^[0-9a-f]{40}$ ]] || { echo \"Invalid published gitHead\" >&2; exit 1; }\n source_dir=$(mktemp -d \"${TMPDIR:-/tmp}/cc-safety-net-v<version>-XXXXXXXX\")\n trap 'rm -rf -- \"$source_dir\"' EXIT\n chmod 700 \"$source_dir\"\n git -c init.templateDir= init \"$source_dir\"\n git -c core.hooksPath=/dev/null -C \"$source_dir\" fetch --depth 1 https://github.com/kenryu42/cc-safety-net \"$git_head\"\n git -c core.hooksPath=/dev/null -C \"$source_dir\" checkout --detach \"$git_head\"\n [[ $(git -C \"$source_dir\" rev-parse HEAD) == \"$git_head\" ]] || { echo \"Source checkout mismatch\" >&2; exit 1; }\n printf 'Source checkout: %s\\n' \"$source_dir\"\n trap - EXIT\n ```\n\n Never answer from `main`; it can contain unreleased behavior the installed version does not\n have.\n4. Read `docs/` first; `residual-risk.md` and `secret-protection-known-limitations.md` exist to\n answer whether something is a known gap. For behavior questions, continue into\n `src/analyzer`, `src/guards`, and `src/rules`.\n5. State in the answer which version the source came from. Treat the located source as\n read-only reference; do not edit, build, or run it.\n6. Remove a temporary checkout after the source inspection: `rm -rf -- \"<source_dir>\"`.\n\n## Safety rules\n\n- Help the user operate CC Safety Net, never evade it. Do not change levels, uninstall, edit\n config, or propose a policy that weakens protection to get a blocked command through unless the\n user explicitly asks for that outcome and understands what the block guards against.\n- Never run `hook`; it is the integration entry point that reads hook JSON from stdin, not a\n user-facing command.\n- `logs --prune-legacy` permanently deletes legacy logs. Run it only on an explicit request, and\n run it with `--dry-run` first.\n- `rule remove --delete-source` deletes the local source directory. Ask before using it.\n- Prefer `gui --no-open` and give the user the URL instead of opening a browser from a session.\n- If a command prints an `UPDATE_AVAILABLE:` line, ask the user once whether to run\n `npx -y cc-safety-net@latest update`, continue the workflow without waiting either way, and do\n not raise it again.\n";
2
2
  export{Q};
@@ -1,4 +1,4 @@
1
- import{a,s,z,f,Re,u,Ie,y,p,W}from"./index-c989ehp9.js";import{appendFileSync as re,mkdirSync as oe}from"node:fs";import{isAbsolute as ie,join as I}from"node:path";import{lstatSync as C,readdirSync as j,readFileSync as U,rmdirSync as V,statSync as L,unlinkSync as K,utimesSync as X,writeFileSync as Y}from"node:fs";import{join as m}from"node:path";function ee(t,e={}){return Ie(u(t,e),t.home).policy.audit.retention_days}var S=86400000,Z=".last-prune",J=/^\d{4}-\d{2}$/,Q=/^((\d{4}-\d{2})-\d{2})-.+\.jsonl$/,G=(t)=>Math.floor(t/S);function B(t,e,r=()=>new Date){try{let n=r().getTime();if(!L(e,{throwIfNoEntry:!1})?.isDirectory())return;let o=m(e,Z),c=L(o,{throwIfNoEntry:!1})?.mtimeMs;if(c!==void 0&&G(c)===G(n))return;let i=n-ee(t)*S,d=new Date(n).toISOString().slice(0,7);for(let l of A(e)){if(l.isDirectory()){q(m(e,l.name),i,d);continue}if(l.isFile()&&l.name.endsWith(".jsonl"))te(m(e,l.name),i)}Y(o,"",{mode:384}),X(o,n/1000,n/1000)}catch{}}function q(t,e,r){for(let n of A(t)){if(!n.isDirectory()||!J.test(n.name))continue;let o=m(t,n.name);for(let c of A(o)){if(!c.isFile())continue;let i=Q.exec(c.name);if(!i||i[2]!==n.name)continue;let d=Date.parse(`${i[1]}T00:00:00.000Z`)+S;if(!Number.isFinite(d)||d>=e)continue;b(m(o,c.name))}if(n.name!==r)T(o)}T(t)}function te(t,e){try{let r=C(t);if(r.mtimeMs>=e)return;let n=U(t,"utf-8").split(`
2
- `).filter((o)=>o.trim()).map(ne);if(n.length===0)return;if(n.some((o)=>o===void 0||o>=e))return;if(C(t).mtimeMs!==r.mtimeMs)return;b(t)}catch{}}function ne(t){try{let e=JSON.parse(t).ts,r=typeof e==="string"?Date.parse(e):Number.NaN;return Number.isFinite(r)?r:void 0}catch{return}}function A(t){try{return j(t,{withFileTypes:!0,encoding:"utf8"})}catch{return[]}}function b(t){try{K(t)}catch{}}function T(t){try{V(t)}catch{}}var ae="2.4.6",ce=1e4,de=2000,ue=256,se=32768;function le(t){let e=t.trim();if(!e)return null;let r=e.replace(/[^A-Za-z0-9_.-]+/g,"_");if(r=r.replace(/^[._-]+|[._-]+$/g,"").slice(0,128),!r||r==="."||r==="..")return null;return r}function me(t){return(t??"").replace(/[^A-Za-z0-9]/g,"-").slice(0,180)||"no-cwd"}function _(t,e,r,n,o,c,i={}){let d=le(e);if(!d)return;let l=O(t);if(!l)return;try{let E=(i.now??(()=>new Date))().toISOString(),N=g(z(r),i.failureStage?Number.POSITIVE_INFINITY:ce),h=g(z(n),de),w=i.toolName?g(z(i.toolName),ue):void 0,v=c===null?void 0:g(z(c),se),F=I(l,me(v?.value??null),E.slice(0,7));oe(F,{recursive:!0,mode:448});let H=I(F,`${E.slice(0,10)}-${d}.jsonl`),P={ts:E,id:(i.createId??Re)(),v:ae,sessionId:d,decision:i.decision??"deny",agent:i.agent,shape:i.shape,level:i.level,configFallback:i.configFallback,toolName:w?.value,command:N.value,segment:h.value,...N.truncated||h.truncated||w?.truncated||v?.truncated?{truncated:!0}:{},reason:o,ruleId:i.ruleId,intent:i.intent,failureStage:i.failureStage,errorCode:i.errorCode,cwd:v?.value??null};re(H,`${JSON.stringify(P)}
1
+ import{a,s,z,f,Re,u,Ie,y,p,W}from"./index-fvtsm6ez.js";import{appendFileSync as re,mkdirSync as oe}from"node:fs";import{isAbsolute as ie,join as I}from"node:path";import{lstatSync as C,readdirSync as j,readFileSync as U,rmdirSync as V,statSync as L,unlinkSync as K,utimesSync as X,writeFileSync as Y}from"node:fs";import{join as m}from"node:path";function ee(t,e={}){return Ie(u(t,e),t.home).policy.audit.retention_days}var S=86400000,Z=".last-prune",J=/^\d{4}-\d{2}$/,Q=/^((\d{4}-\d{2})-\d{2})-.+\.jsonl$/,G=(t)=>Math.floor(t/S);function B(t,e,r=()=>new Date){try{let n=r().getTime();if(!L(e,{throwIfNoEntry:!1})?.isDirectory())return;let o=m(e,Z),c=L(o,{throwIfNoEntry:!1})?.mtimeMs;if(c!==void 0&&G(c)===G(n))return;let i=n-ee(t)*S,d=new Date(n).toISOString().slice(0,7);for(let l of A(e)){if(l.isDirectory()){q(m(e,l.name),i,d);continue}if(l.isFile()&&l.name.endsWith(".jsonl"))te(m(e,l.name),i)}Y(o,"",{mode:384}),X(o,n/1000,n/1000)}catch{}}function q(t,e,r){for(let n of A(t)){if(!n.isDirectory()||!J.test(n.name))continue;let o=m(t,n.name);for(let c of A(o)){if(!c.isFile())continue;let i=Q.exec(c.name);if(!i||i[2]!==n.name)continue;let d=Date.parse(`${i[1]}T00:00:00.000Z`)+S;if(!Number.isFinite(d)||d>=e)continue;b(m(o,c.name))}if(n.name!==r)T(o)}T(t)}function te(t,e){try{let r=C(t);if(r.mtimeMs>=e)return;let n=U(t,"utf-8").split(`
2
+ `).filter((o)=>o.trim()).map(ne);if(n.length===0)return;if(n.some((o)=>o===void 0||o>=e))return;if(C(t).mtimeMs!==r.mtimeMs)return;b(t)}catch{}}function ne(t){try{let e=JSON.parse(t).ts,r=typeof e==="string"?Date.parse(e):Number.NaN;return Number.isFinite(r)?r:void 0}catch{return}}function A(t){try{return j(t,{withFileTypes:!0,encoding:"utf8"})}catch{return[]}}function b(t){try{K(t)}catch{}}function T(t){try{V(t)}catch{}}var ae="2.4.8",ce=1e4,de=2000,ue=256,se=32768;function le(t){let e=t.trim();if(!e)return null;let r=e.replace(/[^A-Za-z0-9_.-]+/g,"_");if(r=r.replace(/^[._-]+|[._-]+$/g,"").slice(0,128),!r||r==="."||r==="..")return null;return r}function me(t){return(t??"").replace(/[^A-Za-z0-9]/g,"-").slice(0,180)||"no-cwd"}function _(t,e,r,n,o,c,i={}){let d=le(e);if(!d)return;let l=O(t);if(!l)return;try{let E=(i.now??(()=>new Date))().toISOString(),N=g(z(r),i.failureStage?Number.POSITIVE_INFINITY:ce),h=g(z(n),de),w=i.toolName?g(z(i.toolName),ue):void 0,v=c===null?void 0:g(z(c),se),F=I(l,me(v?.value??null),E.slice(0,7));oe(F,{recursive:!0,mode:448});let H=I(F,`${E.slice(0,10)}-${d}.jsonl`),P={ts:E,id:(i.createId??Re)(),v:ae,sessionId:d,decision:i.decision??"deny",agent:i.agent,shape:i.shape,level:i.level,configFallback:i.configFallback,toolName:w?.value,command:N.value,segment:h.value,...N.truncated||h.truncated||w?.truncated||v?.truncated?{truncated:!0}:{},reason:o,ruleId:i.ruleId,intent:i.intent,failureStage:i.failureStage,errorCode:i.errorCode,cwd:v?.value??null};re(H,`${JSON.stringify(P)}
3
3
  `,{encoding:"utf-8",mode:384}),B(t,l,i.now)}catch{}}function g(t,e){return{value:t.slice(0,e),truncated:t.length>e}}function je(t){let e=t.env.get("CC_SAFETY_NET_AUDIT_HOME");if(t.env.get("NODE_ENV")==="test"&&!e)return null;let r=e||t.home;return r&&ie(r)?r:null}function O(t){let e=je(t);return e?I(e,".cc-safety-net","logs"):null}function x(t,e,r,n=!0,o){if(e.decision.kind==="allow"){if(!r||t.route.kind!=="command")return;let d=k(t);return{decision:"allow",command:d,segment:d,reason:"allowed",cwd:t.context.executionCwd,toolName:t.toolName,level:e.level,...e.configFallback?{configFallback:!0}:{}}}let c=e.decision.evidence,i=c?.command??(n?k(t):"");return{decision:"deny",command:i,segment:c?.segment??i,reason:e.decision.reason,cwd:t.context.executionCwd,toolName:t.toolName,level:e.level,...e.configFallback?{configFallback:!0}:{},ruleId:e.decision.ruleId,intent:e.decision.intent,failureStage:o?.stage,errorCode:o?.errorCode}}function k(t){return"command"in t?t.command??"":""}function M(t,e,r,n){if(!e)return;let o;try{o=r()}catch{return}if(typeof o!=="string"||!o.trim())return;_(t,o,e.command,e.segment,e.reason,e.cwd,{decision:e.decision,agent:n.agent,shape:n.shape,level:e.level,configFallback:e.configFallback,toolName:e.toolName,ruleId:e.ruleId,intent:e.intent,failureStage:e.failureStage,errorCode:e.errorCode})}function he(t,e,r,n){let o;try{o=r()}catch{return}if(typeof o!=="string"||!o.trim())return;_(t,o,e.command??"",e.segment??e.command??"",e.reason,n.cwd??null,{decision:"deny",agent:n.agent,shape:n.shape,toolName:n.toolName??e.toolName,ruleId:e.ruleId,intent:e.intent})}function D(t,e,r){try{let n=W(e,{environment:t,...r.guard});return R(t,e,n,r),n}catch(n){if(!(n instanceof p))throw n;throw R(t,e,n.evaluation,r,!(n.cause instanceof f),{stage:n.stage,errorCode:fe(n.cause)}),n}}function R(t,e,r,n,o=!0,c){M(t,x(e,r,n.guard?.auditAllowed??!1,o,c),n.audit.getSessionId,{agent:n.audit.agent,shape:n.audit.shape})}function fe(t){if(t instanceof s)return a[t.kind].errorCode;if(t instanceof f)return"tool-input-limit";if(t instanceof y)return"structural-shell-syntax-limit";return"unexpected-error"}
4
4
  export{ee,B,je,O,he,D};
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,N,tt,nt,l,ue,de,rt,A,q,oe,S,ot,F,b,Se,M,u,K,Be,ze,I,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,J,j,Pe,me,U,Ke,G,Y,Z,ae,Je,Ye,C,H,Ae,Oe,De,E,Ne,Ee,Fe,h,t,X,ge,y,x,_,st,Me,$e,p,W}from"./chunks/index-c989ehp9.js";import{ee,B,je,O,D}from"./chunks/index-4jcxaxr5.js";var Ud=["-h","--help"];function xt(P,V){let z=Object.entries(P.booleans??{}),Q=Object.entries(P.values??{}),te=Object.entries(P.lists??{}),ne=Object.fromEntries(z.map(([Xe])=>[Xe,!1])),ce={},he=Object.fromEntries(te.map(([Xe])=>[Xe,[]])),ye=[],be=[],xe=!1,Ie=-1;for(let[Xe,et]of V.entries()){if(Xe<=Ie)continue;if(et==="--"){ye.push(...V.slice(Xe+1));break}if(Ud.includes(et)){xe=!0;continue}let Re=z.find(([,lt])=>lt.includes(et));if(Re){ne[Re[0]]=!0;continue}let at=Q.find(([,lt])=>lt.includes(et));if(at){let lt=V[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=te.find(([,lt])=>lt.includes(et));if(ct){let lt=V.slice(Xe+1),pt=lt.findIndex((gt)=>gt.startsWith("-")),mt=lt.slice(0,pt===-1?lt.length:pt);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(...V.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 Ht(P){for(let V of P)console.error(V);return P.length>0}import{readdirSync as Wd,statSync as Ni,unlinkSync as Kd}from"node:fs";import{basename as Mi,dirname as Yd,isAbsolute as Zd,join as Xd,relative as Qd,resolve as eu,sep as tu}from"node:path";var Oi=(P)=>{let V=Date.now()-new Date(P).getTime();if(!Number.isFinite(V))return"";let z=Math.floor(V/60000),Q=Math.floor(z/60),te=Math.floor(Q/24);if(te>0)return`${te}d ago`;if(Q>0)return`${Q}h ago`;if(z>0)return`${z}m ago`;return"just now"},Qr=(P)=>{let V=(P??"").trim().split(/\s+/).filter((te)=>te&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(te)),z=V[0]?.split("/").pop();if(!z)return null;let Q=V[1];return Q&&/^[a-z][a-z0-9-]*$/.test(Q)?`${z} ${Q}`:z};function Fi(P){let V=(te)=>`${te.sessionId}
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,N,tt,nt,l,ue,de,rt,A,q,oe,S,ot,F,b,Se,M,u,K,Be,ze,I,w,T,pe,fe,Le,it,m,e,Te,we,Ve,qe,ie,Ce,se,_e,J,j,Pe,me,U,Ke,G,Y,Z,ae,Je,Ye,C,H,Ae,Oe,De,E,Ne,Ee,Fe,h,t,X,ge,y,x,_,st,Me,$e,p,W}from"./chunks/index-fvtsm6ez.js";import{ee,B,je,O,D}from"./chunks/index-tfdjszbb.js";var Ud=["-h","--help"];function xt(P,V){let z=Object.entries(P.booleans??{}),Q=Object.entries(P.values??{}),te=Object.entries(P.lists??{}),ne=Object.fromEntries(z.map(([Xe])=>[Xe,!1])),ce={},he=Object.fromEntries(te.map(([Xe])=>[Xe,[]])),ye=[],be=[],xe=!1,Ie=-1;for(let[Xe,et]of V.entries()){if(Xe<=Ie)continue;if(et==="--"){ye.push(...V.slice(Xe+1));break}if(Ud.includes(et)){xe=!0;continue}let Re=z.find(([,lt])=>lt.includes(et));if(Re){ne[Re[0]]=!0;continue}let at=Q.find(([,lt])=>lt.includes(et));if(at){let lt=V[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=te.find(([,lt])=>lt.includes(et));if(ct){let lt=V.slice(Xe+1),pt=lt.findIndex((gt)=>gt.startsWith("-")),mt=lt.slice(0,pt===-1?lt.length:pt);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(...V.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 Ht(P){for(let V of P)console.error(V);return P.length>0}import{readdirSync as Wd,statSync as Ni,unlinkSync as Kd}from"node:fs";import{basename as Mi,dirname as Yd,isAbsolute as Zd,join as Xd,relative as Qd,resolve as eu,sep as tu}from"node:path";var Oi=(P)=>{let V=Date.now()-new Date(P).getTime();if(!Number.isFinite(V))return"";let z=Math.floor(V/60000),Q=Math.floor(z/60),te=Math.floor(Q/24);if(te>0)return`${te}d ago`;if(Q>0)return`${Q}h ago`;if(z>0)return`${z}m ago`;return"just now"},Qr=(P)=>{let V=(P??"").trim().split(/\s+/).filter((te)=>te&&!/^[A-Za-z_][A-Za-z0-9_]*=/.test(te)),z=V[0]?.split("/").pop();if(!z)return null;let Q=V[1];return Q&&/^[a-z][a-z0-9-]*$/.test(Q)?`${z} ${Q}`:z};function Fi(P){let V=(te)=>`${te.sessionId}
2
2
  ${Qr(te.segment||te.command)}`,z=P.filter((te)=>te.decision!=="allow"),Q=z.filter((te)=>te.sessionId).reduce((te,ne)=>te.set(V(ne),(te.get(V(ne))??0)+1),new Map);return new Set(z.filter((te)=>te.failureStage||(Q.get(V(te))??0)>=2))}import{existsSync as Gd,readdirSync as Bd,readFileSync as qd}from"node:fs";import{join as Vd}from"node:path";function Yt(P,V){try{return Bd(P,{withFileTypes:!0,encoding:"utf8"}).flatMap((z)=>{let Q=Vd(P,z.name);if(z.isDirectory())return Yt(Q,V);if(z.name.endsWith(".jsonl"))return[Q];return[]})}catch{if(V&&Gd(P))V.count++;return[]}}var zd=["segment","reason","sessionId","decision","agent","ruleId","failureStage"];function Jd(P){if(!P||typeof P!=="object"||Array.isArray(P))return!1;let V=P;if(typeof V.ts!=="string"||typeof V.command!=="string")return!1;return zd.every((z)=>V[z]===void 0||typeof V[z]==="string")}function pn(P,V){try{return qd(P,"utf-8").split(`
3
3
  `).filter(Boolean).flatMap((z)=>{try{let Q=JSON.parse(z);if(!Jd(Q)){if(V)V.count++;return[]}return[Q]}catch{if(V)V.count++;return[]}})}catch{if(V)V.count++;return[]}}function Ct(P){return Array.from(P,(V)=>{let z=V.charCodeAt(0);if(z<=31||z>=127&&z<=159)return`\\x${z.toString(16).padStart(2,"0")}`;return V}).join("")}function nu(P,V){let z=ee(P),Q=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"]}},V);if(Ht(Q.errors))return null;if(Q.values.id!==void 0&&!/^[a-f0-9]{16}$/.test(Q.values.id))return console.error("--id must be 16 hexadecimal characters"),null;let te=Q.values.limit===void 0?20:ji(Q.values.limit);if(te===null)return console.error("--limit must be a positive number"),null;let ne=Q.values.since===void 0?Math.min(30,z):ji(Q.values.since);if(ne===null||ne>z)return console.error(`--since must be a positive number of days no greater than ${z}`),null;let ce={limit:te,limitExplicit:Q.values.limit!==void 0,since:ne,sinceExplicit:Q.values.since!==void 0,all:Q.flags.all,json:Q.flags.json,suspect:Q.flags.suspect,pruneLegacy:Q.flags.pruneLegacy,dryRun:Q.flags.dryRun,id:Q.values.id,agent:Q.values.agent,rule:Q.values.rule,session:Q.values.session,project:Q.values.project===void 0?void 0:eu(Q.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 Hi(P,V,z={}){let Q=nu(P,V);if(!Q)return 1;let te=z.logsDir??O(P);if(Q.pruneLegacy)return ru(te,Q.json,Q.dryRun);if(!te)return console.log(Q.json?"[]":Q.id?`No retained audit log entry found for id ${Ct(Q.id)}.`:"No audit log entries found."),0;B(P,te);let ne={count:0},ce=Yt(te,ne).flatMap((Ie)=>pn(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(Q.id)return au(ce,Q,z.timeZone);let he=Date.now()-Q.since*24*60*60*1000,ye=ce.filter((Ie)=>lu(Ie,Q,te,he)),be=Q.suspect?Fi(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,Q.limit);if(Q.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(uu(Ie.entry,z.timeZone));return 0}function ru(P,V,z){let Q=P?iu(P).map((he)=>Xd(P,he)):[];if(z)return ou(Q,V);let te=[],ne=0,ce=0;for(let he of Q){let ye=Ni(he,{throwIfNoEntry:!1})?.size??0,be=su(he);if(be){te.push(`${Mi(he)}: ${be}`);continue}ne++,ce+=ye}if(V)return console.log(JSON.stringify({removedFiles:ne,removedBytes:ce,failedFiles:te.length})),te.length===0?0:1;console.log(ne===0&&te.length===0?"No legacy audit log files found.":`Removed ${ne} legacy audit log ${ne===1?"file":"files"} (${Ui(ce)}).`);for(let he of te)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 te.length===0?0:1}function ou(P,V){let z=P.reduce((Q,te)=>Q+(Ni(te,{throwIfNoEntry:!1})?.size??0),0);if(V)return console.log(JSON.stringify({dryRun:!0,files:P.length,bytes:z})),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"} (${Ui(z)}).`),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 iu(P){try{return Wd(P,{withFileTypes:!0}).filter((V)=>V.isFile()&&V.name.endsWith(".jsonl")).map((V)=>V.name)}catch{return[]}}function su(P){try{return Kd(P),null}catch(V){return V instanceof Error?V.message:String(V)}}function Ui(P){let V=["B","KiB","MiB","GiB"],z=Math.min(Math.floor(Math.log2(Math.max(P,1))/10),V.length-1);return`${Math.round(P/1024**z*10)/10} ${V[z]}`}function au(P,V,z){let Q=P.filter((ne)=>ne.entry.id===V.id);if(Q.length>1)return console.error(`Multiple audit log entries found for id ${Ct(V.id??"")}.`),1;if(V.json)return console.log(JSON.stringify(Q.map((ne)=>ne.entry),null,2)),0;let te=Q[0];if(!te)return console.log(`No retained audit log entry found for id ${Ct(V.id??"")}.`),0;return console.log(pu(te.entry,z)),0}function lu(P,V,z,Q){if(!V.all&&P.entry.decision==="allow")return!1;if(Date.parse(P.entry.ts)<Q)return!1;if(V.agent!==void 0&&P.entry.agent!==V.agent)return!1;if(V.rule!==void 0&&P.entry.ruleId!==V.rule)return!1;if(V.session!==void 0&&!cu(P,z,V.session))return!1;if(V.project!==void 0&&!du(P.entry.cwd,V.project))return!1;return!0}function cu(P,V,z){if(P.entry.sessionId===z)return!0;return Yd(P.file)===V&&Mi(P.file,".jsonl")===z}function du(P,V){if(!P)return!1;let z=Qd(V,P);return z!==".."&&!z.startsWith(`..${tu}`)&&!Zd(z)}function uu(P,V){let z=Ct(P.id??"-"),Q=Ct(P.decision??"deny"),te=P.cwd?` [${Ct(P.cwd)}]`:"",ne=P.segment||P.command,ce=ne===P.command?"":"↳ ",he=ne.length>50?`${ne.slice(0,50)}…`:ne;return`${z.padEnd(16)} ${Ct(Gi(P.ts,V))} ${Q.padEnd(5)} ${Ct(P.agent??"-").padEnd(15)} ${Ct(P.ruleId??"-").padEnd(20)} ${ce}${Ct(he)}${te}`}function pu(P,V){let z=(te)=>Ct(te===void 0||te===null||te===""?"-":te),Q=P.shape?`${P.agent??"-"} (shape: ${P.shape})`:P.agent??"-";return[`id: ${z(P.id)}`,`ts: ${z(Gi(P.ts,V))}`,`decision: ${z(P.decision)}`,`agent: ${z(Q)}`,`level: ${z(P.level)}`,`tool: ${z(P.toolName)}`,`rule: ${z(P.ruleId)}`,`intent: ${z(P.intent)}`,`stage: ${z(P.failureStage)}`,`error: ${z(P.errorCode)}`,`session: ${z(P.sessionId)}`,`cwd: ${z(P.cwd)}`,`version: ${z(P.v)}`,`truncated: ${z(P.truncated===!0?"yes":void 0)}`,`reason: ${z(P.reason)}`,`command: ${z(P.command)}`,`segment: ${z(P.segment)}`].join(`
4
4
  `)}function Gi(P,V){let z=new Date(P);if(Number.isNaN(z.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:V}).format(z)}function ji(P){let V=Number(P);return Number.isFinite(V)&&V>0?V:null}var Bi={name:"doctor",aliases:["--doctor"],description:"Run diagnostic checks to verify installation and configuration",usage:"doctor [options]",options:[{flags:"--json",description:"Output diagnostics as JSON"},{flags:"--skip-update-check",description:"Skip npm registry version check"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net doctor","cc-safety-net doctor --json","cc-safety-net doctor --skip-update-check"]};var qi={name:"explain",description:"Show step-by-step analysis trace of how a command would be analyzed",usage:"explain [options] <command>",argument:"<command>",options:[{flags:"--json",description:"Output analysis as JSON"},{flags:"--cwd",argument:"<path>",description:"Use custom working directory"},{flags:"-h, --help",description:"Show this help"}],examples:['cc-safety-net explain "git reset --hard"','cc-safety-net explain --json "rm -rf /"','cc-safety-net explain --cwd /tmp "git status"']};var Vi={name:"gui",description:"Open the local policy editor GUI",usage:"gui [options]",options:[{flags:"--no-open",description:"Print the URL without opening a browser"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net gui","cc-safety-net gui --no-open"]};var Yn=[{id:"antigravity-cli",displayName:"Antigravity CLI",doctorOrder:3,runtime:{order:1,flags:["-ac","--agy-cli"],description:"Run as Antigravity CLI PreToolUse hook",legacyTopLevelFlags:[]},install:{order:2,flag:"--agy-cli",artifactKind:"hook config",probeCommand:["agy","--version"]}},{id:"claude-code",displayName:"Claude Code",doctorOrder:1,runtime:{order:2,displayName:"Coding CLI",flags:["-cc","--coding-cli"],legacyFlags:["--claude-code"],description:"Run as Coding CLI PreToolUse hook",legacyTopLevelFlags:["-cc","--claude-code"]},install:{order:3,flag:"--claude-code",artifactKind:"plugin",probeCommand:["claude","--version"]}},{id:"codex",displayName:"Codex",doctorOrder:4,runtime:{order:3,flags:["-cx","--codex"],description:"Run as a Codex PreToolUse hook",legacyTopLevelFlags:[]},install:{order:4,flag:"--codex",artifactKind:"plugin",probeCommand:["codex","--version"]}},{id:"copilot-cli",displayName:"GitHub Copilot CLI",doctorOrder:7,runtime:{order:6,flags:["-cp","--copilot-cli"],description:"Run as GitHub Copilot CLI PreToolUse hook",legacyTopLevelFlags:["-cp","--copilot-cli"]},install:{order:7,flag:"--copilot-cli",artifactKind:"plugin",probeCommand:["copilot","--binary-version"]}},{id:"gemini-cli",displayName:"Gemini CLI",doctorOrder:6,runtime:{order:5,flags:["-gc","--gemini-cli"],description:"Run as Gemini CLI BeforeTool hook",legacyTopLevelFlags:["-gc","--gemini-cli"]},install:{order:6,flag:"--gemini-cli",artifactKind:"extension",probeCommand:["gemini","--version"]}},{id:"grok-build",displayName:"Grok Build",doctorOrder:8,runtime:{order:7,flags:["-gb","--grok-build"],description:"Run as Grok Build PreToolUse hook",legacyTopLevelFlags:[]},install:{order:8,flag:"--grok-build",artifactKind:"hook config",probeCommand:["grok","--version"]}},{id:"hermes-agent",displayName:"Hermes Agent",doctorOrder:9,runtime:{order:8,flags:["-ha","--hermes-agent"],description:"Run as Hermes Agent pre_tool_call hook",legacyTopLevelFlags:[]},install:{order:9,flag:"--hermes-agent",artifactKind:"plugin",probeCommand:["hermes","--version"]}},{id:"kimi-code",displayName:"Kimi Code",doctorOrder:10,runtime:{order:9,flags:["-kc","--kimi-code"],description:"Run as Kimi Code PreToolUse hook",legacyTopLevelFlags:[]},install:{order:10,flag:"--kimi-code",artifactKind:"hook config",probeCommand:["kimi","--version"]}},{id:"openclaw",displayName:"OpenClaw",doctorOrder:11,install:{order:11,flag:"--openclaw",artifactKind:"plugin",probeCommand:["openclaw","--version"]}},{id:"opencode",displayName:"OpenCode",doctorOrder:12,install:{order:12,flag:"--opencode",artifactKind:"plugin",probeCommand:["opencode","--version"]}},{id:"pi",displayName:"Pi",doctorOrder:13,install:{order:13,flag:"--pi",artifactKind:"package",probeCommand:["pi","--version"]}},{id:"cursor",displayName:"Cursor",doctorOrder:5,runtime:{order:4,flags:["-cu","--cursor"],description:"Run as Cursor preToolUse hook",legacyTopLevelFlags:[]},install:{order:5,flag:"--cursor",artifactKind:"hook config",probeCommand:["cursor","--version"]}},{id:"amp",displayName:"Amp Code",doctorOrder:2,install:{order:1,flag:"--amp",artifactKind:"plugin",probeCommand:["amp","--version"]}}],Zn=Yn.slice().sort((P,V)=>P.doctorOrder-V.doctorOrder).map((P)=>P.id),Sn=Yn.filter((P)=>("runtime"in P)).slice().sort((P,V)=>P.runtime.order-V.runtime.order).map((P)=>({id:P.id,displayName:"displayName"in P.runtime?P.runtime.displayName:P.displayName,flags:P.runtime.flags,legacyFlags:"legacyFlags"in P.runtime?P.runtime.legacyFlags:[],description:P.runtime.description,legacyTopLevelFlags:P.runtime.legacyTopLevelFlags})),$t=Yn.slice().sort((P,V)=>P.install.order-V.install.order).map((P)=>({id:P.id,...P.install})).map(({order:P,...V})=>V),fu=Object.fromEntries(Yn.map((P)=>[P.id,P.displayName]));function bt(P){return fu[P]}var mu=Sn.map((P)=>({flags:P.flags.join(", "),description:P.description})),gu=Sn.flatMap((P)=>P.flags.map((V)=>`cc-safety-net hook ${V}`)),zi={name:"hook",description:"Run as an agent CLI hook (reads JSON from stdin)",usage:"hook INTEGRATION_FLAG",options:[...mu,{flags:"-h, --help",description:"Show this help"}],examples:gu};var Ji={name:"install",description:"Install CC Safety Net into a coding agent CLI",usage:"install [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Install ${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}`)]},Wi={name:"uninstall",description:"Uninstall CC Safety Net from a coding agent CLI",usage:"uninstall [TARGET_FLAG]",options:[...$t.map((P)=>({flags:P.flag,description:`Uninstall ${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}`)]},Ki={name:"update",description:"Update every installed CC Safety Net integration to the latest version",usage:"update",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net update"]};var Yi={name:"logs",description:"Browse audit log entries recorded by hooks",usage:"logs [options]",options:[{flags:"--id",argument:"<id>",description:"Show one entry from retained history by its 16-character id (not guaranteed once it is older than the configured retention)"},{flags:"--limit",argument:"<n>",description:"Maximum entries to print",default:"20"},{flags:"--since",argument:"<days>",description:"Only include entries newer than this many days (max: the configured audit retention, 1-365)",default:"30"},{flags:"--agent",argument:"<name>",description:"Filter by agent name"},{flags:"--rule",argument:"<ruleId>",description:"Filter by rule id"},{flags:"--session",argument:"<id>",description:"Filter by session id"},{flags:"--project",argument:"<path>",description:"Filter by project path"},{flags:"--suspect",description:"Only denials that look like false positives"},{flags:"--all",description:"Include allow entries"},{flags:"--prune-legacy",description:"Permanently delete all legacy root-level logs; nested logs are untouched"},{flags:"--dry-run",description:"With --prune-legacy, report what would be deleted and delete nothing"},{flags:"--json",description:"Output entries as JSON"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net logs --id 3fa9c2d1a70e8b42","cc-safety-net logs --agent claude-code","cc-safety-net logs --project . --since 7","cc-safety-net logs --suspect --since 7","cc-safety-net logs --json","cc-safety-net logs --prune-legacy --dry-run","cc-safety-net logs --prune-legacy"]};var Xn={name:"policy",description:"Check and apply project or user policy proposals",usage:"policy <subcommand>",subcommands:[{usage:"check <file>",description:"Validate a policy proposal and print its diff"},{usage:"apply <file>",description:"Apply a proposal after confirming in a terminal"}],options:[{flags:"-g, --global",description:"Use the user-scope policy instead of the project one"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net policy check proposal.json","cc-safety-net policy apply proposal.json","cc-safety-net policy apply proposal.json --global"]};var eo=[{flags:"--ref",argument:"<ref>",description:"Use a branch, tag, or commit"},{flags:"--only",argument:"<rulebook...>",description:"Add only these repository rulebooks"},{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"-h, --help",description:"Show this help"}],to=["cc-safety-net rule add project-rules","cc-safety-net rule add acme/safety-rules","cc-safety-net rule add acme/safety-rules --only aws gcloud","cc-safety-net rule add acme/safety-rules --ref v2 --only aws","cc-safety-net rule add --only terraform aws"],fn={name:"rule",description:"Manage CC Safety Net rule config and rulebook sources",usage:"rule <subcommand>",subcommands:[{usage:"init [--example]",description:"Create inert rule config"},{usage:"add [source] [--ref <ref>] [--only <rulebook...>]",description:"Add rulebook sources and sync"},{usage:"remove <source>",description:"Remove a rulebook source and sync"},{usage:"update [source]",description:"Re-fetch and vendor remote rulebooks"},{usage:"sync",description:"Deprecated: migrate lock and cache leftovers"},{usage:"list",description:"List active rulebooks"},{usage:"wrapper add <command>",description:"Trust a transparent command wrapper"},{usage:"wrapper remove <command>",description:"Remove a transparent command wrapper"},{usage:"wrapper list",description:"List transparent command wrappers"},{usage:"migrate [--cleanup]",description:"Migrate legacy inline rules"},{usage:"doc",description:"Print the rulebook authoring guide"},{usage:"verify",description:"Validate rule config files"}],options:[{flags:"-g, --global",description:"Use user-scope rule config"},{flags:"--cleanup",description:"Delete legacy files after rule migrate verifies them"},{flags:"--delete-source",description:"Delete clean local source directory on remove"},{flags:"--example",description:"Create an inactive example rulebook with rule init"},...eo.slice(0,2),{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net rule init","cc-safety-net rule init --example","cc-safety-net rule wrapper add rtk",...to,"cc-safety-net rule update","cc-safety-net rule migrate --cleanup","cc-safety-net rule verify"]};var Zi={name:"status",description:"Show what the runtime is enforcing right now",usage:"status",options:[{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net status"]};var Xi={name:"statusline",description:"Print status line with mode indicators for shell integration",usage:"statusline --claude-code",options:[{flags:"-cc, --claude-code",description:"Print status line for Claude Code"},{flags:"-h, --help",description:"Show this help"}],examples:["cc-safety-net statusline -cc","cc-safety-net statusline --claude-code"]};var Qn=[Zi,Bi,Yi,qi,fn,Xn,Ji,Ki,Wi,zi,Vi,Xi];function hu(P){return P.aliases??[]}function er(P){let V=P.toLowerCase();return Qn.find((z)=>z.name.toLowerCase()===V||hu(z).some((Q)=>Q.toLowerCase()===V))}import{basename as yu}from"node:path";function tr(P,V=7,z=O(P)){let Q=Date.now()-V*24*60*60*1000,te=[],ne=new Set,ce=0,he,ye,be,xe;if(z)B(P,z);let Ie={count:0},Xe=z?Yt(z,Ie):[];for(let Re of Xe)for(let at of pn(Re,Ie)){if(at.decision==="allow")continue;let ct=new Date(at.ts).getTime();if(ct>=Q){if(ce++,ne.add(at.sessionId??yu(Re,".jsonl")),ye===void 0||ct<=ye)he=at.ts,ye=ct;if(xe===void 0||ct>xe)be=at.ts,xe=ct;vu(te,at,ct)}}let et=te.map((Re)=>({timestamp:Re.ts,command:Re.command,reason:Re.reason,relativeTime:Oi(new Date(Re.ts))}));return{totalBlocked:ce,sessionCount:ne.size,recentEntries:et,oldestEntry:he,newestEntry:be,unreadable:Ie.count}}function vu(P,V,z){let Q=P.findIndex((te)=>z>new Date(te.ts).getTime());if(Q===-1){if(P.length<3)P.push(V);return}if(P.splice(Q,0,V),P.length>3)P.pop()}import{dirname as Au}from"node:path";import{dirname as bu,join as Lu,resolve as wu}from"node:path";var ku="config.json";function Rt(P,V,z,Q){g(xu(P),`${JSON.stringify(V,null,2)}
@@ -18,7 +18,7 @@ ${Qr(te.segment||te.command)}`,z=P.filter((te)=>te.decision!=="allow"),Q=z.filte
18
18
  `)}function or(P){return Zt({rows:P})}function ys(P){let V=[];return V.push("System Info"),V.push(ep(P)),V.join(`
19
19
  `)}function ep(P){let V=["Component","Version"],z=(ne)=>{if(ne===null)return dt.dim("not found");return ne},te=[{label:"cc-safety-net",value:P.version},...Zn.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,z(ne.value)]);return Zt({headers:V,rows:te})}function vs(P){if(P.findings.length===0)return dt.green(`
20
20
  No findings from inspected doctor facts.`);let V={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},z=["error","warning","info"].filter((ne)=>V[ne]>0).map((ne)=>`${V[ne]} ${ne}`),Q=P.findings.length===1?"finding":"findings",te=`
21
- ${P.findings.length} ${Q}: ${z.join(", ")}.`;if(V.error>0)return dt.red(te);if(V.warning>0)return dt.yellow(te);return dt.blue(te)}import{lstatSync as tp}from"node:fs";import{dirname as so}from"node:path";function ao(P,V){try{let z=tp(V);if(z.isSymbolicLink())return{kind:P,path:V,status:"unsafe",issues:["symlink"]};if(!z.isDirectory())return{kind:P,path:V,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:V,status:"unknown",issues:[]};let Q=[...z.uid!==process.getuid()?["ownership"]:[],...(z.mode&18)!==0?["permissions"]:[]];return{kind:P,path:V,status:Q.length>0?"unsafe":"safe",issues:Q}}catch(z){if(typeof z==="object"&&z!==null&&"code"in z&&z.code==="ENOENT")return{kind:P,path:V,status:"not-applicable",issues:[]};return{kind:P,path:V,status:"unknown",issues:[]}}}function bs(P,V){let z=O(P);return{directories:[ao("policy",so(so(V))),ao("config",so(V)),...z?[ao("audit",z)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as np}from"node:child_process";import{existsSync as Ls}from"node:fs";import{delimiter as rp,extname as op,join as ip}from"node:path";import{stripVTControlCharacters as ws}from"node:util";var xs="2.4.6",sp=5000,ap="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function wt(){return xs}function lo(P,V){let z=P[V];if(z)return z;let Q=Object.keys(P).find((te)=>te.toLowerCase()===V.toLowerCase()&&!!P[te]);return Q?P[Q]:z}function lp(P){return(lo(P,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((V)=>V.length>0)}function cp(P,V){let z=op(P)?[P]:[...lp(V).map((Q)=>`${P}${Q}`),P];if(P.includes("/")||P.includes("\\"))return z.find((Q)=>Ls(Q))??P;return(lo(V,"PATH")??"").split(rp).flatMap((Q)=>z.map((te)=>ip(Q,te))).find((Q)=>Ls(Q))??P}function ks(P){if(!/[\s"&|<>^]/.test(P))return P;return`"${P.replace(/"/g,'""')}"`}function Xt(P,V){let[z,...Q]=P,te=V[ap]==="win32"?"win32":process.platform;if(!z||te!=="win32")return{cmd:z??"",args:Q};let ne=cp(z,V);if(!/\.(?:bat|cmd)$/i.test(ne))return{cmd:ne,args:Q};return{cmd:lo(V,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",ks(ne),...Q.map(ks)].join(" ")]}}var mn=async(P,V=sp)=>{let z=await dp(P,{timeoutMs:V});if(z.code!==0)return null;return ws(z.stdout).trim()||ws(z.stderr).trim()||null};function dp(P,V){let[z,...Q]=P;if(!z)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((te)=>{try{let ne=Xt([z,...Q],process.env),ce=np(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),te(Xe)},Ie=setTimeout(()=>{ce.kill(),xe({code:null,stdout:ye,stderr:be})},V.timeoutMs);ce.on("close",(Xe)=>{xe({code:Xe,stdout:ye,stderr:be})}),ce.on("error",()=>{xe({code:null,stdout:ye,stderr:be})})}catch{te({code:null,stdout:"",stderr:""})}})}function ir(P){if(!P)return null;let V=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(P);if(V)return V[1]??null;let z=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(P);if(z)return z[1]??null;return P.split(`
21
+ ${P.findings.length} ${Q}: ${z.join(", ")}.`;if(V.error>0)return dt.red(te);if(V.warning>0)return dt.yellow(te);return dt.blue(te)}import{lstatSync as tp}from"node:fs";import{dirname as so}from"node:path";function ao(P,V){try{let z=tp(V);if(z.isSymbolicLink())return{kind:P,path:V,status:"unsafe",issues:["symlink"]};if(!z.isDirectory())return{kind:P,path:V,status:"unsafe",issues:["not-directory"]};if(process.platform==="win32"||typeof process.getuid!=="function")return{kind:P,path:V,status:"unknown",issues:[]};let Q=[...z.uid!==process.getuid()?["ownership"]:[],...(z.mode&18)!==0?["permissions"]:[]];return{kind:P,path:V,status:Q.length>0?"unsafe":"safe",issues:Q}}catch(z){if(typeof z==="object"&&z!==null&&"code"in z&&z.code==="ENOENT")return{kind:P,path:V,status:"not-applicable",issues:[]};return{kind:P,path:V,status:"unknown",issues:[]}}}function bs(P,V){let z=O(P);return{directories:[ao("policy",so(so(V))),ao("config",so(V)),...z?[ao("audit",z)]:[{kind:"audit",status:"unknown",issues:[]}]]}}import{spawn as np}from"node:child_process";import{existsSync as Ls}from"node:fs";import{delimiter as rp,extname as op,join as ip}from"node:path";import{stripVTControlCharacters as ws}from"node:util";var xs="2.4.8",sp=5000,ap="_CC_SAFETY_NET_TEST_SPAWN_PLATFORM";function wt(){return xs}function lo(P,V){let z=P[V];if(z)return z;let Q=Object.keys(P).find((te)=>te.toLowerCase()===V.toLowerCase()&&!!P[te]);return Q?P[Q]:z}function lp(P){return(lo(P,"PATHEXT")||".COM;.EXE;.BAT;.CMD").split(";").filter((V)=>V.length>0)}function cp(P,V){let z=op(P)?[P]:[...lp(V).map((Q)=>`${P}${Q}`),P];if(P.includes("/")||P.includes("\\"))return z.find((Q)=>Ls(Q))??P;return(lo(V,"PATH")??"").split(rp).flatMap((Q)=>z.map((te)=>ip(Q,te))).find((Q)=>Ls(Q))??P}function ks(P){if(!/[\s"&|<>^]/.test(P))return P;return`"${P.replace(/"/g,'""')}"`}function Xt(P,V){let[z,...Q]=P,te=V[ap]==="win32"?"win32":process.platform;if(!z||te!=="win32")return{cmd:z??"",args:Q};let ne=cp(z,V);if(!/\.(?:bat|cmd)$/i.test(ne))return{cmd:ne,args:Q};return{cmd:lo(V,"COMSPEC")??"cmd.exe",args:["/d","/c",["call",ks(ne),...Q.map(ks)].join(" ")]}}var mn=async(P,V=sp)=>{let z=await dp(P,{timeoutMs:V});if(z.code!==0)return null;return ws(z.stdout).trim()||ws(z.stderr).trim()||null};function dp(P,V){let[z,...Q]=P;if(!z)return Promise.resolve({code:null,stdout:"",stderr:""});return new Promise((te)=>{try{let ne=Xt([z,...Q],process.env),ce=np(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),te(Xe)},Ie=setTimeout(()=>{ce.kill(),xe({code:null,stdout:ye,stderr:be})},V.timeoutMs);ce.on("close",(Xe)=>{xe({code:Xe,stdout:ye,stderr:be})}),ce.on("error",()=>{xe({code:null,stdout:ye,stderr:be})})}catch{te({code:null,stdout:"",stderr:""})}})}function ir(P){if(!P)return null;let V=/Claude Code\s+(\d+\.\d+\.\d+)/i.exec(P);if(V)return V[1]??null;let z=/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.]+)?)/i.exec(P);if(z)return z[1]??null;return P.split(`
22
22
  `)[0]?.trim()||null}async function sr(P=mn){let[V,z,Q,te,ne,ce]=await Promise.all([Promise.all($t.map(async(he)=>[he.id,ir(await P([...he.probeCommand]))])),P(["codex","plugin","list"],30000),P(["amp","plugins","list"],30000),P(["node","--version"]),P(["npm","--version"]),P(["bun","--version"])]);return{version:xs,versions:Object.fromEntries(V),codexPluginListOutput:z,ampPluginListOutput:Q,nodeVersion:ir(te),npmVersion:ir(ne),bunVersion:ir(ce),platform:`${process.platform} ${process.arch}`}}function co(P,V){if(V==="dev")return!1;let z=P.split(".").map(Number),Q=V.split(".").map(Number),[te=0,ne=0,ce=0]=z,[he=0,ye=0,be=0]=Q;if(te!==he)return te>he;if(ne!==ye)return ne>ye;return ce>be}async function Wt(){let P=wt(),V=new AbortController,z=setTimeout(()=>V.abort(),3000);try{let Q=await fetch("https://registry.npmjs.org/cc-safety-net/latest",{signal:V.signal});if(!Q.ok)return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:`npm registry returned ${Q.status}`};let te=await Q.json(),ne=co(te.version,P);return{currentVersion:P,latestVersion:te.version,updateAvailable:ne}}catch(Q){return{currentVersion:P,latestVersion:null,updateAvailable:!1,error:Q instanceof Error?Q.message:"Network error"}}finally{clearTimeout(z)}}import*as _s from"node:readline";var Ps=(P)=>`\x1B[${P}B`,up=(P)=>`\x1B[${P}A`;var Cs=["░","▒","▓","╱","╲","┃","━","┏","┓","┗","┛","╋"];function pp(P){return new Promise((V)=>setTimeout(V,P))}function fp(P,V,z){if(!z)return V(P);if(z.aborted)return Promise.resolve();return new Promise((Q,te)=>{let ne=()=>z.removeEventListener("abort",ce),ce=()=>{ne(),Q()};z.addEventListener("abort",ce,{once:!0}),V(P).then(()=>{ne(),Q()},(he)=>{ne(),te(he)})})}function ar(P){return Math.max(0,Math.min(1,P))}function gn(P){return Math.max(0,Math.min(255,Math.round(P)))}function uo(P){return P<=0.0031308?12.92*P:1.055*P**0.4166666666666667-0.055}function mp(P,V,z){let Q=z*Math.PI/180,te=V*Math.cos(Q),ne=V*Math.sin(Q),ce=(P+0.3963377774*te+0.2158037573*ne)**3,he=(P-0.1055613458*te-0.0638541728*ne)**3,ye=(P-0.0894841775*te-1.291485548*ne)**3;return{blue:gn(uo(ar(-0.0041960863*ce-0.7034186147*he+1.707614701*ye))*255),green:gn(uo(ar(-1.2684380046*ce+2.6097574011*he-0.3413193965*ye))*255),red:gn(uo(ar(4.0767416621*ce-3.3077115913*he+0.2309699292*ye))*255)}}function po(P,V){let z=(V*P*180/Math.PI%360+360)%360;return mp(0.72,0.15,z)}function Es(P,V=0.1){let z=po(V,P);return`\x1B[38;2;${z.red};${z.green};${z.blue}m`}function gp(P,V){return{blue:gn(P.blue+(255-P.blue)*V),green:gn(P.green+(255-P.green)*V),red:gn(P.red+(255-P.red)*V)}}function Ds(P,V,z){let Q=Math.imul(P+2654435769,2246822507)^Math.imul(V+3266489909,668265263)^Math.imul(z+374761393,2654435761),te=Q^Q>>>15,ne=Math.imul(te,739982445),ce=ne^ne>>>12,he=Math.imul(ce,695872825);return((he^he>>>15)>>>0)/4294967296}function hp(P,V,z){let Q=Math.floor(Ds(P,V,z)*Cs.length);return Cs[Q]??"░"}function Ss(P){let V=ar(P);return V*V*V*(V*(V*6-15)+10)}function yp(P){if(P.length===0)return"";let V=[],z=!1,Q="";for(let te of P){let ne=`${te.red};${te.green};${te.blue}`;if(te.bold!==z)V.push(te.bold?"\x1B[1m":"\x1B[22m"),z=te.bold;if(ne!==Q)V.push(`\x1B[38;2;${ne}m`),Q=ne;V.push(te.character)}return`${V.join("")}\x1B[22m\x1B[39m`}function vp(P,V,z,Q,te){return P.map((ne,ce)=>({...po(z,Q+V+ce/te),bold:!1,character:ne}))}function bp(P,V,z,Q,te,ne,ce,he){let ye=Math.max(1,Q*0.75),be=Math.min(1,z/ye),xe=te*Ss(be),Ie=Math.max(0,(z-ye)/Math.max(1,Q-ye)),Xe=(1-Ss(z/Q))*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 pt=po(ne,ce+V+lt/he+Xe),mt=lt+Ds(V,lt,7919)*2-1;if(mt>xe+2)return{...pt,bold:!1,character:" "};let gt=xe-mt,ut=0.8*Math.exp(-(gt*gt)/12.5),vt=Math.min(0.9,ut+et),Dt=!Re&&mt>xe-4;return{...gp(pt,vt),bold:vt>0.3,character:Dt?hp(V,lt,z):ct}})}function Rs(P){return`\x1B[?2026h${P.map((V,z)=>`\x1B8${z>0?Ps(z):""}${yp(V)}`).join("")}\x1B[?2026l`}async function fo(P,V={}){if(!P)return;let z=V.output??process.stdout,Q=V.sleep??pp,te=V.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;z.write(`\x1B[?25l${ne.length>1?`${`
24
24
  `.repeat(ne.length-1)}${up(ne.length-1)}`:""}\x1B7`);try{for(let xe=1;xe<=ye;xe+=1){if(V.signal?.aborted)break;z.write(Rs(ne.map((Ie,Xe)=>bp(Ie,Xe,xe,ye,ce,0.1,te,3)))),await fp(be,Q,V.signal)}}finally{if(z.write(Rs(ne.map((xe,Ie)=>vp(xe,Ie,0.1,te,3)))),z.write("\x1B8"),ne.length>1)z.write(Ps(ne.length-1));z.write(`
@@ -211,7 +211,7 @@ ${he()}`.trim())))}),ne.on("close",(xe)=>{if(clearTimeout(be),xe!==0){Q(Error($o
211
211
  `));return{path:te[0]??z??uf(P),alreadyInstalled:te.length>0}}function sl(P){let V=[],z=Dr(P.environment),Q=["opencode.json","opencode.jsonc"];for(let te of Q){let ne=hf(z,te);if(mf(ne))try{let ce=gf(ne,"utf-8"),he=At(ce),ye=JSON.parse(he);if(qo(ye))return{platform:"opencode",status:"configured",method:"plugin array",configPath:ne,errors:V.length>0?V:void 0}}catch(ce){V.push(`Failed to parse ${te}: ${ce instanceof Error?ce.message:String(ce)}`)}}return{platform:"opencode",status:"n/a",errors:V.length>0?V:void 0}}import{join as yf}from"node:path";function Vo(P){return yf(P.home,".pi","agent","settings.json")}function zo(P){if(typeof P!=="string")return!1;return P==="npm:cc-safety-net"||P.startsWith("npm:cc-safety-net@")}function al(P){let V=Vo(P.environment),z=Ft(V);if(z.kind==="unreadable")return{platform:"pi",status:"not-inspected"};if(z.kind==="missing")return{platform:"pi",status:"n/a"};let Q=ft(z.value,"packages");if(!Array.isArray(Q))return{platform:"pi",status:"n/a"};let te=Q.find((he)=>zo(typeof he==="string"?he:ft(he,"source")));if(te===void 0)return{platform:"pi",status:"n/a"};let ne=ft(te,"extensions");if(Array.isArray(ne)&&ne.some((he)=>typeof he==="string"&&he.startsWith("-")))return{platform:"pi",status:"disabled",method:"package config",configPath:V,errors:["npm:cc-safety-net is installed but its extension is disabled in Pi settings"]};return{platform:"pi",status:"configured",method:"package config",configPath:V}}var vf={amp:Gs,"antigravity-cli":Bs,"claude-code":Js,codex:Ws,"copilot-cli":ca,cursor:ya,"gemini-cli":va,"grok-build":Ea,"hermes-agent":Ma,"kimi-code":In,openclaw:Xa,opencode:sl,pi:al};function Ln(P,V,z){let Q={...z,cwd:V,environment:P};return Zn.map((te)=>bf(vf[te](Q)))}function bf(P){if(P.status==="not-inspected")return{platform:P.platform,detected:!1,configured:!1,inspectionStatus:"not-inspected"};return{platform:P.platform,detected:P.status!=="n/a",configured:P.status==="configured",inspectionStatus:P.status!=="n/a"?"verified":P.errors&&P.errors.length>0?"failed":"not-applicable",method:P.method,configPath:P.configPath,configPaths:P.configPaths,errors:P.errors}}import{join as Lf}from"node:path";var wf=Object.freeze([{command:"git reset --hard",description:"git reset --hard",expectBlocked:!0},{command:"rm -rf /",description:"rm -rf /",expectBlocked:!0},{command:"rm -rf ./node_modules",description:"rm in cwd (safe)",expectBlocked:!1}]),kf=Object.freeze({state:"ready",diagnostics:Object.freeze([]),ruleMetadata:Object.freeze({}),policy:Object.freeze({rules:Object.freeze([]),transparentWrappers:Object.freeze([]),safety:Object.freeze({}),worktreeMode:!1,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:Object.freeze({}),destructiveCommandAllowPaths:Object.freeze([]),secretProtection:Object.freeze({enabled:!0,disabledRules:Object.freeze([]),denyPaths:Object.freeze([]),allowPaths:Object.freeze([])})})}),xf={strict:!1,paranoidRm:!1,paranoidInterpreters:!1,worktreeMode:!1,effectiveLevel:"standard",capabilities:{fail_closed:{enabled:!1,source:"preset",sources:[]},paranoid_rm:{enabled:!1,source:"preset",sources:[]},paranoid_interpreters:{enabled:!1,source:"preset",sources:[]}}};function ll(P){let V=Lf(P.tmpdir,"cc-safety-net-self-test"),z=wf.map((Q)=>{let te=D(P,d("self-test",{command:Q.command},{kind:"command",shell:"auto"},{configCwd:V,executionCwd:V},Q.command),{guard:{dependencies:{loadPolicySnapshot:()=>kf,getModes:()=>xf,findPolicyMutation:()=>null}},audit:{agent:"self-test",getSessionId:()=>{return}}}),ne=Q.expectBlocked?"blocked":"allowed",ce=te.decision.kind==="deny"?"blocked":"allowed";return{command:Q.command,description:Q.description,expected:ne,actual:ce,passed:ne===ce,reason:te.decision.kind==="deny"?te.decision.reason:void 0,ruleId:te.decision.kind==="deny"?te.decision.ruleId:void 0}});return{passed:z.filter((Q)=>Q.passed).length,failed:z.filter((Q)=>!Q.passed).length,total:z.length,results:z}}function Jo(P){let V=xt({label:"doctor",booleans:{json:["--json"],skipUpdateCheck:["--skip-update-check"]}},P);if(Ht(V.errors))return null;return{json:V.flags.json,skipUpdateCheck:V.flags.skipUpdateCheck}}async function cl(P,V={}){let z=await Pn(!V.json,()=>{let Q=Cf(P,V);return{ready:Q,finish:()=>Q}},()=>Rn(),{loadingMessage:"Checking system status…"});if(V.json)console.log(JSON.stringify(z,null,2));else Sf(z);return z.engineSelfTest.failed>0||z.findings.some((Q)=>Q.severity==="error")?1:0}async function Cf(P,V){let z=V.cwd??process.cwd(),Q=await sr(),te=Ln(P,z,{ampPluginListOutput:Q.ampPluginListOutput,codexPluginListOutput:Q.codexPluginListOutput,copilotCliVersion:Q.versions["copilot-cli"]}),ne=os(P,z),ce=is(P),he=E(P,{cwd:z}),ye=he.policy,be=R(ye,P.env),xe=G(ye,be.capabilities),Ie=tr(P,7),Xe=js(P,z),et=V.skipUpdateCheck?{currentVersion:wt(),latestVersion:null,updateAvailable:!1}:await Wt(),Re={hooks:te,engineSelfTest:ll(P),userConfig:ne.userConfig,projectConfig:ne.projectConfig,configState:Ne(he),effectiveRules:ne.effectiveRules,environment:ce,effectiveSafety:{selectedPreset:ye.safety.level??"standard",level:be.effectiveLevel,capabilities:be.capabilities,ruleOverrides:ye.destructiveCommandRuleOverrides,weakenedRuleOverrides:Object.entries(xe).filter(([,at])=>at.source==="rule_override"&&at.override==="off"&&at.inheritedEnabled&&at.changesInherited).map(([at])=>at),ruleCounts:{stored:Object.keys(ye.destructiveCommandRuleOverrides).length,effective:Object.values(xe).filter((at)=>at.changesInherited).length},...he.policyScopes?{policyScopes:he.policyScopes}:{}},...Xe.length>0?{v2Leftovers:Xe}:{},posture:bs(P,ne.userConfig.path),activity:Ie,update:et,system:Q};return{...Re,findings:as(Re)}}function Sf(P){console.log(),console.log(cs(P.hooks)),console.log(),console.log(ds(P.engineSelfTest)),console.log(),console.log(us(P)),console.log(),console.log(ps(P.environment)),console.log(),console.log(fs(P)),console.log(),console.log(ms(P.findings)),console.log(),console.log(gs(P.activity)),console.log(),console.log(ys(P.system)),console.log(),console.log(hs(P.update)),console.log(vs(P))}import{existsSync as Rf}from"node:fs";var Pf=/^[A-Za-z0-9_@%+=:,./-]+$/,dl="Usage: cc-safety-net explain [--json] [--cwd <path>] <command>";function Wo(P){let V=xt({label:"explain",booleans:{json:["--json"]},values:{cwd:["--cwd"]},positionals:"tail"},P);if(Ht(V.errors))return console.error(dl),console.error("Pass -- before a command that starts with dashes."),null;if(V.values.cwd!==void 0&&!Rf(V.values.cwd))return console.error(`Error: --cwd path does not exist: ${V.values.cwd}`),null;let z=V.positionals.length===1?V.positionals[0]:V.positionals.map((Q)=>Pf.test(Q)?Q:`'${Q.replaceAll("'","'\\''")}'`).join(" ");if(!z)return console.error("Error: No command provided"),console.error(dl),null;return{json:V.flags.json,cwd:V.values.cwd,command:z}}function ul(P){if(P)return{dh:"=",dv:"|",dtl:"+",dtr:"+",dbl:"+",dbr:"+",h:"-",v:"|",tl:"+",tr:"+",bl:"+",br:"+",sh:"="};return{dh:"═",dv:"║",dtl:"╔",dtr:"╗",dbl:"╚",dbr:"╝",h:"─",v:"│",tl:"┌",tr:"┐",bl:"└",br:"┘",sh:"━"}}function pl(P,V){let Q=V-18;return[`${P.dtl}${P.dh.repeat(V)}${P.dtr}`,`${P.dv} Command Analysis${" ".repeat(Q)}${P.dv}`,`${P.dbl}${P.dh.repeat(V)}${P.dbr}`]}function Ko(P){return JSON.stringify(P)}function fl(P,V=0){return`[${P.map((Q,te)=>ls(Q,te,V)).join(",")}]`}function jn(P,V,z=70){let Q=P.split(" "),te=[],ne="";for(let ce of Q)if(ne&&ne.length+ce.length+1>z)te.push(ne),ne=ce;else ne=ne?`${ne} ${ce}`:ce;if(ne)te.push(ne);return te.map((ce,he)=>he===0?ce:`${V}${ce}`)}function ml(P,V,z){let Q=[];switch(P.type){case"parse":return null;case"env-strip":return Q.push(""),Q.push(`STEP ${V} ${z.h} Strip environment variables`),Q.push(` Removed: ${P.envVars.map((te)=>`${te}=<redacted>`).join(", ")}`),Q.push(` Tokens: ${Ko(P.output)}`),{lines:Q,incrementStep:!0};case"leading-tokens-stripped":return Q.push(""),Q.push(`STEP ${V} ${z.h} Strip wrappers`),Q.push(` Removed: ${P.removed.join(", ")}`),Q.push(` Tokens: ${Ko(P.output)}`),{lines:Q,incrementStep:!0};case"shell-wrapper":return Q.push(""),Q.push(`STEP ${V} ${z.h} Detect shell wrapper`),Q.push(` Wrapper: ${P.wrapper} -c`),Q.push(` Inner: ${P.innerCommand}`),{lines:Q,incrementStep:!0};case"interpreter":{if(Q.push(""),Q.push(`STEP ${V} ${z.h} Detect interpreter`),Q.push(` Interpreter: ${P.interpreter}`),Q.push(` Code: ${P.codeArg}`),P.paranoidBlocked)Q.push(" Result: ✗ BLOCKED (paranoid mode)");return{lines:Q,incrementStep:!0}}case"busybox":return Q.push(""),Q.push(`STEP ${V} ${z.h} Busybox wrapper`),Q.push(` Subcommand: ${P.subcommand}`),{lines:Q,incrementStep:!0};case"transparent-wrapper":return Q.push(""),Q.push(`STEP ${V} ${z.h} Transparent wrapper`),Q.push(` Wrapper: ${P.wrapper}`),Q.push(` Tokens: ${Ko(P.output)}`),{lines:Q,incrementStep:!0};case"recurse":return{lines:[],incrementStep:!1};case"rule-check":{if(Q.push(""),Q.push(`STEP ${V} ${z.h} Match rules`),Q.push(` Rule: ${P.rule}()`),P.matched)Q.push(" Result: MATCHED");else Q.push(" Result: No match");return{lines:Q,incrementStep:!0}}case"worktree-relaxation":return Q.push(""),Q.push(`STEP ${V} ${z.h} Worktree relaxation`),Q.push(` Mode: ${o.worktree.name}`),Q.push(` Git cwd: ${P.gitCwd}`),Q.push(" Result: Allowed local discard in linked worktree"),{lines:Q,incrementStep:!0};case"temp-root-relaxation":return Q.push(""),Q.push(`STEP ${V} ${z.h} Temp-root relaxation`),Q.push(` Git cwd: ${P.gitCwd}`),Q.push(" Result: Allowed git discard in a temp-root repository"),{lines:Q,incrementStep:!0};case"tmpdir-check":return null;case"fallback-scan":{if(P.embeddedCommandFound)return Q.push(""),Q.push(`STEP ${V} ${z.h} Fallback scan`),Q.push(` Found: ${P.embeddedCommandFound}`),{lines:Q,incrementStep:!0};return null}case"custom-rules-check":{if(P.rulesChecked){if(Q.push(""),Q.push(`STEP ${V} ${z.h} Custom rules`),P.matched)Q.push(" Result: MATCHED");else Q.push(" Result: No match");return{lines:Q,incrementStep:!0}}return null}case"cwd-change":return null;case"dangerous-text":{if(P.matched)return Q.push(""),Q.push(`STEP ${V} ${z.h} Dangerous text check`),Q.push(` Token: ${P.token}`),Q.push(" Result: MATCHED"),{lines:Q,incrementStep:!0};return null}case"strict-unparseable":return Q.push(""),Q.push(`STEP ${V} ${z.h} Strict mode check`),Q.push(` Command: ${P.rawCommand}`),Q.push(" Result: ✗ UNPARSEABLE"),{lines:Q,incrementStep:!0};case"segment-skipped":return null;case"error":return Q.push(""),Q.push(`ERROR: ${P.message}`),{lines:Q,incrementStep:!1};default:return P}}function Yo(P,V){let z=ul(V?.asciiOnly??!1),Q=58,te=[],ne=1;te.push(...pl(z,58)),te.push("");let ce=P.trace.steps.find((Re)=>Re.type==="error");if(ce&&ce.type==="error"){te.push("ERROR"),te.push(` ${ce.message}`),te.push(""),te.push("RESULT"),te.push(` Status: ${P.result==="blocked"?dt.red("BLOCKED"):dt.green("ALLOWED")}`),te.push(""),te.push("CONFIG");let Re=P.configSource??"none";return te.push(` Path: ${Re}`),te.join(`
212
212
  `)}let he=P.trace.steps.find((Re)=>Re.type==="parse");if(he&&he.type==="parse"){te.push("INPUT"),te.push(` ${he.input}`),te.push(""),te.push(`STEP ${ne} ${z.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();te.push(` Segment ${Re+1}: ${fl(at,ct)}`)}}}let ye=P.trace.segments,be=ye.length>1;for(let Re of ye){if(be){te.push("");let pt="";if(he&&he.type==="parse"){let Xr=he.segments[Re.index];if(Xr)pt=Xr.join(" ")}let mt=54,gt=pt,ut=` Segment ${Re.index+1}: `,vt=" ";if(pt){if(ut.length+pt.length+vt.length>mt){let Hd=mt-ut.length-vt.length;gt=`${pt.substring(0,Hd-1)}…`}}let Dt=pt?`${ut}${gt}${vt}`:` Segment ${Re.index+1} `,Nd=pt?`${ut}${dt.cyan(gt)}${vt}`:Dt,$i=58-Dt.length,Ii=Math.floor($i/2),Md=$i-Ii;te.push(`${z.sh.repeat(Ii)}${Nd}${z.sh.repeat(Md)}`)}if(Re.steps.find((pt)=>pt.type==="segment-skipped")){te.push(""),te.push(" (skipped — prior segment blocked)");continue}let ct=!1,lt=!1;for(let pt of Re.steps){let mt=ml(pt,ne,z);if(mt){if(lt=!0,pt.type==="recurse"){te.push("");let gt=" RECURSING ",ut=58-gt.length-4;te.push(` ${z.tl}${z.h}${gt}${z.h.repeat(ut)}`),te.push(` ${z.v}`),ct=!0;continue}for(let gt of mt.lines)if(ct)te.push(` ${z.v} ${gt}`);else te.push(gt);if(mt.incrementStep)ne++}}if(ct)te.push(` ${z.v}`),te.push(` ${z.bl}${z.h.repeat(56)}`);if(!lt)te.push(""),te.push(` ${dt.green("✓")} Allowed (no matching rules)`)}if(te.push(""),te.push("RESULT"),P.result==="blocked"){if(te.push(` Status: ${dt.red("BLOCKED")}`),P.customRule){if(te.push(` Rule: ${P.customRule.id}`),P.customRule.rulebook)te.push(` Rulebook: ${P.customRule.rulebook.name} ${P.customRule.rulebook.version}`);if(P.customRule.source)te.push(` Source: ${P.customRule.source}`);if(P.customRule.override)te.push(` Override: reason ${P.customRule.override.reason}`)}if(P.reason){let Re=jn(P.reason," ");te.push(` Reason: ${Re[0]}`);for(let at=1;at<Re.length;at++)te.push(Re[at]??"")}}else te.push(` Status: ${dt.green("ALLOWED")}`);te.push(""),te.push("CONFIG");let xe=P.configSource??"none",Ie=P.configValid?"":" (invalid)";te.push(` Path: ${xe}${Ie}`);let Xe=P.safetyPresetScope;te.push(` Safety preset: ${P.selectedPreset??"standard"}${Xe?` (${rr(Xe)})`:""}`),te.push(` Effective capabilities: ${P.effectiveLevel}`);let et=Object.entries(P.destructiveCommandRuleOverrides??{});if(te.push(` Rule customizations: ${et.length}`),P.ruleActivation)te.push(` Rule activation: ${P.ruleActivation.id} — ${P.ruleActivation.enabled?"on":"off"} via ${P.ruleActivation.source}`);return te.join(`
213
213
  `)}function Zo(P){return JSON.stringify(P,null,2)}import{resolve as Tf}from"node:path";var Ef=["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 gl(P){let V=0,z={allocateSegment(){return V++},getNextSegmentIndex(){return V},recordGlobal(Q){P.record({kind:"step",scope:"global",step:Q})},recordSegment(Q,te=z.currentSegmentIndex){if(te===void 0)return;P.record({kind:"step",scope:"segment",segmentIndex:te,step:Q})}};return z}function hl(P={}){let V=[],z=P.maxEvents??512,Q={maxTextLength:P.maxTextLength??2048,maxListLength:P.maxListLength??128,maxObjectProperties:P.maxObjectProperties??P.maxListLength??128,maxDepth:P.maxDepth??16},te,ne=new Set;return{record(ce){if(te)return;if(!ce||V.length>=z)return;try{V.push(ei(Df(ce,Q,ne)))}catch{}},finish(){if(te)return te;return te=ei({events:Object.freeze(V)}),te}}}function Df(P,V,z){if(P.kind!=="step")throw TypeError("invalid trace event");let{scope:Q,step:te}=P;Ar(te,z,V);let ne=Xo(te,V,z);if(Q==="global")return{kind:"step",scope:"global",step:ne};if(Q!=="segment")throw TypeError("invalid trace event scope");return{kind:"step",scope:"segment",segmentIndex:P.segmentIndex,step:ne}}function Ar(P,V,z,Q=0,te=new WeakSet){if(typeof P==="string"){let he=P.slice(0,z.maxTextLength);if(!Ge(he))return;for(let ye of Ze(he))for(let be of ye.match(/[^\s"'()$]+/g)??[])V.add(yl(be));return}if(!P||typeof P!=="object"||Q>=z.maxDepth||te.has(P))return;if(te.add(P),Array.isArray(P)){let he=Math.min(P.length,z.maxListLength);for(let ye=0;ye<he;ye++)Ar(P[ye],V,z,Q+1,te);return}let ne=0,ce=new Set;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ne>=z.maxObjectProperties)break;ne++,Ar(he,V,z);let ye=Qo(he,z,V);if(ce.has(ye))continue;ce.add(ye),Ar(P[he],V,z,Q+1,te)}}function Xo(P,V,z,Q=0,te=new WeakSet){if(typeof P==="string")return Qo(P,V,z);if(!P||typeof P!=="object")return P;if(Q>=V.maxDepth)return;if(te.has(P))return;if(te.add(P),Array.isArray(P)){let he=[],ye=Math.min(P.length,V.maxListLength);for(let be=0;be<ye;be++)he.push(Xo(P[be],V,z,Q+1,te));return he}let ne={},ce=0;for(let he in P){if(!Object.hasOwn(P,he))continue;if(ce>=V.maxObjectProperties)break;ce++;let ye=Qo(he,V,z);if(Object.hasOwn(ne,ye))continue;Object.defineProperty(ne,ye,{value:Xo(P[he],V,z,Q+1,te),enumerable:!0,configurable:!0,writable:!0})}return ne}function Qo(P,V,z){let Q=P.slice(0,V.maxTextLength),te=Ge(Q)?Ue(Q):Q,ne=z.size>0?_f(te,z):te;return(Af(ne)?ke(ne):ne).slice(0,V.maxTextLength)}function Af(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&&Ef.some((V)=>P.includes(V))||P.length>=49&&/\b[a-f0-9]{32}\.[A-Za-z0-9]{16}\b/.test(P)}function _f(P,V){return P.replace(/[^\s"'()$]+/g,(z)=>V.has(yl(z))?"<redacted>":z)}function yl(P){let V=2166136261,z=2166136261;for(let Q=0;Q<P.length;Q++)V=Math.imul(V^P.charCodeAt(Q),16777619),z=Math.imul(z^P.charCodeAt(P.length-Q-1),16777619);return`${V>>>0}:${z>>>0}:${P.length}`}function ei(P){if(P&&typeof P==="object"&&!Object.isFrozen(P)){for(let V of Object.values(P))ei(V);Object.freeze(P)}return P}function Nn(P,V={},z){let Q=Tf(V.cwd??process.cwd()),te=V.policySnapshot??E(z,{cwd:Q,userConfigDir:V.userConfigDir}),ne=R(te.policy,z.env),ce=Fe({policySnapshot:te,effectiveCapabilities:ne.capabilities,strict:ne.strict,paranoidRm:ne.paranoidRm,paranoidInterpreters:ne.paranoidInterpreters,worktreeMode:ne.worktreeMode}),he={effectiveLevel:ce.effectiveLevel,selectedPreset:te.policy.safety.level??"standard",...te.policyScopes?{safetyPresetScope:te.policyScopes.levelScope}:{},effectiveCapabilities:ce.effectiveCapabilities,destructiveCommandRuleOverrides:te.policy.destructiveCommandRuleOverrides},{configSource:ye,configValid:be}=If(z,{cwd:Q,userConfigDir:V.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=hl(),Re=gl(et);Re.recordGlobal({type:"parse",input:P,segments:Xe.map((Dt)=>[...Dt])});let at=d("Bash",{command:P},{kind:"command",shell:"auto"},{configCwd:Q,executionCwd:Q},P),ct=W(at,{environment:z,trace:Re,dependencies:{loadPolicySnapshot:()=>te}}),lt=ct.decision.kind==="deny"?ct.decision:null;if(lt&&(ct.stage==="policy-protection"||ct.stage==="secret-protection")){let Dt=$f(lt);return{trace:{steps:[],segments:[{index:0,steps:[{type:"rule-check",rule:Dt.rule,matched:!0,reason:lt.reason}]}]},result:"blocked",reason:k(lt.reason),segment:k(vl(lt,P)),...Dt.ruleId?{ruleId:k(Dt.ruleId)}:{},configSource:ye,configValid:be,...he}}let pt=Re.getNextSegmentIndex();if(lt&&pt>0&&pt<Xe.length)Re.recordSegment({type:"segment-skipped",index:pt,reason:"prior-segment-blocked"},pt);let mt=et.finish(),gt=lt?.ruleId??Of(at,te,ne,z),ut=U.find((Dt)=>Dt.id===gt&&Dt.activationCapability),vt=ut?ce.policy.effectiveDestructiveCommandRules[ut.id]:void 0;return{trace:jf(mt),result:lt?"blocked":"allowed",reason:lt?k(lt.reason):void 0,segment:lt?k(vl(lt,P)):void 0,ruleId:lt?.ruleId?k(lt.ruleId):void 0,customRule:Ff(Nf(lt?.ruleId,te)),configSource:ye,configValid:be,...he,...ut&&vt?{ruleActivation:{id:ut.id,...vt}}:{}}}function vl(P,V){return P.evidence?.segment??V}function $f(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 If(P,V){let z=F(V.cwd),Q=M(P,V),te=K(P,{cwd:V.cwd,userConfigDir:V.userConfigDir});try{if(n(te.projectConfigTarget)!==null){if(Jt(te.projectConfigTarget).errors.length===0)return{configSource:z,configValid:!0};return{configSource:z,configValid:!1}}}catch(ne){if(ne instanceof r)return{configSource:z,configValid:!1};throw ne}try{if(n(te.userConfigTarget)!==null){let ne=Jt(te.userConfigTarget);return{configSource:Q,configValid:ne.errors.length===0}}return{configSource:null,configValid:!0}}catch(ne){if(ne instanceof r)return{configSource:Q,configValid:!1};throw ne}}function Of(P,V,z,Q){let te=V.policy,ne=Ee({...te,destructiveCommandProtectionEnabled:!0,destructiveCommandRuleOverrides:{...te.destructiveCommandRuleOverrides,...Object.fromEntries(U.flatMap((he)=>he.activationCapability?[[he.id,"on"]]:[]))}},V.state==="degraded"?{diagnostics:V.diagnostics,reason:V.reason}:void 0),ce=W(P,{environment:Q,dependencies:{loadPolicySnapshot:()=>ne,getModes:()=>({...z,strict:!0,paranoidRm:!0,paranoidInterpreters:!0}),findSensitiveTarget:()=>null}});return ce.decision.kind==="deny"?ce.decision.ruleId:void 0}function Ff(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 jf(P){let V=P.events.flatMap((Q)=>Q.kind==="step"&&Q.scope==="global"?[Q.step]:[]),z=new Map;for(let Q of P.events){if(Q.kind!=="step"||Q.scope!=="segment")continue;let te=z.get(Q.segmentIndex)??{index:Q.segmentIndex,steps:[]};te.steps.push(Q.step),z.set(Q.segmentIndex,te)}return{steps:V,segments:[...z.values()]}}function Nf(P,V){let z=P?.replace(/^custom\./,"");if(!z||!V.policy.rules.some((Q)=>Q.name===z))return;return V.ruleMetadata[z]??Object.freeze({id:z})}function bl(P){return new Promise((V)=>{process.stdout.write(`${P}
214
- `,()=>V())})}async function Ll(P,V){let z=Wo(V);if(!z)return 1;try{let Q=Nn(z.command,{cwd:z.cwd},P),te=!!process.env.NO_COLOR||!process.stdout.isTTY;return await bl(z.json?Zo(Q):Yo(Q,{asciiOnly:te})),0}catch(Q){let te=Mf(Q instanceof p?Q.cause:Q);if(te===void 0)throw Q;if(z.json)return await bl(JSON.stringify({error:te})),1;return console.error(te),1}}function Mf(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 wl="2.4.6",Ot=" ",rn="cc-safety-net";function kl(P){return P.argument?`${P.flags} ${P.argument}`:P.flags}function Hf(P){return Math.max(...P.map((V)=>kl(V).length))}function Uf(P){return Math.max(...P.map((V)=>V.usage.length))}function Gf(P){return Math.max(...P.map((V)=>`${rn} ${V.usage}`.length))}function Bf(P,V){let z=`${rn} ${P.usage}`;return`${Ot}${z.padEnd(V+2)}${P.description}`}function qt(P,V){return`${Ot}${P.padEnd(Math.max(40,P.length+2))}${V}`}function wn(P,V=console.log){let z=[];if(z.push(`${rn} ${P.name}`),z.push(""),z.push(`${Ot}${P.description}`),z.push(""),z.push("USAGE:"),z.push(`${Ot}${rn} ${P.usage}`),z.push(""),P.subcommands&&P.subcommands.length>0){z.push("SUBCOMMANDS:");let Q=Uf(P.subcommands);for(let te of P.subcommands)z.push(`${Ot}${te.usage.padEnd(Q+2)}${te.description}`);z.push("")}if(P.options.length>0){z.push("OPTIONS:");let Q=Hf(P.options);for(let te of P.options){let ne=kl(te),ce=te.default?`${te.description} (default: ${te.default})`:te.description;z.push(`${Ot}${ne.padEnd(Q+2)}${ce}`)}z.push("")}if(P.examples&&P.examples.length>0){z.push("EXAMPLES:");for(let Q of P.examples)z.push(`${Ot}${Q}`)}V(z.join(`
214
+ `,()=>V())})}async function Ll(P,V){let z=Wo(V);if(!z)return 1;try{let Q=Nn(z.command,{cwd:z.cwd},P),te=!!process.env.NO_COLOR||!process.stdout.isTTY;return await bl(z.json?Zo(Q):Yo(Q,{asciiOnly:te})),0}catch(Q){let te=Mf(Q instanceof p?Q.cause:Q);if(te===void 0)throw Q;if(z.json)return await bl(JSON.stringify({error:te})),1;return console.error(te),1}}function Mf(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 wl="2.4.8",Ot=" ",rn="cc-safety-net";function kl(P){return P.argument?`${P.flags} ${P.argument}`:P.flags}function Hf(P){return Math.max(...P.map((V)=>kl(V).length))}function Uf(P){return Math.max(...P.map((V)=>V.usage.length))}function Gf(P){return Math.max(...P.map((V)=>`${rn} ${V.usage}`.length))}function Bf(P,V){let z=`${rn} ${P.usage}`;return`${Ot}${z.padEnd(V+2)}${P.description}`}function qt(P,V){return`${Ot}${P.padEnd(Math.max(40,P.length+2))}${V}`}function wn(P,V=console.log){let z=[];if(z.push(`${rn} ${P.name}`),z.push(""),z.push(`${Ot}${P.description}`),z.push(""),z.push("USAGE:"),z.push(`${Ot}${rn} ${P.usage}`),z.push(""),P.subcommands&&P.subcommands.length>0){z.push("SUBCOMMANDS:");let Q=Uf(P.subcommands);for(let te of P.subcommands)z.push(`${Ot}${te.usage.padEnd(Q+2)}${te.description}`);z.push("")}if(P.options.length>0){z.push("OPTIONS:");let Q=Hf(P.options);for(let te of P.options){let ne=kl(te),ce=te.default?`${te.description} (default: ${te.default})`:te.description;z.push(`${Ot}${ne.padEnd(Q+2)}${ce}`)}z.push("")}if(P.examples&&P.examples.length>0){z.push("EXAMPLES:");for(let Q of P.examples)z.push(`${Ot}${Q}`)}V(z.join(`
215
215
  `))}function ti(){let P=Gf(Qn),V=[];V.push(`${rn} v${wl}`),V.push(""),V.push("Blocks destructive commands and secret access."),V.push(""),V.push("COMMANDS:");for(let z of Qn)V.push(Bf(z,P));V.push(""),V.push("GLOBAL OPTIONS:"),V.push(`${Ot}-h, --help Show help (use with command for command-specific help)`),V.push(`${Ot}-V, --version Show version`),V.push(""),V.push("HELP:"),V.push(`${Ot}${rn} help <command> Show help for a specific command`),V.push(`${Ot}${rn} <command> --help Show help for a specific command`),V.push(""),V.push("ENVIRONMENT VARIABLES:"),V.push(qt(`${o.level.name}=standard|strict|paranoid`,"Set session safety level")),V.push(qt(`${o.worktree.name}=1`,"Allow local git discards in linked worktrees")),V.push(qt(`${o.debug.name}=1`,"Print diagnostic messages to stderr")),V.push(qt(`${o.auditScope.name}=all|blocked`,"Record all command decisions, or denials only")),V.push(qt("CC_SAFETY_NET_HOME","Override rule config home directory")),V.push(""),V.push("LEGACY ENVIRONMENT VARIABLES (STILL SUPPORTED):"),V.push(qt(`${o.strict.name}=1`,"Force safety.overrides.fail_closed on")),V.push(qt(`${o.paranoid.name}=1`,"Force paranoid_rm and paranoid_interpreters on")),V.push(qt(`${o.paranoidRm.name}=1`,"Force safety.overrides.paranoid_rm on")),V.push(qt(`${o.paranoidInterpreters.name}=1`,"Force safety.overrides.paranoid_interpreters on")),V.push(""),V.push("Documentation: https://ccsafetynet.com/docs"),console.log(V.join(`
216
216
  `))}function xl(){console.log(wl)}function Mn(P,V=console.log){let z=er(P);if(!z)return!1;if(z.name.toLowerCase()!==P.toLowerCase())return!1;return wn(z,V),!0}import{existsSync as mi,readFileSync as bc}from"node:fs";import{join as pi}from"node:path";import*as Kt from"node:readline";function qf(P){return P==="install"?"Install":"Uninstall"}function Vf(P){return P==="install"?"Installing":"Uninstalling"}function zf(P){return P==="install"?"into":"from"}function Rl(P){return P?.available===!0}function Jf(P,V){let z=new Set(V);return P.filter((Q)=>z.has(Q.target)).map((Q)=>Q.target)}function Cl(P,V,z){if(P.every((Q)=>!Q.available))return V;return Array.from({length:P.length},(Q,te)=>te+1).map((Q)=>(V+Q*z+P.length)%P.length).find((Q)=>Rl(P[Q]))}function Wf(P,V,z){if(z.ctrl&&z.name==="c")return"interrupt";if(z.name==="escape"||V==="q")return"abort";if(P==="install"&&(V==="u"||V==="U"))return"update";if(z.name==="up"||V==="k")return"up";if(z.name==="down"||V==="j")return"down";if(z.name==="space"||V===" ")return"toggle";if(z.name==="return"||z.name==="enter")return"confirm";return null}function Kf(P){return{cursor:P.findIndex((V)=>V.available),selected:[]}}function Yf(P,V,z){if(z==="confirm"||z==="update"||z==="abort"||z==="interrupt")return{state:P,done:z};if(z==="up")return{state:{...P,cursor:Cl(V,P.cursor,-1)}};if(z==="down")return{state:{...P,cursor:Cl(V,P.cursor,1)}};let Q=V[P.cursor];if(!Rl(Q))return{state:P};let te=P.selected.includes(Q.target)?P.selected.filter((ne)=>ne!==Q.target):Jf(V,[...P.selected,Q.target]);return{state:{...P,selected:te}}}var Pl="◉",El="◯",Dl=">",Al=" ";function Zf(P,V,z,Q={}){let te=Q.color!==!1,ne=te?dt.dim:(ye)=>ye,ce=te?dt.green:(ye)=>ye,he=te?dt.bold:(ye)=>ye;return["",`${qf(P)} CC Safety Net ${zf(P)}:`,"",...V.map((ye,be)=>{let xe=z.selected.includes(ye.target),Ie=be===z.cursor,Xe=xe?Pl:El,et=Ie?Dl:Al,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":V.some((ye)=>ye.available)?"Space: select Enter: confirm Up/Down: move q/Esc: cancel":`No selectable integrations found for ${P}. q/Esc: close`].join(`
217
217
  `)}var Sl=["global-hook","plugin"];function Xf(P,V,z={}){let Q=z.color!==!1?dt.bold:(ne)=>ne;return["","Install the Kimi Code integration as:","",...[`Global hook — ${V?"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?Pl:El} ${ne}`;return`${he?Dl:Al} ${he?Q(ye):ye}`}),"","Enter: confirm Up/Down: move q/Esc: cancel"].join(`
@@ -4953,7 +4953,7 @@ function render() {
4953
4953
  qs("app-version").textContent = state.version;
4954
4954
  renderSafety();
4955
4955
  qs("destructive-command").innerHTML = '<label class="row master"><input type="checkbox" data-destructive-command-enabled ' + checkbox(state.policy.destructive_command_protection.enabled) + '><span><strong>Destructive command protection</strong><small>Block configurable destructive git, filesystem, and execution patterns. Catastrophic and custom rules remain active when disabled.</small></span><span class="master-badge">' + (state.policy.destructive_command_protection.enabled ? "On" : "Off") + '</span><span class="project-chip-slot" id="destructive-enabled-chip"></span></label>' + '<div id="destructive-command-rules"></div>' + '<section class="rule-tier">' + '<button type="button" class="rule-tier-head" aria-expanded="false" aria-controls="allow-paths-content"><span class="panel-chevron" aria-hidden="true"></span><span class="tier-label"><strong id="allow-paths-label">Allow paths</strong><small>Recursive deletes targeting these paths are not blocked, like /tmp. The home directory, or any path containing it, is rejected.</small></span><span class="tier-counts" id="allow-paths-count"></span></button>' + '<div class="tier-content paths-content" id="allow-paths-content" hidden>' + '<p class="muted">Use an absolute path or a ~/ path. Paste multiple lines to add several paths at once.</p>' + '<div class="paths-add"><input type="text" id="allow-paths-input" data-path-input="allow-paths" autocomplete="off" spellcheck="false" placeholder="/absolute/path or ~/path" aria-labelledby="allow-paths-label"><button type="button" class="icon-button" id="allow-paths-add-button" data-path-add="allow-paths" aria-label="Add allow path">' + pathListIcons.add + "</button></div>" + '<p class="paths-hint" id="allow-paths-hint" hidden></p>' + '<span class="project-chip-slot" id="allow-paths-chip"></span>' + '<ul class="paths-list" id="allow-paths-list"></ul>' + "</div></section>";
4956
- qs("secret").innerHTML = '<label class="row master"><input type="checkbox" id="secret-enabled" ' + checkbox(state.policy.secret_protection.enabled) + '><span><strong>Secret protection</strong><small>Block default sensitive paths, coding CLI credential locations, and configured deny paths.</small></span><span class="master-badge">' + (state.policy.secret_protection.enabled ? "On" : "Off") + '</span><span class="project-chip-slot" id="secret-enabled-chip"></span></label>' + '<div id="secret-patterns"></div>' + '<section class="rule-tier">' + '<button type="button" class="rule-tier-head" aria-expanded="false" aria-controls="deny-paths-content"><span class="panel-chevron" aria-hidden="true"></span><span class="tier-label"><strong id="deny-paths-label">Deny paths</strong><small>Configured paths and everything inside them are blocked while Secret protection is on.</small></span><span class="tier-counts" id="deny-paths-count"></span></button>' + '<div class="tier-content paths-content" id="deny-paths-content" hidden>' + '<p class="muted">Paste multiple lines to add several paths at once.</p>' + '<div class="paths-add"><input type="text" id="deny-paths-input" data-path-input="deny-paths" autocomplete="off" spellcheck="false" placeholder="path/to/protect" aria-labelledby="deny-paths-label"><button type="button" class="icon-button" id="deny-paths-add-button" data-path-add="deny-paths" aria-label="Add deny path">' + pathListIcons.add + "</button></div>" + '<p class="paths-hint" id="deny-paths-hint" hidden></p>' + '<span class="project-chip-slot" id="deny-paths-chip"></span>' + '<ul class="paths-list" id="deny-paths-list"></ul>' + "</div></section>" + '<section class="rule-tier">' + '<button type="button" class="rule-tier-head" aria-expanded="false" aria-controls="secret-allow-paths-content"><span class="panel-chevron" aria-hidden="true"></span><span class="tier-label"><strong id="secret-allow-paths-label">Allow paths</strong><small>Configured files and subtrees are exempt from the pattern rules. Deny paths and coding CLI protections still apply. Entries covering the home directory are rejected, and glob patterns are not supported.</small></span><span class="tier-counts" id="secret-allow-paths-count"></span></button>' + '<div class="tier-content paths-content" id="secret-allow-paths-content" hidden>' + '<p class="muted">Paste multiple lines to add several paths at once.</p>' + '<div class="paths-add"><input type="text" id="secret-allow-paths-input" data-path-input="secret-allow-paths" autocomplete="off" spellcheck="false" placeholder="~/project/.env.test or ~/project/fixtures" aria-labelledby="secret-allow-paths-label"><button type="button" class="icon-button" id="secret-allow-paths-add-button" data-path-add="secret-allow-paths" aria-label="Add allow path">' + pathListIcons.add + "</button></div>" + '<p class="paths-hint" id="secret-allow-paths-hint" hidden></p>' + '<span class="project-chip-slot" id="secret-allow-paths-chip"></span>' + '<ul class="paths-list" id="secret-allow-paths-list"></ul>' + "</div></section>";
4956
+ qs("secret").innerHTML = '<label class="row master"><input type="checkbox" id="secret-enabled" ' + checkbox(state.policy.secret_protection.enabled) + '><span><strong>Secret protection</strong><small>Block default sensitive paths, coding CLI credential locations, and configured deny paths.</small></span><span class="master-badge">' + (state.policy.secret_protection.enabled ? "On" : "Off") + '</span><span class="project-chip-slot" id="secret-enabled-chip"></span></label>' + '<div id="secret-patterns"></div>' + '<section class="rule-tier">' + '<button type="button" class="rule-tier-head" aria-expanded="false" aria-controls="deny-paths-content"><span class="panel-chevron" aria-hidden="true"></span><span class="tier-label"><strong id="deny-paths-label">Deny paths</strong><small>Configured paths and everything inside them are blocked while Secret protection is on.</small></span><span class="tier-counts" id="deny-paths-count"></span></button>' + '<div class="tier-content paths-content" id="deny-paths-content" hidden>' + '<p class="muted">Paste multiple lines to add several paths at once.</p>' + '<div class="paths-add"><input type="text" id="deny-paths-input" data-path-input="deny-paths" autocomplete="off" spellcheck="false" placeholder="path/to/protect" aria-labelledby="deny-paths-label"><button type="button" class="icon-button" id="deny-paths-add-button" data-path-add="deny-paths" aria-label="Add deny path">' + pathListIcons.add + "</button></div>" + '<p class="paths-hint" id="deny-paths-hint" hidden></p>' + '<span class="project-chip-slot" id="deny-paths-chip"></span>' + '<ul class="paths-list" id="deny-paths-list"></ul>' + "</div></section>" + '<section class="rule-tier">' + '<button type="button" class="rule-tier-head" aria-expanded="false" aria-controls="secret-allow-paths-content"><span class="panel-chevron" aria-hidden="true"></span><span class="tier-label"><strong id="secret-allow-paths-label">Allow paths</strong><small>Exact files, subtrees, or one file name under a folder like ~/code/**/.env.local are exempt from pattern rules. Entries covering the home directory are rejected. Deny paths and coding CLI protections still apply.</small></span><span class="tier-counts" id="secret-allow-paths-count"></span></button>' + '<div class="tier-content paths-content" id="secret-allow-paths-content" hidden>' + '<p class="muted">Paste multiple lines to add several paths at once.</p>' + '<div class="paths-add"><input type="text" id="secret-allow-paths-input" data-path-input="secret-allow-paths" autocomplete="off" spellcheck="false" placeholder="~/code/**/.env.local or ~/project/fixtures" aria-labelledby="secret-allow-paths-label"><button type="button" class="icon-button" id="secret-allow-paths-add-button" data-path-add="secret-allow-paths" aria-label="Add allow path">' + pathListIcons.add + "</button></div>" + '<p class="paths-hint" id="secret-allow-paths-hint" hidden></p>' + '<span class="project-chip-slot" id="secret-allow-paths-chip"></span>' + '<ul class="paths-list" id="secret-allow-paths-list"></ul>' + "</div></section>";
4957
4957
  qs("raw").value = state.errors.length ? state.raw : formatPolicy(draftPolicy);
4958
4958
  qs("policy-search").value = "";
4959
4959
  syncSearchState();
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{ye,P,be,c,xe,f,V,ne,ce,d,p}from"./chunks/index-c989ehp9.js";import{Q}from"./chunks/index-q7q195qc.js";import{he,D}from"./chunks/index-4jcxaxr5.js";import{resolve as Os}from"node:path";function Ir(){return{"cc-safety-net":{description:"Operate CC Safety Net: explain blocks, rules, integrations, diagnostics",template:Q.slice(Q.indexOf("# CC Safety Net"))}}}var $c=new Set(["powershell","pwsh"]),Bc=new Set(["bash","dash","ksh","sh","zsh"]);function ks(e={}){return async({directory:t,homeDir:r})=>{let n=Os(t),o;return{config:async(s)=>{o=s;let a=Ir(),u=s.command??{};s.command={...a,...u}},"tool.execute.before":async(s,a)=>{Rn({configCwd:n,homeDir:r,tool:s.tool,sessionID:s.sessionID,toolInput:a.args,route:jc(s.tool,_n(o?.shell)),guardDependencies:e})}}}}function Rn({configCwd:e,homeDir:t,tool:r,sessionID:n,toolInput:o,route:s,guardDependencies:a={}}){let u=t===void 0?c():{...c(),home:t},l=(h,g,I=e)=>{he(u,h,()=>n,{agent:"opencode",toolName:g,cwd:I}),Rs(h)};if(typeof r!=="string"||r.trim()==="")l(P());let i;try{i=ne(o)}catch(h){if(!(h instanceof f))throw h;l(P({toolName:r}),r)}let x=qc(e,o);if(!ce(e)||!x){l(P({command:i,toolName:r}),r);return}let S=d(r,o,s,{configCwd:e,executionCwd:x},i??null);try{let h=D(u,S,{guard:{auditAllowed:xe(u.env),dependencies:a},audit:{agent:"opencode",getSessionId:()=>n}});Cs(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;Cs(h.evaluation);return}}function _n(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($c.has(o))return"powershell";if(Bc.has(o))return"posix";return"auto"}function jc(e,t){if(e==="bash")return{kind:"command",shell:t};return{kind:V(e)}}function qc(e,t){if(!t||typeof t!=="object"||Array.isArray(t))return e;if(!Object.hasOwn(t,"workdir"))return e;let r=t.workdir;if(typeof r!=="string"||r.trim()==="")return null;let n=process.platform==="win32"?Jc(r):r,o=Os(e,n);return ce(o)?o:null}function Jc(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 Cs(e){let t=ye(e,{includeEvidence:!0});if(t)Rs(t)}function Rs(e){throw Error(be(e))}var fe=(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}}},Gc={pipe(){return fe(this,arguments)}},Bt=function(){function e(){}return e.prototype=Gc,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 ie=(e)=>e;var _s=(e)=>()=>e;var St=_s(void 0),As=St;function rt(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 yt(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 jt=(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},qt=new WeakSet;function Jt(e){return typeof e==="string"}function An(e){return typeof e==="number"}function Fs(e){return typeof e==="symbol"}function Cr(e){return Jt(e)||An(e)||Fs(e)}function Tt(e){return typeof e==="function"}function Fn(e){return e!==void 0}function Pn(e){return e!=null}function Ps(e){return!0}function Dn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Or(e){return typeof e==="object"&&e!==null||Tt(e)}var _=C(2,(e,t)=>Or(e)&&(t in e));function Ds(e){return e instanceof Error}var Y="~effect/interfaces/Hash",F=(e)=>{switch(typeof e){case"number":return Ar(e);case"bigint":return G(e.toString(10));case"boolean":return G(String(e));case"symbol":return G(String(e));case"string":return G(e);case"undefined":return G("undefined");case"function":case"object":if(e===null)return G("null");else if(e instanceof Date){if(Number.isNaN(e.getTime()))return G("Invalid Date");return G(e.toISOString())}else if(e instanceof RegExp)return G(e.toString());else{if(qt.has(e))return kr(e);if(Nn.has(e))return Nn.get(e);let t=Wc(e,()=>{if(Kc(e))return e[Y]();else if(typeof e==="function")return kr(e);else if(e instanceof DataView)return Rr(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));else if(Array.isArray(e)||ArrayBuffer.isView(e))return Rr(e);else if(e instanceof Map)return Vc(e);else if(e instanceof Set)return Hc(e);return zc(e)});return Nn.set(e,t),t}default:throw Error(`BUG: unhandled typeof ${typeof e} - please report an issue at https://github.com/Effect-TS/effect/issues`)}},kr=(e)=>{if(!Un.has(e))Un.set(e,Ar(Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)));return Un.get(e)},ue=C(2,(e,t)=>e*53^t),_r=(e)=>e&3221225471|e>>>1&1073741824,Kc=(e)=>_(e,Y),Ar=(e)=>{if(e!==e)return G("NaN");if(e===1/0)return G("Infinity");if(e===-1/0)return G("-Infinity");let t=e|0;if(t!==e)t^=e*4294967295;while(e>4294967295)t^=e/=4294967295;return _r(t)},G=(e)=>{let t=5381,r=e.length;while(r)t=t*33^e.charCodeAt(--r);return _r(t)},Ln=(e,t)=>{let r=12289;for(let n of t)r^=ue(F(n),F(e[n]));return _r(r)},zc=(e)=>Ln(e,jt(e)),$n=(e,t)=>(r)=>{let n=e;for(let o of r)n^=t(o);return _r(n)},Rr=$n(6151,F),Vc=$n(G("Map"),([e,t])=>ue(F(e),F(t))),Hc=$n(G("Set"),F),Un=new WeakMap,Nn=new WeakMap,Mn=new WeakSet;function Wc(e,t){if(Mn.has(e))return G("[Circular]");Mn.add(e);let r=t();return Mn.delete(e),r}var X="~effect/interfaces/Equal";function H(){if(arguments.length===1)return(e)=>ot(e,arguments[0]);return ot(arguments[0],arguments[1])}function ot(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(qt.has(e)||qt.has(t))return!1;return Xc(e,t,Yc)}function Zc(e,t,r){let n=Bn.has(e),o=jn.has(t);if(n&&o)return!0;if(n||o)return!1;Bn.add(e),jn.add(t);let s=r();return Bn.delete(e),jn.delete(t),s}var Bn=new WeakSet,jn=new WeakSet;function Yc(e,t){if(F(e)!==F(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=Ns(e),n=Ns(t);if(r!==n)return!1;let o=r&&n;if(typeof e==="function"&&!o)return!1;return Zc(e,t,()=>{if(o)return e[X](t);else if(Array.isArray(e)){if(!Array.isArray(t)||e.length!==t.length)return!1;return Qc(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 Us(new Uint8Array(e.buffer,e.byteOffset,e.byteLength),new Uint8Array(a.buffer,a.byteOffset,a.byteLength))}return Us(e,t)}else if(e instanceof Map){if(!(t instanceof Map)||e.size!==t.size)return!1;return ru(e,t)}else if(e instanceof Set){if(!(t instanceof Set)||e.size!==t.size)return!1;return ou(e,t)}return eu(e,t)})}function Xc(e,t,r){let n=Fr.get(e);if(!n)n=new WeakMap,Fr.set(e,n);else if(n.has(t))return n.get(t);let o=r(e,t);n.set(t,o);let s=Fr.get(t);if(!s)s=new WeakMap,Fr.set(t,s);return s.set(e,o),o}var Fr=new WeakMap;function Qc(e,t){for(let r=0;r<e.length;r++)if(!ot(e[r],t[r]))return!1;return!0}function Us(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 eu(e,t){let r=jt(e),n=jt(t);if(r.size!==n.size)return!1;for(let o of r)if(!n.has(o)||!ot(e[o],t[o]))return!1;return!0}function tu(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,T]=s[i];if(e(a,x)&&t(u,T)){s[i]=s[s.length-1],s.pop(),l=!0;break}}if(!l)return!1}return!0}}var ru=tu(ot,ot);function nu(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 ou=nu(ot),Ns=(e)=>_(e,X);var Gt=Symbol.for("~effect/Redactable"),su=(e)=>_(e,Gt);function wt(e){if(su(e))return qn(e);return e}function qn(e){return e[Gt](globalThis[It]?.context??au)}var It="~effect/Fiber/currentFiber",Ms=new Map,au={"~effect/Context":{},base:Ms,depth:0,mapUnsafe:Ms,pipe(){return fe(this,arguments)}};function N(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 T=i?.constructor;return T&&T!==Object.prototype.constructor&&T.name?`${T.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 iu;n.add(i);let T;if(Gt in i)T=l(qn(i),x);else if(Array.isArray(i))T=!o||i.length<=1?`[${i.map((S)=>l(S,x)).join(",")}]`:`[
1
+ import{ye,P,be,c,xe,f,V,ne,ce,d,p}from"./chunks/index-fvtsm6ez.js";import{Q}from"./chunks/index-rr4ytenf.js";import{he,D}from"./chunks/index-tfdjszbb.js";import{resolve as Os}from"node:path";function Ir(){return{"cc-safety-net":{description:"Operate CC Safety Net: explain blocks, rules, integrations, diagnostics",template:Q.slice(Q.indexOf("# CC Safety Net"))}}}var $c=new Set(["powershell","pwsh"]),Bc=new Set(["bash","dash","ksh","sh","zsh"]);function ks(e={}){return async({directory:t,homeDir:r})=>{let n=Os(t),o;return{config:async(s)=>{o=s;let a=Ir(),u=s.command??{};s.command={...a,...u}},"tool.execute.before":async(s,a)=>{Rn({configCwd:n,homeDir:r,tool:s.tool,sessionID:s.sessionID,toolInput:a.args,route:jc(s.tool,_n(o?.shell)),guardDependencies:e})}}}}function Rn({configCwd:e,homeDir:t,tool:r,sessionID:n,toolInput:o,route:s,guardDependencies:a={}}){let u=t===void 0?c():{...c(),home:t},l=(h,g,I=e)=>{he(u,h,()=>n,{agent:"opencode",toolName:g,cwd:I}),Rs(h)};if(typeof r!=="string"||r.trim()==="")l(P());let i;try{i=ne(o)}catch(h){if(!(h instanceof f))throw h;l(P({toolName:r}),r)}let x=qc(e,o);if(!ce(e)||!x){l(P({command:i,toolName:r}),r);return}let S=d(r,o,s,{configCwd:e,executionCwd:x},i??null);try{let h=D(u,S,{guard:{auditAllowed:xe(u.env),dependencies:a},audit:{agent:"opencode",getSessionId:()=>n}});Cs(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;Cs(h.evaluation);return}}function _n(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($c.has(o))return"powershell";if(Bc.has(o))return"posix";return"auto"}function jc(e,t){if(e==="bash")return{kind:"command",shell:t};return{kind:V(e)}}function qc(e,t){if(!t||typeof t!=="object"||Array.isArray(t))return e;if(!Object.hasOwn(t,"workdir"))return e;let r=t.workdir;if(typeof r!=="string"||r.trim()==="")return null;let n=process.platform==="win32"?Jc(r):r,o=Os(e,n);return ce(o)?o:null}function Jc(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 Cs(e){let t=ye(e,{includeEvidence:!0});if(t)Rs(t)}function Rs(e){throw Error(be(e))}var fe=(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}}},Gc={pipe(){return fe(this,arguments)}},Bt=function(){function e(){}return e.prototype=Gc,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 ie=(e)=>e;var _s=(e)=>()=>e;var St=_s(void 0),As=St;function rt(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 yt(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 jt=(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},qt=new WeakSet;function Jt(e){return typeof e==="string"}function An(e){return typeof e==="number"}function Fs(e){return typeof e==="symbol"}function Cr(e){return Jt(e)||An(e)||Fs(e)}function Tt(e){return typeof e==="function"}function Fn(e){return e!==void 0}function Pn(e){return e!=null}function Ps(e){return!0}function Dn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Or(e){return typeof e==="object"&&e!==null||Tt(e)}var _=C(2,(e,t)=>Or(e)&&(t in e));function Ds(e){return e instanceof Error}var Y="~effect/interfaces/Hash",F=(e)=>{switch(typeof e){case"number":return Ar(e);case"bigint":return G(e.toString(10));case"boolean":return G(String(e));case"symbol":return G(String(e));case"string":return G(e);case"undefined":return G("undefined");case"function":case"object":if(e===null)return G("null");else if(e instanceof Date){if(Number.isNaN(e.getTime()))return G("Invalid Date");return G(e.toISOString())}else if(e instanceof RegExp)return G(e.toString());else{if(qt.has(e))return kr(e);if(Nn.has(e))return Nn.get(e);let t=Wc(e,()=>{if(Kc(e))return e[Y]();else if(typeof e==="function")return kr(e);else if(e instanceof DataView)return Rr(new Uint8Array(e.buffer,e.byteOffset,e.byteLength));else if(Array.isArray(e)||ArrayBuffer.isView(e))return Rr(e);else if(e instanceof Map)return Vc(e);else if(e instanceof Set)return Hc(e);return zc(e)});return Nn.set(e,t),t}default:throw Error(`BUG: unhandled typeof ${typeof e} - please report an issue at https://github.com/Effect-TS/effect/issues`)}},kr=(e)=>{if(!Un.has(e))Un.set(e,Ar(Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)));return Un.get(e)},ue=C(2,(e,t)=>e*53^t),_r=(e)=>e&3221225471|e>>>1&1073741824,Kc=(e)=>_(e,Y),Ar=(e)=>{if(e!==e)return G("NaN");if(e===1/0)return G("Infinity");if(e===-1/0)return G("-Infinity");let t=e|0;if(t!==e)t^=e*4294967295;while(e>4294967295)t^=e/=4294967295;return _r(t)},G=(e)=>{let t=5381,r=e.length;while(r)t=t*33^e.charCodeAt(--r);return _r(t)},Ln=(e,t)=>{let r=12289;for(let n of t)r^=ue(F(n),F(e[n]));return _r(r)},zc=(e)=>Ln(e,jt(e)),$n=(e,t)=>(r)=>{let n=e;for(let o of r)n^=t(o);return _r(n)},Rr=$n(6151,F),Vc=$n(G("Map"),([e,t])=>ue(F(e),F(t))),Hc=$n(G("Set"),F),Un=new WeakMap,Nn=new WeakMap,Mn=new WeakSet;function Wc(e,t){if(Mn.has(e))return G("[Circular]");Mn.add(e);let r=t();return Mn.delete(e),r}var X="~effect/interfaces/Equal";function H(){if(arguments.length===1)return(e)=>ot(e,arguments[0]);return ot(arguments[0],arguments[1])}function ot(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(qt.has(e)||qt.has(t))return!1;return Xc(e,t,Yc)}function Zc(e,t,r){let n=Bn.has(e),o=jn.has(t);if(n&&o)return!0;if(n||o)return!1;Bn.add(e),jn.add(t);let s=r();return Bn.delete(e),jn.delete(t),s}var Bn=new WeakSet,jn=new WeakSet;function Yc(e,t){if(F(e)!==F(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=Ns(e),n=Ns(t);if(r!==n)return!1;let o=r&&n;if(typeof e==="function"&&!o)return!1;return Zc(e,t,()=>{if(o)return e[X](t);else if(Array.isArray(e)){if(!Array.isArray(t)||e.length!==t.length)return!1;return Qc(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 Us(new Uint8Array(e.buffer,e.byteOffset,e.byteLength),new Uint8Array(a.buffer,a.byteOffset,a.byteLength))}return Us(e,t)}else if(e instanceof Map){if(!(t instanceof Map)||e.size!==t.size)return!1;return ru(e,t)}else if(e instanceof Set){if(!(t instanceof Set)||e.size!==t.size)return!1;return ou(e,t)}return eu(e,t)})}function Xc(e,t,r){let n=Fr.get(e);if(!n)n=new WeakMap,Fr.set(e,n);else if(n.has(t))return n.get(t);let o=r(e,t);n.set(t,o);let s=Fr.get(t);if(!s)s=new WeakMap,Fr.set(t,s);return s.set(e,o),o}var Fr=new WeakMap;function Qc(e,t){for(let r=0;r<e.length;r++)if(!ot(e[r],t[r]))return!1;return!0}function Us(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 eu(e,t){let r=jt(e),n=jt(t);if(r.size!==n.size)return!1;for(let o of r)if(!n.has(o)||!ot(e[o],t[o]))return!1;return!0}function tu(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,T]=s[i];if(e(a,x)&&t(u,T)){s[i]=s[s.length-1],s.pop(),l=!0;break}}if(!l)return!1}return!0}}var ru=tu(ot,ot);function nu(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 ou=nu(ot),Ns=(e)=>_(e,X);var Gt=Symbol.for("~effect/Redactable"),su=(e)=>_(e,Gt);function wt(e){if(su(e))return qn(e);return e}function qn(e){return e[Gt](globalThis[It]?.context??au)}var It="~effect/Fiber/currentFiber",Ms=new Map,au={"~effect/Context":{},base:Ms,depth:0,mapUnsafe:Ms,pipe(){return fe(this,arguments)}};function N(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 T=i?.constructor;return T&&T!==Object.prototype.constructor&&T.name?`${T.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 iu;n.add(i);let T;if(Gt in i)T=l(qn(i),x);else if(Array.isArray(i))T=!o||i.length<=1?`[${i.map((S)=>l(S,x)).join(",")}]`:`[
2
2
  ${s(x+1)}${i.map((S)=>l(S,x+1)).join(`,
3
3
  `+s(x+1))}
4
4
  ${s(x)}]`;else if(i instanceof Date)T=Ls(i);else if(!t?.ignoreToString&&_(i,"toString")&&typeof i.toString==="function"&&i.toString!==Object.prototype.toString&&i.toString!==Array.prototype.toString){let S=cu(i);T=i instanceof Error&&i.cause?`${S} (cause: ${l(i.cause,x)})`:S}else if(Symbol.iterator in i)T=`${i.constructor.name}(${l(Array.from(i),x)})`;else{let S=u(i);if(!o||S.length<=1){let h=`{${S.map((g)=>`${Kt(g)}:${l(i[g],x)}`).join(",")}}`;T=a(i,h)}else{let h=`{