Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 300 additions & 0 deletions .github/workflows/dev-lead-reusable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
name: Dev-Lead Agent (Reusable)

on:
workflow_call:
inputs:
event_name:
description: "GitHub event name (passed by caller for ci-relay routing)"
type: string
required: false
default: ""
secrets:
CLAUDE_CODE_OAUTH_TOKEN:
required: true
GH_PAT_WORKFLOWS:
# Required for cross-repo callers: the default github.token cannot read
# petry-projects/.github-private from another repo. Callers must pass a
# PAT that has read access to petry-projects/.github-private.
required: false
Comment on lines +14 to +18
GOOGLE_API_KEY:
required: false
GH_PAT:
required: false

permissions: {}

jobs:
# ── dispatch ────────────────────────────────────────────────────────────────
# Handles all events except check_run (which is routed by the caller).
# 1. Checks out .github-private scripts/prompts into .dev-lead/
# 2. Runs dev-lead-intent.sh to classify the event into an intent.
# 3. Routes to the appropriate handler based on intent.
dispatch:
if: >-
(inputs.event_name == '' && github.event_name != 'check_run') ||
(inputs.event_name != '' && inputs.event_name != 'check_run')
Comment on lines +33 to +35

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore CI relay for check_run callers

For any caller that triggers this reusable workflow on check_run events, github.event_name is check_run, so this condition skips the only job in the file. Unlike the non-reusable workflow, this new reusable does not define a ci-relay job to emit the dev-lead-ci-failure repository_dispatch event, so failing checks never reach the fix-ci handler. Include the relay in the reusable, or do not skip check_run unless a real caller-side relay has already converted it.

Useful? React with 👍 / 👎.

runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
issues: write
actions: read
checks: read
env:
BOT_USER: ${{ vars.BOT_USER || 'donpetry-bot' }}
TRUSTED_BOTS: ${{ vars.TRUSTED_BOTS || 'copilot-pull-request-reviewer[bot],gemini-code-assist[bot],sonarqubecloud[bot],coderabbitai[bot]' }}
TRIGGER_PHRASES: ${{ vars.TRIGGER_PHRASES || '@dev-lead' }}
DEV_LEAD_ENGINE: ${{ vars.DEV_LEAD_ENGINE || 'claude' }}
DEV_LEAD_DRY_RUN: ${{ vars.DEV_LEAD_DRY_RUN || 'false' }}
GH_TOKEN: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}
DEV_LEAD_SCRIPTS: .dev-lead/scripts
DEV_LEAD_PROMPTS: .dev-lead/prompts/dev-lead

steps:
- name: Checkout caller repo
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
token: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}

- name: Checkout dev-lead scripts
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
repository: petry-projects/.github-private
ref: main
path: .dev-lead

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the helper checkout out of commits

Checking the scripts repo out under the caller worktree leaves .dev-lead as an unignored nested git repository. On reusable runs that actually apply fixes, the existing handlers later stage with git add -A (for example the fix-ci and issue paths), so the generated commit can include .dev-lead as an embedded gitlink/submodule instead of only the intended code changes. Put this checkout outside the caller repository or exclude .dev-lead before any handler can stage changes.

Useful? React with 👍 / 👎.

# Cross-repo callers: github.token cannot read a private org repo — ensure
# GH_PAT_WORKFLOWS is set as an org or repo secret in the calling repo.
token: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}
Comment on lines +63 to +68

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require a token that can read the reusable repo

When this reusable workflow is called from any other private repository without GH_PAT_WORKFLOWS, this checkout falls back to the caller repo's github.token; actions/checkout scopes that token to the current/caller repository, so it cannot read petry-projects/.github-private and the workflow fails before intent classification. Since the workflow is explicitly meant for other repos and the secret is marked optional, callers can configure the reusable exactly as advertised but still fail at this step; make the secret required or otherwise provide a token with access to .github-private for this checkout.

Useful? React with 👍 / 👎.

sparse-checkout: |
scripts
prompts/dev-lead
Comment on lines +60 to +71

