diff --git a/network-poc/static/index.html b/network-poc/static/index.html
index 3f75b4e..0551000 100644
--- a/network-poc/static/index.html
+++ b/network-poc/static/index.html
@@ -1255,8 +1255,9 @@
const lastPromptText = document.getElementById('agent-last-prompt-text');
if (selectedAgents.size === 1) {
const agent = [...selectedAgents][0];
- const lastStep = [...pipelineSteps].reverse().find(s => s.agent === agent && s.status === 'done' && s.input);
- lastPromptDiv.style.display = lastStep ? 'block' : 'none';
+ // Näytetään aina jos agentilla on oletusprompt tai pipeline-historia
+ const hasPrompt = defaultPipelinePrompts[agent] || pipelineSteps.some(s => s.agent === agent);
+ lastPromptDiv.style.display = hasPrompt ? 'block' : 'none';
} else {
lastPromptDiv.style.display = 'none';
}
@@ -1376,6 +1377,21 @@
window.openPromptModal = openPromptModal;
function closePromptModal() {
+ // Tallennetaan muokattu prompti localStorageen
+ if (modalAgent) {
+ const fields = document.getElementById('prompt-modal-fields');
+ const textareas = fields.querySelectorAll('textarea');
+ const parts = [];
+ textareas.forEach((ta, i) => {
+ const key = modalPromptParts[i]?.key || '';
+ parts.push(`${key}: ${ta.value.trim()}`);
+ });
+ const assembled = parts.join('\n\n');
+ if (defaultPipelinePrompts[modalAgent]) {
+ defaultPipelinePrompts[modalAgent].prompt = assembled;
+ localStorage.setItem('kpn-pipeline-prompt-' + modalAgent, assembled);
+ }
+ }
document.getElementById('prompt-modal').style.display = 'none';
}
window.closePromptModal = closePromptModal;
@@ -1399,11 +1415,93 @@
window.rerunFromModal = rerunFromModal;
// "Näytä prompti" -nappi avaa modalin
+ // Oletuspromptit jokaiselle agentille — näkyvät aina, muokattavissa
+ const defaultPipelinePrompts = {
+ manager: { label: 'Suunnittelu', prompt: `List the source files needed for this project. One file per line, format:
+filename.py: one-line description
+
+CONSTRAINTS: the coder can only generate ~400 tokens per file
+- Max 3 files (keep it minimal)
+- Each file must be SHORT: one clear responsibility, no boilerplate
+- Only .py and pyproject.toml files
+
+EXAMPLE: for "FastAPI todo app with SQLite"
+pyproject.toml: project metadata and dependencies
+models.py: SQLAlchemy models and database setup
+main.py: FastAPI app with CRUD endpoints
+
+Project: (käyttäjän kuvaus)` },
+ coder: { label: 'Koodaus', prompt: `Project: (managerin suunnitelma)
+Write ONLY the file "filename.py": description
+
+EXAMPLE: output for a main.py
+from fastapi import FastAPI, Depends
+from sqlalchemy.orm import Session
+from models import get_db, User
+
+app = FastAPI()
+
+@app.get("/users")
+def list_users(db: Session = Depends(get_db)):
+ return db.query(User).all()
+
+IMPORTANT: Keep the code SHORT. Max ~50 lines. No comments, no docstrings. Output ONLY code.` },
+ qa: { label: 'Testit + Validointi', prompt: `Write test_app.py using pytest and FastAPI TestClient. Max 3 tests. Output ONLY code.
+
+EXAMPLE:
+from fastapi.testclient import TestClient
+from main import app
+client = TestClient(app)
+
+def test_create():
+ r = client.post("/users", params={"name": "Test"})
+ assert r.status_code == 200
+
+IMPORTANT: Check consistency
+1. Dockerfile COPY: references files that exist
+2. Dockerfile deps vs imports: all imports covered
+3. docker-compose ports: match EXPOSE
+4. Test imports: match actual module names
+5. pyproject.toml deps: cover all imports` },
+ tester: { label: 'DevOps', prompt: `docker-compose.yml: services, volumes, port mappings
+
+EXAMPLE:
+services:
+ app:
+ build: .
+ ports:
+ - "8000:8000"
+ restart: unless-stopped
+
+README.md: Quick start, development, API endpoints, testing
+IMPORTANT: Use uv for package management (uv sync, uv run)` },
+ data: { label: 'Data', prompt: `SQLAlchemy models and database setup.
+
+EXAMPLE:
+from sqlalchemy import create_engine, Column, Integer, String
+from sqlalchemy.orm import sessionmaker, declarative_base
+
+engine = create_engine("sqlite:///app.db")
+Base = declarative_base()
+
+IMPORTANT: Include get_db() dependency for FastAPI` },
+ };
+
+ // Ladataan muokatut promptit localStoragesta
+ for (const [agent, def] of Object.entries(defaultPipelinePrompts)) {
+ const saved = localStorage.getItem('kpn-pipeline-prompt-' + agent);
+ if (saved) def.prompt = saved;
+ }
+
document.getElementById('agent-open-modal-btn')?.addEventListener('click', () => {
if (selectedAgents.size !== 1) return;
const agent = [...selectedAgents][0];
+ // Viimeisin pipeline-prompti tai oletusprompt
const lastStep = [...pipelineSteps].reverse().find(s => s.agent === agent && s.status === 'done' && s.input);
- if (lastStep) openPromptModal(agent, lastStep.label, lastStep.input);
+ const def = defaultPipelinePrompts[agent];
+ const prompt = lastStep?.input || def?.prompt || agentPrompts[agent]?.prompt || '';
+ const label = lastStep?.label || def?.label || 'Prompti';
+ openPromptModal(agent, label, prompt);
});
function checkAgentConfusion() {
@@ -2034,11 +2132,18 @@
const iconColor = s.status === 'done' ? '#3fb950' : s.status === 'active' ? '#d29922' : '#8b949e';
const arrow = i < pipelineSteps.length - 1 ? ' → ' : '';
// Tooltip: input/output esikatselu
- const tip = esc(`${s.label}\nInput: ${(s.input || '').substring(0, 150)}\nOutput: ${(s.output || '').substring(0, 150)}`).replace(/\n/g, '
');
- return `${icon} ${esc(s.label)}${arrow}`;
+ return `${icon} ${esc(s.label)}${arrow}`;
}).join('');
}
+ window.openPipelineStepModal = function(idx) {
+ const s = pipelineSteps[idx];
+ if (!s) return;
+ // Näytetään modal promptilla (input) ja tuloksella (output)
+ const combined = (s.input ? s.input : '') + (s.output ? '\n\n--- Tulos ---\n' + s.output : '');
+ openPromptModal(s.agent, s.label, combined);
+ };
+
function pipelineClear() {
pipelineSteps.length = 0;
const container = document.getElementById('pipeline-steps');