hookstack-cli 0.1.48 → 0.1.49
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.
- package/README.md +41 -19
- package/bin/cli +42 -10
- package/bin/core.mjs +56 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# hookstack-cli
|
|
2
2
|
|
|
3
|
-
**Install
|
|
3
|
+
**Install agentic hooks in one command — Claude Code, OpenAI Codex, or GitHub Copilot.**
|
|
4
4
|
|
|
5
|
-
[hookstack.app](https://www.hookstack.app) — the community catalogue for
|
|
5
|
+
[hookstack.app](https://www.hookstack.app) — the community catalogue for agentic hooks. Browse, select, and wire them into your project with one command. The same hooks install for any of the three supported agents; only the config file format differs.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -22,35 +22,57 @@ That's it. The CLI fetches the hooks, shows you what will be installed, and patc
|
|
|
22
22
|
npx hookstack-cli@latest install --hooks=<slug1>,<slug2>,...
|
|
23
23
|
|
|
24
24
|
Options:
|
|
25
|
-
--hooks <slugs>
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
|
|
25
|
+
--hooks <slugs> Comma-separated hook slugs (required)
|
|
26
|
+
--project Claude Code, this project — ./.claude (default)
|
|
27
|
+
--global, -g Claude Code, all projects — ~/.claude
|
|
28
|
+
--codex-project OpenAI Codex, this project — ./.codex/hooks.json (committed)
|
|
29
|
+
--codex-profile OpenAI Codex, all projects — ~/.codex/hooks.json
|
|
30
|
+
--copilot GitHub Copilot — ./.claude with paths adapted for Copilot
|
|
31
|
+
--scope <s> "project" (default), "global", "copilot",
|
|
32
|
+
"codex-project", or "codex-profile"
|
|
33
|
+
--with-tests Also install vitest unit tests into tests/hooks/ (project scope only)
|
|
34
|
+
--yes, -y Skip prompts (non-interactive / CI)
|
|
35
|
+
--version, -v Print version
|
|
36
|
+
--help, -h Show help
|
|
33
37
|
```
|
|
34
38
|
|
|
39
|
+
### Target agents & scopes
|
|
40
|
+
|
|
41
|
+
The hook code is identical across agents — only the config file it's wired into changes. Pick a target with a flag (or via the interactive menu):
|
|
42
|
+
|
|
43
|
+
| Flag | Agent | Scope | Config file | Scripts dir |
|
|
44
|
+
|---|---|---|---|---|
|
|
45
|
+
| `--project` (default) | Claude Code | this project | `.claude/settings.json` | `.claude/hooks/` |
|
|
46
|
+
| `--global`, `-g` | Claude Code | all projects | `~/.claude/settings.json` | `~/.claude/hooks/` |
|
|
47
|
+
| `--codex-project` | OpenAI Codex | this project | `.codex/hooks.json` (committed) | `.codex/hooks/` |
|
|
48
|
+
| `--codex-profile` | OpenAI Codex | all projects | `~/.codex/hooks.json` | `~/.codex/hooks/` |
|
|
49
|
+
| `--copilot` | GitHub Copilot | this project | `.claude/` paths adapted | `.claude/hooks/` |
|
|
50
|
+
|
|
51
|
+
Codex and Claude Code expose the same lifecycle event names (`PreToolUse`, `PostToolUse`, `SessionStart`, `Stop`…), so a HookStack hook is portable between them without any change to the `.mjs` — the CLI just writes the appropriate config format.
|
|
52
|
+
|
|
35
53
|
### Interactive mode (default in a terminal)
|
|
36
54
|
|
|
37
55
|
When run in a terminal the CLI opens an interactive prompt:
|
|
38
56
|
|
|
39
|
-
1.
|
|
40
|
-
2.
|
|
41
|
-
3. Shows
|
|
42
|
-
4.
|
|
57
|
+
1. Asks which **target agent** to install for — the menu order is: This project → All my projects → Codex profile → Codex project → GitHub Copilot
|
|
58
|
+
2. Fetches the requested hooks from the registry
|
|
59
|
+
3. Shows an **installation summary** (path, category, events, blocking flag)
|
|
60
|
+
4. Shows a **security panel** (shell access · network · filesystem writes · Snyk score)
|
|
61
|
+
5. Asks for confirmation before writing anything
|
|
43
62
|
|
|
44
63
|
### Non-interactive mode (`--yes` or piped)
|
|
45
64
|
|
|
46
65
|
Skips all prompts — useful in CI or dotfile bootstrap scripts.
|
|
47
66
|
|
|
48
67
|
```bash
|
|
49
|
-
# CI bootstrap
|
|
68
|
+
# CI bootstrap (Claude Code, project)
|
|
50
69
|
npx hookstack-cli@latest install --hooks=pre-bash-secret-detection,pre-bash-guard-git-push-main --yes --scope=project
|
|
51
70
|
|
|
52
71
|
# CI bootstrap with unit tests (avoids SonarQube gating on new files without tests)
|
|
53
72
|
npx hookstack-cli@latest install --hooks=pre-bash-secret-detection,pre-bash-guard-git-push-main --yes --with-tests
|
|
73
|
+
|
|
74
|
+
# CI bootstrap for OpenAI Codex (committed ./.codex/hooks.json)
|
|
75
|
+
npx hookstack-cli@latest install --hooks=pre-bash-secret-detection,pre-bash-guard-git-push-main --yes --scope=codex-project
|
|
54
76
|
```
|
|
55
77
|
|
|
56
78
|
---
|
|
@@ -59,11 +81,11 @@ npx hookstack-cli@latest install --hooks=pre-bash-secret-detection,pre-bash-guar
|
|
|
59
81
|
|
|
60
82
|
For each hook the CLI:
|
|
61
83
|
|
|
62
|
-
- Writes the `.mjs` script to `.claude/hooks
|
|
63
|
-
- Patches `.claude/settings.json` to register the hook on the right lifecycle event
|
|
84
|
+
- Writes the `.mjs` script to the scripts directory for the chosen agent (`.claude/hooks/`, `~/.claude/hooks/`, `.codex/hooks/`, or `~/.codex/hooks/`)
|
|
85
|
+
- Patches the agent's config file (`.claude/settings.json` or `.codex/hooks.json`) to register the hook on the right lifecycle event
|
|
64
86
|
- Optionally writes vitest unit tests to `tests/hooks/` when `--with-tests` is passed (or confirmed interactively)
|
|
65
87
|
|
|
66
|
-
No new dependencies are added to your project. Hooks are plain Node.js scripts — no SDK, no agent modification.
|
|
88
|
+
The same hook `.mjs` is used regardless of agent — Claude Code and Codex share lifecycle event names, so only the config file format changes. No new dependencies are added to your project. Hooks are plain Node.js scripts — no SDK, no agent modification.
|
|
67
89
|
|
|
68
90
|
---
|
|
69
91
|
|
|
@@ -92,7 +114,7 @@ Browse and filter the full catalogue at **[hookstack.app](https://www.hookstack.
|
|
|
92
114
|
## Requirements
|
|
93
115
|
|
|
94
116
|
- Node.js ≥ 18
|
|
95
|
-
- Claude Code
|
|
117
|
+
- One of the supported agents installed — Claude Code, OpenAI Codex, or GitHub Copilot (hooks are wired into the agent's lifecycle)
|
|
96
118
|
|
|
97
119
|
---
|
|
98
120
|
|
package/bin/cli
CHANGED
|
@@ -12,9 +12,11 @@ import {
|
|
|
12
12
|
mergeHooks,
|
|
13
13
|
assertSafeTarget,
|
|
14
14
|
collectIncomingHooks,
|
|
15
|
+
resolveScriptPath,
|
|
15
16
|
buildSecurityRows,
|
|
16
17
|
buildPostInstallHints,
|
|
17
18
|
doInstallTests,
|
|
19
|
+
isGlobalScope,
|
|
18
20
|
} from './core.mjs'
|
|
19
21
|
|
|
20
22
|
const API_BASE = process.env.HOOKSTACK_API_BASE || 'https://hookstack.vercel.app'
|
|
@@ -111,24 +113,31 @@ function doInstall(hooks, dirs, scope, log) {
|
|
|
111
113
|
mkdirSync(dirs.claudeDir, { recursive: true })
|
|
112
114
|
mkdirSync(dirs.hooksDir, { recursive: true })
|
|
113
115
|
|
|
116
|
+
const configName = dirs.format === 'codex' ? 'hooks.json' : 'settings.json'
|
|
114
117
|
let settings = {}
|
|
115
118
|
if (existsSync(dirs.settingsPath)) {
|
|
116
119
|
try {
|
|
117
120
|
settings = JSON.parse(readFileSync(dirs.settingsPath, 'utf8'))
|
|
118
121
|
} catch {
|
|
119
|
-
log.warn(
|
|
122
|
+
log.warn(`Could not parse existing ${configName} — starting fresh`)
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
const incoming = collectIncomingHooks(hooks, { scope, globalRoot: dirs.root })
|
|
124
|
-
|
|
127
|
+
if (dirs.format === 'codex') {
|
|
128
|
+
// Codex hooks.json holds events at the top level (no "hooks" wrapper).
|
|
129
|
+
settings = mergeHooks(settings, incoming)
|
|
130
|
+
} else {
|
|
131
|
+
settings.hooks = mergeHooks(settings.hooks ?? {}, incoming)
|
|
132
|
+
}
|
|
125
133
|
writeFileSync(dirs.settingsPath, JSON.stringify(settings, null, 2) + '\n')
|
|
126
134
|
|
|
127
135
|
let scriptCount = 0
|
|
128
136
|
for (const hook of hooks) {
|
|
129
137
|
if (!hook.script_path || !hook.code_snippet) continue
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
const target = resolveScriptPath(hook.script_path, scope)
|
|
139
|
+
assertSafeTarget(dirs.root, target)
|
|
140
|
+
const dest = join(dirs.root, target)
|
|
132
141
|
mkdirSync(join(dest, '..'), { recursive: true })
|
|
133
142
|
writeFileSync(dest, hook.code_snippet, 'utf8')
|
|
134
143
|
scriptCount++
|
|
@@ -141,6 +150,15 @@ function doInstall(hooks, dirs, scope, log) {
|
|
|
141
150
|
|
|
142
151
|
const plural = (n, word) => `${n} ${word}${n === 1 ? '' : 's'}`
|
|
143
152
|
|
|
153
|
+
// Human-readable destination shown in the confirm prompt and result panel.
|
|
154
|
+
const SCOPE_LABELS = {
|
|
155
|
+
project: './.claude',
|
|
156
|
+
global: '~/.claude',
|
|
157
|
+
copilot: './.claude (Copilot mode)',
|
|
158
|
+
'codex-project': './.codex/hooks.json (Codex)',
|
|
159
|
+
'codex-profile': '~/.codex/hooks.json (Codex)',
|
|
160
|
+
}
|
|
161
|
+
|
|
144
162
|
function onCancel() {
|
|
145
163
|
p.cancel('Installation cancelled.')
|
|
146
164
|
process.exit(0)
|
|
@@ -148,7 +166,7 @@ function onCancel() {
|
|
|
148
166
|
|
|
149
167
|
async function interactiveInstall(slugs, args) {
|
|
150
168
|
console.log('\n' + BANNER + '\n')
|
|
151
|
-
p.intro(pc.bold('hooks') + pc.dim(` Claude Code
|
|
169
|
+
p.intro(pc.bold('hooks') + pc.dim(` Agentic hook installer · Claude Code · Codex · Copilot v${VERSION}`))
|
|
152
170
|
|
|
153
171
|
// Fetch
|
|
154
172
|
const spin = p.spinner()
|
|
@@ -181,6 +199,16 @@ async function interactiveInstall(slugs, args) {
|
|
|
181
199
|
label: 'All my projects',
|
|
182
200
|
hint: '~/.claude — every project on this machine',
|
|
183
201
|
},
|
|
202
|
+
{
|
|
203
|
+
value: 'codex-profile',
|
|
204
|
+
label: 'Codex profile',
|
|
205
|
+
hint: '~/.codex/hooks.json — every project (OpenAI Codex)',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
value: 'codex-project',
|
|
209
|
+
label: 'Codex project',
|
|
210
|
+
hint: './.codex/hooks.json — committed with your repo',
|
|
211
|
+
},
|
|
184
212
|
{
|
|
185
213
|
value: 'copilot',
|
|
186
214
|
label: 'GitHub Copilot project',
|
|
@@ -199,7 +227,7 @@ async function interactiveInstall(slugs, args) {
|
|
|
199
227
|
p.note(securityPanel(secRows), 'Installation Summary')
|
|
200
228
|
|
|
201
229
|
// Confirm
|
|
202
|
-
const scopeLabel = scope
|
|
230
|
+
const scopeLabel = SCOPE_LABELS[scope] ?? './.claude'
|
|
203
231
|
const confirmed = await p.confirm({
|
|
204
232
|
message: `Install ${plural(hooks.length, 'hook')} into ${pc.cyan(scopeLabel)}?`,
|
|
205
233
|
})
|
|
@@ -220,7 +248,7 @@ async function interactiveInstall(slugs, args) {
|
|
|
220
248
|
// Unit tests prompt (project/copilot scope only)
|
|
221
249
|
let testResult = null
|
|
222
250
|
const hooksWithTests = hooks.filter(h => h.test_snippet)
|
|
223
|
-
if (scope
|
|
251
|
+
if (!isGlobalScope(scope) && hooksWithTests.length > 0) {
|
|
224
252
|
const wantTests = await p.confirm({
|
|
225
253
|
message: `Install unit tests for ${plural(hooksWithTests.length, 'hook')} into ${pc.cyan('tests/hooks/')}? ${pc.dim('(vitest — helps SonarQube gating)')}`,
|
|
226
254
|
initialValue: true,
|
|
@@ -276,7 +304,7 @@ async function directInstall(slugs, args) {
|
|
|
276
304
|
const log = { warn: m => console.warn(` ! ${m}`) }
|
|
277
305
|
const result = doInstall(hooks, dirs, args.scope, log)
|
|
278
306
|
console.log(` ✓ ${dirs.settingsPath}`)
|
|
279
|
-
if (args.withTests && args.scope
|
|
307
|
+
if (args.withTests && !isGlobalScope(args.scope)) {
|
|
280
308
|
try {
|
|
281
309
|
const testResult = doInstallTests(hooks, process.cwd(), { mkdirSync, writeFileSync, join: pathJoin })
|
|
282
310
|
if (testResult.testCount > 0)
|
|
@@ -289,8 +317,9 @@ async function directInstall(slugs, args) {
|
|
|
289
317
|
if (hints.length > 0) {
|
|
290
318
|
for (const h of hints) console.warn(` ⚠ ${h.slug}: ${h.hint}`)
|
|
291
319
|
}
|
|
320
|
+
const agent = dirs.format === 'codex' ? 'Codex' : 'Claude Code'
|
|
292
321
|
console.log(`\n✅ ${plural(result.hookCount, 'hook')} installed · star us → ${REPO_URL}`)
|
|
293
|
-
console.log(
|
|
322
|
+
console.log(` Restart ${agent} to activate.\n`)
|
|
294
323
|
}
|
|
295
324
|
|
|
296
325
|
const HELP = `
|
|
@@ -304,7 +333,10 @@ const HELP = `
|
|
|
304
333
|
--hooks <slugs> Comma-separated list of hook slugs (omit for default set)
|
|
305
334
|
--global, -g Install into ~/.claude instead of ./.claude
|
|
306
335
|
--copilot Install into ./.claude with paths adapted for GitHub Copilot
|
|
307
|
-
--
|
|
336
|
+
--codex-project Install into ./.codex/hooks.json (OpenAI Codex, this repo)
|
|
337
|
+
--codex-profile Install into ~/.codex/hooks.json (OpenAI Codex, all projects)
|
|
338
|
+
--scope <s> "project" (default), "global", "copilot",
|
|
339
|
+
"codex-project", or "codex-profile"
|
|
308
340
|
--with-tests Also install vitest unit tests into tests/hooks/ (project scope only)
|
|
309
341
|
--yes, -y Skip prompts (non-interactive install)
|
|
310
342
|
--version, -v Show version
|
package/bin/core.mjs
CHANGED
|
@@ -13,6 +13,19 @@ const BLOCKING_EVENTS = new Set([
|
|
|
13
13
|
|
|
14
14
|
// Matches $CLAUDE_PROJECT_DIR and ${CLAUDE_PROJECT_DIR}.
|
|
15
15
|
export const PROJECT_DIR_RE = /\$\{?CLAUDE_PROJECT_DIR\}?/g
|
|
16
|
+
// Matches the "$CLAUDE_PROJECT_DIR/.claude/" prefix — rewritten to ".codex/" for
|
|
17
|
+
// Codex installs so scripts resolve under the Codex agent directory instead.
|
|
18
|
+
const CLAUDE_PREFIX_RE = /\$\{?CLAUDE_PROJECT_DIR\}?\/\.claude\//g
|
|
19
|
+
|
|
20
|
+
// All recognized install scopes. Claude-family: project, global, copilot
|
|
21
|
+
// (settings.json under .claude). Codex-family: codex-project, codex-profile
|
|
22
|
+
// (hooks.json under .codex, events at the top level).
|
|
23
|
+
export const SCOPES = new Set(['project', 'global', 'copilot', 'codex-project', 'codex-profile'])
|
|
24
|
+
const GLOBAL_SCOPES = new Set(['global', 'codex-profile'])
|
|
25
|
+
const CODEX_SCOPES = new Set(['codex-project', 'codex-profile'])
|
|
26
|
+
|
|
27
|
+
export const isGlobalScope = scope => GLOBAL_SCOPES.has(scope)
|
|
28
|
+
export const isCodexScope = scope => CODEX_SCOPES.has(scope)
|
|
16
29
|
|
|
17
30
|
function splitList(raw) {
|
|
18
31
|
return raw.split(',').map(s => s.trim()).filter(Boolean)
|
|
@@ -39,9 +52,11 @@ export function parseArgs(argv) {
|
|
|
39
52
|
if (arg === '--global' || arg === '-g') { result.scope = 'global'; continue }
|
|
40
53
|
if (arg === '--project') { result.scope = 'project'; continue }
|
|
41
54
|
if (arg === '--copilot') { result.scope = 'copilot'; continue }
|
|
55
|
+
if (arg === '--codex-profile') { result.scope = 'codex-profile'; continue }
|
|
56
|
+
if (arg === '--codex-project') { result.scope = 'codex-project'; continue }
|
|
42
57
|
if (arg.startsWith('--scope=')) {
|
|
43
58
|
const v = arg.slice('--scope='.length)
|
|
44
|
-
if (v
|
|
59
|
+
if (SCOPES.has(v)) result.scope = v
|
|
45
60
|
continue
|
|
46
61
|
}
|
|
47
62
|
if (arg.startsWith('--hooks=')) { result.hooks = splitList(arg.slice('--hooks='.length)); continue }
|
|
@@ -52,16 +67,22 @@ export function parseArgs(argv) {
|
|
|
52
67
|
return result
|
|
53
68
|
}
|
|
54
69
|
|
|
55
|
-
// Resolves where
|
|
70
|
+
// Resolves where the agent directory lives for a given scope.
|
|
71
|
+
// Project/copilot → cwd; global/codex-profile → home.
|
|
72
|
+
// Claude-family uses .claude/settings.json; Codex-family uses .codex/hooks.json.
|
|
73
|
+
// `claudeDir`/`settingsPath` keys are kept for back-compat (they hold the agent
|
|
74
|
+
// dir and config-file path regardless of which agent it targets).
|
|
56
75
|
export function resolveScopeRoot(scope, { cwd, home }) {
|
|
57
|
-
const root = scope
|
|
58
|
-
const
|
|
76
|
+
const root = isGlobalScope(scope) ? home : cwd
|
|
77
|
+
const codex = isCodexScope(scope)
|
|
78
|
+
const agentDir = join(root, codex ? '.codex' : '.claude')
|
|
59
79
|
return {
|
|
60
80
|
scope,
|
|
61
81
|
root,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
format: codex ? 'codex' : 'claude',
|
|
83
|
+
claudeDir: agentDir,
|
|
84
|
+
hooksDir: join(agentDir, 'hooks'),
|
|
85
|
+
settingsPath: join(agentDir, codex ? 'hooks.json' : 'settings.json'),
|
|
65
86
|
}
|
|
66
87
|
}
|
|
67
88
|
|
|
@@ -99,10 +120,27 @@ export function mergeHooks(existing, incoming) {
|
|
|
99
120
|
return merged
|
|
100
121
|
}
|
|
101
122
|
|
|
123
|
+
// Rewrites a hook command's path for the target scope:
|
|
124
|
+
// - global → $CLAUDE_PROJECT_DIR ↦ absolute global root (.claude stays)
|
|
125
|
+
// - copilot → strips $CLAUDE_PROJECT_DIR/ (relative, Copilot compatible)
|
|
126
|
+
// - codex-project → "$CLAUDE_PROJECT_DIR/.claude/" ↦ ".codex/" (relative)
|
|
127
|
+
// - codex-profile → "$CLAUDE_PROJECT_DIR/.claude/" ↦ "<home>/.codex/" (absolute)
|
|
128
|
+
function rewriteCommand(command, scope, globalRoot) {
|
|
129
|
+
if (scope === 'global' && globalRoot)
|
|
130
|
+
return command.replace(PROJECT_DIR_RE, globalRoot)
|
|
131
|
+
if (scope === 'copilot')
|
|
132
|
+
return command.replace(/\$\{?CLAUDE_PROJECT_DIR\}?\//g, '')
|
|
133
|
+
if (scope === 'codex-project')
|
|
134
|
+
return command.replace(CLAUDE_PREFIX_RE, '.codex/')
|
|
135
|
+
if (scope === 'codex-profile' && globalRoot)
|
|
136
|
+
return command.replace(CLAUDE_PREFIX_RE, `${globalRoot}/.codex/`)
|
|
137
|
+
return command
|
|
138
|
+
}
|
|
139
|
+
|
|
102
140
|
// Gathers the hook fragments from an API hook list into a single event→entries
|
|
103
|
-
// map
|
|
104
|
-
//
|
|
105
|
-
//
|
|
141
|
+
// map, rewriting command paths per scope (see rewriteCommand). The resulting map
|
|
142
|
+
// is identical in shape for both Claude (settings.hooks) and Codex (top-level
|
|
143
|
+
// hooks.json) — only doInstall decides how to nest it on disk.
|
|
106
144
|
export function collectIncomingHooks(hooks, { scope = 'project', globalRoot } = {}) {
|
|
107
145
|
const incoming = {}
|
|
108
146
|
for (const hook of hooks) {
|
|
@@ -115,11 +153,7 @@ export function collectIncomingHooks(hooks, { scope = 'project', globalRoot } =
|
|
|
115
153
|
...entry,
|
|
116
154
|
hooks: entry.hooks.map(h => {
|
|
117
155
|
if (!h.command || typeof h.command !== 'string') return h
|
|
118
|
-
|
|
119
|
-
return { ...h, command: h.command.replace(PROJECT_DIR_RE, globalRoot) }
|
|
120
|
-
if (scope === 'copilot')
|
|
121
|
-
return { ...h, command: h.command.replace(/\$\{?CLAUDE_PROJECT_DIR\}?\//g, '') }
|
|
122
|
-
return h
|
|
156
|
+
return { ...h, command: rewriteCommand(h.command, scope, globalRoot) }
|
|
123
157
|
}),
|
|
124
158
|
})
|
|
125
159
|
}
|
|
@@ -128,6 +162,13 @@ export function collectIncomingHooks(hooks, { scope = 'project', globalRoot } =
|
|
|
128
162
|
return incoming
|
|
129
163
|
}
|
|
130
164
|
|
|
165
|
+
// Maps a hook's script_path to its on-disk destination for the target scope.
|
|
166
|
+
// Codex installs relocate scripts from .claude/hooks/ to .codex/hooks/.
|
|
167
|
+
export function resolveScriptPath(scriptPath, scope) {
|
|
168
|
+
if (isCodexScope(scope)) return scriptPath.replace(/^\.claude\//, '.codex/')
|
|
169
|
+
return scriptPath
|
|
170
|
+
}
|
|
171
|
+
|
|
131
172
|
export function isBlockingEvent(event) {
|
|
132
173
|
return BLOCKING_EVENTS.has(event)
|
|
133
174
|
}
|