@codyswann/lisa 2.26.0 → 2.26.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 (32) hide show
  1. package/dist/codex/hooks-installer.d.ts +7 -0
  2. package/dist/codex/hooks-installer.d.ts.map +1 -1
  3. package/dist/codex/hooks-installer.js +45 -3
  4. package/dist/codex/hooks-installer.js.map +1 -1
  5. package/dist/codex/scripts/_extract-edit-paths.sh +49 -0
  6. package/dist/codex/scripts/block-migration-edits.sh +15 -49
  7. package/dist/codex/scripts/format-on-edit.sh +26 -21
  8. package/dist/codex/scripts/lint-on-edit.sh +23 -14
  9. package/dist/codex/scripts/rubocop-on-edit.sh +23 -14
  10. package/dist/codex/scripts/sg-scan-on-edit.sh +22 -12
  11. package/package.json +1 -1
  12. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa/.codex-plugin/plugin.json +1 -2
  14. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  16. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  18. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  20. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -2
  22. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -2
  24. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -2
  26. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  28. package/scripts/generate-codex-plugin-artifacts.mjs +17 -129
  29. package/plugins/lisa/hooks/hooks.json +0 -104
  30. package/plugins/lisa-nestjs/hooks/hooks.json +0 -15
  31. package/plugins/lisa-rails/hooks/hooks.json +0 -19
  32. package/plugins/lisa-typescript/hooks/hooks.json +0 -23
@@ -1,6 +1,13 @@
1
1
  import type { ProjectType } from "../core/config.js";
2
2
  /** Subdirectory inside `.codex/` for Lisa-managed hook scripts */
3
3
  export declare const LISA_HOOKS_SUBDIR: string;
4
+ /**
5
+ * Shared shell helper sourced by every edit-aware hook to resolve the file
6
+ * path(s) a tool touches (handles single-file Edit/Write and multi-file
7
+ * apply_patch). Copied alongside the hook scripts whenever an edit hook is
8
+ * installed so the apply_patch parsing lives in exactly one place.
9
+ */
10
+ export declare const EDIT_PATHS_LIB = "_extract-edit-paths.sh";
4
11
  /** Subdirectory inside `.codex/` for Lisa rules content (read by inject-rules) */
5
12
  export declare const LISA_RULES_SUBDIR = "lisa-rules";
