@codyswann/lisa 4.15.1 → 4.17.0

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 (79) hide show
  1. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +62 -9
  2. package/dist/cli/doctor-override-floor-conflicts.d.ts +15 -0
  3. package/dist/cli/doctor-override-floor-conflicts.d.ts.map +1 -0
  4. package/dist/cli/doctor-override-floor-conflicts.js +225 -0
  5. package/dist/cli/doctor-override-floor-conflicts.js.map +1 -0
  6. package/dist/cli/doctor.d.ts.map +1 -1
  7. package/dist/cli/doctor.js +8 -0
  8. package/dist/cli/doctor.js.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  10. package/dist/core/lisa-owned-hash-ledger.js +2 -0
  11. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  12. package/dist/core/override-floors.d.ts +146 -0
  13. package/dist/core/override-floors.d.ts.map +1 -0
  14. package/dist/core/override-floors.js +200 -0
  15. package/dist/core/override-floors.js.map +1 -0
  16. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  17. package/dist/core/upstream-evidence-manifest.js +8 -2
  18. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  19. package/dist/opencode/plugin-templates/parity-safety-net.sh +62 -9
  20. package/dist/strategies/package-lisa.d.ts +59 -0
  21. package/dist/strategies/package-lisa.d.ts.map +1 -1
  22. package/dist/strategies/package-lisa.js +112 -130
  23. package/dist/strategies/package-lisa.js.map +1 -1
  24. package/package.json +1 -1
  25. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa/hooks/parity-safety-net.sh +62 -9
  28. package/plugins/lisa-agy/hooks/parity-safety-net.sh +62 -9
  29. package/plugins/lisa-agy/plugin.json +1 -1
  30. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  33. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +62 -9
  37. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +62 -9
  39. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo-agy/plugin.json +1 -1
  42. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  57. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  62. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails-agy/plugin.json +1 -1
  67. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  72. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  77. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/src/base/hooks/parity-safety-net.sh +62 -9
@@ -61,12 +61,20 @@
61
61
  # can match — including one whose target sits in the SAME quoted run as the
62
62
  # prose, which the #3106 scoping deliberately does not reach. Upstream exempts
63
63
  # those through an engine-only DISPLAY_COMMANDS list that a grep hook cannot
64
- # replicate. The same text-scan limit means the rm guard treats
64
+ # replicate. WHAT TO DO WHEN YOU HIT IT (issue #3191): do not reword the prose.
65
+ # When the destructive text is a commit message, an issue body or a results
66
+ # table, the string IS the deliverable and there is no narrower spelling that
67
+ # still says what it must say. Write the text to a file with the Write tool and
68
+ # pass it BY PATH — `gh issue comment --body-file <file>`, `git commit -F
69
+ # <file>` — because these guards scan the COMMAND and never the file. block()
70
+ # prints this at the wall so the operator is told rather than left to find it.
71
+ # The same text-scan limit means the rm guard treats
65
72
  # a substitution-wrapped catastrophic delete as verdict-neutral (issue #1982): an
66
73
  # executable `echo "$(rm -rf /)"` is blocked, but so are inert twins the shell
67
74
  # would never expand — a single-quoted `echo '$(rm -rf /)'` or an escaped
68
75
  # `echo "\$(rm -rf /)"` — because the scan has no quote-context awareness. That
69
- # over-block stays inside this accepted class. Workaround: quote-break the string
76
+ # over-block stays inside this accepted class. Workaround: the write-to-a-file
77
+ # and pass-by-path form above, or quote-break the string,
70
78
  # or use the gh-writer heredoc form, whose payload is stripped before the guards
71
79
  # run — spelled with a QUOTE-PAIR delimiter (`<<'EOF'`). That exact spelling is
72
80
  # what the SAFE path recognises; `<<"EOF"` and `<<\EOF` do not reach it, and
@@ -115,15 +123,58 @@ if [ -z "$command_str" ]; then
115
123
  exit 0
116
124
  fi
117
125
 
