# Command Boundary Hook Matching

> Pattern-match what a Bash command actually does in a PreToolUse hook without false-firing on mentions inside quoted args, commit messages, or echo strings. Use when building any hook that decides based on command semantics.

- **Type:** Skill
- **Install:** `agentstack add skill-carloscape-octorato-command-boundary-hook-matching`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [CarlosCaPe](https://agentstack.voostack.com/s/carloscape)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [CarlosCaPe](https://github.com/CarlosCaPe)
- **Source:** https://github.com/CarlosCaPe/octorato/tree/master/skills/command-boundary-hook-matching
- **Website:** https://www.dataqbs.com/octorato

## Install

```sh
agentstack add skill-carloscape-octorato-command-boundary-hook-matching
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Command-Boundary Hook Matching

## Problem

A naive `"gh pr merge" in command` check fires on:

```bash
git commit -m "do not run gh pr merge 96 yet"   # mention in commit message
echo "about to run gh pr merge"                  # echo
for pr in 95 96; do gh pr merge $pr; done        # loop — fires twice, wrong context
```

None of these are the real invocation you want to intercept. The hook either over-fires (false positives trigger unnecessary blocks) or under-fires (quoted indirection evades detection).

## Fix — Quote-Aware Sub-Command Splitter

Split the raw command string on **unquoted** shell separators only, then anchor the pattern at the **start** of each sub-command.

### Step 1 — Join continuations

```python
command = command.replace("\\\n", " ")
```

### Step 2 — Quote-aware split on unquoted separators

Separators: `;` `&&` `||` `|` `\n` and grouping `(` `)` `{` `}`.

```python
import re

def _split_subcmds(cmd: str) -> list[str]:
    parts, buf, depth, in_sq, in_dq = [], [], 0, False, False
    i = 0
    while i [^\s]+\s+'                    # >file
    r'|2>[^\s]+\s+'                   # 2>file
    r')*'
)

def _strip_prefix(sub: str) -> str:
    return _STRIP.sub('', sub)
```

### Step 4 — Anchored pattern match

```python
_PAT_MERGE = re.compile(r'^\s*gh\s+pr\s+merge\b')

def is_merge_command(command: str) -> tuple[bool, str | None]:
    for sub in _split_subcmds(command):
        clean = _strip_prefix(sub)
        if _PAT_MERGE.match(clean):
            # extract PR number
            m = re.search(r'\bgh\s+pr\s+merge\s+(\d+)', clean)
            return True, m.group(1) if m else None
    return False, None
```

`^\s*gh\s+pr\s+merge\b` anchored at the sub-command start ensures it cannot match mid-string inside a quoted arg.

## Fail-Open vs Fail-Closed

| Hook type | On parse error / ambiguity |
|---|---|
| **Context-injection** (informational) | FAIL-OPEN — skip, never block |
| **Gate/block** (authorization) | FAIL-CLOSED — treat ambiguous = not authorized, block |

If shell indirection (`bash -c "..."`, `eval`) makes the real command opaque, a fail-closed gate correctly blocks until a human grants the env-var approval (see [[agent-proof-approval-gate]]).

## Residual Risk

Shell indirection still evades string-based detection:

```bash
bash -c "gh pr merge 96"     # sub-command content is inside a string literal
$(echo gh pr merge 96)       # command substitution
```

This is accepted. The string-matching layer identifies the action; the env-var layer authorizes it. The env channel is immune to indirection (see [[agent-proof-approval-gate]]).

## When to Use

- Any PreToolUse hook that decides based on what a Bash command does.
- Gates on `git push --force`, `wrangler deploy`, `psql ... DROP`, etc.
- Logging/telemetry hooks that want to capture only real invocations.

## Reference Implementation

`~/.claude/scripts/qa-merge-gate.py` — `_split_subcmds` + `_strip_prefix` + anchored `_PAT_MERGE` / `_PAT_DELETE`.

## See also

- [[agent-proof-approval-gate]] — the authorization layer that complements this parsing layer
- [[dry-run-gate-pattern]] — preview-before-execute for destructive ops
- [[hook-profile-gating]] — profile-based hook activation

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [CarlosCaPe](https://github.com/CarlosCaPe)
- **Source:** [CarlosCaPe/octorato](https://github.com/CarlosCaPe/octorato)
- **License:** MIT
- **Homepage:** https://www.dataqbs.com/octorato

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** yes

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-carloscape-octorato-command-boundary-hook-matching
- Seller: https://agentstack.voostack.com/s/carloscape
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
