@codyswann/lisa 3.26.3 → 3.26.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 (65) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh +42 -24
  2. package/all/create-only/.lisaignore +15 -0
  3. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  4. package/dist/core/lisa-owned-hash-ledger.js +1 -0
  5. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.js +3 -3
  7. package/dist/utils/ignore-patterns.d.ts +9 -0
  8. package/dist/utils/ignore-patterns.d.ts.map +1 -1
  9. package/dist/utils/ignore-patterns.js +57 -25
  10. package/dist/utils/ignore-patterns.js.map +1 -1
  11. package/package.json +1 -1
  12. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa/hooks/block-managed-file-edits.sh +42 -24
  15. package/plugins/lisa-agy/plugin.json +1 -1
  16. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  19. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/hooks/block-managed-file-edits.sh +42 -24
  23. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cursor/hooks/block-managed-file-edits.sh +42 -24
  25. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo-agy/plugin.json +1 -1
  28. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  38. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  43. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  48. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails-agy/plugin.json +1 -1
  53. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  58. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  63. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/src/base/hooks/block-managed-file-edits.sh +42 -24
@@ -112,43 +112,61 @@ lisaignored() {
112
112
  local rel="$1"
113
113
  local list="$project_root/.lisaignore"
114
114
  [ -f "$list" ] || return 1
115
+ # Gitignore precedence: patterns are read in order and the LAST one to select
116
+ # the path decides, so `!x` re-includes something an earlier line ignored.
117
+ # This mirrors `matchesAnyPattern` in `src/utils/ignore-patterns.ts`; the two
118
+ # must agree, or an agent gets blocked on a file apply considers the
119
+ # project's, or waved through on one it does not.
120
+ #
121
+ # `ignored` carries shell truth: 0 = ignored, 1 = not.
122
+ local ignored=1
115
123
  local pattern
116
124
  while IFS= read -r pattern || [ -n "$pattern" ]; do
117
125
  pattern="${pattern#"${pattern%%[![:space:]]*}"}"
118
126
  pattern="${pattern%"${pattern##*[![:space:]]}"}"
119
127
  [ -n "$pattern" ] || continue
120
128
  case "$pattern" in \#*) continue ;; esac
121
- # A leading `!` is not gitignore negation here. The real matcher passes
122
- # patterns to minimatch, which negates by default, and it combines them with
123
- # `.some()` — so a single `!scripts/a.mjs` line reports EVERY OTHER PATH as
124
- # ignored. Measured:
125
- #
126
- # patterns=["!scripts/a.mjs"] path=scripts/a.mjs -> ignored=false
127
- # patterns=["!scripts/a.mjs"] path=scripts/b.mjs -> ignored=TRUE
128
- #
129
- # Reproducing that faithfully would mean disabling this guard on a typo.
130
- # Reproducing the intuitive gitignore reading would mean blocking files the
131
- # matcher considers ignored. Both are wrong, so the file is treated as
132
- # claimed and the write is allowed — the same direction this function errs
133
- # everywhere else. Filed upstream; when the matcher stops negating, delete
134
- # this branch rather than teaching it a second wrong answer.
135
- case "$pattern" in !*) return 0 ;; esac
129
+ local negated=1
136
130
  case "$pattern" in
137
- */)
138
- case "$rel" in "${pattern%/}"/* | "${pattern%/}") return 0 ;; esac
131
+ # A bare `!` selects nothing rather than everything.
132
+ !) continue ;;
133
+ !*)
134
+ negated=0
135
+ pattern="${pattern#!}"
139
136
  ;;
137
+ # `\!x` is a literal leading `!`, not a negation.
138
+ \\!*) pattern="${pattern#\\}" ;;
140
139
  esac
141
- # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
142
- case "$rel" in $pattern) return 0 ;; esac
140
+ local selected=1
143
141
  case "$pattern" in
144
- */*) ;;
145
- *)
146
- # shellcheck disable=SC2254 -- ditto, matched against the basename.
147
- case "${rel##*/}" in $pattern) return 0 ;; esac
142
+ */)
143
+ case "$rel" in "${pattern%/}"/* | "${pattern%/}") selected=0 ;; esac
148
144
  ;;
149
145
  esac
146
+ if [ "$selected" -ne 0 ]; then
147
+ # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
148
+ case "$rel" in $pattern) selected=0 ;; esac
149
+ fi
150
+ if [ "$selected" -ne 0 ]; then
151
+ case "$pattern" in
152
+ */*) ;;
153
+ *)
154
+ # shellcheck disable=SC2254 -- ditto, matched against the basename.
155
+ case "${rel##*/}" in $pattern) selected=0 ;; esac
156
+ ;;
157
+ esac
158
+ fi
159
+ [ "$selected" -eq 0 ] || continue
160
+ # A positive match ignores the path; a negated one un-ignores it. Both
161
+ # OVERWRITE any earlier verdict rather than short-circuiting — that is what
162
+ # makes the last matching pattern win.
163
+ if [ "$negated" -eq 0 ]; then
164
+ ignored=1
165
+ else
166
+ ignored=0
167
+ fi
150
168
  done <"$list"
