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 CHANGED
@@ -92,16 +92,30 @@ const contextLines = [];
92
92
 
93
93
  function stripAnsi(s) {
94
94
  return s
95
- .replace(/\x1b\[[0-9;]*[A-Za-z]/g, '')
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
- if (ln.trim()) {
104
- contextLines.push(ln.trim());
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 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
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.5.0",
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 it may be one line or multiple lines. ' +
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_'); }