- name: Protect dev-lead scripts from accidental git staging
run: echo ".dev-lead/" >> .gitignore

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid committing the helper ignore entry

When any non-skip handler later stages with git add -A, this line has already modified or created the caller repository's tracked working tree. If the engine makes no edits, handlers such as fix-ci can still see a dirty tree and commit only this .gitignore change; otherwise every automated fix carries an unrelated ignore entry. Put the helper checkout in .git/info/exclude or outside the caller worktree instead of editing the repository file.

Useful? React with 👍 / 👎.


- name: Classify event intent
id: intent
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
run: bash .dev-lead/scripts/dev-lead-intent.sh

- name: Log intent
run: |
echo "::notice::intent=$INTENT_TYPE reason=$INTENT_REASON"

- name: Skip — log reason
if: env.INTENT_TYPE == 'skip'
run: |
echo "::notice::Skipping event: $INTENT_REASON"

- name: Pre-flight checks
if: env.INTENT_TYPE != 'skip'
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GH_PAT_WORKFLOWS: ${{ secrets.GH_PAT_WORKFLOWS }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GH_PAT: ${{ secrets.GH_PAT }}
run: bash .dev-lead/scripts/dev-lead-preflight.sh

# ── Install engine CLIs ───────────────────────────────────────────────
- name: Cache claude-code CLI
if: env.INTENT_TYPE != 'skip'
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.npm-global
key: claude-code-${{ vars.CLAUDE_CODE_VERSION || 'latest' }}-${{ runner.os }}

- name: Install engine CLIs
if: env.INTENT_TYPE != 'skip'
env:
CLAUDE_CODE_VERSION: ${{ vars.CLAUDE_CODE_VERSION || 'latest' }}
run: |
set -euo pipefail
mkdir -p "$HOME/.npm-global"
npm config set prefix "$HOME/.npm-global"
echo "$HOME/.npm-global/bin" >> "$GITHUB_PATH"
export PATH="$HOME/.npm-global/bin:$PATH"

if ! command -v claude >/dev/null 2>&1; then
npm install -g "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}"

Check warning on line 123 in .github/workflows/dev-lead-reusable.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Using dependencies without locking resolved versions is security-sensitive.

See more on https://sonarcloud.io/project/issues?id=petry-projects_.github-private&issues=AZ4re5lKdsKbhSLG_qdJ&open=AZ4re5lKdsKbhSLG_qdJ&pullRequest=179

Check warning on line 123 in .github/workflows/dev-lead-reusable.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Omitting "--ignore-scripts" can lead to the execution of shell scripts. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=petry-projects_.github-private&issues=AZ4re5lKdsKbhSLG_qdI&open=AZ4re5lKdsKbhSLG_qdI&pullRequest=179
fi

case "$DEV_LEAD_ENGINE" in
gemini)
npm install -g @google/gemini-cli || true ;;

Check warning on line 128 in .github/workflows/dev-lead-reusable.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Using dependencies without locking resolved versions is security-sensitive.

See more on https://sonarcloud.io/project/issues?id=petry-projects_.github-private&issues=AZ4re5lKdsKbhSLG_qdL&open=AZ4re5lKdsKbhSLG_qdL&pullRequest=179

Check warning on line 128 in .github/workflows/dev-lead-reusable.yml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Omitting "--ignore-scripts" can lead to the execution of shell scripts. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=petry-projects_.github-private&issues=AZ4re5lKdsKbhSLG_qdK&open=AZ4re5lKdsKbhSLG_qdK&pullRequest=179
copilot)
if ! gh copilot --version >/dev/null 2>&1; then
gh extension install github/gh-copilot || true
fi ;;
esac

# ── fix-ci ────────────────────────────────────────────────────────────
- name: Run fix-ci
if: env.INTENT_TYPE == 'fix-ci'
env:
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
HEAD_SHA: ${{ fromJson(env.INTENT_CONTEXT).head_sha }}
CHECKS_JSON: ${{ toJson(fromJson(env.INTENT_CONTEXT).checks) }}
REPO: ${{ github.repository }}
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-ci.sh

