feat: ekip davranis protokolunu guclendir

This commit is contained in:
2026-03-18 01:05:18 +03:00
parent 2d78b821d0
commit 4d6abff5c4
3 changed files with 142 additions and 51 deletions

View File

@@ -1,35 +1,26 @@
export function buildBootstrapPrompt(projectPath = null) {
const projectContext = projectPath
? `Aktif proje kok dizini: ${projectPath}. Bundan sonra tum analiz, yorum, gorev parcasi ve dosya referanslarini yalnizca bu proje uzerinden yap. Bu proje disina tasma.`
: "Aktif proje henuz secilmedi. Kullanici proje secene kadar dosya baglaminda varsayim yapma.";
? `Aktif proje kok dizini: ${projectPath}. Tum analiz, yorum, gorev parcasi ve dosya referanslarini yalnizca bu proje uzerinden yapin. Bu proje disina tasmayin.`
: "Aktif proje henuz secilmedi. Kullanici proje secene kadar dosya baglaminda varsayim yapmayin.";
return [
'Team agent modunu aktif et.',
'Team icerisindeki roller -> Team Lead: "Mazlum" (erkek), Frontend Developer: "Berkecan" (erkek), Backend Developer: "Simsar" (erkek), UI/UX Designer: "Aybuke" (disi), iOS Developer: "Ive" (erkek) ve takim uyelerine kahveleri getirmesi icin Trainee: "Irgatov" (erkek).',
"Bu takim yapisini aynen koru.",
"Team agent modunu aktif et.",
'Takim: Team Lead: "Mazlum", Frontend Developer: "Berkecan", Backend Developer: "Simsar", UI/UX Designer: "Aybuke", iOS Developer: "Ive", Trainee: "Irgatov".',
projectContext,
"Irgatov teknik ekip uyesi degildir; yalnizca kahve, icecek, ofis ici lojistik ve basit yardim isleriyle ilgilenir.",
"Irgatov kod mimarisi, dosya yapisi, planlama, bug analizi, teknoloji secimi, UI/UX karari, backend karari veya iOS karari vermez.",
"Teknik gorev dagitimi yaparken Irgatov'a teknik is yazma. Irgatov sadece kahve ve lojistik destek icin konussun.",
"Takim ici tum mesajlarda konusan kisi zorunlu olarak ad etiketiyle baslasin.",
"Her cevap yalnizca su formatla baslasin: `Mazlum:` veya `Berkecan:` veya `Simsar:` veya `Aybuke:` veya `Ive:` veya `Irgatov:`.",
"Etiketsiz cevap verme. `Ben`, `Team Lead`, `Frontend Developer`, `UI/UX Designer`, `biz`, `takim olarak` gibi baslangiclar kullanma.",
"Gecerli ornek: `Mazlum: Buradayim.` Gecersiz ornek: `Buradayim.`",
"Kullanici tek bir kisiye seslenirse sadece o kisi cevap versin ve cevabi kendi ad etiketiyle baslatsin.",
"Kullanici tum takima veya genel bir goreve seslenirse once `Mazlum:` cevap versin. Gerekirse digerleri ayri satirlarda kendi ad etiketiyle devam etsin.",
"Her ekip uyesi her mesajda kendi sabit adini kullanir, isim degistirmez.",
"Ilk cevap olarak yalnizca takimin hazir oldugunu ve rollerin aktiflestigini bildir. Bu ilk cevap da `Mazlum:` ile baslasin."
].join(" ");
}
export function buildProjectSelectionPrompt(projectPath) {
return [
"Proje baglami guncellendi.",
`Yeni aktif proje kok dizini: ${projectPath}.`,
"Bu andan itibaren tum yorum, plan, gorev ve kod onerilerini yalnizca bu proje uzerinden yap.",
"Bu proje disinda dosya, klasor veya kod tabani varsayimi yapma.",
"Irgatov bu proje baglaminda da sadece kahve ve lojistik destek verir; teknik gorev almaz.",
"Kullanici yeni bir proje secene kadar bu proje varsayilan tek calisma alanidir.",
"Bu bildirimi tekrar etme; sadece yeni proje baglamina gore calismaya devam et."
"Davranis protokolu:",
"1. Her cevap mutlaka kisi adi etiketiyle baslar. Yalnizca su baslangiclar kullanilir: `Mazlum:`, `Berkecan:`, `Simsar:`, `Aybuke:`, `Ive:`, `Irgatov:`.",
"2. Kullanicinin hitabi daima `Patron`dur. Tum ekip uyeleri kullaniciya konusurken mutlaka `Patron` diye hitap eder ve saygili, olculu, profesyonel bir dil kullanir.",
"3. Ekip uyeleri kendi aralarindaki samimi dili kullaniciya karsi kullanmaz.",
"4. Tum ekip uyeleri Team Lead icin `Mazlum Bey` hitabini kullanir.",
"5. Tum ekip uyeleri UI/UX Designer icin `UI Hanim` hitabini kullanir.",
"6. Team Lead ve UI/UX Designer haric erkek ekip uyeleri kendi aralarinda gerektiginde `Frontend Kanka`, `Backend Kanka`, `iOS Kanka` gibi hitaplar kullanabilir. Bu hitaplar yalnizca ekip ici konusmalarda kullanilir.",
"7. Ekip ici konusmalar hafif esprili ve ofis ortaminda yanina gidip konusuyormus gibi dogal olabilir. Ancak mizah kisa tutulur, teknik dogruluk her zaman once gelir, gereksiz roleplay yapilmaz.",
"8. Ekip ici diyalog yalnizca gerektiginde kisa tutulur. Basit sorularda gereksiz cok kisili diyalog kurma.",
"9. Kullanici tek bir kisiye seslenirse sadece o kisi cevap verir.",
"10. Kullanici tum ekibe veya genel bir goreve seslenirse once Mazlum cevap verir. Gerekirse diger ekip uyeleri kisa katkilar yapar.",
"11. Proje tamamlandiginda, teslim ozeti veya briefing istendiginde son ozet yalnizca Mazlum tarafindan verilir.",
"12. Irgatov teknik ekip uyesi degildir. Irgatov sadece kahve, icecek, servis, basit ofis lojistigi ve yardim isleriyle ilgilenir. Kod, mimari, dosya yapisi, planlama, bug analizi, teknoloji secimi, UI/UX, backend veya iOS konularinda teknik gorus bildirmez.",
"13. Karakter davranisi teknik dogrulugun onune gecmez. Gereksiz tekrar yapma, gereksiz uzun cevap verme, yanlis ama eglenceli cevap verme.",
"14. Ilk cevap yalnizca Mazlum tarafindan verilir. Takimin aktif oldugunu, rollerin hazir oldugunu ve aktif proje dizinini bildirir. Bu ilk cevap `Mazlum:` ile baslar."
].join(" ");
}

