decree 0.3.0

AI orchestrator for structured, reproducible workflows
Documentation
# Build stage: compile decree
FROM rust:1-slim-bookworm AS builder

WORKDIR /build

# Cache dependencies
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo 'fn main() {}' > src/main.rs && \
    cargo build --release && \
    rm -rf src

# Build the real binary
COPY src/ src/
RUN touch src/main.rs && cargo build --release

# Runtime stage
FROM node:24-bookworm-slim

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
        bash git curl ca-certificates util-linux && \
    rm -rf /var/lib/apt/lists/*

# Copy decree binary
COPY --from=builder /build/target/release/decree /usr/local/bin/decree

# Create entrypoint script
RUN cat <<'ENTRYPOINT_EOF' > /usr/local/bin/entrypoint.sh
#!/usr/bin/env bash
set -euo pipefail

# Default DECREE_CONTAINER to hostname (Docker sets this to 12-char container ID)
DECREE_CONTAINER="${DECREE_CONTAINER:-$HOSTNAME}"
export DECREE_CONTAINER

# Validate DECREE_CONTAINER: only [a-zA-Z0-9_-], no __, no empty
if [[ -z "$DECREE_CONTAINER" ]]; then
  echo "ERROR: DECREE_CONTAINER must not be empty" >&2
  exit 1
fi
if [[ "$DECREE_CONTAINER" == *"__"* ]]; then
  echo "ERROR: DECREE_CONTAINER must not contain '__': $DECREE_CONTAINER" >&2
  exit 1
fi
if ! [[ "$DECREE_CONTAINER" =~ ^[a-zA-Z0-9_-]+$ ]]; then
  echo "ERROR: DECREE_CONTAINER contains invalid characters (only [a-zA-Z0-9_-] allowed): $DECREE_CONTAINER" >&2
  exit 1
fi

# Install AI tool if requested
DECREE_AI="${DECREE_AI:-}"
if [[ -n "$DECREE_AI" ]]; then
  case "$DECREE_AI" in
    opencode)
      if ! command -v opencode &>/dev/null; then
        echo "Installing opencode-ai..."
        npm i -g opencode-ai
      fi
      ;;
    claude)
      if ! command -v claude &>/dev/null; then
        echo "Installing claude-code..."
        npm i -g @anthropic-ai/claude-code
      fi
      ;;
    copilot)
      if ! command -v gh &>/dev/null; then
        echo "Installing GitHub CLI..."
        ARCH=$(dpkg --print-architecture)
        GH_VERSION=$(curl -fsSL https://api.github.com/repos/cli/cli/releases/latest | sed -n 's/.*"tag_name": "v\([^"]*\)".*/\1/p')
        curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${ARCH}.deb" -o /tmp/gh.deb
        dpkg -i /tmp/gh.deb && rm /tmp/gh.deb
      fi
      if ! gh extension list 2>/dev/null | grep -q copilot; then
        echo "Installing GitHub Copilot extension..."
        gh extension install github/gh-copilot
      fi
      ;;
    *)
      echo "WARNING: Unknown DECREE_AI value: $DECREE_AI (supported: opencode, claude, copilot)" >&2
      ;;
  esac
fi

# Initialize decree if .decree/ doesn't exist
if [[ ! -d /work/.decree ]]; then
  decree init --no-color </dev/null
fi

# If CMD arguments were passed, exec them directly
if [[ $# -gt 0 ]]; then
  exec "$@"
fi

# Default behavior: daemon or interactive shell
DECREE_DAEMON="${DECREE_DAEMON:-true}"
if [[ "$DECREE_DAEMON" == "true" ]]; then
  exec decree daemon --no-color --interval "${DECREE_INTERVAL:-2}"
else
  exec bash
fi
ENTRYPOINT_EOF
RUN chmod +x /usr/local/bin/entrypoint.sh

WORKDIR /work

VOLUME ["/work"]

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]