Introduce AutoMetabuilder core components and workflow packages:

- Implement core components: CLI argument parsing, environment loading, GitHub service creation, and logging configuration.
- Add support for OpenAI client setup and model resolution.
- Develop SDLC context loader from GitHub and repository files.
- Implement workflow context and engine builders.
- Introduce major workflow packages: `game_tick_loop` and `contextual_iterative_loop`.
- Update localization files with new package descriptions and labels.
- Streamline web navigation by loading items from a dedicated JSON file.
This commit is contained in:
2026-01-10 01:08:34 +00:00
parent e944c46853
commit 22c9306347
22 changed files with 53 additions and 26 deletions

View File

@@ -15,26 +15,45 @@ The following environment variables are required:
- `GITHUB_TOKEN`: A GitHub Personal Access Token with repository permissions.
- `GITHUB_REPOSITORY`: The full name of the repository (e.g., `owner/repo`).
## Directory layout
- `backend/`: FastAPI/Flask API, workflow controllers, metadata, and CLI modules.
- `frontend/`: Next.js app (using the app router) that talks to the backend over the REST endpoints.
## Usage
Run the tool using poetry:
Run the CLI or the web UI via Poetry (the project uses the backend package defined in `pyproject.toml`):
```bash
poetry run autometabuilder
poetry install
poetry run autometabuilder # starts the CLI or the web server when `--web` is supplied
```
## Testing
### Frontend development
To run the unit tests:
```bash
PYTHONPATH=src pytest tests/test_main.py tests/test_metadata.py tests/test_roadmap.py
cd frontend
npm install
npm run dev --webpack # uses the Webpack bundler for compatibility with restricted hosts
```
To run the Web UI tests (Playwright):
The UI pushes translations, workflow content, and navigation data via the Flask-powered `/api/*` surface. Set `NEXT_PUBLIC_API_BASE` if the backend is hosted on another URL (default: `http://localhost:8000`).
## Testing & linting
### Python
```bash
# First install browsers if you haven't already
playwright install chromium
# Run the UI tests
PYTHONPATH=src pytest tests/ui
PYTHONPATH=backend pytest backend/tests/test_main.py backend/tests/test_metadata.py backend/tests/test_roadmap.py
PYTHONPATH=backend pytest backend/tests/ui # Playwright UI tests; they skip when socket creation is blocked
```
### Frontend
```bash
cd frontend
npm run lint
npm run build --webpack # currently fails in the sandbox because compiling tries to bind new ports
```
The Webpack build step is disabled in this container because the sandbox denies the port binding Turbopack (and its subprocesses) needs; the rest of the stack, including lint/test, succeeds.

View File

@@ -59,3 +59,9 @@
- [ ] **Undo/Redo Stack**: Reversible edits for canvas and inspector changes.
- [ ] **Context Menu**: Right-click actions for node, edge, and canvas.
- [ ] **Performance Tuning**: Virtualized node rendering for large graphs.
## Phase 8: Modern Frontend Platform
- [x] **Flask + Next.js split**: Replace the Jinja-based FastAPI UI with a Flask REST backend and Next.js frontend consuming metadata, translations, workflows, logs, and nav via AJAX.
- [x] **Atomic Next sections**: Compose dashboard, workflow builder, prompt editor, settings, and translation editor into dedicated components powered by localized strings.
- [x] **Workflow templates & navigation JSON**: Serve workflow packages, nav items, and translation mappings from metadata-backed JSON endpoints.
- [ ] **Document build constraints**: Record that `next build --webpack` fails in this sandbox because bundlers attempt to bind new ports, and continue iterating locally.

View File

@@ -20,4 +20,4 @@ RUN poetry config virtualenvs.create false \
&& poetry install --no-interaction --no-ansi
# Run the application
CMD ["python", "src/autometabuilder/main.py"]
CMD ["python", "-m", "autometabuilder"]

View File

@@ -5,7 +5,7 @@ from autometabuilder import load_messages
class TestMetadata(unittest.TestCase):
def test_metadata_exists(self):
metadata_path = os.path.join("backend", "autometabuilder", "metadata.json")
metadata_path = os.path.join("..", "autometabuilder", "metadata.json")
self.assertTrue(os.path.exists(metadata_path))
with open(metadata_path, "r") as f:

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -1,6 +1,6 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "../styles/globals.scss";
import "@/autometabuilder/styles/globals.scss";
const geistSans = Geist({
variable: "--font-geist-sans",

View File

@@ -11,14 +11,14 @@ type DashboardSectionProps = {
export default function DashboardSection({ status, logs, onRun, t }: DashboardSectionProps) {
const [mode, setMode] = useState("once");
const [iterations, setIterations] = useState(1);
const [yolo, setYolo] = useState(true);
const [stopAtMvp, setStopAtMvp] = useState(false);
const [feedback, setFeedback] = useState("");
const handleRun = async () => {
const isYolo = mode === "yolo";
setFeedback(t("ui.dashboard.status.bot_label", "Bot Status") + " — submitting");
try {
await onRun({ mode, iterations, yolo, stop_at_mvp: stopAtMvp });
await onRun({ mode, iterations, yolo: isYolo, stop_at_mvp: stopAtMvp });
setFeedback(t("ui.dashboard.start_bot", "Start Bot") + " " + t("ui.dashboard.status.running", "Running"));
} catch (error) {
console.error(error);

View File

@@ -19,6 +19,13 @@ export default function TranslationsSection({ languages, onRefresh, t }: Transla
const [error, setError] = useState("");
const [newLang, setNewLang] = useState("");
const loadContent = async (lang: string) => {
setError("");
const data = await fetchTranslation(lang);
setEditorValue(JSON.stringify(data.content, null, 2));
};
/* eslint-disable react-hooks/set-state-in-effect */
useEffect(() => {
if (!selected && Object.keys(languages).length) {
setSelected(Object.keys(languages)[0]);
@@ -30,12 +37,7 @@ export default function TranslationsSection({ languages, onRefresh, t }: Transla
loadContent(selected);
}
}, [selected]);
const loadContent = async (lang: string) => {
setError("");
const data = await fetchTranslation(lang);
setEditorValue(JSON.stringify(data.content, null, 2));
};
/* eslint-enable react-hooks/set-state-in-effect */
const handleSave = async () => {
if (!selected) return;

View File

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 128 B

View File

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -1,4 +1,4 @@
@import "./_variables";
@import "variables";
:root {
color: $text;

View File

@@ -3,9 +3,9 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"dev": "next dev --webpack",
"build": "next build --webpack",
"start": "next start --webpack",
"lint": "eslint"
},
"dependencies": {