ccx-relay 2.3.0 → 2.5.0
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/bin/ccx.js +60 -57
- package/package.json +1 -1
package/bin/ccx.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import { load } from '../src/config.js';
|
|
5
5
|
import { enhance, RateLimitError, AuthError, ModelError, TimeoutError, NetworkError } from '../src/gemini.js';
|
|
6
|
-
import { start as spinnerStart, stop as spinnerStop } from '../src/ui.js';
|
|
7
|
-
import {
|
|
6
|
+
import { start as spinnerStart, stop as spinnerStop, clear as spinnerClear } from '../src/ui.js';
|
|
7
|
+
import { createInputHandler } from '../src/input.js';
|
|
8
8
|
|
|
9
9
|
const argv = process.argv.slice(2);
|
|
10
10
|
|
|
@@ -42,8 +42,8 @@ if (argv[0] === 'completion') {
|
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
44
|
process.stdout.write(scripts[shell] + '\n');
|
|
45
|
-
if (shell === 'bash')
|
|
46
|
-
else if (shell === 'zsh')
|
|
45
|
+
if (shell === 'bash') process.stdout.write('# eval "$(ccx completion bash)"\n');
|
|
46
|
+
else if (shell === 'zsh') process.stdout.write('# eval "$(ccx completion zsh)"\n');
|
|
47
47
|
else if (shell === 'powershell') process.stdout.write('# Invoke-Expression (ccx completion powershell)\n');
|
|
48
48
|
process.exit(0);
|
|
49
49
|
}
|
|
@@ -111,68 +111,71 @@ process.stdout.on('resize', () => {
|
|
|
111
111
|
ptyProcess.resize(process.stdout.columns, process.stdout.rows);
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
// ──
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const vk = +m[1], kd = +m[2], cs = +m[3];
|
|
124
|
-
if (vk === 77 && kd === 1 && (cs & 0x0003) && !(cs & 0x000c)) return true;
|
|
125
|
-
}
|
|
114
|
+
// ── Erase current input via readline shortcuts ────────────────────────────────
|
|
115
|
+
// Ctrl+E (go to end of line) + Ctrl+U (kill to start) = clear entire line.
|
|
116
|
+
// Works regardless of visual wrapping — Claude Code's own readline handles it.
|
|
117
|
+
// For multi-line: Backspace through the join, then kill each previous line.
|
|
118
|
+
function eraseInput(line) {
|
|
119
|
+
const lineCount = line.split('\n').length;
|
|
120
|
+
ptyProcess.write('\x05\x15'); // Ctrl+E + Ctrl+U: clear last (or only) line
|
|
121
|
+
for (let i = 1; i < lineCount; i++) {
|
|
122
|
+
ptyProcess.write('\x7f\x05\x15'); // Backspace join + Ctrl+E + Ctrl+U
|
|
126
123
|
}
|
|
127
|
-
return false;
|
|
128
124
|
}
|
|
129
125
|
|
|
130
|
-
// ──
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
let
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
126
|
+
// ── Input handler ──────────────────────────────────────────────────────────────
|
|
127
|
+
process.stdin.setRawMode(true);
|
|
128
|
+
process.stdin.resume();
|
|
129
|
+
|
|
130
|
+
const handler = createInputHandler({
|
|
131
|
+
marker: config.marker,
|
|
132
|
+
|
|
133
|
+
onEnhance: async (line, cursor) => {
|
|
134
|
+
const toSend = line.endsWith(config.marker)
|
|
135
|
+
? line.slice(0, -config.marker.length)
|
|
136
|
+
: line;
|
|
137
|
+
|
|
138
|
+
handler.setBusy(true);
|
|
139
|
+
spinnerStart();
|
|
140
|
+
|
|
141
|
+
let improved;
|
|
142
|
+
try {
|
|
143
|
+
const context = contextLines.length > 0 ? contextLines.join('\n') : null;
|
|
144
|
+
improved = await enhance(toSend, config, context);
|
|
145
|
+
} catch (err) {
|
|
146
|
+
let msg = 'Enhancement failed';
|
|
147
|
+
if (err instanceof RateLimitError) msg = 'Quota exceeded';
|
|
148
|
+
else if (err instanceof AuthError) msg = 'Invalid key — run ccx-init';
|
|
149
|
+
else if (err instanceof ModelError) msg = 'Model not found — run ccx-init';
|
|
150
|
+
else if (err instanceof TimeoutError) msg = `Timed out after ${config.timeoutSeconds}s — original restored`;
|
|
151
|
+
else if (err instanceof NetworkError) msg = 'No connection — original restored';
|
|
152
|
+
await spinnerStop('error', msg);
|
|
153
|
+
eraseInput(line);
|
|
154
|
+
ptyProcess.write(toSend);
|
|
155
|
+
handler.setBusy(false);
|
|
156
|
+
handler.setLine(toSend);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
155
159
|
|
|
156
|
-
|
|
160
|
+
await spinnerStop('success', 'Enhanced');
|
|
161
|
+
eraseInput(line, cursor);
|
|
162
|
+
ptyProcess.write(improved);
|
|
163
|
+
handler.setBusy(false);
|
|
164
|
+
handler.setLine(improved);
|
|
165
|
+
},
|
|
157
166
|
|
|
158
|
-
|
|
159
|
-
ptyProcess.write('\x15'); // Ctrl+U = kill line (clears Claude Code's current input)
|
|
160
|
-
// Small pause so Claude Code processes the Ctrl+U before we type the new text
|
|
161
|
-
await new Promise(r => setTimeout(r, 30));
|
|
162
|
-
ptyProcess.write(improved);
|
|
163
|
-
busy = false;
|
|
164
|
-
}
|
|
167
|
+
onSubmit: () => {},
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
process.stdin.setRawMode(true);
|
|
168
|
-
process.stdin.resume();
|
|
169
|
+
onPassthrough: (chunk) => ptyProcess.write(chunk),
|
|
169
170
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
onCtrlC: () => {
|
|
172
|
+
spinnerClear();
|
|
173
|
+
handler.reset();
|
|
174
|
+
},
|
|
174
175
|
});
|
|
175
176
|
|
|
177
|
+
process.stdin.on('data', chunk => handler.processChunk(chunk));
|
|
178
|
+
|
|
176
179
|
// ── Cleanup ────────────────────────────────────────────────────────────────────
|
|
177
180
|
function cleanupAndExit(code) {
|
|
178
181
|
try { if (process.stdin.isTTY) process.stdin.setRawMode(false); } catch (_) {}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "ccx-relay",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"description": "Transparent PTY wrapper for Claude Code (or any CLI) that lets you refine your prompt with Gemini AI before submitting it, without leaving your terminal session.",
|
|
6
6
|
"main": "bin/ccx.js",
|
|
7
7
|
"bin": {
|