Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
Prevent Claude Code overages by throttling tool usage when getting close to the 5-hour limit and weekly limit. Impose clean code and tdd rules before files are written to disk.
PluginsPython · ShellGitHub @LightspeedDMSSelf-installOwner unconfirmed
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Installable projects can be verified without a deployment domain. Use GitHub sign-in for a personal repository, or the repository verification file above for an organization repository.
Ask about other verification options
Loading the full README. The saved excerpt is shown below.
Intelligent credit consumption throttling and code quality enforcement for Claude Code.Read the full README ↗Intelligent credit consumption throttling and code quality enforcement for Claude Code.
pace-maker blockage-statspace-maker install claude-usage-monitor installs the usage monitoring dashboardgit checkout --, git reset --hard, git clean, or rm. Strict workspace isolation: sessions in different repositories never see each other. CLI: pace-maker sessions list, pace-maker cross-session-awareness on/off~/.claude/projects/<encoded>/memory/ folder to a repo-local .claude-memory/ directory that developers commit to git. When you clone a repo containing .claude-memory/, the SessionStart hook auto-links it — no manual symlink surgery per clone. Local always wins: stale central content is moved to a rename-backup then replaced with the symlink, with rollback on OSError. Optimistic race handling tolerates concurrent sessions. Safety asserts prevent any rmtree outside the central base. CLI: pace-maker localize-memory (seed a fresh project), pace-maker memory-localization on/off/status/unlink (toggle gate, reverse localization). Default enabled; state shown in pace-maker status[pace-maker · <channel>] tag, and relayed third-party reviewer/verifier LLM output (Stage 2 code review, danger-bash Phase 2) in a distinct [pace-maker · reviewer-relay · model=<id>] tag. SessionStart and SubagentStart also emit a manifest declaring the complete closed enumeration of channels pace-maker will ever use plus a four-item never-list, so Claude can recognize its own governance layer instead of misidentifying it as prompt injection — the manifest is the contract, the tag is just an index into it.Install pace-maker as a Claude Code plugin via a marketplace for automatic hook registration and updates.
Prerequisites: A marketplace repo that includes pace-maker (see Setting Up a Marketplace below).
# One-time: add your organization's marketplace
claude plugin marketplace add https://github.com/YourOrg/claude-plugins.git
# Install pace-maker
claude plugin install claude-pace-maker@claude-plugins
That's it. The first Claude Code session after installation automatically bootstraps:
~/.claude-pace-maker/config.json with production defaults~/.claude-pace-maker/source_code_extensions.json for validation~/.claude-pace-maker/pacemaker symlink to the plugin Python package (required for CLI)~/.local/bin/pace-maker CLI symlink (if ~/.local/bin is in PATH)~/.claude-pace-maker/venv with dependencies (requests, pyyaml, claude-agent-sdk) on SessionStartNo install.sh or manual settings.json editing required.
CLI not found after claude plugin install? The plugin is registered in Claude, but bootstrap runs on the first session (or manually):
# Ensure ~/.local/bin is on PATH, then:
pace-maker doctor
# Or without a working CLI yet:
bash "$(find ~/.claude -path '*/claude-pace-maker/scripts/doctor.sh' 2>/dev/null | sort -V | tail -1)"
Add to ~/.bashrc or ~/.zshrc if needed: export PATH="$HOME/.local/bin:$PATH"
pipx install git+https://github.com/LightspeedDMS/claude-pace-maker.git
claude-pace-maker
git clone https://github.com/LightspeedDMS/claude-pace-maker.git
cd claude-pace-maker
./install.sh
To distribute pace-maker across your organization, add it as a submodule in a Claude Code marketplace repository.
mkdir claude-plugins && cd claude-plugins
git init
mkdir -p .claude-plugin
Create .claude-plugin/marketplace.json:
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "your-org-plugins",
"description": "Internal Claude Code plugins for YourOrg",
"plugins": [
{
"name": "claude-pace-maker",
"description": "Usage observability, adaptive pacing, and Langfuse telemetry for Claude Code",
"category": "productivity",
"source": {
"source": "url",
"url": "https://github.com/LightspeedDMS/claude-pace-maker.git"
},
"homepage": "https://github.com/LightspeedDMS/claude-pace-maker"
}
]
}
If you prefer vendoring the plugin source instead of referencing the URL:
git submodule add https://github.com/LightspeedDMS/claude-pace-maker.git plugins/claude-pace-maker
Then update marketplace.json to use a local source:
{
"name": "claude-pace-maker",
"source": "./plugins/claude-pace-maker"
}
git add -A && git commit -m "Add claude-pace-maker plugin"
git remote add origin https://github.com/YourOrg/claude-plugins.git
git push -u origin main
Users install with:
claude plugin marketplace add https://github.com/YourOrg/claude-plugins.git
claude plugin install claude-pace-maker@your-org-plugins
When installed as a plugin, pace-maker uses this structure:
claude-pace-maker/ (repo root = plugin root)
.claude-plugin/
plugin.json (metadata: name, description, author, version)
hooks/
hooks.json (7 hook definitions using ${CLAUDE_PLUGIN_ROOT})
scripts/
hook.sh (unified entry point, lazy-init, Python dispatch)
pace-maker (CLI wrapper script, symlink target)
config/
config.defaults.json (default config.json template)
source_code_extensions.json (file extensions for validation)
src/pacemaker/ (Python source code)
The ${CLAUDE_PLUGIN_ROOT} variable is set by Claude Code at runtime, pointing to the plugin's installation directory. All hook commands reference this variable for portability.
If you previously installed pace-maker via install.sh and want to switch to plugin mode:
# 1. Install the plugin via marketplace (see above)
# 2. Run the migration script to remove legacy hooks from settings.json
cd /path/to/claude-pace-maker
bash migrate-to-plugin.sh
# 3. Verify: settings.json should no longer contain pace-maker hook entries
cat ~/.claude/settings.json | jq '.hooks'
The migration script:
~/.claude/settings.json~/.claude/hooks/ (pre-tool-use.sh, post-tool-use.sh, etc.)~/.claude/hooks/pacemaker/ directoryYour ~/.claude-pace-maker/config.json and database are preserved — no reconfiguration needed.
pace-maker status # Show current status
pace-maker on|off # Master switch - enable/disable ALL hooks
pace-maker weekly-limit on|off # Enable/disable 7-day limit
pace-maker 5-hour-limit on|off # Enable/disable 5-hour limit
pace-maker tempo on|off # Enable/disable session lifecycle (global)
pace-maker tempo session on|off # Override tempo for current session
pace-maker reminder on|off # Enable/disable subagent reminder
pace-maker intent-validation on|off # Enable/disable pre-tool validation
pace-maker tdd on|off # Enable/disable TDD enforcement
pace-maker clean-code list # List all clean code rules
pace-maker clean-code add NAME DESCRIPTION # Add custom clean code rule
pace-maker clean-code remove NAME # Remove clean code rule
pace-maker core-paths list # List core code paths
pace-maker core-paths add PATH # Add core code path
pace-maker core-paths remove PATH # Remove core code path
pace-maker prefer-model opus|sonnet|haiku|auto # Set model preference for subagents
pace-maker langfuse on|off|status|configure # Langfuse telemetry controls
pace-maker loglevel 0-4 # Set log level (0=OFF, 1=ERROR, 2=WARNING, 3=INFO, 4=DEBUG)
pace-maker excluded-paths list # List excluded paths (skip TDD/clean code)
pace-maker excluded-paths add PATH # Add excluded path
pace-maker excluded-paths remove PATH # Remove excluded path
pace-maker secrets add VALUE # Declare a secret value for masking
pace-maker secrets addfile PATH # Declare a file as containing secrets
pace-maker secrets list # List declared secrets
pace-maker danger-bash list # List danger bash rules
pace-maker danger-bash add ID PATTERN CATEGORY DESC # Add custom danger bash rule
pace-maker danger-bash remove ID # Remove danger bash rule
pace-maker blockage-stats # Show validation blockage statistics
pace-maker install claude-usage-monitor # Install claude-usage-monitor companion tool
pace-maker version # Show version
pace-maker help # Show help
Two-stage AI validation system that ensures code quality and intent transparency.
Stage 1: Fast Declaration Check (~2-4 seconds)
Stage 2: Comprehensive Code Review (~10-15 seconds)
Before any code edit, declare in the SAME message as the Write/Edit tool:
Example:
I will modify src/auth.py to add a validate_password() function
that checks password strength, to improve security.
[then use Write/Edit tool in same message]
Files in core paths require a TDD declaration. A file is treated as a core path in two ways:
pace-maker core-paths add/remove, see below): src/, lib/, code/, core/, source/, libraries/, kernel/, app/, routes/, services/, internal/.*.csproj/*.sln (.NET), pyproject.toml/setup.py (Python), package.json (Node), build.gradle/build.gradle.kts/pom.xml (Java/Kotlin), go.mod (Go), Cargo.toml (Rust). This catches production code whose source-root directory is named after the project itself (e.g. a .NET solution's MyProject/Program.cs with no src/ wrapper) rather than a generic word. Test files (excluded paths, non-source extensions, filename-suffix patterns like *_test.go, and dedicated test projects like MyProject.Tests.csproj) never trigger either check.Once a file is identified as core, it requires:
Option A - Declare test coverage:
I will modify src/auth.py to add validate_password().
Test coverage: tests/test_auth.py - test_validate_password_rejects_weak()
Option B - Quote user permission to skip TDD:
I will modify src/auth.py to add validate_password().
User permission to skip TDD: User said "skip tests for this" in message 3.
The quoted permission must exist in the last 5 messages. Fabricated quotes are rejected.
| Violation | Description |
|---|---|
| Hardcoded secrets | API keys, passwords, tokens in code |
| SQL injection | String concatenation in queries |
| Bare except | except: without specific exception type |
| Swallowed exceptions | except: pass without logging |
| Magic numbers | Unexplained numeric literals |
| Mutable defaults | def func(x=[]) anti-pattern |
| Commented-out code | Dead code without explanation |
| Deep nesting | 6+ levels of conditionals |
| Large methods | Functions exceeding ~50 lines |
| Undeclared fallbacks | Hidden default behaviors |
| Logic bugs | Off-by-one errors, boundary issues |
| Over-mocked tests | Tests that mock the code under test |
| Scope creep | Code changes not declared in intent |
| Missing functionality | Declared features not implemented |
| Unauthorized deletions | Removing code not mentioned in intent |
All configuration is externalized for easy customization:
~/.claude-pace-maker/config.json - Main configuration (enable/disable features, log level)~/.claude-pace-maker/source_code_extensions.json - Source file extensions to validate~/.claude-pace-maker/clean_code_rules.yaml - Customizable code quality rules~/.claude-pace-maker/danger_bash_rules.yaml - Customizable dangerous command rules (same merge strategy as clean code rules)~/.claude-pace-maker/core_paths.yaml - Directories requiring TDD enforcement~/.claude/hooks/pacemaker/prompts/ - Validation prompt templatesUse CLI commands to manage rules and paths without editing YAML directly.
⚠️ CRITICAL: When modifying intent validator prompts, disable validation temporarily to prevent infinite loops.
Problem: Editing validation prompts while validation is enabled creates recursive validation loops.
Solution: Temporarily bypass validation during prompt development.
# BEFORE modifying prompts in src/pacemaker/prompts/
pace-maker intent-validation off
# Now safe to edit validation prompt files:
# - src/pacemaker/prompts/pre_tool_use/pre_tool_validator_prompt.md
# - src/pacemaker/prompts/stop/stop_hook_validator_prompt.md
# - src/pacemaker/prompts/common/intent_declaration_prompt.md
# Make your prompt changes...
# AFTER completing prompt modifications
pace-maker intent-validation on
Workflow:
pace-maker intent-validation offsrc/pacemaker/prompts/./install.sh (copies prompts to ~/.claude/hooks/)pace-maker intent-validation onNote: This applies to ALL prompt file modifications that affect validation logic. Prompts for other hooks (session_start, user_commands) don't require validation bypass.
When Anthropic's usage API becomes unavailable (HTTP 429 rate limiting), pace-maker automatically switches to fallback mode — synthesizing utilization estimates from accumulated token costs so that pacing continues uninterrupted.
Normal Mode Fallback Mode
┌──────────────┐ 429 error ┌──────────────────────┐
│ Real API │ ──────────────> │ Synthetic estimates │
│ utilization │ │ from token costs │
│ from Claude │ <────────────── │ + baseline snapshot │
└──────────────┘ API recovers └──────────────────────┘
The mapping from dollar cost to utilization percentage depends on your subscription tier. Pace-maker ships with default coefficients for 5x and 20x tiers, but automatically learns better values over time:
ratio = real_utilization / synthetic_predictionnew = weighted_average(old_coefficient, measured_coefficient)calibrated_coefficients table) and used in preference to defaultsThe UsageModel class (src/pacemaker/usage_model.py) is the unified access point for all usage data:
from pacemaker.usage_model import UsageModel
model = UsageModel()
snapshot = model.get_current_usage() # Returns real or synthetic
if snapshot:
print(f"5h: {snapshot.five_hour_util:.1f}%")
print(f"Synthetic: {snapshot.is_synthetic}")
Key design principles:
get_current_usage() returns real API data in normal mode, synthetic estimates in fallback modeAll fallback state is stored in SQLite tables (replacing previous JSON files):
| Table | Purpose |
|---|---|
api_cache | Last successful API response (raw) |
fallback_state_v2 | Current mode, baseline, tier, rollover offsets |
accumulated_costs | Per-session cost records (INSERT-only) |
backoff_state | Exponential backoff tracking |
calibrated_coefficients | Learned cost→utilization mappings per tier |
profile_cache | Cached profile data for tier detection |
Optional integration with Langfuse for observability and tracing.
pace-maker langfuse on # Enable Langfuse
pace-maker langfuse off # Disable Langfuse
pace-maker langfuse status # Show connection status
pace-maker langfuse configure URL PUBLIC SECRET # Set credentials
Optional per-prompt metadata that Claude emits at the start of responses. The intel line is automatically parsed, stripped from visible output, and attached to the corresponding Langfuse trace as metadata.
Claude emits a single line starting with the § marker:
§ △0.8 ◎surg ■bug ◇0.7 ↻2
| Symbol | Field | Type | Values | Description |
|---|---|---|---|---|
§ | (marker) | - | - | Identifies the intel line (required) |
△ | frustration | float | 0.0 - 1.0 | User frustration level (0=calm, 1=high) |
◎ | specificity | enum | surg, const, outc, expl | How specific the request is |
■ | task_type | enum | bug, feat, refac, research, test, docs, debug, conf, other | Nature of the work |
◇ | quality | float | 0.0 - 1.0 | Claude's confidence in understanding/solution |
↻ | iteration | int | 1 - 9 | Number of attempts on this issue |
Specificity values:
surg = Surgical (very specific, targeted change)const = Constrained (specific scope with boundaries)outc = Outcome-focused (goal stated, approach open)expl = Exploratory (vague, needs clarification)Intel fields are attached to Langfuse traces with the intel_ prefix. Use these keys when building Langfuse dashboards, filters, and analytics:
| Langfuse Metadata Key | Source Field | Type | Example Value |
|---|---|---|---|
intel_frustration | △ | float | 0.8 |
intel_specificity | ◎ | string | "surg" |
intel_task_type | ■ | string | "bug" |
intel_quality | ◇ | float | 0.7 |
intel_iteration | ↻ | int | 2 |
Dashboard examples:
intel_task_type = "bug" to see all bug fix conversationsintel_frustration over time to detect recurring pain pointsintel_specificity to analyze prompt quality patternsintel_iteration >= 4 to identify stuck conversationsintel_quality with session duration for efficiency metricsprompts/session_start/intel_guidance.md)§ intel: Start your response with a § intel line...)§ intel line at the start of a responseAll fields are optional. Missing or invalid fields are silently skipped (no defaults injected).
Controls which model Claude uses for subagent Task tool calls to balance quota consumption across models.
Claude Pro Max has separate quotas for different models (Opus, Sonnet). When one model's quota is consumed faster than another, you can hit rate limits on one while having unused capacity on the other.
Example scenario:
Without intervention, Claude defaults to Sonnet for subagents, exhausting that quota while Opus capacity sits unused.
pace-maker prefer-model opus # Force subagents to use Opus
pace-maker prefer-model sonnet # Force subagents to use Sonnet
pace-maker prefer-model haiku # Force subagents to use Haiku
pace-maker prefer-model auto # No preference (default behavior)
When a model preference is set, the system injects mandatory nudges at two points:
The nudge is assertive:
⚠️ MANDATORY MODEL PREFERENCE: OPUS
You MUST use model: "opus" for ALL Task tool subagent calls.
WHY: This is for QUOTA BALANCING, not capability.
The user needs to balance token consumption across models to maximize
their usage window. Even if the default model 'works fine', using the
preferred model (opus) helps prevent hitting rate limits.
REQUIRED FORMAT:
Task(subagent_type='...', model='opus', prompt='...')
claude --model opusauto to disable nudging and return to default behaviorPrevents Claude from ending sessions prematurely.
When Claude attempts to stop:
| Hook | Function |
|---|---|
| SessionStart | Initializes state, injects intel guidance prompt, Langfuse session creation |
| UserPromptSubmit | Captures prompts, handles CLI commands, deferred trace creation |
| PreToolUse | Intent validation, TDD checks, clean code validation |
| PostToolUse | Credit throttling, subagent reminders, intel parsing, Langfuse trace/span push, activity event recording |
| Stop | Session completion validation, Langfuse trace finalization with token usage |
| SubagentStart/Stop | Tracks subagent context, Langfuse subagent trace management |
~/.claude-pace-maker/config.json:
{
"enabled": true,
"weekly_limit_enabled": true,
"five_hour_limit_enabled": true,
"tempo_enabled": true,
"intent_validation_enabled": true,
"tdd_enabled": true,
"danger_bash_enabled": true,
"subagent_reminder_enabled": true,
"preferred_subagent_model": "auto",
"base_delay": 5,
"max_delay": 350,
"safety_buffer_pct": 95.0,
"preload_hours": 12.0,
"log_level": 2,
"langfuse_enabled": false,
"langfuse_host": "",
"langfuse_public_key": "",
"langfuse_secret_key": ""
}
MIT