Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

🩺 naoru

Heal your broken CI with AI

When a pipeline fails, naoru reads the logs + your PR diff, finds the root cause, and comments the fix right on your pull request.

Release Marketplace License

CI Container Made by CloudDrove


naoru (直る) is Japanese for "to be fixed / to heal." That's the whole job — your pipeline breaks, naoru tells you why and how to make it right.

  ❌ build failed                        🩺 naoru
  ───────────────                        ─────────
  TypeError: Cannot read    ──────▶      Root cause: `user` is undefined at src/auth.ts:42
  properties of undefined                Fix:  - if (user.id)
  (reading 'id')                               + if (user?.id)
                                         Confidence: High
  • 🔎 Root-cause, not noise — reads the failed job's logs and the PR diff, then explains the actual cause.
  • 💬 One sticky comment — updates in place on every re-run. No comment spam.
  • 📋 Works without a PR — on workflow_dispatch/schedule runs (no PR to comment on), the diagnosis is written to the job Step Summary instead. Great for Terraform deploy/drift pipelines.
  • 🧠 Any LLM — Anthropic, OpenAI, OpenRouter, xAI (Grok), Groq, or any OpenAI-compatible endpoint.
  • 🐳 Runs anywhere — GitHub Action or a portable Docker image for GitLab dind, Jenkins, and local.
  • 🛟 Never breaks your build — diagnosis is comment-only and always exits 0.

🚀 Setup in 3 steps

1️⃣ Add your LLM API key as a repository secret

naoru reads the key from GitHub Actions secrets — never hard-code it in YAML.

Via the GitHub UI:

Your repo → SettingsSecrets and variablesActionsNew repository secret

  • Name: ANTHROPIC_API_KEY (or OPENAI_API_KEY, etc.)
  • Secret: paste your key

Or via the CLI:

gh secret set ANTHROPIC_API_KEY --repo your-org/your-repo
# paste the key when prompted

🔑 Where to get a key: Anthropic · OpenAI · OpenRouter · xAI/Grok · Groq

2️⃣ Add a naoru job to your workflow

Drop this into any workflow file (e.g. .github/workflows/ci.yml). It runs only when your build fails:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: make build          # ← your real build/test job

  naoru:
    needs: [build]
    if: ${{ always() && needs.build.result == 'failure' }}
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write       # ← post the comment
      actions: read              # ← read the failed run's logs
    steps:
      - uses: clouddrove/naoru@v0
        with:
          api-key: ${{ secrets.ANTHROPIC_API_KEY }}   # ← the secret from step 1
          provider: anthropic                          # anthropic | openai | openrouter | xai | groq | custom

3️⃣ Open a PR and let a job fail

On the next failing run, naoru posts a 🩺 comment with the root cause and a suggested fix. Done.

⚠️ The permissions block is required — without actions: read naoru can't fetch logs, and without pull-requests: write it can't comment.

💡 Want naoru to go further? fix-mode: suggest posts one-click suggestion comments, fix-mode: pr opens a fix PR — see Auto-fix.

📄 Full examples: examples/usage.yml (per-workflow job) · examples/watcher.yml (one workflow watching all others)


🖼️ What you get

A single comment on the PR, updated in place every run:

🩺 naoru

Failed job: build

Root cause: Type error in src/auth.ts:42user is possibly undefined.

Suggested fix:

- if (user.id)
+ if (user?.id)

Confidence: High · react 👍 / 👎


🤖 Pick your LLM

naoru talks to Anthropic natively and to everything else through the OpenAI-compatible API (driven by base-url). Just set provider — override model/base-url when you want.

provider default model endpoint key secret (example)
anthropic claude-sonnet-4-6 Anthropic SDK ANTHROPIC_API_KEY
openai gpt-4o https://api.openai.com/v1 OPENAI_API_KEY
gemini gemini-3.6-flash https://generativelanguage.googleapis.com/v1beta/openai GEMINI_API_KEY
openrouter openai/gpt-4o https://openrouter.ai/api/v1 OPENROUTER_API_KEY
xai grok-2 https://api.x.ai/v1 XAI_API_KEY
groq llama-3.3-70b-versatile https://api.groq.com/openai/v1 GROQ_API_KEY
custom set base-url any
# Example: cheap & fast with Groq
- uses: clouddrove/naoru@v0
  with:
    api-key: ${{ secrets.GROQ_API_KEY }}
    provider: groq

