@codyswann/lisa 4.9.0 → 4.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/all/copy-overwrite/scripts/lisa-gates.mjs +29 -3
  2. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  3. package/dist/core/lisa-owned-hash-ledger.js +3 -0
  4. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  5. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  6. package/dist/core/upstream-evidence-manifest.js +7 -3
  7. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  8. package/dist/opencode/hooks-installer.d.ts.map +1 -1
  9. package/dist/opencode/hooks-installer.js +39 -17
  10. package/dist/opencode/hooks-installer.js.map +1 -1
  11. package/dist/opencode/plugin-catalog.d.ts.map +1 -1
  12. package/dist/opencode/plugin-catalog.js +5 -0
  13. package/dist/opencode/plugin-catalog.js.map +1 -1
  14. package/dist/opencode/plugin-templates/block-no-verify.sh +464 -0
  15. package/dist/opencode/plugin-templates/lisa-block-no-verify.ts +81 -0
  16. package/package.json +1 -1
  17. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  19. package/plugins/lisa-agy/plugin.json +1 -1
  20. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  22. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  23. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-expo-agy/plugin.json +1 -1
  30. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  35. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  39. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  40. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  45. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  49. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  50. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  54. package/plugins/lisa-rails-agy/plugin.json +1 -1
  55. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  59. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  60. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  65. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  67. package/scripts/copy-opencode-plugin-templates.mjs +4 -0
  68. package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +20 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOrD,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAE/C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAKxD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,oCAAoE,CAAC;AAmClH,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,WAAW,EAAE,EACrC,oBAAoB,EAAE,SAAS,MAAM,EAAE,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAyDrC;AAgGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAyCpE;AAiGD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAa5B"}
1
+ {"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOrD,gFAAgF;AAChF,eAAO,MAAM,sBAAsB,WAAW,CAAC;AAE/C,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AAKxD,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,2EAA2E;AAC3E,eAAO,MAAM,gCAAgC,oCAAoE,CAAC;AAqDlH,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IACzC,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,mEAAmE;IACnE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,WAAW,EAAE,EACrC,oBAAoB,EAAE,SAAS,MAAM,EAAE,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAyDrC;AAgGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAyCpE;AAiGD;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAa5B"}
@@ -5,10 +5,14 @@
5
5
  * `.codex/hooks.json`), OpenCode is mapped to its NATIVE surfaces first, then
6
6
  * falls back to runtime plugins only where genuine behavior is required:
7
7
  *
8
- * - block-no-verify → `permission.bash` deny rules in `opencode.json`. Cheaper
9
- * and more robust than a hook: OpenCode evaluates the parsed command against
10
- * glob patterns and rejects matches before they run (verified-by-run on
11
- * opencode 1.16.2: `git commit … --no-verify` and `HUSKY=0 …` are denied).
8
+ * - block-no-verify → a `tool.execute.before` plugin running the canonical
9
+ * `block-no-verify.sh`, with the `permission.bash` deny globs kept beneath
10
+ * it as a floor. The globs alone were a SPELLING filter: they matched
11
+ * `--no-verify` and two `git commit -n` prefixes and missed `--no-veri`,
12
+ * which git resolves to the same option. Whether a token is an option or
13
+ * merely contains one's text is a question only a tokenizer can answer, so
14
+ * the semantic guard is the plugin and the globs are what survives if the
15
+ * plugin fails to load.
12
16
  * - format-on-edit → OpenCode's BUILT-IN prettier formatter already formats on
13
17
  * edit, so Lisa emits no formatter config (overriding it would be worse than
14
18
  * the default). This is why there is no format plugin below.
@@ -48,22 +52,32 @@ export const OPENCODE_LISA_RULES_SUBDIR = "lisa-rules";
48
52
  /** OpenCode instruction glob that loads Lisa eager rules every session. */
49
53
  export const OPENCODE_EAGER_RULES_INSTRUCTION = `${OPENCODE_CONFIG_DIR}/${OPENCODE_LISA_RULES_SUBDIR}/eager/*.md`;
