@codyswann/lisa 4.4.2 → 4.4.4

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.
Files changed (68) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +2 -18
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa-agy/plugin.json +1 -1
  8. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  9. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  10. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  11. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  12. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  14. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo-agy/plugin.json +1 -1
  18. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  23. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  28. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  33. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  38. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-rails-agy/plugin.json +1 -1
  43. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  48. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  53. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  55. package/scripts/plugin-parity-drift.mjs +66 -11
  56. package/plugins/lisa/.codex-plugin/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
  57. package/plugins/lisa/.codex-plugin/skills/lisa-parity-safety-net-rules/agents/openai.yaml +0 -4
  58. package/plugins/lisa/commands/parity-safety-net-rules.md +0 -6
  59. package/plugins/lisa/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
  60. package/plugins/lisa/skills/lisa-parity-safety-net-rules/agents/openai.yaml +0 -4
  61. package/plugins/lisa-agy/commands/lisa/parity-safety-net-rules.md +0 -6
  62. package/plugins/lisa-agy/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
  63. package/plugins/lisa-copilot/commands/lisa/parity-safety-net-rules.md +0 -6
  64. package/plugins/lisa-copilot/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
  65. package/plugins/lisa-cursor/commands/lisa/parity-safety-net-rules.md +0 -6
  66. package/plugins/lisa-cursor/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
  67. package/plugins/src/base/commands/parity-safety-net-rules.md +0 -6
  68. package/plugins/src/base/skills/lisa-parity-safety-net-rules/SKILL.md +0 -263
