Skip to content

Instantly share code, notes, and snippets.

@oyilmaztekin
Last active May 25, 2026 15:51
Show Gist options
  • Select an option

  • Save oyilmaztekin/e4bfb7d4a6065c0168b6703b90dee215 to your computer and use it in GitHub Desktop.

Select an option

Save oyilmaztekin/e4bfb7d4a6065c0168b6703b90dee215 to your computer and use it in GitHub Desktop.
A CLAUDE.md snippet that makes Claude Code mandatory-use graphify (query/path/explain/wiki) for all cross-module exploration before falling back to grep, find, or ripgrep. Includes the "Before Every Task" orientation checklist and the "graphify-first, grep-last" enforcement block with explicit fallback rules and subagent delegation guidance.
#!/usr/bin/env python3
import json
import re
import shlex
import subprocess
import sys
"""
place this hook script in .claude/hooks/block_search_tools.py in your project.
this script will force claude code subagents to use graphify instead of token consuming commands like grep|find|rg|ripgrep|ag|fd
"""
data = json.load(sys.stdin)
cmd = data.get('tool_input', {}).get('command', '')
# Detect search tools used as standalone pipeline commands.
pattern = re.compile(
r'(?:(?:^|\n|[|;]|&&|\|\|)\s*(?:sudo\s+)?)(grep|find|rg|ripgrep|ag|fd)(?:\s|$)',
re.MULTILINE,
)
if not pattern.search(cmd):
sys.exit(0)
def extract_term(command):
"""Return (term, tool) from a blocked search command."""
try:
parts = shlex.split(command)
except ValueError:
parts = command.split()
tool_idx = None
for i, p in enumerate(parts):
if p.split('/')[-1] in ('grep', 'rg', 'ripgrep', 'ag', 'fd', 'find'):
tool_idx = i
break
if tool_idx is None:
return None, None
tool = parts[tool_idx].split('/')[-1]
args = parts[tool_idx + 1:]
if tool == 'find':
for i, arg in enumerate(args):
if arg in ('-name', '-iname') and i + 1 < len(args):
name = args[i + 1].replace('*', '').replace('?', '').lstrip('.')
if name:
return name, tool
return None, tool
else:
for arg in args:
if not arg.startswith('-') and arg.strip():
return arg.strip('\'"'), tool
return None, tool
term, tool = extract_term(cmd)
# Auto-run graphify query and capture output
graphify_output = None
if term:
try:
result = subprocess.run(
['graphify', 'query', term],
capture_output=True, text=True, timeout=15,
)
out = result.stdout.strip()
if out and 'No matching nodes found' not in out:
graphify_output = out
except Exception:
pass
if graphify_output:
# Graphify found results — block grep and inject them
print(json.dumps({
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
'permissionDecision': 'deny',
'permissionDecisionReason': f'BLOCKED — auto-ran: graphify query "{term}" (results injected into context)',
'additionalContext': f'graphify query "{term}" results:\n{graphify_output}',
},
}))
sys.exit(0)
# Graphify returned nothing — allow grep to proceed as legitimate fallback
if term:
note = f'graphify query "{term}" returned no results — falling back to grep.'
else:
note = 'graphify has no indexed results for this — falling back to grep.'
print(json.dumps({
'hookSpecificOutput': {
'hookEventName': 'PreToolUse',
'additionalContext': note,
},
}))
sys.exit(0)

Before Every Task

  1. Orient with graphify (graphify-out/GRAPH_REPORT.md, then graphify query / graphify path / graphify explain). For browsing by topic, start at graphify-out/wiki/index.md — each community has its own article linking related nodes.
  2. ....

Exploration: graphify-first, grep-last (MANDATORY)

For any "where is X / what uses Y / how does Z connect" question, you must use graphify before any grep, find, ripgrep, or Glob/Grep tool call. Graphify is ~71x cheaper in tokens and surfaces relationships (node → prompt → output model → state field) that grep cannot.

Required workflow:

  1. Start at graphify-out/wiki/index.md to find the right community, or run graphify query "<concept>".
  2. Follow relations with graphify path <a> <b> and graphify explain <node>.
  3. Think in communities and relations, not file paths.

Grep/find is allowed only as a fallback for:

  • Exact literal hunts (error strings, magic constants, specific config keys).
  • Files graphify hasn't indexed (state it explicitly when this happens).
  • Verifying a specific line/symbol after graphify has pointed you to the file.

Subagents must follow the same rule — when delegating exploration (Explore, general-purpose, etc.), instruct them to use graphify first and treat grep as fallback. Do not let the default Explore agent reflexively grep this repo.

If graphify output looks stale, surface that to the user instead of silently switching to grep.

// ...projectPath/.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .../projectPath/.claude/hooks/block_search_tools.py"
}
]
}
],
"SubagentStart": [
{
"hooks": [
{
"type": "command",
"command": "python3 .../projectPath/.claude/hooks/subagent_graphify_context.py"
}
]
}
]
}
}
#!/usr/bin/env python3
import json
import sys
"""
place this hook script in .claude/hooks/block_search_tools.py in your project.
this script will force claude code subagents to use graphify instead of token consuming commands like grep|find|rg|ripgrep|ag|fd
"""
print(json.dumps({
'hookSpecificOutput': {
'hookEventName': 'SubagentStart',
'additionalContext': (
'MANDATORY graphify-first rule (enforced by hook):\n'
'1. Use graphify query "<concept>", graphify path <a> <b>, graphify explain <node> for ALL exploration.\n'
'2. Start at graphify-out/wiki/index.md to find the right community.\n'
'3. Prefer graphify over grep/find/rg/ag/ripgrep/fd. A PreToolUse hook intercepts these: '
'if graphify has results it blocks the grep and injects them; '
'if graphify returns nothing it allows grep to proceed as a legitimate fallback.\n'
'4. Only use Read on a specific file AFTER graphify has pointed you to it.\n'
'5. If graphify truly cannot answer (e.g. the file is not indexed), '
'state that explicitly and then use grep/find as fallback — it will be allowed.'
),
},
}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment