copilot-tracer 1.0.4 → 1.0.6

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/README.md CHANGED
@@ -1,246 +1,217 @@
1
1
  # copilot-tracer
2
2
 
3
- Real-time tracing and prompt-refinement tool for **GitHub Copilot CLI** and **VS Code Copilot extension**.
3
+ Real-time tracing and prompt-refinement companion for **GitHub Copilot CLI**, **Claude Code**, and **VS Code Copilot extension**.
4
4
 
5
- Captures every prompt, response, token usage, AI credits, tool calls, skill invocations, and duration — all in one place. It also includes a prompt optimizer that rewrites prompts using role grounding, imperative clarity, output formatting, constraint injection, and noise removal. Works via native **OpenTelemetry (OTLP)** integration built into GitHub Copilot, with an optional ACP proxy mode for live CLI tracing.
5
+ Captures every prompt, response, token usage, AI credits, tool calls, and duration — all in one place. Runs as a background daemon that collects data from all your projects automatically. Includes a web dashboard with project overview and per-project live tracing.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Features
10
10
 
11
- - **Zero-intrusion capture** — uses Copilot's built-in OTel support. Set 2 env vars, done.
12
- - **Works everywhere** — captures both Copilot CLI (`copilot -p "..."`) and VS Code Copilot Chat
13
- - **Prompt refinement** — rewrites prompts with stronger instructions, clearer outputs, and less noise
14
- - **Real-time web UI** — live dashboard at `http://localhost:4747` with dark theme
15
- - **Full prompt & response** see exactly what you sent and what Copilot replied
16
- - **Token breakdown** — input, output, cached, reasoning, written tokens per request
11
+ - **Daemon mode** — install once, run forever. Collects traces from all projects automatically
12
+ - **Auto project detection** — detects project from `github.copilot.git.repository` in OTLP spans
13
+ - **Zero-intrusion capture** — uses Copilot's built-in OTel support. Set env vars, done.
14
+ - **Works everywhere** — captures GitHub Copilot CLI, Claude Code, and VS Code Copilot Chat
15
+ - **Dashboard** overview of all projects with token usage, credits, and session counts
16
+ - **Live tracer** — real-time trace table per project with detail panel
17
+ - **Prompt refinement** — rewrites prompts with stronger instructions and less noise
17
18
  - **AI Credits tracking** — matches exactly what Copilot terminal reports (e.g. `2.59 cr`)
18
- - **Tool call visibility** — see every tool/skill/MCP invoked during a session
19
- - **ACP proxy mode** — wrap the Copilot CLI to trace live JSON-RPC traffic when needed
20
19
  - **Persistent storage** — SQLite at `~/.copilot-tracer/traces.db`, survives restarts
21
- - **Console + Web UI** — CLI table view or browser dashboard, your choice
22
20
 
23
21
  ---
24
22
 
25
- ## Prerequisites
26
-
27
- - Node.js v18+ (tested on v24)
28
- - GitHub Copilot CLI (`copilot` command available in terminal)
29
- - VS Code 1.99+ with built-in Copilot (no extension install needed)
30
-
31
- ---
32
-
33
- ## Install
23
+ ## Quick Start (one-time setup)
34
24
 
35
25
  ```bash
36
26
  npm install -g copilot-tracer
27
+ copilot-tracer --setup --daemon
37
28
  ```
38
29
 
39
- Requires Node.js v18+. The `copilot-tracer` command is available globally after install.
40
-
41
- > **Building from source**
42
- > ```bash
43
- > git clone https://github.com/chuongnd/copilot-tracer
44
- > cd copilot-tracer && npm install && npx tsc
45
- > npm link # registers global command from local build
46
- > ```
30
+ This will:
31
+ 1. Detect your Copilot CLI and VS Code installation
32
+ 2. Patch `~/.zshrc` with OTEL env vars
33
+ 3. Patch VS Code `settings.json` with terminal env vars
34
+ 4. Enable Claude Code OTLP logs/events and enhanced beta traces
35
+ 5. Start the daemon on port 4747
47
36
 
