@codyswann/lisa 2.26.0 → 2.26.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 (48) 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/rules/base-rules.md +1 -1
  15. package/plugins/lisa/skills/debrief/SKILL.md +9 -3
  16. package/plugins/lisa/skills/implement/SKILL.md +10 -4
  17. package/plugins/lisa/skills/intake/SKILL.md +9 -3
  18. package/plugins/lisa/skills/monitor/SKILL.md +9 -3
  19. package/plugins/lisa/skills/plan/SKILL.md +9 -3
  20. package/plugins/lisa/skills/research/SKILL.md +9 -3
  21. package/plugins/lisa/skills/verify/SKILL.md +9 -3
  22. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  28. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -2
  30. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -2
  32. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -2
  34. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  36. package/plugins/src/base/rules/base-rules.md +1 -1
  37. package/plugins/src/base/skills/debrief/SKILL.md +9 -3
  38. package/plugins/src/base/skills/implement/SKILL.md +10 -4
  39. package/plugins/src/base/skills/intake/SKILL.md +9 -3
  40. package/plugins/src/base/skills/monitor/SKILL.md +9 -3
  41. package/plugins/src/base/skills/plan/SKILL.md +9 -3
  42. package/plugins/src/base/skills/research/SKILL.md +9 -3
  43. package/plugins/src/base/skills/verify/SKILL.md +9 -3
  44. package/scripts/generate-codex-plugin-artifacts.mjs +17 -129
  45. package/plugins/lisa/hooks/hooks.json +0 -104
  46. package/plugins/lisa-nestjs/hooks/hooks.json +0 -15
  47. package/plugins/lisa-rails/hooks/hooks.json +0 -19
  48. package/plugins/lisa-typescript/hooks/hooks.json +0 -23
@@ -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
- }