Decree is an AI orchestrator for structured, reproducible workflows.

It processes migration files through AI-powered routines, with optional
git stash hooks for change tracking and retry on failure.

Core workflow:
  1. Write migration files describing work in .decree/migrations/
  2. Run `decree process` to execute them through AI routines
  3. Routines invoke AI tools (opencode, claude, copilot) to do the work
  4. Optional git stash hooks isolate each routine's changes

Commands:
  decree process              Process all pending migrations + drain inbox
  decree routine              List routines (interactive select + run)
  decree routine <name>       Show routine detail + run pre-checks
  decree verify               Run all routine pre-checks
  decree daemon [--interval]  Continuous inbox + cron monitoring
  decree routine-sync         Sync routine registry with filesystem
  decree status               Show processing progress
  decree cron list            List cron schedules with last/next run times
  decree skill                Install AI assistant skill/instructions
  decree log [ID]             Show routine execution output
  decree init                 Initialize a new decree project
  decree help                 This help text

Message Format:
  Messages use optional YAML frontmatter followed by a markdown body.
  Frontmatter fields control routing and parameters.

  ---
  routine: develop              # Which routine to execute
  custom_field: value           # Custom fields become env vars
  ---
  Description of the work to do.

  The body can be empty. All frontmatter fields are optional — decree
  fills in missing fields automatically (chain, seq, id, routine).
  Migration content is copied into the message body when processed.

Processing Pipeline:
  1. Migration files in .decree/migrations/ are read in alphabetical order
  2. Each migration becomes an inbox message in .decree/inbox/
  3. Messages are normalized (missing fields filled, routine selected). When
     the AI router is consulted to pick a routine, the literal prompt and the
     AI's response are printed to stderr so you can see what was sent.
  4. Lifecycle hooks run (beforeEach — e.g. git stash baseline)
  5. The selected routine executes with parameters as env vars. The bundled
     routines echo each AI prompt before invoking the assistant, so the literal
     prompt/context is captured in the run's routine.log (decree log <ID>).
  6. On success: afterEach hook runs, message deleted from inbox; run.json is
     written to the run directory with metadata about the completed run
  7. On failure: retry strategy applies (hooks handle state management).
     If the run log contains "usage limit" + "reset", decree waits until the
     reset time (SIGINT-aware, exits 130) then retries the migration from scratch.
  8. After all retries: dead-letter the message. If the dead-lettered message
     was a migration, decree process stops immediately and exits non-zero —
     subsequent migrations are not started.
  9. Follow-up messages from routines are processed depth-first
  10. Inbox is fully drained before the next migration starts

Environment Variables:
  Decree sets these env vars before running every routine and hook:

  message_file  Path to message.md in the run directory
  message_id    Full message ID (e.g., D0001-1432-01-add-auth-0)
  message_dir   Run directory path (contains logs from prior attempts)
  chain         Chain ID (D<NNNN>-HHmm-<name>)
  seq           Sequence number in chain

  Hook-only env vars:
  DECREE_HOOK            Hook type name (beforeAll, afterAll, etc.)
  DECREE_ATTEMPT         Current attempt number (beforeEach/afterEach)
  DECREE_MAX_ATTEMPTS    Configured max attempts (beforeEach/afterEach)
  DECREE_ROUTINE_EXIT_CODE  Routine exit code (afterEach only)
  DECREE_PRE_CHECK       Set to "true" during pre-check runs
  DECREE_FINAL_ATTEMPT   "true" on final attempt (afterEach only)
  DECREE_TRIGGER         How the run was initiated: inbox | cron:<stem> | chain

  Retry env vars (set on token-exhaustion retry):
  DECREE_PREVIOUS_SESSION_ID  Claude session ID from the prior attempt, if captured

  Custom frontmatter fields are also passed as env vars.

Defining Routines:
  Routines are shell scripts in .decree/routines/ (nested dirs allowed).
  They call AI tools directly — no magic variables.

  Required structure:
    #!/usr/bin/env bash
    # Title
    #
    # Description shown by `decree routine`.
    set -euo pipefail

    # --- Standard Environment Variables ---
    # message_file  - Path to message.md in the run directory
    # message_id    - Full message ID (e.g., D0001-1432-01-add-auth-0)
    # message_dir   - Run directory path (contains logs from prior attempts)
    # chain         - Chain ID (D<NNNN>-HHmm-<name>)
    # seq           - Sequence number
    message_file="${message_file:-}"
    message_id="${message_id:-}"
    message_dir="${message_dir:-}"
    chain="${chain:-}"
    seq="${seq:-}"

    # Pre-check (required — exit 0 if ready, non-zero if not):
    if [ "${DECREE_PRE_CHECK:-}" = "true" ]; then
        command -v claude >/dev/null 2>&1 || { echo "claude not found" >&2; exit 1; }
        exit 0
    fi

    # Custom params (from frontmatter, discovered automatically):
    my_param="${my_param:-default}"

    # Implementation (call your AI tool directly):
    claude -p "Read ${message_file} and implement the requirements.
    Previous attempt logs (if any) are in ${message_dir} for context."

  Custom parameter discovery:
    Decree scans for var="${var:-default}" patterns after the pre-check
    block. Standard params are excluded. Remaining are custom parameters
    whose values come from message frontmatter.

  Tips:
    - Pre-check failures should print to stderr
    - Use ${message_dir} for prior attempt logs as AI context on retries
    - Call your AI tool directly (e.g., claude -p, copilot -p, opencode run)
    - Use --no-color flag or NO_COLOR env var to disable color output

  Run `decree verify` to check all routines' pre-checks at once.
  Run `decree skill` to install the decree-routine skill for AI-assisted authoring.