50
54
  /**
51
- * `permission.bash` deny patterns that replace Lisa's `block-no-verify` hook.
55
+ * `permission.bash` deny patterns that back up Lisa's `block-no-verify` guard.
52
56
  * Each glob is matched against the parsed shell command; `*` matches any run of
53
57
  * characters. Deny-only (no catch-all allow) so non-matching commands fall
54
58
  * through to the host's / OpenCode's default posture.
55
59
  *
56
- * Documented parity gap. OpenCode gives Lisa a glob list, not a hook it can
57
- * tokenize, so the short `-n` bypass the shell variants close by parsing a
58
- * `git commit` argv is only PARTLY reachable here. The two globs below cover
59
- * the spellings that put `-n` directly after the subcommand — `git commit -n`,
60
- * and by prefix the bundled `-nm "msg"` / `-nam "msg"` — which is every form
61
- * observed in practice. A cluster that reaches `-n` later on the line
62
- * (`git commit -am wip -n`) is NOT matched, and cannot be without a glob broad
63
- * enough to refuse `grep -n`. That is the same precision the `*--no-verify*`
64
- * glob above already ships with: it likewise fires on the flag quoted inside a
65
- * commit message. The tokenizing variants (Claude, agy, Codex) have no such
66
- * gap; this list is a floor, not parity.
60
+ * A FLOOR, and no longer the guard. The premise this list was written under —
61
+ * "OpenCode gives Lisa a glob list, not a hook it can tokenize" — is false:
62
+ * `tool.execute.before` fires for the `bash` tool under `opencode run` and a
63
+ * throw cancels the call, which is what `lisa-block-no-verify.ts` now uses to
64
+ * run the same tokenizing script Claude, Codex, Cursor and Copilot run.
65
+ *
66
+ * What a glob list cannot do is separate a token that IS an option from a token
67
+ * that merely contains one's text, and that limit runs in both directions.
68
+ * `git commit --no-veri` is an abbreviation git accepts and no glob here
69
+ * matches, and no addition fixes the class, because which abbreviations git
70
+ * accepts is a property of git's parser; `git commit -am wip -n` reaches `-n`
71
+ * late on the line and cannot be matched without a glob broad enough to refuse
72
+ * `grep -n`. In the other direction these globs are too eager: `*--no-verify*`
73
+ * fires on the flag quoted inside a commit message, which the tokenizing guard
74
+ * correctly allows.
75
+ *
76
+ * Both costs are accepted rather than removed. A guard that is silently absent
77
+ * reads exactly like a guard that is passing, so if the plugin fails to load —
78
+ * a Bun transpile error, a run that skips project plugins — the literal
79
+ * spellings are still refused here. A reworded commit message is a cheaper
80
+ * failure than an unguarded `--no-verify`.
67
81
  */
