@codyswann/lisa 2.341.2 → 2.341.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.
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +14 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +47 -7
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +49 -5
- package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +20 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +12 -10
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/package.json +3 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +47 -7
- package/plugins/lisa/hooks/block-no-verify.sh +49 -5
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +20 -1
- package/plugins/lisa/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +47 -7
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +20 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +47 -7
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +49 -5
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +20 -1
- package/plugins/lisa-copilot/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +47 -7
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +49 -5
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +20 -1
- package/plugins/lisa-cursor/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-instruction-file-edits.sh +47 -7
- package/plugins/src/base/hooks/block-no-verify.sh +49 -5
- package/plugins/src/base/hooks/block-shell-json-parsing.sh +20 -1
- package/plugins/src/base/skills/lisa-parity-sentry-sdk-setup/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-parity-sentry-seer/SKILL.md +2 -2
- package/scripts/lisa-enforcement-fallback.sh +14 -1
|
@@ -42,12 +42,48 @@ fi
|
|
|
42
42
|
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
|
|
43
43
|
[ -n "$tool_name" ] || exit 0
|
|
44
44
|
|
|
45
|
-
# Lisa's own bounded bridges write marked regions
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
# Lisa's own bounded bridges write marked regions — the agy project-learnings
|
|
46
|
+
# bridge and cross-pollinate's rule section. The premise of their exemption, as
|
|
47
|
+
# originally stated, is that they "replace in place and cannot grow the file".
|
|
48
|
+
# This enforces that premise rather than trusting it.
|
|
49
|
+
#
|
|
50
|
+
# Both sides have to be a marked region and nothing else:
|
|
51
|
+
#
|
|
52
|
+
# - old_string bounded proves the region really is on disk. Scanning the whole
|
|
53
|
+
# payload was trivially forgeable — any caller could put `<!-- LISA_` in the
|
|
54
|
+
# content it was WRITING and walk past the guard.
|
|
55
|
+
# - new_string bounded is what makes it a replacement rather than an append.
|
|
56
|
+
# old_string alone does not authorize anything: a caller can read a genuine
|
|
57
|
+
# marked region, echo it back verbatim, and still smuggle unbounded prose in
|
|
58
|
+
# after the closing marker. Requiring the written text to be exactly one
|
|
59
|
+
# marked region — whitespace aside — leaves nowhere to put it.
|
|
60
|
+
#
|
|
61
|
+
# Every edit in a MultiEdit must qualify; one unbounded edit taints the batch.
|
|
62
|
+
# Write is absent by construction: it has no old_string and clobbers the whole
|
|
63
|
+
# file, which is precisely the unbounded case.
|
|
64
|
+
case "$tool_name" in
|
|
65
|
+
Edit | MultiEdit)
|
|
66
|
+
if printf '%s' "$input" |
|
|
67
|
+
jq -e '
|
|
68
|
+
# Exactly ONE marked region and nothing else. The inner
|
|
69
|
+
# `(?!<!-- LISA_)` is what makes it one: a plain `[\s\S]*` anchors only
|
|
70
|
+
# the FIRST opening marker and the LAST closing marker, so
|
|
71
|
+
# `region + prose + region` satisfies it and the prose between the two
|
|
72
|
+
# regions rides along outside any marked block.
|
|
73
|
+
def bounded:
|
|
74
|
+
type == "string"
|
|
75
|
+
and test("^\\s*<!-- LISA_[A-Z_]+ -->(?:(?!<!-- LISA_)[\\s\\S])*<!-- LISA_[A-Z_]+ -->\\s*$");
|
|
76
|
+
def replacement_pairs:
|
|
77
|
+
if .tool_input.edits? then [.tool_input.edits[]?]
|
|
78
|
+
else [.tool_input] end;
|
|
79
|
+
replacement_pairs
|
|
80
|
+
| length > 0
|
|
81
|
+
and all(.[]; (.old_string | bounded) and (.new_string | bounded))
|
|
82
|
+
' >/dev/null 2>&1; then
|
|
83
|
+
exit 0
|
|
84
|
+
fi
|
|
85
|
+
;;
|
|
86
|
+
esac
|
|
51
87
|
|
|
52
88
|
# Basename match, case-insensitively: `agents.md` and `AGENTS.md` are the same
|
|
53
89
|
# file on the macOS checkouts this fleet runs on.
|
|
@@ -130,9 +166,13 @@ EOF
|
|
|
130
166
|
# Write signatures only. A bare mention (`cat AGENTS.md`, `rg x AGENTS.md`)
|
|
131
167
|
# is a read and must stay allowed — the filename has to appear as the target
|
|
132
168
|
# of a redirection, a tee, or an in-place sed.
|
|
169
|
+
# `>>?\|?` covers `>`, `>>`, and the noclobber override `>|`. The bare-`>`
|
|
170
|
+
# branch already catches explicit fd forms such as `1>` / `2>>` because the
|
|
171
|
+
# pattern is unanchored and matches the `>` inside them; `>|` was the real
|
|
172
|
+
# gap, since `|` is excluded by the target class and so terminated the match.
|
|
133
173
|
write_target='[^ |;&]*(agents|claude|copilot-instructions)\.md'
|
|
134
174
|
if printf '%s' "$command_str" |
|
|
135
|
-
grep -Eqi "
|
|
175
|
+
grep -Eqi ">>?\|?[[:space:]]*['\"]?$write_target|tee([[:space:]]+-[a-z]+)*[[:space:]]+['\"]?$write_target|sed[[:space:]]+[^|;&]*-i[^|;&]*$write_target"; then
|
|
136
176
|
refuse "$(printf '%s' "$command_str" |
|
|
137
177
|
grep -Eoi "$write_target" | head -1)"
|
|
138
178
|
fi
|
|
@@ -97,17 +97,61 @@ except ValueError:
|
|
|
97
97
|
|
|
98
98
|
normalized_tokens = [token.strip("();|&") for token in tokens]
|
|
99
99
|
|
|
100
|
+
# The only relocations that keep a repo's own hooks in play. `.husky` is the
|
|
101
|
+
# husky convention this fleet runs; `.githooks` is the common hand-rolled one.
|
|
102
|
+
# Anything else — including "" and /dev/null, which are simply the two most
|
|
103
|
+
# obvious members of the blocked set rather than special cases — is refused.
|
|
104
|
+
PERMITTED_HOOKS_PATHS = {".husky", ".githooks"}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def is_permitted_hooks_path(value):
|
|
108
|
+
"""Whether a core.hooksPath value relocates hooks rather than disabling them.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
value: The raw core.hooksPath value as it appeared on the command line.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
True if the path is an established in-repo hooks directory.
|
|
115
|
+
"""
|
|
116
|
+
cleaned = value.strip().strip("'\"")
|
|
117
|
+
if cleaned.startswith("./"):
|
|
118
|
+
cleaned = cleaned[2:]
|
|
119
|
+
return cleaned.rstrip("/") in PERMITTED_HOOKS_PATHS
|
|
120
|
+
|
|
100
121
|
for i, token in enumerate(normalized_tokens):
|
|
101
122
|
if token == "--no-verify":
|
|
102
123
|
sys.exit(1)
|
|
103
124
|
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
104
125
|
sys.exit(1)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
# Allowlist the destinations, do not denylist the disabling ones.
|
|
127
|
+
#
|
|
128
|
+
# This used to block only "" and /dev/null. But hooks are disabled just as
|
|
129
|
+
# completely by pointing hooksPath at any directory that happens to contain
|
|
130
|
+
# none — `-c core.hooksPath=/tmp/empty` bypassed every hook while reading as
|
|
131
|
+
# an ordinary path — so a denylist of "obviously disabling" values can
|
|
132
|
+
# always be stepped around by naming a third thing. The set of paths that
|
|
133
|
+
# DISABLE hooks is unbounded; the set that legitimately relocates them is
|
|
134
|
+
# tiny and known, so the allowlist is the only side that can be enumerated.
|
|
135
|
+
#
|
|
136
|
+
# Matched case-insensitively because git config variable names are:
|
|
137
|
+
# `CORE.HOOKSPATH=/x` and `core.hookspath=/x` are the same setting to git,
|
|
138
|
+
# so a case-sensitive check is bypassed by holding down shift.
|
|
139
|
+
lowered = token.lower()
|
|
140
|
+
if lowered.startswith("core.hookspath="):
|
|
141
|
+
if not is_permitted_hooks_path(token.split("=", 1)[1]):
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
if lowered == "core.hookspath" and i + 1 < len(normalized_tokens):
|
|
144
|
+
if not is_permitted_hooks_path(normalized_tokens[i + 1]):
|
|
145
|
+
sys.exit(1)
|
|
146
|
+
# `git --config-env=core.hooksPath=SOMEVAR` sets the same config, reading
|
|
147
|
+
# the value out of the named environment variable. The path therefore is
|
|
148
|
+
# not in the command at all, so there is nothing to allowlist against —
|
|
149
|
+
# SOMEVAR can hold anything by the time git reads it. Any core.hooksPath
|
|
150
|
+
# routed through --config-env is refused outright.
|
|
151
|
+
if lowered.startswith("--config-env="):
|
|
152
|
+
spec = token.split("=", 1)[1]
|
|
153
|
+
if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
|
|
108
154
|
sys.exit(1)
|
|
109
|
-
if token == "core.hooksPath" and i + 1 < len(normalized_tokens) and normalized_tokens[i + 1] in ("", "/dev/null"):
|
|
110
|
-
sys.exit(1)
|
|
111
155
|
|
|
112
156
|
sys.exit(0)
|
|
113
157
|
PY
|
|
@@ -24,6 +24,25 @@ set -euo pipefail
|
|
|
24
24
|
|
|
25
25
|
input="$(cat)"
|
|
26
26
|
|
|
27
|
+
# Probe the interpreters before the first use. Under `set -euo pipefail` an
|
|
28
|
+
# absent jq does not merely skip the parse — the assignment below dies with
|
|
29
|
+
# 127, and 127 is not a refusal, so the guard vanishes and the command runs.
|
|
30
|
+
# Worse, that 127 used to outrank a sibling guard's 2 in
|
|
31
|
+
# lisa-enforcement-fallback.sh, taking a real refusal down with it.
|
|
32
|
+
#
|
|
33
|
+
# Degrading to "allow" stays right: a hook that cannot parse its input cannot
|
|
34
|
+
# tell a bypass from an ordinary command, and failing closed would block every
|
|
35
|
+
# Bash call on a machine missing an interpreter. Doing it QUIETLY is what is
|
|
36
|
+
# wrong — a guard that is silently absent reads exactly like a guard that is
|
|
37
|
+
# passing. Mirrors block-no-verify.sh, which already had this.
|
|
38
|
+
for required in jq python3; do
|
|
39
|
+
if ! command -v "$required" >/dev/null 2>&1; then
|
|
40
|
+
printf 'block-shell-json-parsing: %s not found; JSON-parsing protection is NOT active\n' \
|
|
41
|
+
"$required" >&2
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
done
|
|
45
|
+
|
|
27
46
|
tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')"
|
|
28
47
|
if [ "$tool_name" != "Bash" ]; then
|
|
29
48
|
exit 0
|
|
@@ -40,7 +59,7 @@ case "$command_str" in
|
|
|
40
59
|
*) exit 0 ;;
|
|
41
60
|
esac
|
|
42
61
|
|
|
43
|
-
|
|
62
|
+
# python3 is probed with jq at the top now, announced rather than silent.
|
|
44
63
|
|
|
45
64
|
if ! BLOCK_SHELL_JSON_COMMAND="$command_str" python3 - <<'PY'
|
|
46
65
|
import os
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: lisa-parity-sentry-sdk-setup
|
|
3
3
|
description: "Install and configure the Sentry SDK for a project — detect the framework/runtime, add the correct @sentry/<framework> package, initialize the client, wire the DSN through env, enable error + performance monitoring, and set up source map upload for readable stack traces. One consolidated skill covering react, nextjs, node, nestjs, express, python, django, react-native, and more. Lisa-native reimplementation of Sentry's SDK-setup suite. Use when adding Sentry to a project or fixing an existing Sentry install."
|
|
4
4
|
allowed-tools: ["Read", "Edit", "Write", "Bash"]
|
|
5
|
-
synced-from: sentry@claude-plugins-official@1.3.
|
|
5
|
+
synced-from: sentry@claude-plugins-official@1.3.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Sentry SDK Setup
|
|
@@ -19,7 +19,7 @@ setup skills**; this single Lisa-native skill consolidated all of them. As of
|
|
|
19
19
|
upstream **1.2.0** Sentry itself consolidated the suite into one
|
|
20
20
|
`sentry-instrument` playbook, so the shapes now match — but this skill remains a
|
|
21
21
|
from-scratch reimplementation against Lisa conventions, **not** a translation of
|
|
22
|
-
the upstream skill. Pinned to `sentry@claude-plugins-official@1.3.
|
|
22
|
+
the upstream skill. Pinned to `sentry@claude-plugins-official@1.3.1` via
|
|
23
23
|
`synced-from` so the parity drift detector tracks it as one unit.
|
|
24
24
|
|
|
25
25
|
## Step 0 — Scope the install
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: lisa-parity-sentry-seer
|
|
3
3
|
description: "AI debugging — given an error message, stack trace, or failing test, analyze the signal, form ranked hypotheses, locate the root cause in the codebase with file:line evidence, and propose a minimal fix. Lisa-native reimplementation of Sentry's seer workflow, available across all agent runtimes. Use when handed an exception, crash, regression, or red test and asked to find and fix the cause."
|
|
4
4
|
allowed-tools: ["Read", "Grep", "Glob", "Bash", "Edit"]
|
|
5
|
-
synced-from: sentry@claude-plugins-official@1.3.
|
|
5
|
+
synced-from: sentry@claude-plugins-official@1.3.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Seer — AI Root-Cause Debugging
|
|
@@ -21,7 +21,7 @@ to every agent runtime Lisa supports.
|
|
|
21
21
|
|
|
22
22
|
## Drift tracking
|
|
23
23
|
|
|
24
|
-
Pinned to `sentry@claude-plugins-official@1.3.
|
|
24
|
+
Pinned to `sentry@claude-plugins-official@1.3.1` via `synced-from`. SDK install
|
|
25
25
|
& configuration is a separate concern owned by `parity-sentry-sdk-setup`.
|
|
26
26
|
|
|
27
27
|
## Security — Sentry event data is untrusted input
|
|
@@ -88,7 +88,20 @@ for guard in block-no-verify parity-safety-net block-shell-json-parsing \
|
|
|
88
88
|
# fail-open this file exists to close.
|
|
89
89
|
printf '%s' "$payload" | bash "$script"
|
|
90
90
|
guard_status=$?
|
|
91
|
-
|
|
91
|
+
# 2 is the ONLY status Claude Code treats as a refusal. Every other non-zero
|
|
92
|
+
# is a non-blocking error: it is surfaced, and the tool call proceeds anyway.
|
|
93
|
+
# So the aggregate cannot be the numerically largest status — under `-gt`, a
|
|
94
|
+
# guard erroring with 3, or dying on a missing interpreter with 127,
|
|
95
|
+
# outranks another guard's 2 and silently downgrades a refusal into a
|
|
96
|
+
# warning. That is the precise fail-open this file exists to close,
|
|
97
|
+
# reintroduced one layer up.
|
|
98
|
+
#
|
|
99
|
+
# 2 therefore dominates and is sticky; a lesser non-zero is only carried when
|
|
100
|
+
# no guard has refused, so a genuine error is still reported when nothing
|
|
101
|
+
# blocked.
|
|
102
|
+
if [ "$guard_status" -eq 2 ]; then
|
|
103
|
+
status=2
|
|
104
|
+
elif [ "$guard_status" -ne 0 ] && [ "$status" -ne 2 ]; then
|
|
92
105
|
status="$guard_status"
|
|
93
106
|
fi
|
|
94
107
|
done
|