151
- return 1
169
+ return "$ignored"
152
170
  }
153
171
 
154
172
  # A candidate rewritten relative to the project, so an absolute target from a
@@ -5,3 +5,18 @@
5
5
  # - Exact file names: eslint.config.mjs
6
6
  # - Directory patterns: .claude/hooks/
7
7
  # - Glob patterns: *.example.json, **/*.custom.ts
8
+ # - Negation: !scripts/keep.mjs re-includes a path an earlier line ignored.
9
+ # Patterns are read in order and the LAST one to match a path wins.
10
+ #
11
+ # What ignoring actually does: `lisa apply` skips the path entirely and reports
12
+ # `Kept (.lisaignore)`. The file becomes yours outright — which also means it
13
+ # stops receiving upstream fixes, permanently and silently. That is the point
14
+ # when you mean it, and a trap when you do not.
15
+ #
16
+ # So ignore a file to DECLARE a fork you have decided on, never to quiet a
17
+ # warning you have not read. In particular, do not ignore a Lisa-owned guard
18
+ # (anything under scripts/, or any path with a `lisa-` segment) to silence
19
+ # `lisa doctor` — apply already preserves your edits to those, so ignoring buys
20
+ # nothing and replaces a true warning with the false line "Enforcement guards
21
+ # match the installed Lisa version". Declare what your version defends with a
22
+ # `lisa-guard-capabilities:` line instead.
@@ -1 +1 @@
1
- {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAiVjC,CAAC"}
1
+ {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAkVjC,CAAC"}
@@ -224,6 +224,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
224
224
  "3882bd338e65b9db9e97aa9e70426f0f7a0d191539df51636e1a5e93a2501137",
225
225
  "92e0ff52fcb29bc112ddcf1c3d85432032596572a84f3c5806a5b0c286ae55b3",
226
226
  "c1b2abf324269c0248136500eb37d7c43559552f152a7c5d07a23c219fdc70b2",
227
+ "d517c6ab5dedf577ca713484cb41eb886f8580a6a351206901e4bf13f66421b5",
227
228
  ]),
