wormclaude 1.0.22 → 1.0.23

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, Static, useApp, useInput } from 'ink';
3
+ import { render, Box, Text, 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';
@@ -11,6 +11,7 @@ import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig
11
11
  import { sanitizeError, sanitizeOutput } from './errorsan.js';
12
12
  import { cleanModelText } from './textclean.js';
13
13
  import { MarkdownDisplay } from './markdown.js';
14
+ import { highlight } from './highlight.js';
14
15
  import { resolveAtMentions } from './atmention.js';
15
16
  import { summarizeTools } from './toolSummary.js';
16
17
  import { pickTipId, tipText } from './tips.js';
@@ -85,10 +86,19 @@ const _initHistory = () => {
85
86
  sys.push({ role: 'system', content: _memCtx });
86
87
  return sys;
87
88
  };
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ı.
89
+ // ALT-SCREEN (alternate screen) — ink normal buffer'da resize'da eski dinamik çerçeveyi
90
+ // silemeyip KASKAD yapıyor (scrollback reflow). Alt-screen tüm ekranı yönetir resize'da
91
+ // temiz yeniden çizim, kaskad YOK. Kopyalama: alt-screen'de fare seçimi sınırlı olduğundan
92
+ // /kopyala komutu (OSC52 ile panoya) sağlanır. ?1049h alt-screen · ?1007h alternate-scroll.
93
+ try {
94
+ process.stdout.write('\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H');
95
+ }
96
+ catch { }
97
+ const _leaveAlt = () => { try {
98
+ process.stdout.write('\x1b[?1007l\x1b[?1049l');
99
+ }
100
+ catch { } };
101
+ process.on('exit', _leaveAlt);
92
102
  function useDimensions() {
93
103
  const [d, setD] = useState({
94
104
  cols: process.stdout.columns || 80,
@@ -148,6 +158,91 @@ function wrapText(text, width) {
148
158
  }
149
159
  return out;
150
160
  }
161
+ // Inline markdown (`code` **bold**) → segment'ler.
162
+ function inlineSegs(text, base) {
163
+ const segs = [];
164
+ const re = /(`[^`]+`|\*\*[^*]+\*\*)/g;
165
+ let last = 0;
166
+ let m;
167
+ while ((m = re.exec(text)) !== null) {
168
+ if (m.index > last)
169
+ segs.push({ text: text.slice(last, m.index), color: base });
170
+ const f = m[0];
171
+ if (f.startsWith('`'))
172
+ segs.push({ text: f.slice(1, -1), color: theme.green });
173
+ else
174
+ segs.push({ text: f.slice(2, -2), color: base, bold: true });
175
+ last = re.lastIndex;
176
+ }
177
+ if (last < text.length)
178
+ segs.push({ text: text.slice(last), color: base });
179
+ return segs.length ? segs : [{ text, color: base }];
180
+ }
181
+ // Markdown metnini segment-satırlarına çevirir; kod blokları sözdizimi-vurgulu (highlight()).
182
+ function mdToSegLines(text, W) {
183
+ const out = [];
184
+ const white = theme.white;
185
+ let inCode = false, fence = '', lang = '', code = [];
186
+ const fenceRe = /^ *(`{3,}|~{3,}) *(\w*)? *$/;
187
+ const flush = () => {
188
+ if (lang)
189
+ out.push([{ text: ' ' + lang, dim: true }]);
190
+ for (const toks of highlight(code.join('\n'), lang)) {
191
+ const segs = [{ text: '│ ', dim: true }];
192
+ if (toks.length)
193
+ for (const tk of toks)
194
+ segs.push({ text: tk.text, color: tk.color });
195
+ else
196
+ segs.push({ text: ' ' });
197
+ out.push(segs);
198
+ }
199
+ code = [];
200
+ lang = '';
201
+ fence = '';
202
+ };
203
+ for (const line of text.split('\n')) {
204
+ if (inCode) {
205
+ const fm = line.match(fenceRe);
206
+ if (fm && fm[1][0] === fence[0] && fm[1].length >= fence.length) {
207
+ inCode = false;
208
+ flush();
209
+ }
210
+ else
211
+ code.push(line);
212
+ continue;
213
+ }
214
+ const fm = line.match(fenceRe);
215
+ if (fm) {
216
+ inCode = true;
217
+ fence = fm[1];
218
+ lang = (fm[2] || '').trim();
219
+ continue;
220
+ }
221
+ const hm = line.match(/^ *(#{1,4}) +(.*)/);
222
+ const um = line.match(/^([ \t]*)([-*+]) +(.*)/);
223
+ const om = line.match(/^([ \t]*)(\d+)\. +(.*)/);
224
+ if (hm) {
225
+ const lvl = hm[1].length;
226
+ out.push([{ text: hm[2], color: lvl <= 2 ? theme.redBright : white, bold: lvl <= 3, italic: lvl > 3 }]);
227
+ }
228
+ else if (um || om) {
229
+ const [, ws, marker, itemText] = (um || om);
230
+ const prefix = om ? `${marker}. ` : '• ';
231
+ const wrapped = wrapText(itemText, Math.max(8, W - ws.length - prefix.length));
232
+ wrapped.forEach((w, i) => out.push([{ text: ' '.repeat(ws.length) + (i === 0 ? prefix : ' '), dim: true }, ...inlineSegs(w, white)]));
233
+ }
234
+ else if (line.trim() === '') {
235
+ out.push([]);
236
+ }
237
+ else {
238
+ for (const w of wrapText(line, W))
239
+ out.push(inlineSegs(w, white));
240
+ }
241
+ }
242
+ if (inCode)
243
+ flush();
244
+ return out;
245
+ }
151
246
  // Tüm öğeleri görsel satır dizisine çevirir (kaydırma satır granülerliğinde).
152
247
  function buildLines(items, cols) {
153
248
  const lines = [];
@@ -171,7 +266,8 @@ function buildLines(items, cols) {
171
266
  lines.push([{ text: '╰' + '─'.repeat(W - 2) + '╯', dim: true }]);
172
267
  }
173
268
  else if (it.kind === 'assistant') {
174
- wrapText(it.text, W - 2).forEach((ln, i) => lines.push(i === 0 ? [{ text: '⏺ ', color: redB, bold: true }, { text: ln, color: white }] : [{ text: ' ' + ln, color: white }]));
269
+ const seg = mdToSegLines(it.text, W - 2);
270
+ seg.forEach((segs, i) => lines.push(i === 0 ? [{ text: '⏺ ', color: redB, bold: true }, ...segs] : [{ text: ' ', dim: true }, ...segs]));
175
271
  }
176
272
  else if (it.kind === 'tool') {
177
273
  const n = it.result.split('\n').length, chars = it.result.length;
@@ -440,9 +536,24 @@ function App() {
440
536
  else if (key.downArrow)
441
537
  setCmdSel((s) => (s + 1) % n);
442
538
  });
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.
539
+ // Geçmişte kaydırma: PageUp/PageDown her zaman; boş input'ta ok tuşları (fare tekerleği
540
+ // alternate-scroll ile ok yollar tekerlekle kaydırma). Alt-screen olduğundan biz yönetiriz.
541
+ useInput((_inp, key) => {
542
+ if (!started || perm || ask || lang === null)
543
+ return;
544
+ if (key.pageUp)
545
+ setScroll((s) => s + 10);
546
+ else if (key.pageDown)
547
+ setScroll((s) => Math.max(0, s - 10));
548
+ else if (input === '' && !busy) {
549
+ if (key.upArrow)
550
+ setScroll((s) => s + 3);
551
+ else if (key.downArrow)
552
+ setScroll((s) => Math.max(0, s - 3));
553
+ }
554
+ });
555
+ // Yeni mesaj gelince en alta dön
556
+ useEffect(() => { setScroll(0); }, [items.length]);
446
557
  // Başlangıçta güven seviyesini (Doğrulanmış Araştırmacı Programı) çek → StatusLine rozeti
447
558
  useEffect(() => {
448
559
  if (!started)
@@ -695,6 +806,31 @@ function App() {
695
806
  v = filtered[Math.min(cmdSel, filtered.length - 1)].name;
696
807
  }
697
808
  const tok = v.split(' ')[0];
809
+ // /kopyala [hepsi] — son yanıtı (veya tüm sohbeti) panoya kopyalar (OSC52, fare gerekmez).
810
+ if (tok === '/kopyala' || tok === '/copy') {
811
+ const arg = v.slice(tok.length).trim().toLowerCase();
812
+ const en = getLang() === 'en';
813
+ const asst = items.filter((it) => it.kind === 'assistant').map((it) => it.text);
814
+ let text = '';
815
+ if (arg === 'all' || arg === 'hepsi') {
816
+ text = items.map((it) => it.kind === 'user' ? '> ' + it.text : it.kind === 'assistant' ? it.text : '').filter(Boolean).join('\n\n');
817
+ }
818
+ else
819
+ text = asst[asst.length - 1] || '';
820
+ if (!text) {
821
+ push({ kind: 'note', text: en ? 'Nothing to copy.' : 'Kopyalanacak içerik yok.' });
822
+ return;
823
+ }
824
+ try {
825
+ const b64 = Buffer.from(text, 'utf8').toString('base64');
826
+ process.stdout.write(`\x1b]52;c;${b64}\x07`);
827
+ push({ kind: 'note', text: en ? `✓ Copied to clipboard (${text.length} chars).` : `✓ Panoya kopyalandı (${text.length} karakter).` });
828
+ }
829
+ catch {
830
+ push({ kind: 'note', text: en ? 'Copy failed.' : 'Kopyalama başarısız.' });
831
+ }
832
+ return;
833
+ }
698
834
  // /agent ve /multi-agent — tek / çoklu alt-agent
699
835
  if (tok === '/agent' || tok === '/multi-agent') {
700
836
  const task = v.slice(tok.length).trim();
@@ -788,9 +924,10 @@ function App() {
788
924
  runAgent(v);
789
925
  }
790
926
  const { cols, rows } = useDimensions();
791
- // İlk açılış: dil seçimi (banner stdout'a basıldı; burada yalnız seçim footer'ı)
927
+ // İlk açılış: dil seçimi
792
928
  if (lang === null) {
793
- return (React.createElement(Box, { flexDirection: "column", width: cols },
929
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
930
+ React.createElement(Banner, null),
794
931
  React.createElement(Box, { flexDirection: "column", marginTop: 1, paddingX: 1 },
795
932
  React.createElement(Text, { color: theme.redBright, bold: true }, t('lang.title')),
796
933
  React.createElement(Box, { flexDirection: "column", marginTop: 1 },
@@ -802,11 +939,14 @@ function App() {
802
939
  t('lang.en'))),
803
940
  React.createElement(Text, { color: theme.greyDim },
804
941
  " ",
805
- t('lang.hint')))));
942
+ t('lang.hint'))),
943
+ React.createElement(Box, { flexGrow: 1 }),
944
+ React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
806
945
  }
807
- // Açılış: klasöre güven sorusu (banner stdout'ta; burada yalnız soru footer'ı)
946
+ // Açılış: klasöre güven sorusu (WormClaude tarzı)
808
947
  if (!started) {
809
- return (React.createElement(Box, { flexDirection: "column", width: cols },
948
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
949
+ React.createElement(Banner, null),
810
950
  React.createElement(Box, { flexDirection: "column", marginTop: 1, paddingX: 1 },
811
951
  React.createElement(Text, { color: theme.redBright, bold: true }, t('trust.accessing')),
812
952
  React.createElement(Text, null, " "),
@@ -824,14 +964,36 @@ function App() {
824
964
  t('trust.no'))),
825
965
  React.createElement(Text, { color: theme.greyDim },
826
966
  " ",
827
- t('trust.hint')))));
967
+ t('trust.hint'))),
968
+ React.createElement(Box, { flexGrow: 1 }),
969
+ React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
828
970
  }
829
- return (React.createElement(Box, { flexDirection: "column", width: cols },
830
- React.createElement(Static, { items: items }, (item, i) => React.createElement(RenderItem, { key: i, item: item })),
831
- streaming ? (React.createElement(Box, { marginTop: 1, flexDirection: "row" },
832
- React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
833
- React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
834
- React.createElement(MarkdownDisplay, { text: streaming })))) : null,
971
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
972
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden" }, (() => {
973
+ // Alt-screen + satır-pencere pager: tüm öğeleri satıra çevir, pencere göster.
974
+ // (Kopyalama /kopyala komutuyla; resize'da kaskad olmaz.)
975
+ const all = buildLines(items, cols);
976
+ const reserve = busy ? 4 : 6;
977
+ const avail = Math.max(4, rows - reserve - (streaming ? 3 : 0) - 2);
978
+ const maxScroll = Math.max(0, all.length - avail);
979
+ const off = Math.min(scroll, maxScroll);
980
+ const startLine = Math.max(0, all.length - avail - off);
981
+ const view = all.slice(startLine, startLine + avail);
982
+ return (React.createElement(React.Fragment, null,
983
+ startLine > 0 ? React.createElement(Text, { color: theme.greyDim },
984
+ " \u2191 ",
985
+ startLine,
986
+ " sat\u0131r \u00B7 PageUp") : null,
987
+ view.map((segs, i) => (React.createElement(Text, { key: i }, segs.length === 0 ? ' ' : segs.map((s, j) => (React.createElement(Text, { key: j, color: s.dim ? theme.greyDim : s.color, bold: s.bold, italic: s.italic }, linkify(s.text))))))),
988
+ off > 0 ? React.createElement(Text, { color: theme.greyDim },
989
+ " \u2193 ",
990
+ off,
991
+ " sat\u0131r \u00B7 PageDown / Son") : null,
992
+ streaming && off === 0 ? (React.createElement(Box, { flexDirection: "row" },
993
+ React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
994
+ React.createElement(Box, { flexDirection: "column" },
995
+ React.createElement(Text, { color: theme.white }, streaming)))) : null));
996
+ })()),
835
997
  perm ? (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.red, paddingX: 1, marginTop: 1 },
836
998
  React.createElement(Text, { color: theme.redBright, bold: true }, t('perm.title', perm.label)),
837
999
  permMode === 'feedback' ? (React.createElement(React.Fragment, null,
package/dist/commands.js CHANGED
@@ -18,6 +18,7 @@ import { programText, tier } from './program.js';
18
18
  export const COMMANDS = [
19
19
  { name: '/help', desc: 'komutları ve ipuçlarını göster' },
20
20
  { name: '/clear', desc: 'sohbeti ve geçmişi temizle' },
21
+ { name: '/kopyala', desc: 'son yanıtı panoya kopyala (/kopyala hepsi · tüm sohbet)' },
21
22
  { name: '/compact', desc: 'geçmişi modelle özetleyip bağlamı küçült' },
22
23
  { name: '/context', desc: 'bağlam / token kullanımını göster' },
23
24
  { name: '/cost', desc: 'oturum token tahminini göster' },
package/dist/theme.js CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.22';
19
+ export const VERSION = '1.0.23';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {