@codyswann/lisa 4.8.0 → 4.8.2

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 (85) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +72 -0
  2. package/dist/codex/hooks-installer.d.ts +24 -0
  3. package/dist/codex/hooks-installer.d.ts.map +1 -1
  4. package/dist/codex/hooks-installer.js +46 -7
  5. package/dist/codex/hooks-installer.js.map +1 -1
  6. package/dist/codex/scripts/block-migration-edits.sh +75 -3
  7. package/dist/codex/scripts/block-suppress-directives.sh +72 -2
  8. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  9. package/dist/core/lisa-owned-hash-ledger.js +2 -0
  10. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  11. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  12. package/dist/core/upstream-evidence-manifest.js +15 -4
  13. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  14. package/dist/migrations/ensure-lighthouse-collect-options.d.ts +3 -1
  15. package/dist/migrations/ensure-lighthouse-collect-options.d.ts.map +1 -1
  16. package/dist/migrations/ensure-lighthouse-collect-options.js +12 -4
  17. package/dist/migrations/ensure-lighthouse-collect-options.js.map +1 -1
  18. package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts +51 -0
  19. package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts.map +1 -0
  20. package/dist/migrations/ensure-nightly-e2e-workflow-pins.js +122 -0
  21. package/dist/migrations/ensure-nightly-e2e-workflow-pins.js.map +1 -0
  22. package/dist/migrations/index.d.ts +1 -0
  23. package/dist/migrations/index.d.ts.map +1 -1
  24. package/dist/migrations/index.js +3 -0
  25. package/dist/migrations/index.js.map +1 -1
  26. package/expo/create-only/lighthouserc.js +1 -1
  27. package/package.json +1 -1
  28. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-agy/plugin.json +1 -1
  31. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  34. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo-agy/plugin.json +1 -1
  41. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/hooks/block-migration-edits.sh +79 -10
  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-copilot/hooks/block-migration-edits.sh +79 -10
  54. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs-cursor/hooks/block-migration-edits.sh +79 -10
  56. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  59. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  64. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-rails-agy/plugin.json +1 -1
  69. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-typescript/hooks/block-suppress-directives.sh +71 -3
  74. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  75. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript-copilot/hooks/block-suppress-directives.sh +71 -3
  77. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-cursor/hooks/block-suppress-directives.sh +71 -3
  79. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  82. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/src/nestjs/hooks/block-migration-edits.sh +79 -10
  85. package/plugins/src/typescript/hooks/block-suppress-directives.sh +71 -3
@@ -8,23 +8,96 @@
8
8
  # break the schema/migration contract.
9
9
  # Reference: https://docs.claude.com/en/docs/claude-code/hooks
10
10
  # Exit code 2 blocks the tool call and surfaces stderr to Claude.
11
+ #
12
+ # WHAT A REFUSAL HOOK OWES THAT AN ON-EDIT HOOK DOES NOT. The five PostToolUse
13
+ # scripts may bail out when they cannot run: the write has already happened and
14
+ # a skipped lint is a missed report. This one decides whether the write happens
15
+ # at all, so a bail-out is a PERMIT — it lets through exactly the edit it exists
16
+ # to stop, while reporting success. Every exit below is therefore one of three
17
+ # things and never a fourth: out of scope (nothing to judge), judged by the
18
+ # project's declared task, or judged by the built-in. "Could not judge" refuses.
11
19
 
12
20
  JSON_INPUT=$(cat)
13
21
 
22
+ # Project rule (host rules, .agents/rules/): never parse JSON in shell with
23
+ # grep/sed/cut/awk — always use jq.
24
+ #
25
+ # FAIL CLOSED, reversed from the `exit 0` this shipped with. Without jq the
26
+ # payload cannot be read at all, so the hook cannot tell a migration edit from
27
+ # any other write — and it said so on stderr while permitting the edit anyway,
28
+ # which is a guard announcing that it did not check and then waving the write
29
+ # through. The refusal is unscoped for the same reason: with no parse there is
30
+ # no path to scope it to.
14
31
  if ! command -v jq >/dev/null 2>&1; then