228
229
  "scripts/lisa-hooks/block-no-verify.sh": Object.freeze([
229
230
  "031ef7a53fc49cb18665105b834b7b50ad6a43317e0821949809e877e0562ce4",
@@ -1 +1 @@
1
- {"version":3,"file":"lisa-owned-hash-ledger.js","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,6EAA6E;AAE7E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAE/B,MAAM,CAAC,MAAM,CAAC;IAChB,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC;QACtC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC;QACxC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC;QACxC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC;QACxC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC;QACxC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,+BAA+B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC7C,kEAAkE;KACnE,CAAC;IACF,uBAAuB,EAAE,MAAM,CAAC,MAAM,CAAC;QACrC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC;QACtC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC;QACtC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,0BAA0B,EAAE,MAAM,CAAC,MAAM,CAAC;QACxC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,yBAAyB,EAAE,MAAM,CAAC,MAAM,CAAC;QACvC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,gCAAgC,EAAE,MAAM,CAAC,MAAM,CAAC;QAC9C,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,gCAAgC,EAAE,MAAM,CAAC,MAAM,CAAC;QAC9C,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,sCAAsC,EAAE,MAAM,CAAC,MAAM,CAAC;QACpD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,2CAA2C,EAAE,MAAM,CAAC,MAAM,CAAC;QACzD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wCAAwC,EAAE,MAAM,CAAC,MAAM,CAAC;QACtD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,qCAAqC,EAAE,MAAM,CAAC,MAAM,CAAC;QACnD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,yCAAyC,EAAE,MAAM,CAAC,MAAM,CAAC;QACvD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,uCAAuC,EAAE,MAAM,CAAC,MAAM,CAAC;QACrD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,+BAA+B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC7C,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,oCAAoC,EAAE,MAAM,CAAC,MAAM,CAAC;QAClD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,sCAAsC,EAAE,MAAM,CAAC,MAAM,CAAC;QACpD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wBAAwB,EAAE,MAAM,CAAC,MAAM,CAAC;QACtC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,iDAAiD,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/D,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,oDAAoD,EAAE,MAAM,CAAC,MAAM,CAAC;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,gDAAgD,EAAE,MAAM,CAAC,MAAM,CAAC;QAC9D,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,uCAAuC,EAAE,MAAM,CAAC,MAAM,CAAC;QACrD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,gDAAgD,EAAE,MAAM,CAAC,MAAM,CAAC;QAC9D,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,yCAAyC,EAAE,MAAM,CAAC,MAAM,CAAC;QACvD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,qCAAqC,EAAE,MAAM,CAAC,MAAM,CAAC;QACnD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,2BAA2B,EAAE,MAAM,CAAC,MAAM,CAAC;QACzC,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,mCAAmC,EAAE,MAAM,CAAC,MAAM,CAAC;QACjD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,4BAA4B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC1C,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,kCAAkC,EAAE,MAAM,CAAC,MAAM,CAAC;QAChD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,4BAA4B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC1C,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,4BAA4B,EAAE,MAAM,CAAC,MAAM,CAAC;QAC1C,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wCAAwC,EAAE,MAAM,CAAC,MAAM,CAAC;QACtD,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,sDAAsD,EAAE,MAAM,CAAC,MAAM,CAAC;QACpE,kEAAkE;KACnE,CAAC;IACF,oDAAoD,EAAE,MAAM,CAAC,MAAM,CAAC;QAClE,kEAAkE;KACnE,CAAC;IACF,uCAAuC,EAAE,MAAM,CAAC,MAAM,CAAC;QACrD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;IACF,wCAAwC,EAAE,MAAM,CAAC,MAAM,CAAC;QACtD,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;QAClE,kEAAkE;KACnE,CAAC;CACH,CAAC,CAAC;AACH,sEAAsE"}
1
+ {"version":3,"file":"lisa-owned-hash-ledger.js","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
12
12
  "all/copy-overwrite/scripts/lisa-gates.mjs": "98652623c7451d05e48239e1678ad7977a323eaf7ff5ca4b4f3bbc0798396859",
13
13
  "all/copy-overwrite/scripts/lisa-hooks/block-direct-issue-create.sh": "8d1f04ef5c5da9db9190e22b37a435e118e39d6e7deafc5402b4593c8e9db2b2",
14
14
  "all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh": "3e709e1ec8a5843c00684bc477ad32ddab2c5fdb11f71d5aeec0c49609eaf025",
15
- "all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh": "92e0ff52fcb29bc112ddcf1c3d85432032596572a84f3c5806a5b0c286ae55b3",
15
+ "all/copy-overwrite/scripts/lisa-hooks/block-managed-file-edits.sh": "d517c6ab5dedf577ca713484cb41eb886f8580a6a351206901e4bf13f66421b5",
16
16
  "all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh": "83a8b8108654086afb6a6f23a8f09f9f041eb2cd4094c5531fa7ab1f75e873dc",
17
17
  "all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh": "234cb82cc9033bd04940a553d4ddc95b078e585e41777d6529d887676edc40b1",
18
18
  "all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh": "1d4fbe135f1bb4e861d286a6512ed201d4522250dfe3f9eba6584bc5ff166a95",
@@ -25,7 +25,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
25
25
  "all/copy-overwrite/scripts/schemas/lisa-command-envelope.v1.schema.json": "d153b7c2953a30f180e38f09e98240c63327f5196eeba9bdf545e5a1f125a879",
26
26
  "all/copy-overwrite/scripts/schemas/lisa-state-contract.v1.schema.json": "6b7afe4ffeba8aaa53871d8855172f7eacde05953b1321145218ec638ff5c1c4",
27
27
  "all/create-only/.agents/rules/README.md": "fd260fd9b2934d0d698a8098dfff07fedc071849d588e602d773666678c3d540",
28
- "all/create-only/.lisaignore": "3c9827e7e98daa46510b5357cd6dd9406da1821e64cc46d2eea9f311cd6d06b9",
28
+ "all/create-only/.lisaignore": "735dc0a28a19e3aebc3d71b1ddf8b077e96ab17013d0f1c49d87c08558a315df",
29
29
  "all/create-only/scripts/remote-agent-aws-setup.sh": "bb0e67a52ea5badd0f291961c3a9fb25559f1f35e3042642d389a49eb47f7e31",
30
30
  "all/create-only/specs/.keep": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
31
31
  "all/create-only/state/README.md": "6e36aaa9dab171d7d4d6351042e7ddc44ec834bb7f140bbb70b83972c3181fe3",
@@ -339,7 +339,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
339
339
  "plugins/src/base/hooks/block-direct-issue-create.sh": "22184f1dd1b96e818741bd4ae659446299535d7ce594817155edebac4172a7ba",
340
340
  "plugins/src/base/hooks/block-instruction-file-edits.agy.sh": "aa249cae53caeb3e0fb6d6af114e2756084f45a2896332fb063a9f20e4902125",
341
341
  "plugins/src/base/hooks/block-instruction-file-edits.sh": "d47314b66d6ce85f77d6e058f861eede4462b2b0a33d54e82ff1c931167ec3f7",
342
- "plugins/src/base/hooks/block-managed-file-edits.sh": "b0156d4b4e7fe3b28c6d0798db67af62b7fbc8247e6f1cc5092adf4c94e417af",
342
+ "plugins/src/base/hooks/block-managed-file-edits.sh": "5b11b2a9e31f4fe1b5ed62340024269722c42eebe22987e4dce327a519506832",
343
343
  "plugins/src/base/hooks/block-no-verify.agy.sh": "81c51041f8cb47bf79692c485c4f42ac7ed0a5a830821dc514cb468bc7a06b83",
344
344
  "plugins/src/base/hooks/block-no-verify.sh": "3ff7255f395a9552366faaac34d383f4c95fecfb39882cd1d57854356422fe61",
345
345
  "plugins/src/base/hooks/block-shell-json-parsing.agy.sh": "dc688efe382e7b8fe6f6c88bb0fde851527128cae778599559f23a93f1c9ec86",
@@ -24,6 +24,15 @@ export interface IgnorePatterns {
24
24
  export declare function parseIgnorePatterns(content: string): readonly string[];
25
25
  /**
26
26
  * Check if a relative path matches any of the ignore patterns
27
+ *
28
+ * Gitignore semantics: patterns are evaluated in order and the LAST one to
29
+ * select the path decides, so a `!` line re-includes something an earlier
30
+ * pattern ignored. Previously every pattern was combined with `.some()` and
31
+ * handed to minimatch verbatim, which negates by default — so `!scripts/a.mjs`
32
+ * selected every path that was NOT `scripts/a.mjs`, and one such line reported
33
+ * the entire project as ignored. An ignored path is not a candidate for apply,
34
+ * so that silently switched off Lisa's management of the whole repository, with
35
+ * no error and nothing to notice.
27
36
  * @param relativePath - Path relative to project root
28
37
  * @param patterns - Array of gitignore-style patterns
29
38
  * @returns True if the path should be ignored
@@ -1 +1 @@
1
- {"version":3,"file":"ignore-patterns.d.ts","sourceRoot":"","sources":["../../src/utils/ignore-patterns.ts"],"names":[],"mappings":"AAoCA;;GAEG;AACH,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAKtE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAwCT;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
1
+ {"version":3,"file":"ignore-patterns.d.ts","sourceRoot":"","sources":["../../src/utils/ignore-patterns.ts"],"names":[],"mappings":"AAoCA;;GAEG;AACH,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAKtE;AAkDD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,OAAO,CAgBT;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
@@ -38,8 +38,55 @@ export function parseIgnorePatterns(content) {
38
38
  .map(line => line.trim())
39
39
  .filter(line => line.length > 0 && !line.startsWith("#"));
40
40
  }
41
+ /**
42
+ * Whether one already-normalized path is selected by one pattern.
43
+ *
44
+ * The pattern here is always positive — negation is stripped by the caller and
45
+ * handled as precedence, never passed through to minimatch. That matters: a `!`
46
+ * reaching minimatch means "match everything EXCEPT", the opposite of what a
47
+ * gitignore reader intends.
48
+ * @param normalizedPath - Forward-slash path relative to the project root
49
+ * @param pattern - A single positive gitignore-style pattern
50
+ * @returns True when the pattern selects the path
51
+ */
52
+ function patternSelects(normalizedPath, pattern) {
53
+ // Handle directory patterns (ending with /)
54
+ if (pattern.endsWith("/")) {
55
+ const dirPattern = pattern.slice(0, -1);
56
+ return (normalizedPath.startsWith(`${dirPattern}/`) ||
57
+ normalizedPath === dirPattern);
58
+ }
59
+ // Handle exact match
60
+ if (normalizedPath === pattern) {
61
+ return true;
62
+ }
63
+ // Handle glob patterns
64
+ if (minimatchFn(normalizedPath, pattern, { dot: true })) {
65
+ return true;
66
+ }
67
+ // Handle patterns that should match anywhere in path
68
+ if (!pattern.includes("/")) {
69
+ // Pattern without slashes matches any path segment
70
+ const segments = normalizedPath.split("/");
71
+ return segments.some(segment => minimatchFn(segment, pattern, { dot: true }));
72
+ }
73
+ // Handle patterns starting with **/ (match anywhere)
74
+ if (pattern.startsWith("**/")) {
75
+ return minimatchFn(normalizedPath, pattern, { dot: true });
76
+ }
77
+ return false;
78
+ }
41
79
  /**
42
80
  * Check if a relative path matches any of the ignore patterns
81
+ *
82
+ * Gitignore semantics: patterns are evaluated in order and the LAST one to
83
+ * select the path decides, so a `!` line re-includes something an earlier
84
+ * pattern ignored. Previously every pattern was combined with `.some()` and
85
+ * handed to minimatch verbatim, which negates by default — so `!scripts/a.mjs`
86
+ * selected every path that was NOT `scripts/a.mjs`, and one such line reported
87
+ * the entire project as ignored. An ignored path is not a candidate for apply,
88
+ * so that silently switched off Lisa's management of the whole repository, with
89
+ * no error and nothing to notice.
43
90
  * @param relativePath - Path relative to project root
44
91
  * @param patterns - Array of gitignore-style patterns
45
92
  * @returns True if the path should be ignored
@@ -47,33 +94,18 @@ export function parseIgnorePatterns(content) {
47
94
  export function matchesAnyPattern(relativePath, patterns) {
48
95
  // Normalize path separators
49
96
  const normalizedPath = relativePath.replace(/\\/g, "/");
50
- return patterns.some(pattern => {
51
- // Handle directory patterns (ending with /)
52
- if (pattern.endsWith("/")) {
53
- const dirPattern = pattern.slice(0, -1);
54
- return (normalizedPath.startsWith(`${dirPattern}/`) ||
55
- normalizedPath === dirPattern);
56
- }
57
- // Handle exact match
58
- if (normalizedPath === pattern) {
59
- return true;
60
- }
61
- // Handle glob patterns
62
- if (minimatchFn(normalizedPath, pattern, { dot: true })) {
63
- return true;
64
- }
65
- // Handle patterns that should match anywhere in path
66
- if (!pattern.includes("/")) {
67
- // Pattern without slashes matches any path segment
68
- const segments = normalizedPath.split("/");
69
- return segments.some(segment => minimatchFn(segment, pattern, { dot: true }));
97
+ return patterns.reduce((ignored, raw) => {
98
+ const negated = raw.startsWith("!");
99
+ // A bare `!` selects nothing rather than everything; `\!x` is a literal `!x`.
100
+ const pattern = negated ? raw.slice(1) : raw.replace(/^\\!/, "!");
101
+ if (pattern.length === 0) {
102
+ return ignored;
70
103
  }
71
- // Handle patterns starting with **/ (match anywhere)
72
- if (pattern.startsWith("**/")) {
73
- return minimatchFn(normalizedPath, pattern, { dot: true });
104
+ if (!patternSelects(normalizedPath, pattern)) {
105
+ return ignored;
74
106
  }
75
- return false;
76
- });
107
+ return !negated;
108
+ }, false);
77
109
  }