48
- ---
49
-
50
- ## Setup (one-time)
51
-
52
- Run the auto-setup command. It detects your Copilot CLI and VS Code installation and injects the required config automatically:
37
+ Then apply env vars in your current shell:
53
38
 
54
39
  ```bash
55
- copilot-tracer --setup
40
+ source ~/.zshrc
56
41
  ```
57
42
 
58
- What it does:
59
- - Detects `copilot` CLI path and version
60
- - Detects VS Code version and confirms built-in Copilot
61
- - Patches `~/.zshrc` (or `~/.bashrc`) with OTEL env vars
62
- - Patches VS Code `settings.json` with `terminal.integrated.env.osx` block
43
+ Restart VS Code once. After that, the daemon collects traces from all your Copilot
44
+ and Claude Code sessions automatically. Claude Code content flags are enabled by
45
+ setup so prompts and responses can be displayed in the local dashboard.
63
46
 
64
- Example output:
65
- ```
66
- ✅ GitHub Copilot CLI detected — /usr/bin/copilot v1.0.77
67
- ✅ Visual Studio Code detected — 1.131.0 (Built-in Copilot)
68
- ✅ Shell profile patched: .zshrc
69
- ✅ VS Code settings patched
70
- ```
47
+ Open **http://localhost:4747** to see the dashboard.
71
48
 
72
- Then apply the env vars:
49
+ ---
73
50
 
74
- ```bash
75
- source ~/.zshrc
76
- ```
51
+ ## How It Works
77
52
 
78
- Restart VS Code completely (Cmd+Q, then reopen).
53
+ ```
54
+ ┌─────────────────────────────────────────────────────────┐
55
+ │ copilot-tracer --daemon (runs once, stays running) │
56
+ │ │
57
+ │ OTLP Receiver ← Copilot CLI + Claude Code + VS Code │
58
+ │ (auto-detects project from github.copilot.git.repository)│
59
+ │ │
60
+ │ SQLite DB → Dashboard + Live Tracer (Socket.io) │
61
+ └─────────────────────────────────────────────────────────┘
62
+
63
+ Copilot CLI / Claude Code / VS Code Copilot Chat
64
+
65
+ │ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4747
66
+
67
+ POST /v1/traces and /v1/logs (OpenTelemetry OTLP JSON)
68
+
69
+
70
+ copilot-tracer parses spans and events → tokens, credits, tool calls
71
+
72
+
73
+ SQLite DB (~/.copilot-tracer/traces.db)
74
+
75
+ ├→ Dashboard: all projects overview
76
+ └→ Live Tracer: real-time per-project view
77
+ ```
79
78
 
80
79
  ---
81
80
 
82
- ## Manual Setup (alternative)
81
+ ## Usage
83
82
 
84
- If you prefer to configure manually, add these to `~/.zshrc`:
83
+ ### Daemon mode (recommended)
85
84
 
86
85
  ```bash
87
- export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4747
88
- export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
89
- export COPILOT_OTEL_ENABLED=true
90
- ```
86
+ # First time: setup + start daemon
87
+ copilot-tracer --setup --daemon
91
88
 
92
- For VS Code, add to `~/Library/Application Support/Code/User/settings.json`:
89
+ # Subsequent starts
90
+ copilot-tracer --daemon
93
91
 
94
- ```json
95
- "terminal.integrated.env.osx": {
96
- "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4747",
97
- "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true",
98
- "COPILOT_OTEL_ENABLED": "true"
99
- }
92
+ # Custom port
93
+ copilot-tracer --daemon --port 8080
100
94
  ```
101
95
 
102
- ---
96
+ ### Normal mode (legacy)
103
97
 
104
- ## Start Tracer
98
+ Per-session mode with optional ACP proxy for live CLI tracing:
105
99
 
