mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-23 00:01:24 -07:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 88a12098d4 | |||
| 4293ddfcc7 | |||
| 53a573d7ef | |||
| 6b6f0a3655 | |||
| 3795f40cf6 | |||
| 67b46ee365 | |||
| ad612d9311 | |||
| 00aaba6759 | |||
| 48e6cc179e | |||
| 746be830fe | |||
| f65cacfea4 | |||
| 8d6b0effc3 | |||
| 6efdbd3e48 | |||
| a2841350ba | |||
| 928ce879ea | |||
| 5dc5b4ed4a | |||
| 98781cd97d | |||
| daaa631071 | |||
| 506184d739 | |||
| 9b3fef4f68 | |||
| 83d15895f1 | |||
| 03877eae3b | |||
| d17a813cc3 | |||
| a6e460e595 | |||
| 4449f3f43c | |||
| 7789469bd1 | |||
| 4464ff23fc | |||
| 8a6ec5978c | |||
| c460745bcb | |||
| 278858ed11 | |||
| a58e3f5654 | |||
| 821cb2be9b | |||
| 3250033366 | |||
| 7344507c7b | |||
| e9df8d2914 | |||
| 9e90ccefb4 | |||
| c1b15d23b6 | |||
| 20669e964c | |||
| c0e78767b7 | |||
| 75ab39ca3a | |||
| a11f347928 |
@@ -2,7 +2,8 @@ name: '🧠 Gemini CLI Bot: Brain'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Every 24 hours
|
||||
- cron: '0 0 * * *' # Nightly (Strategic Metrics)
|
||||
- cron: '0 */4 * * *' # Every 4 hours (Issue Fixing)
|
||||
issue_comment:
|
||||
types: ['created']
|
||||
workflow_dispatch:
|
||||
@@ -26,7 +27,16 @@ on:
|
||||
enable_prs:
|
||||
description: 'Enable PRs (automatically promote changes to PRs)'
|
||||
type: 'boolean'
|
||||
default: false
|
||||
default: true
|
||||
mandate:
|
||||
description: 'Mandate to execute'
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'auto'
|
||||
- 'issue-fixer'
|
||||
- 'metrics'
|
||||
- 'interactive'
|
||||
default: 'auto'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || github.ref }}'
|
||||
@@ -36,11 +46,11 @@ jobs:
|
||||
reasoning:
|
||||
name: 'Brain (Reasoning Layer)'
|
||||
runs-on: 'ubuntu-latest'
|
||||
timeout-minutes: 60
|
||||
if: |
|
||||
github.repository == 'google-gemini/gemini-cli' && (
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive != 'true') ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive == 'true') ||
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event_name == 'issue_comment' && github.event.comment.user.login != 'gemini-cli[bot]' && contains(github.event.comment.body, '@gemini-cli') && contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association))
|
||||
)
|
||||
# The reasoning phase is strictly readonly.
|
||||
@@ -48,45 +58,50 @@ jobs:
|
||||
contents: 'read'
|
||||
issues: 'read'
|
||||
actions: 'read'
|
||||
outputs:
|
||||
sha: ${{ steps.get_sha.outputs.sha }}
|
||||
env:
|
||||
GEMINI_CLI_TRUST_WORKSPACE: 'true'
|
||||
steps:
|
||||
- name: 'Determine Checkout Ref'
|
||||
id: 'determine_ref'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
run: |
|
||||
REF="${{ github.ref }}"
|
||||
if [ -n "$ISSUE_NUMBER" ]; then
|
||||
PR_HEAD=$(gh pr view "$ISSUE_NUMBER" --repo "${{ github.repository }}" --json headRefName --jq .headRefName 2>/dev/null || echo "")
|
||||
if [ -n "$PR_HEAD" ]; then
|
||||
REF="$PR_HEAD"
|
||||
fi
|
||||
fi
|
||||
echo "ref=$REF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Checkout'
|
||||
- name: 'Checkout Branch (Agent Code)'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ steps.determine_ref.outputs.ref }}'
|
||||
ref: '${{ github.ref }}'
|
||||
path: 'agent-code'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Get Current SHA'
|
||||
id: 'get_sha'
|
||||
working-directory: agent-code
|
||||
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'agent-code/package-lock.json'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
working-directory: agent-code
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build Gemini CLI'
|
||||
working-directory: agent-code
|
||||
run: 'npm run bundle'
|
||||
|
||||
- name: 'Checkout Main (Target Repo)'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: 'main'
|
||||
path: 'repo-target'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Download Previous State'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
if [ "${{ github.event.inputs.clear_memory }}" = "true" ]; then
|
||||
@@ -95,19 +110,19 @@ jobs:
|
||||
fi
|
||||
|
||||
# Find the last successful run of this workflow
|
||||
LAST_RUN_ID=$(gh run list --workflow "${{ github.workflow }}" --status success --limit 1 --json databaseId --jq '.[0].databaseId')
|
||||
LAST_RUN_ID=$(gh run list -R "${{ github.repository }}" --workflow "${{ github.workflow }}" --status success --limit 1 --json databaseId --jq '.[0].databaseId')
|
||||
|
||||
if [ -n "$LAST_RUN_ID" ]; then
|
||||
echo "Found previous successful run: $LAST_RUN_ID"
|
||||
|
||||
# Download brain memory to a temp dir so we can selectively restore only persistent state
|
||||
mkdir -p .temp_brain_data
|
||||
gh run download "$LAST_RUN_ID" -n brain-data -D .temp_brain_data || echo "brain-data not found"
|
||||
gh run download "$LAST_RUN_ID" -R "${{ github.repository }}" -n brain-data -D .temp_brain_data || echo "brain-data not found"
|
||||
|
||||
# Restore only persistent memory files
|
||||
cp .temp_brain_data/tools/gemini-cli-bot/lessons-learned.md tools/gemini-cli-bot/lessons-learned.md 2>/dev/null || true
|
||||
mkdir -p tools/gemini-cli-bot/history/
|
||||
cp .temp_brain_data/tools/gemini-cli-bot/history/*.csv tools/gemini-cli-bot/history/ 2>/dev/null || true
|
||||
cp .temp_brain_data/tools/gemini-cli-bot/lessons-learned.md repo-target/tools/gemini-cli-bot/lessons-learned.md 2>/dev/null || true
|
||||
mkdir -p repo-target/tools/gemini-cli-bot/history/
|
||||
cp .temp_brain_data/tools/gemini-cli-bot/history/*.csv repo-target/tools/gemini-cli-bot/history/ 2>/dev/null || true
|
||||
rm -rf .temp_brain_data
|
||||
else
|
||||
echo "No previous successful run found."
|
||||
@@ -115,25 +130,62 @@ jobs:
|
||||
|
||||
- name: 'Collect Current Metrics'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
working-directory: agent-code
|
||||
run: 'npx tsx tools/gemini-cli-bot/metrics/index.ts'
|
||||
|
||||
- name: 'Run Brain Phases'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GEMINI_MODEL: 'gemini-3-flash-preview'
|
||||
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
|
||||
ENABLE_PRS: "${{ github.event.inputs.enable_prs || 'false' }}"
|
||||
GEMINI_CLI_HOME: '../agent-code/tools/gemini-cli-bot'
|
||||
ENABLE_PRS: "${{ github.event.inputs.enable_prs || 'true' }}"
|
||||
TRIGGER_ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
TRIGGER_COMMENT_ID: '${{ github.event.comment.id || github.event.inputs.comment_id }}'
|
||||
# Enable detailed activity logging for debugging
|
||||
GEMINI_TELEMETRY_ENABLED: 'true'
|
||||
GEMINI_TELEMETRY_LOG_PROMPTS: 'true'
|
||||
GEMINI_TELEMETRY_OUTFILE: 'brain-telemetry.json'
|
||||
GEMINI_DEBUG_LOG_FILE: 'brain-debug.log'
|
||||
GH_PAGER: ''
|
||||
working-directory: repo-target
|
||||
run: |
|
||||
PROMPT_PATH="tools/gemini-cli-bot/brain/scheduled.md"
|
||||
if [ "${{ github.event_name }}" = "issue_comment" ] || [ "${{ github.event.inputs.run_interactive }}" = "true" ]; then
|
||||
PROMPT_PATH="tools/gemini-cli-bot/brain/interactive.md"
|
||||
export ENABLE_PRS="true"
|
||||
# Determine intent and prompt
|
||||
MANDATE_INPUT="${{ github.event.inputs.mandate || 'auto' }}"
|
||||
|
||||
# Initialize defaults
|
||||
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/scheduled.md"
|
||||
MANDATE="Your specific mandate for this run: Implement surgical fixes for repository issues (issue-fixer skill)."
|
||||
|
||||
# Resolve Mandate and Prompt File
|
||||
if [ "$MANDATE_INPUT" = "issue-fixer" ]; then
|
||||
echo "Trigger: Manual Override (issue-fixer)"
|
||||
MANDATE="Your specific mandate for this run: Implement surgical fixes for repository issues (issue-fixer skill)."
|
||||
elif [ "$MANDATE_INPUT" = "metrics" ]; then
|
||||
echo "Trigger: Manual Override (metrics)"
|
||||
MANDATE="Your specific mandate for this run: Analyze repository metrics to identify bottlenecks and self-evolve (metrics skill)."
|
||||
elif [ "$MANDATE_INPUT" = "interactive" ]; then
|
||||
echo "Trigger: Manual Override (interactive)"
|
||||
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/interactive.md"
|
||||
MANDATE="Your specific mandate for this run: Respond to the user request in <untrusted_context>."
|
||||
elif [ "${{ github.event_name }}" = "issue_comment" ] || [ "${{ github.event.inputs.run_interactive }}" = "true" ]; then
|
||||
echo "Trigger: Issue/PR Comment or Interactive Dispatch"
|
||||
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/interactive.md"
|
||||
MANDATE="Your specific mandate for this run: Respond to the user request in <untrusted_context>."
|
||||
elif [ "${{ github.event.schedule }}" = "0 0 * * *" ]; then
|
||||
echo "Trigger: Nightly Schedule (Metrics)"
|
||||
MANDATE="Your specific mandate for this run: Analyze repository metrics to identify bottlenecks and self-evolve (metrics skill)."
|
||||
else
|
||||
echo "Trigger: Scheduled or Manual Dispatch (Default: Issue-Fixer)"
|
||||
fi
|
||||
|
||||
echo "Selected Prompt: $PROMPT_FILE"
|
||||
echo "Selected Mandate: $MANDATE"
|
||||
|
||||
# Prepare Context if available
|
||||
touch trigger_context.md
|
||||
if [ -n "$TRIGGER_ISSUE_NUMBER" ]; then
|
||||
echo "<untrusted_context>" > trigger_context.md
|
||||
@@ -151,16 +203,18 @@ jobs:
|
||||
echo "</untrusted_context>" >> trigger_context.md
|
||||
fi
|
||||
|
||||
if [ "$ENABLE_PRS" = "true" ]; then
|
||||
echo "**System Directive**: PR creation is ENABLED for this run. You MUST activate the **'prs' skill** to stage your changes and generate a \`pr-description.md\` file if you are proposing fixes." >> trigger_context.md
|
||||
echo "**CRITICAL System Directive**: You MUST ONLY propose and implement a **SINGLE** improvement or fix per run. Bundling unrelated changes (e.g., a documentation update and a script fix, or a metrics update and a logic fix) into a single PR is STRICTLY FORBIDDEN and will result in immediate rejection during the critique phase. If you identify multiple issues, pick the most impactful one and ignore the others for now." >> trigger_context.md
|
||||
else
|
||||
echo "**System Directive**: PR creation is DISABLED for this run. You MUST NOT stage files or attempt to create a PR description." >> trigger_context.md
|
||||
# Pass PR Enablement Directive
|
||||
PR_DIRECTIVE="PR creation is DISABLED. You MUST NOT stage files."
|
||||
if [ "${{ github.event.inputs.enable_prs || 'true' }}" = "true" ] || [ "${{ github.event_name }}" = "issue_comment" ]; then
|
||||
PR_DIRECTIVE="PR creation is ENABLED. You MUST activate the 'prs' skill to stage changes if proposing fixes."
|
||||
fi
|
||||
echo "" >> trigger_context.md
|
||||
|
||||
cat trigger_context.md "$PROMPT_PATH" > combined_prompt.md
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat combined_prompt.md)"
|
||||
# Assemble final prompt: Context + Base Brain + Specific Mandate
|
||||
echo "System: $PR_DIRECTIVE" > combined_prompt.md
|
||||
cat trigger_context.md "$PROMPT_FILE" >> combined_prompt.md
|
||||
echo -e "\n\n# MANDATE FOR THIS RUN\n$MANDATE" >> combined_prompt.md
|
||||
|
||||
node ../agent-code/bundle/gemini.js --policy ../agent-code/tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat combined_prompt.md)"
|
||||
|
||||
if [ -n "$TRIGGER_ISSUE_NUMBER" ] && [ ! -s "issue-comment.md" ] && [ ! -s "pr-comment.md" ]; then
|
||||
echo "Agent failed to respond. Generating fallback error message."
|
||||
@@ -171,17 +225,20 @@ jobs:
|
||||
|
||||
- name: 'Run Critique Phase'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
working-directory: repo-target
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GEMINI_MODEL: 'gemini-3-flash-preview'
|
||||
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
|
||||
GEMINI_CLI_HOME: '../agent-code/tools/gemini-cli-bot'
|
||||
GH_PAGER: ''
|
||||
run: |
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes staged. Skipping critique."
|
||||
echo "[APPROVED]" > critique_result.txt
|
||||
else
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat tools/gemini-cli-bot/.gemini/skills/critique/SKILL.md)" 2>&1 | tee critique_output.log
|
||||
node ../agent-code/bundle/gemini.js --policy ../agent-code/tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat ../agent-code/tools/gemini-cli-bot/.gemini/skills/critique/SKILL.md)" 2>&1 | tee critique_output.log
|
||||
|
||||
if [ "${PIPESTATUS[0]}" -eq 0 ] && grep -q "\[APPROVED\]" critique_output.log && ! grep -q "\[REJECTED\]" critique_output.log; then
|
||||
echo "[APPROVED]" > critique_result.txt
|
||||
@@ -193,28 +250,42 @@ jobs:
|
||||
|
||||
- name: 'Generate Patch'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
working-directory: repo-target
|
||||
run: |
|
||||
touch bot-changes.patch
|
||||
touch pr-description.md
|
||||
touch pr-labels.txt
|
||||
touch branch-name.txt
|
||||
touch issue-comment.md
|
||||
touch pr-comment.md
|
||||
if [ -f critique_result.txt ] && grep -q "\[APPROVED\]" critique_result.txt && ! grep -q "\[REJECTED\]" critique_result.txt; then
|
||||
git diff --staged > bot-changes.patch
|
||||
else
|
||||
echo "Critique did not approve. Skipping patch generation."
|
||||
fi
|
||||
|
||||
- name: 'Stage Artifacts'
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p staged-artifacts/tools/gemini-cli-bot/history
|
||||
cp repo-target/tools/gemini-cli-bot/lessons-learned.md staged-artifacts/tools/gemini-cli-bot/ 2>/dev/null || true
|
||||
cp repo-target/tools/gemini-cli-bot/history/*.csv staged-artifacts/tools/gemini-cli-bot/history/ 2>/dev/null || true
|
||||
cp repo-target/brain-telemetry.json staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/brain-debug.log staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/bot-changes.patch staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/pr-description.md staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/branch-name.txt staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/pr-comment.md staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/pr-number.txt staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/issue-comment.md staged-artifacts/ 2>/dev/null || true
|
||||
cp repo-target/pr-labels.txt staged-artifacts/ 2>/dev/null || true
|
||||
|
||||
- name: 'Archive Brain Data'
|
||||
if: always()
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'brain-data'
|
||||
path: |
|
||||
tools/gemini-cli-bot/lessons-learned.md
|
||||
tools/gemini-cli-bot/history/*.csv
|
||||
bot-changes.patch
|
||||
pr-description.md
|
||||
branch-name.txt
|
||||
pr-comment.md
|
||||
pr-number.txt
|
||||
issue-comment.md
|
||||
path: staged-artifacts/
|
||||
retention-days: 90
|
||||
|
||||
publish:
|
||||
@@ -241,25 +312,10 @@ jobs:
|
||||
permission-pull-requests: 'write'
|
||||
permission-issues: 'write'
|
||||
|
||||
- name: 'Determine Checkout Ref'
|
||||
id: 'determine_ref'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
run: |
|
||||
REF="main"
|
||||
if [ -n "$ISSUE_NUMBER" ]; then
|
||||
PR_HEAD=$(gh pr view "$ISSUE_NUMBER" --repo "${{ github.repository }}" --json headRefName --jq .headRefName 2>/dev/null || echo "")
|
||||
if [ -n "$PR_HEAD" ]; then
|
||||
REF="$PR_HEAD"
|
||||
fi
|
||||
fi
|
||||
echo "ref=$REF" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ steps.determine_ref.outputs.ref }}'
|
||||
ref: '${{ needs.reasoning.outputs.sha }}'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
@@ -286,23 +342,20 @@ jobs:
|
||||
fi
|
||||
|
||||
if [[ ! "$BRANCH_NAME" =~ ^bot/ ]]; then
|
||||
echo "Error: Branch name '$BRANCH_NAME' does not start with 'bot/'. Safety abort."
|
||||
exit 1
|
||||
echo "Warning: Branch name '$BRANCH_NAME' does not start with 'bot/'. Prepending 'bot/' for safety."
|
||||
BRANCH_NAME="bot/$BRANCH_NAME"
|
||||
fi
|
||||
|
||||
git checkout -B "$BRANCH_NAME"
|
||||
git apply "${{ runner.temp }}/brain-data/bot-changes.patch"
|
||||
git apply --3way --ignore-whitespace "${{ runner.temp }}/brain-data/bot-changes.patch"
|
||||
git add .
|
||||
|
||||
PR_TITLE="🤖 Gemini Bot Maintenance Update"
|
||||
if [ -s "${{ runner.temp }}/brain-data/pr-description.md" ]; then
|
||||
git commit -F "${{ runner.temp }}/brain-data/pr-description.md"
|
||||
else
|
||||
git commit -m "🤖 Gemini Bot Productivity Optimizations"
|
||||
fi
|
||||
|
||||
PR_TITLE="🤖 Gemini Bot Productivity Optimizations"
|
||||
if [ -s "${{ runner.temp }}/brain-data/pr-description.md" ]; then
|
||||
PR_TITLE=$(head -n 1 "${{ runner.temp }}/brain-data/pr-description.md")
|
||||
else
|
||||
git commit -m "$PR_TITLE"
|
||||
fi
|
||||
|
||||
if ! git push origin "$BRANCH_NAME" --force; then
|
||||
@@ -325,6 +378,14 @@ jobs:
|
||||
gh pr create --draft --title "🤖 Gemini Bot Productivity Optimizations" --body "Automated changes generated by Gemini CLI Bot." --head "$NEW_BRANCH_NAME" --base main
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -s "${{ runner.temp }}/brain-data/pr-labels.txt" ]; then
|
||||
while IFS= read -r label; do
|
||||
if [ -n "$label" ]; then
|
||||
gh pr edit "$BRANCH_NAME" --add-label "$label" || true
|
||||
fi
|
||||
done < "${{ runner.temp }}/brain-data/pr-labels.txt"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: 'Post PR/Issue Comment'
|
||||
|
||||
@@ -18,6 +18,7 @@ jobs:
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Limit open pull requests per user'
|
||||
if: "github.actor != 'gemini-cli[bot]' && github.actor != 'gemini-cli-robot'"
|
||||
uses: 'Homebrew/actions/limit-pull-requests@9ceb7934560eb61d131dde205a6c2d77b2e1529d' # master
|
||||
with:
|
||||
except-author-associations: 'MEMBER,OWNER,COLLABORATOR'
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
handleAtCommand,
|
||||
escapeAtSymbols,
|
||||
unescapeLiteralAt,
|
||||
checkPermissions,
|
||||
} from './atCommandProcessor.js';
|
||||
import {
|
||||
FileDiscoveryService,
|
||||
@@ -1539,4 +1540,23 @@ describe('unescapeLiteralAt', () => {
|
||||
const input = 'user@example.com and @scope/pkg';
|
||||
expect(unescapeLiteralAt(escapeAtSymbols(input))).toBe(input);
|
||||
});
|
||||
|
||||
describe('checkPermissions', () => {
|
||||
it('should handle ENAMETOOLONG gracefully in checkPermissions', async () => {
|
||||
const longPath = 'a'.repeat(5000);
|
||||
const query = `@${longPath}`;
|
||||
|
||||
const localMockConfig = {
|
||||
getTargetDir: () => '.',
|
||||
validatePathAccess: () => true,
|
||||
getResourceRegistry: () => ({
|
||||
findResourceByUri: () => undefined,
|
||||
}),
|
||||
} as unknown as Config;
|
||||
|
||||
// checkPermissions should not throw ENAMETOOLONG
|
||||
const permissions = await checkPermissions(query, localMockConfig);
|
||||
expect(permissions).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -188,9 +188,15 @@ export async function checkPermissions(
|
||||
const pathName = part.content.substring(1);
|
||||
if (!pathName) continue;
|
||||
|
||||
const resolvedPathName = resolveToRealPath(
|
||||
path.resolve(config.getTargetDir(), pathName),
|
||||
);
|
||||
let resolvedPathName: string;
|
||||
try {
|
||||
resolvedPathName = resolveToRealPath(
|
||||
path.resolve(config.getTargetDir(), pathName),
|
||||
);
|
||||
} catch {
|
||||
// If path resolution fails (e.g. ENAMETOOLONG), skip this path
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.validatePathAccess(resolvedPathName, 'read')) {
|
||||
if (await fileExists(resolvedPathName)) {
|
||||
|
||||
@@ -134,6 +134,44 @@ describe('Turn', () => {
|
||||
expect(turn.getDebugResponses().length).toBe(2);
|
||||
});
|
||||
|
||||
it('should not duplicate text in getResponseText when both chunks and consolidated response are present', async () => {
|
||||
const mockResponseStream = (async function* () {
|
||||
// Chunk 1
|
||||
yield {
|
||||
type: StreamEventType.CHUNK,
|
||||
value: {
|
||||
candidates: [{ content: { parts: [{ text: 'Hello' }] } }],
|
||||
} as GenerateContentResponse,
|
||||
};
|
||||
// Chunk 2
|
||||
yield {
|
||||
type: StreamEventType.CHUNK,
|
||||
value: {
|
||||
candidates: [{ content: { parts: [{ text: ' world' }] } }],
|
||||
} as GenerateContentResponse,
|
||||
};
|
||||
// Final consolidated response (as yielded by GeminiChat)
|
||||
yield {
|
||||
type: StreamEventType.CHUNK,
|
||||
value: {
|
||||
candidates: [{
|
||||
content: { parts: [{ text: 'Hello world' }] },
|
||||
finishReason: 'STOP'
|
||||
}],
|
||||
} as GenerateContentResponse,
|
||||
};
|
||||
})();
|
||||
mockSendMessageStream.mockResolvedValue(mockResponseStream);
|
||||
|
||||
for await (const _ of turn.run({ model: 'm' }, [{ text: 'req' }], new AbortController().signal)) {
|
||||
// consume stream
|
||||
}
|
||||
|
||||
const text = turn.getResponseText();
|
||||
// CURRENT BUGGY BEHAVIOR: "Hello world Hello world"
|
||||
expect(text).toBe('Hello world');
|
||||
});
|
||||
|
||||
it('should yield tool_call_request events for function calls', async () => {
|
||||
const mockResponseStream = (async function* () {
|
||||
yield {
|
||||
|
||||
@@ -472,10 +472,21 @@ export class Turn {
|
||||
*/
|
||||
getResponseText(): string {
|
||||
if (this.cachedResponseText === undefined) {
|
||||
this.cachedResponseText = this.debugResponses
|
||||
.map((response) => getResponseText(response))
|
||||
.filter((text): text is string => text !== null)
|
||||
.join(' ');
|
||||
let result = '';
|
||||
for (const response of this.debugResponses) {
|
||||
const text = getResponseText(response);
|
||||
if (text) {
|
||||
// Heuristic to handle both delta and cumulative responses:
|
||||
// If the new text starts with our current result, it's likely a cumulative
|
||||
// update (or a redundant final consolidated response).
|
||||
if (result && text.startsWith(result)) {
|
||||
result = text;
|
||||
} else {
|
||||
result += text;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.cachedResponseText = result;
|
||||
}
|
||||
return this.cachedResponseText;
|
||||
}
|
||||
|
||||
@@ -230,48 +230,6 @@ describe('sanitizeEnvironment', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('value-first security: secret values must be caught even for allowed variable names', () => {
|
||||
it('should redact ALWAYS_ALLOWED variables whose values contain a GitHub token', () => {
|
||||
const env = {
|
||||
HOME: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
PATH: '/usr/bin',
|
||||
};
|
||||
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
|
||||
expect(sanitized).toEqual({ PATH: '/usr/bin' });
|
||||
});
|
||||
|
||||
it('should redact ALWAYS_ALLOWED variables whose values contain a certificate', () => {
|
||||
const env = {
|
||||
SHELL:
|
||||
'-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----',
|
||||
USER: 'alice',
|
||||
};
|
||||
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
|
||||
expect(sanitized).toEqual({ USER: 'alice' });
|
||||
});
|
||||
|
||||
it('should redact user-allowlisted variables whose values contain a secret', () => {
|
||||
const env = {
|
||||
MY_SAFE_VAR: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
OTHER: 'fine',
|
||||
};
|
||||
const sanitized = sanitizeEnvironment(env, {
|
||||
allowedEnvironmentVariables: ['MY_SAFE_VAR'],
|
||||
blockedEnvironmentVariables: [],
|
||||
enableEnvironmentVariableRedaction: true,
|
||||
});
|
||||
expect(sanitized).toEqual({ OTHER: 'fine' });
|
||||
});
|
||||
|
||||
it('should NOT redact GEMINI_CLI_ variables even if their value looks like a secret (fully trusted)', () => {
|
||||
const env = {
|
||||
GEMINI_CLI_INTERNAL: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
|
||||
};
|
||||
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
|
||||
expect(sanitized).toEqual(env);
|
||||
});
|
||||
});
|
||||
|
||||
it('should ensure all names in the sets are capitalized', () => {
|
||||
for (const name of ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES) {
|
||||
expect(name).toBe(name.toUpperCase());
|
||||
@@ -412,15 +370,16 @@ describe('getSecureSanitizationConfig', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should filter out variables from allowed list that match NEVER_ALLOWED_NAME_PATTERNS', () => {
|
||||
it('should not filter out variables from allowed list that match NEVER_ALLOWED_NAME_PATTERNS', () => {
|
||||
const requestedConfig = {
|
||||
allowedEnvironmentVariables: ['SAFE_VAR', 'MY_SECRET_TOKEN'],
|
||||
allowedEnvironmentVariables: ['SAFE_VAR', 'MY_SECRET_TOKEN', 'GH_TOKEN'],
|
||||
};
|
||||
|
||||
const config = getSecureSanitizationConfig(requestedConfig);
|
||||
|
||||
expect(config.allowedEnvironmentVariables).toContain('SAFE_VAR');
|
||||
expect(config.allowedEnvironmentVariables).not.toContain('MY_SECRET_TOKEN');
|
||||
expect(config.allowedEnvironmentVariables).toContain('MY_SECRET_TOKEN');
|
||||
expect(config.allowedEnvironmentVariables).toContain('GH_TOKEN');
|
||||
});
|
||||
|
||||
it('should deduplicate variables in allowed and blocked lists', () => {
|
||||
|
||||
@@ -14,9 +14,11 @@ export function sanitizeEnvironment(
|
||||
processEnv: NodeJS.ProcessEnv,
|
||||
config: EnvironmentSanitizationConfig,
|
||||
): NodeJS.ProcessEnv {
|
||||
// Enable strict sanitization in GitHub actions.
|
||||
const isStrictSanitization =
|
||||
!!processEnv['GITHUB_SHA'] || processEnv['SURFACE'] === 'Github';
|
||||
|
||||
// Always sanitize when in GitHub actions.
|
||||
if (!config.enableEnvironmentVariableRedaction && !isStrictSanitization) {
|
||||
return { ...processEnv };
|
||||
}
|
||||
@@ -150,22 +152,7 @@ function shouldRedactEnvironmentVariable(
|
||||
key = key.toUpperCase();
|
||||
value = value?.toUpperCase();
|
||||
|
||||
if (key.startsWith('GEMINI_CLI_')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
for (const pattern of NEVER_ALLOWED_VALUE_PATTERNS) {
|
||||
if (pattern.test(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (key.startsWith('GIT_CONFIG_')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// User overrides take precedence.
|
||||
if (allowedSet?.has(key)) {
|
||||
return false;
|
||||
}
|
||||
@@ -173,14 +160,20 @@ function shouldRedactEnvironmentVariable(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES.has(key)) {
|
||||
// These are never redacted.
|
||||
if (
|
||||
ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES.has(key) ||
|
||||
key.startsWith('GEMINI_CLI_')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These are always redacted.
|
||||
if (NEVER_ALLOWED_ENVIRONMENT_VARIABLES.has(key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If in strict mode (e.g. GitHub Action), and not explicitly allowed, redact it.
|
||||
if (isStrictSanitization) {
|
||||
return true;
|
||||
}
|
||||
@@ -191,6 +184,15 @@ function shouldRedactEnvironmentVariable(
|
||||
}
|
||||
}
|
||||
|
||||
// Redact if the value looks like a key/cert.
|
||||
if (value) {
|
||||
for (const pattern of NEVER_ALLOWED_VALUE_PATTERNS) {
|
||||
if (pattern.test(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -212,12 +214,6 @@ export function getSecureSanitizationConfig(
|
||||
if (NEVER_ALLOWED_ENVIRONMENT_VARIABLES.has(upperKey)) {
|
||||
return false;
|
||||
}
|
||||
// Never allow variables that match sensitive name patterns
|
||||
for (const pattern of NEVER_ALLOWED_NAME_PATTERNS) {
|
||||
if (pattern.test(upperKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -602,6 +602,19 @@ describe('resolveToRealPath', () => {
|
||||
/Infinite recursion detected/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle ENAMETOOLONG gracefully', () => {
|
||||
const longPath = path.resolve('/' + 'a'.repeat(5000));
|
||||
|
||||
vi.spyOn(fs, 'realpathSync').mockImplementation(() => {
|
||||
const err = new Error('ENAMETOOLONG') as NodeJS.ErrnoException;
|
||||
err.code = 'ENAMETOOLONG';
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Should return the path itself if realpathSync fails with ENAMETOOLONG
|
||||
expect(resolveToRealPath(longPath)).toBe(longPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeRelative', () => {
|
||||
|
||||
@@ -440,7 +440,10 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
|
||||
e &&
|
||||
typeof e === 'object' &&
|
||||
'code' in e &&
|
||||
(e.code === 'ENOENT' || e.code === 'EISDIR')
|
||||
(e.code === 'ENOENT' ||
|
||||
e.code === 'EISDIR' ||
|
||||
e.code === 'ENAMETOOLONG' ||
|
||||
e.code === 'EINVAL')
|
||||
) {
|
||||
try {
|
||||
const stat = fs.lstatSync(p);
|
||||
@@ -457,7 +460,10 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
|
||||
lstatError &&
|
||||
typeof lstatError === 'object' &&
|
||||
'code' in lstatError &&
|
||||
(lstatError.code === 'ENOENT' || lstatError.code === 'EISDIR')
|
||||
(lstatError.code === 'ENOENT' ||
|
||||
lstatError.code === 'EISDIR' ||
|
||||
lstatError.code === 'ENAMETOOLONG' ||
|
||||
lstatError.code === 'EINVAL')
|
||||
)
|
||||
) {
|
||||
throw lstatError;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2021"],
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2023"],
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "dom"],
|
||||
"lib": ["ES2023", "dom"],
|
||||
"sourceMap": true,
|
||||
/*
|
||||
* skipLibCheck is necessary because the a2a-server package depends on
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"security": {
|
||||
"environmentVariableRedaction": {
|
||||
"allowed": ["GH_TOKEN", "GITHUB_TOKEN"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,8 +114,12 @@ above:
|
||||
4. **Strict Scope Constraint**: You are STRICTLY FORBIDDEN from modifying or
|
||||
staging any file that was not already staged by the investigation phase. You
|
||||
must ONLY critique and fix the files explicitly included in
|
||||
`git diff --staged`. Do not attempt to complete pending tasks from the
|
||||
memory ledger or introduce unrelated refactoring to unstaged files.
|
||||
`git diff --staged`. Furthermore, within those staged files, you must ONLY
|
||||
fix the specific logical or technical flaw you identified. You are
|
||||
STRICTLY FORBIDDEN from performing unrelated refactoring, changing type
|
||||
signatures, or modifying code outside the immediate scope of the required fix.
|
||||
Do not attempt to complete pending tasks from the memory ledger or introduce
|
||||
unrelated "cleanups".
|
||||
5. Re-stage the file with `git add`. **CRITICAL: You MUST use `git add` to
|
||||
stage your fixes.**
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: issue-fixer
|
||||
description: Proactively identify and implement surgical fixes for small-effort issues and maintain existing PRs to reduce repository backlog.
|
||||
---
|
||||
|
||||
# Skill: Issue Fixer
|
||||
|
||||
## Goal
|
||||
|
||||
Proactively identify and implement surgical fixes for "small effort" issues and
|
||||
maintain existing PRs to reduce the repository backlog.
|
||||
|
||||
## High-Level Expectations
|
||||
|
||||
1. **Maintenance**: Prioritize driving existing `bot-fix` PRs to completion. Check for CI failures, merge conflicts, or requested changes.
|
||||
2. **Discovery**: Find open issues labeled `effort/small`. Prioritize those with clear reproduction steps. You are STRICTLY FORBIDDEN from using local commands (e.g., `npm run lint`, `npm run typecheck`, or grepping for TODOs) to find new work. You are also STRICTLY FORBIDDEN from attempting to fix issues with the internal metrics scripts or other repository tooling on your own; your ONLY source of new tasks is the GitHub issue tracker.
|
||||
3. **Autonomous Implementation**: You are responsible for the entire fix: research, code changes, and test verification.
|
||||
4. **Surgical Precision**: Changes must be minimal and strictly focused on the identified issue. Avoid "drive-by" refactoring.
|
||||
5. **Local Verification (MANDATORY TIMEOUTS)**: You MUST run `timeout 10m npm run preflight` (or `timeout 5m npm test ...`) locally and iterate on any failures before finalizing your PR. Wrapping test commands in `timeout` is **MANDATORY** to prevent hanging the CI environment if your changes introduce an infinite loop.
|
||||
6. **Expert Mentions**: Identify the domain expert for the affected files and CC them in the PR description.
|
||||
7. **Focused Contributions**: Limit your active PRs to ~10 at a time. Try to complete existing PRs before opening new ones. If a maintainer closes a PR, that may be an indication that they are rejecting the fix.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Inventory & Drive PRs**:
|
||||
- **ACTIVATE SKILLS**: You MUST call `activate_skill(name="memory")` and `activate_skill(name="prs")` before continuing.
|
||||
- Use the `prs` skill to list all open PRs labeled `bot-fix`.
|
||||
- If any require attention (CI failure, requested changes), focus your entire run on resolving ONE of them.
|
||||
- Do NOT start a new issue fix if an existing PR needs work.
|
||||
2. **Search for Candidates**: If no PRs need attention, search for `effort/small` issues: `gh issue list --label "effort/small" --limit 10 --json number,title,url`.
|
||||
- **CRITICAL**: `gh issue list` is your ONLY source for new tasks. Do not attempt to fix issues you discover independently in the codebase (such as broken metrics scripts) unless they correspond to a specific assigned issue from the tracker. If `gh` fails, you MUST diagnose the environment or abort the discovery phase. You are STRICTLY FORBIDDEN from using `google_web_search` to query GitHub, as it indexes closed issues. Do NOT run local discovery commands (e.g., `npm run lint`, `npm run typecheck`) to look for "easy fixes".
|
||||
3. **Select ONE Issue** and implement a fix on a new branch.
|
||||
- **Efficient Searching**: When searching the codebase with `grep_search`, you MUST search one top-level folder at a time (e.g. `packages/core`, `packages/cli`) to avoid timeouts. Avoid searching problematic directories with large data files like `memory-tests` and `last_brain_data` unless absolutely necessary.
|
||||
4. **Verify**: You MUST run `timeout 10m npm run preflight` to verify. Wrapping test commands in `timeout` is mandatory to prevent hanging the CI environment if your changes introduce an infinite loop.
|
||||
5. **Use the `prs` Skill** to stage changes and prepare the draft PR (labels: `bot-fix`, `issue-fixer`). Ensure you write `pr-description.md` and `pr-labels.txt` to the **workspace root**.
|
||||
@@ -20,7 +20,13 @@ maintainability.
|
||||
- Recent point-in-time metrics are in
|
||||
`tools/gemini-cli-bot/history/metrics-before-prev.csv` and the current run's
|
||||
metrics.
|
||||
- **Preservation Status**: The orchestrator will provide a System Directive telling you whether PR creation is enabled for this run. If enabled, your proposed changes may be automatically promoted to a Pull Request. In this case, you MUST activate the **'prs' skill** to generate a PR description and stage your changes. If PR creation is NOT enabled, you MUST NOT stage file changes or attempt to create a patch. Instead, simply report your findings.
|
||||
## Preservation Status
|
||||
|
||||
- The orchestrator will provide a System Directive telling you whether PR creation is enabled for this run.
|
||||
- **ACTIVATE SKILLS**: You MUST call `activate_skill(name="memory")` at the start of your investigation.
|
||||
- **PR Skill**: If PR creation is enabled, you MUST call `activate_skill(name="prs")` to generate a PR description and stage your changes. You MUST write `pr-description.md` and `pr-labels.txt` to the **workspace root**.
|
||||
- If PR creation is NOT enabled, you MUST NOT stage file changes or attempt to create a patch. Instead, simply report your findings.
|
||||
- **Ownership Mandate**: Any PR generated by this skill MUST use the `bot-fix` and `metrics` labels in `pr-labels.txt`.
|
||||
|
||||
## Repo Policy Priorities
|
||||
|
||||
|
||||
@@ -10,6 +10,18 @@ description: Expertise in managing the Git and GitHub Pull Request lifecycle, in
|
||||
Standardize how the Gemini CLI Bot stages its changes, generates Pull Request
|
||||
descriptions, and manages the lifecycle of both new and existing PRs.
|
||||
|
||||
## Mandatory PR Driver (Ownership)
|
||||
|
||||
You are the "owner" of all PRs labeled `bot-fix`. You MUST proactively drive them toward a resolution (either completion or informed escalation):
|
||||
1. **Inventory**: Use `gh pr list --label "bot-fix" --json number,title,headRefName,statusCheckRollup,comments` to find your active PRs.
|
||||
2. **Resolution Priority**:
|
||||
- **Fixable CI/Feedback**: If a `bot-fix` PR has failing status checks or new maintainer comments, you MUST prioritize fixing the CI or responding to the feedback before starting new work.
|
||||
- **Unresolvable Roadblocks**: If you identify a persistent failure that appears to be an environment issue, a flaky test, or a fundamental architectural blocker that requires human intervention, you MUST NOT keep looping. Instead:
|
||||
1. Summarize the blocker and your failed attempts in `pr-comment.md`.
|
||||
2. Explicitly ask for maintainer help.
|
||||
3. Record the learning in `lessons-learned.md`.
|
||||
4. Move on to a different task.
|
||||
|
||||
## Staging & Patch Preparation (MANDATORY)
|
||||
|
||||
If you are proposing fixes and PR creation is enabled (per the System Directive):
|
||||
@@ -21,17 +33,21 @@ If you are proposing fixes and PR creation is enabled (per the System Directive)
|
||||
unrelated refactor, or a metrics script update. Metrics and fixes MUST
|
||||
be in separate PRs.
|
||||
2. **Generate PR Description**: Use the `write_file` tool to create
|
||||
`pr-description.md`.
|
||||
`pr-description.md` **at the workspace root**.
|
||||
- **Title**: The very first line MUST be a concise, conventional title.
|
||||
- **Body**: The rest should be the markdown body explaining the change, why
|
||||
it is recommended, and the expected impact.
|
||||
3. **Stage Fixes**: You MUST explicitly stage your fixes using the
|
||||
- **Body**: Explain the change and expected impact. You MUST identify the domain expert for the affected files and mention them (cc @<user>).
|
||||
- **Labels**: Use the `write_file` tool to create `pr-labels.txt` **at the workspace root** containing one label per line. You MUST ALWAYS add the `bot-fix` label.
|
||||
3. **Branch Naming (Optional)**: If you wish to specify a custom branch name,
|
||||
use `write_file` to create `branch-name.txt` **at the workspace root**. **CRITICAL**: The branch name
|
||||
MUST start with the `bot/` prefix. If you do not specify a branch name, one
|
||||
will be generated for you.
|
||||
4. **Stage Fixes**: You MUST explicitly stage your fixes using the
|
||||
`git add <files>` command.
|
||||
4. **Internal File Protection (CRITICAL)**: You are STRICTLY FORBIDDEN from
|
||||
5. **Internal File Protection (CRITICAL)**: You are STRICTLY FORBIDDEN from
|
||||
staging internal bot management files. If they are accidentally staged, you
|
||||
MUST unstage them using `git reset <file>`.
|
||||
- **NEVER STAGE**: `pr-description.md`, `lessons-learned.md`,
|
||||
`branch-name.txt`, `pr-comment.md`, `pr-number.txt`, `issue-comment.md`, or
|
||||
`branch-name.txt`, `pr-comment.md`, `pr-number.txt`, `issue-comment.md`, `pr-labels.txt`, or
|
||||
anything in `history/`.
|
||||
|
||||
## Unblocking & PR Updates (Recovery)
|
||||
@@ -40,12 +56,14 @@ If you are continuing work on an existing Task or responding to a comment on an
|
||||
existing bot PR:
|
||||
|
||||
1. **Target Existing Branch**: Use `write_file` to generate `branch-name.txt`
|
||||
containing the current branch name (e.g., `bot/task-BT-01`).
|
||||
2. **Track PR ID**: Use `write_file` to generate `pr-number.txt` containing the
|
||||
**at the workspace root** containing the current branch name. **CRITICAL**: The branch name MUST start
|
||||
with the `bot/` prefix (e.g., `bot/task-BT-01`). If it does not, your PR
|
||||
creation will be rejected by the safety gate.
|
||||
2. **Track PR ID**: Use `write_file` to generate `pr-number.txt` **at the workspace root** containing the
|
||||
numeric PR ID.
|
||||
3. **Respond to Maintainers**:
|
||||
- For general responses, write your markdown comment to `issue-comment.md`.
|
||||
- For specific PR feedback, write your markdown response to `pr-comment.md`.
|
||||
4. **Handle CI Failures**: Diagnose failing checks using `gh run view`. Your
|
||||
- For general responses, write your markdown comment to `issue-comment.md` **at the workspace root**.
|
||||
- For specific PR feedback, write your markdown response to `pr-comment.md` **at the workspace root**.
|
||||
4. **Handle CI Failures**: Diagnose failing checks using `gh --no-pager run view` or `gh api`. Your
|
||||
priority must be generating a new patch and staging it with `git add` to fix
|
||||
the failure.
|
||||
|
||||
@@ -64,8 +64,8 @@ Root-Cause' workflow** to the **'worker' agent**:
|
||||
|
||||
If investigation confirms a change is required:
|
||||
|
||||
- **Activate PR Skill**: You MUST activate the **'prs' skill** to manage
|
||||
staging, PR descriptions, and branch targeting.
|
||||
- **ACTIVATE PR SKILL FIRST**: You MUST activate the **'prs' skill** BEFORE
|
||||
staging any changes or generating PR data.
|
||||
- **One Thing at a Time**: You MUST ONLY propose and implement a **single fix or
|
||||
improvement per run**.
|
||||
- **Surgical Changes**: Apply the minimal set of changes needed to address the
|
||||
@@ -74,7 +74,7 @@ If investigation confirms a change is required:
|
||||
user's specific request. You are STRICTLY FORBIDDEN from including any
|
||||
unrelated updates when operating in interactive mode.
|
||||
- **Acknowledgment**: Use the `write_file` tool to write a brief acknowledgement
|
||||
to `issue-comment.md`.
|
||||
to `issue-comment.md` **at the workspace root**.
|
||||
|
||||
### 3. Question & Answer (Q&A)
|
||||
|
||||
@@ -83,7 +83,8 @@ If the user's request is purely informational:
|
||||
- **Evidence-Based Answers**: Delegate the information gathering to the
|
||||
**'worker' agent** to verify facts before answering.
|
||||
- **Output**: You MUST use the `write_file` tool to save your response to
|
||||
`issue-comment.md`. DO NOT simply output your response to the console.
|
||||
`issue-comment.md` **at the workspace root**. DO NOT simply output your
|
||||
response to the console.
|
||||
|
||||
## Execution Constraints
|
||||
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
# Phase: Scheduled Agent (Strategic Investigation & Optimization)
|
||||
# Phase: Scheduled Agent
|
||||
|
||||
## 1. MANDATORY START: Activate Mandate Skill
|
||||
|
||||
Your **MANDATE FOR THIS RUN** (provided at the end of this prompt) explicitly
|
||||
dictates your task for this session. It will ask you to use a specific skill
|
||||
(e.g., `issue-fixer` or `metrics`).
|
||||
|
||||
**You MUST call the `activate_skill` tool as the VERY FIRST ACTION of your FIRST
|
||||
TURN to load the instructions for your mandate.**
|
||||
|
||||
1. Identify the skill name from your **MANDATE FOR THIS RUN**.
|
||||
2. Call `activate_skill(name="<skill-name>")`.
|
||||
3. Follow the detailed workflow and instructions provided by the activated
|
||||
skill. Do NOT perform any other actions until the skill is activated.
|
||||
|
||||
## Goal
|
||||
|
||||
Analyze repository health metrics, identify bottlenecks, and propose proactive
|
||||
improvements to the repository's workflows and automation. You must maintain
|
||||
high architectural standards, security rigor, and maintainer-focused
|
||||
productivity.
|
||||
Execute the task specified in your **MANDATE FOR THIS RUN**. Maintain high
|
||||
architectural standards, security rigor, and maintainer-focused productivity.
|
||||
|
||||
## CRITICAL: ONE THING AT A TIME
|
||||
|
||||
You are STRICTLY FORBIDDEN from proposing or implementing more than one
|
||||
improvement or fix per run. Bundling unrelated changes (e.g., a documentation
|
||||
update and a script fix) into a single PR is a failure of your primary mandate.
|
||||
You are specifically forbidden from combining metrics script updates and logic
|
||||
fixes/improvements in the same PR. If you identify multiple opportunities:
|
||||
If you identify multiple opportunities:
|
||||
|
||||
1. Select the **single most impactful** improvement.
|
||||
2. Focus your entire investigation and implementation on ONLY that improvement.
|
||||
@@ -50,43 +61,56 @@ You MUST use the following skills to manage persistent state and PRs:
|
||||
|
||||
## Instructions
|
||||
|
||||
### 1. Investigation & Triage (Mandatory Delegation)
|
||||
### 1. Hypothesis Testing & Strategic Pivoting
|
||||
|
||||
You MUST delegate the **'metrics' workflow** to the **'worker' agent**:
|
||||
|
||||
1. Invoke the 'worker' agent and instruct it to use the **'metrics' skill**.
|
||||
2. Pass the current date and the relevant portions of the Task Ledger (ensuring
|
||||
all untrusted data is wrapped in <untrusted_context> tags) for grounding.
|
||||
3. Use the worker's summarized results to identify trends, anomalies, and
|
||||
opportunities for proactive improvement.
|
||||
|
||||
### 2. Hypothesis Testing & Deep Dive
|
||||
|
||||
For any detected bottlenecks or opportunities:
|
||||
For any detected bugs, bottlenecks, or opportunities:
|
||||
|
||||
- Formulate competing hypotheses.
|
||||
- Delegate data-intensive evidence gathering (e.g., slicing logs, batch issue
|
||||
analysis - ensuring all untrusted data is wrapped in <untrusted_context> tags)
|
||||
to the worker agent.
|
||||
- Select the optimal path based on the empirical evidence returned. You MUST
|
||||
ONLY execute on a **single path** to ensure the resulting PR is focused and
|
||||
surgical.
|
||||
- Delegate high-volume or data-intensive evidence gathering (e.g., slicing logs,
|
||||
batch issue analysis) to the **'worker' agent** if necessary.
|
||||
- **Iterative Refinement**: Select the most likely path first. However, if your
|
||||
initial implementation fails verification (e.g. tests still fail), you MUST
|
||||
explicitly pivot to your second hypothesis rather than infinitely patching the
|
||||
first one.
|
||||
- **Bail Out**: If all your formulated hypotheses fail to yield a verified fix,
|
||||
abort the task and record the findings. Delivering NO change is better than
|
||||
delivering a broken or "best-guess" fix.
|
||||
|
||||
## Execution Constraints
|
||||
|
||||
- **One Thing at a Time**: You MUST ONLY propose and implement a **single
|
||||
improvement or fix per run**. If you identify multiple opportunities, select
|
||||
the one with the highest impact and record the others in `lessons-learned.md`
|
||||
for future runs.
|
||||
improvement or fix per run**.
|
||||
- **Surgical Changes**: Apply the minimal set of changes needed to address the
|
||||
identified opportunity correctly and safely.
|
||||
- **Strict Scope**: You are STRICTLY FORBIDDEN from bundling unrelated updates
|
||||
into a single PR.
|
||||
- **Mandatory Delegation**: You MUST delegate the following workflows to the
|
||||
**'worker' agent**:
|
||||
- Repository metrics collection and initial triage ('metrics' skill).
|
||||
- High-volume data collection or log analysis.
|
||||
- **Do NOT delegate to the 'generalist' agent.**
|
||||
- **Delegation Guidelines**: Do NOT delegate to the 'generalist' agent. Delegate
|
||||
data-intensive tasks (like repository metrics collection) to the 'worker'
|
||||
agent.
|
||||
- **Verification vs. Discovery**: Local commands (e.g. `npm run lint`,
|
||||
`npm run typecheck`) are for VERIFYING fixes to explicitly assigned tasks
|
||||
only. They must NEVER be used for unprompted "fishing expeditions" to find new
|
||||
work.
|
||||
- **Monorepo Build Order**: When verifying the workspace or diagnosing errors,
|
||||
you MUST run `npm run build` BEFORE running `npm run typecheck`. In a clean
|
||||
state, `tsc` will report widespread errors (TS6305) if the project's build
|
||||
artifacts do not yet exist. These are environment issues, not code bugs.
|
||||
- **Strict Read-Only Reasoning**: You cannot push code or post comments via API.
|
||||
Your only way to effect change is by writing to specific files and explicitly
|
||||
staging file changes using the `git add` command.
|
||||
|
||||
## Loop Prevention & Success Criteria
|
||||
|
||||
To ensure efficiency and prevent infinite reasoning loops:
|
||||
|
||||
1. **Monitor Your Progress**: If you have attempted the same sequence of
|
||||
actions (e.g., Edit -> Test -> Fail) for the same problem more than **3
|
||||
times**, you MUST stop and re-evaluate your fundamental hypothesis.
|
||||
2. **Failure Threshold**: If you cannot find a verified solution after **2
|
||||
distinct hypotheses** (max 6 total edit/test cycles), you MUST abort the
|
||||
task.
|
||||
3. **Reporting Failure**: If you abort, summarize the roadblocks you
|
||||
encountered in `lessons-learned.md`. It is better to deliver NO changes than
|
||||
to burn excessive tokens on a loop.
|
||||
4. **Verification is Key**: A task is only "complete" when all relevant tests
|
||||
pass. Never stage a change that you know still fails tests.
|
||||
|
||||
@@ -2,15 +2,23 @@
|
||||
# This policy guarantees permission for shell commands and file writing in the bot's CI environment.
|
||||
|
||||
[[rule]]
|
||||
toolName = ["run_shell_command", "write_file", "replace"]
|
||||
toolName = ["run_shell_command", "write_file", "replace", "activate_skill"]
|
||||
decision = "allow"
|
||||
# Max priority to ensure it overrides all default and workspace rules.
|
||||
priority = 999
|
||||
# Explicitly target the headless environment to match the specificity of default denial rules.
|
||||
interactive = false
|
||||
# Capture output without triggering a policy downgrade to ASK_USER.
|
||||
allowRedirection = true
|
||||
|
||||
[[rule]]
|
||||
toolName = "invoke_agent"
|
||||
decision = "allow"
|
||||
priority = 999
|
||||
interactive = false
|
||||
|
||||
[[rule]]
|
||||
toolName = "google_web_search"
|
||||
decision = "deny"
|
||||
priority = 999
|
||||
interactive = false
|
||||
|
||||
Reference in New Issue
Block a user