78
110
  /**
79
111
  * Load and parse .lisaignore file from a project directory
@@ -1 +1 @@
1
- {"version":3,"file":"ignore-patterns.js","sourceRoot":"","sources":["../../src/utils/ignore-patterns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ignore-patterns.js","sourceRoot":"","sources":["../../src/utils/ignore-patterns.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -129,7 +129,7 @@
129
129
  }
130
130
  },
131
131
  "name": "@codyswann/lisa",
132
- "version": "3.26.3",
132
+ "version": "3.26.4",
133
133
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
134
134
  "main": "dist/index.js",
135
135
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -109,43 +109,61 @@ lisaignored() {
109
109
  local rel="$1"
110
110
  local list="$project_root/.lisaignore"
111
111
  [ -f "$list" ] || return 1
112
+ # Gitignore precedence: patterns are read in order and the LAST one to select
113
+ # the path decides, so `!x` re-includes something an earlier line ignored.
114
+ # This mirrors `matchesAnyPattern` in `src/utils/ignore-patterns.ts`; the two
115
+ # must agree, or an agent gets blocked on a file apply considers the
116
+ # project's, or waved through on one it does not.
117
+ #
118
+ # `ignored` carries shell truth: 0 = ignored, 1 = not.
119
+ local ignored=1
112
120
  local pattern
113
121
  while IFS= read -r pattern || [ -n "$pattern" ]; do
114
122
  pattern="${pattern#"${pattern%%[![:space:]]*}"}"
115
123
  pattern="${pattern%"${pattern##*[![:space:]]}"}"
116
124
  [ -n "$pattern" ] || continue
117
125
  case "$pattern" in \#*) continue ;; esac
118
- # A leading `!` is not gitignore negation here. The real matcher passes
119
- # patterns to minimatch, which negates by default, and it combines them with
120
- # `.some()` — so a single `!scripts/a.mjs` line reports EVERY OTHER PATH as
121
- # ignored. Measured:
122
- #
123
- # patterns=["!scripts/a.mjs"] path=scripts/a.mjs -> ignored=false
124
- # patterns=["!scripts/a.mjs"] path=scripts/b.mjs -> ignored=TRUE
125
- #
126
- # Reproducing that faithfully would mean disabling this guard on a typo.
127
- # Reproducing the intuitive gitignore reading would mean blocking files the
128
- # matcher considers ignored. Both are wrong, so the file is treated as
129
- # claimed and the write is allowed — the same direction this function errs
130
- # everywhere else. Filed upstream; when the matcher stops negating, delete
131
- # this branch rather than teaching it a second wrong answer.
132
- case "$pattern" in !*) return 0 ;; esac
126
+ local negated=1
133
127
  case "$pattern" in
134
- */)
135
- case "$rel" in "${pattern%/}"/* | "${pattern%/}") return 0 ;; esac
128
+ # A bare `!` selects nothing rather than everything.
129
+ !) continue ;;
130
+ !*)
131
+ negated=0
132
+ pattern="${pattern#!}"
136
133
  ;;
134
+ # `\!x` is a literal leading `!`, not a negation.
135
+ \\!*) pattern="${pattern#\\}" ;;
137
136
  esac
138
- # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
139
- case "$rel" in $pattern) return 0 ;; esac
137
+ local selected=1
140
138
  case "$pattern" in
141
- */*) ;;
142
- *)
143
- # shellcheck disable=SC2254 -- ditto, matched against the basename.
144
- case "${rel##*/}" in $pattern) return 0 ;; esac
139
+ */)
140
+ case "$rel" in "${pattern%/}"/* | "${pattern%/}") selected=0 ;; esac
145
141
  ;;
146
142
  esac
143
+ if [ "$selected" -ne 0 ]; then
144
+ # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
145
+ case "$rel" in $pattern) selected=0 ;; esac
146
+ fi
147
+ if [ "$selected" -ne 0 ]; then
148
+ case "$pattern" in
149
+ */*) ;;
150
+ *)
151
+ # shellcheck disable=SC2254 -- ditto, matched against the basename.
152
+ case "${rel##*/}" in $pattern) selected=0 ;; esac
153
+ ;;
154
+ esac
155
+ fi
156
+ [ "$selected" -eq 0 ] || continue
157
+ # A positive match ignores the path; a negated one un-ignores it. Both
158
+ # OVERWRITE any earlier verdict rather than short-circuiting — that is what
159
+ # makes the last matching pattern win.
160
+ if [ "$negated" -eq 0 ]; then
161
+ ignored=1
162
+ else
163
+ ignored=0
164
+ fi
147
165
  done <"$list"