@@ -1,263 +0,0 @@
1
- ---
2
- name: lisa-parity-safety-net-rules
3
- description: "View, set, and verify the…"
4
- allowed-tools: ["Read", "Edit", "Write", "Bash"]
5
- synced-from: safety-net@cc-marketplace@1.0.6
6
- ---
7
-
8
- # Parity Safety-Net Rules
9
-
10
- Manage the **custom guard rules** that Lisa's safety-net hook enforces on every
11
- Bash command. The hook (`hooks/parity-safety-net.sh`, registered as a
12
- `PreToolUse` matcher on `Bash`) ships with built-in guards against catastrophic
13
- commands; this skill lets a project **view**, **set**, and **verify** *additional*
14
- project-specific rules on top of those built-ins.
15
-
16
- > **Lisa-native reimplementation.** Upstream 0.9.0 shipped two rule-management
17
- > skills (`set-custom-rules` + `verify-custom-rules`), which this skill
18
- > consolidates. Upstream 1.0.6 consolidated them too (into `cc-safety-net`) and
19
- > moved custom rules to a JSON rulebook system driven by the
20
- > `npx cc-safety-net rule` CLI. Lisa **deliberately keeps** its simpler
21
- > ERE-lines-file design: the Lisa hook must run identically on Codex, agy,
22
- > Copilot, Cursor, and Claude without an npx dependency, and a flat regex file
23
- > is auditable in any of those runtimes. It is reimplemented from scratch
24
- > against Lisa conventions — it does **not** port or invoke upstream plugin
25
- > code.
26
- >
27
- > **Drift tracking.** Pinned to `safety-net@cc-marketplace@1.0.6`.
28
- > `scripts/plugin-parity-drift.mjs` compares this pin against the upstream
29
- > version in the plugin cache and flags staleness. **Do not port or copy upstream
30
- > plugin code.**
31
- >
32
- > **2.0.3 review.** Upstream 2.0.2–2.0.3 added Kimi Code and Amp installation
33
- > support and release tooling. Those changes do not alter the upstream
34
- > rule-management skill or Lisa's project-local ERE rule contract, and Lisa does
35
- > not currently ship hooks for those runtimes, so no behavior is absorbed here.
36
- >
37
- > **2.0.4 review.** Method, so the next person can re-run it rather than trust
38
- > it: hash the sorted (relative path, content) manifest of every rule-bearing
39
- > directory in the plugin cache under both versions, then diff the trees
40
- > excluding `dist/`, `.git/`, and `node_modules/`. Result — `src/` (230 files),
41
- > `skills/` (1), and `hooks/` (1) are byte-identical across 2.0.3 and 2.0.4
42
- > (`src` digest `7e346e205fbd1aa7` both sides). The only non-generated
43
- > differences are the `version` string in `package.json`, `kimi.plugin.json`,
44
- > and `.claude-plugin/plugin.json`, plus a test-harness fix in
45
- > `tests/cli/cli-statusline.test.ts` that tolerates EPIPE when an oversized
46
- > payload is flushed at the bounded reader's cap. Everything else is rebuilt
47
- > `dist/` output. No rule, guard family, or skill surface moved, so nothing is
48
- > absorbed here — and, critically, **2.0.4 does not close the gap recorded
49
- > below**, which therefore carries forward unchanged rather than being dropped
50
- > at the new pin.
51
- >
52
- > **Known gap at the 2.0.4 pin.** Upstream 2.0.0 rebuilt its engine and added two
53
- > guard families this hook does **not** mirror: `secret.*` (blocks reading or
54
- > copying SSH keys, `.env` files, cloud credentials, and coding-CLI credential
55
- > stores) and `rm.git-metadata` (blocks deleting the `.git` control plane). Both
56
- > were measured against this hook rather than assumed — see the probe table in
57
- > `parity/plugin-routing/safety-net@cc-marketplace.md`. Absorbing `secret.*` also
58
- > needs a matcher change, because this hook is registered on `Bash` only while
59
- > upstream also screens file tools. That work is tracked separately in
60
- > `parity/FOLLOWUPS.md` §5; **do not treat this skill's built-in guard list as
61
- > covering secret access.**
62
-
63
- ## How the rules work
64
-
65
- - The hook always enforces its **built-in guards** (see below). These cannot be
66
- disabled from the rules file — they are the floor.
67
- - **Custom rules** live in a project-local file, one **POSIX extended regular
68
- expression (ERE)** per line. Blank lines and lines beginning with `#` are
69
- ignored. Matching is case-insensitive (`grep -Ei`).
70
- - If *any* built-in guard or custom rule matches the proposed command, the hook
71
- exits non-zero and the Bash call is **blocked**, with the reason shown to the
72
- agent.
73
-
74
- ### Rules file location
75
-
76
- Resolved in this order:
77
-
78
- 1. `$SAFETY_NET_RULES_FILE` (explicit override), else
79
- 2. `${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt`
80
-
81
- ```bash
82
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
83
- ```
84
-
85
- ### Built-in guards (always on)
86
-
87
- 1. `rm -rf` of a filesystem root, `$HOME`/`~`, or a top-level wildcard — with
88
- quote-aware boundaries, so wrapper/interpreter forms like
89
- `bash -c "rm -rf /"` and `python -c "… os.system('rm -rf /')"` match too.
90
- Path-prefixed spellings (`/bin/rm`, `./rm`) count as `rm` for every rm
91
- guard.
92
- 2. `rm -rf` target hardening: the cwd itself (`.`/`./`), `..`-traversal paths,
93
- home-anchored `~/…` paths, absolute paths outside the project (`/tmp`,
94
- `/var/tmp`, and `$TMPDIR` are allowed), `$VAR` targets other than `$TMPDIR`,
95
- and **any** recursive forced delete while the working directory is `$HOME`.
96
- 3. Force-pushing a protected branch
97
- (`main`/`master`/`production`/`prod`/`release`). `--force-with-lease` is
98
- intentionally allowed, and so is force-pushing a feature branch (sanctioned
99
- rebase workflow). Acceptable parity divergence: a refspec force-push
100
- (`git push origin +main`) is not blocked — upstream 1.0.6 allows it too.
101
- 4. `git reset --hard` / `git reset --merge` while the working tree is **dirty**
102
- (would discard work). Clean-tree resets are intentionally allowed.
103
- 5. `git rebase --abort` / `git rebase --quit` while the in-progress rebase
104
- holds human-made conflict resolutions (they would be discarded). A clean or
105
- untouched rebase state stays abortable — an agent-recoverable wedge; the
106
- apply backend and a missing `AUTO_MERGE` ref fail closed. Deliberate
107
- divergence: upstream blocks `rebase --abort` unconditionally.
108
- 6. `git checkout` discards: the `--` pathspec form (with or without a ref),
109
- `-f`/`--force`, `--pathspec-from-file`, and bare `git checkout .`.
110
- Branch switching and `-b`/`-B` creation stay allowed.
111
- 7. `git switch --discard-changes` / `-f`/`--force`.
112
- 8. `git restore` touching the worktree — only `git restore --staged <path>`
113
- without `--worktree` is allowed (unstaging is safe).
114
- 9. `git stash drop` / `git stash clear` (push/pop/list/apply stay allowed).
115
- 10. `git clean` with a force flag and no dry-run — `-n`/`--dry-run` anywhere
116
- makes it a safe preview.
117
- 11. `git branch -D` (or `-d` combined with `-f`, clustered or split);
118
- safe `-d` and rename `-m` stay allowed.
119
- 12. `git tag -d`, `git reflog delete`, `git worktree remove --force`.
120
- 13. Deletion via `find … -delete`, `find … -exec rm -rf`, and `xargs … rm -rf`
121
- (plain non-recursive `rm` on find/xargs output stays allowed).
122
- 14. Disk destroyers: `dd of=/dev/…`, `mkfs … /dev/…`, `shred`.
123
- 15. Destructive SQL — `DROP DATABASE/SCHEMA/TABLE`, `TRUNCATE TABLE`.
124
-
125
- Every git guard sees through leading git **global options** (`-C <path>`,
126
- `-c <k>=<v>`, `--git-dir[=…]`, `--no-pager`, …), so `git -C /path <destructive>`
127
- is screened the same as `git <destructive>`.
128
-
129
- Malformed hook input fails **closed** (exit 2 denies the command). A text-scan
130
- hook cannot exempt display commands, so prose like
131
- `echo "docs about rm -rf /"` can false-positive — quote-break the string or use
132
- the gh-writer heredoc form (payload is stripped before the guards run). For the
133
- same reason the rm guard treats substitution-wrapping as verdict-neutral (issue
134
- #1982): an executable `echo "$(rm -rf /)"` is blocked, and so are inert twins the
135
- shell would never expand — a single-quoted `echo '$(rm -rf /)'` or an escaped
136
- `echo "\$(rm -rf /)"` — since the scan has no quote-context awareness. The same
137
- quote-break or heredoc workaround applies. Spell that heredoc workaround with a
138
- **quote-pair** delimiter (`gh issue create --body-file - <<'EOF'`) — only that
139
- spelling reaches the payload-strip path. `<<"EOF"` and `<<\EOF` do not, and a
140
- `gh … <<\EOF` writer now fails closed instead.
141
-
142
- ## What this does and does not defend against
143
-
144
- Read this before treating the safety net as a wall.
145
-
146
- **What it is for:** stopping *accidental* catastrophic commands — an agent (or a
147
- human) that reaches for `rm -rf /`, force-pushes `main`, drops a table, or writes
148
- to a raw device by mistake. Within that job the guards above are the floor and
149
- they are always on.
150
-
151
- **What it is not:** a security boundary against a deliberate adversary. Two
152
- structural reasons, both accepted rather than open bugs:
153
-
154
- - **It is a text scan, not a shell engine.** It has no quote-context awareness,
155
- which produces the false positives described above and means an operation
156
- spelled in a way the patterns do not anticipate is not matched.
157
- - **The here-doc classifier re-implements bash's lexer in Python** to decide
158
- which payload text is inert data. Every divergence from real bash is a shape it
159
- mis-classifies. Eight distinct divergences were found and fixed across issues
160
- #1958 and #1993; the class is open-ended, so it is now closed as an accepted
161
- limitation rather than chased indefinitely.
162
-
163
- **Why that is tolerable:** a here-doc mis-classification degrades to the content
164
- guards rather than bypassing them. Only the narrow `gh issue|pr` writer form ever
165
- strips payload text; every other path passes the **raw** command to the guards,
166
- and since issue #1982 those guards see through substitution wrappers. So a
167
- mis-read here-doc costs no guard coverage for any destructive class listed above.
168
-
169
- **The honest bottom line:** an agent that is actively trying to run something
170
- destructive has many paths that never involve a here-doc at all — the safety net
171
- does not claim to close them. Do not rely on it as the only control over what an
172
- agent may execute; treat it as the last line of defence against mistakes, and
173
- keep the real boundary (credentials, permissions, environment isolation) outside
174
- the agent.
175
-
176
- ## View the current rules
177
-
178
- ```bash
179
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
180
- if [ -f "$RULES_FILE" ]; then
181
- echo "Custom safety-net rules ($RULES_FILE):"
182
- grep -vE '^[[:space:]]*(#|$)' "$RULES_FILE" || echo "(no active rules)"
183
- else
184
- echo "No custom rules file yet ($RULES_FILE). Only built-in guards are active."
185
- fi
186
- ```
187
-
188
- `Read` the file to show comments and structure as well.
189
-
190
- ## Set (add or edit) a rule
191
-
192
- A rule is an ERE matched against the full command string. Keep rules **specific**
193
- to avoid blocking legitimate work — anchor on the dangerous verb and its target.
194
-
195
- 1. Ensure the file exists, then **append** a commented rule (use `Edit`/`Write`,
196
- or append from the shell):
197
-
198
- ```bash
199
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
200
- mkdir -p "$(dirname "$RULES_FILE")"
201
- {
202
- echo "# Block deleting a Kubernetes namespace"
203
- echo 'kubectl[[:space:]]+delete[[:space:]]+namespace'
204
- } >> "$RULES_FILE"
205
- ```
206
-
207
- 2. **Always verify** the new rule (next section) before considering it set —
208
- confirm it blocks what it should and allows what it shouldn't.
209
-
210
- Editing/removing: open the file with `Edit` and change or delete the line.
211
- Removing a rule never affects the built-in guards.
212
-
213
- ## Verify the rules
214
-
215
- Two checks — both should pass before you trust a rule.
216
-
217
- ### 1. The ERE is valid
218
-
219
- An invalid regex would make the hook error on every command. Validate it:
220
-
221
- ```bash
222
- printf '%s' "$RULE" | grep -Eq -- "$RULE" 2>/dev/null && echo "valid ERE" \
223
- || echo "INVALID ERE — fix before saving"
224
- ```
225
-
226
- ### 2. The rule behaves as intended
227
-
228
- Drive the **actual hook** with a fake `PreToolUse` payload and assert the exit
229
- code (non-zero = blocked, 0 = allowed). Build the JSON with `jq` so the test
230
- command line itself never contains the dangerous literal:
231
-
232
- ```bash
233
- HOOK="${CLAUDE_PLUGIN_ROOT}/hooks/parity-safety-net.sh"
234
-
235
- check() { # check <expect: block|allow> <command>
236
- jq -nc --arg c "$2" '{tool_name:"Bash",tool_input:{command:$c}}' \
237
- | bash "$HOOK" >/dev/null 2>&1
238
- local code=$?
239
- local got=allow; [ "$code" -ne 0 ] && got=block
240
- printf '%-5s want=%-5s got=%-5s %s\n' \
241
- "$([ "$got" = "$1" ] && echo OK || echo FAIL)" "$1" "$got" "$2"
242
- }
243
-
244
- # Should block (matches the new rule):
245
- check block "kubectl delete namespace prod"
246
- # Should allow (must not over-match):
247
- check allow "kubectl get pods"
248
- ```
249
-
250
- Report a table of cases with want/got/verdict. If any case disagrees, tighten the
251
- ERE and re-verify.
252
-
253
- ## Rules
254
-
255
- - **Built-in guards are the floor** — custom rules only *add* blocks; they cannot
256
- weaken the built-ins.
257
- - **Prefer specific over broad** — a rule that blocks too much trains users to
258
- bypass the safety net. Anchor on verb + target.
259
- - **Verify every rule against the real hook** before saving — never ship an
260
- unverified or syntactically invalid ERE.
261
- - **Never weaken the net to unblock yourself.** If a built-in guard fires on a
262
- command that is genuinely safe, run it manually outside the agent after the
263
- user confirms — do not edit the hook to remove the guard.
@@ -1,4 +0,0 @@
1
- display_name: "Parity Safety Net Rules"
2
- short_description: "View, set, and verify the…"
3
- default_prompt:
4
- - "Use $lisa-parity-safety-net-rules: View, set, and verify the…."
@@ -1,6 +0,0 @@
1
- ---
2
- description: "View, set, and verify the custom guard rules enforced by Lisa's safety-net PreToolUse Bash hook — the cross-agent equivalent of the upstream safety-net plugin's set/verify-custom-rules skills."
3
- argument-hint: "[view | set <regex> | verify]"
4
- ---
5
-
6
- Use the /lisa-parity-safety-net-rules skill to view, set, or verify the project-local custom guard rules enforced by Lisa's safety-net Bash hook (`parity-safety-net.sh`). $ARGUMENTS
@@ -1,263 +0,0 @@
1
- ---
2
- name: lisa-parity-safety-net-rules
3
- description: "View, set, and verify the custom guard rules enforced by Lisa's safety-net PreToolUse Bash hook (parity-safety-net.sh). The consolidated cross-agent equivalent of the upstream safety-net plugin's set-custom-rules + verify-custom-rules skills — manages a project-local list of extended-regex patterns that block destructive shell commands, on Codex, agy, Copilot, Cursor, and Claude."
4
- allowed-tools: ["Read", "Edit", "Write", "Bash"]
5
- synced-from: safety-net@cc-marketplace@1.0.6
6
- ---
7
-
8
- # Parity Safety-Net Rules
9
-
10
- Manage the **custom guard rules** that Lisa's safety-net hook enforces on every
11
- Bash command. The hook (`hooks/parity-safety-net.sh`, registered as a
12
- `PreToolUse` matcher on `Bash`) ships with built-in guards against catastrophic
13
- commands; this skill lets a project **view**, **set**, and **verify** *additional*
14
- project-specific rules on top of those built-ins.
15
-
16
- > **Lisa-native reimplementation.** Upstream 0.9.0 shipped two rule-management
17
- > skills (`set-custom-rules` + `verify-custom-rules`), which this skill
18
- > consolidates. Upstream 1.0.6 consolidated them too (into `cc-safety-net`) and
19
- > moved custom rules to a JSON rulebook system driven by the
20
- > `npx cc-safety-net rule` CLI. Lisa **deliberately keeps** its simpler
21
- > ERE-lines-file design: the Lisa hook must run identically on Codex, agy,
22
- > Copilot, Cursor, and Claude without an npx dependency, and a flat regex file
23
- > is auditable in any of those runtimes. It is reimplemented from scratch
24
- > against Lisa conventions — it does **not** port or invoke upstream plugin
25
- > code.
26
- >
27
- > **Drift tracking.** Pinned to `safety-net@cc-marketplace@1.0.6`.
28
- > `scripts/plugin-parity-drift.mjs` compares this pin against the upstream
29
- > version in the plugin cache and flags staleness. **Do not port or copy upstream
30
- > plugin code.**
31
- >
32
- > **2.0.3 review.** Upstream 2.0.2–2.0.3 added Kimi Code and Amp installation
33
- > support and release tooling. Those changes do not alter the upstream
34
- > rule-management skill or Lisa's project-local ERE rule contract, and Lisa does
35
- > not currently ship hooks for those runtimes, so no behavior is absorbed here.
36
- >
37
- > **2.0.4 review.** Method, so the next person can re-run it rather than trust
38
- > it: hash the sorted (relative path, content) manifest of every rule-bearing
39
- > directory in the plugin cache under both versions, then diff the trees
40
- > excluding `dist/`, `.git/`, and `node_modules/`. Result — `src/` (230 files),
41
- > `skills/` (1), and `hooks/` (1) are byte-identical across 2.0.3 and 2.0.4
42
- > (`src` digest `7e346e205fbd1aa7` both sides). The only non-generated
43
- > differences are the `version` string in `package.json`, `kimi.plugin.json`,
44
- > and `.claude-plugin/plugin.json`, plus a test-harness fix in
45
- > `tests/cli/cli-statusline.test.ts` that tolerates EPIPE when an oversized
46
- > payload is flushed at the bounded reader's cap. Everything else is rebuilt
47
- > `dist/` output. No rule, guard family, or skill surface moved, so nothing is
48
- > absorbed here — and, critically, **2.0.4 does not close the gap recorded
49
- > below**, which therefore carries forward unchanged rather than being dropped
50
- > at the new pin.
51
- >
52
- > **Known gap at the 2.0.4 pin.** Upstream 2.0.0 rebuilt its engine and added two
53
- > guard families this hook does **not** mirror: `secret.*` (blocks reading or
54
- > copying SSH keys, `.env` files, cloud credentials, and coding-CLI credential
55
- > stores) and `rm.git-metadata` (blocks deleting the `.git` control plane). Both
56
- > were measured against this hook rather than assumed — see the probe table in
57
- > `parity/plugin-routing/safety-net@cc-marketplace.md`. Absorbing `secret.*` also
58
- > needs a matcher change, because this hook is registered on `Bash` only while
59
- > upstream also screens file tools. That work is tracked separately in
60
- > `parity/FOLLOWUPS.md` §5; **do not treat this skill's built-in guard list as
61
- > covering secret access.**
62
-
63
- ## How the rules work
64
-
65
- - The hook always enforces its **built-in guards** (see below). These cannot be
66
- disabled from the rules file — they are the floor.
67
- - **Custom rules** live in a project-local file, one **POSIX extended regular
68
- expression (ERE)** per line. Blank lines and lines beginning with `#` are
69
- ignored. Matching is case-insensitive (`grep -Ei`).
70
- - If *any* built-in guard or custom rule matches the proposed command, the hook
71
- exits non-zero and the Bash call is **blocked**, with the reason shown to the
72
- agent.
73
-
74
- ### Rules file location
75
-
76
- Resolved in this order:
77
-
78
- 1. `$SAFETY_NET_RULES_FILE` (explicit override), else
79
- 2. `${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt`
80
-
81
- ```bash
82
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
83
- ```
84
-
85
- ### Built-in guards (always on)
86
-
87
- 1. `rm -rf` of a filesystem root, `$HOME`/`~`, or a top-level wildcard — with
88
- quote-aware boundaries, so wrapper/interpreter forms like
89
- `bash -c "rm -rf /"` and `python -c "… os.system('rm -rf /')"` match too.
90
- Path-prefixed spellings (`/bin/rm`, `./rm`) count as `rm` for every rm
91
- guard.
92
- 2. `rm -rf` target hardening: the cwd itself (`.`/`./`), `..`-traversal paths,
93
- home-anchored `~/…` paths, absolute paths outside the project (`/tmp`,
94
- `/var/tmp`, and `$TMPDIR` are allowed), `$VAR` targets other than `$TMPDIR`,
95
- and **any** recursive forced delete while the working directory is `$HOME`.
96
- 3. Force-pushing a protected branch
97
- (`main`/`master`/`production`/`prod`/`release`). `--force-with-lease` is
98
- intentionally allowed, and so is force-pushing a feature branch (sanctioned
99
- rebase workflow). Acceptable parity divergence: a refspec force-push
100
- (`git push origin +main`) is not blocked — upstream 1.0.6 allows it too.
101
- 4. `git reset --hard` / `git reset --merge` while the working tree is **dirty**
102
- (would discard work). Clean-tree resets are intentionally allowed.
103
- 5. `git rebase --abort` / `git rebase --quit` while the in-progress rebase
104
- holds human-made conflict resolutions (they would be discarded). A clean or
105
- untouched rebase state stays abortable — an agent-recoverable wedge; the
106
- apply backend and a missing `AUTO_MERGE` ref fail closed. Deliberate
107
- divergence: upstream blocks `rebase --abort` unconditionally.
108
- 6. `git checkout` discards: the `--` pathspec form (with or without a ref),
109
- `-f`/`--force`, `--pathspec-from-file`, and bare `git checkout .`.
110
- Branch switching and `-b`/`-B` creation stay allowed.
111
- 7. `git switch --discard-changes` / `-f`/`--force`.
112
- 8. `git restore` touching the worktree — only `git restore --staged <path>`
113
- without `--worktree` is allowed (unstaging is safe).
114
- 9. `git stash drop` / `git stash clear` (push/pop/list/apply stay allowed).
115
- 10. `git clean` with a force flag and no dry-run — `-n`/`--dry-run` anywhere
116
- makes it a safe preview.
117
- 11. `git branch -D` (or `-d` combined with `-f`, clustered or split);
118
- safe `-d` and rename `-m` stay allowed.
119
- 12. `git tag -d`, `git reflog delete`, `git worktree remove --force`.
120
- 13. Deletion via `find … -delete`, `find … -exec rm -rf`, and `xargs … rm -rf`
121
- (plain non-recursive `rm` on find/xargs output stays allowed).
122
- 14. Disk destroyers: `dd of=/dev/…`, `mkfs … /dev/…`, `shred`.
123
- 15. Destructive SQL — `DROP DATABASE/SCHEMA/TABLE`, `TRUNCATE TABLE`.
124
-
125
- Every git guard sees through leading git **global options** (`-C <path>`,
126
- `-c <k>=<v>`, `--git-dir[=…]`, `--no-pager`, …), so `git -C /path <destructive>`
127
- is screened the same as `git <destructive>`.
128
-
129
- Malformed hook input fails **closed** (exit 2 denies the command). A text-scan
130
- hook cannot exempt display commands, so prose like
131
- `echo "docs about rm -rf /"` can false-positive — quote-break the string or use
132
- the gh-writer heredoc form (payload is stripped before the guards run). For the
133
- same reason the rm guard treats substitution-wrapping as verdict-neutral (issue
134
- #1982): an executable `echo "$(rm -rf /)"` is blocked, and so are inert twins the
135
- shell would never expand — a single-quoted `echo '$(rm -rf /)'` or an escaped
136
- `echo "\$(rm -rf /)"` — since the scan has no quote-context awareness. The same
137
- quote-break or heredoc workaround applies. Spell that heredoc workaround with a
138
- **quote-pair** delimiter (`gh issue create --body-file - <<'EOF'`) — only that
139
- spelling reaches the payload-strip path. `<<"EOF"` and `<<\EOF` do not, and a
140
- `gh … <<\EOF` writer now fails closed instead.
141
-
142
- ## What this does and does not defend against
143
-
144
- Read this before treating the safety net as a wall.
145
-
146
- **What it is for:** stopping *accidental* catastrophic commands — an agent (or a
147
- human) that reaches for `rm -rf /`, force-pushes `main`, drops a table, or writes
148
- to a raw device by mistake. Within that job the guards above are the floor and
149
- they are always on.
150
-
151
- **What it is not:** a security boundary against a deliberate adversary. Two
152
- structural reasons, both accepted rather than open bugs:
153
-
154
- - **It is a text scan, not a shell engine.** It has no quote-context awareness,
155
- which produces the false positives described above and means an operation
156
- spelled in a way the patterns do not anticipate is not matched.
157
- - **The here-doc classifier re-implements bash's lexer in Python** to decide
158
- which payload text is inert data. Every divergence from real bash is a shape it
159
- mis-classifies. Eight distinct divergences were found and fixed across issues
160
- #1958 and #1993; the class is open-ended, so it is now closed as an accepted
161
- limitation rather than chased indefinitely.
162
-
163
- **Why that is tolerable:** a here-doc mis-classification degrades to the content
164
- guards rather than bypassing them. Only the narrow `gh issue|pr` writer form ever
165
- strips payload text; every other path passes the **raw** command to the guards,
166
- and since issue #1982 those guards see through substitution wrappers. So a
167
- mis-read here-doc costs no guard coverage for any destructive class listed above.
168
-
169
- **The honest bottom line:** an agent that is actively trying to run something
170
- destructive has many paths that never involve a here-doc at all — the safety net
171
- does not claim to close them. Do not rely on it as the only control over what an
172
- agent may execute; treat it as the last line of defence against mistakes, and
173
- keep the real boundary (credentials, permissions, environment isolation) outside
174
- the agent.
175
-
176
- ## View the current rules
177
-
178
- ```bash
179
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
180
- if [ -f "$RULES_FILE" ]; then
181
- echo "Custom safety-net rules ($RULES_FILE):"
182
- grep -vE '^[[:space:]]*(#|$)' "$RULES_FILE" || echo "(no active rules)"
183
- else
184
- echo "No custom rules file yet ($RULES_FILE). Only built-in guards are active."
185
- fi
186
- ```
187
-
188
- `Read` the file to show comments and structure as well.
189
-
190
- ## Set (add or edit) a rule
191
-
192
- A rule is an ERE matched against the full command string. Keep rules **specific**
193
- to avoid blocking legitimate work — anchor on the dangerous verb and its target.
194
-
195
- 1. Ensure the file exists, then **append** a commented rule (use `Edit`/`Write`,
196
- or append from the shell):
197
-
198
- ```bash
199
- RULES_FILE="${SAFETY_NET_RULES_FILE:-${CLAUDE_PROJECT_DIR:-$PWD}/.claude/safety-net-rules.txt}"
200
- mkdir -p "$(dirname "$RULES_FILE")"
201
- {
202
- echo "# Block deleting a Kubernetes namespace"
203
- echo 'kubectl[[:space:]]+delete[[:space:]]+namespace'
204
- } >> "$RULES_FILE"
205
- ```
206
-
207
- 2. **Always verify** the new rule (next section) before considering it set —
208
- confirm it blocks what it should and allows what it shouldn't.
209
-
210
- Editing/removing: open the file with `Edit` and change or delete the line.
211
- Removing a rule never affects the built-in guards.
212
-
213
- ## Verify the rules
214
-
215
- Two checks — both should pass before you trust a rule.
216
-
217
- ### 1. The ERE is valid
218
-
219
- An invalid regex would make the hook error on every command. Validate it:
220
-
221
- ```bash
222
- printf '%s' "$RULE" | grep -Eq -- "$RULE" 2>/dev/null && echo "valid ERE" \
223
- || echo "INVALID ERE — fix before saving"
224
- ```
225
-
226
- ### 2. The rule behaves as intended
227
-
228
- Drive the **actual hook** with a fake `PreToolUse` payload and assert the exit
229
- code (non-zero = blocked, 0 = allowed). Build the JSON with `jq` so the test
230
- command line itself never contains the dangerous literal:
231
-
232
- ```bash
233
- HOOK="${CLAUDE_PLUGIN_ROOT}/hooks/parity-safety-net.sh"
234
-
235
- check() { # check <expect: block|allow> <command>
236
- jq -nc --arg c "$2" '{tool_name:"Bash",tool_input:{command:$c}}' \
237
- | bash "$HOOK" >/dev/null 2>&1
238
- local code=$?
239
- local got=allow; [ "$code" -ne 0 ] && got=block
240
- printf '%-5s want=%-5s got=%-5s %s\n' \
241
- "$([ "$got" = "$1" ] && echo OK || echo FAIL)" "$1" "$got" "$2"
242
- }
243
-
244
- # Should block (matches the new rule):
245
- check block "kubectl delete namespace prod"
246
- # Should allow (must not over-match):
247
- check allow "kubectl get pods"
248
- ```
249
-
250
- Report a table of cases with want/got/verdict. If any case disagrees, tighten the
251
- ERE and re-verify.
252
-
253
- ## Rules
254
-
255
- - **Built-in guards are the floor** — custom rules only *add* blocks; they cannot
256
- weaken the built-ins.
257
- - **Prefer specific over broad** — a rule that blocks too much trains users to
258
- bypass the safety net. Anchor on verb + target.
259
- - **Verify every rule against the real hook** before saving — never ship an
260
- unverified or syntactically invalid ERE.
261
- - **Never weaken the net to unblock yourself.** If a built-in guard fires on a
262
- command that is genuinely safe, run it manually outside the agent after the
263
- user confirms — do not edit the hook to remove the guard.
@@ -1,4 +0,0 @@
1
- display_name: "Parity Safety Net Rules"
2
- short_description: "View, set, and verify the custom guard rules enforced by Lisa's safety-net PreToolUse Bash hook (parity-safety-net.sh)"
3
- default_prompt:
4
- - "Use $lisa-parity-safety-net-rules: View, set, and verify the custom guard rules enforced by Lisa's safety-net PreToolUse Bash hook (parity-safety-net.sh)."
@@ -1,6 +0,0 @@
1
- ---
2
- description: "View, set, and verify the custom guard rules enforced by Lisa's safety-net PreToolUse Bash hook — the cross-agent equivalent of the upstream safety-net plugin's set/verify-custom-rules skills."
3
- argument-hint: "[view | set <regex> | verify]"
4
- ---
5
-
6
- Use the /lisa-parity-safety-net-rules skill to view, set, or verify the project-local custom guard rules enforced by Lisa's safety-net Bash hook (`parity-safety-net.sh`). $ARGUMENTS