Paranneltu project-pipelinen promptit ja tiedostoparsinta

Managerin prompti:
- Selkeämpi formaatti: "filename.py: what this file contains"
- Eksplisiittiset säännöt: max 4 tiedostoa, ei polkuja, vain tiedostonimet
- Sallitut tiedostopäätteet: .py, .txt, .json, .html

Tiedostoparsinta tiukennettu:
- Hylkää polut (chucknorris/fastapi/...) — vaatii ettei sisällä /
- Vaatii tiedostopäätteen (.xyz)
- Ei välilyöntejä nimessä

Koodarin prompti:
- "Project:" konteksti ensin, sitten tarkka tiedostokohtainen ohje
- "Write correct, working code. No explanations."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jaakko Vanhala
2026-04-05 20:34:23 +03:00
parent 2e7b86deeb
commit d5ab6272d3

View File

@@ -1878,11 +1878,15 @@
// Vaihe 1: Manageri pilkkoo projektin tiedostoiksi // Vaihe 1: Manageri pilkkoo projektin tiedostoiksi
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`);
pipelineStep('manager', 'Suunnittelu', 'active', task); pipelineStep('manager', 'Suunnittelu', 'active', task);
const managerPrompt = `You are a project manager. Break this task into individual source files. const managerPrompt = `List the source files needed for this project. One file per line, format:
For each file, write one line: FILENAME: description filename.py: what this file contains
List only the essential files (max 5). No explanations.
Task: ${task}`; Rules:
- Max 4 files
- Only .py, .txt, .json, .html files
- No directories, no paths, just filenames
Project: ${task}`;
const plan = await kpnRun(agentPrompts.manager.model, managerPrompt); const plan = await kpnRun(agentPrompts.manager.model, managerPrompt);
if (!plan) { termLog(' ✗ Pipeline keskeytyi (manageri)', '#f85149'); return; } if (!plan) { termLog(' ✗ Pipeline keskeytyi (manageri)', '#f85149'); return; }
pipelineStep('manager', 'Suunnittelu', 'done', task, plan); pipelineStep('manager', 'Suunnittelu', 'done', task, plan);
@@ -1890,12 +1894,17 @@ Task: ${task}`;
// Parsitaan tiedostolista: "FILENAME: description" -rivit // Parsitaan tiedostolista: "FILENAME: description" -rivit
const fileList = plan.split('\n') const fileList = plan.split('\n')
.map(line => line.trim()) .map(line => line.trim())
.filter(line => line.includes(':') && (line.includes('.') || line.includes('/'))) .filter(line => line.includes(':'))
.map(line => { .map(line => {
const [name, ...desc] = line.replace(/^[\d\.\-\*]+\s*/, '').split(':'); const [name, ...desc] = line.replace(/^[\d\.\-\*\s]+/, '').split(':');
return { name: name.trim().replace(/\*+/g, ''), desc: desc.join(':').trim() }; return { name: name.trim().replace(/\*+/g, '').replace(/`/g, ''), desc: desc.join(':').trim() };
}) })
.filter(f => f.name.length > 0 && f.name.length < 50); .filter(f => {
// Tiedostonimen pitää sisältää piste + tunniste, ei polkuja
const n = f.name;
return n.length > 0 && n.length < 40 && !n.includes('/') && !n.includes(' ')
&& /\.\w{1,5}$/.test(n); // Päättyy tiedostopäätteeseen (.py, .txt, .json jne.)
});
if (fileList.length === 0) { if (fileList.length === 0) {
// Fallback: manageri ei tuottanut tiedostolistaa, käytetään koko vastausta ohjeena // Fallback: manageri ei tuottanut tiedostolistaa, käytetään koko vastausta ohjeena
@@ -1926,8 +1935,10 @@ Task: ${task}`;
).join('\n\n') + '\n\n'; ).join('\n\n') + '\n\n';
} }
const coderPrompt = `${context}Write ONLY the file "${file.name}": ${file.desc} const coderPrompt = `${context}Project: ${task}
Project: ${task}`; Write ONLY the file "${file.name}".
Purpose: ${file.desc}
Write correct, working code. No explanations.`;
const code = await kpnRun(agentPrompts.coder.model, coderPrompt); const code = await kpnRun(agentPrompts.coder.model, coderPrompt);
if (!code) { if (!code) {
termLog(` ✗ Pipeline keskeytyi (${file.name})`, '#f85149'); termLog(` ✗ Pipeline keskeytyi (${file.name})`, '#f85149');