Esimerkkiprojektit HTML UI:lla + Mermaid-kaavio raporttiin + tooltips
- Esimerkkipromptit sisältävät nyt HTML-käyttöliittymän - Manageri generoi index.html tiedoston, Dockerfile kopioi sen - README: docker compose up → http://localhost:8000 - Raporttiin Mermaid-kaavio agenttien workflowsta (CDN) - Pipeline-vaiheiden hover näyttää selityksen Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1459,12 +1459,14 @@ filename.py: one-line description
|
|||||||
CONSTRAINTS: the coder can only generate ~400 tokens per file
|
CONSTRAINTS: the coder can only generate ~400 tokens per file
|
||||||
- Max 3 files (keep it minimal)
|
- Max 3 files (keep it minimal)
|
||||||
- Each file must be SHORT: one clear responsibility, no boilerplate
|
- Each file must be SHORT: one clear responsibility, no boilerplate
|
||||||
- Only .py and pyproject.toml files
|
- Only .py, .html and pyproject.toml files
|
||||||
|
- If the project has a UI, include one index.html served by FastAPI at /
|
||||||
|
|
||||||
EXAMPLE: for "FastAPI todo app with SQLite"
|
EXAMPLE: for "FastAPI todo app with SQLite"
|
||||||
pyproject.toml: project metadata and dependencies
|
pyproject.toml: project metadata and dependencies
|
||||||
models.py: SQLAlchemy models and database setup
|
models.py: SQLAlchemy models and database setup
|
||||||
main.py: FastAPI app with CRUD endpoints
|
main.py: FastAPI app with CRUD endpoints and serves index.html at /
|
||||||
|
index.html: simple HTML UI with forms and fetch() to call the API
|
||||||
|
|
||||||
Project: (käyttäjän kuvaus)` },
|
Project: (käyttäjän kuvaus)` },
|
||||||
coder: { label: 'Koodaus', prompt: `Project: (managerin suunnitelma)
|
coder: { label: 'Koodaus', prompt: `Project: (managerin suunnitelma)
|
||||||
@@ -2168,8 +2170,9 @@ IMPORTANT: Include get_db() dependency for FastAPI` },
|
|||||||
const icon = s.status === 'done' ? '✓' : s.status === 'active' ? '◷' : '◯';
|
const icon = s.status === 'done' ? '✓' : s.status === 'active' ? '◷' : '◯';
|
||||||
const iconColor = s.status === 'done' ? '#3fb950' : s.status === 'active' ? '#d29922' : '#8b949e';
|
const iconColor = s.status === 'done' ? '#3fb950' : s.status === 'active' ? '#d29922' : '#8b949e';
|
||||||
const arrow = i < pipelineSteps.length - 1 ? ' <span style="color:#30363d">→</span> ' : '';
|
const arrow = i < pipelineSteps.length - 1 ? ' <span style="color:#30363d">→</span> ' : '';
|
||||||
// Tooltip: input/output esikatselu
|
const stepDescs = { 'Suunnittelu': 'Pilkkoo tiedostoiksi', 'Review': 'Arvioi koodin laadun', 'Testit': 'Kirjoittaa testit', 'Dockerfile': 'Docker-image', 'Compose': 'Palvelumääritys', 'README': 'Dokumentaatio', 'Validointi': 'Tarkistaa yhteensopivuuden', 'Korjaukset': 'Korjaa löydetyt bugit' };
|
||||||
return `<span onclick="openPipelineStepModal(${i})" style="cursor:pointer;padding:2px 4px;border-radius:3px;transition:background 0.2s" onmouseenter="this.style.background='#21262d'" onmouseleave="this.style.background='transparent'"><span style="color:${iconColor}">${icon}</span> <span style="color:${color}">${esc(s.label)}</span></span>${arrow}`;
|
const desc = stepDescs[s.label] || s.label;
|
||||||
|
return `<span onclick="openPipelineStepModal(${i})" title="${desc}" style="cursor:pointer;padding:2px 4px;border-radius:3px;transition:background 0.2s" onmouseenter="this.style.background='#21262d'" onmouseleave="this.style.background='transparent'"><span style="color:${iconColor}">${icon}</span> <span style="color:${color}">${esc(s.label)}</span></span>${arrow}`;
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2364,14 +2367,16 @@ filename.py: one-line description
|
|||||||
CONSTRAINTS — the coder can only generate ~400 tokens per file:
|
CONSTRAINTS — the coder can only generate ~400 tokens per file:
|
||||||
- Max 3 files (keep it minimal)
|
- Max 3 files (keep it minimal)
|
||||||
- Each file must be SHORT: one clear responsibility, no boilerplate
|
- Each file must be SHORT: one clear responsibility, no boilerplate
|
||||||
- Only .py and pyproject.toml files
|
- Only .py, .html and pyproject.toml files
|
||||||
|
- If the project has a UI, include one index.html served by FastAPI at /
|
||||||
- No directories, no paths, just filenames
|
- No directories, no paths, just filenames
|
||||||
- List dependencies first, then main app
|
- List dependencies first, then main app
|
||||||
|
|
||||||
EXAMPLE for "FastAPI todo app with SQLite":
|
EXAMPLE for "FastAPI todo app with SQLite":
|
||||||
pyproject.toml: project metadata and dependencies
|
pyproject.toml: project metadata and dependencies
|
||||||
models.py: SQLAlchemy models and database setup
|
models.py: SQLAlchemy models and database setup
|
||||||
main.py: FastAPI app with CRUD endpoints
|
main.py: FastAPI app with CRUD endpoints and serves index.html at /
|
||||||
|
index.html: simple HTML UI with forms and fetch() to call the API
|
||||||
|
|
||||||
Project: ${task}`;
|
Project: ${task}`;
|
||||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[1] Manageri</span> — projektin suunnittelu`);
|
termLog(`\n<span style="color:#d29922;font-weight:bold">[1] Manageri</span> — projektin suunnittelu`);
|
||||||
@@ -2646,7 +2651,7 @@ ${Object.entries(generatedFiles).map(([n, c]) => `--- ${n} ---\n${c}`).join('\n\
|
|||||||
const mainFile = Object.keys(generatedFiles).find(f => f.includes('main') || f.includes('app')) || Object.keys(generatedFiles)[0];
|
const mainFile = Object.keys(generatedFiles).find(f => f.includes('main') || f.includes('app')) || Object.keys(generatedFiles)[0];
|
||||||
const hasPyproject = 'pyproject.toml' in generatedFiles;
|
const hasPyproject = 'pyproject.toml' in generatedFiles;
|
||||||
const hasRequirements = 'requirements.txt' in generatedFiles;
|
const hasRequirements = 'requirements.txt' in generatedFiles;
|
||||||
const pyFiles = Object.keys(generatedFiles).filter(f => f.endsWith('.py'));
|
const codeFiles = Object.keys(generatedFiles).filter(f => f.endsWith('.py') || f.endsWith('.html'));
|
||||||
// Dockerfile-templatti: ei anneta mallin keksiä omaa
|
// Dockerfile-templatti: ei anneta mallin keksiä omaa
|
||||||
let depLines;
|
let depLines;
|
||||||
if (hasPyproject) {
|
if (hasPyproject) {
|
||||||
@@ -2660,7 +2665,7 @@ ${Object.entries(generatedFiles).map(([n, c]) => `--- ${n} ---\n${c}`).join('\n\
|
|||||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
${depLines}
|
${depLines}
|
||||||
COPY ${pyFiles.join(' ')} ./
|
COPY ${codeFiles.join(' ')} ./
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
CMD ["uv", "run", "uvicorn", "${mainFile.replace('.py','')}:app", "--host", "0.0.0.0", "--port", "8000"]`;
|
CMD ["uv", "run", "uvicorn", "${mainFile.replace('.py','')}:app", "--host", "0.0.0.0", "--port", "8000"]`;
|
||||||
// Generoidaan Dockerfile suoraan templatesta, ei mallilla
|
// Generoidaan Dockerfile suoraan templatesta, ei mallilla
|
||||||
@@ -2694,8 +2699,8 @@ volumes:
|
|||||||
pipelineStep('tester', 'README', 'active', 'README.md');
|
pipelineStep('tester', 'README', 'active', 'README.md');
|
||||||
const readmePrompt = `Write a minimal README.md. Include ONLY:
|
const readmePrompt = `Write a minimal README.md. Include ONLY:
|
||||||
1. One-line description
|
1. One-line description
|
||||||
2. Quick start: docker compose up
|
2. Quick start: docker compose up → open http://localhost:8000
|
||||||
3. Development: uv sync && uv run uvicorn main:app --reload
|
3. Development: uv sync && uv run uvicorn main:app --reload → http://localhost:8000
|
||||||
4. API endpoints (if applicable)
|
4. API endpoints (if applicable)
|
||||||
5. Testing: uv run pytest
|
5. Testing: uv run pytest
|
||||||
Max 20 lines.
|
Max 20 lines.
|
||||||
@@ -2822,6 +2827,11 @@ ${fixableFiles}`;
|
|||||||
<h1>🔥 Kipinä Projektiraportti</h1>
|
<h1>🔥 Kipinä Projektiraportti</h1>
|
||||||
<p style="color:#8b949e;margin-bottom:20px">${task} — ${new Date().toLocaleString('fi-FI')} — ${fileEntries.length} tiedostoa, ${steps.length} vaihetta</p>
|
<p style="color:#8b949e;margin-bottom:20px">${task} — ${new Date().toLocaleString('fi-FI')} — ${fileEntries.length} tiedostoa, ${steps.length} vaihetta</p>
|
||||||
|
|
||||||
|
<h2>🔄 Agenttien workflow</h2>
|
||||||
|
<div class="mermaid">
|
||||||
|
${generateMermaidDiagram(steps)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>📋 Pipeline-vaiheet</h2>
|
<h2>📋 Pipeline-vaiheet</h2>
|
||||||
${stepsHtml}
|
${stepsHtml}
|
||||||
|
|
||||||
@@ -2833,9 +2843,40 @@ ${filesHtml}
|
|||||||
|
|
||||||
<hr style="border-color:#30363d;margin:24px 0">
|
<hr style="border-color:#30363d;margin:24px 0">
|
||||||
<p style="color:#8b949e;font-size:12px">Generoitu Kipinä Agentic Playground v0.2.2 — <a href="https://kipina.studio">kipina.studio</a></p>
|
<p style="color:#8b949e;font-size:12px">Generoitu Kipinä Agentic Playground v0.2.2 — <a href="https://kipina.studio">kipina.studio</a></p>
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
||||||
|
mermaid.initialize({ startOnLoad: true, theme: 'dark', themeVariables: { primaryColor: '#58a6ff', primaryTextColor: '#c9d1d9', lineColor: '#30363d', primaryBorderColor: '#30363d' } });
|
||||||
|
</script>
|
||||||
</body></html>`;
|
</body></html>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateMermaidDiagram(steps) {
|
||||||
|
const agentLabels = { manager: 'Manageri', coder: 'Koodari', tester: 'DevOps', qa: 'QA', data: 'Data' };
|
||||||
|
const agentStyles = { manager: 'fill:#1c1206,stroke:#d29922,color:#d29922', coder: 'fill:#0d1a0d,stroke:#3fb950,color:#3fb950', tester: 'fill:#0d1117,stroke:#58a6ff,color:#58a6ff', qa: 'fill:#170d22,stroke:#a371f7,color:#a371f7', data: 'fill:#1a0d22,stroke:#d2a8ff,color:#d2a8ff' };
|
||||||
|
|
||||||
|
let lines = ['graph TD'];
|
||||||
|
let prevId = null;
|
||||||
|
const usedAgents = new Set();
|
||||||
|
|
||||||
|
steps.forEach((s, i) => {
|
||||||
|
const id = `S${i}`;
|
||||||
|
const agent = agentLabels[s.agent] || s.agent;
|
||||||
|
const label = s.label.replace(/"/g, "'");
|
||||||
|
usedAgents.add(s.agent);
|
||||||
|
|
||||||
|
lines.push(` ${id}["${agent}: ${label}"]`);
|
||||||
|
if (prevId) {
|
||||||
|
lines.push(` ${prevId} --> ${id}`);
|
||||||
|
}
|
||||||
|
if (agentStyles[s.agent]) {
|
||||||
|
lines.push(` style ${id} ${agentStyles[s.agent]}`);
|
||||||
|
}
|
||||||
|
prevId = id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return lines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
// Yksinkertainen pipeline (vanha: manageri → koodari → testaaja)
|
// Yksinkertainen pipeline (vanha: manageri → koodari → testaaja)
|
||||||
async function kpnPipelineSimple(task) {
|
async function kpnPipelineSimple(task) {
|
||||||
termLog(`<span style="color:#a371f7;font-weight:bold">━━━ Pipeline käynnistyy ━━━</span>`);
|
termLog(`<span style="color:#a371f7;font-weight:bold">━━━ Pipeline käynnistyy ━━━</span>`);
|
||||||
@@ -3152,7 +3193,7 @@ ${filesHtml}
|
|||||||
'kpn run coder-3b': ['"binary search tree in rust"', '"REST API with Flask"', '"async web scraper in python"'],
|
'kpn run coder-3b': ['"binary search tree in rust"', '"REST API with Flask"', '"async web scraper in python"'],
|
||||||
'kpn run manager': ['"suunnittele REST API"', '"priorisoi tiimin tehtävät"'],
|
'kpn run manager': ['"suunnittele REST API"', '"priorisoi tiimin tehtävät"'],
|
||||||
'kpn run tester': ['"testaa login-toiminto"'],
|
'kpn run tester': ['"testaa login-toiminto"'],
|
||||||
'kpn project': ['"FastAPI + SQLite REST API for users"', '"Flask todo app with database"', '"CLI tool for CSV processing in Python"'],
|
'kpn project': ['"FastAPI + SQLite REST API for users with HTML UI at /"', '"Flask todo app with Jinja2 templates and form UI"', '"FastAPI bookmark manager with HTML frontend and search"'],
|
||||||
'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
|
'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user