wormclaude 1.0.20 → 1.0.21

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/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import React, { useState, useRef, useEffect } from 'react';
3
- import { render, Box, Text, useApp, useInput } from 'ink';
3
+ import { render, Box, Text, Static, useApp, useInput } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
5
  import * as path from 'node:path';
6
6
  import { theme, VERSION } from './theme.js';
@@ -85,18 +85,10 @@ const _initHistory = () => {
85
85
  sys.push({ role: 'system', content: _memCtx });
86
86
  return sys;
87
87
  };
88
- // FULLSCREEN (alternate screen) — WormClaude'un yöntemi: tüm ekranı ink yönetir,
89
- // scrollback YOK, resize'da HER ŞEY yeniden çizilir → sarmalanma/kaskad olmaz.
90
- // ?1049h alt-screen · ?1007h alternate-scroll (fare tekerleği → ok tuşu; seçim bozulmaz)
91
- try {
92
- process.stdout.write('\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H');
93
- }
94
- catch { }
95
- const _leaveAlt = () => { try {
96
- process.stdout.write('\x1b[?1007l\x1b[?1049l');
97
- }
98
- catch { } };
99
- process.on('exit', _leaveAlt);
88
+ // NORMAL buffer (alt-screen YOK) — transcript <Static> ile gerçek terminal scrollback'ine
89
+ // basılır. Böylece terminalin doğal kaydırması (fare tekerleği / Shift+PageUp) ve metin
90
+ // seçip kopyalama tam çalışır. Eskiden ?1049h alt-screen kullanılıyordu; o, scrollback'i ve
91
+ // kopyalamayı engelliyordu — kaldırıldı.
100
92
  function useDimensions() {
101
93
  const [d, setD] = useState({
102
94
  cols: process.stdout.columns || 80,
@@ -448,25 +440,9 @@ function App() {
448
440
  else if (key.downArrow)
449
441
  setCmdSel((s) => (s + 1) % n);
450
442
  });
451
- // Geçmişte kaydırma: PageUp/PageDown her zaman; boş input'ta ok tuşları (fare
452
- // tekerleği alternate-scroll ile ok tuşu yollar tekerlekle kaydırma).
453
- useInput((_inp, key) => {
454
- if (!started || perm || ask || lang === null)
455
- return;
456
- // satır-temelli; üst sınır render'da off=min(scroll,maxScroll) ile kırpılır
457
- if (key.pageUp)
458
- setScroll((s) => s + 10);
459
- else if (key.pageDown)
460
- setScroll((s) => Math.max(0, s - 10));
461
- else if (input === '' && !busy) {
462
- if (key.upArrow)
463
- setScroll((s) => s + 3);
464
- else if (key.downArrow)
465
- setScroll((s) => Math.max(0, s - 3));
466
- }
467
- });
468
- // Yeni mesaj gelince en alta dön
469
- useEffect(() => { setScroll(0); }, [items.length]);
443
+ // NOT: Transcript artık <Static> ile terminal scrollback'ine basılıyor; kaydırma
444
+ // terminalin kendi işi (fare tekerleği / Shift+PageUp). Tuşları YAKALAMIYORUZ ki
445
+ // doğal kaydırma + metin seçip kopyalama bozulmasın.
470
446
  // Başlangıçta güven seviyesini (Doğrulanmış Araştırmacı Programı) çek → StatusLine rozeti
471
447
  useEffect(() => {
472
448
  if (!started)
@@ -856,33 +832,12 @@ function App() {
856
832
  React.createElement(Box, { flexGrow: 1 }),
857
833
  React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
858
834
  }
859
- return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
860
- React.createElement(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden" }, (() => {
861
- // Satır-temelli kaydırma (Claude tarzı): tüm öğeleri satıra çevir, pencere göster
862
- const all = buildLines(items, cols);
863
- const reserve = busy ? 4 : 6;
864
- const avail = Math.max(4, rows - reserve - (streaming ? 3 : 0) - 2); // -2: göstergeler
865
- const maxScroll = Math.max(0, all.length - avail);
866
- const off = Math.min(scroll, maxScroll); // alttan kaç satır yukarı
867
- const startLine = Math.max(0, all.length - avail - off);
868
- const view = all.slice(startLine, startLine + avail);
869
- return (React.createElement(React.Fragment, null,
870
- startLine > 0 ? React.createElement(Text, { color: theme.greyDim },
871
- " \u2191 ",
872
- startLine,
873
- " sat\u0131r \u00B7 PageUp") : null,
874
- view.map((segs, i) => (React.createElement(Text, { key: i }, segs.length === 0
875
- ? ' '
876
- : segs.map((s, j) => (React.createElement(Text, { key: j, color: s.dim ? theme.greyDim : s.color, bold: s.bold }, linkify(s.text))))))),
877
- off > 0 ? React.createElement(Text, { color: theme.greyDim },
878
- " \u2193 ",
879
- off,
880
- " sat\u0131r \u00B7 PageDown / Son") : null,
881
- streaming && off === 0 ? (React.createElement(Box, { flexDirection: "row" },
882
- React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
883
- React.createElement(Box, { flexDirection: "column" },
884
- React.createElement(Text, { color: theme.white }, streaming)))) : null));
885
- })()),
835
+ return (React.createElement(Box, { flexDirection: "column", width: cols },
836
+ React.createElement(Static, { items: items }, (item, i) => React.createElement(RenderItem, { key: i, item: item })),
837
+ streaming ? (React.createElement(Box, { marginTop: 1, flexDirection: "row" },
838
+ React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
839
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
840
+ React.createElement(MarkdownDisplay, { text: streaming })))) : null,
886
841
  perm ? (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.red, paddingX: 1, marginTop: 1 },
887
842
  React.createElement(Text, { color: theme.redBright, bold: true }, t('perm.title', perm.label)),
888
843
  permMode === 'feedback' ? (React.createElement(React.Fragment, null,
@@ -0,0 +1,132 @@
1
+ // Bağımlılıksız terminal sözdizimi vurgusu (lowlight/highlight.js yerine hafif tokenizer).
2
+ // Kod bloklarını dile göre token'lara böler; her token'a tema rengi atar. Asla patlamaz —
3
+ // tanımadığı dilde ortak kurallarla (string/sayı/yorum/anahtar kelime) renklendirir.
4
+ import { theme } from './theme.js';
5
+ // Dil bazlı anahtar kelime setleri (ortak + spesifik).
6
+ const KW = {
7
+ js: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'new', 'class', 'extends', 'super', 'this', 'typeof', 'instanceof', 'in', 'of', 'await', 'async', 'yield', 'try', 'catch', 'finally', 'throw', 'import', 'export', 'from', 'default', 'delete', 'void', 'null', 'undefined', 'true', 'false'],
8
+ ts: ['interface', 'type', 'enum', 'implements', 'public', 'private', 'protected', 'readonly', 'abstract', 'namespace', 'declare', 'as', 'keyof', 'infer', 'satisfies'],
9
+ py: ['def', 'class', 'return', 'if', 'elif', 'else', 'for', 'while', 'break', 'continue', 'import', 'from', 'as', 'with', 'try', 'except', 'finally', 'raise', 'lambda', 'yield', 'global', 'nonlocal', 'pass', 'assert', 'del', 'in', 'is', 'not', 'and', 'or', 'None', 'True', 'False', 'self', 'async', 'await'],
10
+ bash: ['if', 'then', 'else', 'elif', 'fi', 'for', 'while', 'do', 'done', 'case', 'esac', 'function', 'in', 'return', 'export', 'local', 'echo', 'cd', 'source', 'set', 'unset'],
11
+ go: ['func', 'package', 'import', 'var', 'const', 'type', 'struct', 'interface', 'map', 'chan', 'go', 'defer', 'return', 'if', 'else', 'for', 'range', 'switch', 'case', 'select', 'break', 'continue', 'nil', 'true', 'false'],
12
+ rust: ['fn', 'let', 'mut', 'const', 'struct', 'enum', 'impl', 'trait', 'pub', 'use', 'mod', 'match', 'if', 'else', 'for', 'while', 'loop', 'return', 'self', 'Self', 'where', 'async', 'await', 'move', 'ref', 'true', 'false'],
13
+ java: ['public', 'private', 'protected', 'class', 'interface', 'extends', 'implements', 'static', 'final', 'void', 'new', 'return', 'if', 'else', 'for', 'while', 'switch', 'case', 'break', 'continue', 'try', 'catch', 'finally', 'throw', 'throws', 'import', 'package', 'this', 'super', 'null', 'true', 'false'],
14
+ css: ['important', 'inherit', 'initial', 'auto', 'none', 'flex', 'grid', 'block', 'inline'],
15
+ };
16
+ function langKeywords(lang) {
17
+ const l = (lang || '').toLowerCase();
18
+ const base = [];
19
+ if (l === 'ts' || l === 'tsx' || l === 'typescript')
20
+ base.push(...KW.js, ...KW.ts);
21
+ else if (l === 'js' || l === 'jsx' || l === 'javascript' || l === 'json' || l === 'node')
22
+ base.push(...KW.js);
23
+ else if (l === 'py' || l === 'python')
24
+ base.push(...KW.py);
25
+ else if (l === 'sh' || l === 'bash' || l === 'shell' || l === 'zsh')
26
+ base.push(...KW.bash);
27
+ else if (l === 'go' || l === 'golang')
28
+ base.push(...KW.go);
29
+ else if (l === 'rs' || l === 'rust')
30
+ base.push(...KW.rust);
31
+ else if (l === 'java')
32
+ base.push(...KW.java);
33
+ else if (l === 'css' || l === 'scss')
34
+ base.push(...KW.css);
35
+ else
36
+ base.push(...KW.js, ...KW.py); // bilinmeyen dil → en yaygın iki set
37
+ return new Set(base);
38
+ }
39
+ const lineComment = (lang) => {
40
+ const l = (lang || '').toLowerCase();
41
+ if (['py', 'python', 'sh', 'bash', 'shell', 'zsh', 'yaml', 'yml', 'rb', 'ruby', 'toml'].includes(l))
42
+ return '#';
43
+ return '//';
44
+ };
45
+ // Bir satırı token'lara böl. Basit ama sağlam bir durum makinesi.
46
+ function tokenizeLine(line, kw, lc) {
47
+ const toks = [];
48
+ let i = 0;
49
+ const n = line.length;
50
+ const push = (text, color) => { if (text)
51
+ toks.push({ text, color }); };
52
+ while (i < n) {
53
+ const ch = line[i];
54
+ // satır yorumu (// veya #)
55
+ if (line.startsWith(lc, i)) {
56
+ push(line.slice(i), theme.synComment);
57
+ break;
58
+ }
59
+ // dizgi: " ' `
60
+ if (ch === '"' || ch === "'" || ch === '`') {
61
+ let j = i + 1;
62
+ while (j < n && line[j] !== ch) {
63
+ if (line[j] === '\\')
64
+ j++;
65
+ j++;
66
+ }
67
+ push(line.slice(i, Math.min(j + 1, n)), theme.synString);
68
+ i = j + 1;
69
+ continue;
70
+ }
71
+ // sayı
72
+ if (/[0-9]/.test(ch) && (i === 0 || !/[A-Za-z_]/.test(line[i - 1]))) {
73
+ let j = i;
74
+ while (j < n && /[0-9a-fA-FxX._]/.test(line[j]))
75
+ j++;
76
+ push(line.slice(i, j), theme.synNumber);
77
+ i = j;
78
+ continue;
79
+ }
80
+ // tanımlayıcı / anahtar kelime
81
+ if (/[A-Za-z_$@]/.test(ch)) {
82
+ let j = i;
83
+ while (j < n && /[A-Za-z0-9_$]/.test(line[j]))
84
+ j++;
85
+ const word = line.slice(i, j);
86
+ const after = line[j];
87
+ if (kw.has(word))
88
+ push(word, theme.synKeyword);
89
+ else if (after === '(')
90
+ push(word, theme.synFunc); // fonksiyon çağrısı
91
+ else if (/^[A-Z]/.test(word))
92
+ push(word, theme.synType); // Tip/Sınıf
93
+ else
94
+ push(word); // düz
95
+ i = j;
96
+ continue;
97
+ }
98
+ // tek karakter (operatör/noktalama) — düz
99
+ push(ch);
100
+ i++;
101
+ }
102
+ return toks;
103
+ }
104
+ /** Kodu satır-satır token dizilerine çevirir. Her satır Tok[]. */
105
+ export function highlight(code, lang = '') {
106
+ try {
107
+ const kw = langKeywords(lang);
108
+ const lc = lineComment(lang);
109
+ let blockComment = false; // /* ... */ için
110
+ return code.split('\n').map((line) => {
111
+ // çok-satırlı /* */ yorum bloğu (basit destek)
112
+ if (blockComment) {
113
+ const end = line.indexOf('*/');
114
+ if (end === -1)
115
+ return [{ text: line, color: theme.synComment }];
116
+ blockComment = false;
117
+ const rest = tokenizeLine(line.slice(end + 2), kw, lc);
118
+ return [{ text: line.slice(0, end + 2), color: theme.synComment }, ...rest];
119
+ }
120
+ const start = line.indexOf('/*');
121
+ if (start !== -1 && line.indexOf('*/', start) === -1) {
122
+ blockComment = true;
123
+ const pre = tokenizeLine(line.slice(0, start), kw, lc);
124
+ return [...pre, { text: line.slice(start), color: theme.synComment }];
125
+ }
126
+ return tokenizeLine(line, kw, lc);
127
+ });
128
+ }
129
+ catch {
130
+ return code.split('\n').map((l) => [{ text: l, color: theme.synString }]);
131
+ }
132
+ }
package/dist/markdown.js CHANGED
@@ -4,6 +4,18 @@ import React from 'react';
4
4
  import { Text, Box } from 'ink';
5
5
  import stringWidth from 'string-width';
6
6
  import { theme } from './theme.js';
7
+ import { highlight } from './highlight.js';
8
+ // Kod bloğunu sözdizimi-vurgulu render eder (sol kenarda ince kırmızı çubuk + dil etiketi).
9
+ function CodeBlock({ code, lang }) {
10
+ const hl = highlight(code.join('\n'), lang);
11
+ return (React.createElement(Box, { flexDirection: "column", marginY: 1 },
12
+ lang ? React.createElement(Text, { color: theme.greyDim },
13
+ ' ',
14
+ lang) : null,
15
+ hl.map((toks, i) => (React.createElement(Box, { key: i },
16
+ React.createElement(Text, { color: theme.greyDim }, "\u2502 "),
17
+ React.createElement(Text, null, toks.length ? toks.map((tk, j) => React.createElement(Text, { key: j, color: tk.color }, tk.text)) : ' '))))));
18
+ }
7
19
  // ── Inline markdown: **bold** *italik* ~~strike~~ `code` [text](url) <u>u</u> url ──
8
20
  const INLINE_RE = /(\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|<u>.*?<\/u>|https?:\/\/\S+)/g;
9
21
  export function RenderInline({ text }) {
@@ -122,7 +134,7 @@ export function MarkdownDisplay({ text, width }) {
122
134
  const blocks = [];
123
135
  let key = 0;
124
136
  const push = (n) => blocks.push(React.createElement(React.Fragment, { key: key++ }, n));
125
- let inCode = false, codeFence = '', codeLines = [];
137
+ let inCode = false, codeFence = '', codeLang = '', codeLines = [];
126
138
  let inTable = false, tHeaders = [], tRows = [];
127
139
  const flushTable = () => { if (tHeaders.length)
128
140
  push(React.createElement(Table, { headers: tHeaders, rows: tRows, width: W })); inTable = false; tHeaders = []; tRows = []; };
@@ -137,9 +149,10 @@ export function MarkdownDisplay({ text, width }) {
137
149
  if (inCode) {
138
150
  const fm = line.match(fenceRe);
139
151
  if (fm && fm[1][0] === codeFence[0] && fm[1].length >= codeFence.length) {
140
- push(React.createElement(Box, { flexDirection: "column", marginY: 1 }, codeLines.map((c, i) => React.createElement(Text, { key: i, color: theme.green }, c))));
152
+ push(React.createElement(CodeBlock, { code: codeLines, lang: codeLang }));
141
153
  inCode = false;
142
154
  codeFence = '';
155
+ codeLang = '';
143
156
  codeLines = [];
144
157
  }
145
158
  else
@@ -153,6 +166,7 @@ export function MarkdownDisplay({ text, width }) {
153
166
  flushTable();
154
167
  inCode = true;
155
168
  codeFence = fm[1];
169
+ codeLang = (fm[2] || '').trim();
156
170
  return;
157
171
  }
158
172
  if (rm && !inTable) {
@@ -214,7 +228,7 @@ export function MarkdownDisplay({ text, width }) {
214
228
  React.createElement(RenderInline, { text: line })));
215
229
  });
216
230
  if (inCode)
217
- push(React.createElement(Box, { flexDirection: "column", marginY: 1 }, codeLines.map((c, i) => React.createElement(Text, { key: i, color: theme.green }, c))));
231
+ push(React.createElement(CodeBlock, { code: codeLines, lang: codeLang }));
218
232
  if (inTable)
219
233
  flushTable();
220
234
  return React.createElement(Box, { flexDirection: "column" }, blocks);
package/dist/theme.js CHANGED
@@ -7,5 +7,13 @@ export const theme = {
7
7
  greyDim: '#666666',
8
8
  green: '#4ade80',
9
9
  errorRed: '#ff6b6b',
10
+ // ── Sözdizimi vurgusu (kod blokları) ──
11
+ synKeyword: '#ff5c5c', // anahtar kelimeler (if, function, def…) — marka kırmızısı
12
+ synString: '#4ade80', // dizgiler "…" '…' `…`
13
+ synNumber: '#f0b429', // sayılar
14
+ synComment: '#666666', // yorumlar
15
+ synFunc: '#4fc3f7', // fonksiyon/metot adları
16
+ synType: '#a78bfa', // tip/sınıf adları, sabitler
17
+ synProp: '#e0e0e0', // özellik/anahtar adları
10
18
  };
11
- export const VERSION = '1.0.19';
19
+ export const VERSION = '1.0.21';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.20",
3
+ "version": "1.0.21",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {