Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dignity

A CLI toolkit for Claude Code: token metrics, status line generation, and declarative hook dispatch.

Installation

uv pip install -e .

CLI Commands

tokens

Display token metrics from a Claude Code transcript file.

dignity tokens <path_to_transcript.jsonl>
dignity tokens ~/.claude/transcript.jsonl --output json

Output 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

statusline

Generate a status line for shell integration. Reads JSON from stdin.

echo '{"model": {"display_name": "Claude"}, ...}' | dignity statusline

Output includes model name, git branch, and token usage percentage.

dispatch

Process hook events with declarative rules. Reads JSON from stdin.

echo '{"prompt": "implement feature"}' | dignity dispatch UserPromptSubmit

Supported events: UserPromptSubmit, Stop, SubagentStop

spec

Spec management commands for creating and tracking development specifications.

Create a spec

dignity spec create my-feature --type feature
dignity spec create fix-bug --type bug

Creates scaffolded files in specs/active/{name}/:

  • spec.md - Spec document with YAML frontmatter
  • context.md - Context and decisions
  • tasks.yaml - Task tracking
  • dependencies.md - Task dependency graph
  • validation-checklist.md - Validation tracking

Query specs

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

Task management

# 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-002

Task IDs are auto-generated as {CODE}-{NNN} (e.g., MF-001).

TodoWrite integration

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 --json

The 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 --json

Lifecycle management

dignity spec archive my-feature    # Move to archive
dignity spec restore my-feature    # Restore to active

Configuration

Spec directory location is configurable via environment variable or TOML file:

# Environment variable
export DIGNITY_SPECS_DIR=/path/to/specs

Or create dignity.toml in your project root:

specs_dir = "specs"

Default: specs/ relative to current directory.

Hook Dispatch

Declarative rule-based activation system for Claude Code hooks.

Shell Hook Integration

Add to ~/.claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      {"type": "command", "command": "dignity dispatch UserPromptSubmit"}
    ],
    "Stop": [
      {"type": "command", "command": "dignity dispatch Stop"}
    ]
  }
}

Configuration

Rules are loaded from (first found wins):

  1. DIGNITY_RULES_PATH environment variable
  2. .claude/rules.json (project-local)
  3. ~/.claude/hooks/rules.json (global)

Rules Format

{
  "rules": {
    "suggest-tdd": {
      "priority": "high",
      "action": {
        "type": "suggest_skill",
        "skill": "code-test",
        "reason": "Implementation task detected"
      },
      "triggers": {
        "UserPromptSubmit": {
          "keywords": ["implement", "add", "create"]
        }
      }
    }
  }
}

Action Types

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 Types

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

Trigger Groups

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).

Regex Capture Groups

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"
  }
}

State Interpolation

Use {state.key} in inject_context to interpolate state values:

{
  "action": {
    "type": "inject_context",
    "context": "Current focus: {state.focus}"
  }
}

State Management

File-based state storage for cross-hook persistence.

Location: ~/.claude/state/{session_id}-{key}

State is session-scoped and supports:

  • set_state action: Set from captured values
  • clear_state action: Remove state key
  • state_exists trigger: Fire when key exists
  • {state.key} interpolation: Inject state into context

See resources/focus-rules.example.json for a complete example.

Development

Running Tests

uv run pytest tests/

Type Checking

uv run pyright src/dignity/

Linting

uv run ruff check src/dignity/
uv run ruff format src/dignity/

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages