wormclaude 1.0.220 → 1.0.222
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/i18n.js +2 -0
- package/dist/theme.js +1 -1
- package/dist/tools.js +129 -3
- package/dist/tui.js +14 -0
- package/package.json +1 -1
package/dist/i18n.js
CHANGED
|
@@ -95,6 +95,7 @@ const STR = {
|
|
|
95
95
|
'tui.mcpConnected': '🔌 {0} MCP sunucusu bağlandı',
|
|
96
96
|
'tui.autoCompacted': '✎ bağlam otomatik özetlendi',
|
|
97
97
|
'tui.loopStop': 'Aynı adım tekrarlandı, döngü önlemek için durduruldu.',
|
|
98
|
+
'tui.userStopped': '■ durduruldu (Esc).',
|
|
98
99
|
'tui.connErr': '[bağlantı hatası: {0}]',
|
|
99
100
|
'tui.learned': '✎ eğitim datasına eklendi',
|
|
100
101
|
'tui.userCancel': 'kullanıcı iptal etti',
|
|
@@ -162,6 +163,7 @@ const STR = {
|
|
|
162
163
|
'tui.mcpConnected': '🔌 {0} MCP server(s) connected',
|
|
163
164
|
'tui.autoCompacted': '✎ context auto-summarized',
|
|
164
165
|
'tui.loopStop': 'Same step repeated, stopped to avoid a loop.',
|
|
166
|
+
'tui.userStopped': '■ stopped (Esc).',
|
|
165
167
|
'tui.connErr': '[connection error: {0}]',
|
|
166
168
|
'tui.learned': '',
|
|
167
169
|
'tui.userCancel': 'user cancelled',
|
package/dist/theme.js
CHANGED
package/dist/tools.js
CHANGED
|
@@ -183,7 +183,8 @@ const BASH_DESCRIPTION = `Run a shell command and return its output. Working dir
|
|
|
183
183
|
- Quote paths with spaces. Chain dependent commands with && in ONE call; use ; if you do not care about failures; no newlines between commands.
|
|
184
184
|
- timeout: optional ms (max ${MAX_BASH_TIMEOUT_MS}, default ${DEFAULT_BASH_TIMEOUT_MS}). run_in_background: true for long or never-exiting commands (returns a task id; read later with TaskOutput).
|
|
185
185
|
- git: commit only when asked; avoid destructive ops (reset --hard, push --force) unless clearly best.`;
|
|
186
|
-
const READ_DESCRIPTION = `Read a file from the local filesystem. file_path must be ABSOLUTE. Returns content with line numbers (cat -n format), up to ${MAX_LINES_TO_READ} lines; optional offset/limit for long files. Cannot read directories (use ls via Bash). A missing file returns an error.`;
|
|
186
|
+
const READ_DESCRIPTION = `Read a file from the local filesystem. file_path must be ABSOLUTE. Returns content with line numbers (cat -n format), up to ${MAX_LINES_TO_READ} lines; optional offset/limit for long files. Cannot read directories (use ls via Bash). A missing file returns an error. For STRUCTURED DATA (.csv, .xlsx, .json, .pdf, ...) use ReadDataFile instead — Read garbles binary spreadsheets.`;
|
|
187
|
+
const READ_DATA_DESCRIPTION = `Read and PARSE a structured-data file (.csv, .tsv, .json, .jsonl, .txt, .xlsx, .xls, .pdf) into clean text/rows. You MUST use THIS (not Read) for spreadsheets and data files — Read mangles binary .xlsx/.pdf. CSV/TSV/XLSX → table rows (first row = headers); JSON/JSONL → pretty JSON; PDF → text extracted page by page; TXT → raw text. Use offset/max_rows to paginate large files. For real ANALYSIS, read the structure here then write a Python/JS script that processes the WHOLE file (never just the sample shown).`;
|
|
187
188
|
const WRITE_DESCRIPTION = `Writes a file to the local filesystem.
|
|
188
189
|
|
|
189
190
|
Usage:
|
|
@@ -192,7 +193,11 @@ Usage:
|
|
|
192
193
|
- Prefer the Edit tool for modifying existing files — it only sends the diff. Only use this tool to create new files or for complete rewrites.
|
|
193
194
|
- NEVER create documentation files (*.md) or README files unless explicitly requested by the User.
|
|
194
195
|
- Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`;
|
|
195
|
-
const EDIT_DESCRIPTION = `Exact string replacement in a file. You MUST Read the file at least once first (errors otherwise).
|
|
196
|
+
const EDIT_DESCRIPTION = `Exact string replacement in a file. You MUST Read the file at least once first (errors otherwise).
|
|
197
|
+
- old_string MUST be the EXACT literal text from the file — including all whitespace, indentation and newlines. Do NOT include the line-number prefix from Read output, and NEVER escape characters (escaping breaks the literal match and the edit fails).
|
|
198
|
+
- old_string MUST uniquely identify the SINGLE place to change: include at least 3 lines of context BEFORE and AFTER the target, matching indentation precisely. If it matches multiple places or does not match exactly, the edit fails.
|
|
199
|
+
- To change EVERY occurrence (e.g. renaming a variable), set replace_all: true.
|
|
200
|
+
- new_string is the exact replacement text (also exact whitespace/indentation); ensure the result is correct and idiomatic.`;
|
|
196
201
|
const GLOB_DESCRIPTION = `- Fast file pattern matching tool that works with any codebase size
|
|
197
202
|
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
|
|
198
203
|
- Returns matching file paths sorted by modification time
|
|
@@ -325,6 +330,22 @@ export const toolSchemas = [
|
|
|
325
330
|
},
|
|
326
331
|
},
|
|
327
332
|
},
|
|
333
|
+
{
|
|
334
|
+
type: 'function',
|
|
335
|
+
function: {
|
|
336
|
+
name: 'ReadDataFile',
|
|
337
|
+
description: READ_DATA_DESCRIPTION,
|
|
338
|
+
parameters: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
file_path: { type: 'string', description: 'Absolute path to the structured-data file (.csv, .tsv, .json, .jsonl, .txt, .xlsx, .xls, .pdf).' },
|
|
342
|
+
max_rows: { type: 'number', description: 'Optional: max rows/pages to DISPLAY (default 100 for tables, 10 for PDF). Only limits the sample shown.' },
|
|
343
|
+
offset: { type: 'number', description: 'Optional: 0-based row/page to start from (pagination through large files).' },
|
|
344
|
+
},
|
|
345
|
+
required: ['file_path'],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
328
349
|
{
|
|
329
350
|
type: 'function',
|
|
330
351
|
function: {
|
|
@@ -708,7 +729,7 @@ const computerToolSchemas = [
|
|
|
708
729
|
// Bu yüzden modele YALNIZ sade, build+güvenlik çekirdeğini veriyoruz → araçları gerçekten
|
|
709
730
|
// çağırıp işi yapar, planı ekrana basmaz. (Gerekirse buraya yeni araç eklenir.)
|
|
710
731
|
const CORE_TOOLS = new Set([
|
|
711
|
-
'Bash', 'PowerShell', 'Read', 'Write', 'Edit', 'Glob', 'Grep',
|
|
732
|
+
'Bash', 'PowerShell', 'Read', 'ReadDataFile', 'Write', 'Edit', 'Glob', 'Grep',
|
|
712
733
|
'WebFetch', 'WebSearch', 'CloneSite', 'Sleep', 'SecurityScan', 'CodeAudit', 'JwtAudit', 'CreateDocument',
|
|
713
734
|
'TaskOutput', // uzun komutları arka planda çalıştırıp bitişini yoklamak için (run_in_background)
|
|
714
735
|
'AskUserQuestion', // GERİ EKLENDİ: FLAT şema (options: string[]) ile 32B temiz çağrı üretiyor
|
|
@@ -738,6 +759,7 @@ export function allToolSchemas() {
|
|
|
738
759
|
}
|
|
739
760
|
const TOOL_META = {
|
|
740
761
|
Read: { readOnly: true, concurrencySafe: true, validate: (a) => (a && a.file_path && String(a.file_path) !== 'undefined' ? null : 'file_path gerekli (geçerli bir dosya yolu ver)') },
|
|
762
|
+
ReadDataFile: { readOnly: true, concurrencySafe: true, validate: (a) => (a && a.file_path ? null : 'file_path gerekli') },
|
|
741
763
|
Glob: { readOnly: true, concurrencySafe: true },
|
|
742
764
|
Grep: { readOnly: true, concurrencySafe: true },
|
|
743
765
|
WebFetch: { readOnly: true, validate: (a) => (a && a.url ? null : 'url gerekli') },
|
|
@@ -1241,6 +1263,8 @@ export function toolLabel(name, args) {
|
|
|
1241
1263
|
return `Bash(${String(args.command).slice(0, 80)})`;
|
|
1242
1264
|
if (name === 'Read')
|
|
1243
1265
|
return `Read(${args.file_path})`;
|
|
1266
|
+
if (name === 'ReadDataFile')
|
|
1267
|
+
return `📊 ReadDataFile(${args.file_path})`;
|
|
1244
1268
|
if (name === 'Write')
|
|
1245
1269
|
return `Write(${args.file_path})`;
|
|
1246
1270
|
if (name === 'Edit')
|
|
@@ -1457,6 +1481,47 @@ function syntaxWarn(fp, content) {
|
|
|
1457
1481
|
const err = syntaxCheck(fp, content);
|
|
1458
1482
|
return err ? `\n⚠️ SYNTAX HATASI bu düzenlemeden sonra: ${err}\nDosya yazıldı ama BOZUK — bu hatayı şimdi düzelt (doğru sözdizimiyle tekrar Edit et).` : '';
|
|
1459
1483
|
}
|
|
1484
|
+
// CSV/TSV ayrıştırıcı — tırnak içi ayraç/satırsonu ve "" kaçışını doğru işler.
|
|
1485
|
+
function parseCSV(text, sep) {
|
|
1486
|
+
const rows = [];
|
|
1487
|
+
let row = [];
|
|
1488
|
+
let field = '';
|
|
1489
|
+
let inQ = false;
|
|
1490
|
+
for (let i = 0; i < text.length; i++) {
|
|
1491
|
+
const c = text[i];
|
|
1492
|
+
if (inQ) {
|
|
1493
|
+
if (c === '"') {
|
|
1494
|
+
if (text[i + 1] === '"') {
|
|
1495
|
+
field += '"';
|
|
1496
|
+
i++;
|
|
1497
|
+
}
|
|
1498
|
+
else
|
|
1499
|
+
inQ = false;
|
|
1500
|
+
}
|
|
1501
|
+
else
|
|
1502
|
+
field += c;
|
|
1503
|
+
}
|
|
1504
|
+
else if (c === '"')
|
|
1505
|
+
inQ = true;
|
|
1506
|
+
else if (c === sep) {
|
|
1507
|
+
row.push(field);
|
|
1508
|
+
field = '';
|
|
1509
|
+
}
|
|
1510
|
+
else if (c === '\n') {
|
|
1511
|
+
row.push(field);
|
|
1512
|
+
rows.push(row);
|
|
1513
|
+
row = [];
|
|
1514
|
+
field = '';
|
|
1515
|
+
}
|
|
1516
|
+
else if (c !== '\r')
|
|
1517
|
+
field += c;
|
|
1518
|
+
}
|
|
1519
|
+
if (field.length || row.length) {
|
|
1520
|
+
row.push(field);
|
|
1521
|
+
rows.push(row);
|
|
1522
|
+
}
|
|
1523
|
+
return rows.filter((r) => r.length > 1 || (r.length === 1 && r[0] !== ''));
|
|
1524
|
+
}
|
|
1460
1525
|
export async function executeTool(name, args) {
|
|
1461
1526
|
try {
|
|
1462
1527
|
if (name === 'See') {
|
|
@@ -1594,6 +1659,67 @@ export async function executeTool(name, args) {
|
|
|
1594
1659
|
.join('\n');
|
|
1595
1660
|
return { ok: true, output: numbered.slice(0, 40000) };
|
|
1596
1661
|
}
|
|
1662
|
+
if (name === 'ReadDataFile') {
|
|
1663
|
+
const fp = resolveWs(args.file_path);
|
|
1664
|
+
if (!fs.existsSync(fp))
|
|
1665
|
+
return { ok: false, output: `Error: file does not exist: ${fp}` };
|
|
1666
|
+
if (fs.statSync(fp).isDirectory())
|
|
1667
|
+
return { ok: false, output: 'Error: path is a directory.' };
|
|
1668
|
+
const ext = path.extname(fp).toLowerCase();
|
|
1669
|
+
const maxRows = Number(args.max_rows) || (ext === '.pdf' ? 10 : 100);
|
|
1670
|
+
const off = Math.max(0, Number(args.offset) || 0);
|
|
1671
|
+
readFiles.add(norm(fp));
|
|
1672
|
+
const tbl = (head, body) => [head.join(' | '), head.map(() => '---').join(' | '), ...body.map((r) => r.map((c) => (c == null ? '' : String(c))).join(' | '))].join('\n');
|
|
1673
|
+
try {
|
|
1674
|
+
if (ext === '.csv' || ext === '.tsv') {
|
|
1675
|
+
const rows = parseCSV(fs.readFileSync(fp, 'utf8'), ext === '.tsv' ? '\t' : ',');
|
|
1676
|
+
const head = rows[0] || [];
|
|
1677
|
+
const body = rows.slice(1 + off, 1 + off + maxRows);
|
|
1678
|
+
return { ok: true, output: `[${ext.slice(1).toUpperCase()} — ${fp}] ${Math.max(0, rows.length - 1)} satır × ${head.length} sütun (${off + 1}–${off + body.length} gösteriliyor)\n\n${tbl(head, body).slice(0, 30000)}` };
|
|
1679
|
+
}
|
|
1680
|
+
if (ext === '.json') {
|
|
1681
|
+
const data = JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
1682
|
+
return { ok: true, output: `[JSON — ${fp}]\n${JSON.stringify(data, null, 2).slice(0, 30000)}` };
|
|
1683
|
+
}
|
|
1684
|
+
if (ext === '.jsonl' || ext === '.ndjson') {
|
|
1685
|
+
const items = fs.readFileSync(fp, 'utf8').split(/\r?\n/).filter(Boolean).map((l) => { try {
|
|
1686
|
+
return JSON.parse(l);
|
|
1687
|
+
}
|
|
1688
|
+
catch {
|
|
1689
|
+
return l;
|
|
1690
|
+
} });
|
|
1691
|
+
const slice = items.slice(off, off + maxRows);
|
|
1692
|
+
return { ok: true, output: `[JSONL — ${fp}] ${items.length} kayıt (${off + 1}–${off + slice.length})\n${JSON.stringify(slice, null, 2).slice(0, 30000)}` };
|
|
1693
|
+
}
|
|
1694
|
+
if (ext === '.txt' || ext === '.log' || ext === '.md') {
|
|
1695
|
+
return { ok: true, output: `[TXT — ${fp}]\n${fs.readFileSync(fp, 'utf8').slice(0, 40000)}` };
|
|
1696
|
+
}
|
|
1697
|
+
if (ext === '.xlsx' || ext === '.xls') {
|
|
1698
|
+
const ExcelJS = await import('exceljs');
|
|
1699
|
+
const wb = new (ExcelJS.default || ExcelJS).Workbook();
|
|
1700
|
+
await wb.xlsx.readFile(fp);
|
|
1701
|
+
const parts = [];
|
|
1702
|
+
wb.eachSheet((ws) => {
|
|
1703
|
+
const rows = [];
|
|
1704
|
+
ws.eachRow((row) => rows.push((row.values || []).slice(1).map((v) => (v && (v.text ?? v.result)) ?? v)));
|
|
1705
|
+
const head = rows[0] || [];
|
|
1706
|
+
const body = rows.slice(1 + off, 1 + off + maxRows);
|
|
1707
|
+
parts.push(`### Sayfa: ${ws.name} (${Math.max(0, rows.length - 1)} satır × ${head.length} sütun)\n${tbl(head, body)}`);
|
|
1708
|
+
});
|
|
1709
|
+
return { ok: true, output: `[XLSX — ${fp}]\n${(parts.join('\n\n') || '(boş)').slice(0, 30000)}` };
|
|
1710
|
+
}
|
|
1711
|
+
if (ext === '.pdf') {
|
|
1712
|
+
const { PDFParse } = await import('pdf-parse');
|
|
1713
|
+
const r = await new PDFParse({ data: fs.readFileSync(fp) }).getText();
|
|
1714
|
+
const txt = String(r?.text || '').trim();
|
|
1715
|
+
return { ok: true, output: txt ? `[PDF — ${fp}]\n${txt.slice(0, 40000)}` : 'PDF metin çıkmadı (taranmış/görsel olabilir).' };
|
|
1716
|
+
}
|
|
1717
|
+
return { ok: false, output: `Desteklenmeyen veri tipi: ${ext}. Düz metin/kod için Read kullan.` };
|
|
1718
|
+
}
|
|
1719
|
+
catch (e) {
|
|
1720
|
+
return { ok: false, output: 'Veri dosyası okuma hatası: ' + (e?.message || e) };
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1597
1723
|
if (name === 'Write') {
|
|
1598
1724
|
const fp = resolveWs(args.file_path);
|
|
1599
1725
|
const _existed = fs.existsSync(fp);
|
package/dist/tui.js
CHANGED
|
@@ -795,6 +795,14 @@ export async function runTui() {
|
|
|
795
795
|
} // Hayır / No
|
|
796
796
|
return;
|
|
797
797
|
}
|
|
798
|
+
// ÇALIŞAN TURU DURDUR: perm/ask dialogu YOKKEN Esc → uçuştaki isteği kes (app'i kapatma).
|
|
799
|
+
// Altyapı (turnAbort→signal→api.ts reader.cancel + loop break) hazırdı ama bu dal eksikti:
|
|
800
|
+
// model çok dosya okuyup takıldığında Esc'in hiçbir etkisi olmuyordu.
|
|
801
|
+
if (busy && turnAbort && key && key.name === 'escape') {
|
|
802
|
+
turnAbort.abort();
|
|
803
|
+
printItem({ kind: 'note', text: t('tui.userStopped') });
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
798
806
|
if (key && key.ctrl && key.name === 'c') {
|
|
799
807
|
if (inputBuf) {
|
|
800
808
|
inputBuf = '';
|
|
@@ -802,6 +810,12 @@ export async function runTui() {
|
|
|
802
810
|
refresh();
|
|
803
811
|
return;
|
|
804
812
|
}
|
|
813
|
+
// Tur çalışıyorsa ilk Ctrl+C turu durdurur (app'i kapatmaz); boştayken çift-C çıkış.
|
|
814
|
+
if (busy && turnAbort) {
|
|
815
|
+
turnAbort.abort();
|
|
816
|
+
printItem({ kind: 'note', text: t('tui.userStopped') });
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
805
819
|
const now = Date.now();
|
|
806
820
|
if (now - ctrlcAt < 2000) {
|
|
807
821
|
quit();
|