wormclaude 1.0.14 → 1.0.16
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/dist/agent.js +10 -1
- package/dist/api.js +12 -10
- package/dist/atmention.js +117 -0
- package/dist/auth.js +10 -0
- package/dist/cli.js +40 -6
- package/dist/commands.js +44 -8
- package/dist/errorsan.js +94 -0
- package/dist/markdown.js +221 -0
- package/dist/memory.js +41 -0
- package/dist/safejson.js +166 -0
- package/dist/streamparser.js +158 -0
- package/dist/subagents.js +119 -0
- package/dist/textclean.js +37 -0
- package/dist/theme.js +1 -1
- package/dist/tools.js +128 -14
- package/package.json +3 -2
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// İsimli/uzman alt-ajan tanımları + kullanıcı-seviyesi loader.
|
|
2
|
+
// Agent tool'u opsiyonel `subagent_type` ile çağrılınca buradan uzman prompt + tool-kısıtı çözülür.
|
|
3
|
+
// Gemini builtin-agents + subagent-manager'dan uyarlandı; WormClaude güvenlik kimliğine göre.
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
// ── Gömülü uzman ajanlar ──────────────────────────────────────────────────────
|
|
8
|
+
const BUILTINS = [
|
|
9
|
+
{
|
|
10
|
+
name: 'general-purpose',
|
|
11
|
+
description: 'Karmaşık soruları araştırma, kod arama ve çok adımlı görevler için genel amaçlı ajan.',
|
|
12
|
+
system: 'Sen bir WormClaude alt-ajanısın: tek bir görevi uçtan uca bitirmek için doğmuş otonom bir işçi. ' +
|
|
13
|
+
'İstenen neyse onu yap — fazlası değil, eksiği değil. Geniş arama için Grep/Glob, bilinen yol için Read kullan. ' +
|
|
14
|
+
'Gereksiz dosya YARATMA; *.md/README dosyalarını yalnız açıkça istenirse oluştur. ' +
|
|
15
|
+
'Bu görev dışında hafızan yok. Bitince yaptıklarının ve istenen sonuçların kısa, net bir raporunu döndür. ' +
|
|
16
|
+
'Yanıtta paylaştığın dosya yolları MUTLAK olmalı; göreli yol kullanma. Emoji kullanma.',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'security-recon',
|
|
20
|
+
description: 'YETKİLİ güvenlik testlerinde keşif: port/servis/teknoloji tespiti, yüzey analizi, raporlama.',
|
|
21
|
+
system: 'Sen bir WormClaude güvenlik-keşif alt-ajanısın. SADECE yetkili/izinli hedeflerde çalış. ' +
|
|
22
|
+
'Görevin: pasif ve aktif keşif (port/servis tespiti, teknoloji parmak izi, dizin/endpoint yüzeyi, ' +
|
|
23
|
+
'sürüm tespiti) yapıp bulgularını yapılandırılmış bir rapor olarak döndürmek. ' +
|
|
24
|
+
'Yıkıcı işlem, gerçek istismar veya hizmet-dışı bırakma YAPMA — yalnız keşif ve analiz. ' +
|
|
25
|
+
'Bulguları önem derecesiyle (bilgi/düşük/orta/yüksek) sınıflandır. Komut çıktısını ham bırakma, yorumla. Emoji kullanma.',
|
|
26
|
+
tools: ['Bash', 'Read', 'Grep', 'Glob', 'WebFetch', 'WebSearch', 'TaskOutput'],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'code-explorer',
|
|
30
|
+
description: 'Büyük kod tabanlarını haritalama: mimari, modül ilişkileri, giriş noktaları.',
|
|
31
|
+
system: 'Sen bir WormClaude kod-keşif alt-ajanısın. Görevin bir kod tabanını hızla haritalamak: ' +
|
|
32
|
+
'dizin yapısı, ana modüller, giriş noktaları, önemli soyutlamalar ve bağımlılıklar. ' +
|
|
33
|
+
'Grep/Glob ile geniş tara, Read ile kritik dosyaları incele. Dosya YARATMA/DEĞİŞTİRME. ' +
|
|
34
|
+
'Sonucu: kısa mimari özet + ilgili dosya yolları (MUTLAK) + kod parçaları olarak döndür. Emoji kullanma.',
|
|
35
|
+
tools: ['Grep', 'Glob', 'Read', 'TaskOutput'],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
// ── Kullanıcı-seviyesi ajanlar: ~/.wormclaude/agents/*.md ─────────────────────
|
|
39
|
+
function agentsDir() {
|
|
40
|
+
return path.join(os.homedir(), '.wormclaude', 'agents');
|
|
41
|
+
}
|
|
42
|
+
/** Minimal frontmatter ayrıştırma (yaml-parser gelene kadar; name/description/tools + body). */
|
|
43
|
+
function parseAgentFile(content, fallbackName) {
|
|
44
|
+
const m = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/.exec(content);
|
|
45
|
+
if (!m)
|
|
46
|
+
return null;
|
|
47
|
+
const [, fm, body] = m;
|
|
48
|
+
const get = (key) => {
|
|
49
|
+
const r = new RegExp(`^${key}\\s*:\\s*(.+)$`, 'mi').exec(fm);
|
|
50
|
+
return r ? r[1].trim().replace(/^["']|["']$/g, '') : undefined;
|
|
51
|
+
};
|
|
52
|
+
const name = (get('name') || fallbackName).trim();
|
|
53
|
+
const description = get('description') || '';
|
|
54
|
+
const system = body.trim();
|
|
55
|
+
if (!name || !system)
|
|
56
|
+
return null;
|
|
57
|
+
// tools: [A, B] veya A,B
|
|
58
|
+
let tools;
|
|
59
|
+
const rawTools = get('tools');
|
|
60
|
+
if (rawTools) {
|
|
61
|
+
tools = rawTools.replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
62
|
+
if (!tools.length)
|
|
63
|
+
tools = undefined;
|
|
64
|
+
}
|
|
65
|
+
// İsim guard: rezerve/biçim
|
|
66
|
+
const RESERVED = new Set(['self', 'system', 'user', 'model', 'tool', 'config', 'default']);
|
|
67
|
+
if (!/^[a-zA-Z0-9_-]{2,50}$/.test(name) || RESERVED.has(name.toLowerCase()))
|
|
68
|
+
return null;
|
|
69
|
+
return { name, description, system, tools };
|
|
70
|
+
}
|
|
71
|
+
function loadUserAgents() {
|
|
72
|
+
try {
|
|
73
|
+
const dir = agentsDir();
|
|
74
|
+
if (!fs.existsSync(dir))
|
|
75
|
+
return [];
|
|
76
|
+
return fs.readdirSync(dir)
|
|
77
|
+
.filter((f) => f.endsWith('.md'))
|
|
78
|
+
.map((f) => {
|
|
79
|
+
try {
|
|
80
|
+
const def = parseAgentFile(fs.readFileSync(path.join(dir, f), 'utf8'), f.replace(/\.md$/, ''));
|
|
81
|
+
return def;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.filter((d) => d !== null);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Ajanı isme göre çöz. Öncelik: kullanıcı dosyası > gömülü. Bulunamazsa null. */
|
|
94
|
+
export function resolveSubagent(type) {
|
|
95
|
+
if (!type)
|
|
96
|
+
return null;
|
|
97
|
+
const t = type.trim().toLowerCase();
|
|
98
|
+
const user = loadUserAgents().find((a) => a.name.toLowerCase() === t);
|
|
99
|
+
if (user)
|
|
100
|
+
return user;
|
|
101
|
+
return BUILTINS.find((a) => a.name.toLowerCase() === t) || null;
|
|
102
|
+
}
|
|
103
|
+
/** Tüm kullanılabilir ajanları listele (kullanıcı dosyaları gömülüleri gölgeler). */
|
|
104
|
+
export function listSubagents() {
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
const out = [];
|
|
107
|
+
for (const a of [...loadUserAgents(), ...BUILTINS]) {
|
|
108
|
+
const k = a.name.toLowerCase();
|
|
109
|
+
if (seen.has(k))
|
|
110
|
+
continue;
|
|
111
|
+
seen.add(k);
|
|
112
|
+
out.push(a);
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/** Agent tool açıklamasına eklenecek "kullanılabilir tipler" satırı. */
|
|
117
|
+
export function subagentTypesHint() {
|
|
118
|
+
return listSubagents().map((a) => `- ${a.name}: ${a.description}`).join('\n');
|
|
119
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Model metnindeki sızıntıları temizler: özel tokenlar + parçalı tool-call markup.
|
|
2
|
+
// Qwen-14B + vLLM(hermes) bazen ham çıktıya kontrol token'ları veya tool-call etiketleri sızdırır.
|
|
3
|
+
// ChatML / Qwen özel token'ları
|
|
4
|
+
const SPECIAL_TOKENS = [
|
|
5
|
+
'<|endoftext|>', '<|im_start|>', '<|im_end|>', '<|im_sep|>',
|
|
6
|
+
'<|endofturn|>', '<|fim_prefix|>', '<|fim_middle|>', '<|fim_suffix|>',
|
|
7
|
+
];
|
|
8
|
+
// Hermes tool-call markup (metne sızarsa) — bloğun TAMAMINI kaldır.
|
|
9
|
+
const TOOL_CALL_BLOCK = /<tool_call>[\s\S]*?<\/tool_call>/g;
|
|
10
|
+
const TOOL_RESPONSE_BLOCK = /<tool_response>[\s\S]*?<\/tool_response>/g;
|
|
11
|
+
// Açık kalan (kapanışı gelmemiş) tek etiketler
|
|
12
|
+
const STRAY_TOOL_TAGS = /<\/?(tool_call|tool_response)>/g;
|
|
13
|
+
/**
|
|
14
|
+
* Görüntülenecek/saklanacak model metnini temizler. İçeriği bozmadan yalnız
|
|
15
|
+
* sızan kontrol token'larını ve tool-call markup'ını çıkarır.
|
|
16
|
+
*/
|
|
17
|
+
export function cleanModelText(text) {
|
|
18
|
+
if (!text)
|
|
19
|
+
return text;
|
|
20
|
+
let out = text;
|
|
21
|
+
out = out.replace(TOOL_CALL_BLOCK, '');
|
|
22
|
+
out = out.replace(TOOL_RESPONSE_BLOCK, '');
|
|
23
|
+
out = out.replace(STRAY_TOOL_TAGS, '');
|
|
24
|
+
for (const tok of SPECIAL_TOKENS) {
|
|
25
|
+
if (out.includes(tok))
|
|
26
|
+
out = out.split(tok).join('');
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Streaming sırasında güvenli temizlik: yalnız TAM özel-token'ları ve kapanmış blokları
|
|
32
|
+
* temizler — yarım gelen bir etiketi (ör. "<tool_") sonraki chunk'ı bekleyeceği için bırakır.
|
|
33
|
+
* Akan tamponun tamamına uygulanır (parçalı token'ı bölmemek için).
|
|
34
|
+
*/
|
|
35
|
+
export function cleanStreamingBuffer(buffer) {
|
|
36
|
+
return cleanModelText(buffer);
|
|
37
|
+
}
|
package/dist/theme.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -8,6 +8,8 @@ import * as os from 'node:os';
|
|
|
8
8
|
import * as path from 'node:path';
|
|
9
9
|
import { loadConfig } from './api.js';
|
|
10
10
|
import { runAgentLoop } from './agent.js';
|
|
11
|
+
import { resolveSubagent, subagentTypesHint } from './subagents.js';
|
|
12
|
+
import { saveMemoryFact } from './memory.js';
|
|
11
13
|
import { tasks } from './tasks.js';
|
|
12
14
|
import { getMcpToolSchemas, callMcpTool } from './mcp.js';
|
|
13
15
|
import { getSkills, getSkill, buildSkillPrompt } from './skills.js';
|
|
@@ -25,6 +27,47 @@ export function getTodos() { return todosStore; }
|
|
|
25
27
|
const MAX_LINES_TO_READ = 2000;
|
|
26
28
|
const DEFAULT_BASH_TIMEOUT_MS = 120000;
|
|
27
29
|
const MAX_BASH_TIMEOUT_MS = 600000;
|
|
30
|
+
// Bash çalışma dizini — oturum boyunca kalıcı. execSync her çağrıda taze shell açtığı için
|
|
31
|
+
// `cd` normalde kaybolur; burada cwd'yi takip edip her komuta geçiriyoruz.
|
|
32
|
+
let bashCwd;
|
|
33
|
+
export function getBashCwd() { return bashCwd || process.cwd(); }
|
|
34
|
+
export function setBashCwd(dir) { bashCwd = dir; }
|
|
35
|
+
// Komutu çalıştırır ve sonrasında cwd değişikliğini (cd) yakalayıp bashCwd'yi günceller.
|
|
36
|
+
// POSIX: pwd'yi temp dosyaya yazan, çıkış kodunu KORUYAN sarmalayıcı (hata propagasyonu bozulmaz).
|
|
37
|
+
// Windows: komutu olduğu gibi çalıştır, sonra baştaki `cd <hedef>`'i regex ile yakala (best-effort).
|
|
38
|
+
function runBashCapturingCwd(command, timeout) {
|
|
39
|
+
const cwd = getBashCwd();
|
|
40
|
+
const opts = { encoding: 'utf8', timeout, maxBuffer: 10 * 1024 * 1024, windowsHide: true, cwd };
|
|
41
|
+
if (process.platform === 'win32') {
|
|
42
|
+
const out = execSync(command, opts);
|
|
43
|
+
// best-effort: "cd <hedef>" / "cd /d <hedef>" (zincirsiz tek komut)
|
|
44
|
+
const m = /^\s*cd\s+(?:\/d\s+)?"?([^"&|<>]+?)"?\s*$/i.exec(command);
|
|
45
|
+
if (m) {
|
|
46
|
+
const nd = path.resolve(cwd, m[1].trim());
|
|
47
|
+
try {
|
|
48
|
+
if (fs.statSync(nd).isDirectory())
|
|
49
|
+
bashCwd = nd;
|
|
50
|
+
}
|
|
51
|
+
catch { /* yok say */ }
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
// POSIX
|
|
56
|
+
const pwdFile = path.join(os.tmpdir(), `wc_pwd_${process.pid}_${Date.now()}.tmp`);
|
|
57
|
+
const wrapped = `{ ${command}\n}; __wc=$?; pwd > '${pwdFile}' 2>/dev/null; exit $__wc`;
|
|
58
|
+
try {
|
|
59
|
+
return execSync(wrapped, { ...opts, shell: '/bin/sh' });
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
try {
|
|
63
|
+
const p = fs.readFileSync(pwdFile, 'utf8').trim();
|
|
64
|
+
if (p)
|
|
65
|
+
bashCwd = p;
|
|
66
|
+
fs.unlinkSync(pwdFile);
|
|
67
|
+
}
|
|
68
|
+
catch { /* dosya yoksa yok say */ }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
28
71
|
// WormClaude davranışı: bir dosyayı düzenlemeden/üzerine yazmadan önce
|
|
29
72
|
// en az bir kez okumuş olman gerekir. Okunan dosyaları burada izliyoruz.
|
|
30
73
|
const readFiles = new Set();
|
|
@@ -301,12 +344,13 @@ export const toolSchemas = [
|
|
|
301
344
|
type: 'function',
|
|
302
345
|
function: {
|
|
303
346
|
name: 'Agent',
|
|
304
|
-
description: AGENT_DESCRIPTION,
|
|
347
|
+
description: AGENT_DESCRIPTION + '\n\nAvailable subagent_type values (specialized prompt + restricted tools):\n' + subagentTypesHint(),
|
|
305
348
|
parameters: {
|
|
306
349
|
type: 'object',
|
|
307
350
|
properties: {
|
|
308
351
|
description: { type: 'string', description: 'A short (3-5 word) description of the task' },
|
|
309
352
|
prompt: { type: 'string', description: 'The detailed, self-contained task for the sub-agent to perform' },
|
|
353
|
+
subagent_type: { type: 'string', description: 'Optional: specialized agent to use (e.g. general-purpose, security-recon, code-explorer). Selects a tailored system prompt and a restricted tool set. Omit for a general sub-agent.' },
|
|
310
354
|
run_in_background: { type: 'boolean', description: 'Run the sub-agent in the background and return a task id (default false)' },
|
|
311
355
|
},
|
|
312
356
|
required: ['description', 'prompt'],
|
|
@@ -327,6 +371,21 @@ export const toolSchemas = [
|
|
|
327
371
|
},
|
|
328
372
|
},
|
|
329
373
|
},
|
|
374
|
+
{
|
|
375
|
+
type: 'function',
|
|
376
|
+
function: {
|
|
377
|
+
name: 'SaveMemory',
|
|
378
|
+
description: 'Save a single concise fact to long-term memory so it persists across sessions. Use when the user explicitly asks you to remember something, or when you detect a durable preference, decision, project convention, or constraint worth keeping. Keep the fact short and self-contained. Do NOT use for transient or session-only details.',
|
|
379
|
+
parameters: {
|
|
380
|
+
type: 'object',
|
|
381
|
+
properties: {
|
|
382
|
+
fact: { type: 'string', description: 'The specific, self-contained fact to remember (e.g. "User prefers Turkish responses", "Project uses pnpm not npm").' },
|
|
383
|
+
scope: { type: 'string', enum: ['project', 'global'], description: 'project = this project only (default), global = all projects.' },
|
|
384
|
+
},
|
|
385
|
+
required: ['fact'],
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
},
|
|
330
389
|
{
|
|
331
390
|
type: 'function',
|
|
332
391
|
function: {
|
|
@@ -540,6 +599,7 @@ const TOOL_META = {
|
|
|
540
599
|
Scroll: { needsPermission: true, validate: (a) => (a && a.direction ? null : 'direction gerekli') },
|
|
541
600
|
WebSearch: { readOnly: true, needsPermission: true, validate: (a) => (a && a.query ? null : 'query gerekli') },
|
|
542
601
|
TodoWrite: { readOnly: true, validate: (a) => (a && Array.isArray(a.todos) ? null : 'todos dizisi gerekli') },
|
|
602
|
+
SaveMemory: { validate: (a) => (a && a.fact && String(a.fact).trim() ? null : 'fact gerekli') },
|
|
543
603
|
PowerShell: { needsPermission: true, validate: (a) => (a && a.command ? null : 'command gerekli') },
|
|
544
604
|
NotebookEdit: { needsPermission: true, validate: (a) => (a && a.notebook_path ? null : 'notebook_path gerekli') },
|
|
545
605
|
REPL: { needsPermission: true, validate: (a) => (a && a.language && a.code ? null : 'language ve code gerekli') },
|
|
@@ -664,13 +724,53 @@ export async function executeToolCalls(calls, hooks) {
|
|
|
664
724
|
return results;
|
|
665
725
|
}
|
|
666
726
|
// Alt-agent'a verilecek araç seti (özyineleme/iç içe agent engellenir).
|
|
667
|
-
|
|
668
|
-
|
|
727
|
+
// allow verilirse yalnız o adlardaki araçlar bırakılır (uzman ajan tool-kısıtı).
|
|
728
|
+
function subAgentTools(allow) {
|
|
729
|
+
let list = allToolSchemas().filter((t) => t.function.name !== 'Agent' && t.function.name !== 'Skill');
|
|
730
|
+
if (allow && allow.length) {
|
|
731
|
+
const set = new Set(allow);
|
|
732
|
+
const filtered = list.filter((t) => set.has(t.function.name));
|
|
733
|
+
if (filtered.length)
|
|
734
|
+
list = filtered; // boş kalırsa kısıtı yok say (güvenli geri dönüş)
|
|
735
|
+
}
|
|
736
|
+
return list;
|
|
669
737
|
}
|
|
670
738
|
const SUBAGENT_SYSTEM = 'You are a WormClaude sub-agent: an autonomous worker spawned to complete one specific task. ' +
|
|
671
739
|
'Use your tools (Bash, Read, Write, Edit, Glob, Grep, WebFetch, TaskOutput) to do the work, ' +
|
|
672
740
|
'then return a concise final report of what you did and any results requested. ' +
|
|
673
741
|
'You have no memory beyond this task. Be thorough and finish the task end-to-end.';
|
|
742
|
+
// Levenshtein mesafesi (küçük, dep'siz) — bilinmeyen tool adına en yakın öneriler.
|
|
743
|
+
function levenshtein(a, b) {
|
|
744
|
+
const m = a.length, n = b.length;
|
|
745
|
+
if (!m)
|
|
746
|
+
return n;
|
|
747
|
+
if (!n)
|
|
748
|
+
return m;
|
|
749
|
+
let prev = Array.from({ length: n + 1 }, (_, i) => i);
|
|
750
|
+
let cur = new Array(n + 1);
|
|
751
|
+
for (let i = 1; i <= m; i++) {
|
|
752
|
+
cur[0] = i;
|
|
753
|
+
for (let j = 1; j <= n; j++) {
|
|
754
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
755
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + cost);
|
|
756
|
+
}
|
|
757
|
+
[prev, cur] = [cur, prev];
|
|
758
|
+
}
|
|
759
|
+
return prev[n];
|
|
760
|
+
}
|
|
761
|
+
// Bilinmeyen tool adı için " Did you mean 'X'?" önerisi (en yakın ≤3, makul mesafe).
|
|
762
|
+
function suggestTools(unknown) {
|
|
763
|
+
const names = allToolSchemas().map((t) => t.function.name);
|
|
764
|
+
const ranked = names
|
|
765
|
+
.map((n) => ({ n, d: levenshtein(unknown.toLowerCase(), n.toLowerCase()) }))
|
|
766
|
+
.filter((x) => x.d <= Math.max(3, Math.floor(unknown.length / 2)))
|
|
767
|
+
.sort((a, b) => a.d - b.d)
|
|
768
|
+
.slice(0, 3)
|
|
769
|
+
.map((x) => `"${x.n}"`);
|
|
770
|
+
if (!ranked.length)
|
|
771
|
+
return '';
|
|
772
|
+
return ranked.length > 1 ? ` Did you mean one of: ${ranked.join(', ')}?` : ` Did you mean ${ranked[0]}?`;
|
|
773
|
+
}
|
|
674
774
|
export function toolLabel(name, args) {
|
|
675
775
|
try {
|
|
676
776
|
if (name === 'Bash')
|
|
@@ -688,9 +788,11 @@ export function toolLabel(name, args) {
|
|
|
688
788
|
if (name === 'WebFetch')
|
|
689
789
|
return `WebFetch(${args.url})`;
|
|
690
790
|
if (name === 'Agent')
|
|
691
|
-
return `Agent(${args.description || ''}${args.run_in_background ? ', bg' : ''})`;
|
|
791
|
+
return `Agent(${args.description || ''}${args.subagent_type ? ':' + args.subagent_type : ''}${args.run_in_background ? ', bg' : ''})`;
|
|
692
792
|
if (name === 'TaskOutput')
|
|
693
793
|
return `TaskOutput(${args.task_id})`;
|
|
794
|
+
if (name === 'SaveMemory')
|
|
795
|
+
return `SaveMemory(${String(args.fact || '').slice(0, 50)})`;
|
|
694
796
|
if (name === 'Skill')
|
|
695
797
|
return `Skill(${args.name})`;
|
|
696
798
|
if (name === 'WebSearch')
|
|
@@ -755,9 +857,9 @@ function walk(dir, out, depth = 0) {
|
|
|
755
857
|
function globToRegex(pattern) {
|
|
756
858
|
let re = pattern
|
|
757
859
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
758
|
-
.replace(/\*\*/g, '
|
|
860
|
+
.replace(/\*\*/g, '\u0000')
|
|
759
861
|
.replace(/\*/g, '[^/\\\\]*')
|
|
760
|
-
.replace(
|
|
862
|
+
.replace(/\u0000/g, '.*')
|
|
761
863
|
.replace(/\?/g, '.');
|
|
762
864
|
return new RegExp(re + '$', 'i');
|
|
763
865
|
}
|
|
@@ -819,7 +921,7 @@ export async function executeTool(name, args) {
|
|
|
819
921
|
if (name === 'Bash') {
|
|
820
922
|
if (args.run_in_background) {
|
|
821
923
|
const task = tasks.create('shell', String(args.command).slice(0, 60));
|
|
822
|
-
const child = spawn(String(args.command), { shell: true, windowsHide: true });
|
|
924
|
+
const child = spawn(String(args.command), { shell: true, windowsHide: true, cwd: getBashCwd() });
|
|
823
925
|
task.child = child;
|
|
824
926
|
child.stdout?.on('data', (d) => tasks.append(task.id, d.toString()));
|
|
825
927
|
child.stderr?.on('data', (d) => tasks.append(task.id, d.toString()));
|
|
@@ -833,14 +935,16 @@ export async function executeTool(name, args) {
|
|
|
833
935
|
let timeout = Number(args.timeout) || DEFAULT_BASH_TIMEOUT_MS;
|
|
834
936
|
if (timeout > MAX_BASH_TIMEOUT_MS)
|
|
835
937
|
timeout = MAX_BASH_TIMEOUT_MS;
|
|
836
|
-
const out =
|
|
837
|
-
encoding: 'utf8', timeout, maxBuffer: 10 * 1024 * 1024, windowsHide: true,
|
|
838
|
-
});
|
|
938
|
+
const out = runBashCapturingCwd(String(args.command), timeout);
|
|
839
939
|
return { ok: true, output: (out || '(no output)').slice(0, 20000) };
|
|
840
940
|
}
|
|
841
941
|
if (name === 'Agent') {
|
|
942
|
+
// Uzman ajan seçimi: subagent_type verilirse özel prompt + tool-kısıtı uygulanır.
|
|
943
|
+
const def = resolveSubagent(args.subagent_type);
|
|
944
|
+
const sysPrompt = def ? def.system : SUBAGENT_SYSTEM;
|
|
945
|
+
const subTools = subAgentTools(def?.tools);
|
|
842
946
|
const subMessages = [
|
|
843
|
-
{ role: 'system', content:
|
|
947
|
+
{ role: 'system', content: sysPrompt },
|
|
844
948
|
{ role: 'user', content: String(args.prompt || '') },
|
|
845
949
|
];
|
|
846
950
|
if (args.run_in_background) {
|
|
@@ -850,7 +954,7 @@ export async function executeTool(name, args) {
|
|
|
850
954
|
const { finalText } = await runAgentLoop({
|
|
851
955
|
config: cfg(),
|
|
852
956
|
messages: subMessages,
|
|
853
|
-
tools:
|
|
957
|
+
tools: subTools,
|
|
854
958
|
executeTool,
|
|
855
959
|
hooks: {
|
|
856
960
|
onText: (t) => tasks.append(task.id, t),
|
|
@@ -870,7 +974,7 @@ export async function executeTool(name, args) {
|
|
|
870
974
|
const { finalText } = await runAgentLoop({
|
|
871
975
|
config: cfg(),
|
|
872
976
|
messages: subMessages,
|
|
873
|
-
tools:
|
|
977
|
+
tools: subTools,
|
|
874
978
|
executeTool,
|
|
875
979
|
});
|
|
876
980
|
return { ok: true, output: finalText || '(sub-agent returned no text)' };
|
|
@@ -882,6 +986,16 @@ export async function executeTool(name, args) {
|
|
|
882
986
|
const body = t.output.slice(-9000) || '(no output yet)';
|
|
883
987
|
return { ok: true, output: `[${t.id}] ${t.kind} · ${t.status} · ${t.label}\n\n${body}` };
|
|
884
988
|
}
|
|
989
|
+
if (name === 'SaveMemory') {
|
|
990
|
+
try {
|
|
991
|
+
const scope = args.scope === 'global' ? 'global' : 'project';
|
|
992
|
+
const file = saveMemoryFact(String(args.fact || ''), scope);
|
|
993
|
+
return { ok: true, output: `Hatıra kaydedildi (${scope}): "${String(args.fact).trim()}" → ${file}` };
|
|
994
|
+
}
|
|
995
|
+
catch (e) {
|
|
996
|
+
return { ok: false, output: `Hatıra kaydedilemedi: ${e?.message || e}` };
|
|
997
|
+
}
|
|
998
|
+
}
|
|
885
999
|
if (name === 'Read') {
|
|
886
1000
|
const fp = args.file_path;
|
|
887
1001
|
if (!fs.existsSync(fp))
|
|
@@ -1220,7 +1334,7 @@ export async function executeTool(name, args) {
|
|
|
1220
1334
|
}
|
|
1221
1335
|
if (name.startsWith('mcp__'))
|
|
1222
1336
|
return await callMcpTool(name, args);
|
|
1223
|
-
return { ok: false, output: `Unknown tool: ${name}
|
|
1337
|
+
return { ok: false, output: `Unknown tool: ${name}.${suggestTools(name)} Use exactly the registered tool names.` };
|
|
1224
1338
|
}
|
|
1225
1339
|
catch (e) {
|
|
1226
1340
|
return { ok: false, output: `Error: ${e?.message || String(e)}` };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wormclaude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"ink": "^5.0.1",
|
|
20
20
|
"ink-spinner": "^5.0.0",
|
|
21
21
|
"ink-text-input": "^6.0.0",
|
|
22
|
-
"react": "^18.3.1"
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"string-width": "^7.2.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"@types/node": "^22.10.2",
|