106
100
  ```bash
107
- copilot-tracer --ui web --port 4747 --no-proxy
108
- ```
101
+ # Web UI only (read from DB)
102
+ copilot-tracer --ui web --no-proxy
109
103
 
110
- Open **http://localhost:4747** shows "waiting for copilot CLI activity".
111
-
112
- ---
104
+ # With project path
105
+ copilot-tracer --ui web --no-proxy --project-path /path/to/repo
113
106
 
114
- ## Use Copilot Normally
107
+ # With ACP proxy (wraps copilot CLI)
108
+ copilot-tracer --ui web
109
+ ```
115
110
 
116
- No change to how you use Copilot. Just run as usual:
111
+ ### Setup only
117
112
 
118
113
  ```bash
119
- # Copilot CLI
120
- copilot -p "how to convert microservice to modular" --allow-all-tools
121
-
122
- # Or use Copilot Chat in VS Code
114
+ # Just patch env vars without starting
115
+ copilot-tracer --setup
123
116
  ```
124
117
 
125
- Traces appear instantly in the web UI as each request completes.
126
-
127
118
  ---
128
119
 
129
- ## How It Works
120
+ ## Dashboard
130
121
 
131
- ```
132
- copilot CLI / VS Code Copilot Chat
133
- |
134
- | reads OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4747
135
- |
136
- ↓ POST /v1/traces (OpenTelemetry OTLP JSON)
137
- copilot-tracer OTLP receiver
138
- |
139
-
140
- Parse spans → extract prompt, response, tokens, credits, tool calls
141
- |
142
-
143
- SQLite DB (~/.copilot-tracer/traces.db)
144
- |
145
-
146
- Web UI (Socket.io real-time) + Console table
147
- ```
122
+ Open http://localhost:4747 after starting the daemon.
148
123
 
149
- Copilot has built-in OpenTelemetry instrumentation. When `OTEL_EXPORTER_OTLP_ENDPOINT` is set, it pushes all trace data to that endpoint automatically — both CLI and VS Code extension.
124
+ - **Summary cards** total projects, sessions, tokens, credits
125
+ - **Project cards** — each project shows path, session count, tokens, credits, last active
126
+ - **Click a project** → opens live tracer filtered to that project
127
+ <img width="737" height="410" alt="image" src="https://github.com/user-attachments/assets/dc20653b-8774-46b3-9a42-6e7bb934aded" />
150
128
 
151
129
  ---
152
130
 
153
- ## Web UI
131
+ ## Live Tracer
154
132
 
155
- Open http://localhost:4747 after starting the tracer.
133
+ Real-time trace table for a specific project.
156
134
 
157
135
  **Table columns:**
158
136
  | Date/Time | Prompt | AI Credits | Duration | Cached | Written | Reasoning | Skills | Agents | MCPs |
159
137
 
160
138
  **Interactive features:**
161
- - Click any row → detail panel: full prompt, full response, reasoning text, call graph
139
+ - Click any row → detail panel: full prompt, response, reasoning, call graph
162
140
  - Click AI Credits → cost breakdown per token type
163
- - Click Reasoning count → full reasoning text
164
- - Click Skills / Agents / MCPs pill → filtered call list with input/output/duration
165
- - Real-time updates via Socket.io — no page refresh needed
166
- - Dark theme
141
+ - Click Reasoning → full reasoning text
142
+ - Click Skills / Agents / MCPs → filtered call list
143
+ - Real-time updates via Socket.io
144
+
145
+ <img width="1504" height="742" alt="image" src="https://github.com/user-attachments/assets/f334108f-c587-4228-8120-7dac2f85f90b" />
146
+
147
+ <img width="1061" height="696" alt="image" src="https://github.com/user-attachments/assets/1555d5a2-dbca-4f1d-b102-3d2865dcee68" />
167
148
 
168
149
  ---
169
150
 
170
- ## Console UI
151
+ ## Prompt Refinement
171
152
 
172
- ```bash
173
- copilot-tracer --ui console --no-proxy
174
- ```
153
+ The web UI includes a prompt optimizer. Click "Refine Prompt" in the trace detail panel.
175
154
 
176
- Live updating table in terminal. Same columns as web UI. TOTALS row pinned at top.
155
+ Techniques applied:
156
+ - Role grounding, imperative clarity, output format, chain-of-thought
157
+ - Noise removal, constraint injection, redundancy cleanup
177
158
 
178
159
  ---
179
160
 
180
- ## Prompt Refinement
161
+ ## CLI Flags
181
162
 
182
- The web UI exposes `POST /api/refine` to improve a raw prompt before sending it to Copilot.
163
+ | Flag | Description |
164
+ |------|-------------|
165
+ | `--daemon` | Run as background daemon (always-on OTLP receiver) |
166
+ | `--setup` | Auto-detect and configure env vars |
167
+ | `--port <port>` | Web UI port (default: 4747) |
168
+ | `--ui <mode>` | UI mode: console \| web \| both (normal mode only) |
169
+ | `--no-proxy` | Web/console only, no ACP proxy (normal mode only) |
170
+ | `--project-path <path>` | Project source path (normal mode only) |
171
+ | `--session <id>` | Custom session ID (normal mode only) |
172
+ | `--debug` | Verbose logging |
183
173
 
184
- It currently applies these techniques:
174
+ ---
185
175
 
186
- - **Role grounding** — adds an expert persona when the prompt has none
187
- - **Imperative clarity** — turns soft phrasing into direct instructions
188
- - **Output format** — asks for JSON, markdown, bullets, or steps when missing
189
- - **Reasoning guidance** — adds step-by-step thinking for multi-step tasks
190
- - **Noise removal** — strips filler like “please”, “could you”, and “I think”
191
- - **Constraint injection** — adds language, tone, length, and audience constraints
192
- - **Redundancy cleanup** — removes repeated or conflicting instructions
176
+ ## Manual Setup (alternative)
193
177
 
194
- Example request:
178
+ Add to `~/.zshrc`:
195
179
 
196
180
  ```bash