15
- echo "⚠ block-migration-edits: jq not available, allowing edit" >&2
16
- exit 0
32
+ cat >&2 <<'MSG'
33
+ ❌ Blocked: block-migration-edits cannot inspect this write.
34
+
35
+ `jq` is not installed, so the tool payload cannot be parsed — this hook cannot
36
+ tell whether the write targets a TypeORM migration. It refuses rather than
37
+ permitting an edit it was unable to check.
38
+
39
+ Install jq (`brew install jq`, `apt-get install jq`), or declare the
40
+ `migration-provenance` gate at `pre-tool` in .lisa.config.json to have your own
41
+ check decide instead.
42
+ MSG
43
+ exit 2
17
44
  fi
18
45
 
19
- FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
46
+ FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
20
47
 
21
48
  if [ -z "$FILE_PATH" ]; then
22
49
  exit 0
23
50
  fi
24
51
 
52
+ # Scope. Outside a migration path this hook does nothing at all, so there is
53
+ # nothing here for a declaration to take over — the same reason the sibling
54
+ # on-edit hook filters by file type and source directory BEFORE it consults.
25
55
  case "$FILE_PATH" in
26
- */migrations/*.ts|*/migrations/*.js)
27
- cat >&2 <<EOF
56
+ */migrations/*.ts | */migrations/*.js) ;;
57
+ *) exit 0 ;;
58
+ esac
59
+
60
+ # Where this script lives, resolved BEFORE any `cd`. `$0` is the path the
61
+ # harness invoked, and the façade helper ships beside it.
62
+ LISA_HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Gate façade. The project's declaration decides BEFORE the built-in refuses.
66
+ # Full contract, and why an undeclared project sees no change at all, in
67
+ # lisa-edit-gate.sh beside this file.
68
+ #
69
+ # THE WORKING DIRECTORY IS PART OF THE WIRING HERE, which is why this hook was
70
+ # left out of the change that wired the five on-edit ones. Those hooks already
71
+ # `cd "$CLAUDE_PROJECT_DIR"` to run a tool against the tree; this one acts on
72
+ # the payload and never establishes a directory at all. The helper resolves
73
+ # .lisa.config.json and the gate registry RELATIVE to the current directory, so
74
+ # without the `cd` it would read whatever directory the harness happened to
75
+ # launch in and report "nothing declared" for a project that had declared.
76
+ #
77
+ # The `cd` is safe for the built-in below because the built-in reads only the
78
+ # path out of the payload — nothing it does depends on the working directory.
79
+ # That is what keeps an undeclared project on exactly the command, and exactly
80
+ # the exit status, it had before.
81
+ #
82
+ # ONE PROPERTY, so there is no all-or-nothing question here: this hook proves
83
+ # `migration-provenance` and nothing else.
84
+ # ---------------------------------------------------------------------------
85
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ] &&
86
+ [ -f "$LISA_HOOK_DIR/lisa-edit-gate.sh" ] &&
87
+ cd "$CLAUDE_PROJECT_DIR" 2>/dev/null; then
88
+ # shellcheck source=/dev/null
89
+ . "$LISA_HOOK_DIR/lisa-edit-gate.sh"
90
+ if LISA_GATE_COMMANDS="$(lisa_edit_gate_tasks pre-tool migration-provenance)"; then
91
+ # The declared task's own exit status decides the write: non-zero refuses,
92
+ # exactly as the built-in's outright refusal does. A project that declares
93
+ # this gate is saying its own check knows which migrations came from the
94
+ # model, which is the judgement the built-in makes by refusing them all.
95
+ lisa_edit_gate_run "$FILE_PATH" "$LISA_GATE_COMMANDS"
96
+ exit $?
97
+ fi
98
+ fi
99
+
100
+ cat >&2 <<EOF
28
101
  ❌ Blocked: Direct edits to TypeORM migration files are not allowed.
29
102
 
30
103
  File: $FILE_PATH
@@ -53,8 +126,4 @@ If you believe this edit is an out-of-band migration:
53
126
  Do NOT silently hand-write a migration. See the nestjs-rules skill
54
127
  for the full rationale.
55
128
  EOF
56
- exit 2
57
- ;;
58
- esac
59
-
60
- exit 0
129
+ exit 2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -8,23 +8,96 @@
8
8
  # break the schema/migration contract.
9
9
  # Reference: https://docs.claude.com/en/docs/claude-code/hooks
10
10
  # Exit code 2 blocks the tool call and surfaces stderr to Claude.
11
+ #
12
+ # WHAT A REFUSAL HOOK OWES THAT AN ON-EDIT HOOK DOES NOT. The five PostToolUse
13
+ # scripts may bail out when they cannot run: the write has already happened and
14
+ # a skipped lint is a missed report. This one decides whether the write happens
15
+ # at all, so a bail-out is a PERMIT — it lets through exactly the edit it exists
16
+ # to stop, while reporting success. Every exit below is therefore one of three
17
+ # things and never a fourth: out of scope (nothing to judge), judged by the
18
+ # project's declared task, or judged by the built-in. "Could not judge" refuses.
11
19
 
12
20
  JSON_INPUT=$(cat)
13
21
 
22
+ # Project rule (host rules, .agents/rules/): never parse JSON in shell with
23
+ # grep/sed/cut/awk — always use jq.
24
+ #
25
+ # FAIL CLOSED, reversed from the `exit 0` this shipped with. Without jq the
26
+ # payload cannot be read at all, so the hook cannot tell a migration edit from
27
+ # any other write — and it said so on stderr while permitting the edit anyway,
28
+ # which is a guard announcing that it did not check and then waving the write
29
+ # through. The refusal is unscoped for the same reason: with no parse there is
30
+ # no path to scope it to.
14
31
  if ! command -v jq >/dev/null 2>&1; then
15
- echo "⚠ block-migration-edits: jq not available, allowing edit" >&2
16
- exit 0
32
+ cat >&2 <<'MSG'
33
+ ❌ Blocked: block-migration-edits cannot inspect this write.
34
+
35
+ `jq` is not installed, so the tool payload cannot be parsed — this hook cannot
36
+ tell whether the write targets a TypeORM migration. It refuses rather than
37
+ permitting an edit it was unable to check.
38
+
39
+ Install jq (`brew install jq`, `apt-get install jq`), or declare the
40
+ `migration-provenance` gate at `pre-tool` in .lisa.config.json to have your own
41
+ check decide instead.
42
+ MSG
43
+ exit 2
17
44
  fi
18
45
 
19
- FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
46
+ FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
20
47
 
21
48
  if [ -z "$FILE_PATH" ]; then
22
49
  exit 0
23
50
  fi
24
51
 
52
+ # Scope. Outside a migration path this hook does nothing at all, so there is
53
+ # nothing here for a declaration to take over — the same reason the sibling
54
+ # on-edit hook filters by file type and source directory BEFORE it consults.
25
55
  case "$FILE_PATH" in
26
- */migrations/*.ts|*/migrations/*.js)
27
- cat >&2 <<EOF
56
+ */migrations/*.ts | */migrations/*.js) ;;
57
+ *) exit 0 ;;
58
+ esac
59
+
60
+ # Where this script lives, resolved BEFORE any `cd`. `$0` is the path the
61
+ # harness invoked, and the façade helper ships beside it.
62
+ LISA_HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Gate façade. The project's declaration decides BEFORE the built-in refuses.
66
+ # Full contract, and why an undeclared project sees no change at all, in
67
+ # lisa-edit-gate.sh beside this file.
68
+ #
69
+ # THE WORKING DIRECTORY IS PART OF THE WIRING HERE, which is why this hook was
70
+ # left out of the change that wired the five on-edit ones. Those hooks already
71
+ # `cd "$CLAUDE_PROJECT_DIR"` to run a tool against the tree; this one acts on
72
+ # the payload and never establishes a directory at all. The helper resolves
73
+ # .lisa.config.json and the gate registry RELATIVE to the current directory, so
74
+ # without the `cd` it would read whatever directory the harness happened to
75
+ # launch in and report "nothing declared" for a project that had declared.
76
+ #
77
+ # The `cd` is safe for the built-in below because the built-in reads only the
78
+ # path out of the payload — nothing it does depends on the working directory.
79
+ # That is what keeps an undeclared project on exactly the command, and exactly
80
+ # the exit status, it had before.
81
+ #
82
+ # ONE PROPERTY, so there is no all-or-nothing question here: this hook proves
83
+ # `migration-provenance` and nothing else.
84
+ # ---------------------------------------------------------------------------
85
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ] &&
86
+ [ -f "$LISA_HOOK_DIR/lisa-edit-gate.sh" ] &&
87
+ cd "$CLAUDE_PROJECT_DIR" 2>/dev/null; then
88
+ # shellcheck source=/dev/null
89
+ . "$LISA_HOOK_DIR/lisa-edit-gate.sh"
90
+ if LISA_GATE_COMMANDS="$(lisa_edit_gate_tasks pre-tool migration-provenance)"; then
91
+ # The declared task's own exit status decides the write: non-zero refuses,
92
+ # exactly as the built-in's outright refusal does. A project that declares
93
+ # this gate is saying its own check knows which migrations came from the
94
+ # model, which is the judgement the built-in makes by refusing them all.
95
+ lisa_edit_gate_run "$FILE_PATH" "$LISA_GATE_COMMANDS"
96
+ exit $?
97
+ fi
98
+ fi
99
+
100
+ cat >&2 <<EOF
28
101
  ❌ Blocked: Direct edits to TypeORM migration files are not allowed.
29
102
 
30
103
  File: $FILE_PATH
@@ -53,8 +126,4 @@ If you believe this edit is an out-of-band migration:
53
126
  Do NOT silently hand-write a migration. See the nestjs-rules skill
54
127
  for the full rationale.
55
128
  EOF
56
- exit 2
57
- ;;
58
- esac
59
-
60
- exit 0
129
+ exit 2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -8,23 +8,96 @@
8
8
  # break the schema/migration contract.
9
9
  # Reference: https://docs.claude.com/en/docs/claude-code/hooks
10
10
  # Exit code 2 blocks the tool call and surfaces stderr to Claude.
11
+ #
12
+ # WHAT A REFUSAL HOOK OWES THAT AN ON-EDIT HOOK DOES NOT. The five PostToolUse
13
+ # scripts may bail out when they cannot run: the write has already happened and
14
+ # a skipped lint is a missed report. This one decides whether the write happens
15
+ # at all, so a bail-out is a PERMIT — it lets through exactly the edit it exists
16
+ # to stop, while reporting success. Every exit below is therefore one of three
17
+ # things and never a fourth: out of scope (nothing to judge), judged by the
18
+ # project's declared task, or judged by the built-in. "Could not judge" refuses.
11
19
 
12
20
  JSON_INPUT=$(cat)
13
21
 
22
+ # Project rule (host rules, .agents/rules/): never parse JSON in shell with
23
+ # grep/sed/cut/awk — always use jq.
24
+ #
25
+ # FAIL CLOSED, reversed from the `exit 0` this shipped with. Without jq the
26
+ # payload cannot be read at all, so the hook cannot tell a migration edit from
27
+ # any other write — and it said so on stderr while permitting the edit anyway,
28
+ # which is a guard announcing that it did not check and then waving the write
29
+ # through. The refusal is unscoped for the same reason: with no parse there is
30
+ # no path to scope it to.
14
31
  if ! command -v jq >/dev/null 2>&1; then
15
- echo "⚠ block-migration-edits: jq not available, allowing edit" >&2
16
- exit 0
32
+ cat >&2 <<'MSG'
33
+ ❌ Blocked: block-migration-edits cannot inspect this write.
34
+
35
+ `jq` is not installed, so the tool payload cannot be parsed — this hook cannot
36
+ tell whether the write targets a TypeORM migration. It refuses rather than
37
+ permitting an edit it was unable to check.
38
+
39
+ Install jq (`brew install jq`, `apt-get install jq`), or declare the
40
+ `migration-provenance` gate at `pre-tool` in .lisa.config.json to have your own
41
+ check decide instead.
42
+ MSG
43
+ exit 2
17
44
  fi
18
45
 
19
- FILE_PATH=$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
46
+ FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
20
47
 
21
48
  if [ -z "$FILE_PATH" ]; then
22
49
  exit 0
23
50
  fi
24
51
 
52
+ # Scope. Outside a migration path this hook does nothing at all, so there is
53
+ # nothing here for a declaration to take over — the same reason the sibling
54
+ # on-edit hook filters by file type and source directory BEFORE it consults.
25
55
  case "$FILE_PATH" in
26
- */migrations/*.ts|*/migrations/*.js)
27
- cat >&2 <<EOF
56
+ */migrations/*.ts | */migrations/*.js) ;;
57
+ *) exit 0 ;;
58
+ esac
59
+
60
+ # Where this script lives, resolved BEFORE any `cd`. `$0` is the path the
61
+ # harness invoked, and the façade helper ships beside it.
62
+ LISA_HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Gate façade. The project's declaration decides BEFORE the built-in refuses.
66
+ # Full contract, and why an undeclared project sees no change at all, in
67
+ # lisa-edit-gate.sh beside this file.
68
+ #
69
+ # THE WORKING DIRECTORY IS PART OF THE WIRING HERE, which is why this hook was
70
+ # left out of the change that wired the five on-edit ones. Those hooks already
71
+ # `cd "$CLAUDE_PROJECT_DIR"` to run a tool against the tree; this one acts on
72
+ # the payload and never establishes a directory at all. The helper resolves
73
+ # .lisa.config.json and the gate registry RELATIVE to the current directory, so
74
+ # without the `cd` it would read whatever directory the harness happened to
75
+ # launch in and report "nothing declared" for a project that had declared.
76
+ #
77
+ # The `cd` is safe for the built-in below because the built-in reads only the
78
+ # path out of the payload — nothing it does depends on the working directory.
79
+ # That is what keeps an undeclared project on exactly the command, and exactly
80
+ # the exit status, it had before.
81
+ #
82
+ # ONE PROPERTY, so there is no all-or-nothing question here: this hook proves
83
+ # `migration-provenance` and nothing else.
84
+ # ---------------------------------------------------------------------------
85
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ] &&
86
+ [ -f "$LISA_HOOK_DIR/lisa-edit-gate.sh" ] &&
87
+ cd "$CLAUDE_PROJECT_DIR" 2>/dev/null; then
88
+ # shellcheck source=/dev/null
89
+ . "$LISA_HOOK_DIR/lisa-edit-gate.sh"
90
+ if LISA_GATE_COMMANDS="$(lisa_edit_gate_tasks pre-tool migration-provenance)"; then
91
+ # The declared task's own exit status decides the write: non-zero refuses,
92
+ # exactly as the built-in's outright refusal does. A project that declares
93
+ # this gate is saying its own check knows which migrations came from the
94
+ # model, which is the judgement the built-in makes by refusing them all.
95
+ lisa_edit_gate_run "$FILE_PATH" "$LISA_GATE_COMMANDS"
96
+ exit $?
97
+ fi
98
+ fi
99
+
100
+ cat >&2 <<EOF
28
101
  ❌ Blocked: Direct edits to TypeORM migration files are not allowed.
29
102
 
30
103
  File: $FILE_PATH
@@ -53,8 +126,4 @@ If you believe this edit is an out-of-band migration:
53
126
  Do NOT silently hand-write a migration. See the nestjs-rules skill
54
127
  for the full rationale.
55
128
  EOF
56
- exit 2
57
- ;;
58
- esac
59
-
60
- exit 0
129
+ exit 2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -13,13 +13,42 @@
13
13
  # and identifiers that merely mention these tokens are not flagged.
14
14
  # Exit code 2 blocks the tool call and surfaces stderr to Claude.
15
15
  # Reference: https://docs.claude.com/en/docs/claude-code/hooks
16
+ #
17
+ # WHAT A REFUSAL HOOK OWES THAT AN ON-EDIT HOOK DOES NOT. The five PostToolUse
18
+ # scripts may bail out when they cannot run: the write has already happened and
19
+ # a skipped lint is a missed report. This one decides whether the write happens
20
+ # at all, so a bail-out is a PERMIT — it lets through exactly the text it exists
21
+ # to stop, while reporting success. Every exit below is therefore one of three
22
+ # things and never a fourth: out of scope (nothing to judge), judged by the
23
+ # project's declared task, or judged by the built-in. "Could not judge" refuses.
16
24
 
17
25
  JSON_INPUT=$(cat)
18
26
 
19
27
  # Project rule (host rules, .agents/rules/): never parse JSON in shell with
20
- # grep/sed/cut/awk — always use jq. Fail open without jq so we never hard-block
21
- # the agent on missing tooling.
22
- command -v jq >/dev/null 2>&1 || exit 0
28
+ # grep/sed/cut/awk — always use jq.
29
+ #
30
+ # FAIL CLOSED, reversed from the `exit 0` this shipped with. Without jq the
31
+ # payload cannot be read at all: not the path, not the file type, not the new
32
+ # text. Permitting there is the one branch that let a suppression through while
33
+ # the hook reported success, and it could not even say which write it had waved
34
+ # past. The refusal is deliberately unscoped for the same reason — with no
35
+ # parse there is no file type to scope it to. This is the same shape the
36
+ # sibling on-edit hook already ships for a missing `oxlint`: fail loudly rather
37
+ # than silently skip.
38
+ if ! command -v jq >/dev/null 2>&1; then
39
+ cat >&2 <<'MSG'
40
+ ❌ Blocked: block-suppress-directives cannot inspect this write.
41
+
42
+ `jq` is not installed, so the tool payload cannot be parsed — this hook cannot
43
+ tell whether the write adds an error-suppression directive. It refuses rather
44
+ than permitting an edit it was unable to check.
45
+
46
+ Install jq (`brew install jq`, `apt-get install jq`), or declare the
47
+ `suppression-residue` gate at `pre-tool` in .lisa.config.json to have your own
48
+ check decide instead.
49
+ MSG
50
+ exit 2
51
+ fi
23
52
 
24
53
  FILE_PATH=$(printf '%s' "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')
25
54
  [ -n "$FILE_PATH" ] || exit 0
@@ -31,6 +60,45 @@ case "${FILE_PATH##*.}" in
31
60
  *) exit 0 ;;
32
61
  esac
33
62
 
63
+ # Where this script lives, resolved BEFORE any `cd`. `$0` is the path the
64
+ # harness invoked, and the façade helper ships beside it.
65
+ LISA_HOOK_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
66
+
67
+ # ---------------------------------------------------------------------------
68
+ # Gate façade. The project's declaration decides BEFORE the built-in judges
69
+ # anything. Full contract, and why an undeclared project sees no change at all,
70
+ # in lisa-edit-gate.sh beside this file.
71
+ #
72
+ # THE WORKING DIRECTORY IS PART OF THE WIRING HERE, which is why this hook was
73
+ # left out of the change that wired the five on-edit ones. Those hooks already
74
+ # `cd "$CLAUDE_PROJECT_DIR"` to run a tool against the tree; this one acts on
75
+ # the payload and never establishes a directory at all. The helper resolves
76
+ # .lisa.config.json and the gate registry RELATIVE to the current directory, so
77
+ # without the `cd` it would read whatever directory the harness happened to
78
+ # launch in and report "nothing declared" for a project that had declared.
79
+ #
80
+ # The `cd` is safe for the built-in below because the built-in reads only the
81
+ # payload and the absolute path inside it — nothing it does depends on the
82
+ # working directory. That is what keeps an undeclared project on exactly the
83
+ # command, and exactly the exit status, it had before.
84
+ #
85
+ # ONE PROPERTY, so there is no all-or-nothing question here: this hook proves
86
+ # `suppression-residue` and nothing else.
87
+ # ---------------------------------------------------------------------------
88
+ if [ -n "${CLAUDE_PROJECT_DIR:-}" ] &&
89
+ [ -f "$LISA_HOOK_DIR/lisa-edit-gate.sh" ] &&
90
+ cd "$CLAUDE_PROJECT_DIR" 2>/dev/null; then
91
+ # shellcheck source=/dev/null
92
+ . "$LISA_HOOK_DIR/lisa-edit-gate.sh"
93
+ if LISA_GATE_COMMANDS="$(lisa_edit_gate_tasks pre-tool suppression-residue)"; then
94
+ # The declared task's own exit status decides the write: non-zero refuses,
95
+ # exactly as the built-in's match does. `lisa_edit_gate_run` exits 2 on the
96
+ # first failure, which is this surface's refusal status.
97
+ lisa_edit_gate_run "$FILE_PATH" "$LISA_GATE_COMMANDS"
98
+ exit $?
99
+ fi
100
+ fi
101
+
34
102
  # Resolve the new text per tool shape:
35
103
  # Write -> tool_input.content
36
104
  # MultiEdit -> tool_input.edits[].new_string
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
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.8.0",
3
+ "version": "4.8.2",
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"