mirror of
https://github.com/johndoe6345789/AutoMetabuilder.git
synced 2026-04-24 13:54:59 +00:00
Implement MVP milestone tracking with multi-iteration task loop and roadmap utilities. Update Web UI to display MVP status.
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
- [x] **Declarative Task Processing**: Move more logic into JSON/YAML specifications.
|
||||
- [x] **Feedback Loop**: Support for the AI to read comments on PRs it created.
|
||||
|
||||
## Phase 3: Advanced Automation
|
||||
## Phase 3: Advanced Automation (MVP)
|
||||
- [x] **Automated Testing**: Integration with test runners to verify changes before PR.
|
||||
- [x] **Linting Integration**: Automatically run and fix linting issues.
|
||||
- [x] **Multi-Model Support**: Easily switch between different LLM providers.
|
||||
|
||||
@@ -17,6 +17,7 @@ from .github_integration import GitHubIntegration, get_repo_name_from_env
|
||||
from .docker_utils import run_command_in_docker
|
||||
from .web.server import start_web_ui
|
||||
from .integrations.notifications import notify_all
|
||||
from .roadmap_utils import update_roadmap, is_mvp_reached
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -77,11 +78,6 @@ def get_sdlc_context(gh: GitHubIntegration, msgs: dict) -> str:
|
||||
return sdlc_context
|
||||
|
||||
|
||||
def update_roadmap(content: str):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
with open("ROADMAP.md", "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
logger.info("ROADMAP.md updated successfully.")
|
||||
|
||||
|
||||
def list_files(directory: str = "."):
|
||||
@@ -349,34 +345,55 @@ def main():
|
||||
messages.append({"role": "user", "content": msgs["user_next_step"]})
|
||||
|
||||
model_name = os.environ.get("LLM_MODEL", prompt.get("model", DEFAULT_MODEL))
|
||||
response = get_completion(client, model_name, messages, tools)
|
||||
|
||||
resp_msg = response.choices[0].message
|
||||
logger.info(
|
||||
resp_msg.content
|
||||
if resp_msg.content
|
||||
else msgs["info_tool_call_requested"]
|
||||
)
|
||||
|
||||
# Handle tool calls
|
||||
tool_results = handle_tool_calls(resp_msg, tool_map, gh, msgs, dry_run=args.dry_run, yolo=args.yolo)
|
||||
|
||||
if args.once and tool_results:
|
||||
logger.info(msgs.get("info_second_pass", "Performing second pass with tool results..."))
|
||||
# Multi-iteration loop
|
||||
iteration = 0
|
||||
max_iterations = 10
|
||||
|
||||
while iteration < max_iterations:
|
||||
iteration += 1
|
||||
logger.info(f"--- Iteration {iteration} ---")
|
||||
|
||||
response = get_completion(client, model_name, messages, tools)
|
||||
resp_msg = response.choices[0].message
|
||||
|
||||
logger.info(
|
||||
resp_msg.content
|
||||
if resp_msg.content
|
||||
else msgs["info_tool_call_requested"]
|
||||
)
|
||||
|
||||
messages.append(resp_msg)
|
||||
|
||||
if not resp_msg.tool_calls:
|
||||
# If no more tools requested, we are done
|
||||
notify_all(f"AutoMetabuilder task complete: {resp_msg.content[:100]}...")
|
||||
break
|
||||
|
||||
# Handle tool calls
|
||||
tool_results = handle_tool_calls(resp_msg, tool_map, gh, msgs, dry_run=args.dry_run, yolo=args.yolo)
|
||||
messages.extend(tool_results)
|
||||
|
||||
response = get_completion(client, model_name, messages, tools)
|
||||
final_msg = response.choices[0].message
|
||||
logger.info(final_msg.content if final_msg.content else msgs["info_tool_call_requested"])
|
||||
|
||||
# Notify about task completion
|
||||
notify_all(f"AutoMetabuilder task complete: {final_msg.content[:100]}...")
|
||||
|
||||
# In a multi-iteration loop, we would call handle_tool_calls again here.
|
||||
# For --once, we just do one more pass.
|
||||
if final_msg.tool_calls:
|
||||
handle_tool_calls(final_msg, tool_map, gh, msgs, dry_run=args.dry_run, yolo=args.yolo)
|
||||
if args.yolo and is_mvp_reached():
|
||||
logger.info("MVP reached. Stopping YOLO loop.")
|
||||
notify_all("AutoMetabuilder YOLO loop stopped: MVP reached.")
|
||||
break
|
||||
|
||||
if args.once:
|
||||
# If --once is set, we do one more pass to show the final result
|
||||
logger.info(msgs.get("info_second_pass", "Performing second pass with tool results..."))
|
||||
response = get_completion(client, model_name, messages, tools)
|
||||
final_msg = response.choices[0].message
|
||||
logger.info(final_msg.content if final_msg.content else msgs["info_tool_call_requested"])
|
||||
notify_all(f"AutoMetabuilder task complete: {final_msg.content[:100]}...")
|
||||
|
||||
# For --once, we still handle tool calls if any in the second pass, but then stop.
|
||||
if final_msg.tool_calls:
|
||||
handle_tool_calls(final_msg, tool_map, gh, msgs, dry_run=args.dry_run, yolo=args.yolo)
|
||||
break
|
||||
else:
|
||||
logger.warning(f"Reached maximum iterations ({max_iterations}). Stopping.")
|
||||
notify_all(f"AutoMetabuilder stopped: Reached {max_iterations} iterations.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
40
src/autometabuilder/roadmap_utils.py
Normal file
40
src/autometabuilder/roadmap_utils.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import os
|
||||
import re
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("autometabuilder")
|
||||
|
||||
def update_roadmap(content: str):
|
||||
"""Update ROADMAP.md with new content."""
|
||||
with open("ROADMAP.md", "w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
logger.info("ROADMAP.md updated successfully.")
|
||||
|
||||
|
||||
def is_mvp_reached() -> bool:
|
||||
"""Check if the MVP section in ROADMAP.md is completed."""
|
||||
if not os.path.exists("ROADMAP.md"):
|
||||
return False
|
||||
|
||||
with open("ROADMAP.md", "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Find the MVP section
|
||||
mvp_match = re.search(r"## .*?\(MVP\)(.*?)##", content, re.DOTALL | re.IGNORECASE)
|
||||
if not mvp_match:
|
||||
# Try finding it if it's the last section
|
||||
mvp_match = re.search(r"## .*?\(MVP\)(.*)", content, re.DOTALL | re.IGNORECASE)
|
||||
|
||||
if not mvp_match:
|
||||
return False
|
||||
|
||||
mvp_section = mvp_match.group(1)
|
||||
# Check if there are any unchecked items [ ]
|
||||
if "[ ]" in mvp_section:
|
||||
return False
|
||||
|
||||
# If there are checked items [x], and no unchecked items, we consider it reached
|
||||
if "[x]" in mvp_section:
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -8,6 +8,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from dotenv import load_dotenv, set_key
|
||||
import subprocess
|
||||
import sys
|
||||
from ..roadmap_utils import is_mvp_reached
|
||||
|
||||
app = FastAPI()
|
||||
security = HTTPBasic()
|
||||
@@ -92,6 +93,7 @@ async def read_item(request: Request, username: str = Depends(get_current_user))
|
||||
translations = get_translations()
|
||||
prompt_content = get_prompt_content()
|
||||
is_running = bot_process is not None
|
||||
mvp_status = is_mvp_reached()
|
||||
return templates.TemplateResponse("index.html", {
|
||||
"request": request,
|
||||
"logs": logs,
|
||||
@@ -99,6 +101,7 @@ async def read_item(request: Request, username: str = Depends(get_current_user))
|
||||
"translations": translations,
|
||||
"prompt_content": prompt_content,
|
||||
"is_running": is_running,
|
||||
"mvp_reached": mvp_status,
|
||||
"username": username
|
||||
})
|
||||
|
||||
@@ -135,7 +138,10 @@ async def update_settings(request: Request, username: str = Depends(get_current_
|
||||
|
||||
@app.get("/api/status")
|
||||
async def get_status(username: str = Depends(get_current_user)):
|
||||
return {"is_running": bot_process is not None}
|
||||
return {
|
||||
"is_running": bot_process is not None,
|
||||
"mvp_reached": is_mvp_reached()
|
||||
}
|
||||
|
||||
@app.get("/api/logs")
|
||||
async def get_logs(username: str = Depends(get_current_user)):
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
<span class="badge bg-success">Idle</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>MVP Milestone:
|
||||
{% if mvp_reached %}
|
||||
<span class="badge bg-primary">Reached ✓</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">In Progress</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form action="/run" method="post" class="mt-2">
|
||||
@@ -111,13 +118,17 @@
|
||||
if (data.is_running) {
|
||||
statusContainer.innerHTML = `
|
||||
<p>Status: <span class="badge bg-warning text-dark">Bot Running...</span></p>
|
||||
<p>MVP Milestone: ${data.mvp_reached ? '<span class="badge bg-primary">Reached ✓</span>' : '<span class="badge bg-secondary">In Progress</span>'}</p>
|
||||
<div class="progress mt-2">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
|
||||
</div>
|
||||
`;
|
||||
runBtn.disabled = true;
|
||||
} else {
|
||||
statusContainer.innerHTML = `<p>Status: <span class="badge bg-success">Idle</span></p>`;
|
||||
statusContainer.innerHTML = `
|
||||
<p>Status: <span class="badge bg-success">Idle</span></p>
|
||||
<p>MVP Milestone: ${data.mvp_reached ? '<span class="badge bg-primary">Reached ✓</span>' : '<span class="badge bg-secondary">In Progress</span>'}</p>
|
||||
`;
|
||||
runBtn.disabled = false;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user