148
- return 1
166
+ return "$ignored"
149
167
  }
150
168
 
151
169
  # A candidate rewritten relative to the project, so an absolute target from a
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -109,43 +109,61 @@ lisaignored() {
109
109
  local rel="$1"
110
110
  local list="$project_root/.lisaignore"
111
111
  [ -f "$list" ] || return 1
112
+ # Gitignore precedence: patterns are read in order and the LAST one to select
113
+ # the path decides, so `!x` re-includes something an earlier line ignored.
114
+ # This mirrors `matchesAnyPattern` in `src/utils/ignore-patterns.ts`; the two
115
+ # must agree, or an agent gets blocked on a file apply considers the
116
+ # project's, or waved through on one it does not.
117
+ #
118
+ # `ignored` carries shell truth: 0 = ignored, 1 = not.
119
+ local ignored=1
112
120
  local pattern
113
121
  while IFS= read -r pattern || [ -n "$pattern" ]; do
114
122
  pattern="${pattern#"${pattern%%[![:space:]]*}"}"
115
123
  pattern="${pattern%"${pattern##*[![:space:]]}"}"
116
124
  [ -n "$pattern" ] || continue
117
125
  case "$pattern" in \#*) continue ;; esac
118
- # A leading `!` is not gitignore negation here. The real matcher passes
119
- # patterns to minimatch, which negates by default, and it combines them with
120
- # `.some()` — so a single `!scripts/a.mjs` line reports EVERY OTHER PATH as
121
- # ignored. Measured:
122
- #
123
- # patterns=["!scripts/a.mjs"] path=scripts/a.mjs -> ignored=false
124
- # patterns=["!scripts/a.mjs"] path=scripts/b.mjs -> ignored=TRUE
125
- #
126
- # Reproducing that faithfully would mean disabling this guard on a typo.
127
- # Reproducing the intuitive gitignore reading would mean blocking files the
128
- # matcher considers ignored. Both are wrong, so the file is treated as
129
- # claimed and the write is allowed — the same direction this function errs
130
- # everywhere else. Filed upstream; when the matcher stops negating, delete
131
- # this branch rather than teaching it a second wrong answer.
132
- case "$pattern" in !*) return 0 ;; esac
126
+ local negated=1
133
127
  case "$pattern" in
134
- */)
135
- case "$rel" in "${pattern%/}"/* | "${pattern%/}") return 0 ;; esac
128
+ # A bare `!` selects nothing rather than everything.
129
+ !) continue ;;
130
+ !*)
131
+ negated=0
132
+ pattern="${pattern#!}"
136
133
  ;;
134
+ # `\!x` is a literal leading `!`, not a negation.
135
+ \\!*) pattern="${pattern#\\}" ;;
137
136
  esac
138
- # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
139
- case "$rel" in $pattern) return 0 ;; esac
137
+ local selected=1
140
138
  case "$pattern" in
141
- */*) ;;
142
- *)
143
- # shellcheck disable=SC2254 -- ditto, matched against the basename.
144
- case "${rel##*/}" in $pattern) return 0 ;; esac
139
+ */)
140
+ case "$rel" in "${pattern%/}"/* | "${pattern%/}") selected=0 ;; esac
145
141
  ;;
146
142
  esac
143
+ if [ "$selected" -ne 0 ]; then
144
+ # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
145
+ case "$rel" in $pattern) selected=0 ;; esac
146
+ fi
147
+ if [ "$selected" -ne 0 ]; then
148
+ case "$pattern" in
149
+ */*) ;;
150
+ *)
151
+ # shellcheck disable=SC2254 -- ditto, matched against the basename.
152
+ case "${rel##*/}" in $pattern) selected=0 ;; esac
153
+ ;;
154
+ esac
155
+ fi
156
+ [ "$selected" -eq 0 ] || continue
157
+ # A positive match ignores the path; a negated one un-ignores it. Both
158
+ # OVERWRITE any earlier verdict rather than short-circuiting — that is what
159
+ # makes the last matching pattern win.
160
+ if [ "$negated" -eq 0 ]; then
161
+ ignored=1
162
+ else
163
+ ignored=0
164
+ fi
147
165
  done <"$list"