🐳 Run it outside GitHub Actions (Docker / GitLab dind / Jenkins / local)

The same engine ships as a node:24-alpine image — ghcr.io/clouddrove/naoru — so it runs anywhere, including docker-in-docker runners. It reads the failed-job log from --log-file/stdin and prints the diagnosis. Give it --repo + --pr + GITHUB_TOKEN and it also posts the GitHub PR comment.

# Pipe a failed build log straight into the container
cat build.log | docker run -i \
  -e NAORU_API_KEY=$LLM_KEY \
  -e NAORU_PROVIDER=anthropic \
  ghcr.io/clouddrove/naoru:latest
GitLab CI (docker-in-docker)
naoru:
  stage: .post
  image: ghcr.io/clouddrove/naoru:latest
  when: on_failure
  variables:
    NAORU_PROVIDER: xai
    NAORU_MODEL: grok-2
  script:
    - cat build.log | node /app/dist-cli/index.js
  # NAORU_API_KEY set as a masked CI/CD variable (Settings → CI/CD → Variables)

See examples/gitlab-dind.yml.

CLI env vars / flags
env flag meaning
NAORU_API_KEY --api-key LLM key
NAORU_PROVIDER --provider provider preset (default anthropic)
NAORU_MODEL --model model id
NAORU_BASE_URL --base-url endpoint override
NAORU_LOG_FILE --log-file path to failed-job log (else read stdin)
NAORU_DIFF_FILE --diff-file path to diff (optional)
NAORU_JOB_NAME --job-name name of the failed job (default pipeline)
NAORU_PROMPT --prompt override the model instructions (optional)
NAORU_MAX_TOKENS --max-tokens cap the model response (default 2048)
NAORU_REDACT_PATTERNS --redact-patterns extra regexes to scrub, one per line
NAORU_MAX_LOG_LINES --max-log-lines tail N lines of log (default 500)
GITHUB_TOKEN --github-token post a comment (optional)
--repo owner/name GitHub repo for comment (optional)
--pr N PR number for comment (optional)

With no GitHub target, the CLI prints the diagnosis to stdout and exits 0.


⚙️ Action reference

Inputs (mirrors action.yml)
input required default description
api-key LLM API key (provider-specific).
provider anthropic anthropic | openai | gemini | openrouter | xai | groq | custom
base-url per-provider Override endpoint. Required when provider: custom.
model per-provider Model id.
github-token ${{ github.token }} Token for reading logs and posting comments.
max-log-lines 500 Tail this many log lines.
failed-job-name auto-detect Explicit failed job name.
prompt built-in Override the model instructions — see Custom prompt.
max-tokens 2048 Cap the model response. Too low truncates the diff mid-hunk and disables fix-mode.
redact-patterns Extra regexes (one per line) scrubbed before anything reaches the model — see What leaves your runner.
protected-paths Extra regexes (one per line) fix-mode must never modify.
fix-mode off off | suggest | pr — see Auto-fix.
Outputs
output description
root-cause Diagnosed root cause.
confidence high | medium | low
comment-url URL of the posted/updated PR comment.
fix-pr-url URL of the auto-opened fix PR (fix-mode: pr only).
tokens-input Prompt tokens billed for the diagnosis (empty if the provider reports none).
tokens-output Completion tokens billed.
tokens-total Total tokens billed.

🔒 What leaves your runner

naoru sends the tailed job log and the PR diff to the LLM provider you configured. Before that happens it scrubs the recognisable secret shapes — PEM private key blocks, JWTs, Bearer headers, GitHub/OpenAI/Slack/Google tokens, AWS key IDs, credentials embedded in connection strings, and labelled assignments like API_KEY=….

GitHub already masks secrets it knows about. Redaction covers the rest: a token that arrived in an API response, a printed environment, a connection string in a stack trace.

Add your own shapes with redact-patterns (one regex per line):

- uses: clouddrove/naoru@v0
  if: failure()
  with:
    api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    redact-patterns: |
      ACME-[0-9]{4,}
      internal\.example\.com