197
- curl -s http://localhost:4747/api/refine \
198
- -H 'content-type: application/json' \
199
- -d '{"prompt":"help me write a plan for migrating a monolith"}'
181
+ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4747
182
+ export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
183
+ export COPILOT_OTEL_ENABLED=true
184
+
185
+ # Claude Code telemetry
186
+ export CLAUDE_CODE_ENABLE_TELEMETRY=1
187
+ export CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
188
+ export OTEL_LOGS_EXPORTER=otlp
189
+ export OTEL_TRACES_EXPORTER=otlp
190
+ export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
191
+ export OTEL_LOG_USER_PROMPTS=1
192
+ export OTEL_LOG_ASSISTANT_RESPONSES=1
200
193
  ```
201
194
 
202
- Response shape:
195
+ Claude Code exports standard OTLP logs/events and optional beta traces. See
196
+ [Anthropic's monitoring documentation](https://code.claude.com/docs/en/monitoring-usage)
197
+ for protocol and content controls.
198
+
199
+ For VS Code, add to `~/Library/Application Support/Code/User/settings.json`:
203
200
 
204
201
  ```json
205
- {
206
- "ok": true,
207
- "optimized": "",
208
- "issues": [],
209
- "techniques": [],
210
- "origTok": 10,
211
- "newTok": 22,
212
- "inputSavingPct": 0
202
+ "terminal.integrated.env.osx": {
203
+ "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4747",
204
+ "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT": "true",
205
+ "COPILOT_OTEL_ENABLED": "true"
213
206
  }
214
207
  ```
215
208
 
216
209
  ---
217
210
 
218
- ## CLI Flags
219
-
220
- | Flag | Description |
221
- |------|-------------|
222
- | `--ui web` | Start web UI (default) |
223
- | `--ui console` | Start console table UI |
224
- | `--ui both` | Both web + console |
225
- | `--port 4747` | Web UI port (default: 4747) |
226
- | `--no-proxy` | Web/console only, no ACP proxy |
227
- | `--session <id>` | Custom session ID |
228
- | `--setup` | Auto-detect and configure env vars |
229
- | `--debug` | Verbose logging |
230
-
231
- ---
232
-
233
211
  ## Storage
234
212
 
235
213
  Traces persist to `~/.copilot-tracer/traces.db` (SQLite). Safe to keep across sessions.
236
214
 
237
- To test the web UI without running a live Copilot session:
238
-
239
- ```bash
240
- node test-seed.mjs # seeds 4 sample traces (source build only)
241
- copilot-tracer --ui web --no-proxy --session test-session-001
242
- ```
243
-
244
215
  ---
245
216
 
246
217
  ## Build & Publish
@@ -253,18 +224,24 @@ npx tsc # compile to dist/
253
224
  To publish a new release to npm:
254
225
 
255
226
  ```bash
256
- # Bump patch version (1.0.0 → 1.0.1), build, publish, git-tag
227
+ # Login first
228
+ npm login
229
+
230
+ # Patch version (1.0.4 → 1.0.5)
257
231
  bash scripts/publish.sh
258
232
 
259
- # Bump minor version
233
+ # Minor version
260
234
  bash scripts/publish.sh minor
261
235
 
262
- # Publish a beta pre-release
236
+ # Major version
237
+ bash scripts/publish.sh major
238
+
239
+ # Beta pre-release
263
240
  bash scripts/publish.sh --tag beta --pre beta
264
241
  ```
265
242
 
266
243
  The script will:
267
- 1. Check npm authentication (`npm login` required first)
244
+ 1. Check npm authentication
268
245
  2. Verify git working tree is clean
269
246
  3. Type-check + build
270
247
  4. Verify `better-sqlite3` native module loads
package/dist/cli.js CHANGED
@@ -4,7 +4,8 @@ import { spawn } from 'child_process';
4
4
  import { randomUUID } from 'crypto';
5
5
  import readline from 'readline';
6
6
  import fs from 'fs';
7
- import { createSession, getTraces, getSessionSummary } from './db.js';
7
+ import path from 'path';
8
+ import { createSession, getTraces, getSessionSummary, ensureProject } from './db.js';
8
9
  import { handleAcpMessage, traceEvents } from './proxy.js';
9
10
  import { renderConsoleTable } from './consoleUi.js';
10
11
  import { startWebServer } from './webServer.js';
@@ -20,91 +21,109 @@ program
20
21
  .option('--session <id>', 'Filter by session ID')
21
22
  .option('--debug', 'Dump ALL raw ACP messages to stderr (use to discover real method names)')
22
23
  .option('--setup', 'Auto-detect copilot CLI + VS Code and configure OTLP env vars')
24
+ .option('--project-path <path>', 'Project source path (defaults to cwd)')
25
+ .option('--daemon', 'Run as background daemon — collects all OTLP data, no ACP proxy')
23
26
  .allowUnknownOption()
24
27
  .parse();
25
28
  const opts = program.opts();
26
29
  const port = parseInt(opts.port);
27
30
  // Handle --setup: patch env files, apply to current process, then fall through to start web UI
28
31
  if (opts.setup) {
29
- runSetup(port);
30
- // Force no-proxy web mode — user just needs to open the browser
31
- opts.proxy = false;
32
- opts.ui = 'web';
33
- }
34
- const sessionId = opts.session || randomUUID();
35
- createSession(sessionId);
36
- console.log(`\n 🤖 Copilot Tracer | Session: ${sessionId}\n`);
37
- // Start Web UI
38
- if (opts.ui === 'web' || opts.ui === 'both') {
39
- startWebServer(port, sessionId);
40
- setTimeout(() => open(`http://localhost:${port}/`), 1500);
32
+ runSetup(port, opts.daemon);
33
+ if (!opts.daemon) {
34
+ opts.proxy = false;
35
+ opts.ui = 'web';
36
+ }
41
37
  }