126
+ # Guidance printed under an ordinary destructive-guard refusal.
127
+ #
128
+ # It teaches the remedy at the wall, the way block_heredoc() does (gardener
129
+ # #1789, extended by issue #3191). It used to end in "narrow the command so it
130
+ # no longer matches the guard", which is advice the accepted prose class CANNOT
131
+ # follow: when the destructive text is a commit message, an issue body or a
132
+ # results table, the string IS the deliverable and there is no narrower spelling
133
+ # that still says what it must say. An operator acts on a printed remedy, so a
134
+ # remedy that cannot be performed is worse than none — it spends the detour and
135
+ # teaches that the refusal was a formality. The remedy that does work is the
136
+ # sibling heredoc one, one surface over: write the text with the Write tool,
137
+ # then pass it BY PATH. The guards scan the COMMAND and never the file, so the
138
+ # path form carries the text through intact.
139
+ #
140
+ # Both branches are printed on every ordinary refusal, deliberately: this guard
141
+ # is a text scan and CANNOT tell which one applies, so naming both and letting
142
+ # the reader pick is the only honest shape. Deciding for them is what the
143
+ # engine-only DISPLAY_COMMANDS list does, and the header explains why a grep
144
+ # hook cannot have one.
145
+ DESTRUCTIVE_GUIDANCE="$(
146
+ cat <<'EOF'
147
+ This command matched a destructive-operation guard. The guard is a text scan,
148
+ not a shell engine, so it cannot tell a destructive command you are RUNNING
149
+ from the same text quoted as CONTENT you are writing. Take the branch that
150
+ describes yours:
151
+
152
+ - The destructive text is only QUOTED CONTENT — a commit message, an issue or
153
+ PR body, a report, a results table, a note. Do not try to reword it; the
154
+ string is the deliverable and no narrower spelling still says what it must.
155
+ Write the text to a file with the Write tool and pass it BY PATH instead:
156
+ `gh issue comment --body-file <file>`, `gh pr create --body-file <file>`,
157
+ `git commit -F <file>`, or read the file back with the Read tool. This guard
158
+ scans the command, never the file, so the path form goes through intact.
159
+
160
+ - You genuinely intend to run the destructive operation. Ask the user to
161
+ confirm, then run it manually outside the agent.
162
+ EOF
163
+ )"
164
+
118
165
  # block() prints the reason to stderr (surfaced to the model) and exits 2 so the
119
166
  # Bash tool call is denied. $1 = human-readable reason for the block.
167
+ #
168
+ # $2 = optional guidance REPLACING $DESTRUCTIVE_GUIDANCE. block_heredoc() passes
169
+ # its own, because the two remedies are mutually exclusive by construction
170
+ # (issue #1958 F6): a heredoc payload IS executable shell, so the quoted-content
171
+ # branch is exactly the misdirection that finding removed, and the file-based
172
+ # execution advice is exactly what an ordinary prose refusal must not be given.
120
173
  block() {
121
174
  cat >&2 <<EOF
122
175
  Blocked by safety-net: $1
123
176
 
124
- This command matched a destructive-operation guard. If it is genuinely safe and
125
- intentional, ask the user to confirm, then run it manually outside the agent, or
126
- narrow the command so it no longer matches the guard.
177
+ ${2:-$DESTRUCTIVE_GUIDANCE}
127
178
  EOF
128
179
  exit 2
129
180
  }
@@ -199,20 +250,22 @@ EOF
199
250
  # depends on the command shape (issue #1958): `git commit -m "$(cat <<EOF …)"`
200
251
  # attempts get the commit -F text; every other heredoc denial gets the
201
252
  # file-based execution guidance instead — the commit text is misleading there.
253
+ # Both are passed as block()'s SECOND argument, which REPLACES the ordinary
254
+ # destructive guidance rather than appending to it (issue #3191): that guidance
255
+ # offers a quoted-content branch, and a heredoc payload is executable shell, so
256
+ # printing it here would reintroduce the misdirection #1958 F6 removed.
202
257
  # The git-commit detection inlines the GIT_GLOBAL_OPTS shape (defined later in
203
258
  # this file, after the heredoc dispatch runs) so `git -C <path> commit` and
204
259
  # `git -c k=v commit` spellings are still recognized.