68
82
  const NO_VERIFY_DENY_PATTERNS = {
69
83
  "*--no-verify*": "deny",
@@ -73,8 +87,16 @@ const NO_VERIFY_DENY_PATTERNS = {
73
87
  "*HUSKY_SKIP_HOOKS=*": "deny",
74
88
  "*core.hooksPath*/dev/null*": "deny",
75
89
  };
76
- /** Canonical policy files used by universal OpenCode adapters. */
90
+ /**
91
+ * Canonical policy files used by universal OpenCode adapters.
92
+ *
93
+ * Kept in step with `canonicalSupportFiles` in
94
+ * `scripts/copy-opencode-plugin-templates.mjs`, which stages the same list into
95
+ * `dist/` for the packaged installer. A file listed here and not there resolves
96
+ * in a source checkout and is missing from every installed copy.
97
+ */
77
98
  const PLUGIN_SUPPORT_FILES = [
99
+ "block-no-verify.sh",
78
100
  "parity-safety-net.sh",
79
101
  "parity-safety-net-heredoc.py",
80
102
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/opencode/hooks-installer.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-catalog.d.ts","sourceRoot":"","sources":["../../src/opencode/plugin-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC,WAAW,GAAG,GAAG,CAAC,EAAE,CAAC;CAC1D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,kBAAkB,EA8CvD,CAAC"}
1
+ {"version":3,"file":"plugin-catalog.d.ts","sourceRoot":"","sources":["../../src/opencode/plugin-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,eAAe,EAAE,SAAS,CAAC,WAAW,GAAG,GAAG,CAAC,EAAE,CAAC;CAC1D;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,kBAAkB,EAmDvD,CAAC"}
@@ -8,6 +8,11 @@ export const PLUGIN_CATALOG = [
8
8
  templateFilename: "lisa-parity-safety-net.ts",
9
9
  forProjectTypes: ["*"],
10
10
  },
11
+ {
12
+ id: "block-no-verify",
13
+ templateFilename: "lisa-block-no-verify.ts",
14
+ forProjectTypes: ["*"],
15
+ },
11
16
  {
12
17
  id: "session-bootstrap",
13
18
  templateFilename: "lisa-session-bootstrap.ts",
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-catalog.js","sourceRoot":"","sources":["../../src/opencode/plugin-catalog.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkC;IAC3D;QACE,EAAE,EAAE,mBAAmB;QACvB,gBAAgB,EAAE,2BAA2B;QAC7C,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,gBAAgB,EAAE,2BAA2B;QAC7C,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,8BAA8B;QAClC,gBAAgB,EAAE,sCAAsC;QACxD,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,2BAA2B;QAC/B,gBAAgB,EAAE,mCAAmC;QACrD,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,2BAA2B;QAC/B,gBAAgB,EAAE,mCAAmC;QACrD,eAAe,EAAE,CAAC,YAAY,CAAC;KAChC;IACD;QACE,EAAE,EAAE,cAAc;QAClB,gBAAgB,EAAE,sBAAsB;QACxC,eAAe,EAAE,CAAC,YAAY,CAAC;KAChC;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,gBAAgB,EAAE,yBAAyB;QAC3C,eAAe,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC;KACzC;IACD;QACE,EAAE,EAAE,uBAAuB;QAC3B,gBAAgB,EAAE,+BAA+B;QACjD,eAAe,EAAE,CAAC,QAAQ,CAAC;KAC5B;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,gBAAgB,EAAE,yBAAyB;QAC3C,eAAe,EAAE,CAAC,OAAO,CAAC;KAC3B;CACF,CAAC"}
1
+ {"version":3,"file":"plugin-catalog.js","sourceRoot":"","sources":["../../src/opencode/plugin-catalog.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAkC;IAC3D;QACE,EAAE,EAAE,mBAAmB;QACvB,gBAAgB,EAAE,2BAA2B;QAC7C,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,gBAAgB,EAAE,yBAAyB;QAC3C,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,gBAAgB,EAAE,2BAA2B;QAC7C,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,8BAA8B;QAClC,gBAAgB,EAAE,sCAAsC;QACxD,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,2BAA2B;QAC/B,gBAAgB,EAAE,mCAAmC;QACrD,eAAe,EAAE,CAAC,GAAG,CAAC;KACvB;IACD;QACE,EAAE,EAAE,2BAA2B;QAC/B,gBAAgB,EAAE,mCAAmC;QACrD,eAAe,EAAE,CAAC,YAAY,CAAC;KAChC;IACD;QACE,EAAE,EAAE,cAAc;QAClB,gBAAgB,EAAE,sBAAsB;QACxC,eAAe,EAAE,CAAC,YAAY,CAAC;KAChC;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,gBAAgB,EAAE,yBAAyB;QAC3C,eAAe,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC;KACzC;IACD;QACE,EAAE,EAAE,uBAAuB;QAC3B,gBAAgB,EAAE,+BAA+B;QACjD,eAAe,EAAE,CAAC,QAAQ,CAAC;KAC5B;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,gBAAgB,EAAE,yBAAyB;QAC3C,eAAe,EAAE,CAAC,OAAO,CAAC;KAC3B;CACF,CAAC"}
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse hook for Bash: blocks commands that bypass git's verification hooks.
3
+ # Bypassing pre-commit/pre-push hooks (which exist for a reason) is blocked in
4
+ # all of its forms; the fix is to address the underlying issue, not silence the
5
+ # check. See feedback_never_no_verify in user memory.
6
+ #
7
+ # Blocked bypass vectors:
8
+ # 1. --no-verify, and any prefix abbreviation git would accept for it;
9
+ # 2. HUSKY=0 / HUSKY_SKIP_HOOKS=... — disables husky-managed git hooks;
10
+ # 3. core.hooksPath pointed anywhere but an allowlisted in-repo hooks dir;
11
+ # 4. --config-env=core.hooksPath=VAR, in both the one- and two-token spellings;
12
+ # 5. GIT_CONFIG_KEY_<n>=core.hooksPath env-var-style command-scope config;
13
+ # 6. the short `-n`, as a real argv token of a `git commit` — bare, or bundled
14
+ # into a short-option cluster such as `-nm "msg"`.
15
+ #
16
+ # The line below is what lets `lisa apply` tell a downstream copy of this guard
17
+ # that is BEHIND from one that is AHEAD. Byte comparison cannot: both look like
18
+ # "differs from mine", and guessing "behind" is how a fork's stronger guard gets
19
+ # silently replaced by a weaker upstream one. Refresh compares the two declared
20
+ # sets, and refuses to overwrite a copy declaring anything this one does not.
21
+ #
22
+ # Add a name here in the same commit that closes a vector. A hardening that
23
+ # forgets to is invisible to refresh, and shows up as an unexplained diff at
24
+ # review time instead of a named capability.
25
+ # lisa-guard-capabilities: no-verify-abbrev, husky-env, hookspath-allowlist, config-env, git-config-key, no-verify-short
26
+ #
27
+ # Shell-token matching avoids false positives from issue bodies, heredocs, and
28
+ # commit-message prose while still catching quoted real argv values such as
29
+ # `git -c "core.hooksPath=/dev/null"`.
30
+ #
31
+ # The short `-n` form used to be excluded here, and the stated reason was that
32
+ # "grep cannot distinguish a real -n option from -n in commit-message prose or
33
+ # an unrelated piped command". That was true of the matcher it was written for.
34
+ # It is not true of this one: the command is TOKENIZED, not grepped, so `-m "fix
35
+ # the -n flag"` is one token holding the message and `grep -n` is a separate
36
+ # command — the two cases the comment called inseparable are separated by the
37
+ # same machinery `--config-env` and `GIT_CONFIG_KEY_<n>` already depend on.
38
+ #
39
+ # A rationale that outlives the implementation it describes is worse than none:
40
+ # it reads as a considered decision and stops the next reader from re-examining
41
+ # it. Measured against real git, `git commit -n`, `-nm msg`, and `-anm msg` all
42
+ # skip pre-commit exactly as completely as `--no-verify`, and `-nm` is the more
43
+ # likely spelling in practice because it reads as an ordinary message flag.
44
+ #
45
+ # What the old comment got right is the part about `-n` being common, and that
46
+ # concern is answered by SCOPE rather than by giving up: `-n` is `--dry-run` for
47
+ # `push`, `--no-stat` for `merge`, and a line count for head/tail/sort/grep, so
48
+ # the match is confined to the argv of a `git commit` invocation and nothing
49
+ # else. See `git_commit_skips_verification` below.
50
+ set -euo pipefail
51
+
52
+ input="$(cat)"
53
+
54
+ # Both interpreters must be probed BEFORE they are used, and a missing one must
55
+ # be announced rather than swallowed.
56
+ #
57
+ # `jq` used to be called unguarded here while `python3` two lines below was
58
+ # guarded. Under `set -e` an absent jq aborted the script with 127, and Claude
59
+ # Code treats any non-2 exit as a NON-BLOCKING hook error — so the hook that
60
+ # enforces "never --no-verify" silently permitted the very thing it exists to
61
+ # stop. It was not hypothetical: agent containers routinely ship no jq (that is
62
+ # why jq is now a pinned toolchain entry). The Codex variant of this script has
63
+ # always had the guard, so this was a parity gap rather than a design choice.
64
+ #
65
+ # Degrading to "allow" is still the right behaviour — a hook that cannot parse
66
+ # its input cannot tell a bypass from an ordinary command, and failing closed
67
+ # would block every Bash call on a machine missing an interpreter. What is NOT
68
+ # right is doing it quietly, so the operator gets one line on stderr saying the
69
+ # protection is off. A guard that is silently absent reads exactly like a guard
70
+ # that is passing.
71
+ for required in jq python3; do
72
+ if ! command -v "$required" >/dev/null 2>&1; then
73
+ printf 'block-no-verify: %s not found; --no-verify protection is NOT active\n' \
74
+ "$required" >&2
75
+ exit 0
76
+ fi
77
+ done
78
+
79
+ tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null || true)"
80
+ if [ "$tool_name" != "Bash" ]; then
81
+ exit 0
82
+ fi
83
+
84
+ command_str="$(printf '%s' "$input" | jq -r '.tool_input.command // empty' 2>/dev/null || true)"
85
+ if [ -z "$command_str" ]; then
86
+ exit 0
87
+ fi
88
+
89
+ if ! BLOCK_NO_VERIFY_COMMAND="$command_str" python3 - <<'PY'
90
+ import os
91
+ import re
92
+ import shlex
93
+ import sys
94
+
95
+ command = os.environ.get("BLOCK_NO_VERIFY_COMMAND", "")
96
+
97
+
98
+ def strip_heredocs(text: str) -> str:
99
+ lines = text.splitlines()
100
+ output = []
101
+ pending = []
102
+ marker_pattern = re.compile(
103
+ r"<<-?\s*(?:'([^']+)'|\"([^\"]+)\"|([A-Za-z_][A-Za-z0-9_]*))"
104
+ )
105
+ index = 0
106
+ while index < len(lines):
107
+ line = lines[index]
108
+ output.append(line)
109
+ pending.extend(
110
+ next(group for group in match.groups() if group)
111
+ for match in marker_pattern.finditer(line)
112
+ )
113
+ index += 1
114
+ while pending and index < len(lines):
115
+ if lines[index].strip() == pending[0]:
116
+ output.append(lines[index])
117
+ pending.pop(0)
118
+ index += 1
119
+ break
120
+ index += 1
121
+ return "\n".join(output)
122
+
123
+
124
+ try:
125
+ tokens = shlex.split(strip_heredocs(command), posix=True)
126
+ except ValueError:
127
+ sys.exit(0)
128
+
129
+ normalized_tokens = [token.strip("();|&") for token in tokens]
130
+
131
+ # The only relocations that keep a repo's own hooks in play. `.husky` is the
132
+ # husky convention this fleet runs; `.githooks` is the common hand-rolled one.
133
+ # Anything else — including "" and /dev/null, which are simply the two most
134
+ # obvious members of the blocked set rather than special cases — is refused.
135
+ PERMITTED_HOOKS_PATHS = {".husky", ".githooks"}
136
+
137
+
138
+ def is_permitted_hooks_path(value):
139
+ """Whether a core.hooksPath value relocates hooks rather than disabling them.
140
+
141
+ Args:
142
+ value: The raw core.hooksPath value as it appeared on the command line.
143
+
144
+ Returns:
145
+ True if the path is an established in-repo hooks directory.
146
+ """
147
+ cleaned = value.strip().strip("'\"")
148
+ if cleaned.startswith("./"):
149
+ cleaned = cleaned[2:]
150
+ return cleaned.rstrip("/") in PERMITTED_HOOKS_PATHS
151
+
152
+
153
+ # Git resolves any UNAMBIGUOUS abbreviation of a long option, so `--no-verify`
154
+ # is only the longest of the spellings that disable verification: `git commit
155
+ # --no-veri` skips hooks exactly as completely. An equality check therefore
156
+ # enforced the guard against the one spelling nobody in a hurry types.
157
+ #
158
+ # Matched as "a prefix of --no-verify" rather than by listing abbreviations,
159
+ # because the set of accepted abbreviations is a property of git's parser and
160
+ # changes with the surrounding options. `--no-verbose` is NOT caught, and must
161
+ # not be: it diverges from `--no-verify` at the character after `--no-ver`, so
162
+ # it fails the prefix test.
163
+ #
164
+ # The floor is `--no-v`. Shorter is refused anyway — bare `--no-` is not a flag
165
+ # — and blocking an abbreviation git would reject as ambiguous costs nothing,
166
+ # while missing one git accepts costs the whole guard.
167
+ NO_VERIFY = "--no-verify"
168
+ NO_VERIFY_MIN_PREFIX = len("--no-v")
169
+
170
+
171
+ def disables_verification(token):
172
+ """Whether a token is `--no-verify` or an abbreviation git would accept.
173
+
174
+ Args:
175
+ token: A single shell token from the command line.
176
+
177
+ Returns:
178
+ True if git would read this token as --no-verify.
179
+ """
180
+ return (
181
+ len(token) >= NO_VERIFY_MIN_PREFIX
182
+ and len(token) <= len(NO_VERIFY)
183
+ and NO_VERIFY.startswith(token)
184
+ )
185
+
186
+
187
+ # `git commit -n` is the short spelling of --no-verify and skips pre-commit and
188
+ # commit-msg identically. Matching it needs one thing the `--no-verify` scan
189
+ # above does not: SCOPE. `-n` means --dry-run to `git push`, --no-stat to `git
190
+ # merge`, and a line count to head/tail/sort/grep, so a token-anywhere match
191
+ # would refuse ordinary commands all day. Only `git commit` reads it as a hook
192
+ # bypass, so only that invocation's argv is scanned.
193
+ #
194
+ # The tokenizer below is a SECOND pass, deliberately not shared with the one the
195
+ # checks above use. `shlex.split` leaves `|` and `;` glued to their neighbours,
196
+ # which those checks paper over by stripping the punctuation off each token —
197
+ # fine when the question is "does this token look like a bypass anywhere in the
198
+ # line", fatal when the question is "where does this command end", because
199
+ # `git commit -m x && grep -n foo` would then read as one long git invocation
200
+ # and refuse the grep. `punctuation_chars=True` emits the operators as their own
201
+ # tokens, so the invocation's boundary is a token the scan can stop at.
202
+ COMMAND_SEPARATORS = {
203
+ ";", "|", "||", "&", "&&", "(", ")", "<", ">", ">>", "<<", "&|",
204
+ }
205
+
206
+ # git's own options that take a SEPARATE value token, which therefore must be
207
+ # skipped when looking for the subcommand: `git -c core.hooksPath=x commit` and
208
+ # `git -C /repo commit` both reach `commit`.
209
+ GIT_GLOBAL_SEPARATE_VALUE = {
210
+ "-c", "-C", "--config-env", "--git-dir", "--work-tree",
211
+ "--namespace", "--exec-path", "--super-prefix",
212
+ }
213
+
214
+ # `git commit` long options whose value can be a separate token. Listed so the
215
+ # VALUE is never mistaken for a flag — `git commit --author "A -n B" -m x`
216
+ # must stay allowed. Only genuinely value-taking options belong here: adding a
217
+ # boolean one by mistake would swallow the token after it, and `git commit
218
+ # --amend -n` would go unnoticed.
219
+ COMMIT_LONG_SEPARATE_VALUE = {
220
+ "--message", "--file", "--author", "--date", "--template", "--cleanup",
221
+ "--reuse-message", "--reedit-message", "--fixup", "--squash",
222
+ "--pathspec-from-file", "--trailer",
223
+ }
224
+
225
+ # Short `git commit` options that REQUIRE a value: `-m`/`-F`/`-c`/`-C`/`-t`.
226
+ # In a cluster the value is whatever follows them in the same token, or the next
227
+ # token when nothing does — which is why `-mn` is the message "n" and not a
228
+ # bypass, and `-nm msg` IS one.
229
+ COMMIT_SHORT_REQUIRED_VALUE = set("mFcCt")
230
+
231
+ # Short options taking an OPTIONAL value, which git only ever reads attached:
232
+ # `-uno` is --untracked-files=no, `-Skeyid` is --gpg-sign=keyid. The cluster
233
+ # ends at them either way, so `-un` is untracked-files "n" rather than a bypass.
234
+ COMMIT_SHORT_OPTIONAL_VALUE = set("uS")
235
+
236
+
237
+ def line_boundaries_as_separators(text):
238
+ """Turn newlines into command separators, keeping continuations intact.
239
+
240
+ A newline ends a command exactly as `;` does, but shlex treats it as plain
241
+ whitespace — so `git commit -m x` on one line and `grep -n foo` on the next
242
+ read as ONE invocation and the grep gets refused. That is the same
243
+ false-positive class the short-form match exists to avoid, arriving through
244
+ the boundary rather than through the token.
245
+
246
+ Backslash-newline is joined first, because there the newline is NOT a
247
+ boundary: `git commit \\` + newline + `-nm x` is one command, and turning
248
+ that newline into a separator would hide a real bypass.
249
+
250
+ Args:
251
+ text: The command line, heredoc payloads already stripped.
252
+
253
+ Returns:
254
+ The same command with line breaks spelled as separators.
255
+ """
256
+ joined = text.replace("\r\n", "\n").replace("\\\n", " ")
257
+ return joined.replace("\n", " ; ")
258
+
259
+
260
+ def shell_tokens(text):
261
+ """Tokenize a command with shell operators kept as their own tokens.
262
+
263
+ Args:
264
+ text: The command line, heredoc payloads already stripped.
265
+
266
+ Returns:
267
+ The token list, with `;`, `|`, `&&`, `(` and friends standing alone.
268
+ """
269
+ lexer = shlex.shlex(
270
+ line_boundaries_as_separators(text), posix=True, punctuation_chars=True
271
+ )
272
+ lexer.whitespace_split = True
273
+ # shlex treats `#` as a comment introducer by default and would silently
274
+ # truncate the rest of the line; `shlex.split` disables it, and so must this.
275
+ lexer.commenters = ""
276
+ return list(lexer)
277
+
278
+
279
+ def cluster_skips_verification(cluster):
280
+ """Read a short-option cluster the way git's own parser reads it.
281
+
282
+ Args:
283
+ cluster: A single token beginning with one `-`, e.g. `-nm` or `-mn`.
284
+
285
+ Returns:
286
+ A pair (bypasses, consumes_next_token). `bypasses` is True when a real
287
+ `-n` option is present; `consumes_next_token` is True when the cluster
288
+ ends in a value-taking option whose value is the following token.
289
+ """
290
+ body = cluster[1:]
291
+ for offset, letter in enumerate(body):
292
+ if letter == "n":
293
+ return (True, False)
294
+ if letter in COMMIT_SHORT_REQUIRED_VALUE:
295
+ # Everything after this letter is the value. It is a separate token
296
+ # only when nothing is attached.
297
+ return (False, offset == len(body) - 1)
298
+ if letter in COMMIT_SHORT_OPTIONAL_VALUE:
299
+ return (False, False)
300
+ return (False, False)
301
+
302
+
303
+ def commit_bypasses_verification(argv):
304
+ """Whether a `git commit` invocation's argv carries a real short `-n`.
305
+
306
+ Args:
307
+ argv: Tokens following the `commit` subcommand, to the end of the line.
308
+
309
+ Returns:
310
+ True if git would read one of them as --no-verify.
311
+ """
312
+ index = 0
313
+ while index < len(argv):
314
+ token = argv[index]
315
+ index += 1
316
+ # `--` ends the options; everything after it is a pathspec, and a file
317
+ # legitimately named `-n` is not a bypass.
318
+ if token in COMMAND_SEPARATORS or token == "--":
319
+ return False
320
+ if token.startswith("--"):
321
+ if token in COMMIT_LONG_SEPARATE_VALUE:
322
+ index += 1
323
+ continue
324
+ # A bare `-` is git's stdin placeholder, not an option cluster.
325
+ if not token.startswith("-") or token == "-":
326
+ continue
327
+ bypasses, consumes_next = cluster_skips_verification(token)
328
+ if bypasses:
329
+ return True
330
+ if consumes_next:
331
+ index += 1
332
+ return False
333
+
334
+
335
+ def subcommand_after_git(tokens, start):
336
+ """Find the subcommand a `git` token introduces, and its argv.
337
+
338
+ Args:
339
+ tokens: The full operator-aware token list.
340
+ start: Index just past the `git` token.
341
+
342
+ Returns:
343
+ A pair (subcommand, argv), or None when the invocation names none.
344
+ """
345
+ index = start
346
+ while index < len(tokens):
347
+ token = tokens[index]
348
+ if token in COMMAND_SEPARATORS:
349
+ return None
350
+ if not token.startswith("-"):
351
+ return (token, tokens[index + 1:])
352
+ index += 2 if token in GIT_GLOBAL_SEPARATE_VALUE else 1
353
+ return None
354
+
355
+
356
+ def git_commit_skips_verification(text):
357
+ """Whether the command runs `git commit` with the short `-n` bypass.
358
+
359
+ Args:
360
+ text: The command line, heredoc payloads already stripped.
361
+
362
+ Returns:
363
+ True if any `git commit` invocation on the line skips verification.
364
+ """
365
+ try:
366
+ scoped_tokens = shell_tokens(text)
367
+ except ValueError:
368
+ return False
369
+ for index, token in enumerate(scoped_tokens):
370
+ # `/usr/bin/git` and a bare `git` are the same program; an env prefix
371
+ # (`HUSKY=1 git commit -n`) simply sits in an earlier token.
372
+ if token != "git" and not token.endswith("/git"):
373
+ continue
374
+ found = subcommand_after_git(scoped_tokens, index + 1)
375
+ if found and found[0] == "commit" and commit_bypasses_verification(found[1]):
376
+ return True
377
+ return False
378
+
379
+
380
+ if git_commit_skips_verification(strip_heredocs(command)):
381
+ sys.exit(1)
382
+
383
+ for i, token in enumerate(normalized_tokens):
384
+ if disables_verification(token):
385
+ sys.exit(1)
386
+ if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
387
+ sys.exit(1)
388
+ # Allowlist the destinations, do not denylist the disabling ones.
389
+ #
390
+ # This used to block only "" and /dev/null. But hooks are disabled just as
391
+ # completely by pointing hooksPath at any directory that happens to contain
392
+ # none — `-c core.hooksPath=/tmp/empty` bypassed every hook while reading as
393
+ # an ordinary path — so a denylist of "obviously disabling" values can
394
+ # always be stepped around by naming a third thing. The set of paths that
395
+ # DISABLE hooks is unbounded; the set that legitimately relocates them is
396
+ # tiny and known, so the allowlist is the only side that can be enumerated.
397
+ #
398
+ # Matched case-insensitively because git config variable names are:
399
+ # `CORE.HOOKSPATH=/x` and `core.hookspath=/x` are the same setting to git,
400
+ # so a case-sensitive check is bypassed by holding down shift.
401
+ lowered = token.lower()
402
+ if lowered.startswith("core.hookspath="):
403
+ if not is_permitted_hooks_path(token.split("=", 1)[1]):
404
+ sys.exit(1)
405
+ if lowered == "core.hookspath" and i + 1 < len(normalized_tokens):
406
+ if not is_permitted_hooks_path(normalized_tokens[i + 1]):
407
+ sys.exit(1)
408
+ # `git --config-env=core.hooksPath=SOMEVAR` sets the same config, reading
409
+ # the value out of the named environment variable. The path therefore is
410
+ # not in the command at all, so there is nothing to allowlist against —
411
+ # SOMEVAR can hold anything by the time git reads it. Any core.hooksPath
412
+ # routed through --config-env is refused outright.
413
+ if lowered.startswith("--config-env="):
414
+ spec = token.split("=", 1)[1]
415
+ if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
416
+ sys.exit(1)
417
+ # git accepts `--config-env <name>=<envvar>` as TWO tokens as well as one,
418
+ # and guarding only the `=` spelling was worse than missing the separate
419
+ # form outright: the trailing `core.hooksPath=.husky` then fell through to
420
+ # the allowlist above, which reads `.husky` as a PATH and permits it. But
421
+ # here it is an ENVIRONMENT VARIABLE NAME, and `env '.husky=/dev/null' git
422
+ # --config-env core.hooksPath=.husky` really does resolve hooksPath to
423
+ # /dev/null. The allowlist was being used as the bypass.
424
+ #
425
+ # Checked at the `--config-env` token, which the loop reaches first, so the
426
+ # refusal happens before the value token can be mistaken for a path.
427
+ if lowered == "--config-env" and i + 1 < len(normalized_tokens):
428
+ spec = normalized_tokens[i + 1]
429
+ if spec.split("=", 1)[0].strip().strip("'\"").lower() == "core.hookspath":
430
+ sys.exit(1)
431
+ # `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath
432
+ # GIT_CONFIG_VALUE_0=/dev/null git commit` sets command-scope config the
433
+ # same way `-c core.hooksPath=...` does — env-var-style assignments ahead of
434
+ # the invocation instead of a flag — so it disables every hook just as
435
+ # completely while matching none of the token shapes above. Upstream missed
436
+ # this until a downstream fork hardened its own copy against it, which is
437
+ # the one direction a guard must never be caught in.
438
+ #
439
+ # The index is matched as `\d+` rather than pinned to 0: git accepts any
440
+ # index below GIT_CONFIG_COUNT, so a single-index check is evaded by typing
441
+ # a 1. Refused outright, like --config-env=, because the path lives in a
442
+ # separate GIT_CONFIG_VALUE_<n> token that can be exported earlier,
443
+ # reordered, or left out entirely — there is nothing here to allowlist
444
+ # against.
445
+ key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
446
+ if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
447
+ sys.exit(1)
448
+
449
+ sys.exit(0)
450
+ PY
451
+ then
452
+ cat >&2 <<'EOF'
453
+ Blocked: this command bypasses pre-commit/pre-push hooks (--no-verify or its
454
+ short form -n, HUSKY=0, or core.hooksPath disabling). `git commit -n` and a
455
+ cluster such as `-nm "msg"` skip pre-commit exactly as completely as the long
456
+ flag. Fix the underlying issue (security audit, lint,
457
+ typecheck, tests, formatting) instead. If a fix is genuinely impossible, ask the
458
+ user to make the risk-acceptance decision and add a specific documented ignore;
459
+ never bypass the hook.
460
+ EOF
461
+ exit 2
462
+ fi
463
+
464
+ exit 0