# ── fix-reviews ───────────────────────────────────────────────────────
- name: Run fix-reviews
if: env.INTENT_TYPE == 'fix-reviews'
env:
INTENT_TYPE: fix-reviews
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
HEAD_SHA: ${{ fromJson(env.INTENT_CONTEXT).head_sha }}
REPO: ${{ github.repository }}
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Push review fixes back to the PR branch

For review/comment intents that route to this script, the reusable workflow has only performed the generic caller checkout, and dev-lead-fix-reviews.sh does not check out the PR head or push after the writer commits. On pull_request_review, pull_request_review_comment, or issue_comment runs, successful fixes are therefore left as local runner commits and never update the PR, unlike the fix-ci path which explicitly gh pr checkouts and pushes. Check out the PR branch and push after a successful handler run.

Useful? React with 👍 / 👎.


# ── fix-bot-comment ───────────────────────────────────────────────────
- name: Run fix-bot-comment
if: env.INTENT_TYPE == 'fix-bot-comment'
env:
INTENT_TYPE: fix-bot-comment
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
HEAD_SHA: ${{ fromJson(env.INTENT_CONTEXT).head_sha }}
REPO: ${{ github.repository }}
Comment on lines +169 to +172

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Forward bot comment details to the handler

When a trusted bot leaves a PR issue comment, this step invokes the fix-bot-comment path but only provides the PR number/SHA. The script renders prompts/dev-lead/fix-bot-comment.md from ACTOR and COMMENT_BODY, both of which default to empty here, so the writer is asked to address a blank bot comment and cannot know which finding to fix. Include the triggering comment author/body in the intent context or fetch the comment before running this handler.

Useful? React with 👍 / 👎.

REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh

# ── human ─────────────────────────────────────────────────────────────
- name: Run human
if: env.INTENT_TYPE == 'human'
env:
INTENT_TYPE: human
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
HEAD_SHA: ${{ fromJson(env.INTENT_CONTEXT).head_sha }}
REPO: ${{ github.repository }}
Comment on lines +184 to +187

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the user request into the human handler

For @dev-lead PR comments or review comments, the intent context only carries the PR number/head SHA, and this env block forwards only those fields. scripts/dev-lead-fix-reviews.sh then defaults ACTOR, USER_INSTRUCTION, and PR_DESCRIPTION to empty before rendering prompts/dev-lead/human.md, so the reusable run starts the writer with a blank Instruction block and cannot implement the request that triggered it. Include the comment body/actor in the context or fetch them before invoking the handler.

Useful? React with 👍 / 👎.

REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh

# ── human-pr ──────────────────────────────────────────────────────────
- name: Run human-pr
if: env.INTENT_TYPE == 'human-pr'
env:
INTENT_TYPE: human-pr
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
HEAD_SHA: ${{ fromJson(env.INTENT_CONTEXT).head_sha }}
REPO: ${{ github.repository }}
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh

# ── issue ─────────────────────────────────────────────────────────────
- name: Run issue
if: env.INTENT_TYPE == 'issue'
env:
ISSUE_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).issue_number }}
REPO: ${{ github.repository }}
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-issue.sh

# ── rebase ────────────────────────────────────────────────────────────
- name: Run rebase
if: env.INTENT_TYPE == 'rebase'
env:
INTENT_TYPE: rebase
PR_NUMBER: ${{ fromJson(env.INTENT_CONTEXT).pr_number }}
REPO: ${{ github.repository }}
REVIEW_ENGINE: ${{ env.DEV_LEAD_ENGINE }}
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
PROMPTS_DIR: .dev-lead/prompts/dev-lead
run: bash .dev-lead/scripts/dev-lead-fix-reviews.sh

# ── ci-relay ──────────────────────────────────────────────────────────────────
# Handles check_run completed failure events. No LLM — resolves PR number and
# fires repository_dispatch dev-lead-ci-failure back to the calling repo.
ci-relay:
if: >-
github.event_name == 'check_run' &&
github.event.action == 'completed' &&
github.event.check_run.conclusion == 'failure' &&
!startsWith(github.event.check_run.name, 'dev-lead / ')
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read

