ccx-relay 2.5.0 → 2.8.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 +38 -14
- package/package.json +1 -1
- package/src/gemini.js +6 -1
package/bin/ccx.js
CHANGED
|
@@ -92,16 +92,30 @@ const contextLines = [];
|
|
|
92
92
|
|
|
93
93
|
function stripAnsi(s) {
|
|
94
94
|
return s
|
|
95
|
-
.replace(/\x1b\[[
|
|
96
|
-
.replace(/\x1b\][^\x07]*\x07/g, '')
|
|
95
|
+
.replace(/\x1b\[[\d;?]*[A-Za-z]/g, '') // CSI (including ?-prefixed private modes)
|
|
96
|
+
.replace(/\x1b\][^\x07]*\x07/g, '') // OSC
|
|
97
|
+
.replace(/\x1b[()#;?][A-Za-z]/g, '') // other 2-byte ESC sequences
|
|
97
98
|
.replace(/\r/g, '');
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
// Prefixes that indicate Claude Code UI output, not user content
|
|
102
|
+
const UI_PREFIXES = ['>', '◆', '◇', '│', '└', '✓', '✗', '⚠', '?', '!', '·', '▸', '▶', '↓', '↑'];
|
|
103
|
+
|
|
104
|
+
function isUiLine(t) {
|
|
105
|
+
if (UI_PREFIXES.some(p => t.startsWith(p))) return true;
|
|
106
|
+
// Claude Code spinner / status lines
|
|
107
|
+
if (/^[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/.test(t)) return true;
|
|
108
|
+
// Lines that are mostly punctuation/symbols (borders, separators)
|
|
109
|
+
if (/^[-─═╌┄]{3,}$/.test(t)) return true;
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
100
113
|
ptyProcess.onData(data => {
|
|
101
114
|
process.stdout.write(data);
|
|
102
115
|
for (const ln of stripAnsi(data).split('\n')) {
|
|
103
|
-
|
|
104
|
-
|
|
116
|
+
const t = ln.trim();
|
|
117
|
+
if (t.length > 12 && !isUiLine(t)) {
|
|
118
|
+
contextLines.push(t);
|
|
105
119
|
if (contextLines.length > CONTEXT_MAX) contextLines.shift();
|
|
106
120
|
}
|
|
107
121
|
}
|
|
@@ -111,15 +125,25 @@ process.stdout.on('resize', () => {
|
|
|
111
125
|
ptyProcess.resize(process.stdout.columns, process.stdout.rows);
|
|
112
126
|
});
|
|
113
127
|
|
|
114
|
-
// ── Erase current input
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
// For multi-line
|
|
118
|
-
function eraseInput(line) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
// ── Erase current input ───────────────────────────────────────────────────────
|
|
129
|
+
// Pure backspace approach — avoids Ctrl+U (\x15) which toggles Claude Code's
|
|
130
|
+
// manual mode, and Ctrl+D (\x04) which sends EOF on empty lines.
|
|
131
|
+
// For multi-line (Shift+Enter blocks): ANSI line-clear sequence per line.
|
|
132
|
+
function eraseInput(line, cursor) {
|
|
133
|
+
const parts = line.split('\n');
|
|
134
|
+
if (parts.length > 1) {
|
|
135
|
+
// Move to end of last line first, then clear upward
|
|
136
|
+
const lastLineLen = parts[parts.length - 1].length;
|
|
137
|
+
const cursorOnLastLine = cursor - (line.length - lastLineLen);
|
|
138
|
+
const afterOnLast = lastLineLen - Math.max(0, cursorOnLastLine);
|
|
139
|
+
if (afterOnLast > 0) ptyProcess.write(`\x1b[${afterOnLast}C`);
|
|
140
|
+
ptyProcess.write('\x1b[2K');
|
|
141
|
+
for (let i = 1; i < parts.length; i++) ptyProcess.write('\x1b[1A\x1b[2K');
|
|
142
|
+
ptyProcess.write('\x1b[G');
|
|
143
|
+
} else {
|
|
144
|
+
const after = line.length - cursor;
|
|
145
|
+
if (after > 0) ptyProcess.write(`\x1b[${after}C`);
|
|
146
|
+
ptyProcess.write(Buffer.alloc(line.length, 0x7f));
|
|
123
147
|
}
|
|
124
148
|
}
|
|
125
149
|
|
|
@@ -150,7 +174,7 @@ const handler = createInputHandler({
|
|
|
150
174
|
else if (err instanceof TimeoutError) msg = `Timed out after ${config.timeoutSeconds}s — original restored`;
|
|
151
175
|
else if (err instanceof NetworkError) msg = 'No connection — original restored';
|
|
152
176
|
await spinnerStop('error', msg);
|
|
153
|
-
eraseInput(line);
|
|
177
|
+
eraseInput(line, cursor);
|
|
154
178
|
ptyProcess.write(toSend);
|
|
155
179
|
handler.setBusy(false);
|
|
156
180
|
handler.setLine(toSend);
|
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.8.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": {
|
package/src/gemini.js
CHANGED
|
@@ -7,10 +7,15 @@ export class NetworkError extends Error {}
|
|
|
7
7
|
const SYSTEM_PROMPT =
|
|
8
8
|
'Rewrite the following text to be grammatically correct and clearer, ' +
|
|
9
9
|
'while preserving its original intent and meaning exactly. ' +
|
|
10
|
-
'This is text typed into a terminal prompt
|
|
10
|
+
'This is text typed into a terminal prompt intended for an AI coding assistant — ' +
|
|
11
|
+
'preserve all technical terms, file paths, command names, code snippets, and ' +
|
|
12
|
+
'programming identifiers exactly as written. ' +
|
|
13
|
+
'It may be one line or multiple lines. ' +
|
|
11
14
|
'Respond with EXACTLY ONE rewritten version preserving the same line structure and count. ' +
|
|
12
15
|
'Do not offer multiple options or alternatives. Do not add headings, bullet ' +
|
|
13
16
|
'points, markdown formatting, quotes, explanations, or any commentary. ' +
|
|
17
|
+
'If recent terminal context is provided before the text, use it only to clarify ' +
|
|
18
|
+
'abbreviations or references — do not include context content in the output. ' +
|
|
14
19
|
'Output must contain nothing but the rewritten text itself.';
|
|
15
20
|
|
|
16
21
|
function isGroq(apiKey) { return apiKey.startsWith('gsk_'); }
|