lifeloop-cli 0.3.1

Provider-neutral lifecycle abstraction and normalizer for AI harnesses
Documentation
# Created from the nantobv/.github workflow template "Claude Code".
# Wires this repo into the nantobv reusable Claude Code responder.
#
# Prerequisites (one-time, org-level — already in place for nantobv):
#   - The Anthropic Claude GitHub App installed on the org with
#     "All repositories" scope.
#   - An org-level Actions secret CLAUDE_CODE_OAUTH_TOKEN reachable
#     from this repo.
#
# Behavior, security gate (@claude must come from OWNER/MEMBER/COLLABORATOR),
# and SHA-pinned action versions live in the reusable workflow:
#   nantobv/.github/.github/workflows/claude-reusable.yml
name: Claude Code

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    # Only `opened`. Adding `assigned` would re-fire @claude on every
    # assignment change of an issue whose body/title already contains
    # @claude, burning quota on routine triage actions.
    types: [opened]
  pull_request_review:
    types: [submitted]

# Caller-level permissions must be at least as broad as what the
# reusable workflow's job-level block requests, otherwise GitHub
# pre-validates and rejects the run with `startup_failure` before any
# job starts. These mirror claude-reusable.yml's `jobs.claude.permissions:`
# block one-for-one. Each is required: contents (post a comment / branch),
# issues + pull-requests (write replies), id-token (OIDC for the
# action's auth), actions read (read workflow context).
permissions:
  contents: write
  pull-requests: write
  issues: write
  id-token: write
  actions: read

jobs:
  claude:
    # Short-circuit before invoking the reusable: only fire when an
    # @claude mention comes from OWNER/MEMBER/COLLABORATOR. Without
    # this, every issue comment / PR review on this repo spins up a
    # runner just to let the reusable's identical filter no-op. The
    # reusable keeps the same filter as defense-in-depth.
    if: |
      (github.event_name == 'issue_comment' &&
        contains(github.event.comment.body, '@claude') &&
        contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
      (github.event_name == 'pull_request_review_comment' &&
        contains(github.event.comment.body, '@claude') &&
        contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
      (github.event_name == 'pull_request_review' &&
        contains(github.event.review.body, '@claude') &&
        contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association)) ||
      (github.event_name == 'issues' &&
        (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) &&
        contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.issue.author_association))
    uses: nantobv/.github/.github/workflows/claude-reusable.yml@main # zizmor: ignore[unpinned-uses] same-org reusable workflow tracks @main by design; pinact exception declared org-wide in nantobv/.github/.pinact.yaml.
    # Pass only the secret the reusable declares it needs; avoid the
    # blast radius of `secrets: inherit`.
    secrets:
      CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}