148
- return 1
166
+ return "$ignored"
149
167
  }
150
168
 
151
169
  # A candidate rewritten relative to the project, so an absolute target from a
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -109,43 +109,61 @@ lisaignored() {
109
109
  local rel="$1"
110
110
  local list="$project_root/.lisaignore"
111
111
  [ -f "$list" ] || return 1
112
+ # Gitignore precedence: patterns are read in order and the LAST one to select
113
+ # the path decides, so `!x` re-includes something an earlier line ignored.
114
+ # This mirrors `matchesAnyPattern` in `src/utils/ignore-patterns.ts`; the two
115
+ # must agree, or an agent gets blocked on a file apply considers the
116
+ # project's, or waved through on one it does not.
117
+ #
118
+ # `ignored` carries shell truth: 0 = ignored, 1 = not.
119
+ local ignored=1
112
120
  local pattern
113
121
  while IFS= read -r pattern || [ -n "$pattern" ]; do
114
122
  pattern="${pattern#"${pattern%%[![:space:]]*}"}"
115
123
  pattern="${pattern%"${pattern##*[![:space:]]}"}"
116
124
  [ -n "$pattern" ] || continue
117
125
  case "$pattern" in \#*) continue ;; esac
118
- # A leading `!` is not gitignore negation here. The real matcher passes
119
- # patterns to minimatch, which negates by default, and it combines them with
120
- # `.some()` — so a single `!scripts/a.mjs` line reports EVERY OTHER PATH as
121
- # ignored. Measured:
122
- #
123
- # patterns=["!scripts/a.mjs"] path=scripts/a.mjs -> ignored=false
124
- # patterns=["!scripts/a.mjs"] path=scripts/b.mjs -> ignored=TRUE
125
- #
126
- # Reproducing that faithfully would mean disabling this guard on a typo.
127
- # Reproducing the intuitive gitignore reading would mean blocking files the
128
- # matcher considers ignored. Both are wrong, so the file is treated as
129
- # claimed and the write is allowed — the same direction this function errs
130
- # everywhere else. Filed upstream; when the matcher stops negating, delete
131
- # this branch rather than teaching it a second wrong answer.
132
- case "$pattern" in !*) return 0 ;; esac
126
+ local negated=1
133
127
  case "$pattern" in
134
- */)
135
- case "$rel" in "${pattern%/}"/* | "${pattern%/}") return 0 ;; esac
128
+ # A bare `!` selects nothing rather than everything.
129
+ !) continue ;;
130
+ !*)
131
+ negated=0
132
+ pattern="${pattern#!}"
136
133
  ;;
134
+ # `\!x` is a literal leading `!`, not a negation.
135
+ \\!*) pattern="${pattern#\\}" ;;
137
136
  esac
138
- # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
139
- case "$rel" in $pattern) return 0 ;; esac
137
+ local selected=1
140
138
  case "$pattern" in
141
- */*) ;;
142
- *)
143
- # shellcheck disable=SC2254 -- ditto, matched against the basename.
144
- case "${rel##*/}" in $pattern) return 0 ;; esac
139
+ */)
140
+ case "$rel" in "${pattern%/}"/* | "${pattern%/}") selected=0 ;; esac
145
141
  ;;
146
142
  esac
143
+ if [ "$selected" -ne 0 ]; then
144
+ # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
145
+ case "$rel" in $pattern) selected=0 ;; esac
146
+ fi
147
+ if [ "$selected" -ne 0 ]; then
148
+ case "$pattern" in
149
+ */*) ;;
150
+ *)
151
+ # shellcheck disable=SC2254 -- ditto, matched against the basename.
152
+ case "${rel##*/}" in $pattern) selected=0 ;; esac
153
+ ;;
154
+ esac
155
+ fi
156
+ [ "$selected" -eq 0 ] || continue
157
+ # A positive match ignores the path; a negated one un-ignores it. Both
158
+ # OVERWRITE any earlier verdict rather than short-circuiting — that is what
159
+ # makes the last matching pattern win.
160
+ if [ "$negated" -eq 0 ]; then
161
+ ignored=1
162
+ else
163
+ ignored=0
164
+ fi
147
165
  done <"$list"
