AgentStack
SKILL verified MIT Self-run

Command Boundary Hook Matching

skill-carloscape-octorato-command-boundary-hook-matching · by CarlosCaPe

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.

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-carloscape-octorato-command-boundary-hook-matching

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution Used

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Command Boundary Hook Matching? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Command-Boundary Hook Matching

Problem

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

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

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

Step 2 — Quote-aware split on unquoted separators

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

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

_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 -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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.