205
260
  block_heredoc() {
206
261
  if scan -E "$command_str" \
207
262
  '(^|[^[:alnum:]_-])git[[:space:]]+(-[^;&|[:space:]]+([[:space:]]+[^-;&|[:space:]][^;&|[:space:]]*)?[[:space:]]+)*commit([^[:alnum:]_-]|$)'; then
208
- block "$1
209
- Heredoc commit invocations are blocked (the payload is executable shell).
263
+ block "$1" "Heredoc commit invocations are blocked (the payload is executable shell).
210
264
  Fix: write the commit message to a file and run \`git commit -F <file>\`.
211
265
  Every commit must also carry a Co-authored-by trailer for a supported agent
212
266
  (Claude/Codex/OpenCode) — the commit-msg hook enforces this."
213
267
  fi
214
- block "$1
215
- Heredoc payloads are blocked here (the payload is executable shell).
268
+ block "$1" "Heredoc payloads are blocked here (the payload is executable shell).
216
269
  Fix: write the payload to a file with the Write tool, then execute that file
217
270
  directly (for example \`python3 <file>\` or \`bash <file>\`)."
218
271
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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": "4.15.1",
3
+ "version": "4.17.0",
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"
@@ -61,12 +61,20 @@
61
61
  # can match — including one whose target sits in the SAME quoted run as the
62
62
  # prose, which the #3106 scoping deliberately does not reach. Upstream exempts
63
63
  # those through an engine-only DISPLAY_COMMANDS list that a grep hook cannot
64
- # replicate. The same text-scan limit means the rm guard treats
64
+ # replicate. WHAT TO DO WHEN YOU HIT IT (issue #3191): do not reword the prose.
65
+ # When the destructive text is a commit message, an issue body or a results
66
+ # table, the string IS the deliverable and there is no narrower spelling that
67
+ # still says what it must say. Write the text to a file with the Write tool and
68
+ # pass it BY PATH — `gh issue comment --body-file <file>`, `git commit -F
69
+ # <file>` — because these guards scan the COMMAND and never the file. block()
70
+ # prints this at the wall so the operator is told rather than left to find it.
71
+ # The same text-scan limit means the rm guard treats
65
72
  # a substitution-wrapped catastrophic delete as verdict-neutral (issue #1982): an
66
73
  # executable `echo "$(rm -rf /)"` is blocked, but so are inert twins the shell
67
74
  # would never expand — a single-quoted `echo '$(rm -rf /)'` or an escaped
68
75
  # `echo "\$(rm -rf /)"` — because the scan has no quote-context awareness. That
69
- # over-block stays inside this accepted class. Workaround: quote-break the string
76
+ # over-block stays inside this accepted class. Workaround: the write-to-a-file
77
+ # and pass-by-path form above, or quote-break the string,
70
78
  # or use the gh-writer heredoc form, whose payload is stripped before the guards
71
79
  # run — spelled with a QUOTE-PAIR delimiter (`<<'EOF'`). That exact spelling is
72
80
  # what the SAFE path recognises; `<<"EOF"` and `<<\EOF` do not reach it, and
@@ -115,15 +123,58 @@ if [ -z "$command_str" ]; then
115
123
  exit 0
116
124
  fi
117
125
 
126
+ # Guidance printed under an ordinary destructive-guard refusal.
127
+ #
128
+ # It teaches the remedy at the wall, the way block_heredoc() does (gardener
129
+ # #1789, extended by issue #3191). It used to end in "narrow the command so it
130
+ # no longer matches the guard", which is advice the accepted prose class CANNOT
131
+ # follow: when the destructive text is a commit message, an issue body or a
132
+ # results table, the string IS the deliverable and there is no narrower spelling
133
+ # that still says what it must say. An operator acts on a printed remedy, so a
134
+ # remedy that cannot be performed is worse than none — it spends the detour and
135
+ # teaches that the refusal was a formality. The remedy that does work is the
136
+ # sibling heredoc one, one surface over: write the text with the Write tool,
137
+ # then pass it BY PATH. The guards scan the COMMAND and never the file, so the
138
+ # path form carries the text through intact.
139
+ #
140
+ # Both branches are printed on every ordinary refusal, deliberately: this guard
141
+ # is a text scan and CANNOT tell which one applies, so naming both and letting
142
+ # the reader pick is the only honest shape. Deciding for them is what the
143
+ # engine-only DISPLAY_COMMANDS list does, and the header explains why a grep
144
+ # hook cannot have one.
145
+ DESTRUCTIVE_GUIDANCE="$(
146
+ cat <<'EOF'
147
+ This command matched a destructive-operation guard. The guard is a text scan,
148
+ not a shell engine, so it cannot tell a destructive command you are RUNNING
149
+ from the same text quoted as CONTENT you are writing. Take the branch that
150
+ describes yours:
151
+
152
+ - The destructive text is only QUOTED CONTENT — a commit message, an issue or
153
+ PR body, a report, a results table, a note. Do not try to reword it; the
154
+ string is the deliverable and no narrower spelling still says what it must.
155
+ Write the text to a file with the Write tool and pass it BY PATH instead:
156
+ `gh issue comment --body-file <file>`, `gh pr create --body-file <file>`,
157
+ `git commit -F <file>`, or read the file back with the Read tool. This guard
158
+ scans the command, never the file, so the path form goes through intact.
159
+
160
+ - You genuinely intend to run the destructive operation. Ask the user to
161
+ confirm, then run it manually outside the agent.
162
+ EOF
163
+ )"
164
+
118
165
  # block() prints the reason to stderr (surfaced to the model) and exits 2 so the
