Skip to main content

Hooks and Context Injection

SCM uses hooks to automatically inject context into your AI coding sessions. This guide explains how the hook system works and how to configure it.

How Context Injection Works

When you start a Claude Code session, SCM automatically injects your configured context (fragments, profiles) into the conversation. This happens through a SessionStart hook that runs before each session.

The Flow

  1. You run scm run or start Claude Code in a project with SCM configured
  2. SCM assembles context from your default profile, bundles, and tags
  3. Context is written to a temporary file in .scm/context/
  4. The SessionStart hook injects this context into the AI session
  5. The context file is deleted after injection (one-time use)

Automatic Hook Setup

When you run scm init or scm mcp serve, SCM automatically configures hooks in your AI tool's settings:

Claude Code

SCM adds a hook to .claude/settings.json:

{
"hooks": {
"SessionStart": {
"hooks": [
{
"type": "command",
"command": "scm hook inject-context <hash>",
"timeout": 60
}
]
}
}
}

Gemini

Similar configuration in .gemini/settings.json.

Manual Hook Management

Apply Hooks

Regenerate and apply hooks to your backend configuration:

# Apply to all backends
scm hooks apply

# Apply to specific backend
scm hooks apply --backend claude-code

# Apply without regenerating context
scm hooks apply --no-regenerate

Via MCP

{
"tool": "apply_hooks",
"arguments": {
"backend": "claude-code",
"regenerate_context": true
}
}

Context Assembly

What Gets Included

Context is assembled from:

  1. Default Profile - Your configured default profile
  2. Profile Parents - Any parent profiles inherited
  3. Bundles - All bundles referenced by the profile
  4. Tagged Fragments - Fragments matching profile tags

Assembly Order

Fragments are assembled in a deterministic order:

  1. Parent profiles (in order specified)
  2. Direct bundles (alphabetically)
  3. Tag-matched fragments (alphabetically)

Deduplication

SCM automatically deduplicates content:

  • Same fragment from multiple sources appears once
  • Content-hash based deduplication catches identical content even from different bundles

Context Size Management

Size Warning

SCM warns when assembled context exceeds 16KB:

SCM: warning: assembled context is 24KB (recommended max: 16KB)
SCM: warning: large context may reduce LLM effectiveness; consider using fewer/smaller fragments

Research shows that LLM performance degrades with larger context, particularly for middle-positioned content. See the Distillation Guide for details.

Reducing Context Size

If you see size warnings:

  1. Use distillation - Distill verbose fragments to compressed versions
  2. Be selective - Only include fragments relevant to current work
  3. Split profiles - Create task-specific profiles instead of one large profile
  4. Review bundles - Remove unused bundles from profiles

Hook Commands

inject-context

The primary hook command that injects context:

scm hook inject-context <hash>
  • <hash> - Content hash identifying the context file
  • Reads from .scm/context/<hash>.md
  • Outputs context to stdout for the AI to consume
  • Deletes the context file after reading

Environment Variables

The hook system uses:

VariableDescription
SCM_CONTEXT_FILEPath to the context file to inject
SCM_VERBOSEEnable verbose output for debugging

Debugging Hooks

Check Hook Configuration

# View Claude Code settings
cat .claude/settings.json | jq '.hooks'

# View current context file
ls -la .scm/context/

Test Context Assembly

# Preview what would be injected
scm run --dry-run --print

# Assemble and show context
scm run --print

Verbose Mode

Enable verbose output to see hook execution:

SCM_VERBOSE=1 scm run

Custom Hooks

While SCM manages its own hooks, you can add custom hooks alongside SCM's:

{
"hooks": {
"SessionStart": {
"hooks": [
{
"type": "command",
"command": "scm hook inject-context abc123"
},
{
"type": "command",
"command": "my-custom-hook.sh"
}
]
}
}
}

Note: SCM identifies its hooks by an internal marker (_scm field) and only updates its own hooks, leaving your custom hooks intact.

Troubleshooting

Context Not Injected

  1. Check hooks are applied: cat .claude/settings.json
  2. Verify context file exists: ls .scm/context/
  3. Run with verbose: SCM_VERBOSE=1 scm run

Stale Context

If context seems outdated:

# Regenerate and reapply hooks
scm hooks apply --regenerate

Hook Timeout

If hooks timeout, increase the timeout in settings or optimize your context assembly (reduce fragments, use distillation).

Integration with Profiles

Hooks work seamlessly with profiles:

# .scm/profiles/default.yaml
description: My default development context
bundles:
- go-development
- testing-patterns
tags:
- best-practices

When this is your default profile, every session automatically gets these bundles and tagged fragments injected.

Best Practices

  1. Keep context focused - Include only what's relevant to your current work
  2. Use profiles - Create different profiles for different tasks
  3. Monitor size - Watch for size warnings and optimize as needed
  4. Test changes - Use --dry-run to preview context changes
  5. Version control - Commit your .scm/ configuration