透過在接近5小時限制和每週限制時對工具使用進行限流,防止 Claude Code 超額。在將檔案寫入磁碟之前強制執行乾淨程式碼和 TDD 規則。
AI 翻譯
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.
外掛Python · ShellGitHub @LightspeedDMS自行安裝營運者未確認
試用過了嗎? 點一下就好,不需要登入。
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
安裝型專案無需部署網域也可驗證。個人儲存庫可透過 GitHub 登入驗證,組織儲存庫可使用上方的儲存庫驗證檔案。
正在載入完整 README。 以下為已儲存的摘錄。
Intelligent credit consumption throttling and code quality enforcement for Claude Code.檢視完整 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