148
- return 1
166
+ return "$ignored"
149
167
  }
150
168
 
151
169
  # A candidate rewritten relative to the project, so an absolute target from a
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "3.26.3",
3
+ "version": "3.26.4",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -109,43 +109,61 @@ lisaignored() {
109
109
  local rel="$1"
110
110
  local list="$project_root/.lisaignore"
111
111
  [ -f "$list" ] || return 1
112
+ # Gitignore precedence: patterns are read in order and the LAST one to select
113
+ # the path decides, so `!x` re-includes something an earlier line ignored.
114
+ # This mirrors `matchesAnyPattern` in `src/utils/ignore-patterns.ts`; the two
115
+ # must agree, or an agent gets blocked on a file apply considers the
116
+ # project's, or waved through on one it does not.
117
+ #
118
+ # `ignored` carries shell truth: 0 = ignored, 1 = not.
119
+ local ignored=1
112
120
  local pattern
113
121
  while IFS= read -r pattern || [ -n "$pattern" ]; do
114
122
  pattern="${pattern#"${pattern%%[![:space:]]*}"}"
115
123
  pattern="${pattern%"${pattern##*[![:space:]]}"}"
116
124
  [ -n "$pattern" ] || continue
117
125
  case "$pattern" in \#*) continue ;; esac
118
- # A leading `!` is not gitignore negation here. The real matcher passes
119
- # patterns to minimatch, which negates by default, and it combines them with
120
- # `.some()` — so a single `!scripts/a.mjs` line reports EVERY OTHER PATH as
121
- # ignored. Measured:
122
- #
123
- # patterns=["!scripts/a.mjs"] path=scripts/a.mjs -> ignored=false
124
- # patterns=["!scripts/a.mjs"] path=scripts/b.mjs -> ignored=TRUE
125
- #
126
- # Reproducing that faithfully would mean disabling this guard on a typo.
127
- # Reproducing the intuitive gitignore reading would mean blocking files the
128
- # matcher considers ignored. Both are wrong, so the file is treated as
129
- # claimed and the write is allowed — the same direction this function errs
130
- # everywhere else. Filed upstream; when the matcher stops negating, delete
131
- # this branch rather than teaching it a second wrong answer.
132
- case "$pattern" in !*) return 0 ;; esac
126
+ local negated=1
133
127
  case "$pattern" in
134
- */)
135
- case "$rel" in "${pattern%/}"/* | "${pattern%/}") return 0 ;; esac
128
+ # A bare `!` selects nothing rather than everything.
129
+ !) continue ;;
130
+ !*)
131
+ negated=0
132
+ pattern="${pattern#!}"
136
133
  ;;
134
+ # `\!x` is a literal leading `!`, not a negation.
135
+ \\!*) pattern="${pattern#\\}" ;;
137
136
  esac
138
- # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
139
- case "$rel" in $pattern) return 0 ;; esac
137
+ local selected=1
140
138
  case "$pattern" in
141
- */*) ;;
142
- *)
143
- # shellcheck disable=SC2254 -- ditto, matched against the basename.
144
- case "${rel##*/}" in $pattern) return 0 ;; esac
139
+ */)
140
+ case "$rel" in "${pattern%/}"/* | "${pattern%/}") selected=0 ;; esac
145
141
  ;;
146
142
  esac
143
+ if [ "$selected" -ne 0 ]; then
144
+ # shellcheck disable=SC2254 -- the pattern is a glob on purpose.
145
+ case "$rel" in $pattern) selected=0 ;; esac
146
+ fi
147
+ if [ "$selected" -ne 0 ]; then
148
+ case "$pattern" in
149
+ */*) ;;
150
+ *)
151
+ # shellcheck disable=SC2254 -- ditto, matched against the basename.
152
+ case "${rel##*/}" in $pattern) selected=0 ;; esac
153
+ ;;
154
+ esac
155
+ fi
156
+ [ "$selected" -eq 0 ] || continue
157
+ # A positive match ignores the path; a negated one un-ignores it. Both
158
+ # OVERWRITE any earlier verdict rather than short-circuiting — that is what
159
+ # makes the last matching pattern win.
160
+ if [ "$negated" -eq 0 ]; then
161
+ ignored=1
162
+ else
163
+ ignored=0
164
+ fi
147
165
  done <"$list"
148
- return 1
166
+ return "$ignored"
149
167
  }
150
168
 
151
169
  # A candidate rewritten relative to the project, so an absolute target from a