Monivaiheinen projektipipeline: kpn project -komento
Uusi kpn project -komento rakentaa ohjelmistoprojektin tiedosto kerrallaan: 1. Manageri pilkkoo projektin tiedostoiksi (max 5) → parsii "FILENAME: description" -rivit 2. Koodari generoi jokaisen tiedoston erikseen → saa kontekstina aiemmin generoidut tiedostot 3. Testaaja arvioi koko projektin → etsii bugeja ja puutteita 4. Korjausluuppi: jos testaaja löytää ongelmia → koodari saa review-palautteen ja korjaa → testaaja arvioi uudelleen Fallback: jos manageri ei tuota tiedostolistaa, generoidaan yhtenä kokonaisuutena. kpn pipeline säilyy yksinkertaisena 3-vaiheisena (manageri → koodari → testaaja). Esimerkkejä: kpn project "FastAPI + SQLite REST API for users" kpn project "Flask todo app with database" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1819,27 +1819,111 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline: manageri → koodari → testaaja
|
// Pipeline: manageri → koodari (per tiedosto) → testaaja → korjausluuppi
|
||||||
async function kpnPipeline(task) {
|
async function kpnPipeline(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>`);
|
||||||
|
|
||||||
// Vaihe 1: Manageri analysoi
|
// Vaihe 1: Manageri pilkkoo projektin tiedostoiksi
|
||||||
termLog(`\n<span style="color:#d29922;font-weight:bold">[1/3] Manageri</span> — tehtävän analyysi`);
|
termLog(`\n<span style="color:#d29922;font-weight:bold">[1] Manageri</span> — projektin suunnittelu`);
|
||||||
const managerPrompt = `Analysoi seuraava ohjelmistokehitystehtävä ja kirjoita koodarille selkeä tekninen ohje mitä koodata. Vastaa lyhyesti.\n\nTehtävä: ${task}`;
|
const managerPrompt = `You are a project manager. Break this task into individual source files.
|
||||||
|
For each file, write one line: FILENAME: description
|
||||||
|
List only the essential files (max 5). No explanations.
|
||||||
|
|
||||||
|
Task: ${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; }
|
||||||
|
|
||||||
// Vaihe 2: Koodari toteuttaa
|
// Parsitaan tiedostolista: "FILENAME: description" -rivit
|
||||||
termLog(`\n<span style="color:#3fb950;font-weight:bold">[2/3] Koodari</span> — toteutus`);
|
const fileList = plan.split('\n')
|
||||||
const coderPrompt = `${plan}\n\nKirjoita koodi yllä olevan ohjeen mukaisesti.`;
|
.map(line => line.trim())
|
||||||
const code = await kpnRun(agentPrompts.coder.model, coderPrompt);
|
.filter(line => line.includes(':') && (line.includes('.') || line.includes('/')))
|
||||||
if (!code) { termLog(' ✗ Pipeline keskeytyi (koodari)', '#f85149'); return; }
|
.map(line => {
|
||||||
|
const [name, ...desc] = line.replace(/^[\d\.\-\*]+\s*/, '').split(':');
|
||||||
|
return { name: name.trim().replace(/\*+/g, ''), desc: desc.join(':').trim() };
|
||||||
|
})
|
||||||
|
.filter(f => f.name.length > 0 && f.name.length < 50);
|
||||||
|
|
||||||
// Vaihe 3: Testaaja arvioi
|
if (fileList.length === 0) {
|
||||||
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[3/3] Testaaja</span> — arviointi`);
|
// Fallback: manageri ei tuottanut tiedostolistaa, käytetään koko vastausta ohjeena
|
||||||
const testerPrompt = `Arvioi seuraava koodi lyhyesti. Onko siinä bugeja? Puuttuuko testejä? Anna arvosana 1-5.\n\nTehtävä: ${task}\n\nKoodi:\n${code}`;
|
termLog(' <span style="color:#8b949e">Ei tiedostojakoa — generoidaan yhtenä kokonaisuutena</span>');
|
||||||
await kpnRun(agentPrompts.tester.model, testerPrompt);
|
termLog(`\n<span style="color:#3fb950;font-weight:bold">[2] Koodari</span> — toteutus`);
|
||||||
|
const code = await kpnRun(agentPrompts.coder.model, `${plan}\n\nWrite the code.`);
|
||||||
|
if (code) {
|
||||||
|
termLog(`\n<span style="color:#a371f7;font-weight:bold">━━━ Pipeline valmis ━━━</span>`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
termLog(` <span style="color:#8b949e">${fileList.length} tiedostoa: ${fileList.map(f => f.name).join(', ')}</span>`);
|
||||||
|
|
||||||
|
// Vaihe 2: Koodari generoi tiedosto kerrallaan, konteksti ketjutetaan
|
||||||
|
const generatedFiles = {};
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
const file = fileList[i];
|
||||||
|
termLog(`\n<span style="color:#3fb950;font-weight:bold">[${i + 2}] Koodari</span> — ${esc(file.name)}`);
|
||||||
|
|
||||||
|
// Rakennetaan konteksti: aiemmin generoidut tiedostot
|
||||||
|
let context = '';
|
||||||
|
const prevFiles = Object.entries(generatedFiles);
|
||||||
|
if (prevFiles.length > 0) {
|
||||||
|
context = 'Already written files:\n' + prevFiles.map(([name, code]) =>
|
||||||
|
`--- ${name} ---\n${code}`
|
||||||
|
).join('\n\n') + '\n\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
const coderPrompt = `${context}Write ONLY the file "${file.name}": ${file.desc}
|
||||||
|
Project: ${task}`;
|
||||||
|
const code = await kpnRun(agentPrompts.coder.model, coderPrompt);
|
||||||
|
if (!code) {
|
||||||
|
termLog(` ✗ Pipeline keskeytyi (${file.name})`, '#f85149');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
generatedFiles[file.name] = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vaihe 3: Testaaja arvioi koko projektin
|
||||||
|
const allCode = Object.entries(generatedFiles)
|
||||||
|
.map(([name, code]) => `--- ${name} ---\n${code}`)
|
||||||
|
.join('\n\n');
|
||||||
|
|
||||||
|
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[${fileList.length + 2}] Testaaja</span> — arviointi`);
|
||||||
|
const reviewPrompt = `Review this project. List bugs or issues. Be brief.
|
||||||
|
If the code is correct, say "LGTM".
|
||||||
|
|
||||||
|
${allCode}`;
|
||||||
|
const review = await kpnRun(agentPrompts.tester.model, reviewPrompt);
|
||||||
|
|
||||||
|
// Vaihe 4: Korjausluuppi — jos testaaja löysi ongelmia
|
||||||
|
if (review && !review.toLowerCase().includes('lgtm') && !review.toLowerCase().includes('looks good')) {
|
||||||
|
termLog(`\n<span style="color:#d29922;font-weight:bold">[${fileList.length + 3}] Koodari</span> — korjaukset`);
|
||||||
|
const fixPrompt = `Fix the issues found in the review.
|
||||||
|
Review feedback: ${review}
|
||||||
|
|
||||||
|
Current code:
|
||||||
|
${allCode}
|
||||||
|
|
||||||
|
Write the corrected code.`;
|
||||||
|
const fixedCode = await kpnRun(agentPrompts.coder.model, fixPrompt);
|
||||||
|
if (fixedCode) {
|
||||||
|
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[${fileList.length + 4}] Testaaja</span> — uudelleenarviointi`);
|
||||||
|
await kpnRun(agentPrompts.tester.model, `Review the corrected code briefly:\n${fixedCode}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
termLog(`\n<span style="color:#a371f7;font-weight:bold">━━━ Pipeline valmis (${Object.keys(generatedFiles).length} tiedostoa) ━━━</span>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yksinkertainen pipeline (vanha: manageri → koodari → testaaja)
|
||||||
|
async function kpnPipelineSimple(task) {
|
||||||
|
termLog(`<span style="color:#a371f7;font-weight:bold">━━━ Pipeline käynnistyy ━━━</span>`);
|
||||||
|
termLog(`\n<span style="color:#d29922;font-weight:bold">[1/3] Manageri</span>`);
|
||||||
|
const plan = await kpnRun(agentPrompts.manager.model, `Analyse this task briefly and write a technical spec for a coder:\n${task}`);
|
||||||
|
if (!plan) return;
|
||||||
|
termLog(`\n<span style="color:#3fb950;font-weight:bold">[2/3] Koodari</span>`);
|
||||||
|
const code = await kpnRun(agentPrompts.coder.model, `${plan}\n\nWrite the code.`);
|
||||||
|
if (!code) return;
|
||||||
|
termLog(`\n<span style="color:#58a6ff;font-weight:bold">[3/3] Testaaja</span>`);
|
||||||
|
await kpnRun(agentPrompts.tester.model, `Review briefly:\n${code}`);
|
||||||
termLog(`\n<span style="color:#a371f7;font-weight:bold">━━━ Pipeline valmis ━━━</span>`);
|
termLog(`\n<span style="color:#a371f7;font-weight:bold">━━━ Pipeline valmis ━━━</span>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1858,7 +1942,8 @@
|
|||||||
if (sub === 'help' || !sub) {
|
if (sub === 'help' || !sub) {
|
||||||
termLog(' kpn hello — iloinen tervehdys verkosta', '#a5d6ff');
|
termLog(' kpn hello — iloinen tervehdys verkosta', '#a5d6ff');
|
||||||
termLog(' kpn run <malli> "<prompti>" — aja tehtävä verkossa', '#a5d6ff');
|
termLog(' kpn run <malli> "<prompti>" — aja tehtävä verkossa', '#a5d6ff');
|
||||||
termLog(' kpn pipeline "<tehtävä>" — manageri → koodari → testaaja', '#a5d6ff');
|
termLog(' kpn pipeline "<tehtävä>" — nopea: manageri → koodari → testaaja', '#a5d6ff');
|
||||||
|
termLog(' kpn project "<kuvaus>" — projekti: tiedostojako + generointi + review', '#a5d6ff');
|
||||||
termLog(' kpn load — lataa kielimalli omalle koneelle', '#a5d6ff');
|
termLog(' kpn load — lataa kielimalli omalle koneelle', '#a5d6ff');
|
||||||
termLog(' kpn status — verkon tila', '#a5d6ff');
|
termLog(' kpn status — verkon tila', '#a5d6ff');
|
||||||
termLog(' kpn models — käytettävissä olevat mallit', '#a5d6ff');
|
termLog(' kpn models — käytettävissä olevat mallit', '#a5d6ff');
|
||||||
@@ -1926,13 +2011,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sub === 'pipeline') {
|
if (sub === 'pipeline') {
|
||||||
const afterPipeline = cmd.replace(/^kpn\s+pipeline\s*/, '');
|
const afterCmd = cmd.replace(/^kpn\s+pipeline\s*/, '');
|
||||||
const pMatch = afterPipeline.match(/^"(.+)"$|^'(.+)'$|^(.+)$/);
|
const pMatch = afterCmd.match(/^"(.+)"$|^'(.+)'$|^(.+)$/);
|
||||||
const pTask = (pMatch && (pMatch[1] || pMatch[2] || pMatch[3] || '')).trim();
|
const pTask = (pMatch && (pMatch[1] || pMatch[2] || pMatch[3] || '')).trim();
|
||||||
if (!pTask) {
|
if (!pTask) {
|
||||||
termLog(' Käyttö: kpn pipeline "<tehtävä>"', '#f85149');
|
termLog(' Käyttö: kpn pipeline "<tehtävä>"', '#f85149');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
kpnPipelineSimple(pTask);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub === 'project') {
|
||||||
|
const afterCmd = cmd.replace(/^kpn\s+project\s*/, '');
|
||||||
|
const pMatch = afterCmd.match(/^"(.+)"$|^'(.+)'$|^(.+)$/);
|
||||||
|
const pTask = (pMatch && (pMatch[1] || pMatch[2] || pMatch[3] || '')).trim();
|
||||||
|
if (!pTask) {
|
||||||
|
termLog(' Käyttö: kpn project "<projektin kuvaus>"', '#f85149');
|
||||||
|
termLog(' Esim: kpn project "FastAPI + SQLite REST API for users"', '#8b949e');
|
||||||
|
return;
|
||||||
|
}
|
||||||
kpnPipeline(pTask);
|
kpnPipeline(pTask);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1969,7 +2067,7 @@
|
|||||||
|
|
||||||
// Tab-completion: ennustava komennonsyöttö sana kerrallaan
|
// Tab-completion: ennustava komennonsyöttö sana kerrallaan
|
||||||
const kpnCommands = {
|
const kpnCommands = {
|
||||||
'kpn': ['help', 'run', 'pipeline', 'load', 'status', 'models', 'hello', 'clear'],
|
'kpn': ['help', 'run', 'project', 'pipeline', 'load', 'status', 'models', 'hello', 'clear'],
|
||||||
'kpn run': ['coder', 'coder-3b', 'manager', 'tester', 'qa', 'data', 'observer', 'qwen-coder', 'qwen-coder-3b', 'smollm-135m', 'qwen-05b', 'phi3-mini'],
|
'kpn run': ['coder', 'coder-3b', 'manager', 'tester', 'qa', 'data', 'observer', 'qwen-coder', 'qwen-coder-3b', 'smollm-135m', 'qwen-05b', 'phi3-mini'],
|
||||||
'kpn load': ['1', '2'],
|
'kpn load': ['1', '2'],
|
||||||
'kpn pipeline': ['"'],
|
'kpn pipeline': ['"'],
|
||||||
@@ -1980,6 +2078,7 @@
|
|||||||
'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 pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
|
'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user