mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-11 10:30:46 -07:00
feat: consolidate backlog analysis tools into scripts/backlog-analysis
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import json
|
||||
import urllib.request
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import concurrent.futures
|
||||
|
||||
API_KEY = "REDACTED_API_KEY"
|
||||
MODEL = "gemini-3-flash-preview"
|
||||
URL = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:generateContent?key={API_KEY}"
|
||||
BUGS_FILE = 'data/bugs.json'
|
||||
|
||||
with open(BUGS_FILE, 'r') as f:
|
||||
bugs = json.load(f)
|
||||
|
||||
def extract_keywords(text):
|
||||
words = re.findall(r'\b[A-Z][a-zA-Z0-9]+\b|\b\w+\.tsx?\b|\b\w+Service\b|\b\w+Command\b', text)
|
||||
words = list(set([w for w in words if len(w) > 4]))
|
||||
return words[:8]
|
||||
|
||||
def search_codebase(keywords):
|
||||
context = ""
|
||||
for kw in keywords:
|
||||
try:
|
||||
kw_clean = kw.replace('"', '\\"')
|
||||
cmd = f'grep -rn "{kw_clean}" ../../packages | grep -vE "node_modules|dist|build|\\.test\\." | head -n 8'
|
||||
out = subprocess.check_output(cmd, shell=True, text=True, stderr=subprocess.STDOUT)
|
||||
if out:
|
||||
context += f"\n--- Matches for {kw_clean} ---\n{out}\n"
|
||||
except:
|
||||
pass
|
||||
return context
|
||||
|
||||
def process_issue(issue):
|
||||
if issue.get('analysis') and issue['analysis'] != "Failed to analyze autonomously" and len(issue['analysis']) > 30:
|
||||
return issue
|
||||
|
||||
title = issue.get('title', '')
|
||||
body = issue.get('body', '')[:1500]
|
||||
|
||||
keywords = extract_keywords(title + " " + body)
|
||||
code_context = search_codebase(keywords)
|
||||
|
||||
prompt = f"""You are a senior software engineer analyzing bug reports for the gemini-cli codebase.
|
||||
Based on the bug description and the provided codebase search context, pinpoint exactly which files and logic are responsible for the bug.
|
||||
DO NOT GUESS. If the context isn't enough, provide your best technical hypothesis.
|
||||
|
||||
Rating Effort Level:
|
||||
- small (1 day): Localized fix (1-2 files), clear cause.
|
||||
- medium (2-3 days): Touches multiple components or hard to trace.
|
||||
- large (>3 days): Architectural issues, Windows/WSL-specific, core protocols.
|
||||
|
||||
Bug Title: {title}
|
||||
Bug Body: {body}
|
||||
|
||||
Codebase Search Context:
|
||||
{code_context[:8000]}
|
||||
|
||||
Output ONLY valid JSON (no markdown block):
|
||||
{{
|
||||
"analysis": "technical analysis of root cause and fix",
|
||||
"effort_level": "small|medium|large",
|
||||
"reasoning": "justification with specific files/lines found"
|
||||
}}
|
||||
"""
|
||||
data = {
|
||||
"contents": [{"role": "user", "parts": [{"text": prompt}]}],
|
||||
"generationConfig": {"temperature": 0.1}
|
||||
}
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(URL, data=json.dumps(data).encode('utf-8'), headers={'Content-Type': 'application/json'})
|
||||
with urllib.request.urlopen(req, timeout=60) as response:
|
||||
res = json.loads(response.read().decode('utf-8'))
|
||||
txt = res['candidates'][0]['content']['parts'][0]['text']
|
||||
txt = txt.replace('```json', '').replace('```', '').strip()
|
||||
parsed = json.loads(txt)
|
||||
|
||||
issue['analysis'] = parsed.get('analysis', 'Failed to analyze')
|
||||
issue['effort_level'] = parsed.get('effort_level', 'medium')
|
||||
issue['reasoning'] = parsed.get('reasoning', 'Could not determine')
|
||||
print(f"Completed {issue['number']} -> {issue['effort_level']}", flush=True)
|
||||
except Exception as e:
|
||||
print(f"Failed {issue['number']}: {e}", flush=True)
|
||||
|
||||
return issue
|
||||
|
||||
def main():
|
||||
to_analyze = [b for b in bugs if b.get('analysis') == "Failed to analyze autonomously" or not b.get('analysis') or len(b.get('analysis', '')) < 30]
|
||||
|
||||
# Only process 5 at a time
|
||||
to_analyze = to_analyze[:5]
|
||||
print(f"Starting single-turn analysis for {len(to_analyze)} bugs...", flush=True)
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
||||
list(executor.map(process_issue, to_analyze))
|
||||
|
||||
# Final save
|
||||
with open(BUGS_FILE, 'w') as f:
|
||||
json.dump(bugs, f, indent=2)
|
||||
|
||||
print("Done processing 5 bugs.", flush=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user