Comment on lines +248 to +250

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Grant the CI relay token write access

For reusable runs invoked by a caller's check_run event without a PAT in GH_PAT_WORKFLOWS, this job falls back to github.token and then posts to repos/$RELAY_REPO/dispatches; GitHub's Create repository dispatch endpoint requires the token's Contents permission to be write, but this job restricts it to read, so the relay fails with a 403 before the fix-ci path can run. The non-reusable workflow inherits contents: write, so mirror that here or require a PAT for the relay.

Useful? React with 👍 / 👎.

steps:
- name: Check for associated non-fork PR
id: check-pr
env:
CHECK_RUN_PRS: ${{ toJson(github.event.check_run.pull_requests) }}
REPO_FULL_NAME: ${{ github.repository }}
run: |
set -euo pipefail
pr_count=$(echo "$CHECK_RUN_PRS" | jq 'length')
if [ "$pr_count" -eq 0 ]; then
echo "::notice::check_run has no associated PRs — skipping relay"
echo "should_relay=false" >> "$GITHUB_OUTPUT"
exit 0
fi
head_repo_url=$(echo "$CHECK_RUN_PRS" | jq -r '.[0].head.repo.url // empty')
expected_url="https://api.github.com/repos/${REPO_FULL_NAME}"
if [ -n "$head_repo_url" ] && [ "$head_repo_url" != "$expected_url" ]; then
echo "::notice::check_run is from a fork — skipping relay"
echo "should_relay=false" >> "$GITHUB_OUTPUT"
exit 0
fi
pr_number=$(echo "$CHECK_RUN_PRS" | jq -r '.[0].number')
echo "should_relay=true" >> "$GITHUB_OUTPUT"
echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT"

- name: Emit repository_dispatch dev-lead-ci-failure
if: steps.check-pr.outputs.should_relay == 'true'
env:
GH_TOKEN: ${{ secrets.GH_PAT_WORKFLOWS || github.token }}
RELAY_PR_NUMBER: ${{ steps.check-pr.outputs.pr_number }}
RELAY_HEAD_SHA: ${{ github.event.check_run.head_sha }}
RELAY_REPO: ${{ github.repository }}
RELAY_CHECK_NAME: ${{ github.event.check_run.name }}
RELAY_CONCLUSION: ${{ github.event.check_run.conclusion }}
RELAY_DETAILS_URL: ${{ github.event.check_run.details_url }}
RELAY_APP_SLUG: ${{ github.event.check_run.app.slug }}
run: |
set -euo pipefail
payload=$(jq -n \
--argjson pr_number "$RELAY_PR_NUMBER" \
--arg head_sha "$RELAY_HEAD_SHA" \
--arg repo "$RELAY_REPO" \
--arg name "$RELAY_CHECK_NAME" \
--arg conclusion "$RELAY_CONCLUSION" \
--arg details_url "$RELAY_DETAILS_URL" \
--arg app_slug "$RELAY_APP_SLUG" \
'{event_type:"dev-lead-ci-failure",client_payload:{pr_number:$pr_number,head_sha:$head_sha,repo:$repo,checks:[{name:$name,conclusion:$conclusion,details_url:$details_url,app_slug:$app_slug}]}}')
echo "$payload" | gh api \
--method POST "repos/$RELAY_REPO/dispatches" --input -
echo "::notice::Relayed CI failure for PR $RELAY_PR_NUMBER"
2 changes: 2 additions & 0 deletions .github/workflows/dev-lead.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: Dev-Lead Agent
# Phase 7 shadow period: dev-lead.yml and claude.yml run in parallel until ~2026-05-29.
# Tracking: petry-projects/.github-private#180

on:
pull_request:
Expand Down
4 changes: 3 additions & 1 deletion scripts/dev-lead-fix-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -euo pipefail
# Called when a CI check fails on a PR.
# Env: PR_NUMBER, HEAD_SHA, CHECKS_JSON, REPO, GITHUB_REPOSITORY,
# DEV_LEAD_DRY_RUN, REVIEW_ENGINE, GH_TOKEN
# Optional: PROMPTS_DIR (defaults to prompts/dev-lead relative to CWD)

