A domain-agnostic guidance framework for Claude Code. Injects relevant knowledge just-in-time based on what you're doing.
Ways = automated, contextual guidance triggered by keywords, commands, and file patterns.
This repo ships with software development ways, but the mechanism is general-purpose. You could have ways for:
- Excel/Office productivity
- AWS operations
- Financial analysis
- Research workflows
- Anything with patterns Claude should know about
You: "let's discuss the architecture"
→ ADR way loads (architecture decision format, workflow)
You: "there's a bug in the auth"
→ Debugging way + Security way load
Claude runs: git commit
→ Commits way loads (conventional commit format)
Ways load once per session when triggered. No manual invocation needed.
# Backup existing config if any
[ -d ~/.claude ] && mv ~/.claude ~/.claude-backup-$(date +%Y%m%d)
# Clone
git clone https://github.com/aaronsb/claude-code-config ~/.claude
# Make hooks executable
chmod +x ~/.claude/hooks/**/*.sh ~/.claude/hooks/*.sh 2>/dev/null
# Restart Claude Code - ways are now activecore.md loads at session start with:
- Behavioral guidance: Collaboration style, communication norms, uncertainty handling
- Operational rules: File operation constraints, attribution settings
- Ways index: Dynamic table of all available ways (generated by macro)
Then, as you work:
- UserPromptSubmit scans your message for pattern matches
- PostToolUse scans commands, file paths, and descriptions
- Matching ways inject via
additionalContext- Claude sees them - Each way loads once per session - marker files prevent re-triggering
Ways don't continuously re-hook. When a way triggers:
First match → Output guidance + create marker file
Second match → Marker exists → No-op (silent)
Markers live in /tmp/.claude-way-{domain}-{wayname}-{session_id}. This prevents noise - you see each way once, when first relevant, then it stays quiet.
See docs/architecture.md for detailed Mermaid diagrams of the trigger flow and state machine.
~/.claude/hooks/ways/
├── core.md # Loads at startup (static guidance)
├── macro.sh # Generates dynamic ways table
├── show-core.sh # Combines macro + core.md
├── check-prompt.sh # Keyword + semantic matching
├── check-bash-post.sh # Command matching
├── check-file-post.sh # File path matching
├── show-way.sh # Once-per-session gating
├── semantic-match.sh # Gzip NCD similarity scoring
└── {domain}/ # Domain directories
└── {wayname}/
├── way.md # Frontmatter + guidance
└── macro.sh # Optional dynamic context
Ways config lives in ~/.claude/ways.json:
{
"disabled": ["itops"]
}| Field | Purpose |
|---|---|
disabled |
Array of domain names to skip (e.g., ["itops", "softwaredev"]) |
Disabled domains are completely ignored - no pattern matching, no output.
Each way is self-contained with YAML frontmatter:
---
match: regex
pattern: pattern1|pattern2|regex.*
files: \.tsx$|components/.*
commands: npm\ run\ build
macro: prepend
---
# Way Name
## Guidance
- Compact, actionable pointsCreate a directory in ~/.claude/hooks/ways/{domain}/{wayname}/ and add way.md. For project-local: $PROJECT/.claude/ways/{domain}/{wayname}/way.md.
| Field | Purpose |
|---|---|
match: |
regex (default) or semantic |
pattern: |
Regex matched against user prompts |
files: |
Regex matched against file paths (Edit/Write) |
commands: |
Regex matched against bash commands |
macro: |
prepend or append - run macro.sh for dynamic context |
description: |
Reference text for NCD similarity (semantic mode) |
vocabulary: |
Domain words for keyword counting (semantic mode) |
threshold: |
NCD similarity threshold (default 0.58, lower = stricter) |
For ambiguous triggers like "design" (software design vs UI design), ways can use semantic matching instead of regex:
---
match: semantic
description: software system design architecture patterns database schema
vocabulary: design architecture pattern schema api component factory
threshold: 0.55 # Optional: stricter matching (default 0.58)
---Semantic matching combines two techniques:
- Keyword counting - how many domain-specific words appear in the prompt
- Gzip NCD - compression-based similarity (Normalized Compression Distance)
Match if: domain_keywords >= 2 OR ncd_similarity < 0.58
This correctly distinguishes:
- ✓ "design the database schema" → triggers (software design)
- ✗ "button design looks off" → no trigger (UI design)
Zero dependencies - uses only bash, gzip, bc, grep (universal on all distros).
Static guidance can't know your environment. Macros add dynamic state detection:
Way = guidance (the "how")
Macro = state detection (the "what is")
Output = contextual guidance (the "how, given what is")
Example: github.macro.sh detects solo vs team project:
#!/bin/bash
gh repo view &>/dev/null || { echo "**Note**: Not a GitHub repo"; exit 0; }
CONTRIBUTORS=$(timeout 2 gh api repos/:owner/:repo/contributors --jq 'length' 2>/dev/null)
if [[ "$CONTRIBUTORS" -le 2 ]]; then
echo "**Context**: Solo project - PR optional"
else
echo "**Context**: Team project - PR recommended"
fiMacros are optional. Ways without macros work as pure static guidance.
See ADR-004 for full macro documentation.
Projects can have custom ways in .claude/ways/{domain}/{wayname}/:
Security Note: Project-local macros are disabled by default. Static way content (way.md) always loads, but macro.sh scripts only run for trusted projects. To trust a project:
echo "/path/to/your/project" >> ~/.claude/trusted-project-macrosThis prevents malicious repositories from executing code via way macros.
your-project/.claude/ways/
└── myproject/
├── api/
│ └── way.md # Project API conventions
├── deployment/
│ ├── way.md # How we deploy
│ └── macro.sh # Query deployment status
└── testing/
└── way.md # Override global testing way
Project ways override global ways with the same path. A template is auto-created on first session.
This repo ships with 20 development-focused ways:
| Way | Triggers On |
|---|---|
| adr | docs/adr/*.md, "architect", "decision" |
| api | "endpoint", "rest", "graphql" |
| commits | git commit, "push to remote" |
| config | .env, "environment variable" |
| debugging | "bug", "broken", "investigate" |
| deps | npm install, "dependency", "package" |
| docs | README.md, "documentation" |
| errors | "error handling", "exception" |
| github | gh, "pull request", "issue" |
| knowledge | .claude/ways/*.md, "ways" |
| migrations | "migration", "schema" |
| patches | *.patch, "git apply" |
| performance | "slow", "optimize", "profile" |
| quality | "refactor", "code review", "solid" |
| release | "deploy", "version", "changelog" |
| security | "auth", "secret", "token" |
| ssh | ssh, scp, "remote server" |
| subagents | "delegate", "subagent" |
| testing | pytest, jest, "coverage", "tdd" |
| tracking | .claude/todo-*.md, "multi-session" |
Replace these entirely if your domain isn't software dev. The framework doesn't care.
- 6 specialized subagents for complex tasks (requirements, architecture, planning, review, workflow, organization)
- ADR-driven workflow guidance
- GitHub-first patterns (auto-detects
ghavailability) - Status line with git branch and API usage
Claude Code has built-in Skills (semantically-discovered knowledge bundles). How do ways compare?
| Aspect | Skills | Ways |
|---|---|---|
| Discovery | Semantic (Claude decides based on intent) | Pattern (regex on prompts/tools/files) |
| Trigger | User asks something matching skill description | Hook events (tool use, file edit, keywords) |
| Invocation | Claude requests permission to use | Automatic injection (no permission needed) |
| Frequency | Per semantic match | Once per session (marker-gated) |
| Dynamic context | Via bundled scripts | Via macro.sh |
| Tool restriction | allowed-tools field |
Not supported |
They complement each other:
- Skills = "Teach Claude how to do X" (semantic, on-demand knowledge)
- Ways = "Remind Claude when doing Y" (event-driven, just-in-time guidance)
Use Skills for:
- Semantic discovery ("explain this code" → explaining-code skill)
- Tool restrictions (read-only security review)
- Multi-file reference docs with progressive disclosure
Use Ways for:
- Tool-triggered guidance (
git commit→ commit format reminder) - File-triggered guidance (editing
.env→ config best practices) - Session-gated delivery (show once, not repeatedly)
- Dynamic context (query GitHub API for contributor count)
Skills can't detect tool execution. Ways now support semantic matching via gzip NCD (see above). Together they cover both intent-based and event-based guidance.
An extensible macro framework for contextual guidance - lightweight, portable, deterministic.
| Feature | Why It Matters |
|---|---|
| Pattern matching | Predictable, debuggable (no semantic black box) |
| Shell macros | Dynamic context from any source (APIs, files, system state) |
| Zero dependencies | Bash + jq - runs anywhere |
| Domain-agnostic | Swap software dev ways for finance, ops, research, anything |
| Fully hackable | Plain text files, fork and customize in minutes |
The string matching is intentionally simple - it keeps the system portable and transparent. The power comes from macros that can query anything and inject real-time context.
cd ~/.claude && git pullMIT