View File

@@ -5,46 +5,143 @@ import { buildBootstrapPrompt } from "./bootstrapPrompt.js";
import { LogService } from "./logService.js";
import { PtyService } from "./ptyService.js";
import { getClaudeEnv, getPublicRuntimeConfig } from "./config.js";
import { findMentionedMember } from "./teamConfig.js";
import { findMentionedMember, findMentionedMembers } from "./teamConfig.js";
function cleanChunk(value) {
return stripAnsi(value).replace(/\r/g, "");
}
function normalizeText(value) {
return String(value ?? "")
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.trim();
}
function isLikelyFollowUp(prompt) {
const normalized = String(prompt ?? "").trim();
const normalized = normalizeText(prompt);
const wordCount = normalized.split(/\s+/).filter(Boolean).length;
return [
wordCount <= 8,
/^(evet|hayir|tamam|olur|olsun|sade|sekersiz|şekersiz|detaylandir|detaylandır|devam|peki|neden|nasil|nasıl)\b/i.test(normalized)
/^(evet|hayir|tamam|olur|olsun|sade|sekersiz|detaylandir|detaylandir|devam|peki|neden|nasil|biraz ac|kisalt|ornek ver)\b/i.test(normalized)
].some(Boolean);
}
function buildRoutedPrompt(prompt, lastDirectedMember = null) {
const explicitTarget = findMentionedMember(prompt);
const targetMember = explicitTarget ?? (lastDirectedMember && isLikelyFollowUp(prompt) ? lastDirectedMember : null);
function isBriefingRequest(prompt) {
const normalized = normalizeText(prompt);
return [
"brief",
"briefing",
"ozet",
"durum raporu",
"tamamlandi mi",
"teslim durumu",
"son durum",
"rapor ver",
"breef"
].some((token) => normalized.includes(token));
}
if (!targetMember) {
return {
targetMember: null,
routedPrompt: `Not: Bu genel mesajdir. Once Mazlum cevap versin ve konusan herkes ad etiketi kullansin. Irgatov teknik gorev almaz; sadece kahve ve lojistik destek verir. Kullanici mesaji: ${prompt}`
};
}
function isCoordinationRequest(prompt) {
const normalized = normalizeText(prompt);
return [
"kendi aranizda konusun",
"aranizda konusun",
"toplanin",
"koordine olun",
"birbirinizle konusun",
"tartisin",
"degerlendirin",
"ekipce karar verin",
"kendi aralarinizda",
"kendi aranizda"
].some((token) => normalized.includes(token));
}
function buildGeneralPrompt(prompt) {
return {
mode: "general",
targetMember: null,
routedPrompt: `Yonlendirme notu: Bu mesaj tum ekibe yoneliktir. Once Mazlum cevap versin. Gerekirse diger ekip uyeleri kendi ad etiketiyle kisa katkilar yapsin. Kullaniciya konusurken herkes Patron diye hitap etsin. Ekip ici diyalog sadece gerekiyorsa kisa olsun. Gereksiz roleplay yapma. Sonuc net ve uygulanabilir olsun. Kullanici mesaji: ${prompt}`
};
}
function buildBriefingPrompt(prompt) {
return {
mode: "briefing",
targetMember: null,
routedPrompt: `Yonlendirme notu: Bu mesaj proje ozeti veya teslim briefigi gerektiriyor. Son ozet yalnizca Mazlum tarafindan verilsin. Cevap Mazlum: ile baslasin. Kullaniciya mutlaka Patron diye hitap et. Ozet duzenli, yonetsel ve net olsun. Gerekirse yapilanlar, kalan riskler ve sonraki adimlar kisaca belirtilsin. Diger ekip uyeleri yalnizca zorunluysa kisa katkida bulunsun. Kullanici mesaji: ${prompt}`
};
}
function buildCoordinationPrompt(prompt) {
return {
mode: "coordination",
targetMember: null,
routedPrompt: `Yonlendirme notu: Bu mesaj kisa ekip ici koordinasyon gerektiriyor. Once Mazlum durumu acsin. Gerekirse ilgili ekip uyeleri kendi ad etiketiyle kisa konussun. Ekip ici hitap kurallarini uygula: Mazlum Bey, UI Hanim, erkek ekip uyeleri arasinda gerektiginde Frontend Kanka, Backend Kanka, iOS Kanka. Diyalog kisa olsun. Ardindan net sonuc veya karar acikca verilsin. Kullaniciya donecek cerceve saygili olsun ve Patron hitabi korunsun. Kullanici mesaji: ${prompt}`
};
}
function buildDirectPrompt(prompt, targetMember) {
if (targetMember.name === "Irgatov") {
return {
mode: "irgatov_direct",
targetMember,
routedPrompt: `Not: Bu mesaj Irgatov icindir. Irgatov sadece kahve, icecek, servis ve basit lojistik destek konularinda cevap versin. Teknik plan, kod, mimari veya dosya yapisi onermesin. Cevap \`Irgatov:\` ile baslasin. Kullanici mesaji: ${prompt}`
routedPrompt: `Yonlendirme notu: Bu mesaj Irgatov icindir. Yalnizca Irgatov cevap versin. Cevap Irgatov: ile baslasin. Kullaniciya Patron diye hitap et. Irgatov sadece kahve, icecek, servis ve basit ofis lojistigi konularinda cevap verir. Teknik plan, kod, mimari, dosya yapisi veya teknoloji secimi hakkinda gorus bildirmez. Kullanici mesaji: ${prompt}`
};
}
return {
mode: "direct",
targetMember,
routedPrompt: `Not: Bu mesaj ${targetMember.name} icindir. Yalnizca ${targetMember.name} cevap versin ve cevap \`${targetMember.name}:\` ile baslasin. Kullanici mesaji: ${prompt}`
routedPrompt: `Yonlendirme notu: Bu mesaj dogrudan ${targetMember.name} icindir. Yalnizca ${targetMember.name} cevap versin. Cevap mutlaka ${targetMember.name}: ile baslasin. Kullaniciya mutlaka Patron diye hitap et. Kullaniciya karsi saygili, net ve profesyonel dil kullan. Gereksiz ekip ici diyalog kurma. Gerekirse cok kisa ofis tonu kullanabilirsin ama teknik icerigi golgeleme. Kullanici mesaji: ${prompt}`
};
}
function buildFollowUpPrompt(prompt, lastDirectedMember = null) {
if (!lastDirectedMember) {
return buildGeneralPrompt(prompt);
}
if (lastDirectedMember.name === "Irgatov") {
return buildDirectPrompt(prompt, lastDirectedMember);
}
return {
mode: "follow_up",
targetMember: lastDirectedMember,
routedPrompt: `Yonlendirme notu: Bu mesaj onceki konusmanin devamidir. Mumkunse onceki hedef kisi cevap versin. Cevap mevcut baglami korusun. Kullaniciya mutlaka Patron diye hitap et. Yalnizca gerekliyse kisa cevap ver. Gereksiz yeni ekip diyalogu baslatma. Cevap ${lastDirectedMember.name}: ile baslasin. Kullanici mesaji: ${prompt}`
};
}
function buildRoutedPrompt(prompt, lastDirectedMember = null) {
const mentionedMembers = findMentionedMembers(prompt);
if (isBriefingRequest(prompt)) {
return buildBriefingPrompt(prompt);
}
if (isCoordinationRequest(prompt)) {
return buildCoordinationPrompt(prompt);
}
if (mentionedMembers.length === 1) {
return buildDirectPrompt(prompt, mentionedMembers[0]);
}
if (mentionedMembers.length > 1) {
return buildGeneralPrompt(prompt);
}
if (lastDirectedMember && isLikelyFollowUp(prompt)) {
return buildFollowUpPrompt(prompt, lastDirectedMember);
}
return buildGeneralPrompt(prompt);
}
export class SessionManager {
constructor({ io, config }) {
this.io = io;

View File

@@ -2,9 +2,9 @@ const TEAM_MEMBERS = [
{ id: "mazlum", name: "Mazlum", aliases: ["mazlum", "team lead", "lead"] },
{ id: "berkecan", name: "Berkecan", aliases: ["berkecan", "frontend developer", "frontend"] },
{ id: "simsar", name: "Simsar", aliases: ["simsar", "backend developer", "backend"] },
{ id: "aybuke", name: "Aybuke", aliases: ["aybuke", "aybüke", "ui/ux designer", "designer"] },
{ id: "ive", name: "Ive", aliases: ["ive", "ios developer", "ios"] },
{ id: "irgatov", name: "Irgatov", aliases: ["irgatov", "trainee", "intern"] }
{ id: "aybuke", name: "Aybuke", aliases: ["aybuke", "aybüke", "ui/ux designer", "designer", "ui"] },
{ id: "ive", name: "Ive", aliases: ["ive", "ios developer", "ios", "ioscu", "ios developer"] },
{ id: "irgatov", name: "Irgatov", aliases: ["irgatov", "trainee", "intern", "stajyer"] }
];
function normalizeText(value) {
@@ -14,18 +14,21 @@ function normalizeText(value) {
.toLowerCase();
}
export function findMentionedMember(prompt) {
export function findMentionedMembers(prompt) {
const normalizedPrompt = normalizeText(prompt);
const matches = [];
for (const member of TEAM_MEMBERS) {
for (const alias of member.aliases) {
if (normalizedPrompt.includes(normalizeText(alias))) {
return member;
}
if (member.aliases.some((alias) => normalizedPrompt.includes(normalizeText(alias)))) {
matches.push(member);
}
}
return null;
return matches;
}
export function findMentionedMember(prompt) {
return findMentionedMembers(prompt)[0] ?? null;
}
export { TEAM_MEMBERS };