source "$(dirname "$0")/engine.sh"

Expand All @@ -14,6 +15,7 @@ REPO="${REPO:-${GITHUB_REPOSITORY:-}}"
MAX_CI_CYCLES="${MAX_CI_CYCLES:-3}"
LOG_MAX_LINES="${LOG_MAX_LINES:-200}"
MARKER_PREFIX="<!-- dev-lead-fix-ci sha="
export PROMPTS_DIR="${PROMPTS_DIR:-prompts/dev-lead}"

check_idempotency() {
local existing
Expand All @@ -35,7 +37,7 @@ collect_logs() {
}

build_prompt() {
local prompt_template="prompts/dev-lead/fix-ci.md"
local prompt_template="${PROMPTS_DIR}/fix-ci.md"
local check_name app_slug details_url
check_name=$(echo "$CHECKS_JSON" | jq -r '.[0].name // "unknown"')
app_slug=$(echo "$CHECKS_JSON" | jq -r '.[0].app_slug // "github-actions"')
Expand Down
4 changes: 3 additions & 1 deletion scripts/dev-lead-fix-issue.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
# dev-lead-fix-issue.sh — handles the issue intent
# Optional: PROMPTS_DIR (defaults to prompts/dev-lead relative to CWD)

source "$(dirname "$0")/engine.sh"

ISSUE_NUMBER="${ISSUE_NUMBER:-}"
REPO="${REPO:-${GITHUB_REPOSITORY:-}}"
DEV_LEAD_DRY_RUN="${DEV_LEAD_DRY_RUN:-false}"
export PROMPTS_DIR="${PROMPTS_DIR:-prompts/dev-lead}"

check_existing_pr() {
local existing
Expand Down Expand Up @@ -37,7 +39,7 @@ main() {
export ISSUE_TITLE ISSUE_BODY ORG_STANDARDS_HINT

local prompt_file="/tmp/dev-lead-fix-issue-prompt-$$.md"
envsubst < "prompts/dev-lead/fix-issue.md" > "$prompt_file"
envsubst < "${PROMPTS_DIR}/fix-issue.md" > "$prompt_file"

if [ "$DEV_LEAD_DRY_RUN" = "true" ]; then
echo "[dry-run] fix-issue: would implement issue #${ISSUE_NUMBER} using prompt: $prompt_file"
Expand Down
4 changes: 3 additions & 1 deletion scripts/dev-lead-fix-reviews.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
# dev-lead-fix-reviews.sh — handles review-related intents
# Optional: PROMPTS_DIR (defaults to prompts/dev-lead relative to CWD)

source "$(dirname "$0")/engine.sh"

Expand All @@ -9,6 +10,7 @@ PR_NUMBER="${PR_NUMBER:-}"
REPO="${REPO:-${GITHUB_REPOSITORY:-}}"
HEAD_SHA="${HEAD_SHA:-}"
DEV_LEAD_DRY_RUN="${DEV_LEAD_DRY_RUN:-false}"
export PROMPTS_DIR="${PROMPTS_DIR:-prompts/dev-lead}"

if [ -z "$PR_NUMBER" ] && [ "$INTENT_TYPE" != "rebase" ]; then
echo "::error::PR_NUMBER is required"
Expand All @@ -19,7 +21,7 @@ build_and_run() {
local template_name="$1"
local prompt_file="/tmp/dev-lead-${template_name}-prompt-$$.md"
# Export required vars then envsubst
envsubst < "prompts/dev-lead/${template_name}.md" > "$prompt_file"
envsubst < "${PROMPTS_DIR}/${template_name}.md" > "$prompt_file"

if [ "$DEV_LEAD_DRY_RUN" = "true" ]; then
echo "[dry-run] would run engine with prompt: $prompt_file ($(wc -l < "$prompt_file") lines)"
Expand Down
Loading