A CLI toolkit for Claude Code: token metrics, status line generation, and declarative hook dispatch.
uv pip install -e .Display token metrics from a Claude Code transcript file.
dignity tokens <path_to_transcript.jsonl>
dignity tokens ~/.claude/transcript.jsonl --output jsonOutput format (text):
In: 3.5K | Out: 1.8K | Cache: 12.5K | Total: 17.8K | Ctx: 9.5K
Metrics:
- In: Input tokens sent to Claude
- Out: Output tokens generated by Claude
- Cache: Cached tokens (reads + creation)
- Total: Total tokens (input + output + cache)
- Ctx: Context length from most recent message
Generate a status line for shell integration. Reads JSON from stdin.
echo '{"model": {"display_name": "Claude"}, ...}' | dignity statuslineOutput includes model name, git branch, and token usage percentage.
Process hook events with declarative rules. Reads JSON from stdin.
echo '{"prompt": "implement feature"}' | dignity dispatch UserPromptSubmitSupported events: UserPromptSubmit, Stop, SubagentStop
Spec management commands for creating and tracking development specifications.
dignity spec create my-feature --type feature
dignity spec create fix-bug --type bugCreates scaffolded files in specs/active/{name}/:
spec.md- Spec document with YAML frontmattercontext.md- Context and decisionstasks.yaml- Task trackingdependencies.md- Task dependency graphvalidation-checklist.md- Validation tracking
dignity spec list # List all specs
dignity spec list --status Active # Filter by status
dignity spec show my-feature # Show spec details
dignity spec progress my-feature # Show completion stats# List tasks
dignity spec task list my-feature
# Manual task operations (CLI use)
dignity spec task add my-feature "Implement X" "Implementing X"
dignity spec task update my-feature MF-001 --status completed
dignity spec task start my-feature MF-001
dignity spec task complete my-feature MF-001
dignity spec task discard my-feature MF-002Task IDs are auto-generated as {CODE}-{NNN} (e.g., MF-001).
Sync tasks directly from Claude Code's TodoWrite payloads:
# Sync entire task list (replaces all tasks, updates statuses)
echo '{"todos": [
{"content": "Create types", "status": "completed", "activeForm": "Creating types"},
{"content": "Write tests", "status": "in_progress", "activeForm": "Writing tests"},
{"content": "Implement parser", "status": "pending", "activeForm": "Implementing parser"}
]}' | dignity spec task sync my-feature --json
# Append tasks from JSON (single or batch)
echo '{"content": "New task", "activeForm": "Adding task"}' | dignity spec task add my-feature --jsonThe sync command mirrors TodoWrite semantics - each call replaces the entire task list with updated statuses.
# Update single task (upsert - creates if ID doesn't exist)
echo '{"content": "Updated", "status": "completed", "activeForm": "Updating"}' | \
dignity spec task update my-feature MF-001 --jsondignity spec archive my-feature # Move to archive
dignity spec restore my-feature # Restore to activeSpec directory location is configurable via environment variable or TOML file:
# Environment variable
export DIGNITY_SPECS_DIR=/path/to/specsOr create dignity.toml in your project root:
specs_dir = "specs"Default: specs/ relative to current directory.
Declarative rule-based activation system for Claude Code hooks.
Add to ~/.claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{"type": "command", "command": "dignity dispatch UserPromptSubmit"}
],
"Stop": [
{"type": "command", "command": "dignity dispatch Stop"}
]
}
}Rules are loaded from (first found wins):
DIGNITY_RULES_PATHenvironment variable.claude/rules.json(project-local)~/.claude/hooks/rules.json(global)
{
"rules": {
"suggest-tdd": {
"priority": "high",
"action": {
"type": "suggest_skill",
"skill": "code-test",
"reason": "Implementation task detected"
},
"triggers": {
"UserPromptSubmit": {
"keywords": ["implement", "add", "create"]
}
}
}
}
}| Type | Fields | Description |
|---|---|---|
suggest_skill |
skill, reason |
Suggest invoking a skill |
remind |
message |
Show reminder in output |
block |
reason |
Block with error (SubagentStop) |
inject_context |
context |
Add context to prompt |
set_state |
key, value_from |
Set state from captured value |
clear_state |
key |
Clear a state key |
| Trigger | Fields | Hook Events |
|---|---|---|
keywords |
Word list | UserPromptSubmit |
intent_patterns |
Regex list | UserPromptSubmit |
tool_result |
tool_name, parameter_patterns |
Stop |
todo_state |
any_completed, all_completed |
Stop |
skill_invoked |
skill |
Stop |
output_missing |
required_patterns |
SubagentStop |
files_changed |
path_patterns, content_patterns |
Stop |
state_exists |
key |
UserPromptSubmit, Stop |
Combine triggers with AND/OR logic using explicit groups:
- Within a group: AND (all triggers must match)
- Across groups: OR (any group triggers the rule)
{
"triggers": {
"Stop": {
"groups": [
{
"tool_result": {"tool_name": ["TodoWrite"]},
"files_changed": {"path_patterns": ["specs/.*/tasks\\.md"]}
},
{
"skill_invoked": {"skill": "spec-archive"}
}
]
}
}
}This fires when: (TodoWrite used AND tasks.md changed) OR (spec-archive invoked).
Extract values from path patterns using named captures:
{
"files_changed": {
"path_patterns": ["specs/active/(?P<spec_id>[^/]+)/tasks\\.md"]
}
}Access captured values in actions:
{
"action": {
"type": "set_state",
"key": "focus",
"value_from": "captured.spec_id"
}
}Use {state.key} in inject_context to interpolate state values:
{
"action": {
"type": "inject_context",
"context": "Current focus: {state.focus}"
}
}File-based state storage for cross-hook persistence.
Location: ~/.claude/state/{session_id}-{key}
State is session-scoped and supports:
set_stateaction: Set from captured valuesclear_stateaction: Remove state keystate_existstrigger: Fire when key exists{state.key}interpolation: Inject state into context
See resources/focus-rules.example.json for a complete example.
uv run pytest tests/uv run pyright src/dignity/uv run ruff check src/dignity/
uv run ruff format src/dignity/