119
166
  # Bash tool call is denied. $1 = human-readable reason for the block.
167
+ #
168
+ # $2 = optional guidance REPLACING $DESTRUCTIVE_GUIDANCE. block_heredoc() passes
169
+ # its own, because the two remedies are mutually exclusive by construction
170
+ # (issue #1958 F6): a heredoc payload IS executable shell, so the quoted-content
171
+ # branch is exactly the misdirection that finding removed, and the file-based
172
+ # execution advice is exactly what an ordinary prose refusal must not be given.
120
173
  block() {
121
174
  cat >&2 <<EOF
122
175
  Blocked by safety-net: $1
123
176
 
124
- This command matched a destructive-operation guard. If it is genuinely safe and
125
- intentional, ask the user to confirm, then run it manually outside the agent, or
126
- narrow the command so it no longer matches the guard.
177
+ ${2:-$DESTRUCTIVE_GUIDANCE}
127
178
  EOF
128
179
  exit 2
129
180
  }
@@ -199,20 +250,22 @@ EOF
199
250
  # depends on the command shape (issue #1958): `git commit -m "$(cat <<EOF …)"`
200
251
  # attempts get the commit -F text; every other heredoc denial gets the
201
252
  # file-based execution guidance instead — the commit text is misleading there.
253
+ # Both are passed as block()'s SECOND argument, which REPLACES the ordinary
254
+ # destructive guidance rather than appending to it (issue #3191): that guidance
255
+ # offers a quoted-content branch, and a heredoc payload is executable shell, so
256
+ # printing it here would reintroduce the misdirection #1958 F6 removed.
202
257
  # The git-commit detection inlines the GIT_GLOBAL_OPTS shape (defined later in
203
258
  # this file, after the heredoc dispatch runs) so `git -C <path> commit` and
204
259
  # `git -c k=v commit` spellings are still recognized.
205
260
  block_heredoc() {
206
261
  if scan -E "$command_str" \
207
262
  '(^|[^[:alnum:]_-])git[[:space:]]+(-[^;&|[:space:]]+([[:space:]]+[^-;&|[:space:]][^;&|[:space:]]*)?[[:space:]]+)*commit([^[:alnum:]_-]|$)'; then
208
- block "$1
209
- Heredoc commit invocations are blocked (the payload is executable shell).
263
+ block "$1" "Heredoc commit invocations are blocked (the payload is executable shell).
210
264
  Fix: write the commit message to a file and run \`git commit -F <file>\`.
211
265
  Every commit must also carry a Co-authored-by trailer for a supported agent
212
266
  (Claude/Codex/OpenCode) — the commit-msg hook enforces this."
213
267
  fi
214
- block "$1
215
- Heredoc payloads are blocked here (the payload is executable shell).
268
+ block "$1" "Heredoc payloads are blocked here (the payload is executable shell).
216
269
  Fix: write the payload to a file with the Write tool, then execute that file
217
270
  directly (for example \`python3 <file>\` or \`bash <file>\`)."
218
271
  }