Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"name": "codex-collab",
"source": "./",
"description": "Claude Code と OpenAI Codex CLI の協調タスク実行プラグイン",
"version": "0.32.2",
"version": "0.33.0",
"author": {
"name": "masuP9"
}
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codex-collab",
"version": "0.32.2",
"version": "0.33.0",
"description": "Claude Code と OpenAI Codex CLI の協調タスク実行プラグイン",
"author": {
"name": "masuP9"
Expand Down
46 changes: 29 additions & 17 deletions docs/bash-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The PreToolUse hook (`hooks/enforce-skill-usage.sh`) detects skill context using

### Primary Method (Reliable)

Check if the Bash command includes `export CODEX_SKILL_CONTEXT=1`.
Check if the Bash command contains a line starting with `export CODEX_SKILL_CONTEXT=1` (substring mentions are ignored — only a line-start export is honored).

All codex-collab skills set this environment variable at the beginning of their Bash blocks:

Expand All @@ -51,7 +51,7 @@ fi
[ -f "$HELPERS" ] && source "$HELPERS"
```

When this marker is present, all Bash commands are allowed without blocking.
When this marker is present on a line by itself (with optional leading whitespace), all Bash commands are allowed without blocking. Quoting the string inside an `echo` or assigning it to a variable does **not** trigger the bypass.

### Note on jq Dependency

Expand All @@ -61,32 +61,44 @@ The hook script requires `jq` to parse the JSON input from Claude Code. If `jq`

When **not** in skill context, the hook checks for these patterns to identify codex-collab operations:

| Pattern | Description | Example | Limitation |
|---------|-------------|---------|------------|
| `(^\|[;&\|]\|\$\(\|`)[[:space:]]*codex_[A-Za-z0-9_]+` | Helper function calls **at execution position** (line start, after `;` `&` `\|`, inside `$(...)` or backticks) | `codex_run_exec` | Mentions in argument text (commit messages, PR bodies, grep patterns) are allowed |
| `\bsource\b.*codex-helpers\.sh` or `\.\s+.*codex-helpers\.sh` | Direct path sourcing (source/dot) | `source ./scripts/codex-helpers.sh` | Indirect refs not detected |
| `\$HELPERS.*codex-helpers` or `HELPERS=.*codex-helpers` | Variable reference/definition | `HELPERS="./codex-helpers.sh"` | - |
| `\bCODEX_PROMPT\b` | codex-collab variables | `$CODEX_PROMPT` | - |
| Pattern | Description | Limitation |
|---------|-------------|------------|
| `(^\|[;&\|]\|\$\(\|`)[[:space:]]*codex_(run_exec\|run_review\|save_session_state\|save_thread)\b` | Side-effect helper calls at execution position (external execution / review / session-state writes) | Pure transforms and mentions in argument text are allowed |

### Known Limitations

- **Indirect sourcing**: `source "$HELPERS"` where `HELPERS` is set elsewhere may not be detected
- **Heredoc false positives**: If a heredoc body has a helper function name at line start, it may trigger a false positive (the anchor matches line-start text regardless of context)
- **`HELPERS=` pattern**: The `HELPERS=.*codex-helpers` pattern matches anywhere in the command string, including inside message text. No real incidents observed; current behavior is maintained
- **Indirect execution**: Patterns like `env codex_x` or `bash -c 'codex_x ...'` are not detected — this is an accepted tradeoff in the fail-open design
- **Indirect execution**: Patterns like `bash -c 'codex_run_exec ...'` or `env codex_run_exec ...` are not detected — this is an accepted tradeoff in the fail-open design
- **Heredoc false positives**: If a heredoc body has a guarded helper name at line start, it may trigger a false positive (the anchor matches line-start text regardless of heredoc context)
- **Pure transforms intentionally unguarded**: `codex_strip_ansi`, `codex_infer_verdict`, `codex_extract_review_findings`, and other read-only/transform helpers are intentionally **not** guarded — adding them would increase false positives with no safety benefit
- **Source/HELPERS=/CODEX_PROMPT intentionally unguarded**: These speculative patterns have been removed as they were outside the scope of the soft guard's purpose (see "Sunset criteria" below)

### Sunset criteria

This hook is a soft guard, not a security boundary. Consider removing it entirely if any of the following is observed:

1. Repeated false positives that cannot be fixed without broadening exceptions
2. Multiple recorded blocks that all end with mechanical marker-prepending rather than skill adoption
3. Maintenance work continues with no recorded examples of the hook successfully guiding a user to a skill

When the hook blocks a command, record the real incident in a PR or issue (manual log — no permanent instrumentation). If the criteria above accumulate, open a plan to remove the hook entirely.

## What Happens When Blocked

If you try to execute codex-collab operations outside a skill context, you'll see a message like:
If you try to execute a codex-collab side-effect helper outside a skill context, you'll see a message like:

```
This Bash command uses codex-collab helper functions directly.
This command calls a codex-collab side-effect helper directly
(codex_run_exec / codex_run_review / codex_save_session_state / codex_save_thread).

Use the appropriate skill instead:
This is a soft guard against accidental direct use of internal APIs,
not a security boundary. Preferred entry points:
- /codex-collab [task] - Start collaboration
- /strong-inference [problem] - Investigate problems
- /devils-advocate [proposal] - Stress-test designs

If you are doing this intentionally, start the line with:
export CODEX_SKILL_CONTEXT=1; <your command>

See docs/bash-usage.md for details.
```

Expand All @@ -98,10 +110,10 @@ You're trying to execute codex-collab Bash commands directly instead of through

### "I need to run a custom command"

If you need to run custom commands that use codex-collab helpers:
If you need to run custom commands that use codex-collab side-effect helpers:

1. **Recommended**: Create a new skill or modify an existing one
2. **Workaround**: Ensure your command includes `export CODEX_SKILL_CONTEXT=1` at the beginning
2. **Workaround**: Start the command (or its first line) with `export CODEX_SKILL_CONTEXT=1` — placing it in a quoted string or after an `echo` is not sufficient; it must be an actual line-start export

### "The hook is blocking legitimate commands"

Expand Down
60 changes: 32 additions & 28 deletions hooks/enforce-skill-usage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: enforce-skill-usage
description: Enforce using skills instead of direct Bash execution for codex-collab operations
description: Soft guard against accidental direct use of codex-collab side-effect helpers
---

# Codex-Collab Skill Usage Enforcement
Expand All @@ -9,68 +9,72 @@ description: Enforce using skills instead of direct Bash execution for codex-col
> The shell script works in both foreground and background agents (no LLM required).
> See `plugin.json` for the hook registration.

This document describes the detection logic used by `enforce-skill-usage.sh` to block direct Bash execution of codex-collab operations.
This document describes the detection logic used by `enforce-skill-usage.sh` to soft-guard direct Bash execution of codex-collab side-effect helpers.

**Purpose**: This is a soft guard against accidental direct use of internal APIs, not a security boundary. Detection misses (false negatives) are intentionally accepted in exchange for reduced false-positive rate and lower maintenance cost.

## Skill Context Detection

**IMPORTANT**: First check if we are currently executing within a skill context.

The **primary and most reliable** method is checking for the `CODEX_SKILL_CONTEXT` environment variable in the command text.

**How to detect skill context (in order of reliability):**
**How to detect skill context:**

1. **Environment variable (RELIABLE)**: The command includes `export CODEX_SKILL_CONTEXT=1`
- All `/codex-collab`, `/strong-inference`, and `/devils-advocate` commands set this at the beginning
1. **Line-start export (RELIABLE)**: The command contains a line that starts with `export CODEX_SKILL_CONTEXT=1` (optional leading whitespace allowed)
- All `/codex-collab`, `/strong-inference`, and `/devils-advocate` commands set this at the beginning of each Bash block
- This is the definitive signal that the command is running in skill context

2. **Heuristic (less reliable)**: You are currently processing a `/codex-collab`, `/strong-inference`, or `/devils-advocate` command
- Note: This may not be reliably accessible to the hook depending on platform implementation
- **Substring mentions are NOT honored**: `echo "CODEX_SKILL_CONTEXT=1"` or a value embedded inside a quoted string does not bypass the guard

**If skill context is detected**: Allow all Bash commands to proceed without blocking.

## Detection Patterns (only when NOT in skill context)

Check if the command contains any of these patterns using word-boundary matching:
Check if the command matches the following pattern at an execution position (line start, after `;` `&` `|`, inside `$(...)` or backticks):

1. **Helper function calls**: `\bcodex_[A-Za-z0-9_]+\b`
- Matches: `codex_run_exec`, `codex_build_exec_command`, `codex_write_prompt`, etc.
- Does NOT match: variable names like `my_codex_var` or strings in comments
**Side-effect helper calls**: `(^|[;&|]|\$\(|`)[[:space:]]*codex_(run_exec|run_review|save_session_state|save_thread)\b`

2. **Sourcing helpers (direct filename)**: `\bsource\b.*codex-helpers\.sh` or `\.\s+.*codex-helpers\.sh`
- Matches: `source /path/to/codex-helpers.sh`, `. ./scripts/codex-helpers.sh`
- Note: Does NOT match indirect references like `source "$HELPERS"` where HELPERS is a variable
- This is a limitation; indirect sourcing may slip through
Protected functions (external execution and session-state writes):
- `codex_run_exec` — runs external codex CLI
- `codex_run_review` — runs codex review subprocess
- `codex_save_session_state` — writes session state to filesystem
- `codex_save_thread` — writes named thread to filesystem

3. **Helper variable reference**: `\$HELPERS.*codex-helpers` or `HELPERS=.*codex-helpers`
- Catches variable definitions and usages pointing to codex-helpers.sh
**Intentionally NOT guarded** (not side-effect helpers):
- Pure transforms: `codex_strip_ansi`, `codex_infer_verdict`, `codex_extract_review_findings`, `codex_get_field`, etc.
- Read-only helpers: `codex_load_session_state`, `codex_load_thread`, `codex_tmp_path`, etc.
- Speculative patterns: `source codex-helpers.sh`, `HELPERS=...`, `CODEX_PROMPT` — removed to reduce false positives

4. **Codex-collab variables**: `\bCODEX_PROMPT\b`
- Only when used as variable names
## If Pattern Detected (and NOT in skill context)

## If Patterns Detected (and NOT in skill context)

1. **BLOCK** this Bash execution
2. **Inform the user** that codex-collab operations should use skills
1. **BLOCK** this Bash execution (exit 2)
2. **Inform the user** that this is a soft guard for side-effect helpers
3. **Suggest** the appropriate skill:
- For starting collaboration: `/codex-collab [task]`
- For investigating problems: `/strong-inference [problem]`
- For stress-testing designs: `/devils-advocate [proposal]`
4. **Offer bypass** via `export CODEX_SKILL_CONTEXT=1; <command>` for intentional direct use

## If No Patterns Detected OR In Skill Context
## If No Pattern Detected OR In Skill Context

Allow the Bash command to proceed normally without any output.

## Response Format

**If blocking** (exit 2, patterns detected AND not in skill context):
**If blocking** (exit 2, pattern detected AND not in skill context):
```
This Bash command uses codex-collab helper functions directly.
This command calls a codex-collab side-effect helper directly
(codex_run_exec / codex_run_review / codex_save_session_state / codex_save_thread).

Use the appropriate skill instead:
This is a soft guard against accidental direct use of internal APIs,
not a security boundary. Preferred entry points:
- /codex-collab [task] - Start collaboration
- /strong-inference [problem] - Investigate problems
- /devils-advocate [proposal] - Stress-test designs

If you are doing this intentionally, start the line with:
export CODEX_SKILL_CONTEXT=1; <your command>

See docs/bash-usage.md for details.
```

Expand Down
27 changes: 14 additions & 13 deletions hooks/enforce-skill-usage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,28 @@ command -v jq &>/dev/null || exit 0
COMMAND=$(jq -r '.tool_input.command // empty' 2>/dev/null) || exit 0
[ -z "$COMMAND" ] && exit 0

# Skill context marker — allow everything
echo "$COMMAND" | grep -qF 'CODEX_SKILL_CONTEXT=1' && exit 0

# Check for codex-collab patterns
PATTERN='(^|[;&|]|\$\(|`)[[:space:]]*codex_[A-Za-z0-9_]+'
PATTERN="$PATTERN"'|\bsource\b.*codex-helpers\.sh'
PATTERN="$PATTERN"'|\.[ \t]+.*codex-helpers\.sh'
# shellcheck disable=SC2016 # $HELPERS is a literal grep pattern (single-quoted intentionally, not a variable)
PATTERN="$PATTERN"'|\$HELPERS.*codex-helpers'
PATTERN="$PATTERN"'|HELPERS=.*codex-helpers'
PATTERN="$PATTERN"'|\bCODEX_PROMPT\b'
# Skill context marker — soft guard opt-in, only honored at line start
echo "$COMMAND" | grep -qE '^[[:space:]]*export[[:space:]]+CODEX_SKILL_CONTEXT=1' && exit 0

# Soft guard: only side-effect helpers (external execution / review / session-state writes).
# Pure transforms (codex_strip_ansi etc.) and speculative sourcing/variable patterns
# are intentionally NOT guarded — see docs/bash-usage.md "Sunset criteria".
PATTERN='(^|[;&|]|\$\(|`)[[:space:]]*codex_(run_exec|run_review|save_session_state|save_thread)\b'

if echo "$COMMAND" | grep -qE "$PATTERN"; then
cat >&2 << 'MSG'
This Bash command uses codex-collab helper functions directly.
This command calls a codex-collab side-effect helper directly
(codex_run_exec / codex_run_review / codex_save_session_state / codex_save_thread).

Use the appropriate skill instead:
This is a soft guard against accidental direct use of internal APIs,
not a security boundary. Preferred entry points:
- /codex-collab [task] - Start collaboration
- /strong-inference [problem] - Investigate problems
- /devils-advocate [proposal] - Stress-test designs

If you are doing this intentionally, start the line with:
export CODEX_SKILL_CONTEXT=1; <your command>

See docs/bash-usage.md for details.
MSG
exit 2
Expand Down
Loading
Loading