6
13
  /** Filename of the Codex hooks config file inside `.codex/` */
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/codex/hooks-installer.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,QAA6B,CAAC;AAE5D,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C,+DAA+D;AAC/D,eAAO,MAAM,cAAc,eAAe,CAAC;AA+G3C,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,WAAW,EAAE,GACpC,OAAO,CAAC,kBAAkB,CAAC,CA2C7B"}
1
+ {"version":3,"file":"hooks-installer.d.ts","sourceRoot":"","sources":["../../src/codex/hooks-installer.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUrD,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,QAA6B,CAAC;AAE5D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;AAEvD,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C,+DAA+D;AAC/D,eAAO,MAAM,cAAc,eAAe,CAAC;AA0H3C,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,SAAS,WAAW,EAAE,GACpC,OAAO,CAAC,kBAAkB,CAAC,CA4D7B"}
@@ -7,10 +7,24 @@
7
7
  * 3. For inject-rules: also mirror Lisa rules into `.codex/lisa-rules/`
8
8
  * 4. Tagged-merge `.codex/hooks.json`
9
9
  *
10
- * Codex hook event support map (vs. Lisa's existing Claude Code hooks):
10
+ * Codex hook event support map (vs. Lisa's existing Claude Code hooks),
11
+ * verified against codex-cli 0.125.0 by source-read + runtime tests:
11
12
  * - SessionStart, PreToolUse, PostToolUse, UserPromptSubmit, Stop ✅
12
13
  * - PermissionRequest ✅ (Codex-only)
13
14
  * - SubagentStart, SessionEnd, Notification, PreCompact ❌ (Codex doesn't have these)
15
+ *
16
+ * Claude hooks intentionally NOT ported (no Codex equivalent):
17
+ * - enforce-team-first.sh — gates Claude's TeamCreate/Skill/ToolSearch agent-
18
+ * team orchestration; Codex's multi-agent model is different.
19
+ * - inject-flow-context.sh — fires on SubagentStart, which Codex lacks.
20
+ * - the SessionEnd `entire` hook — Codex has no SessionEnd event.
21
+ * inject-rules also fires on SubagentStart under Claude; on Codex only its
22
+ * SessionStart variant applies (Codex has no per-subagent start event).
23
+ *
24
+ * Codex does NOT execute plugin-bundled hooks (a `.codex-plugin` `hooks`
25
+ * pointer / `hooks/hooks.json` never fires), so this installer writes hooks
26
+ * into the project's own `.codex/hooks.json` instead. See
27
+ * scripts/generate-codex-plugin-artifacts.mjs for the build-side counterpart.
14
28
  * @module codex/hooks-installer
15
29
  */
16
30
  import * as fse from "fs-extra";
@@ -20,6 +34,13 @@ import { fileURLToPath } from "node:url";
20
34
  import { mergeLisaHooks, parseHooksFile, serializeHooksFile, } from "./hooks-merger.js";
21
35
  /** Subdirectory inside `.codex/` for Lisa-managed hook scripts */
22
36
  export const LISA_HOOKS_SUBDIR = path.join("hooks", "lisa");
37
+ /**
38
+ * Shared shell helper sourced by every edit-aware hook to resolve the file
39
+ * path(s) a tool touches (handles single-file Edit/Write and multi-file
40
+ * apply_patch). Copied alongside the hook scripts whenever an edit hook is
41
+ * installed so the apply_patch parsing lives in exactly one place.
42
+ */
43
+ export const EDIT_PATHS_LIB = "_extract-edit-paths.sh";
23
44
  /** Subdirectory inside `.codex/` for Lisa rules content (read by inject-rules) */
24
45
  export const LISA_RULES_SUBDIR = "lisa-rules";
25
46
  /** Filename of the Codex hooks config file inside `.codex/` */
@@ -85,6 +106,7 @@ const HOOK_CATALOG = [
85
106
  matcher: WRITE_MATCHER,
86
107
  scriptFilename: "format-on-edit.sh",
87
108
  forProjectTypes: ["typescript"],
109
+ needsEditPathLib: true,
88
110
  },
89
111
  {
90
112
  id: "lint-on-edit",
@@ -92,13 +114,15 @@ const HOOK_CATALOG = [
92
114
  matcher: WRITE_MATCHER,
93
115
  scriptFilename: "lint-on-edit.sh",
94
116
  forProjectTypes: ["typescript"],
117
+ needsEditPathLib: true,
95
118
  },
96
119
  {
97
120
  id: "sg-scan-on-edit",
98
121
  event: "PostToolUse",
99
122
  matcher: WRITE_MATCHER,
100
123
  scriptFilename: "sg-scan-on-edit.sh",
101
- forProjectTypes: ["typescript"],
124
+ forProjectTypes: ["typescript", "rails"],
125
+ needsEditPathLib: true,
102
126
  },
103
127
  {
104
128
  id: "rubocop-on-edit",
@@ -106,6 +130,7 @@ const HOOK_CATALOG = [
106
130
  matcher: WRITE_MATCHER,
107
131
  scriptFilename: "rubocop-on-edit.sh",
108
132
  forProjectTypes: ["rails"],
133
+ needsEditPathLib: true,
109
134
  },
110
135
  {
111
136
  id: "block-migration-edits",
@@ -113,6 +138,7 @@ const HOOK_CATALOG = [
113
138
  matcher: WRITE_MATCHER,
114
139
  scriptFilename: "block-migration-edits.sh",
115
140
  forProjectTypes: ["nestjs"],
141
+ needsEditPathLib: true,
116
142
  },
117
143
  ];
118
144
  /**
@@ -138,6 +164,17 @@ export async function installHooks(lisaDir, destDir, detectedTypes) {
138
164
  await chmod(scriptDest, 0o755);
139
165
  return path.join(LISA_HOOKS_SUBDIR, entry.scriptFilename);
140
166
  }));
167
+ // Step 1b: copy the shared edit-path helper when any installed hook sources
168
+ // it. Edit-aware hooks (format/lint/sg-scan/rubocop/block-migration) source
169
+ // this for apply_patch path parsing.
170
+ const libFiles = applicable.some(e => e.needsEditPathLib)
171
+ ? await (async () => {
172
+ const libDest = path.join(hooksDir, EDIT_PATHS_LIB);
173
+ await copyFile(resolveBundledScript(EDIT_PATHS_LIB), libDest);
174
+ await chmod(libDest, 0o755);
175
+ return [path.join(LISA_HOOKS_SUBDIR, EDIT_PATHS_LIB)];
176
+ })()
177
+ : [];
141
178
  // Step 2: mirror rules from Lisa into .codex/lisa-rules/ (only when
142
179
  // inject-rules is being installed — i.e., always, since it's a "*" hook)
143
180
  const ruleFiles = applicable.some(e => e.id === "inject-rules")
@@ -150,7 +187,12 @@ export async function installHooks(lisaDir, destDir, detectedTypes) {
150
187
  const merged = mergeLisaHooks(existing, lisaHookSpecs);
151
188
  await writeFile(hooksFilePath, serializeHooksFile(merged), "utf8");
152
189
  return {
153
- managedFiles: Object.freeze([...scriptFiles, ...ruleFiles, HOOKS_FILENAME]),
190
+ managedFiles: Object.freeze([
191
+ ...scriptFiles,
192
+ ...libFiles,
193
+ ...ruleFiles,
194
+ HOOKS_FILENAME,
195
+ ]),
154
196
  hookEntries: lisaHookSpecs.length,
155
197
  };
156
198
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/codex/hooks-installer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks-installer.js","sourceRoot":"","sources":["../../src/codex/hooks-installer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # Lisa-managed Codex hook helper (sourced, not executed directly).
3
+ #
4
+ # Provides `lisa_extract_edit_paths`, the single source of truth for turning a
5
+ # Codex Pre/PostToolUse hook envelope into the list of file paths the tool
6
+ # touches. Every edit-aware Lisa hook (format/lint/sg-scan/rubocop/block-
7
+ # migration) sources this so the apply_patch parsing lives in exactly one place.
8
+ #
9
+ # Tool envelope shapes (verified against codex-cli 0.125.0 by capturing real
10
+ # hook stdin):
11
+ # Edit / Write → tool_input.file_path (single string)
12
+ # apply_patch → tool_input.command (a STRING containing the full patch,
13
+ # NOT an array — there is no command[1])
14
+ #
15
+ # An apply_patch patch encodes its targets as header lines:
16
+ # *** Add File: <path>
17
+ # *** Update File: <path>
18
+ # *** Delete File: <path>
19
+ # A single patch may carry MANY files, so callers must loop over the output.
20
+
21
+ # Print, one per line, every file path the tool envelope intends to write.
22
+ # Emits nothing (and returns 0) when jq is unavailable or no path is found, so
23
+ # callers can fail open.
24
+ #
25
+ # $1 - the full hook stdin JSON
26
+ lisa_extract_edit_paths() {
27
+ local json="$1"
28
+ command -v jq >/dev/null 2>&1 || return 0
29
+
30
+ local tool_name
31
+ tool_name="$(printf '%s' "$json" | jq -r '.tool_name // .tool // empty')"
32
+
33
+ if [ "$tool_name" = "apply_patch" ]; then
34
+ local patch_text
35
+ patch_text="$(printf '%s' "$json" | jq -r '.tool_input.command // empty')"
36
+ [ -n "$patch_text" ] || return 0
37
+ # Walk the patch line-by-line with bash-native string ops (NOT grep/sed on
38
+ # JSON — see .claude/rules/PROJECT_RULES.md) to pull out every file header.
39
+ while IFS= read -r line; do
40
+ case "$line" in
41
+ "*** Add File: "* | "*** Update File: "* | "*** Delete File: "*)
42
+ printf '%s\n' "${line#*File: }"
43
+ ;;
44
+ esac
45
+ done <<<"$patch_text"
46
+ else
47
+ printf '%s' "$json" | jq -r '.tool_input.file_path // empty'
48
+ fi
49
+ }
@@ -4,59 +4,23 @@
4
4
  # to regenerate from entity diffs instead — hand-written migrations drift
5
5
  # from entity metadata and break the schema/migration contract.
6
6
  #
7
- # Codex blocks the tool call when stdout has hookSpecificOutput.permissionDecision
8
- # set to "deny", or when the script exits non-zero with a deny message.
9
- #
10
- # Tool envelope shapes (Codex CLI 0.125.0):
11
- # Edit / Write → tool_input.file_path (single string)
12
- # apply_patch → tool_input.command (["apply_patch", "<patch>"])
13
- #
14
- # The patch text encodes target file paths via "*** Update File: <path>" /
15
- # "*** Add File: <path>" / "*** Delete File: <path>" directives. We must
16
- # extract every such path so apply_patch can't sneak past this guard.
17
- set -euo pipefail
7
+ # Codex blocks the tool call when the script exits non-zero with a deny message
8
+ # on stderr (exit 2). The shared extractor resolves every target path from the
9
+ # tool envelope — including multi-file apply_patch patches — so an edit can't
10
+ # slip a migration change past this guard.
11
+ set -uo pipefail
18
12
 
19
13
  JSON_INPUT="$(cat)"
20
14
 
21
- if ! command -v jq >/dev/null 2>&1; then
22
- # Without jq we can't reliably parse — fail open (allow the edit). This
23
- # matches the project rule against grep/sed/cut/awk-based JSON parsing.
24
- exit 0
25
- fi
26
-
27
- # Determine which tool fired. Codex puts the tool name at .tool_name on
28
- # every PreToolUse envelope; falling back to .tool just in case.
29
- TOOL_NAME="$(echo "$JSON_INPUT" | jq -r '.tool_name // .tool // empty')"
15
+ # Without jq we can't reliably parse — fail open (allow the edit). This matches
16
+ # the project rule against grep/sed/cut/awk-based JSON parsing.
17
+ command -v jq >/dev/null 2>&1 || exit 0
30
18
 
31
- # Collect every candidate path the tool intends to write. Newline-separated.
32
- CANDIDATE_PATHS=""
33
- case "$TOOL_NAME" in
34
- apply_patch)
35
- # Pull the full patch string out of tool_input.command[1], then extract
36
- # every "*** {Update,Add,Delete} File: <path>" header line. jq handles
37
- # the JSON; we use bash-native string splitting (NOT grep/cut on JSON)
38
- # to walk the patch text line-by-line.
39
- PATCH_TEXT="$(echo "$JSON_INPUT" | jq -r '.tool_input.command[1] // empty')"
40
- if [ -n "${PATCH_TEXT}" ]; then
41
- while IFS= read -r line; do
42
- case "$line" in
43
- "*** Update File: "*|"*** Add File: "*|"*** Delete File: "*)
44
- CANDIDATE_PATHS+="${line#*: }"$'\n'
45
- ;;
46
- esac
47
- done <<<"${PATCH_TEXT}"
48
- fi
49
- ;;
50
- *)
51
- # Edit / Write / anything else: single file under tool_input.file_path
52
- SINGLE_PATH="$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')"
53
- [ -n "${SINGLE_PATH}" ] && CANDIDATE_PATHS="${SINGLE_PATH}"
54
- ;;
55
- esac
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ # shellcheck source=/dev/null
21
+ . "${SCRIPT_DIR}/_extract-edit-paths.sh"
56
22
 
57
- [ -n "${CANDIDATE_PATHS}" ] || exit 0
58
-
59
- # Walk every candidate; deny on the first migration match.
23
+ # Walk every candidate path; deny on the first migration match.
60
24
  while IFS= read -r FILE_PATH; do
61
25
  [ -n "${FILE_PATH}" ] || continue
62
26
  case "${FILE_PATH}" in
@@ -73,6 +37,8 @@ EOF
73
37
  exit 2
74
38
  ;;
75
39
  esac
76
- done <<<"${CANDIDATE_PATHS}"
40
+ done <<EOF
41
+ $(lisa_extract_edit_paths "$JSON_INPUT")
42
+ EOF
77
43
 
78
44
  exit 0
@@ -1,15 +1,9 @@
1
1
  #!/usr/bin/env bash
2
2
  # Lisa-managed Codex hook script (PostToolUse Edit|Write|apply_patch).
3
- # Runs Prettier on the just-edited file. Reads tool_input.file_path from
4
- # stdin JSON.
5
- #
6
- # Note on apply_patch: Codex's apply_patch tool envelope exposes the patch
7
- # under tool_input.command (an array), not tool_input.file_path. This script
8
- # only formats single-file Edit/Write tool calls. apply_patch fires get
9
- # silently skipped (FILE_PATH empty) — that's acceptable here because the
10
- # next save/edit will still run the formatter, and the user can always run
11
- # `prettier --write` against modified files manually.
12
- set -euo pipefail
3
+ # Runs Prettier on every just-edited file. Resolves the target file(s) from the
4
+ # tool envelope via the shared extractor, which handles both single-file
5
+ # Edit/Write (tool_input.file_path) and multi-file apply_patch (tool_input.command).
6
+ set -uo pipefail
13
7
 
14
8
  JSON_INPUT="$(cat)"
15
9
 
@@ -17,19 +11,30 @@ JSON_INPUT="$(cat)"
17
11
  # with grep/sed/cut/awk — always use jq. Fail open without jq so we don't
18
12
  # block the agent on missing tooling.
19
13
  command -v jq >/dev/null 2>&1 || exit 0
20
- FILE_PATH="$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')"
21
14
 
22
- [ -n "${FILE_PATH}" ] || exit 0
23
- [ -f "${FILE_PATH}" ] || exit 0
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ # shellcheck source=/dev/null
17
+ . "${SCRIPT_DIR}/_extract-edit-paths.sh"
24
18
 
25
- case "${FILE_PATH##*.}" in
26
- ts|tsx|js|jsx|mjs|cjs|json|md|yaml|yml|css|scss|html) ;;
27
- *) exit 0 ;;
28
- esac
29
-
30
- # Prefer the project-local prettier; fall back to a globally installed one
19
+ # Resolve the formatter once, up front.
31
20
  if [ -x "./node_modules/.bin/prettier" ]; then
32
- ./node_modules/.bin/prettier --write "${FILE_PATH}" >/dev/null 2>&1 || true
21
+ PRETTIER="./node_modules/.bin/prettier"
33
22
  elif command -v prettier >/dev/null 2>&1; then
34
- prettier --write "${FILE_PATH}" >/dev/null 2>&1 || true
23
+ PRETTIER="prettier"
24
+ else
25
+ exit 0
35
26
  fi
27
+
28
+ while IFS= read -r FILE_PATH; do
29
+ [ -n "${FILE_PATH}" ] || continue
30
+ [ -f "${FILE_PATH}" ] || continue
31
+ case "${FILE_PATH##*.}" in
32
+ ts | tsx | js | jsx | mjs | cjs | json | md | yaml | yml | css | scss | html) ;;
33
+ *) continue ;;
34
+ esac
35
+ "$PRETTIER" --write "${FILE_PATH}" >/dev/null 2>&1 || true
36
+ done <<EOF
37
+ $(lisa_extract_edit_paths "$JSON_INPUT")
38
+ EOF
39
+
40
+ exit 0
@@ -1,23 +1,19 @@
1
1
  #!/usr/bin/env bash
2
2
  # Lisa-managed Codex hook script (PostToolUse Edit|Write|apply_patch).
3
- # Runs ESLint --fix on the just-edited file. If unfixable errors remain,
4
- # exits non-zero so Codex sees the failure and the agent self-corrects.
5
- set -euo pipefail
3
+ # Runs ESLint --fix on every just-edited file. If unfixable errors remain on
4
+ # any file, exits non-zero so Codex sees the failure and the agent self-corrects.
5
+ # Resolves target file(s) via the shared extractor (Edit/Write + apply_patch).
6
+ set -uo pipefail
6
7
 
7
8
  JSON_INPUT="$(cat)"
8
9
 
9
10
  # Project rule (.claude/rules/PROJECT_RULES.md): never parse JSON in shell
10
11
  # with grep/sed/cut/awk — always use jq. Fail open without jq.
11
12
  command -v jq >/dev/null 2>&1 || exit 0
12
- FILE_PATH="$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')"
13
13
 
14
- [ -n "${FILE_PATH}" ] || exit 0
15
- [ -f "${FILE_PATH}" ] || exit 0
16
-
17
- case "${FILE_PATH##*.}" in
18
- ts|tsx|js|jsx|mjs|cjs) ;;
19
- *) exit 0 ;;
20
- esac
14
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15
+ # shellcheck source=/dev/null
16
+ . "${SCRIPT_DIR}/_extract-edit-paths.sh"
21
17
 
22
18
  if [ -x "./node_modules/.bin/eslint" ]; then
23
19
  ESLINT="./node_modules/.bin/eslint"
@@ -27,6 +23,19 @@ else
27
23
  exit 0
28
24
  fi
29
25
 
30
- # Auto-fix what we can; surface anything left so the agent fixes it itself
31
- "$ESLINT" --fix "${FILE_PATH}" >/dev/null 2>&1 || true
32
- "$ESLINT" --quiet "${FILE_PATH}"
26
+ STATUS=0
27
+ while IFS= read -r FILE_PATH; do
28
+ [ -n "${FILE_PATH}" ] || continue
29
+ [ -f "${FILE_PATH}" ] || continue
30
+ case "${FILE_PATH##*.}" in
31
+ ts | tsx | js | jsx | mjs | cjs) ;;
32
+ *) continue ;;
33
+ esac
34
+ # Auto-fix what we can; surface anything left so the agent fixes it itself.
35
+ "$ESLINT" --fix "${FILE_PATH}" >/dev/null 2>&1 || true
36
+ "$ESLINT" --quiet "${FILE_PATH}" || STATUS=1
37
+ done <<EOF
38
+ $(lisa_extract_edit_paths "$JSON_INPUT")
39
+ EOF
40
+
41
+ exit "$STATUS"
@@ -1,24 +1,20 @@
1
1
  #!/usr/bin/env bash
2
2
  # Lisa-managed Codex hook script (PostToolUse Edit|Write|apply_patch).
3
- # Runs RuboCop -a (safe autocorrect) on the just-edited Ruby file, then
4
- # checks for remaining unfixable errors. Blocking — non-zero exit forces
5
- # the agent to fix.
6
- set -euo pipefail
3
+ # Runs RuboCop -a (safe autocorrect) on every just-edited Ruby file, then checks
4
+ # for remaining unfixable errors. Blocking — a non-zero exit on any file forces
5
+ # the agent to fix. Resolves target file(s) via the shared extractor
6
+ # (Edit/Write + apply_patch).
7
+ set -uo pipefail
7
8
 
8
9
  JSON_INPUT="$(cat)"
9
10
 
10
11
  # Project rule (.claude/rules/PROJECT_RULES.md): never parse JSON in shell
11
12
  # with grep/sed/cut/awk — always use jq. Fail open without jq.
12
13
  command -v jq >/dev/null 2>&1 || exit 0
13
- FILE_PATH="$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty')"
14
14
 
15
- [ -n "${FILE_PATH}" ] || exit 0
16
- [ -f "${FILE_PATH}" ] || exit 0
17
-
18
- case "${FILE_PATH##*.}" in
19
- rb|rake) ;;
20
- *) exit 0 ;;
21
- esac
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ # shellcheck source=/dev/null
17
+ . "${SCRIPT_DIR}/_extract-edit-paths.sh"
22
18
 
23
19
  if command -v bundle >/dev/null 2>&1 && [ -f "./Gemfile" ]; then
24
20
  RUBOCOP=(bundle exec rubocop)
@@ -28,5 +24,18 @@ else
28
24
  exit 0
29
25
  fi
30
26
 
31
- "${RUBOCOP[@]}" -a "${FILE_PATH}" >/dev/null 2>&1 || true
32
- "${RUBOCOP[@]}" "${FILE_PATH}"
27
+ STATUS=0
28
+ while IFS= read -r FILE_PATH; do
29
+ [ -n "${FILE_PATH}" ] || continue
30
+ [ -f "${FILE_PATH}" ] || continue
31
+ case "${FILE_PATH##*.}" in
32
+ rb | rake) ;;
33
+ *) continue ;;
34
+ esac
35
+ "${RUBOCOP[@]}" -a "${FILE_PATH}" >/dev/null 2>&1 || true
36
+ "${RUBOCOP[@]}" "${FILE_PATH}" || STATUS=1
37
+ done <<EOF
38
+ $(lisa_extract_edit_paths "$JSON_INPUT")
39
+ EOF
40
+
41
+ exit "$STATUS"
@@ -1,23 +1,20 @@
1
1
  #!/usr/bin/env bash
2
2
  # Lisa-managed Codex hook script (PostToolUse Edit|Write|apply_patch).
3
- # Runs ast-grep scan against the project, reporting only errors involving
4
- # the just-edited file. Blocking — non-zero exit forces the agent to fix.
5
- set -euo pipefail
3
+ # Runs ast-grep scan on every just-edited source file (TypeScript/JS or Ruby),
4
+ # reporting only errors involving those files. Blocking — a non-zero exit on any
5
+ # file forces the agent to fix. Resolves target file(s) via the shared extractor
6
+ # (Edit/Write + apply_patch).
7
+ set -uo pipefail
6
8
 
7
9
  JSON_INPUT="$(cat)"
8
10
 
9
11
  # Project rule (.claude/rules/PROJECT_RULES.md): never parse JSON in shell
10
12
  # with grep/sed/cut/awk — always use jq. Fail open without jq.
11
13
  command -v jq >/dev/null 2>&1 || exit 0
12
- FILE_PATH="$(echo "$JSON_INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)"
13
14
 
14
- [ -n "${FILE_PATH}" ] || exit 0
15
- [ -f "${FILE_PATH}" ] || exit 0
16
-
17
- case "${FILE_PATH##*.}" in
18
- ts|tsx|js|jsx|mjs|cjs) ;;
19
- *) exit 0 ;;
20
- esac
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ # shellcheck source=/dev/null
17
+ . "${SCRIPT_DIR}/_extract-edit-paths.sh"
21
18
 
22
19
  if [ -x "./node_modules/.bin/ast-grep" ]; then
23
20
  AST_GREP="./node_modules/.bin/ast-grep"
@@ -31,4 +28,17 @@ fi
31
28
 
32
29
  [ -f "./sgconfig.yml" ] || exit 0
33
30
 
34
- "$AST_GREP" scan "${FILE_PATH}" 2>&1
31
+ STATUS=0
32
+ while IFS= read -r FILE_PATH; do
33
+ [ -n "${FILE_PATH}" ] || continue
34
+ [ -f "${FILE_PATH}" ] || continue
35
+ case "${FILE_PATH##*.}" in
36
+ ts | tsx | js | jsx | mjs | cjs | rb | rake) ;;
37
+ *) continue ;;
38
+ esac
39
+ "$AST_GREP" scan "${FILE_PATH}" 2>&1 || STATUS=1
40
+ done <<EOF
41
+ $(lisa_extract_edit_paths "$JSON_INPUT")
42
+ EOF
43
+
44
+ exit "$STATUS"
package/package.json CHANGED
@@ -82,7 +82,7 @@
82
82
  "lodash": ">=4.18.1"
83
83
  },
84
84
  "name": "@codyswann/lisa",
85
- "version": "2.26.0",
85
+ "version": "2.26.1",
86
86
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
87
87
  "main": "dist/index.js",
88
88
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -12,7 +12,6 @@
12
12
  "workflow"
13
13
  ],
14
14
  "skills": "./skills/",
15
- "hooks": "./hooks/hooks.json",
16
15
  "interface": {
17
16
  "displayName": "Lisa",
18
17
  "shortDescription": "Universal project governance workflows",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
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": "2.26.0",
3
+ "version": "2.26.1",
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-harper-fabric",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
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": "2.26.0",
3
+ "version": "2.26.1",
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-nestjs",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
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": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -15,7 +15,6 @@
15
15
  "lisa-typescript"
16
16
  ],
17
17
  "skills": "./skills/",
18
- "hooks": "./hooks/hooks.json",
19
18
  "interface": {
20
19
  "displayName": "Lisa NestJS",
21
20
  "shortDescription": "NestJS workflow guidance",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
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": "2.26.0",
3
+ "version": "2.26.1",
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"
@@ -15,7 +15,6 @@
15
15
  "lisa"
16
16
  ],
17
17
  "skills": "./skills/",
18
- "hooks": "./hooks/hooks.json",
19
18
  "interface": {
20
19
  "displayName": "Lisa Rails",
21
20
  "shortDescription": "Ruby on Rails workflows",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint 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": "2.26.0",
3
+ "version": "2.26.1",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -14,7 +14,6 @@
14
14
  "dependencies": [
15
15
  "lisa"
16
16
  ],
17
- "hooks": "./hooks/hooks.json",
18
17
  "interface": {
19
18
  "displayName": "Lisa TypeScript",
20
19
  "shortDescription": "TypeScript lifecycle checks",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.26.0",
3
+ "version": "2.26.1",
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": "2.26.0",
3
+ "version": "2.26.1",
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"
@@ -2,9 +2,19 @@
2
2
  /**
3
3
  * Generate Codex plugin artifacts from the built Claude plugin directories.
4
4
  *
5
- * Claude remains Lisa's production path; this script makes the Codex side
6
- * durable by deriving .codex-plugin metadata and compatible hook manifests
7
- * every time plugins are rebuilt.
5
+ * Claude remains Lisa's production path; this script derives the .codex-plugin
6
+ * metadata (skills + MCP pointers) every time plugins are rebuilt.
7
+ *
8
+ * NOTE ON HOOKS: this script does NOT emit Codex hooks. Codex (codex-cli
9
+ * 0.125.0) does not execute plugin-bundled hooks — its plugin manifest parser
10
+ * only honors `skills`, `mcpServers`, `apps`, and interface fields, and a
11
+ * runtime test confirmed a bundled `hooks/hooks.json` never fires. Lisa's
12
+ * Codex hooks are instead installed into the project's `.codex/hooks.json`
13
+ * by `src/codex/hooks-installer.ts` (run during `lisa` apply). Hooks with no
14
+ * Codex equivalent are intentionally not ported: `enforce-team-first.sh`
15
+ * (Claude-team-specific), `inject-flow-context.sh` and any SubagentStart hook
16
+ * (Codex has no SubagentStart event), and the SessionEnd `entire` hook (Codex
17
+ * has no SessionEnd event).
8
18
  */
9
19
  import fs from "node:fs";
10
20
  import path from "node:path";
@@ -29,17 +39,10 @@ if (!fs.existsSync(claudeManifestPath)) {
29
39
 
30
40
  const claudeManifest = JSON.parse(fs.readFileSync(claudeManifestPath, "utf8"));
31
41
  const pluginName = claudeManifest.name;
32
- const UNSUPPORTED_CODEX_HOOK_SCRIPTS = new Set([
33
- "hooks/enforce-team-first.sh",
34
- "hooks/inject-flow-context.sh",
35
- "hooks/inject-rules.sh",
36
- ]);
37
- const codexHooks = convertHooks(pluginName, claudeManifest.hooks ?? {});
38
42
 
39
- writeCodexManifest(pluginName, versionArg, codexHooks);
40
- writeCodexHooks(codexHooks);
43
+ writeCodexManifest(pluginName, versionArg);
41
44
 
42
- function writeCodexManifest(pluginName, version, hooksFile) {
45
+ function writeCodexManifest(pluginName, version) {
43
46
  const metadata = metadataFor(pluginName);
44
47
  const manifest = {
45
48
  name: pluginName,
@@ -50,7 +53,7 @@ function writeCodexManifest(pluginName, version, hooksFile) {
50
53
  ...(claudeManifest.dependencies
51
54
  ? { dependencies: claudeManifest.dependencies }
52
55
  : {}),
53
- ...componentPointers(hooksFile),
56
+ ...componentPointers(),
54
57
  interface: {
55
58
  displayName: metadata.displayName,
56
59
  shortDescription: metadata.shortDescription,
@@ -70,7 +73,7 @@ function writeCodexManifest(pluginName, version, hooksFile) {
70
73
  );
71
74
  }
72
75
 
73
- function componentPointers(hooksFile) {
76
+ function componentPointers() {
74
77
  return {
75
78
  ...(fs.existsSync(path.join(pluginDir, "skills"))
76
79
  ? { skills: "./skills/" }
@@ -78,124 +81,9 @@ function componentPointers(hooksFile) {
78
81
  ...(fs.existsSync(path.join(pluginDir, ".mcp.json"))
79
82
  ? { mcpServers: "./.mcp.json" }
80
83
  : {}),
81
- ...(hooksFile ? { hooks: "./hooks/hooks.json" } : {}),
82
84
  };
83
85
  }
84
86
 
85
- function writeCodexHooks(hooksFile) {
86
- const hooksDir = path.join(pluginDir, "hooks");
87
- const hooksPath = path.join(hooksDir, "hooks.json");
88
- if (!hooksFile) {
89
- if (fs.existsSync(hooksPath)) {
90
- fs.rmSync(hooksPath);
91
- }
92
- return;
93
- }
94
- fs.mkdirSync(hooksDir, { recursive: true });
95
- fs.writeFileSync(hooksPath, `${JSON.stringify(hooksFile, null, 2)}\n`);
96
- }
97
-
98
- function convertHooks(pluginName, claudeHooks) {
99
- const supportedEvents = new Set([
100
- "UserPromptSubmit",
101
- "PostToolUse",
102
- "PreToolUse",
103
- "Stop",
104
- "SessionStart",
105
- ]);
106
- const entries = Object.entries(claudeHooks)
107
- .filter(([event]) => supportedEvents.has(event))
108
- .map(([event, groups]) => [event, convertHookGroups(pluginName, groups)])
109
- .filter(([, groups]) => groups.length > 0);
110
- return entries.length > 0
111
- ? { hooks: Object.fromEntries(entries) }
112
- : undefined;
113
- }
114
-
115
- function convertHookGroups(pluginName, groups) {
116
- return groups
117
- .map(group => ({
118
- ...(group.matcher !== undefined
119
- ? { matcher: normalizeMatcher(group.matcher) }
120
- : {}),
121
- hooks: (group.hooks ?? [])
122
- .map(hook => convertHookHandler(pluginName, hook))
123
- .filter(Boolean),
124
- }))
125
- .filter(group => group.hooks.length > 0);
126
- }
127
-
128
- function normalizeMatcher(matcher) {
129
- const normalized = String(matcher).replaceAll("Write|Edit", "Edit|Write");
130
- return normalized.includes("apply_patch")
131
- ? normalized
132
- : normalized.replaceAll("Edit|Write", "Edit|Write|apply_patch");
133
- }
134
-
135
- function convertHookHandler(pluginName, hook) {
136
- if (hook.type !== "command" || typeof hook.command !== "string") {
137
- return undefined;
138
- }
139
- const command = convertHookCommand(pluginName, hook.command);
140
- if (command === undefined) {
141
- return undefined;
142
- }
143
- return {
144
- type: "command",
145
- command,
146
- ...(hook.timeout !== undefined ? { timeout: hook.timeout } : {}),
147
- ...(hook.statusMessage !== undefined
148
- ? { statusMessage: hook.statusMessage }
149
- : {}),
150
- };
151
- }
152
-
153
- function convertHookCommand(pluginName, command) {
154
- const pluginScript = command.match(
155
- /\$\{CLAUDE_PLUGIN_ROOT\}\/(hooks\/[^ "';]+)/
156
- );
157
- if (!pluginScript) {
158
- return normalizeInlineCommand(command);
159
- }
160
- if (UNSUPPORTED_CODEX_HOOK_SCRIPTS.has(pluginScript[1])) {
161
- return undefined;
162
- }
163
- return buildPluginScriptRunner(pluginName, pluginScript[1]);
164
- }
165
-
166
- function normalizeInlineCommand(command) {
167
- const entireMatch = command.match(
168
- /^command -v entire >\/dev\/null 2>&1 && entire hooks claude-code ([a-z-]+) \|\| true$/
169
- );
170
- if (!entireMatch) {
171
- return command;
172
- }
173
- return `if command -v entire >/dev/null 2>&1; then entire hooks claude-code ${entireMatch[1]}; fi`;
174
- }
175
-
176
- function buildPluginScriptRunner(pluginName, scriptPath) {
177
- const script = JSON.stringify(scriptPath);
178
- const plugin = JSON.stringify(pluginName);
179
- return [
180
- "bash -lc '",
181
- `plugin=${shellQuote(plugin)}; script=${shellQuote(script)}; `,
182
- "repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); ",
183
- 'for root in "${CODEX_PLUGIN_ROOT:-}" "${CLAUDE_PLUGIN_ROOT:-}" "$repo/plugins/$plugin" "$HOME/.codex/plugins/cache/lisa/$plugin/local"; do ',
184
- '[ -n "$root" ] || continue; ',
185
- 'if [ -x "$root/$script" ]; then CLAUDE_PLUGIN_ROOT="$root" CODEX_PLUGIN_ROOT="$root" exec "$root/$script"; fi; ',
186
- "done; ",
187
- 'found=$(find "$HOME/.codex/plugins/cache" -path "*/$plugin/*/$script" -type f -exec ls -t {} + 2>/dev/null | head -n 1); ',
188
- '[ -n "$found" ] || exit 0; ',
189
- "root=${found%/$script}; ",
190
- 'CLAUDE_PLUGIN_ROOT="$root" CODEX_PLUGIN_ROOT="$root" exec "$found"',
191
- "'",
192
- ].join("");
193
- }
194
-
195
- function shellQuote(jsonStringLiteral) {
196
- return jsonStringLiteral.replaceAll("'", "'\\''");
197
- }
198
-
199
87
  function metadataFor(pluginName) {
200
88
  const map = {
201
89
  lisa: {
@@ -1,104 +0,0 @@
1
- {
2
- "hooks": {
3
- "UserPromptSubmit": [
4
- {
5
- "matcher": "",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code user-prompt-submit; fi"
10
- }
11
- ]
12
- }
13
- ],
14
- "PostToolUse": [
15
- {
16
- "matcher": "Task",
17
- "hooks": [
18
- {
19
- "type": "command",
20
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code post-task; fi"
21
- }
22
- ]
23
- },
24
- {
25
- "matcher": "TodoWrite",
26
- "hooks": [
27
- {
28
- "type": "command",
29
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code post-todo; fi"
30
- }
31
- ]
32
- }
33
- ],
34
- "PreToolUse": [
35
- {
36
- "matcher": "Task",
37
- "hooks": [
38
- {
39
- "type": "command",
40
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code pre-task; fi"
41
- }
42
- ]
43
- },
44
- {
45
- "matcher": "Bash",
46
- "hooks": [
47
- {
48
- "type": "command",
49
- "command": "bash -lc 'plugin=\"lisa\"; script=\"hooks/block-no-verify.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
50
- }
51
- ]
52
- }
53
- ],
54
- "Stop": [
55
- {
56
- "matcher": "",
57
- "hooks": [
58
- {
59
- "type": "command",
60
- "command": "bash -lc 'plugin=\"lisa\"; script=\"hooks/notify-ntfy.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
61
- }
62
- ]
63
- },
64
- {
65
- "matcher": "",
66
- "hooks": [
67
- {
68
- "type": "command",
69
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code stop; fi"
70
- }
71
- ]
72
- }
73
- ],
74
- "SessionStart": [
75
- {
76
- "matcher": "startup",
77
- "hooks": [
78
- {
79
- "type": "command",
80
- "command": "bash -lc 'plugin=\"lisa\"; script=\"hooks/install-pkgs.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
81
- }
82
- ]
83
- },
84
- {
85
- "matcher": "",
86
- "hooks": [
87
- {
88
- "type": "command",
89
- "command": "bash -lc 'plugin=\"lisa\"; script=\"hooks/setup-jira-cli.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
90
- }
91
- ]
92
- },
93
- {
94
- "matcher": "",
95
- "hooks": [
96
- {
97
- "type": "command",
98
- "command": "if command -v entire >/dev/null 2>&1; then entire hooks claude-code session-start; fi"
99
- }
100
- ]
101
- }
102
- ]
103
- }
104
- }
@@ -1,15 +0,0 @@
1
- {
2
- "hooks": {
3
- "PreToolUse": [
4
- {
5
- "matcher": "Edit|Write|apply_patch",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "bash -lc 'plugin=\"lisa-nestjs\"; script=\"hooks/block-migration-edits.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
10
- }
11
- ]
12
- }
13
- ]
14
- }
15
- }
@@ -1,19 +0,0 @@
1
- {
2
- "hooks": {
3
- "PostToolUse": [
4
- {
5
- "matcher": "Edit|Write|apply_patch",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "bash -lc 'plugin=\"lisa-rails\"; script=\"hooks/rubocop-on-edit.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
10
- },
11
- {
12
- "type": "command",
13
- "command": "bash -lc 'plugin=\"lisa-rails\"; script=\"hooks/sg-scan-on-edit.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
14
- }
15
- ]
16
- }
17
- ]
18
- }
19
- }
@@ -1,23 +0,0 @@
1
- {
2
- "hooks": {
3
- "PostToolUse": [
4
- {
5
- "matcher": "Edit|Write|apply_patch",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "bash -lc 'plugin=\"lisa-typescript\"; script=\"hooks/format-on-edit.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
10
- },
11
- {
12
- "type": "command",
13
- "command": "bash -lc 'plugin=\"lisa-typescript\"; script=\"hooks/sg-scan-on-edit.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
14
- },
15
- {
16
- "type": "command",
17
- "command": "bash -lc 'plugin=\"lisa-typescript\"; script=\"hooks/lint-on-edit.sh\"; repo=$(git rev-parse --show-toplevel 2>/dev/null || pwd); for root in \"${CODEX_PLUGIN_ROOT:-}\" \"${CLAUDE_PLUGIN_ROOT:-}\" \"$repo/plugins/$plugin\" \"$HOME/.codex/plugins/cache/lisa/$plugin/local\"; do [ -n \"$root\" ] || continue; if [ -x \"$root/$script\" ]; then CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$root/$script\"; fi; done; found=$(find \"$HOME/.codex/plugins/cache\" -path \"*/$plugin/*/$script\" -type f -exec ls -t {} + 2>/dev/null | head -n 1); [ -n \"$found\" ] || exit 0; root=${found%/$script}; CLAUDE_PLUGIN_ROOT=\"$root\" CODEX_PLUGIN_ROOT=\"$root\" exec \"$found\"'"
18
- }
19
- ]
20
- }
21
- ]
22
- }
23
- }