wormclaude 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -0
- package/dist/agent.js +61 -0
- package/dist/api.js +163 -0
- package/dist/auth.js +108 -0
- package/dist/cli.js +851 -0
- package/dist/commands.js +540 -0
- package/dist/compact.js +53 -0
- package/dist/i18n.js +177 -0
- package/dist/learn.js +47 -0
- package/dist/links.js +31 -0
- package/dist/mcp.js +104 -0
- package/dist/memory.js +135 -0
- package/dist/skills.js +275 -0
- package/dist/tasks.js +63 -0
- package/dist/theme.js +11 -0
- package/dist/tips.js +60 -0
- package/dist/toolSummary.js +24 -0
- package/dist/tools.js +1136 -0
- package/dist/usage.js +71 -0
- package/package.json +44 -0
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Çoklu dil (TR/EN) — arayüz metinleri. İlk açılışta dil seçilir, .wormclaude/settings.json'a yazılır.
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
let current = 'tr';
|
|
5
|
+
export function setLang(l) { current = l; }
|
|
6
|
+
export function getLang() { return current; }
|
|
7
|
+
// Klasör bazlı ayar: her proje klasöründe bir kez sorulur (.wormclaude/settings.json)
|
|
8
|
+
const SETTINGS = path.join(process.cwd(), '.wormclaude', 'settings.json');
|
|
9
|
+
export function loadLang() {
|
|
10
|
+
try {
|
|
11
|
+
const j = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
|
|
12
|
+
if (j.lang === 'tr' || j.lang === 'en')
|
|
13
|
+
return j.lang;
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
export function saveLang(l) {
|
|
19
|
+
let j = {};
|
|
20
|
+
try {
|
|
21
|
+
j = JSON.parse(fs.readFileSync(SETTINGS, 'utf8'));
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
j.lang = l;
|
|
25
|
+
try {
|
|
26
|
+
fs.mkdirSync(path.dirname(SETTINGS), { recursive: true });
|
|
27
|
+
fs.writeFileSync(SETTINGS, JSON.stringify(j, null, 2));
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
}
|
|
31
|
+
const STR = {
|
|
32
|
+
tr: {
|
|
33
|
+
'lang.title': 'Dil seçin / Select language',
|
|
34
|
+
'lang.tr': '1. Türkçe',
|
|
35
|
+
'lang.en': '2. English',
|
|
36
|
+
'lang.hint': 'Enter onayla · ↑↓ veya 1-2 seç',
|
|
37
|
+
'trust.accessing': 'Çalışma alanına erişiliyor:',
|
|
38
|
+
'trust.check': 'Güvenlik kontrolü: Bu, senin oluşturduğun ya da güvendiğin bir proje mi?\n(Kendi kodun, bilinen bir açık-kaynak proje, ya da ekibinin işi gibi.)\nDeğilse devam etmeden içeriğine bak.',
|
|
39
|
+
'trust.canDo': 'WormClaude bu klasörde dosya okuyup, düzenleyip komut çalıştırabilir.',
|
|
40
|
+
'trust.yes': '1. Evet, bu klasöre güveniyorum',
|
|
41
|
+
'trust.no': '2. Hayır, çık',
|
|
42
|
+
'trust.hint': 'Enter onayla · Esc iptal · ↑↓ veya 1-2 seç',
|
|
43
|
+
'banner.subtitle': 'uncensored security + code · /help komutlar',
|
|
44
|
+
'input.placeholder': 'bir şey yaz... (/ komutlar)',
|
|
45
|
+
'menu.navHint': '↑↓ gez · Enter seç',
|
|
46
|
+
'menu.noCmd': '(komut yok)',
|
|
47
|
+
'menu.moreUp': '↑ {0} daha…',
|
|
48
|
+
'menu.moreDown': '↓ {0} daha…',
|
|
49
|
+
'perm.title': 'İzin gerekiyor: {0}',
|
|
50
|
+
'perm.yes': '1. Evet, çalıştır',
|
|
51
|
+
'perm.yesAlways': '2. Evet, bu araç için bir daha sorma',
|
|
52
|
+
'perm.no': '3. Hayır — bunun yerine ne yapılacağını söyle',
|
|
53
|
+
'perm.hint': '↑↓ / 1-3 seç · Enter onayla · Esc reddet',
|
|
54
|
+
'perm.feedbackPrompt': 'Ne yapılmasını istiyorsun? (boş bırakıp Enter = sadece reddet)',
|
|
55
|
+
'perm.feedbackPlaceholder': 'yönlendirmeni yaz...',
|
|
56
|
+
'note.interrupted': 'Kesildi (Esc).',
|
|
57
|
+
'note.autoCompacted': 'Bağlam otomatik özetlendi (oto-compact).',
|
|
58
|
+
'note.reactive': 'Bağlam taştı — özetlenip yeniden deneniyor (reactive compact).',
|
|
59
|
+
'note.budget': 'Bütçe limiti aşıldı (${0}). Durduruldu.',
|
|
60
|
+
'note.maxTurns': 'Maks tur ({0}) ulaşıldı.',
|
|
61
|
+
'note.cmdErr': 'Komut hatası: {0}',
|
|
62
|
+
'phase.autoCompact': 'Oto-compact (bağlam doluyor)',
|
|
63
|
+
'phase.cmd': 'Komut: {0}',
|
|
64
|
+
'phase.toolsMulti': '{0} araç çalışıyor',
|
|
65
|
+
'phase.toolRun': 'Çalıştırıyor: {0}',
|
|
66
|
+
'mcp.connected': 'MCP: {0} sunucu bağlı, {1} araç',
|
|
67
|
+
'mcp.errors': ' · {0} hata ({1})',
|
|
68
|
+
'pill.running': '⏳ {0} çalışıyor',
|
|
69
|
+
'pill.done': '✓ {0} görev bitti',
|
|
70
|
+
},
|
|
71
|
+
en: {
|
|
72
|
+
'lang.title': 'Select language / Dil seçin',
|
|
73
|
+
'lang.tr': '1. Türkçe',
|
|
74
|
+
'lang.en': '2. English',
|
|
75
|
+
'lang.hint': 'Enter to confirm · ↑↓ or 1-2 to select',
|
|
76
|
+
'trust.accessing': 'Accessing workspace:',
|
|
77
|
+
'trust.check': 'Security check: Is this a project you created or trust?\n(Your own code, a known open-source project, or your team\'s work.)\nIf not, review its contents before continuing.',
|
|
78
|
+
'trust.canDo': 'WormClaude can read, edit files and run commands in this folder.',
|
|
79
|
+
'trust.yes': '1. Yes, I trust this folder',
|
|
80
|
+
'trust.no': '2. No, exit',
|
|
81
|
+
'trust.hint': 'Enter to confirm · Esc to cancel · ↑↓ or 1-2 to select',
|
|
82
|
+
'banner.subtitle': 'uncensored security + code · /help commands',
|
|
83
|
+
'input.placeholder': 'type something... (/ for commands)',
|
|
84
|
+
'menu.navHint': '↑↓ move · Enter select',
|
|
85
|
+
'menu.noCmd': '(no command)',
|
|
86
|
+
'menu.moreUp': '↑ {0} more…',
|
|
87
|
+
'menu.moreDown': '↓ {0} more…',
|
|
88
|
+
'perm.title': 'Permission needed: {0}',
|
|
89
|
+
'perm.yes': '1. Yes, run it',
|
|
90
|
+
'perm.yesAlways': '2. Yes, don\'t ask again for this tool',
|
|
91
|
+
'perm.no': '3. No, suggest changes',
|
|
92
|
+
'perm.hint': '↑↓ / 1-3 select · Enter confirm · Esc deny',
|
|
93
|
+
'perm.feedbackPrompt': 'What should it do instead? (leave empty + Enter = just reject)',
|
|
94
|
+
'perm.feedbackPlaceholder': 'type your guidance...',
|
|
95
|
+
'note.interrupted': 'Interrupted (Esc).',
|
|
96
|
+
'note.autoCompacted': 'Context auto-summarized (auto-compact).',
|
|
97
|
+
'note.reactive': 'Context overflowed — summarizing and retrying (reactive compact).',
|
|
98
|
+
'note.budget': 'Budget limit exceeded (${0}). Stopped.',
|
|
99
|
+
'note.maxTurns': 'Max turns ({0}) reached.',
|
|
100
|
+
'note.cmdErr': 'Command error: {0}',
|
|
101
|
+
'phase.autoCompact': 'Auto-compact (context filling)',
|
|
102
|
+
'phase.cmd': 'Command: {0}',
|
|
103
|
+
'phase.toolsMulti': '{0} tools running',
|
|
104
|
+
'phase.toolRun': 'Running: {0}',
|
|
105
|
+
'mcp.connected': 'MCP: {0} server(s) connected, {1} tool(s)',
|
|
106
|
+
'mcp.errors': ' · {0} error ({1})',
|
|
107
|
+
'pill.running': '⏳ {0} running',
|
|
108
|
+
'pill.done': '✓ {0} task(s) done',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
// Komut açıklamaları (slash menüsü + /help)
|
|
112
|
+
const CMD_DESC = {
|
|
113
|
+
tr: {
|
|
114
|
+
'/help': 'komutları ve ipuçlarını göster',
|
|
115
|
+
'/clear': 'sohbeti ve geçmişi temizle',
|
|
116
|
+
'/compact': 'geçmişi modelle özetleyip bağlamı küçült',
|
|
117
|
+
'/context': 'bağlam / token kullanımını göster',
|
|
118
|
+
'/cost': 'oturum token ve maliyetini göster',
|
|
119
|
+
'/config': 'ayarları göster / değiştir (url, key, model)',
|
|
120
|
+
'/model': 'modeli göster / değiştir',
|
|
121
|
+
'/diff': 'git diff göster',
|
|
122
|
+
'/commit': 'stage edilmiş değişiklikleri modelle commit et',
|
|
123
|
+
'/review': 'mevcut diff’i modelle incele',
|
|
124
|
+
'/init': 'projeyi tarayıp WORMCLAUDE.md üret',
|
|
125
|
+
'/memory': 'WORMCLAUDE.md hafızasını oku / satır ekle',
|
|
126
|
+
'/doctor': 'sistem ve backend sağlık kontrolü',
|
|
127
|
+
'/mcp': 'MCP sunucularını listele / yeniden bağlan',
|
|
128
|
+
'/tasks': 'arka plan görevlerini (shell/agent) listele',
|
|
129
|
+
'/kill': 'bir arka plan görevini durdur: /kill <id>',
|
|
130
|
+
'/dream': 'arka planda hafızayı konsolide et',
|
|
131
|
+
'/lang': 'arayüz dilini değiştir (tr/en)',
|
|
132
|
+
'/skills': 'skill\'leri listele / indir / yeniden yükle',
|
|
133
|
+
'/skillify': 'bu oturumu yeniden kullanılabilir skill\'e çevir',
|
|
134
|
+
'/learn': 'web-öğrenme datasını göster / aç-kapa (eğitim için)',
|
|
135
|
+
'/agent': 'tek bir alt-agent\'a görev ver: /agent <görev>',
|
|
136
|
+
'/multi-agent': 'paralel çoklu-agent koordinasyonu: /multi-agent <görev>',
|
|
137
|
+
'/export': 'sohbeti dosyaya kaydet',
|
|
138
|
+
'/resume': 'en son kaydedilen oturumu yükle',
|
|
139
|
+
'/quit': 'çıkış',
|
|
140
|
+
},
|
|
141
|
+
en: {
|
|
142
|
+
'/help': 'show commands and tips',
|
|
143
|
+
'/clear': 'clear conversation and history',
|
|
144
|
+
'/compact': 'summarize history with the model to shrink context',
|
|
145
|
+
'/context': 'show context / token usage',
|
|
146
|
+
'/cost': 'show session tokens and cost',
|
|
147
|
+
'/config': 'show / change settings (url, key, model)',
|
|
148
|
+
'/model': 'show / change the model',
|
|
149
|
+
'/diff': 'show git diff',
|
|
150
|
+
'/commit': 'commit staged changes with a model-written message',
|
|
151
|
+
'/review': 'review the current diff with the model',
|
|
152
|
+
'/init': 'scan the project and generate WORMCLAUDE.md',
|
|
153
|
+
'/memory': 'read WORMCLAUDE.md memory / append a line',
|
|
154
|
+
'/doctor': 'system and backend health check',
|
|
155
|
+
'/mcp': 'list MCP servers / reconnect',
|
|
156
|
+
'/tasks': 'list background tasks (shell/agent)',
|
|
157
|
+
'/kill': 'stop a background task: /kill <id>',
|
|
158
|
+
'/dream': 'consolidate memory in the background',
|
|
159
|
+
'/lang': 'change interface language (tr/en)',
|
|
160
|
+
'/skills': 'list / install / reload skills',
|
|
161
|
+
'/skillify': 'turn this session into a reusable skill',
|
|
162
|
+
'/learn': 'show / toggle web-learning training data',
|
|
163
|
+
'/agent': 'give a task to one sub-agent: /agent <task>',
|
|
164
|
+
'/multi-agent': 'parallel multi-agent coordination: /multi-agent <task>',
|
|
165
|
+
'/export': 'save the conversation to a file',
|
|
166
|
+
'/resume': 'load the most recently saved session',
|
|
167
|
+
'/quit': 'exit',
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
export function t(key, ...args) {
|
|
171
|
+
let s = STR[current][key] ?? STR.tr[key] ?? key;
|
|
172
|
+
args.forEach((a, i) => { s = s.replace(`{${i}}`, String(a)); });
|
|
173
|
+
return s;
|
|
174
|
+
}
|
|
175
|
+
export function cmdDesc(name) {
|
|
176
|
+
return CMD_DESC[current][name] ?? CMD_DESC.tr[name] ?? '';
|
|
177
|
+
}
|
package/dist/learn.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Öğrenme döngüsü — model web'de arayıp cevap üretince, {soru→cevap} çiftini
|
|
2
|
+
// SFT eğitim formatında (instruction/response/kat/len) dosyaya yazar.
|
|
3
|
+
// Sonra modelegitim/build_sft + LoRA ile model bu konuyu KALICI öğrenir.
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
const DIR = path.join(process.cwd(), '.wormclaude', 'training');
|
|
7
|
+
const FILE = path.join(DIR, 'learned.jsonl');
|
|
8
|
+
let enabled = process.env.WORMCLAUDE_LEARN !== '0'; // varsayılan açık
|
|
9
|
+
let count = 0;
|
|
10
|
+
try {
|
|
11
|
+
count = fs.readFileSync(FILE, 'utf8').split('\n').filter(Boolean).length;
|
|
12
|
+
}
|
|
13
|
+
catch { }
|
|
14
|
+
export function isLearnEnabled() { return enabled; }
|
|
15
|
+
export function setLearnEnabled(b) { enabled = b; }
|
|
16
|
+
export function getLearnFile() { return FILE; }
|
|
17
|
+
export function getLearnCount() { return count; }
|
|
18
|
+
// Web-temelli bir Q&A çiftini eğitim datasına ekle (yerel + backend).
|
|
19
|
+
export function recordLearned(instruction, response, sources = [], config) {
|
|
20
|
+
if (!enabled)
|
|
21
|
+
return false;
|
|
22
|
+
const instr = (instruction || '').trim();
|
|
23
|
+
const resp = (response || '').trim();
|
|
24
|
+
// Çöp/çok kısa kayıtları alma
|
|
25
|
+
if (instr.length < 3 || resp.length < 40)
|
|
26
|
+
return false;
|
|
27
|
+
if (/^\/[a-z]/i.test(instr))
|
|
28
|
+
return false; // slash komutu değil
|
|
29
|
+
const src = sources.slice(0, 8);
|
|
30
|
+
// 1) Yerel kopya (offline cache)
|
|
31
|
+
try {
|
|
32
|
+
fs.mkdirSync(DIR, { recursive: true });
|
|
33
|
+
fs.appendFileSync(FILE, JSON.stringify({ instruction: instr, response: resp, kat: 'web', len: resp.length, ts: Date.now(), sources: src }) + '\n');
|
|
34
|
+
count++;
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
// 2) Backend'e gönder (merkezi korpus — fine-tune için). Fire-and-forget.
|
|
38
|
+
if (config && process.env.WORMCLAUDE_LEARN_REMOTE !== '0') {
|
|
39
|
+
fetch(`${config.baseUrl}/learn`, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json', ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}) },
|
|
42
|
+
body: JSON.stringify({ instruction: instr, response: resp, sources: src, kat: 'web' }),
|
|
43
|
+
signal: AbortSignal.timeout(8000),
|
|
44
|
+
}).catch(() => { });
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
package/dist/links.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Tıklanabilir terminal linkleri (OSC8) — Claude Code ink/supports-hyperlinks.ts uyarlaması.
|
|
2
|
+
// Destekleyen terminallerde metindeki URL'leri/markdown linkleri tıklanabilir yapar.
|
|
3
|
+
const HYPERLINK_TERMINALS = ['ghostty', 'Hyper', 'kitty', 'alacritty', 'iTerm.app', 'iTerm2', 'WezTerm', 'vscode'];
|
|
4
|
+
export function supportsHyperlinks() {
|
|
5
|
+
const env = process.env;
|
|
6
|
+
if (env.WORMCLAUDE_HYPERLINKS === '0')
|
|
7
|
+
return false;
|
|
8
|
+
if (env.WORMCLAUDE_HYPERLINKS === '1' || env.FORCE_HYPERLINK)
|
|
9
|
+
return true;
|
|
10
|
+
if (env.WT_SESSION)
|
|
11
|
+
return true; // Windows Terminal
|
|
12
|
+
if (env.TERM_PROGRAM === 'Apple_Terminal')
|
|
13
|
+
return false;
|
|
14
|
+
const tp = env.TERM_PROGRAM || '';
|
|
15
|
+
const lc = env.LC_TERMINAL || '';
|
|
16
|
+
if (HYPERLINK_TERMINALS.some((x) => tp.includes(x) || lc.includes(x)))
|
|
17
|
+
return true;
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
// OSC8: ESC ] 8 ;; URL ESC \ TEXT ESC ] 8 ;; ESC \
|
|
21
|
+
const ESC = '';
|
|
22
|
+
export function osc8(url, text) {
|
|
23
|
+
return `${ESC}]8;;${url}${ESC}\\${text}${ESC}]8;;${ESC}\\`;
|
|
24
|
+
}
|
|
25
|
+
// Markdown [metin](url) ve düz URL'leri tek geçişte tıklanabilir yapar.
|
|
26
|
+
// Alternation soldan eşleştiği için markdown linkin url'i ayrıca tekrar sarılmaz.
|
|
27
|
+
export function linkify(s) {
|
|
28
|
+
if (!s || !supportsHyperlinks())
|
|
29
|
+
return s;
|
|
30
|
+
return s.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)|(https?:\/\/[^\s<>"')\]]+)/g, (_m, mdText, mdUrl, bareUrl) => (mdUrl ? osc8(mdUrl, mdText) : osc8(bareUrl, bareUrl)));
|
|
31
|
+
}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// MCP (Model Context Protocol) istemcisi — açık protokol, bulut-bağımsız.
|
|
2
|
+
// .wormclaude/mcp.json'daki sunuculara bağlanır, araçlarını OpenAI function
|
|
3
|
+
// şemasına çevirip modele sunar, çağrıları ilgili MCP sunucusuna yönlendirir.
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
7
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
8
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
9
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
10
|
+
const servers = [];
|
|
11
|
+
const toolMap = new Map();
|
|
12
|
+
const CONFIG_PATH = path.join(process.cwd(), '.wormclaude', 'mcp.json');
|
|
13
|
+
function sanitize(s) {
|
|
14
|
+
return s.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
15
|
+
}
|
|
16
|
+
export function getMcpConfigPath() { return CONFIG_PATH; }
|
|
17
|
+
export function getMcpToolSchemas() {
|
|
18
|
+
return [...toolMap.values()].map((v) => v.schema);
|
|
19
|
+
}
|
|
20
|
+
export function getMcpServers() { return servers; }
|
|
21
|
+
// .wormclaude/mcp.json formatı (Claude Code uyumlu):
|
|
22
|
+
// { "mcpServers": {
|
|
23
|
+
// "filesystem": { "command": "npx", "args": ["-y","@modelcontextprotocol/server-filesystem","C:\\yol"] },
|
|
24
|
+
// "uzak": { "type": "http", "url": "https://..." }
|
|
25
|
+
// } }
|
|
26
|
+
export async function connectMcpServers(timeoutMs = 15000) {
|
|
27
|
+
servers.length = 0;
|
|
28
|
+
toolMap.clear();
|
|
29
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
30
|
+
return servers;
|
|
31
|
+
let cfg;
|
|
32
|
+
try {
|
|
33
|
+
cfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
servers.push({ name: '(mcp.json)', status: 'error', toolNames: [], error: `parse: ${e?.message || e}` });
|
|
37
|
+
return servers;
|
|
38
|
+
}
|
|
39
|
+
const defs = cfg?.mcpServers || cfg?.servers || {};
|
|
40
|
+
for (const [name, raw] of Object.entries(defs)) {
|
|
41
|
+
const state = { name, status: 'connected', toolNames: [] };
|
|
42
|
+
try {
|
|
43
|
+
let transport;
|
|
44
|
+
if (raw.command) {
|
|
45
|
+
transport = new StdioClientTransport({
|
|
46
|
+
command: raw.command,
|
|
47
|
+
args: raw.args || [],
|
|
48
|
+
env: { ...process.env, ...(raw.env || {}) },
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else if (raw.url) {
|
|
52
|
+
const url = new URL(raw.url);
|
|
53
|
+
transport = raw.type === 'sse' ? new SSEClientTransport(url) : new StreamableHTTPClientTransport(url);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
throw new Error('config: "command" veya "url" gerekli');
|
|
57
|
+
}
|
|
58
|
+
const client = new Client({ name: 'wormclaude', version: '1.0.0' }, { capabilities: {} });
|
|
59
|
+
await Promise.race([
|
|
60
|
+
client.connect(transport),
|
|
61
|
+
new Promise((_, rej) => setTimeout(() => rej(new Error('bağlantı zaman aşımı')), timeoutMs)),
|
|
62
|
+
]);
|
|
63
|
+
const { tools } = await client.listTools();
|
|
64
|
+
for (const t of tools) {
|
|
65
|
+
const prefixed = `mcp__${sanitize(name)}__${sanitize(t.name)}`;
|
|
66
|
+
toolMap.set(prefixed, {
|
|
67
|
+
client,
|
|
68
|
+
original: t.name,
|
|
69
|
+
schema: {
|
|
70
|
+
type: 'function',
|
|
71
|
+
function: {
|
|
72
|
+
name: prefixed,
|
|
73
|
+
description: t.description || `${name}: ${t.name}`,
|
|
74
|
+
parameters: t.inputSchema || { type: 'object', properties: {} },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
state.toolNames.push(prefixed);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
state.status = 'error';
|
|
83
|
+
state.error = e?.message || String(e);
|
|
84
|
+
}
|
|
85
|
+
servers.push(state);
|
|
86
|
+
}
|
|
87
|
+
return servers;
|
|
88
|
+
}
|
|
89
|
+
export async function callMcpTool(name, args) {
|
|
90
|
+
const entry = toolMap.get(name);
|
|
91
|
+
if (!entry)
|
|
92
|
+
return { ok: false, output: `Bilinmeyen MCP aracı: ${name}` };
|
|
93
|
+
try {
|
|
94
|
+
const res = await entry.client.callTool({ name: entry.original, arguments: args || {} });
|
|
95
|
+
const content = Array.isArray(res?.content) ? res.content : [];
|
|
96
|
+
const text = content
|
|
97
|
+
.map((c) => (c?.type === 'text' ? c.text : c?.type === 'image' ? '[image]' : JSON.stringify(c)))
|
|
98
|
+
.join('\n');
|
|
99
|
+
return { ok: !res?.isError, output: (text || '(içerik yok)').slice(0, 20000) };
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
return { ok: false, output: `MCP hata: ${e?.message || e}` };
|
|
103
|
+
}
|
|
104
|
+
}
|
package/dist/memory.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Otomatik hafıza — SessionMemory (sık, hafif) + autoDream (seyrek, derin) birleşik.
|
|
2
|
+
// Tetik eşikleri ve kilit mekanizması Claude Code'dan uyarlandı.
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { completeText } from './agent.js';
|
|
6
|
+
import { approxTokens } from './usage.js';
|
|
7
|
+
import { tasks } from './tasks.js';
|
|
8
|
+
// SessionMemory eşikleri (sessionMemoryUtils.ts'den)
|
|
9
|
+
const MIN_INIT_TOKENS = 10000; // ilk tetik için minimum bağlam
|
|
10
|
+
const MIN_TOKENS_BETWEEN = 5000; // güncellemeler arası bağlam büyümesi
|
|
11
|
+
const MIN_TOOL_CALLS = 3; // güncellemeler arası min araç çağrısı
|
|
12
|
+
// autoDream gate (config.ts'den)
|
|
13
|
+
const DREAM_MIN_HOURS = 24;
|
|
14
|
+
const SCAN_THROTTLE_MS = 10 * 60 * 1000;
|
|
15
|
+
const LOCK_STALE_MS = 60 * 60 * 1000;
|
|
16
|
+
const MEM_DIR = path.join(process.cwd(), '.wormclaude');
|
|
17
|
+
const MEM_FILE = path.join(MEM_DIR, 'memory.md');
|
|
18
|
+
const LOCK_FILE = path.join(MEM_DIR, '.consolidate-lock');
|
|
19
|
+
let initialized = false;
|
|
20
|
+
let inProgress = false;
|
|
21
|
+
let tokensAtLast = 0;
|
|
22
|
+
let toolCountAtLast = 0;
|
|
23
|
+
function countToolCalls(history) {
|
|
24
|
+
let n = 0;
|
|
25
|
+
for (const m of history)
|
|
26
|
+
if (m.role === 'assistant' && Array.isArray(m.tool_calls))
|
|
27
|
+
n += m.tool_calls.length;
|
|
28
|
+
return n;
|
|
29
|
+
}
|
|
30
|
+
function lastAssistantHadTools(history) {
|
|
31
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
32
|
+
if (history[i].role === 'assistant')
|
|
33
|
+
return Array.isArray(history[i].tool_calls) && history[i].tool_calls.length > 0;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
// SessionMemory tetik koşulu: bağlam yeterince büyüdü VE (yeterli araç çağrısı VEYA son tur araçsız)
|
|
38
|
+
export function shouldExtract(history) {
|
|
39
|
+
if (inProgress)
|
|
40
|
+
return false;
|
|
41
|
+
const t = approxTokens(history);
|
|
42
|
+
if (!initialized) {
|
|
43
|
+
if (t < MIN_INIT_TOKENS)
|
|
44
|
+
return false;
|
|
45
|
+
initialized = true;
|
|
46
|
+
}
|
|
47
|
+
const grew = t - tokensAtLast >= MIN_TOKENS_BETWEEN;
|
|
48
|
+
if (!grew)
|
|
49
|
+
return false; // token eşiği HER ZAMAN zorunlu (CC notu)
|
|
50
|
+
const enoughTools = countToolCalls(history) - toolCountAtLast >= MIN_TOOL_CALLS;
|
|
51
|
+
return enoughTools || !lastAssistantHadTools(history);
|
|
52
|
+
}
|
|
53
|
+
// Arka planda hafızayı günceller (mevcut not + yeni konuşma → merge).
|
|
54
|
+
export function triggerMemory(history, config) {
|
|
55
|
+
if (inProgress)
|
|
56
|
+
return;
|
|
57
|
+
inProgress = true;
|
|
58
|
+
tokensAtLast = approxTokens(history);
|
|
59
|
+
toolCountAtLast = countToolCalls(history);
|
|
60
|
+
const task = tasks.create('dream', 'oto-hafıza güncelleme');
|
|
61
|
+
const sys = history.find((m) => m.role === 'system');
|
|
62
|
+
const convo = history.filter((m) => m.role !== 'system');
|
|
63
|
+
(async () => {
|
|
64
|
+
try {
|
|
65
|
+
let prev = '';
|
|
66
|
+
try {
|
|
67
|
+
prev = fs.readFileSync(MEM_FILE, 'utf8');
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
tasks.append(task.id, 'konuşmadan kalıcı bilgiler çıkarılıyor…\n');
|
|
71
|
+
const out = await completeText([
|
|
72
|
+
sys ?? { role: 'system', content: 'You maintain long-term memory.' },
|
|
73
|
+
...convo,
|
|
74
|
+
{
|
|
75
|
+
role: 'user',
|
|
76
|
+
content: 'NOT: Bu mesaj konuşmanın parçası değil. Yukarıdaki konuşmadan kalıcı, faydalı bilgileri ' +
|
|
77
|
+
'(kullanıcı tercihleri, kararlar, proje durumu, önemli dosyalar) çıkar ve aşağıdaki mevcut ' +
|
|
78
|
+
'hafızaya MERGE et — kopya yazma, çelişeni güncelle. TÜM güncel hafızayı markdown madde imleriyle ' +
|
|
79
|
+
'döndür. Göreli tarihleri mutlak yap.\n\nMEVCUT HAFIZA:\n' + (prev || '(boş)'),
|
|
80
|
+
},
|
|
81
|
+
], config);
|
|
82
|
+
fs.mkdirSync(MEM_DIR, { recursive: true });
|
|
83
|
+
fs.writeFileSync(MEM_FILE, out);
|
|
84
|
+
tasks.append(task.id, `\nhafıza güncellendi: ${MEM_FILE} (${out.length} karakter)`);
|
|
85
|
+
tasks.finish(task.id, 'done');
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
tasks.append(task.id, `\n[hata: ${e?.message || e}]`);
|
|
89
|
+
tasks.finish(task.id, 'error');
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
inProgress = false;
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
}
|
|
96
|
+
// ── autoDream kilit (consolidationLock.ts'den) ────────────────────────────────
|
|
97
|
+
function isProcessRunning(pid) {
|
|
98
|
+
try {
|
|
99
|
+
process.kill(pid, 0);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Kilit alır; başka canlı process tutuyorsa null döner. Önceki mtime'ı döndürür (rollback için).
|
|
107
|
+
export function tryAcquireDreamLock() {
|
|
108
|
+
try {
|
|
109
|
+
const st = fs.statSync(LOCK_FILE);
|
|
110
|
+
if (Date.now() - st.mtimeMs < LOCK_STALE_MS) {
|
|
111
|
+
const pid = parseInt(fs.readFileSync(LOCK_FILE, 'utf8'), 10);
|
|
112
|
+
if (pid && isProcessRunning(pid))
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
fs.mkdirSync(MEM_DIR, { recursive: true });
|
|
116
|
+
fs.writeFileSync(LOCK_FILE, String(process.pid));
|
|
117
|
+
return st.mtimeMs;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
fs.mkdirSync(MEM_DIR, { recursive: true });
|
|
121
|
+
fs.writeFileSync(LOCK_FILE, String(process.pid));
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// autoDream zaman geçidi: son konsolidasyondan (lock mtime) bu yana ≥ DREAM_MIN_HOURS mı?
|
|
126
|
+
export function dreamTimeGatePassed() {
|
|
127
|
+
try {
|
|
128
|
+
const st = fs.statSync(LOCK_FILE);
|
|
129
|
+
return (Date.now() - st.mtimeMs) / 3_600_000 >= DREAM_MIN_HOURS;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return true; // hiç konsolide edilmemiş
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export function getMemoryPath() { return MEM_FILE; }
|