42
- // Start Console UI refresh loop
43
- if (opts.ui === 'console' || opts.ui === 'both') {
44
- const refreshConsole = () => {
45
- const entries = getTraces(sessionId, 50);
46
- const summary = getSessionSummary(sessionId);
47
- renderConsoleTable(entries, summary ?? undefined);
48
- };
49
- traceEvents.on('trace:update', refreshConsole);
50
- traceEvents.on('trace:done', refreshConsole);
51
- refreshConsole();
38
+ // ── Daemon mode: run as always-on OTLP receiver ───────────────────────────
39
+ if (opts.daemon) {
40
+ console.log(`\n 🤖 Copilot Tracer Daemon Mode`);
41
+ console.log(` 📡 Listening for OTLP traces on port ${port}`);
42
+ console.log(` 🌐 Dashboard: http://localhost:${port}/`);
43
+ console.log(` Press Ctrl+C to stop\n`);
44
+ startWebServer(port);
45
+ process.on('SIGINT', () => { process.exit(0); });
46
+ process.on('SIGTERM', () => { process.exit(0); });
52
47
  }
53
- // ACP Proxy — wrap copilot CLI
54
- if (opts.proxy !== false) {
55
- const copilotArgs = ['--acp', '--stdio', ...program.args];
56
- const child = spawn(opts.cmd, copilotArgs, {
57
- stdio: ['pipe', 'pipe', 'inherit'],
58
- });
59
- if (!child.pid) {
60
- console.error(`\n ❌ Failed to start: ${opts.cmd} ${copilotArgs.join(' ')}`);
61
- console.error(' Make sure GitHub Copilot CLI is installed: npm install -g @github/copilot-cli\n');
62
- process.exit(1);
48
+ else {
49
+ // ── Normal mode: per-session with optional ACP proxy ─────────────────────
50
+ const sessionId = opts.session || randomUUID();
51
+ // Resolve project path and create project record
52
+ const projectPath = opts.projectPath
53
+ ? path.resolve(opts.projectPath)
54
+ : process.cwd();
55
+ const projectId = ensureProject(projectPath);
56
+ createSession(sessionId, projectId);
57
+ console.log(`\n 🤖 Copilot Tracer | Session: ${sessionId}`);
58
+ console.log(` 📁 Project: ${projectPath}\n`);
59
+ // Start Web UI
60
+ if (opts.ui === 'web' || opts.ui === 'both') {
61
+ startWebServer(port, sessionId, projectId);
62
+ setTimeout(() => open(`http://localhost:${port}/`), 1500);
63
63
  }
64
- // Parse newline-delimited JSON (NDJSON) from copilot
65
- const stdinRl = readline.createInterface({ input: process.stdin });
66
- const stdoutRl = readline.createInterface({ input: child.stdout });
67
- const debug = opts.debug === true;
68
- const logFile = debug ? fs.createWriteStream(`/tmp/copilot-tracer-${sessionId.slice(0, 8)}.ndjson`, { flags: 'a' }) : null;
69
- function debugLog(direction, raw, parsed) {
70
- if (!debug)
71
- return;
72
- const entry = JSON.stringify({ ts: new Date().toISOString(), dir: direction, raw, parsed });
73
- process.stderr.write('[TRACER] ' + entry + '\n');
74
- logFile?.write(entry + '\n');
64
+ // Start Console UI refresh loop
65
+ if (opts.ui === 'console' || opts.ui === 'both') {
66
+ const refreshConsole = () => {
67
+ const entries = getTraces(sessionId, 50);
68
+ const summary = getSessionSummary(sessionId);
69
+ renderConsoleTable(entries, summary ?? undefined);
70
+ };
71
+ traceEvents.on('trace:update', refreshConsole);
72
+ traceEvents.on('trace:done', refreshConsole);
73
+ refreshConsole();
75
74
  }
76
- // stdin copilot (user copilot = 'out' direction from user's perspective)
77
- stdinRl.on('line', (line) => {
78
- let parsed;
79
- try {
80
- parsed = JSON.parse(line);
81
- handleAcpMessage(sessionId, parsed, 'out'); // outbound = user sending to copilot
82
- }
83
- catch (e) {
84
- // Not JSON plain text from terminal, not ACP
85
- if (debug)
86
- process.stderr.write(`[TRACER] stdin non-JSON: ${line}\n`);
75
+ // ACP Proxy wrap copilot CLI
76
+ if (opts.proxy !== false) {
77
+ const copilotArgs = ['--acp', '--stdio', ...program.args];
78
+ const child = spawn(opts.cmd, copilotArgs, {
79
+ stdio: ['pipe', 'pipe', 'inherit'],
80
+ });
81
+ if (!child.pid) {
82
+ console.error(`\n ❌ Failed to start: ${opts.cmd} ${copilotArgs.join(' ')}`);
83
+ console.error(' Make sure GitHub Copilot CLI is installed: npm install -g @github/copilot-cli\n');
84
+ process.exit(1);
87
85
  }
88
- debugLog('→ copilot', line, parsed);
89
- child.stdin.write(line + '\n');
90
- });
91
- // copilot stdout (copilot user = 'in' direction from user's perspective)
92
- stdoutRl.on('line', (line) => {
93
- let parsed;
94
- try {
95
- parsed = JSON.parse(line);
96
- handleAcpMessage(sessionId, parsed, 'in'); // inbound = copilot sending to user
86
+ const stdinRl = readline.createInterface({ input: process.stdin });
87
+ const stdoutRl = readline.createInterface({ input: child.stdout });
88
+ const debug = opts.debug === true;
89
+ const logFile = debug ? fs.createWriteStream(`/tmp/copilot-tracer-${sessionId.slice(0, 8)}.ndjson`, { flags: 'a' }) : null;
90
+ function debugLog(direction, raw, parsed) {
91
+ if (!debug)
92
+ return;
93
+ const entry = JSON.stringify({ ts: new Date().toISOString(), dir: direction, raw, parsed });
94
+ process.stderr.write('[TRACER] ' + entry + '\n');
95
+ logFile?.write(entry + '\n');
97
96
  }
98
- catch (e) {
99
- if (debug)
100
- process.stderr.write(`[TRACER] stdout non-JSON: ${line}\n`);
101
- }
102
- debugLog('← copilot', line, parsed);
103
- process.stdout.write(line + '\n');
104
- });
105
- child.on('exit', (code) => {
106
- console.log(`\n Copilot CLI exited (code ${code})\n`);
107
- process.exit(code ?? 0);
108
- });
109
- process.on('SIGINT', () => { child.kill(); process.exit(0); });
97
+ stdinRl.on('line', (line) => {
98
+ let parsed;
99
+ try {
100
+ parsed = JSON.parse(line);
101
+ handleAcpMessage(sessionId, parsed, 'out');
102
+ }
103
+ catch (e) {
104
+ if (debug)
105
+ process.stderr.write(`[TRACER] stdin non-JSON: ${line}\n`);
106
+ }
107
+ debugLog('→ copilot', line, parsed);
108
+ child.stdin.write(line + '\n');
109
+ });
110
+ stdoutRl.on('line', (line) => {
111
+ let parsed;
112
+ try {
113
+ parsed = JSON.parse(line);
114
+ handleAcpMessage(sessionId, parsed, 'in');
115
+ }
116
+ catch (e) {
117
+ if (debug)
118
+ process.stderr.write(`[TRACER] stdout non-JSON: ${line}\n`);
119
+ }
120
+ debugLog('← copilot', line, parsed);
121
+ process.stdout.write(line + '\n');
122
+ });
123
+ child.on('exit', (code) => {
124
+ console.log(`\n Copilot CLI exited (code ${code})\n`);
125
+ process.exit(code ?? 0);
126
+ });
127
+ process.on('SIGINT', () => { child.kill(); process.exit(0); });
128
+ }
110
129
  }