Routine Registry & Shared Routines:
  Routines are registered in config.yml under `routines` (project-local) and
  `shared_routines` (shared library). A routine must be registered AND enabled
  to be discoverable or executable. Deprecated routines are treated as disabled.

  Config layout:
    routine_source: "~/.decree/routines"    # shared routines directory
    max_attempts: 3                         # global default
    routines:
      develop:
        enabled: true
      gmail-sync:
        enabled: true
        max_attempts: 5       # overrides global max_attempts for this routine
      actual-budget:
        enabled: true
        timeout_s: 60         # kill with SIGTERM after 60 s; treated as exit 1
    shared_routines:
      deploy:
        enabled: true

  Per-routine overrides:
    max_attempts Overrides the global attempt cap for that routine only.
    timeout_s    If set, the routine process is killed with SIGTERM after the
                 given seconds and the attempt is treated as exit code 1.

  Directory layering:
    1. .decree/routines/       — project-local (wins if present)
    2. routine_source path     — shared library (fallback)

  Discovery runs automatically at `decree process`, `decree daemon`, and
  `decree init`. New project routines are registered as enabled; new shared
  routines as disabled. Routines whose files disappear are marked deprecated.

  Run `decree routine-sync` to trigger discovery manually:
    decree routine-sync                    # use routine_source from config
    decree routine-sync --source ~/other   # override shared directory

  Hooks bypass the registry — they only need the script to exist on disk.

Lifecycle Hooks (config.yml):
  hooks:
    beforeAll: ""      # Routine to run before all processing
    afterAll: ""       # Routine to run after all processing
    beforeEach: ""     # Routine to run before each message
    afterEach: ""      # Routine to run after each message
    onDeadLetter: ""   # Routine to run when a message is dead-lettered

  onDeadLetter fires exactly once when a message moves to inbox/dead/ after
  exhausting all retries. It does NOT fire on beforeEach failures.

  Hooks receive additional env vars:
    DECREE_HOOK            — hook type name
    DECREE_ATTEMPT         — current attempt number (beforeEach/afterEach)
    DECREE_MAX_ATTEMPTS    — configured max attempts (beforeEach/afterEach)
    DECREE_ROUTINE_EXIT_CODE — routine exit code (afterEach only)
    DECREE_FINAL_ATTEMPT   — "true" in afterEach on the last attempt only
    DECREE_TRIGGER         — how the run was initiated (inbox/cron:<stem>/chain)

  onDeadLetter additional env vars:
    DECREE_ATTEMPT              Equals max_attempts (all attempts were exhausted)
    DECREE_MAX_ATTEMPTS         Configured max attempts
    DECREE_ROUTINE_EXIT_CODE    Exit code of the last attempt
    DECREE_TRIGGER              How the run was initiated

Cron Scheduling:
  Place .md files with a `cron` frontmatter field in .decree/cron/:

  ---
  cron: "0 9 * * 1-5"
  routine: develop
  ---
  Run the weekday morning task.

  Common schedules:
    * * * * *       Every minute
    0 * * * *       Every hour
    0 9 * * *       Daily at 9:00 AM
    0 9 * * 1-5     Weekdays at 9:00 AM
    0 0 * * 0       Weekly on Sunday
    0 0 1 * *       Monthly on the 1st
    */15 * * * *    Every 15 minutes

  Run `decree daemon` to start monitoring cron and inbox.
  Run `decree cron list` to inspect live schedule status (last run, next fire).

run.json:
  After every completed run (success or dead-letter), decree writes run.json
  to the run directory. Fields:

    message_id    Full message ID
    routine       Routine name used for processing
    trigger       How the run was initiated (inbox, cron:<stem>, chain)
    migration     Migration filename, if this was a migration run
    attempts      Number of attempts made
    exit_code     Exit code of the final attempt
    start         ISO-8601 timestamp when the run started
    end           ISO-8601 timestamp when the run ended
    duration_s    Total elapsed seconds

AI Skill Installation:
  Install Decree guidance for Claude or GitHub Copilot:

    decree skill --scope project --target claude
        Presents an interactive selection of Claude skills to install.
        Skills are installed under .claude/skills/<name>/SKILL.md.

    decree skill --scope project --target copilot
        Installs skills under .github/skills/<name>/SKILL.md.

    decree skill --scope user --target claude
        Installs Claude skills under ~/.claude/skills/<name>/SKILL.md.

    decree skill --scope user --target copilot
        Not supported; exits non-zero.

  Available Claude skills:
    decree          Work within the Decree migration ecosystem safely and idiomatically
                    (.claude/skills/decree/SKILL.md)
    decree-routine  Authoring guide for Decree routine scripts
                    (.claude/skills/decree-routine/SKILL.md)
    sow             Guide for writing a Statement of Work document
                    (.claude/skills/sow/SKILL.md)

  Flags:
    --skill <name>  Install a specific skill (repeatable; for scripting/non-TTY)
    --all           Install all available skills for the selected target
    --force         Overwrite an existing file that differs from the bundled
                    template (default: no-op if already matching).

  The command is idempotent — if the installed file already matches the
  bundled template, it does nothing.

Getting Started:
  1. decree init                            # Set up project
  2. decree skill --target claude --all     # Install AI skills (decree, decree-routine, sow)
  3. decree verify                          # Check routines are ready
  4. decree process                         # Execute all migrations
  5. decree routine                         # Run individual routines interactively
