thebird 1.2.56 → 1.2.57

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.
@@ -0,0 +1,130 @@
1
+ export function createReadline({ term, getCompletions, onLine, getPrompt }) {
2
+ let buf = '';
3
+ let pos = 0;
4
+ let histIdx = -1;
5
+ let escBuf = '';
6
+ let inEsc = false;
7
+
8
+ const write = s => term.write(s);
9
+
10
+ function promptStr() { return '\r\n\x1b[32m' + getPrompt() + ' $ \x1b[0m'; }
11
+ function showPrompt() { write(promptStr()); }
12
+
13
+ function redraw() {
14
+ write('\r\x1b[2K\x1b[32m' + getPrompt() + ' $ \x1b[0m' + buf);
15
+ if (pos < buf.length) write('\x1b[' + (buf.length - pos) + 'D');
16
+ }
17
+
18
+ function insert(ch) {
19
+ buf = buf.slice(0, pos) + ch + buf.slice(pos);
20
+ pos++;
21
+ redraw();
22
+ }
23
+
24
+ function delBefore() {
25
+ if (!pos) return;
26
+ buf = buf.slice(0, pos - 1) + buf.slice(pos);
27
+ pos--;
28
+ redraw();
29
+ }
30
+
31
+ function delAfter() {
32
+ if (pos >= buf.length) return;
33
+ buf = buf.slice(0, pos) + buf.slice(pos + 1);
34
+ redraw();
35
+ }
36
+
37
+ function moveTo(n) {
38
+ pos = Math.max(0, Math.min(buf.length, n));
39
+ const pLen = getPrompt().length + 3;
40
+ if (pLen + pos > 0) write('\r\x1b[' + (pLen + pos) + 'C');
41
+ else write('\r');
42
+ }
43
+
44
+ function handleTab() {
45
+ const word = buf.slice(0, pos).split(/\s+/).pop() || '';
46
+ const completions = getCompletions(buf, word);
47
+ if (!completions.length) return;
48
+ if (completions.length === 1) {
49
+ const rest = completions[0].slice(word.length);
50
+ buf = buf.slice(0, pos) + rest + buf.slice(pos);
51
+ pos += rest.length;
52
+ redraw();
53
+ return;
54
+ }
55
+ const common = completions.reduce((a, b) => {
56
+ let i = 0;
57
+ while (i < a.length && a[i] === b[i]) i++;
58
+ return a.slice(0, i);
59
+ });
60
+ if (common.length > word.length) {
61
+ const rest = common.slice(word.length);
62
+ buf = buf.slice(0, pos) + rest + buf.slice(pos);
63
+ pos += rest.length;
64
+ redraw();
65
+ return;
66
+ }
67
+ write('\r\n' + completions.join(' '));
68
+ redraw();
69
+ }
70
+
71
+ function commit() {
72
+ const line = buf;
73
+ write('\r\n');
74
+ const hist = getHistory();
75
+ if (line.trim()) hist.unshift(line);
76
+ buf = '';
77
+ pos = 0;
78
+ histIdx = -1;
79
+ onLine(line);
80
+ }
81
+
82
+ function getHistory() { return window.__debug?.shell?.history || []; }
83
+
84
+ function histNav(dir) {
85
+ const hist = getHistory();
86
+ if (!hist.length) return;
87
+ histIdx = Math.max(-1, Math.min(hist.length - 1, histIdx + dir));
88
+ buf = histIdx === -1 ? '' : hist[histIdx];
89
+ pos = buf.length;
90
+ redraw();
91
+ }
92
+
93
+ const ESC_MAP = {
94
+ '[A': () => histNav(1),
95
+ '[B': () => histNav(-1),
96
+ '[C': () => { if (pos < buf.length) { pos++; write('\x1b[C'); } },
97
+ '[D': () => { if (pos > 0) { pos--; write('\x1b[D'); } },
98
+ '[H': () => moveTo(0),
99
+ '[F': () => moveTo(buf.length),
100
+ '[3~': () => delAfter(),
101
+ '[1~': () => moveTo(0),
102
+ '[4~': () => moveTo(buf.length),
103
+ };
104
+
105
+ function onData(data) {
106
+ if (inEsc) {
107
+ escBuf += data;
108
+ if (escBuf === '[') return;
109
+ if (escBuf.match(/^\[[\x40-\x7E]$/) || escBuf.match(/^\[\d+~$/)) {
110
+ const handler = ESC_MAP[escBuf];
111
+ if (handler) handler();
112
+ inEsc = false; escBuf = '';
113
+ return;
114
+ }
115
+ if (escBuf.length > 6) { inEsc = false; escBuf = ''; }
116
+ return;
117
+ }
118
+ if (data === '\x1b') { inEsc = true; escBuf = ''; return; }
119
+ if (data === '\x01') { moveTo(0); return; }
120
+ if (data === '\x05') { moveTo(buf.length); return; }
121
+ if (data === '\x0b') { buf = buf.slice(0, pos); redraw(); return; }
122
+ if (data === '\x15') { buf = buf.slice(pos); pos = 0; redraw(); return; }
123
+ if (data === '\x09') { handleTab(); return; }
124
+ if (data === '\r') { commit(); return; }
125
+ if (data === '\x7f') { delBefore(); return; }
126
+ if (data >= ' ') { insert(data); return; }
127
+ }
128
+
129
+ return { onData, showPrompt };
130
+ }
package/docs/shell.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createMachine, createActor } from './vendor/xstate.js';
2
2
  import { createNodeEnv } from './shell-node.js';
3
+ import { createReadline } from './shell-readline.js';
3
4
 
4
5
  function resolvePath(cwd, p) {
5
6
  if (!p || p === '~') return '/';
@@ -134,7 +135,6 @@ export function createShell({ term, onPreviewWrite }) {
134
135
 
135
136
  async function run(line, onData) {
136
137
  if (!line.trim()) return;
137
- ctx.history.push(line);
138
138
  const st = actor.getSnapshot().value;
139
139
  if (st === 'node-repl' && line.trim() !== 'exit') { await ctx.nodeEval(line); return; }
140
140
  if (line.trim() === 'exit') { BUILTINS.exit(actor); return; }
@@ -162,16 +162,30 @@ export function createShell({ term, onPreviewWrite }) {
162
162
  }
163
163
  }
164
164
 
165
- const prompt = () => term.write('\r\n\x1b[32m' + ctx.cwd + ' $ \x1b[0m');
166
- let buf = '';
165
+ function getCompletions(line, word) {
166
+ const snap = window.__debug.idbSnapshot || {};
167
+ const files = Object.keys(snap);
168
+ const tokens = line.trim().split(/\s+/);
169
+ if (tokens.length <= 1 && !line.includes(' ')) {
170
+ const cmds = Object.keys(BUILTINS);
171
+ return cmds.filter(c => c.startsWith(word));
172
+ }
173
+ return files.filter(f => f.startsWith(word));
174
+ }
175
+
176
+ const rl = createReadline({
177
+ term,
178
+ getCompletions,
179
+ getPrompt: () => ctx.cwd,
180
+ onLine: line => run(line, onData).then(() => rl.showPrompt()),
181
+ });
167
182
 
168
183
  function onData(data) {
169
184
  if (data === '\x03') {
170
185
  actor.send({ type: 'ERROR' });
171
186
  inputQueue = [];
172
- buf = '';
173
187
  term.write('^C');
174
- prompt();
188
+ rl.showPrompt();
175
189
  return;
176
190
  }
177
191
  const st = actor.getSnapshot().value;
@@ -179,23 +193,13 @@ export function createShell({ term, onPreviewWrite }) {
179
193
  inputQueue.push(data);
180
194
  return;
181
195
  }
182
- if (data === '\r') {
183
- term.write('\r\n');
184
- const line = buf;
185
- buf = '';
186
- run(line, onData).then(() => prompt());
187
- } else if (data === '\x7f') {
188
- if (buf.length) { buf = buf.slice(0, -1); term.write('\x08 \x08'); }
189
- } else {
190
- buf += data;
191
- term.write(data);
192
- }
196
+ rl.onData(data);
193
197
  }
194
198
 
195
199
  term.onData(onData);
196
200
  onPreviewWrite && (window.__debug.shell.onPreviewWrite = onPreviewWrite);
197
201
  const runPublic = line => run(line, onData);
198
202
  window.__debug.shell.run = runPublic;
199
- prompt();
203
+ rl.showPrompt();
200
204
  return { run: runPublic };
201
205
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.56",
3
+ "version": "1.2.57",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",