Every diagnosis also reports what it cost — tokens-input / tokens-output / tokens-total outputs, a 🧮 line on the PR comment, and a run annotation.

⚠️ Never run naoru with fix-mode on pull_request_target for fork PRs. That combination hands a write-scoped token to a workflow whose code the PR author controls. naoru detects it and refuses to write, diagnosing only — but the safe trigger is pull_request.


✍️ Custom prompt

The prompt input replaces naoru's built-in instructions, so you can change the tone or length, or teach it something about your repo:

- uses: clouddrove/naoru@v0
  if: failure()
  with:
    api-key: ${{ secrets.ANTHROPIC_API_KEY }}
    prompt: |
      You are a blunt SRE. Diagnose the failure in under 80 words, no pleasantries.
      This repo uses pnpm, not npm — never suggest an npm command.

naoru always appends the failed job name, changed files, PR diff, job logs, and the diff-format rules that fix-mode relies on. So an override changes how the model answers, and can't accidentally break auto-fix. The same thing works from the CLI via --prompt / NAORU_PROMPT.


🔧 Auto-fix (experimental)

naoru can go beyond diagnosing and act on the fix. Opt in with fix-mode:

- uses: clouddrove/naoru@v0
  if: failure()
  with:
    api-key: ${{ secrets.NAORU_API_KEY }}
    fix-mode: suggest   # or: pr
mode what happens extra permissions
suggest Posts one-click GitHub suggestion review comments on the PR, one per hunk. pull-requests: write
pr Applies the diff on a naoru/fix-<run-id> branch and opens a PR targeting the failing branch (labelled naoru-fix). Never pushes to your branch. contents: write, pull-requests: write

Safety rails, always on:

  • Acts only on high-confidence diagnoses that include a diff.
  • Refuses to write on a fork pull_request_target run, where the token is write-scoped but the PR head is not yours.
  • Never touches files that execute during checkout, install, or build — .github/, Dockerfile, Makefile, .npmrc, .yarnrc, shell scripts — nor absolute or .. paths. Opening a fix PR makes CI run that branch, and CI logs are untrusted input. Extend the list with protected-paths.
  • package.json is deliberately not blocked, since a dependency-version fix is one of the most common legitimate repairs. Add ^package\.json$ to protected-paths if you'd rather it were.
  • Caps patches at 300 changed lines; refuses ambiguous or non-matching hunks rather than guessing.
  • Skips runs on naoru's own naoru/fix-* branches, so a failing fix can't loop.
  • Best-effort: any fix error is a warning, the diagnosis still ships, your pipeline is never blocked.

⚠️ fix-mode: pr also needs the repo setting Settings → Actions → General → "Allow GitHub Actions to create and approve pull requests" enabled, or PR creation fails with "GitHub Actions is not permitted to create or approve pull requests".

🤖 Comments and fix PRs are authored by whatever identity github-token carries — the default token shows as github-actions[bot]. To have them authored as naoru, create a GitHub App with that name and pass its installation token as github-token.


💡 Good to know

  • Sticky comment — naoru embeds a hidden marker (<!-- naoru -->) and updates the same comment in place every run, so PRs never fill with duplicate diagnoses.
  • Cost — each diagnosis is a single LLM call over the tailed logs (max-log-lines, default 500) plus the PR diff. Want it cheaper? Use provider: groq or openai with model: gpt-4o-mini, and keep max-log-lines modest.
  • Fail-safe — if the LLM call, log fetch, or comment post errors, naoru emits a warning (Action) or stderr line (CLI) and exits 0. A broken diagnostician should never block your pipeline.

🤝 Contributing

Issues and PRs welcome. npm ci && npm test runs the suite; the CI gate also checks the bundles still build (npm run build && npm run build:cli). dist/ and dist-cli/ are gitignored — the release workflow builds them on a tag push and commits them onto the release tag, which is what uses: clouddrove/naoru@v0 resolves.

📜 License

Apache-2.0 © 2026 CloudDrove Inc.

Built with 🩺 by CloudDrove — we build and run cloud infrastructure.

About

Heal your broken CI with AI — diagnoses why your pipeline failed and comments the fix on your PR.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages