cc-prompter 0.1.11 → 0.2.1

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,19 +1,55 @@
1
1
  # CC Prompter
2
2
 
3
- **Shift+Alt 点击页面元素 → Claude Code 直接改代码 → 页面自动刷新**
3
+ **哪里不对点哪里,So easy!**
4
4
 
5
5
  一个 Vite 插件,将 [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) 接入你的前端开发工作流。通过可视化元素定位 + 常驻 PTY 会话,实现「点击 → 描述 → 代码自动修改 → HMR 刷新」的闭环。
6
6
 
7
+ > 🎯 **精确定位修改点** — 不再需要费劲描述「左上角那个蓝色按钮」或者截图了,直接点击页面元素,源码路径、行列号自动带上。
8
+ >
9
+ > 💰 **节省 tokens 神器** — 不需要让 Claude 反复探索项目结构、搜索文件、猜测位置,一个点击搞定所有上下文。
10
+ >
11
+ > ⚡ **所见即所得** — 改完自动 HMR 刷新,边看边改,开发体验拉满。
12
+
13
+ **Shift+Alt 点击页面元素 → Claude Code 直接改代码 → 页面自动刷新**
14
+
7
15
  ![CC Prompter Demo](./demo.png)
8
16
 
9
17
  ## 快速开始
10
18
 
11
19
  ### 安装
12
20
 
21
+ #### macOS / Linux
22
+
23
+ ```bash
24
+ npm install cc-prompter
25
+ ```
26
+
27
+ macOS 和 Linux **开箱即用**,PTY 模块(`node-pty-prebuilt-multiarch`)会自动安装。
28
+
29
+ #### Windows
30
+
31
+ Windows 需要额外安装 PTY 原生模块(npm 上的 PTY 包不含 Windows 预编译):
32
+
13
33
  ```bash
34
+ # 1. 安装 cc-prompter
14
35
  npm install cc-prompter
36
+
37
+ # 2. 安装 PTY 模块(跳过自动编译,只装 JS 部分)
38
+ cd node_modules/cc-prompter
39
+ npm install @homebridge/node-pty-prebuilt-multiarch --ignore-scripts
40
+
41
+ # 3. 下载 Windows 原生二进制
42
+ # 从 GitHub Releases 下载对应版本:
43
+ # https://github.com/nicabar/node-pty-prebuilt-multiarch/releases
44
+ # 找到 win32-x64 的 prebuild .tar.gz 文件,下载并解压到:
45
+ # node_modules/@homebridge/node-pty-prebuilt-multiarch/build/Release/
15
46
  ```
16
47
 
48
+ > **如果 GitHub 下载超时**(国内网络),可以:
49
+ > - 使用 GitHub 镜像下载:`https://ghfast.top/https://github.com/nicabar/node-pty-prebuilt-multiarch/releases/download/...`
50
+ > - 或者在有网络的环境下载后拷贝过去
51
+ > - Node 18 / 20 / 22 均支持
52
+
17
53
  > 前提:本地需要已安装 [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)(终端执行 `claude` 命令可用)。插件仅在 dev 模式生效,`npm run build` 不会注入任何代码。
18
54
 
19
55
  ### 配置
@@ -43,7 +79,18 @@ export default defineConfig({
43
79
  复制下面这行,粘贴给 Claude Code,它会自动帮你完成安装和配置:
44
80
 
45
81
  ```
46
- 帮我安装 cc-prompter 并在 vite.config.ts 中添加 ccPromptPlugin() 插件配置,这是一个 Vite 插件,用于集成 Claude Code CLI 和 code-inspector 元素定位功能
82
+ 帮我安装 cc-prompter 并在 vite.config.ts 中添加 ccPromptPlugin() 插件配置,这是一个 Vite 插件,用于集成 Claude Code CLI 和 code-inspector 元素定位功能。安装方法:
83
+ - macOS / Linux: 直接 npm install cc-prompter 即可
84
+ - Windows: npm install cc-prompter 后,还需要安装 PTY 原生模块:
85
+ 1. cd node_modules/cc-prompter
86
+ 2. npm install @homebridge/node-pty-prebuilt-multiarch --ignore-scripts
87
+ 3. 从 https://github.com/nicabar/node-pty-prebuilt-multiarch/releases 下载 win32-x64 的 prebuild 压缩包
88
+ 4. 解压到 node_modules/@homebridge/node-pty-prebuilt-multiarch/build/Release/
89
+ 如果 GitHub 下载超时,使用 ghfast.top 镜像。
90
+
91
+ 配置方法:在 vite.config.ts 的 plugins 数组中添加 ccPromptPlugin(),示例:
92
+ import { ccPromptPlugin } from 'cc-prompter';
93
+ plugins: [ccPromptPlugin(), react()]
47
94
  ```
48
95
 
49
96
  ## 怎么用
package/dist/index.cjs CHANGED
@@ -115,25 +115,41 @@ function cwdToProjectDir(cwd) {
115
115
  }
116
116
  function findRecentJsonl(cwd, afterMs) {
117
117
  const projectsDir = findClaudeProjectsDir();
118
+ if (!fs.existsSync(projectsDir)) return null;
119
+ const candidates = [];
120
+ const collectFromDir = (dir) => {
121
+ try {
122
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
123
+ for (const f of files) {
124
+ const fp = path.join(dir, f);
125
+ try {
126
+ const stat = fs.statSync(fp);
127
+ if (stat.mtimeMs > afterMs) {
128
+ candidates.push({ path: fp, mtime: stat.mtimeMs });
129
+ }
130
+ } catch {
131
+ }
132
+ }
133
+ } catch {
134
+ }
135
+ };
118
136
  const projectSubdir = cwdToProjectDir(cwd);
119
137
  const targetDir = path.join(projectsDir, projectSubdir);
120
- if (!fs.existsSync(targetDir)) return null;
121
- const files = fs.readdirSync(targetDir).filter((f) => f.endsWith(".jsonl"));
122
- let best = null;
123
- for (const f of files) {
124
- const fp = path.join(targetDir, f);
138
+ collectFromDir(targetDir);
139
+ if (candidates.length === 0) {
125
140
  try {
126
- const stat = fs.statSync(fp);
127
- if (stat.mtimeMs > afterMs) {
128
- if (!best || stat.mtimeMs > best.mtime) {
129
- best = { path: fp, mtime: stat.mtimeMs };
141
+ const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ if (entry.isDirectory() && entry.name !== projectSubdir) {
144
+ collectFromDir(path.join(projectsDir, entry.name));
130
145
  }
131
146
  }
132
147
  } catch {
133
- continue;
134
148
  }
135
149
  }
136
- return best?.path || null;
150
+ if (candidates.length === 0) return null;
151
+ candidates.sort((a, b) => b.mtime - a.mtime);
152
+ return candidates[0].path;
137
153
  }
138
154
  function sessionIdFromJsonlPath(jsonPath) {
139
155
  const base = path.basename(jsonPath, ".jsonl");
@@ -143,6 +159,9 @@ function sessionIdFromJsonlPath(jsonPath) {
143
159
  function stripAnsi(s) {
144
160
  return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\].*?(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[\?]?[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\[[0-9;]*m/g, "").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b[^[\]()]?.?/g, "").replace(/\r/g, "");
145
161
  }
162
+ function stripSpinner(s) {
163
+ return s.replace(/[✻✶✽✢·●✳◇◆▸▹⏵⏶]+/g, "").replace(/\w{3,}ing[…\s]*\(\d{1,3}s?\)?/g, "").replace(/\w{3,}ing…/g, "").replace(/\s{2,}/g, " ").trim();
164
+ }
146
165
  var PtySession = class extends import_events.EventEmitter {
147
166
  id;
148
167
  cwd;
@@ -179,6 +198,12 @@ var PtySession = class extends import_events.EventEmitter {
179
198
  // last emitted progress text
180
199
  interrupted = false;
181
200
  // set when user sends interrupt
201
+ lastSpinnerSec = 0;
202
+ // highest spinner seconds counter seen
203
+ lastSpinnerAt = 0;
204
+ // timestamp when spinner was last active
205
+ emittedTools = /* @__PURE__ */ new Set();
206
+ // dedup tool call emissions
182
207
  constructor(id, cwd) {
183
208
  super();
184
209
  this.id = id;
@@ -292,6 +317,9 @@ var PtySession = class extends import_events.EventEmitter {
292
317
  this.ptyDoneEmitted = false;
293
318
  this.lastProgress = "";
294
319
  this.interrupted = false;
320
+ this.lastSpinnerSec = 0;
321
+ this.lastSpinnerAt = 0;
322
+ this.emittedTools.clear();
295
323
  if (!this.messageSentAt) {
296
324
  this.messageSentAt = Date.now();
297
325
  console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);
@@ -353,14 +381,24 @@ var PtySession = class extends import_events.EventEmitter {
353
381
  * - Timing: (Xs · ↓NNN tokens)
354
382
  */
355
383
  parseBusyOutput() {
384
+ if (this.busyBuffer.length > 3e3) {
385
+ this.busyBuffer = this.busyBuffer.slice(-2e3);
386
+ }
356
387
  this.emitProgress();
388
+ const secMatches = [...this.busyBuffer.matchAll(/\w{3,}ing[…\s]*\((\d{1,3})s?/g)];
389
+ for (const m of secMatches) {
390
+ const sec = parseInt(m[1]);
391
+ if (sec > this.lastSpinnerSec) {
392
+ this.lastSpinnerSec = sec;
393
+ this.lastSpinnerAt = Date.now();
394
+ }
395
+ }
357
396
  const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);
358
397
  if (respMatch) {
359
398
  let raw = respMatch[1];
360
399
  raw = raw.replace(/[✳✶✻✽✢·].*$/s, "").trim();
361
400
  raw = raw.replace(/─{3,}.*$/s, "").trim();
362
- raw = raw.replace(/Brewed for.*$/s, "").trim();
363
- raw = raw.replace(/Sautéed for.*$/s, "").trim();
401
+ raw = raw.replace(/\w{3,}ed (?:for|in) \d{1,4}s?.*$/s, "").trim();
364
402
  raw = raw.replace(/esctointerrupt.*$/s, "").trim();
365
403
  if (raw.length > this.ptyResponseText.length) {
366
404
  this.ptyResponseText = raw;
@@ -369,14 +407,34 @@ var PtySession = class extends import_events.EventEmitter {
369
407
  }
370
408
  const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\(([^)]+)\)/);
371
409
  if (toolCallMatch) {
372
- const toolName = toolCallMatch[1];
373
- const filePath = toolCallMatch[2];
374
- this.emit("message", {
375
- type: "assistant_tool",
376
- tool: { name: toolName, input: { file: filePath } }
377
- });
410
+ const sig = `${toolCallMatch[1]}:${toolCallMatch[2]}`;
411
+ if (!this.emittedTools.has(sig)) {
412
+ this.emittedTools.add(sig);
413
+ this.emit("message", {
414
+ type: "assistant_tool",
415
+ tool: { name: toolCallMatch[1], input: { file: toolCallMatch[2] } }
416
+ });
417
+ }
418
+ }
419
+ const winToolMatch = this.busyBuffer.match(/● (Reading|Editing|Writing|Searching|Bashing)[^\n]*\n\s*⎿\s*(.+)/);
420
+ if (!toolCallMatch && winToolMatch) {
421
+ const filePath = winToolMatch[2].trim();
422
+ const sig = `${winToolMatch[1]}:${filePath}`;
423
+ if (!this.emittedTools.has(sig)) {
424
+ this.emittedTools.add(sig);
425
+ this.emit("message", {
426
+ type: "assistant_tool",
427
+ tool: { name: winToolMatch[1], input: { file: filePath } }
428
+ });
429
+ }
378
430
  }
379
- if (!this.ptyDoneEmitted && /(?:Brewed|Sautéed) for/.test(this.busyBuffer)) {
431
+ if (this.ptyDoneEmitted) return;
432
+ const cleanBuf = stripSpinner(this.busyBuffer);
433
+ const hasDoneMarker = /\b\w+ed (?:for|in) \d{1,4}s?\b/i.test(cleanBuf);
434
+ const spinnerTimeout = this.lastSpinnerAt > 0 && Date.now() - this.lastSpinnerAt > 1e4 && this.ptyResponseEmitted > 0;
435
+ if (hasDoneMarker || spinnerTimeout) {
436
+ const reason = hasDoneMarker ? "completion marker" : `spinner timeout (${Math.round((Date.now() - this.lastSpinnerAt) / 1e3)}s since last activity)`;
437
+ console.log(`[pty-session ${this.id}] detected done via ${reason}`);
380
438
  this.ptyDoneEmitted = true;
381
439
  if (this.ptyResponseText.length > this.ptyResponseEmitted) {
382
440
  this.emitIncrementalText();
@@ -384,7 +442,7 @@ var PtySession = class extends import_events.EventEmitter {
384
442
  if (this.ptyResponseEmitted === 0) {
385
443
  const finalMatch = this.busyBuffer.match(/⏺(.+)/s);
386
444
  if (finalMatch) {
387
- let text = finalMatch[1].replace(/[✳✶✻✽✢·].*$/s, "").replace(/─{3,}.*$/s, "").replace(/Brewed for.*$/s, "").replace(/esctointerrupt.*$/s, "").trim();
445
+ let text = finalMatch[1].replace(/[✳✶✻✽✢·].*$/s, "").replace(/─{3,}.*$/s, "").replace(/\w{3,}ed (?:for|in) \d{1,4}s?.*$/s, "").replace(/esctointerrupt.*$/s, "").trim();
388
446
  if (text.length > 0) {
389
447
  this.emitUserIfNeeded();
390
448
  this.history.push({
@@ -397,7 +455,7 @@ var PtySession = class extends import_events.EventEmitter {
397
455
  }
398
456
  }
399
457
  }
400
- const durMatch = this.busyBuffer.match(/Brewed for (\d+)s/);
458
+ const durMatch = cleanBuf.match(/\w+ed (?:for|in) (\d{1,4})s?/i);
401
459
  const durationMs = durMatch ? parseInt(durMatch[1]) * 1e3 : 0;
402
460
  if (this.history.filter((m) => m.role === "user").length <= 1 && this.lastUserContent) {
403
461
  this.title = this.lastUserContent.slice(0, 60);
@@ -407,33 +465,6 @@ var PtySession = class extends import_events.EventEmitter {
407
465
  this.lastActivityAt = Date.now();
408
466
  this.emit("message", { type: "done", durationMs });
409
467
  }
410
- if (!this.ptyDoneEmitted && this.status === "busy" && Date.now() - this.busySince > 5e3 && this.ptyResponseEmitted > 0) {
411
- const promptIndicators = [
412
- /for shortcuts/,
413
- /\/effort/,
414
- /refactor/
415
- ];
416
- for (const re of promptIndicators) {
417
- if (re.test(this.ptyBuffer)) {
418
- console.log(`[pty-session ${this.id}] detected prompt return \u2192 done (fallback)`);
419
- this.ptyDoneEmitted = true;
420
- if (this.ptyResponseText.length > this.ptyResponseEmitted) {
421
- this.emitIncrementalText();
422
- }
423
- if (this.history.filter((m) => m.role === "user").length <= 1 && this.lastUserContent) {
424
- this.title = this.lastUserContent.slice(0, 60);
425
- this.emit("title-change", this.title);
426
- }
427
- this.status = "ready";
428
- this.lastActivityAt = Date.now();
429
- this.emit("message", { type: "done", durationMs: 0 });
430
- return;
431
- }
432
- }
433
- }
434
- if (!this.ptyDoneEmitted && this.status === "busy" && Date.now() - this.busySince > 1e4 && Math.random() < 0.02) {
435
- console.log(`[pty-session ${this.id}] still busy after ${Math.round((Date.now() - this.busySince) / 1e3)}s, buffer tail: ${JSON.stringify(this.busyBuffer.slice(-300))}`);
436
- }
437
468
  }
438
469
  /** Emit only the newly arrived characters (incremental streaming) */
439
470
  emitIncrementalText() {
@@ -512,6 +543,9 @@ var PtySession = class extends import_events.EventEmitter {
512
543
  // ── JSONL Discovery & Parsing (structured events) ─────
513
544
  async discoverJsonl() {
514
545
  const searchStart = this.messageSentAt - 2e3;
546
+ const projectsDir = findClaudeProjectsDir();
547
+ const expectedSubdir = cwdToProjectDir(this.cwd);
548
+ console.log(`[pty-session ${this.id}] JSONL discovery: projectsDir=${projectsDir}, expected=${expectedSubdir}, cwd=${this.cwd}`);
515
549
  for (let i = 0; i < 120; i++) {
516
550
  if (this.killed) return;
517
551
  const jsonl = findRecentJsonl(this.cwd, searchStart);
@@ -522,6 +556,17 @@ var PtySession = class extends import_events.EventEmitter {
522
556
  this.startTailingJsonl();
523
557
  return;
524
558
  }
559
+ if (i === 10) {
560
+ try {
561
+ if (fs.existsSync(projectsDir)) {
562
+ const dirs = fs.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
563
+ console.log(`[pty-session ${this.id}] JSONL not found in expected dir. Available project dirs: ${dirs.join(", ")}`);
564
+ } else {
565
+ console.log(`[pty-session ${this.id}] projectsDir does not exist: ${projectsDir}`);
566
+ }
567
+ } catch {
568
+ }
569
+ }
525
570
  await new Promise((r) => setTimeout(r, 500));
526
571
  }
527
572
  console.warn(`[pty-session ${this.id}] JSONL not found after 60s \u2014 using PTY output parsing`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/vite-plugin.ts","../src/sidecar.ts","../src/pty-session.ts","../src/assets.ts"],"sourcesContent":["/**\n * CC Prompter — Package Entry\n *\n * Vite plugin that adds Claude Code PTY sessions to your dev workflow.\n *\n * Usage:\n * import { ccPromptPlugin } from 'cc-prompter';\n * // in vite.config.ts plugins: [ccPromptPlugin()]\n */\n\nexport { ccPromptPlugin } from './vite-plugin.js';\nexport type { CcPromptOptions } from './vite-plugin.js';\n","/**\n * CC Prompter — Vite Plugin\n *\n * 为 Vite dev server 提供:\n * 1. 内置 code-inspector-plugin(Shift+Alt 悬停定位源码)\n * 2. 启动 Sidecar Express server 管理 PTY sessions(自动选择空闲端口)\n * 3. 注入轻量脚本监听 code-inspector 事件,弹出 Claude 面板\n * 4. 提供 /__panel/ HTML 给 iframe\n *\n * 仅在 dev 模式生效,build 时不注入脚本。\n *\n * 用法:plugins: [ccPromptPlugin()]\n * 等同于同时配置 codeInspectorPlugin + cc-prompt-plugin。\n */\n\nimport type { Plugin } from 'vite';\nimport type { Server } from 'http';\nimport { codeInspectorPlugin } from 'code-inspector-plugin';\nimport { startSidecar } from './sidecar.js';\nimport { getPanelHtml, getInjectScript } from './assets.js';\n\nexport interface CcPromptOptions {\n /** Sidecar 启动端口,默认 3456(被占用时自动 +1) */\n port?: number;\n /** 项目根目录,默认 vite config.root */\n root?: string;\n /** 是否启用 code-inspector,默认 true */\n inspector?: boolean;\n}\n\nexport function ccPromptPlugin(options?: CcPromptOptions): Plugin[] {\n const enableInspector = options?.inspector !== false;\n let projectRoot = process.cwd();\n let sidecarServer: Server | null = null;\n let actualPort = 0;\n\n const startPort = options?.port || 3456;\n\n let cleanedUp = false;\n function cleanup() {\n if (cleanedUp) return;\n cleanedUp = true;\n if (sidecarServer) {\n sidecarServer.close();\n sidecarServer = null;\n }\n }\n\n // ── 1. code-inspector plugin ──\n const inspectorPlugin = enableInspector\n ? codeInspectorPlugin({\n bundler: 'vite',\n behavior: {\n locate: false,\n copy: false,\n },\n hideDomPathAttr: true,\n hideConsole: true,\n })\n : null;\n\n // ── 2. cc-prompter plugin ──\n const promptPlugin: Plugin = {\n name: 'cc-prompt-plugin',\n\n configResolved(config) {\n projectRoot = options?.root || config.root;\n },\n\n configureServer(server) {\n // Start sidecar — auto-picks available port\n sidecarServer = startSidecar(projectRoot, { startPort });\n\n // Wait for sidecar to actually start, then grab port\n const checkPort = () => {\n const addr = sidecarServer?.address();\n if (addr && typeof addr === 'object') {\n actualPort = addr.port;\n console.log(`[cc-prompter] Sidecar port: ${actualPort}`);\n }\n };\n setTimeout(checkPort, 200);\n setTimeout(checkPort, 1000);\n\n // Expose actual sidecar port to inject script\n server.middlewares.use('/__cc-port', (req, res) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.end(String(actualPort || startPort));\n });\n\n // Clean up on dev server close\n server.httpServer?.on('close', cleanup);\n process.on('SIGTERM', () => { cleanup(); process.exit(0); });\n process.on('SIGINT', () => { cleanup(); process.exit(0); });\n process.on('exit', cleanup);\n },\n\n // Only inject in dev mode (ctx.server is undefined during build)\n transformIndexHtml: {\n order: 'post',\n handler(html, ctx) {\n if (!ctx.server) return html;\n const script = getInjectScript();\n return html.replace('</body>', `<script>${script}</script></body>`);\n },\n },\n\n closeBundle() {\n cleanup();\n },\n };\n\n // Return composed plugin array\n const plugins: Plugin[] = [promptPlugin];\n if (inspectorPlugin) plugins.unshift(inspectorPlugin);\n return plugins;\n}\n","/**\n * CC Prompter — Sidecar API Server\n *\n * Express server 运行在端口 3456,管理 PTY session 生命周期。\n * 提供 REST API + SSE 流式响应。\n */\n\nimport express from 'express';\nimport { createServer, type Server } from 'http';\nimport { PtySession } from './pty-session.js';\nimport { getPanelHtml } from './assets.js';\nimport type {\n SessionInfo,\n CreateSessionRequest,\n SendMessageRequest,\n SendCommandRequest,\n SseEvent,\n} from './types.js';\n\n// ── Session Manager ─────────────────────────────────────\n\nclass SessionManager {\n private sessions = new Map<string, PtySession>();\n private counter = 0;\n\n async create(cwd: string): Promise<PtySession> {\n const id = `s${++this.counter}-${Date.now().toString(36)}`;\n const session = new PtySession(id, cwd);\n this.sessions.set(id, session);\n\n // Clean up on exit\n session.on('exit', () => {\n // Keep in map for history, but mark exited\n });\n\n await session.spawn();\n return session;\n }\n\n get(id: string): PtySession | undefined {\n return this.sessions.get(id);\n }\n\n list(): SessionInfo[] {\n return Array.from(this.sessions.values()).map(s => {\n const info = s.getInfo();\n return {\n id: info.id,\n title: info.title,\n status: info.status,\n createdAt: info.createdAt,\n lastActivityAt: info.lastActivityAt,\n messageCount: info.messageCount,\n lastMessagePreview: info.lastMessagePreview,\n };\n });\n }\n\n destroy(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n session.kill();\n this.sessions.delete(id);\n return true;\n }\n\n destroyAll(): void {\n for (const session of this.sessions.values()) {\n session.kill();\n }\n this.sessions.clear();\n }\n}\n\n// ── Sidecar Server ──────────────────────────────────────\n\nexport interface SidecarOptions {\n startPort?: number;\n}\n\nexport function startSidecar(projectRoot: string, options?: SidecarOptions): Server {\n const startPort = options?.startPort || 3456;\n const app = express();\n app.use(express.json());\n\n const manager = new SessionManager();\n\n // ── CORS for iframe ──\n app.use((req, res, next) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.sendStatus(204);\n return;\n }\n next();\n });\n\n // ── Panel HTML (served to iframe) ──\n app.get('/__panel/', (_req, res) => {\n const html = getPanelHtml();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Content-Length', Buffer.byteLength(html));\n res.end(html);\n });\n\n // ── Panel favicon (no-op) ──\n app.get('/favicon.ico', (_req, res) => {\n res.sendStatus(204);\n });\n\n // ── List sessions ──\n app.get('/api/sessions', (_req, res) => {\n res.json(manager.list());\n });\n\n // ── Create session ──\n app.post<Record<string, string>, any, CreateSessionRequest>('/api/sessions', async (req, res) => {\n try {\n const cwd = req.body?.cwd || projectRoot;\n const session = await manager.create(cwd);\n res.json(session.getInfo());\n } catch (err: any) {\n console.error('[cc-prompter] Failed to create session:', err);\n res.status(500).json({ error: err.message, stack: err.stack });\n }\n });\n\n // ── Send message (SSE stream) ──\n app.post<Record<string, string>, any, SendMessageRequest>(\n '/api/sessions/:id/message',\n async (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n if (session.status === 'exited') {\n res.status(410).json({ error: 'Session exited' });\n return;\n }\n if (session.status === 'busy') {\n res.status(409).json({ error: 'Session busy' });\n return;\n }\n\n const { content, sourceInfo } = req.body;\n if (!content) {\n res.status(400).json({ error: 'Missing content' });\n return;\n }\n\n // Build prompt with optional source context (single-line to avoid multi-line input mode)\n let prompt = content;\n if (sourceInfo) {\n const relPath = sourceInfo.path;\n const parts = [\n `[source: ${relPath}:${sourceInfo.line}:${sourceInfo.column}]`,\n ];\n if (sourceInfo.elementInfo) {\n parts.push(`[element: ${sourceInfo.elementInfo}]`);\n }\n prompt = parts.join(' ') + ' ' + content;\n }\n\n // SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n // Forward session events as SSE\n const onMessage = (evt: SseEvent) => {\n res.write(`data: ${JSON.stringify(evt)}\\n\\n`);\n\n // Stop streaming on 'done'\n if (evt.type === 'done') {\n cleanup();\n }\n };\n\n const onError = (err: Error) => {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n };\n\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n session.removeListener('message', onMessage);\n session.removeListener('error', onError);\n res.end();\n };\n\n session.on('message', onMessage);\n session.on('error', onError);\n\n // Client disconnect — delay registration to avoid premature close\n // (Express/Node HTTP can fire 'close' early on SSE connections)\n let cleanedUp = false;\n setTimeout(() => {\n req.on('close', () => {\n if (!cleanedUp) {\n cleanup();\n }\n });\n }, 3000);\n\n // Send to PTY\n try {\n await session.sendMessage(prompt);\n } catch (err: any) {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n }\n },\n );\n\n // ── Send command ──\n app.post<Record<string, string>, any, SendCommandRequest>(\n '/api/sessions/:id/command',\n (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n\n const { command } = req.body;\n if (!command) {\n res.status(400).json({ error: 'Missing command' });\n return;\n }\n\n try {\n session.sendCommand(command);\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n },\n );\n\n // ── Interrupt session (Escape key) ──\n app.post('/api/sessions/:id/interrupt', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n try {\n session.interrupt();\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n });\n\n // ── Delete session ──\n app.delete('/api/sessions/:id', (req, res) => {\n if (manager.destroy(req.params.id)) {\n res.json({ ok: true });\n } else {\n res.status(404).json({ error: 'Session not found' });\n }\n });\n\n // ── Session history ──\n app.get('/api/sessions/:id/history', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session.getHistory());\n });\n\n const server = createServer(app);\n const MAX_PORT = startPort + 10;\n\n function tryListen(port: number): Promise<Server> {\n return new Promise((resolve, reject) => {\n server.listen(port, () => {\n console.log(`[cc-prompter] Sidecar running on http://localhost:${port}`);\n resolve(server);\n });\n server.on('error', (err: any) => {\n if (err.code === 'EADDRINUSE' && port < MAX_PORT) {\n console.log(`[cc-prompter] Port ${port} in use, trying ${port + 1}...`);\n tryListen(port + 1).then(resolve, reject);\n } else {\n reject(err);\n }\n });\n });\n }\n\n // Fire-and-forget listen (returns server immediately for API compat)\n tryListen(startPort).catch((err) => {\n console.error(`[cc-prompter] Failed to start sidecar:`, err.message);\n });\n\n // Graceful shutdown\n server.on('close', () => {\n manager.destroyAll();\n });\n\n return server;\n}\n","/**\n * CC Prompter — PTY Session\n *\n * 每个实例管理一个常驻的 claude CLI 进程(通过 node-pty)。\n *\n * 输出解析策略(双通道):\n * 1. PTY 输出解析(主要)— 实时从 TUI 输出提取响应文本\n * 2. JSONL transcript(辅助)— 结构化事件,用于 tool_use 等\n *\n * 就绪检测:解析 PTY 输出中的提示符(\"for shortcuts\"、\"/effort\")。\n */\n\nimport { createRequire } from 'module';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nconst _metaUrl = typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url);\nconst require = createRequire(_metaUrl);\nimport { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { IPty } from 'node-pty-prebuilt-multiarch';\nimport type {\n SessionStatus,\n ChatMessage,\n ToolUseInfo,\n JsonlEvent,\n SseEvent,\n} from './types.js';\n\n// Try multiple PTY packages for cross-platform compatibility\nconst PTY_PACKAGES = [\n 'node-pty-prebuilt-multiarch', // macOS / Linux prebuilt\n '@homebridge/node-pty-prebuilt-multiarch', // Better Windows support\n 'node-pty', // Original (needs build tools)\n];\n\nlet _ptyModule: any = null;\nfunction loadPty(): any {\n if (_ptyModule) return _ptyModule;\n const errors: string[] = [];\n for (const pkg of PTY_PACKAGES) {\n try {\n const mod = require(pkg);\n // Verify native binary actually exists — require() can succeed without it\n const modDir = path.dirname(require.resolve(pkg + '/package.json'));\n const buildDir = path.join(modDir, 'build', 'Release');\n if (!fs.existsSync(buildDir) || fs.readdirSync(buildDir).filter(f => f.endsWith('.node')).length === 0) {\n errors.push(`${pkg}: no native binary in ${buildDir}`);\n continue;\n }\n console.log(`[cc-prompter] Loaded PTY from: ${pkg}`);\n _ptyModule = mod;\n return _ptyModule;\n } catch (err: any) {\n errors.push(`${pkg}: ${err.message}`);\n }\n }\n throw new Error(\n `No PTY module available. Tried:\\n${errors.map(e => ' ' + e).join('\\n')}\\n` +\n `Install one: npm install node-pty-prebuilt-multiarch (macOS/Linux) or @homebridge/node-pty-prebuilt-multiarch (Windows)`\n );\n}\n\n// ── Helpers ─────────────────────────────────────────────\n\nfunction resolveClaudeBin(cwd: string): { file: string; args: string[] } {\n // Windows: find node.exe + claude.js directly (bypass broken .cmd wrapper)\n if (process.platform === 'win32') {\n // Check local install first\n const localJs = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(localJs)) {\n return { file: process.execPath, args: [localJs] };\n }\n // Check global npm install (nvm4w puts global modules next to node.exe)\n const globalPrefix = path.dirname(process.execPath);\n const globalJs = path.join(globalPrefix, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(globalJs)) {\n return { file: process.execPath, args: [globalJs] };\n }\n // Check npm global prefix via APPDATA (standard npm location)\n const appDataJs = path.join(process.env.APPDATA || '', 'npm/node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(appDataJs)) {\n return { file: process.execPath, args: [appDataJs] };\n }\n // Fallback: try cmd.exe\n return { file: process.env.COMSPEC || 'cmd.exe', args: ['/c', 'claude'] };\n }\n\n // macOS/Linux\n const local = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude');\n if (fs.existsSync(local)) return { file: local, args: [] };\n return { file: 'claude', args: [] };\n}\n\nfunction findClaudeProjectsDir(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n\n/** Convert a cwd path to the Claude projects directory name */\nfunction cwdToProjectDir(cwd: string): string {\n // Normalize path separators and handle both Unix and Windows paths\n // D:/code/agentplat → -D--code-agentplat\n // /Users/ryan/demo → -Users-ryan-demo\n const normalized = cwd.replace(/\\\\/g, '/').replace(/^[A-Za-z]:/, m => '-' + m[0].toLowerCase());\n return normalized.replace(/^\\//, '').replace(/\\/+/g, '-').replace(/^-+/, '');\n}\n\n/** Scan for the most recently modified .jsonl in a specific project dir */\nfunction findRecentJsonl(cwd: string, afterMs: number): string | null {\n const projectsDir = findClaudeProjectsDir();\n const projectSubdir = cwdToProjectDir(cwd);\n const targetDir = path.join(projectsDir, projectSubdir);\n\n if (!fs.existsSync(targetDir)) return null;\n\n const files = fs.readdirSync(targetDir).filter(f => f.endsWith('.jsonl'));\n let best: { path: string; mtime: number } | null = null;\n for (const f of files) {\n const fp = path.join(targetDir, f);\n try {\n const stat = fs.statSync(fp);\n if (stat.mtimeMs > afterMs) {\n if (!best || stat.mtimeMs > best.mtime) {\n best = { path: fp, mtime: stat.mtimeMs };\n }\n }\n } catch { continue; }\n }\n return best?.path || null;\n}\n\nfunction sessionIdFromJsonlPath(jsonPath: string): string | null {\n const base = path.basename(jsonPath, '.jsonl');\n const match = base.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);\n return match ? match[0] : null;\n}\n\n/** Strip ANSI escape sequences for analysis */\nfunction stripAnsi(s: string): string {\n return s\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\].*?(?:\\x07|\\x1b\\\\)/g, '')\n .replace(/\\x1b\\[[\\?]?[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\[[0-9;]*m/g, '')\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b[^[\\]()]?.?/g, '')\n .replace(/\\r/g, '');\n}\n\n// ── PtySession ──────────────────────────────────────────\n\nexport class PtySession extends EventEmitter {\n readonly id: string;\n readonly cwd: string;\n status: SessionStatus = 'spawning';\n\n private pty: IPty | null = null;\n private jsonlPath: string | null = null;\n private sessionId: string | null = null;\n private history: ChatMessage[] = [];\n private jsonlOffset = 0;\n private jsonlWatcher: fs.FSWatcher | null = null;\n private spawnTime: number;\n private messageSentAt = 0;\n private busySince = 0; // timestamp when last message was sent (for grace period)\n private title = 'New Session';\n private lastActivityAt: number;\n private killed = false;\n private ptyBuffer = ''; // accumulated for prompt detection\n private jsonlDiscoverPromise: Promise<void> | null = null;\n\n // ── PTY streaming fields ──\n private busyBuffer = ''; // accumulated during busy state\n private lastUserContent = ''; // last user message text\n private ptyResponseText = ''; // extracted response text so far\n private ptyResponseEmitted = 0; // chars already emitted\n private usedJsonl = false; // JSONL events received this turn\n private ptyDoneEmitted = false;\n private lastProgress = ''; // last emitted progress text\n private interrupted = false; // set when user sends interrupt\n\n constructor(id: string, cwd: string) {\n super();\n this.id = id;\n this.cwd = cwd;\n this.spawnTime = Date.now();\n this.lastActivityAt = this.spawnTime;\n }\n\n /** Spawn the claude process via PTY */\n async spawn(): Promise<void> {\n const ptyModule = loadPty();\n const { file, args } = resolveClaudeBin(this.cwd);\n\n console.log(`[pty-session ${this.id}] spawning: ${file} ${args.join(' ')} cwd: ${this.cwd}`);\n\n this.pty = ptyModule.spawn(file, args, {\n name: 'xterm-256color',\n cols: 120,\n rows: 30,\n cwd: this.cwd,\n env: { ...process.env } as Record<string, string>,\n });\n\n console.log(`[pty-session ${this.id}] PID: ${this.pty.pid}`);\n\n // Watch PTY output\n this.pty.onData((data: string) => {\n // Debug: log first 500 chars of output to diagnose spawn failures\n if (this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] output: ${JSON.stringify(data.substring(0, 500))}`);\n }\n const clean = stripAnsi(data);\n this.ptyBuffer += clean;\n\n this.detectPrompt();\n\n // Parse for streaming response when busy and JSONL not active\n if (this.status === 'busy' && !this.usedJsonl) {\n this.busyBuffer += clean;\n this.parseBusyOutput();\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n console.log(`[pty-session ${this.id}] exited with code: ${exitCode}`);\n this.status = 'exited';\n this.lastActivityAt = Date.now();\n this.emit('exit', exitCode);\n this.cleanup();\n });\n }\n\n // ── Prompt Detection ──────────────────────────────────\n\n private detectPrompt(): void {\n const indicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n\n for (const re of indicators) {\n if (re.test(this.ptyBuffer) && this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] detected prompt → ready`);\n this.status = 'ready';\n this.emit('ready');\n return;\n }\n }\n\n // After interrupt, detect prompt returning to finish the stream\n if (this.interrupted && this.status === 'busy') {\n for (const re of indicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt after interrupt → done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n }\n\n private async waitUntilReady(timeoutMs = 30_000): Promise<void> {\n if (this.status === 'ready') return;\n if (this.status === 'exited') throw new Error('Session exited');\n\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n const timer = setInterval(() => {\n if (this.status === 'ready') {\n clearInterval(timer);\n resolve();\n } else if (this.status === 'exited') {\n clearInterval(timer);\n reject(new Error('Session exited while waiting'));\n } else if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout waiting for session to be ready'));\n }\n }, 100);\n });\n }\n\n // ── Send Message ──────────────────────────────────────\n\n async sendMessage(content: string): Promise<void> {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status === 'busy') {\n throw new Error('Session busy');\n }\n\n // Wait for claude prompt to be ready\n if (this.status !== 'ready') {\n console.log(`[pty-session ${this.id}] waiting for prompt before sending message...`);\n await this.waitUntilReady();\n }\n\n await new Promise(r => setTimeout(r, 200));\n\n this.status = 'busy';\n this.lastActivityAt = Date.now();\n this.busySince = Date.now();\n\n // Reset streaming state for this turn\n this.busyBuffer = '';\n this.ptyBuffer = ''; // Clear accumulated prompt text to avoid false done detection\n this.lastUserContent = content;\n this.ptyResponseText = '';\n this.ptyResponseEmitted = 0;\n this.usedJsonl = false;\n this.ptyDoneEmitted = false;\n this.lastProgress = '';\n this.interrupted = false;\n\n // JSONL discovery in background\n if (!this.messageSentAt) {\n this.messageSentAt = Date.now();\n console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);\n this.jsonlDiscoverPromise = this.discoverJsonl();\n }\n\n console.log(`[pty-session ${this.id}] writing to PTY: ${JSON.stringify(content.slice(0, 100))}`);\n\n // Write content + double Enter for reliable submission\n this.pty.write(content + '\\r');\n await new Promise(r => setTimeout(r, 150));\n this.pty.write('\\r');\n }\n\n /** Send a slash command to the PTY */\n sendCommand(command: string): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n\n this.pty.write(command + '\\r');\n\n if (command === '/new') {\n this.history = [];\n this.title = 'New Session';\n this.jsonlPath = null;\n this.jsonlOffset = 0;\n this.jsonlWatcher?.close();\n this.jsonlWatcher = null;\n this.sessionId = null;\n this.messageSentAt = 0;\n this.status = 'ready';\n this.ptyBuffer = '';\n }\n }\n\n /** Send Escape to PTY to interrupt current generation */\n interrupt(): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status !== 'busy') return;\n this.interrupted = true;\n this.pty.write('\\x1b');\n\n // Safety timeout: if prompt detection fails, force finish after 5s\n setTimeout(() => {\n if (this.interrupted && this.status === 'busy') {\n console.log(`[pty-session ${this.id}] interrupt timeout → force done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n }\n }, 5000);\n }\n\n // ── PTY Output Parsing (streaming fallback) ───────────\n\n /**\n * Parse PTY output during busy state to extract streaming response.\n *\n * Claude Code TUI patterns:\n * - Spinner frames: ✳ ✶ ✻ ✽ ✢ · (ignore — just animation)\n * - Response text: ⏺<text> or ●<text>\n * - Tool use: ⚡<tool_name> or ✢ editing <file>\n * - Completion: \"Brewed for Xs\" (ONLY reliable indicator)\n * - ⚠️ ❯ appears in input echo too — NOT a completion signal!\n * - Timing: (Xs · ↓NNN tokens)\n */\n private parseBusyOutput(): void {\n // ── 0. Extract progress for UI feedback ──\n this.emitProgress();\n\n // ── 1. Try to extract response text ──\n const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);\n if (respMatch) {\n let raw = respMatch[1];\n raw = raw.replace(/[✳✶✻✽✢·].*$/s, '').trim();\n raw = raw.replace(/─{3,}.*$/s, '').trim();\n raw = raw.replace(/Brewed for.*$/s, '').trim();\n raw = raw.replace(/Sautéed for.*$/s, '').trim();\n raw = raw.replace(/esctointerrupt.*$/s, '').trim();\n\n if (raw.length > this.ptyResponseText.length) {\n this.ptyResponseText = raw;\n this.emitIncrementalText();\n }\n }\n\n // ── 2. Extract tool calls (Update/Read/Edit) ──\n const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/);\n if (toolCallMatch) {\n const toolName = toolCallMatch[1];\n const filePath = toolCallMatch[2];\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: toolName, input: { file: filePath } },\n } as SseEvent);\n }\n\n // ── 3. Detect completion ──\n if (!this.ptyDoneEmitted && /(?:Brewed|Sautéed) for/.test(this.busyBuffer)) {\n this.ptyDoneEmitted = true;\n\n // Final flush: emit any remaining text\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // If no response was emitted at all, try one more aggressive extraction\n if (this.ptyResponseEmitted === 0) {\n const finalMatch = this.busyBuffer.match(/⏺(.+)/s);\n if (finalMatch) {\n let text = finalMatch[1]\n .replace(/[✳✶✻✽✢·].*$/s, '')\n .replace(/─{3,}.*$/s, '')\n .replace(/Brewed for.*$/s, '')\n .replace(/esctointerrupt.*$/s, '')\n .trim();\n if (text.length > 0) {\n this.emitUserIfNeeded();\n this.history.push({\n role: 'assistant', content: text, timestamp: Date.now(),\n });\n this.emit('message', { type: 'assistant_text', content: text } as SseEvent);\n this.ptyResponseEmitted = text.length;\n }\n }\n }\n\n // Extract duration\n const durMatch = this.busyBuffer.match(/Brewed for (\\d+)s/);\n const durationMs = durMatch ? parseInt(durMatch[1]) * 1000 : 0;\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs } as SseEvent);\n }\n\n // ── 3b. Detect completion via prompt return (for Windows / non-English) ──\n // Only check after 5s AND after we've seen actual response content.\n // Claude TUI always shows \"for shortcuts\" in the header — we must distinguish\n // between \"header redraw during processing\" and \"Claude actually finished\".\n if (!this.ptyDoneEmitted && this.status === 'busy'\n && Date.now() - this.busySince > 5000\n && this.ptyResponseEmitted > 0) {\n const promptIndicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n for (const re of promptIndicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt return → done (fallback)`);\n this.ptyDoneEmitted = true;\n\n // Final flush\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n\n // Debug: log busyBuffer tail periodically while stuck in busy\n if (!this.ptyDoneEmitted && this.status === 'busy'\n && Date.now() - this.busySince > 10000\n && Math.random() < 0.02) {\n console.log(`[pty-session ${this.id}] still busy after ${Math.round((Date.now() - this.busySince) / 1000)}s, buffer tail: ${JSON.stringify(this.busyBuffer.slice(-300))}`);\n }\n }\n\n /** Emit only the newly arrived characters (incremental streaming) */\n private emitIncrementalText(): void {\n const newText = this.ptyResponseText.slice(this.ptyResponseEmitted);\n if (newText.length === 0) return;\n\n // First chunk → emit user message + start assistant\n if (this.ptyResponseEmitted === 0) {\n this.emitUserIfNeeded();\n }\n\n this.ptyResponseEmitted = this.ptyResponseText.length;\n this.emit('message', { type: 'assistant_text', content: newText } as SseEvent);\n }\n\n /**\n * Extract and emit progress updates from busyBuffer.\n *\n * Parses PTY output for Claude Code's progress indicators:\n * - \"Thinking for Xs, reading N files\"\n * - \"Thought for Xs, read N files\"\n * - \"Crafting… (Xs · ↓NN tokens)\"\n * - \"Update(file)\" / \"Read(file)\"\n * - \"⎿ Removed N lines\"\n * - \"(Xs · ↓NN tokens)\" timing\n */\n private emitProgress(): void {\n // Strip spinner chars + noise, collapse whitespace\n const text = this.busyBuffer\n .replace(/[✳✶✻✽✢·][a-zA-Z0-9…]{0,4}/g, '')\n .replace(/\\s+/g, ' ');\n\n // Ordered by specificity — last match wins (most recent progress)\n const patterns: [RegExp, (m: RegExpMatchArray) => string][] = [\n // Thinking phase\n [/Thinking for (\\d+s)[^─]{0,60}(reading \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Thought completed\n [/Thought for (\\d+s)[^─]{0,60}(read \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Tool call: Update(file) / Read(file)\n [/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/, (m) => {\n return m[1] + ': ' + m[2].split('/').slice(-2).join('/');\n }],\n // Tool result: ⎿ Removed N lines\n [/⎿\\s*(Removed|Added|Modified|Created)\\s+(\\d+)\\s+(lines?)/, (m) => {\n return m[1] + ' ' + m[2] + ' ' + m[3];\n }],\n // Crafting with timing\n [/Crafting[^─]{0,30}\\(\\d+s[^)]*\\)/, (m) => {\n return m[0].replace(/\\s+/g, ' ').trim();\n }],\n // Simple timing: (5s · ↓9 tokens)\n [/\\((\\d+s)\\s*·\\s*[↓↑]\\s*(\\d+)\\s*tokens?\\)/, (m) => {\n return m[1] + ' · ' + m[2] + ' tokens';\n }],\n ];\n\n // Find the last matching pattern\n let progress = '';\n for (const [re, fn] of patterns) {\n const m = text.match(re);\n if (m) progress = fn(m);\n }\n\n // Emit only if changed\n if (progress && progress !== this.lastProgress) {\n this.lastProgress = progress;\n this.emit('message', { type: 'progress', content: progress } as SseEvent);\n }\n }\n\n /** Emit user message event (only if not already emitted this turn) */\n private emitUserIfNeeded(): void {\n if (!this.lastUserContent) return;\n // Check if user message already in history\n const already = this.history.some(\n m => m.role === 'user' && m.content === this.lastUserContent,\n );\n if (!already) {\n this.history.push({\n role: 'user',\n content: this.lastUserContent,\n timestamp: Date.now(),\n });\n this.emit('message', { type: 'user', content: this.lastUserContent } as SseEvent);\n }\n }\n\n // ── JSONL Discovery & Parsing (structured events) ─────\n\n private async discoverJsonl(): Promise<void> {\n const searchStart = this.messageSentAt - 2000;\n\n // Poll for up to 60 seconds (120 × 500ms)\n for (let i = 0; i < 120; i++) {\n if (this.killed) return;\n const jsonl = findRecentJsonl(this.cwd, searchStart);\n if (jsonl) {\n this.jsonlPath = jsonl;\n this.sessionId = sessionIdFromJsonlPath(jsonl) || null;\n console.log(`[pty-session ${this.id}] found JSONL: ${jsonl} (session: ${this.sessionId})`);\n this.startTailingJsonl();\n return;\n }\n await new Promise(r => setTimeout(r, 500));\n }\n console.warn(`[pty-session ${this.id}] JSONL not found after 60s — using PTY output parsing`);\n }\n\n private startTailingJsonl(): void {\n if (!this.jsonlPath) return;\n\n this.readNewJsonlLines();\n\n try {\n this.jsonlWatcher = fs.watch(\n path.dirname(this.jsonlPath),\n (eventType, filename) => {\n if (filename === path.basename(this.jsonlPath!)) {\n this.readNewJsonlLines();\n }\n },\n );\n } catch (err) {\n console.warn(`[pty-session ${this.id}] fs.watch failed:`, err);\n }\n }\n\n private readNewJsonlLines(): void {\n if (!this.jsonlPath) return;\n\n try {\n const stat = fs.statSync(this.jsonlPath);\n if (stat.size <= this.jsonlOffset) return;\n\n const fd = fs.openSync(this.jsonlPath, 'r');\n const buf = Buffer.alloc(stat.size - this.jsonlOffset);\n fs.readSync(fd, buf, 0, buf.length, this.jsonlOffset);\n fs.closeSync(fd);\n this.jsonlOffset = stat.size;\n\n const text = buf.toString('utf8');\n for (const line of text.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const evt: JsonlEvent = JSON.parse(trimmed);\n this.processJsonlEvent(evt);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // file might be temporarily unavailable\n }\n }\n\n /** Process a JSONL event — marks usedJsonl to disable PTY parsing */\n private processJsonlEvent(evt: JsonlEvent): void {\n // Once JSONL events arrive, disable PTY output parsing\n this.usedJsonl = true;\n\n if (!this.sessionId && evt.sessionId) {\n this.sessionId = evt.sessionId;\n }\n\n switch (evt.type) {\n case 'user': {\n const text = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n : '';\n this.history.push({\n role: 'user',\n content: text,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'user', content: text } as SseEvent);\n if (this.history.filter(m => m.role === 'user').length === 1 && text) {\n this.title = text.slice(0, 60);\n this.emit('title-change', this.title);\n }\n break;\n }\n case 'assistant': {\n const content = evt.message?.content;\n if (!Array.isArray(content)) break;\n\n const texts = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n .trim();\n\n const tools: ToolUseInfo[] = content\n .filter((c: any) => c.type === 'tool_use')\n .map((c: any) => ({\n name: c.name,\n input: c.input || {},\n }));\n\n if (texts || tools.length) {\n this.history.push({\n role: 'assistant',\n content: texts,\n toolUse: tools.length ? tools : undefined,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n }\n\n if (texts) {\n this.emit('message', { type: 'assistant_text', content: texts } as SseEvent);\n }\n for (const t of tools) {\n this.emit('message', { type: 'assistant_tool', tool: t } as SseEvent);\n }\n break;\n }\n case 'system': {\n if (evt.subtype === 'tool_result') {\n const lastAssistant = [...this.history].reverse().find(m => m.role === 'assistant' && m.toolUse?.length);\n if (lastAssistant?.toolUse?.length) {\n const lastTool = lastAssistant.toolUse[lastAssistant.toolUse.length - 1];\n const resultText = typeof evt.message?.content === 'string'\n ? evt.message.content : '';\n lastTool.result = resultText.slice(0, 500);\n }\n const resultContent = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content.map((c: any) => c.text || '').join('')\n : '';\n this.emit('message', { type: 'system', content: resultContent.slice(0, 200) } as SseEvent);\n } else if (evt.subtype === 'turn_duration') {\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: evt.durationMs } as SseEvent);\n }\n break;\n }\n }\n }\n\n // ── Lifecycle ─────────────────────────────────────────\n\n kill(): void {\n this.killed = true;\n this.cleanup();\n if (this.pty) {\n try { this.pty.kill(); } catch { /* already dead */ }\n this.pty = null;\n }\n this.status = 'exited';\n }\n\n private cleanup(): void {\n if (this.jsonlWatcher) {\n this.jsonlWatcher.close();\n this.jsonlWatcher = null;\n }\n }\n\n getInfo(): {\n id: string;\n title: string;\n status: SessionStatus;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n lastMessagePreview: string;\n sessionId: string | null;\n } {\n const lastMsg = this.history[this.history.length - 1];\n return {\n id: this.id,\n title: this.title,\n status: this.status,\n createdAt: this.spawnTime,\n lastActivityAt: this.lastActivityAt,\n messageCount: this.history.length,\n lastMessagePreview: lastMsg ? lastMsg.content.slice(0, 80) : '',\n sessionId: this.sessionId,\n };\n }\n\n getHistory(): ChatMessage[] {\n return [...this.history];\n }\n}\n","/**\n * CC Prompter — Asset Loader\n *\n * 运行时从包根目录读取 panel.html 和 inject.js。\n * 兼容 ESM(import.meta.url)和 CJS(__dirname)。\n *\n * 解析策略:\n * - tsup 构建后:资产文件复制到 dist/,__dirname 是 dist/\n * - Vite 直接引用 TS:__dirname 是 src/,资产文件在上一级\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\n// Resolve __dirname in both ESM and CJS contexts\nconst _dirname = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\n/** Resolve asset path — checks dist/ (build) then parent (dev) */\nfunction assetPath(filename: string): string {\n // 1. Same dir as this file (dist/ after build, or src/ in dev)\n const here = join(_dirname, filename);\n if (existsSync(here)) return here;\n\n // 2. Parent dir (package root — when running from src/ via vite)\n const parent = join(_dirname, '..', filename);\n if (existsSync(parent)) return parent;\n\n // 3. Fallback — let it throw with a clear message\n throw new Error(\n `[cc-prompter] Asset not found: ${filename}\\n` +\n ` Tried: ${here}\\n` +\n ` Tried: ${parent}\\n` +\n ` __dirname: ${_dirname}`\n );\n}\n\nexport function getPanelHtml(): string {\n return readFileSync(assetPath('panel.html'), 'utf8');\n}\n\nexport function getInjectScript(): string {\n return readFileSync(assetPath('inject.js'), 'utf8');\n}\n"],"mappings":"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","names":["require","resolve","import_url","import_meta","express","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/vite-plugin.ts","../src/sidecar.ts","../src/pty-session.ts","../src/assets.ts"],"sourcesContent":["/**\n * CC Prompter — Package Entry\n *\n * Vite plugin that adds Claude Code PTY sessions to your dev workflow.\n *\n * Usage:\n * import { ccPromptPlugin } from 'cc-prompter';\n * // in vite.config.ts plugins: [ccPromptPlugin()]\n */\n\nexport { ccPromptPlugin } from './vite-plugin.js';\nexport type { CcPromptOptions } from './vite-plugin.js';\n","/**\n * CC Prompter — Vite Plugin\n *\n * 为 Vite dev server 提供:\n * 1. 内置 code-inspector-plugin(Shift+Alt 悬停定位源码)\n * 2. 启动 Sidecar Express server 管理 PTY sessions(自动选择空闲端口)\n * 3. 注入轻量脚本监听 code-inspector 事件,弹出 Claude 面板\n * 4. 提供 /__panel/ HTML 给 iframe\n *\n * 仅在 dev 模式生效,build 时不注入脚本。\n *\n * 用法:plugins: [ccPromptPlugin()]\n * 等同于同时配置 codeInspectorPlugin + cc-prompt-plugin。\n */\n\nimport type { Plugin } from 'vite';\nimport type { Server } from 'http';\nimport { codeInspectorPlugin } from 'code-inspector-plugin';\nimport { startSidecar } from './sidecar.js';\nimport { getPanelHtml, getInjectScript } from './assets.js';\n\nexport interface CcPromptOptions {\n /** Sidecar 启动端口,默认 3456(被占用时自动 +1) */\n port?: number;\n /** 项目根目录,默认 vite config.root */\n root?: string;\n /** 是否启用 code-inspector,默认 true */\n inspector?: boolean;\n}\n\nexport function ccPromptPlugin(options?: CcPromptOptions): Plugin[] {\n const enableInspector = options?.inspector !== false;\n let projectRoot = process.cwd();\n let sidecarServer: Server | null = null;\n let actualPort = 0;\n\n const startPort = options?.port || 3456;\n\n let cleanedUp = false;\n function cleanup() {\n if (cleanedUp) return;\n cleanedUp = true;\n if (sidecarServer) {\n sidecarServer.close();\n sidecarServer = null;\n }\n }\n\n // ── 1. code-inspector plugin ──\n const inspectorPlugin = enableInspector\n ? codeInspectorPlugin({\n bundler: 'vite',\n behavior: {\n locate: false,\n copy: false,\n },\n hideDomPathAttr: true,\n hideConsole: true,\n })\n : null;\n\n // ── 2. cc-prompter plugin ──\n const promptPlugin: Plugin = {\n name: 'cc-prompt-plugin',\n\n configResolved(config) {\n projectRoot = options?.root || config.root;\n },\n\n configureServer(server) {\n // Start sidecar — auto-picks available port\n sidecarServer = startSidecar(projectRoot, { startPort });\n\n // Wait for sidecar to actually start, then grab port\n const checkPort = () => {\n const addr = sidecarServer?.address();\n if (addr && typeof addr === 'object') {\n actualPort = addr.port;\n console.log(`[cc-prompter] Sidecar port: ${actualPort}`);\n }\n };\n setTimeout(checkPort, 200);\n setTimeout(checkPort, 1000);\n\n // Expose actual sidecar port to inject script\n server.middlewares.use('/__cc-port', (req, res) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.end(String(actualPort || startPort));\n });\n\n // Clean up on dev server close\n server.httpServer?.on('close', cleanup);\n process.on('SIGTERM', () => { cleanup(); process.exit(0); });\n process.on('SIGINT', () => { cleanup(); process.exit(0); });\n process.on('exit', cleanup);\n },\n\n // Only inject in dev mode (ctx.server is undefined during build)\n transformIndexHtml: {\n order: 'post',\n handler(html, ctx) {\n if (!ctx.server) return html;\n const script = getInjectScript();\n return html.replace('</body>', `<script>${script}</script></body>`);\n },\n },\n\n closeBundle() {\n cleanup();\n },\n };\n\n // Return composed plugin array\n const plugins: Plugin[] = [promptPlugin];\n if (inspectorPlugin) plugins.unshift(inspectorPlugin);\n return plugins;\n}\n","/**\n * CC Prompter — Sidecar API Server\n *\n * Express server 运行在端口 3456,管理 PTY session 生命周期。\n * 提供 REST API + SSE 流式响应。\n */\n\nimport express from 'express';\nimport { createServer, type Server } from 'http';\nimport { PtySession } from './pty-session.js';\nimport { getPanelHtml } from './assets.js';\nimport type {\n SessionInfo,\n CreateSessionRequest,\n SendMessageRequest,\n SendCommandRequest,\n SseEvent,\n} from './types.js';\n\n// ── Session Manager ─────────────────────────────────────\n\nclass SessionManager {\n private sessions = new Map<string, PtySession>();\n private counter = 0;\n\n async create(cwd: string): Promise<PtySession> {\n const id = `s${++this.counter}-${Date.now().toString(36)}`;\n const session = new PtySession(id, cwd);\n this.sessions.set(id, session);\n\n // Clean up on exit\n session.on('exit', () => {\n // Keep in map for history, but mark exited\n });\n\n await session.spawn();\n return session;\n }\n\n get(id: string): PtySession | undefined {\n return this.sessions.get(id);\n }\n\n list(): SessionInfo[] {\n return Array.from(this.sessions.values()).map(s => {\n const info = s.getInfo();\n return {\n id: info.id,\n title: info.title,\n status: info.status,\n createdAt: info.createdAt,\n lastActivityAt: info.lastActivityAt,\n messageCount: info.messageCount,\n lastMessagePreview: info.lastMessagePreview,\n };\n });\n }\n\n destroy(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n session.kill();\n this.sessions.delete(id);\n return true;\n }\n\n destroyAll(): void {\n for (const session of this.sessions.values()) {\n session.kill();\n }\n this.sessions.clear();\n }\n}\n\n// ── Sidecar Server ──────────────────────────────────────\n\nexport interface SidecarOptions {\n startPort?: number;\n}\n\nexport function startSidecar(projectRoot: string, options?: SidecarOptions): Server {\n const startPort = options?.startPort || 3456;\n const app = express();\n app.use(express.json());\n\n const manager = new SessionManager();\n\n // ── CORS for iframe ──\n app.use((req, res, next) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.sendStatus(204);\n return;\n }\n next();\n });\n\n // ── Panel HTML (served to iframe) ──\n app.get('/__panel/', (_req, res) => {\n const html = getPanelHtml();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Content-Length', Buffer.byteLength(html));\n res.end(html);\n });\n\n // ── Panel favicon (no-op) ──\n app.get('/favicon.ico', (_req, res) => {\n res.sendStatus(204);\n });\n\n // ── List sessions ──\n app.get('/api/sessions', (_req, res) => {\n res.json(manager.list());\n });\n\n // ── Create session ──\n app.post<Record<string, string>, any, CreateSessionRequest>('/api/sessions', async (req, res) => {\n try {\n const cwd = req.body?.cwd || projectRoot;\n const session = await manager.create(cwd);\n res.json(session.getInfo());\n } catch (err: any) {\n console.error('[cc-prompter] Failed to create session:', err);\n res.status(500).json({ error: err.message, stack: err.stack });\n }\n });\n\n // ── Send message (SSE stream) ──\n app.post<Record<string, string>, any, SendMessageRequest>(\n '/api/sessions/:id/message',\n async (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n if (session.status === 'exited') {\n res.status(410).json({ error: 'Session exited' });\n return;\n }\n if (session.status === 'busy') {\n res.status(409).json({ error: 'Session busy' });\n return;\n }\n\n const { content, sourceInfo } = req.body;\n if (!content) {\n res.status(400).json({ error: 'Missing content' });\n return;\n }\n\n // Build prompt with optional source context (single-line to avoid multi-line input mode)\n let prompt = content;\n if (sourceInfo) {\n const relPath = sourceInfo.path;\n const parts = [\n `[source: ${relPath}:${sourceInfo.line}:${sourceInfo.column}]`,\n ];\n if (sourceInfo.elementInfo) {\n parts.push(`[element: ${sourceInfo.elementInfo}]`);\n }\n prompt = parts.join(' ') + ' ' + content;\n }\n\n // SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n // Forward session events as SSE\n const onMessage = (evt: SseEvent) => {\n res.write(`data: ${JSON.stringify(evt)}\\n\\n`);\n\n // Stop streaming on 'done'\n if (evt.type === 'done') {\n cleanup();\n }\n };\n\n const onError = (err: Error) => {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n };\n\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n session.removeListener('message', onMessage);\n session.removeListener('error', onError);\n res.end();\n };\n\n session.on('message', onMessage);\n session.on('error', onError);\n\n // Client disconnect — delay registration to avoid premature close\n // (Express/Node HTTP can fire 'close' early on SSE connections)\n let cleanedUp = false;\n setTimeout(() => {\n req.on('close', () => {\n if (!cleanedUp) {\n cleanup();\n }\n });\n }, 3000);\n\n // Send to PTY\n try {\n await session.sendMessage(prompt);\n } catch (err: any) {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n }\n },\n );\n\n // ── Send command ──\n app.post<Record<string, string>, any, SendCommandRequest>(\n '/api/sessions/:id/command',\n (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n\n const { command } = req.body;\n if (!command) {\n res.status(400).json({ error: 'Missing command' });\n return;\n }\n\n try {\n session.sendCommand(command);\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n },\n );\n\n // ── Interrupt session (Escape key) ──\n app.post('/api/sessions/:id/interrupt', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n try {\n session.interrupt();\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n });\n\n // ── Delete session ──\n app.delete('/api/sessions/:id', (req, res) => {\n if (manager.destroy(req.params.id)) {\n res.json({ ok: true });\n } else {\n res.status(404).json({ error: 'Session not found' });\n }\n });\n\n // ── Session history ──\n app.get('/api/sessions/:id/history', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session.getHistory());\n });\n\n const server = createServer(app);\n const MAX_PORT = startPort + 10;\n\n function tryListen(port: number): Promise<Server> {\n return new Promise((resolve, reject) => {\n server.listen(port, () => {\n console.log(`[cc-prompter] Sidecar running on http://localhost:${port}`);\n resolve(server);\n });\n server.on('error', (err: any) => {\n if (err.code === 'EADDRINUSE' && port < MAX_PORT) {\n console.log(`[cc-prompter] Port ${port} in use, trying ${port + 1}...`);\n tryListen(port + 1).then(resolve, reject);\n } else {\n reject(err);\n }\n });\n });\n }\n\n // Fire-and-forget listen (returns server immediately for API compat)\n tryListen(startPort).catch((err) => {\n console.error(`[cc-prompter] Failed to start sidecar:`, err.message);\n });\n\n // Graceful shutdown\n server.on('close', () => {\n manager.destroyAll();\n });\n\n return server;\n}\n","/**\n * CC Prompter — PTY Session\n *\n * 每个实例管理一个常驻的 claude CLI 进程(通过 node-pty)。\n *\n * 输出解析策略(双通道):\n * 1. PTY 输出解析(主要)— 实时从 TUI 输出提取响应文本\n * 2. JSONL transcript(辅助)— 结构化事件,用于 tool_use 等\n *\n * 就绪检测:解析 PTY 输出中的提示符(\"for shortcuts\"、\"/effort\")。\n */\n\nimport { createRequire } from 'module';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nconst _metaUrl = typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url);\nconst require = createRequire(_metaUrl);\nimport { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { IPty } from 'node-pty-prebuilt-multiarch';\nimport type {\n SessionStatus,\n ChatMessage,\n ToolUseInfo,\n JsonlEvent,\n SseEvent,\n} from './types.js';\n\n// Try multiple PTY packages for cross-platform compatibility\nconst PTY_PACKAGES = [\n 'node-pty-prebuilt-multiarch', // macOS / Linux prebuilt\n '@homebridge/node-pty-prebuilt-multiarch', // Better Windows support\n 'node-pty', // Original (needs build tools)\n];\n\nlet _ptyModule: any = null;\nfunction loadPty(): any {\n if (_ptyModule) return _ptyModule;\n const errors: string[] = [];\n for (const pkg of PTY_PACKAGES) {\n try {\n const mod = require(pkg);\n // Verify native binary actually exists — require() can succeed without it\n const modDir = path.dirname(require.resolve(pkg + '/package.json'));\n const buildDir = path.join(modDir, 'build', 'Release');\n if (!fs.existsSync(buildDir) || fs.readdirSync(buildDir).filter(f => f.endsWith('.node')).length === 0) {\n errors.push(`${pkg}: no native binary in ${buildDir}`);\n continue;\n }\n console.log(`[cc-prompter] Loaded PTY from: ${pkg}`);\n _ptyModule = mod;\n return _ptyModule;\n } catch (err: any) {\n errors.push(`${pkg}: ${err.message}`);\n }\n }\n throw new Error(\n `No PTY module available. Tried:\\n${errors.map(e => ' ' + e).join('\\n')}\\n` +\n `Install one: npm install node-pty-prebuilt-multiarch (macOS/Linux) or @homebridge/node-pty-prebuilt-multiarch (Windows)`\n );\n}\n\n// ── Helpers ─────────────────────────────────────────────\n\nfunction resolveClaudeBin(cwd: string): { file: string; args: string[] } {\n // Windows: find node.exe + claude.js directly (bypass broken .cmd wrapper)\n if (process.platform === 'win32') {\n // Check local install first\n const localJs = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(localJs)) {\n return { file: process.execPath, args: [localJs] };\n }\n // Check global npm install (nvm4w puts global modules next to node.exe)\n const globalPrefix = path.dirname(process.execPath);\n const globalJs = path.join(globalPrefix, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(globalJs)) {\n return { file: process.execPath, args: [globalJs] };\n }\n // Check npm global prefix via APPDATA (standard npm location)\n const appDataJs = path.join(process.env.APPDATA || '', 'npm/node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(appDataJs)) {\n return { file: process.execPath, args: [appDataJs] };\n }\n // Fallback: try cmd.exe\n return { file: process.env.COMSPEC || 'cmd.exe', args: ['/c', 'claude'] };\n }\n\n // macOS/Linux\n const local = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude');\n if (fs.existsSync(local)) return { file: local, args: [] };\n return { file: 'claude', args: [] };\n}\n\nfunction findClaudeProjectsDir(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n\n/** Convert a cwd path to the Claude projects directory name */\nfunction cwdToProjectDir(cwd: string): string {\n // Normalize path separators and handle both Unix and Windows paths\n // D:/code/agentplat → -D--code-agentplat\n // /Users/ryan/demo → -Users-ryan-demo\n const normalized = cwd.replace(/\\\\/g, '/').replace(/^[A-Za-z]:/, m => '-' + m[0].toLowerCase());\n return normalized.replace(/^\\//, '').replace(/\\/+/g, '-').replace(/^-+/, '');\n}\n\n/** Scan for the most recently modified .jsonl in project dirs */\nfunction findRecentJsonl(cwd: string, afterMs: number): string | null {\n const projectsDir = findClaudeProjectsDir();\n if (!fs.existsSync(projectsDir)) return null;\n\n // Collect candidate JSONL files from target dir and all subdirectories\n const candidates: { path: string; mtime: number }[] = [];\n\n const collectFromDir = (dir: string) => {\n try {\n const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl'));\n for (const f of files) {\n const fp = path.join(dir, f);\n try {\n const stat = fs.statSync(fp);\n if (stat.mtimeMs > afterMs) {\n candidates.push({ path: fp, mtime: stat.mtimeMs });\n }\n } catch { /* skip */ }\n }\n } catch { /* dir might not exist */ }\n };\n\n // Try exact match first\n const projectSubdir = cwdToProjectDir(cwd);\n const targetDir = path.join(projectsDir, projectSubdir);\n collectFromDir(targetDir);\n\n // Fallback: scan ALL project subdirectories (handles Windows path format differences)\n if (candidates.length === 0) {\n try {\n const entries = fs.readdirSync(projectsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== projectSubdir) {\n collectFromDir(path.join(projectsDir, entry.name));\n }\n }\n } catch { /* ignore */ }\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.mtime - a.mtime);\n return candidates[0].path;\n}\n\nfunction sessionIdFromJsonlPath(jsonPath: string): string | null {\n const base = path.basename(jsonPath, '.jsonl');\n const match = base.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);\n return match ? match[0] : null;\n}\n\n/** Strip ANSI escape sequences for analysis */\nfunction stripAnsi(s: string): string {\n return s\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\].*?(?:\\x07|\\x1b\\\\)/g, '')\n .replace(/\\x1b\\[[\\?]?[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\[[0-9;]*m/g, '')\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b[^[\\]()]?.?/g, '')\n .replace(/\\r/g, '');\n}\n\n/** Strip Claude TUI spinner noise from PTY output */\nfunction stripSpinner(s: string): string {\n return s\n .replace(/[✻✶✽✢·●✳◇◆▸▹⏵⏶]+/g, '')\n .replace(/\\w{3,}ing[…\\s]*\\(\\d{1,3}s?\\)?/g, '') // \"Gesticulating… (28s)\"\n .replace(/\\w{3,}ing…/g, '') // \"Topsy-turvying…\"\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\n// ── PtySession ──────────────────────────────────────────\n\nexport class PtySession extends EventEmitter {\n readonly id: string;\n readonly cwd: string;\n status: SessionStatus = 'spawning';\n\n private pty: IPty | null = null;\n private jsonlPath: string | null = null;\n private sessionId: string | null = null;\n private history: ChatMessage[] = [];\n private jsonlOffset = 0;\n private jsonlWatcher: fs.FSWatcher | null = null;\n private spawnTime: number;\n private messageSentAt = 0;\n private busySince = 0; // timestamp when last message was sent (for grace period)\n private title = 'New Session';\n private lastActivityAt: number;\n private killed = false;\n private ptyBuffer = ''; // accumulated for prompt detection\n private jsonlDiscoverPromise: Promise<void> | null = null;\n\n // ── PTY streaming fields ──\n private busyBuffer = ''; // accumulated during busy state\n private lastUserContent = ''; // last user message text\n private ptyResponseText = ''; // extracted response text so far\n private ptyResponseEmitted = 0; // chars already emitted\n private usedJsonl = false; // JSONL events received this turn\n private ptyDoneEmitted = false;\n private lastProgress = ''; // last emitted progress text\n private interrupted = false; // set when user sends interrupt\n private lastSpinnerSec = 0; // highest spinner seconds counter seen\n private lastSpinnerAt = 0; // timestamp when spinner was last active\n private emittedTools = new Set<string>(); // dedup tool call emissions\n\n constructor(id: string, cwd: string) {\n super();\n this.id = id;\n this.cwd = cwd;\n this.spawnTime = Date.now();\n this.lastActivityAt = this.spawnTime;\n }\n\n /** Spawn the claude process via PTY */\n async spawn(): Promise<void> {\n const ptyModule = loadPty();\n const { file, args } = resolveClaudeBin(this.cwd);\n\n console.log(`[pty-session ${this.id}] spawning: ${file} ${args.join(' ')} cwd: ${this.cwd}`);\n\n this.pty = ptyModule.spawn(file, args, {\n name: 'xterm-256color',\n cols: 120,\n rows: 30,\n cwd: this.cwd,\n env: { ...process.env } as Record<string, string>,\n });\n\n console.log(`[pty-session ${this.id}] PID: ${this.pty.pid}`);\n\n // Watch PTY output\n this.pty.onData((data: string) => {\n // Debug: log first 500 chars of output to diagnose spawn failures\n if (this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] output: ${JSON.stringify(data.substring(0, 500))}`);\n }\n const clean = stripAnsi(data);\n this.ptyBuffer += clean;\n\n this.detectPrompt();\n\n // Parse for streaming response when busy and JSONL not active\n if (this.status === 'busy' && !this.usedJsonl) {\n this.busyBuffer += clean;\n this.parseBusyOutput();\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n console.log(`[pty-session ${this.id}] exited with code: ${exitCode}`);\n this.status = 'exited';\n this.lastActivityAt = Date.now();\n this.emit('exit', exitCode);\n this.cleanup();\n });\n }\n\n // ── Prompt Detection ──────────────────────────────────\n\n private detectPrompt(): void {\n const indicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n\n for (const re of indicators) {\n if (re.test(this.ptyBuffer) && this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] detected prompt → ready`);\n this.status = 'ready';\n this.emit('ready');\n return;\n }\n }\n\n // After interrupt, detect prompt returning to finish the stream\n if (this.interrupted && this.status === 'busy') {\n for (const re of indicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt after interrupt → done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n }\n\n private async waitUntilReady(timeoutMs = 30_000): Promise<void> {\n if (this.status === 'ready') return;\n if (this.status === 'exited') throw new Error('Session exited');\n\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n const timer = setInterval(() => {\n if (this.status === 'ready') {\n clearInterval(timer);\n resolve();\n } else if (this.status === 'exited') {\n clearInterval(timer);\n reject(new Error('Session exited while waiting'));\n } else if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout waiting for session to be ready'));\n }\n }, 100);\n });\n }\n\n // ── Send Message ──────────────────────────────────────\n\n async sendMessage(content: string): Promise<void> {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status === 'busy') {\n throw new Error('Session busy');\n }\n\n // Wait for claude prompt to be ready\n if (this.status !== 'ready') {\n console.log(`[pty-session ${this.id}] waiting for prompt before sending message...`);\n await this.waitUntilReady();\n }\n\n await new Promise(r => setTimeout(r, 200));\n\n this.status = 'busy';\n this.lastActivityAt = Date.now();\n this.busySince = Date.now();\n\n // Reset streaming state for this turn\n this.busyBuffer = '';\n this.ptyBuffer = ''; // Clear accumulated prompt text to avoid false done detection\n this.lastUserContent = content;\n this.ptyResponseText = '';\n this.ptyResponseEmitted = 0;\n this.usedJsonl = false;\n this.ptyDoneEmitted = false;\n this.lastProgress = '';\n this.interrupted = false;\n this.lastSpinnerSec = 0;\n this.lastSpinnerAt = 0;\n this.emittedTools.clear();\n\n // JSONL discovery in background\n if (!this.messageSentAt) {\n this.messageSentAt = Date.now();\n console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);\n this.jsonlDiscoverPromise = this.discoverJsonl();\n }\n\n console.log(`[pty-session ${this.id}] writing to PTY: ${JSON.stringify(content.slice(0, 100))}`);\n\n // Write content + double Enter for reliable submission\n this.pty.write(content + '\\r');\n await new Promise(r => setTimeout(r, 150));\n this.pty.write('\\r');\n }\n\n /** Send a slash command to the PTY */\n sendCommand(command: string): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n\n this.pty.write(command + '\\r');\n\n if (command === '/new') {\n this.history = [];\n this.title = 'New Session';\n this.jsonlPath = null;\n this.jsonlOffset = 0;\n this.jsonlWatcher?.close();\n this.jsonlWatcher = null;\n this.sessionId = null;\n this.messageSentAt = 0;\n this.status = 'ready';\n this.ptyBuffer = '';\n }\n }\n\n /** Send Escape to PTY to interrupt current generation */\n interrupt(): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status !== 'busy') return;\n this.interrupted = true;\n this.pty.write('\\x1b');\n\n // Safety timeout: if prompt detection fails, force finish after 5s\n setTimeout(() => {\n if (this.interrupted && this.status === 'busy') {\n console.log(`[pty-session ${this.id}] interrupt timeout → force done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n }\n }, 5000);\n }\n\n // ── PTY Output Parsing (streaming fallback) ───────────\n\n /**\n * Parse PTY output during busy state to extract streaming response.\n *\n * Claude Code TUI patterns:\n * - Spinner frames: ✳ ✶ ✻ ✽ ✢ · (ignore — just animation)\n * - Response text: ⏺<text> or ●<text>\n * - Tool use: ⚡<tool_name> or ✢ editing <file>\n * - Completion: \"Brewed for Xs\" (ONLY reliable indicator)\n * - ⚠️ ❯ appears in input echo too — NOT a completion signal!\n * - Timing: (Xs · ↓NNN tokens)\n */\n private parseBusyOutput(): void {\n // ── 0. Sliding window — prevent unbounded buffer growth from spinner frames ──\n // Windows ConPTY sends character-by-character spinner updates, causing massive buffers.\n // Keep only the last 2000 chars so completion markers and recent tool calls stay visible.\n if (this.busyBuffer.length > 3000) {\n this.busyBuffer = this.busyBuffer.slice(-2000);\n }\n\n // ── 1. Extract progress for UI feedback ──\n this.emitProgress();\n\n // ── 2. Track spinner activity (detect Claude still working) ──\n // Extract seconds counter from spinner text: \"Gesticulating… (28s)\", \"Seasoning… (21s)\"\n // On Windows, these verbs are random (Gesticulating, Seasoning, Topsy-turvying, etc.)\n const secMatches = [...this.busyBuffer.matchAll(/\\w{3,}ing[…\\s]*\\((\\d{1,3})s?/g)];\n for (const m of secMatches) {\n const sec = parseInt(m[1]);\n if (sec > this.lastSpinnerSec) {\n this.lastSpinnerSec = sec;\n this.lastSpinnerAt = Date.now();\n }\n }\n\n // ── 3. Try to extract response text ──\n const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);\n if (respMatch) {\n let raw = respMatch[1];\n raw = raw.replace(/[✳✶✻✽✢·].*$/s, '').trim();\n raw = raw.replace(/─{3,}.*$/s, '').trim();\n raw = raw.replace(/\\w{3,}ed (?:for|in) \\d{1,4}s?.*$/s, '').trim();\n raw = raw.replace(/esctointerrupt.*$/s, '').trim();\n\n if (raw.length > this.ptyResponseText.length) {\n this.ptyResponseText = raw;\n this.emitIncrementalText();\n }\n }\n\n // ── 4. Extract tool calls (with dedup to prevent infinite re-emission) ──\n // macOS format: ⏺ Read(file) | Windows format: ● Reading 1 file… ⎿ file\n const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/);\n if (toolCallMatch) {\n const sig = `${toolCallMatch[1]}:${toolCallMatch[2]}`;\n if (!this.emittedTools.has(sig)) {\n this.emittedTools.add(sig);\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: toolCallMatch[1], input: { file: toolCallMatch[2] } },\n } as SseEvent);\n }\n }\n // Windows format: \"● Reading 1 file…\\n ⎿ src\\\\components\\\\Foo.tsx\"\n const winToolMatch = this.busyBuffer.match(/● (Reading|Editing|Writing|Searching|Bashing)[^\\n]*\\n\\s*⎿\\s*(.+)/);\n if (!toolCallMatch && winToolMatch) {\n const filePath = winToolMatch[2].trim();\n const sig = `${winToolMatch[1]}:${filePath}`;\n if (!this.emittedTools.has(sig)) {\n this.emittedTools.add(sig);\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: winToolMatch[1], input: { file: filePath } },\n } as SseEvent);\n }\n }\n\n // ── 5. Detect completion ──\n if (this.ptyDoneEmitted) return;\n\n // 5a. Explicit completion marker: \"Brewed for Xs\", \"Sautéed for Xs\", etc.\n // Use broad pattern since Claude uses random creative verbs.\n const cleanBuf = stripSpinner(this.busyBuffer);\n const hasDoneMarker = /\\b\\w+ed (?:for|in) \\d{1,4}s?\\b/i.test(cleanBuf);\n\n // 5b. Spinner timeout: spinner was active, then stopped for 10+ seconds.\n // On Windows ConPTY, Claude's TUI sends spinner frames continuously while working.\n // If spinner stops, Claude has finished (or errored out).\n const spinnerTimeout = this.lastSpinnerAt > 0\n && Date.now() - this.lastSpinnerAt > 10000\n && this.ptyResponseEmitted > 0;\n\n if (hasDoneMarker || spinnerTimeout) {\n const reason = hasDoneMarker\n ? 'completion marker'\n : `spinner timeout (${Math.round((Date.now() - this.lastSpinnerAt) / 1000)}s since last activity)`;\n console.log(`[pty-session ${this.id}] detected done via ${reason}`);\n this.ptyDoneEmitted = true;\n\n // Final flush: emit any remaining text\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // If no response was emitted at all, try one more aggressive extraction\n if (this.ptyResponseEmitted === 0) {\n const finalMatch = this.busyBuffer.match(/⏺(.+)/s);\n if (finalMatch) {\n let text = finalMatch[1]\n .replace(/[✳✶✻✽✢·].*$/s, '')\n .replace(/─{3,}.*$/s, '')\n .replace(/\\w{3,}ed (?:for|in) \\d{1,4}s?.*$/s, '')\n .replace(/esctointerrupt.*$/s, '')\n .trim();\n if (text.length > 0) {\n this.emitUserIfNeeded();\n this.history.push({\n role: 'assistant', content: text, timestamp: Date.now(),\n });\n this.emit('message', { type: 'assistant_text', content: text } as SseEvent);\n this.ptyResponseEmitted = text.length;\n }\n }\n }\n\n // Extract duration\n const durMatch = cleanBuf.match(/\\w+ed (?:for|in) (\\d{1,4})s?/i);\n const durationMs = durMatch ? parseInt(durMatch[1]) * 1000 : 0;\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs } as SseEvent);\n }\n }\n\n /** Emit only the newly arrived characters (incremental streaming) */\n private emitIncrementalText(): void {\n const newText = this.ptyResponseText.slice(this.ptyResponseEmitted);\n if (newText.length === 0) return;\n\n // First chunk → emit user message + start assistant\n if (this.ptyResponseEmitted === 0) {\n this.emitUserIfNeeded();\n }\n\n this.ptyResponseEmitted = this.ptyResponseText.length;\n this.emit('message', { type: 'assistant_text', content: newText } as SseEvent);\n }\n\n /**\n * Extract and emit progress updates from busyBuffer.\n *\n * Parses PTY output for Claude Code's progress indicators:\n * - \"Thinking for Xs, reading N files\"\n * - \"Thought for Xs, read N files\"\n * - \"Crafting… (Xs · ↓NN tokens)\"\n * - \"Update(file)\" / \"Read(file)\"\n * - \"⎿ Removed N lines\"\n * - \"(Xs · ↓NN tokens)\" timing\n */\n private emitProgress(): void {\n // Strip spinner chars + noise, collapse whitespace\n const text = this.busyBuffer\n .replace(/[✳✶✻✽✢·][a-zA-Z0-9…]{0,4}/g, '')\n .replace(/\\s+/g, ' ');\n\n // Ordered by specificity — last match wins (most recent progress)\n const patterns: [RegExp, (m: RegExpMatchArray) => string][] = [\n // Thinking phase\n [/Thinking for (\\d+s)[^─]{0,60}(reading \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Thought completed\n [/Thought for (\\d+s)[^─]{0,60}(read \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Tool call: Update(file) / Read(file)\n [/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/, (m) => {\n return m[1] + ': ' + m[2].split('/').slice(-2).join('/');\n }],\n // Tool result: ⎿ Removed N lines\n [/⎿\\s*(Removed|Added|Modified|Created)\\s+(\\d+)\\s+(lines?)/, (m) => {\n return m[1] + ' ' + m[2] + ' ' + m[3];\n }],\n // Crafting with timing\n [/Crafting[^─]{0,30}\\(\\d+s[^)]*\\)/, (m) => {\n return m[0].replace(/\\s+/g, ' ').trim();\n }],\n // Simple timing: (5s · ↓9 tokens)\n [/\\((\\d+s)\\s*·\\s*[↓↑]\\s*(\\d+)\\s*tokens?\\)/, (m) => {\n return m[1] + ' · ' + m[2] + ' tokens';\n }],\n ];\n\n // Find the last matching pattern\n let progress = '';\n for (const [re, fn] of patterns) {\n const m = text.match(re);\n if (m) progress = fn(m);\n }\n\n // Emit only if changed\n if (progress && progress !== this.lastProgress) {\n this.lastProgress = progress;\n this.emit('message', { type: 'progress', content: progress } as SseEvent);\n }\n }\n\n /** Emit user message event (only if not already emitted this turn) */\n private emitUserIfNeeded(): void {\n if (!this.lastUserContent) return;\n // Check if user message already in history\n const already = this.history.some(\n m => m.role === 'user' && m.content === this.lastUserContent,\n );\n if (!already) {\n this.history.push({\n role: 'user',\n content: this.lastUserContent,\n timestamp: Date.now(),\n });\n this.emit('message', { type: 'user', content: this.lastUserContent } as SseEvent);\n }\n }\n\n // ── JSONL Discovery & Parsing (structured events) ─────\n\n private async discoverJsonl(): Promise<void> {\n const searchStart = this.messageSentAt - 2000;\n const projectsDir = findClaudeProjectsDir();\n const expectedSubdir = cwdToProjectDir(this.cwd);\n console.log(`[pty-session ${this.id}] JSONL discovery: projectsDir=${projectsDir}, expected=${expectedSubdir}, cwd=${this.cwd}`);\n\n // Poll for up to 60 seconds (120 × 500ms)\n for (let i = 0; i < 120; i++) {\n if (this.killed) return;\n const jsonl = findRecentJsonl(this.cwd, searchStart);\n if (jsonl) {\n this.jsonlPath = jsonl;\n this.sessionId = sessionIdFromJsonlPath(jsonl) || null;\n console.log(`[pty-session ${this.id}] found JSONL: ${jsonl} (session: ${this.sessionId})`);\n this.startTailingJsonl();\n return;\n }\n // Log available dirs after 5s for debugging\n if (i === 10) {\n try {\n if (fs.existsSync(projectsDir)) {\n const dirs = fs.readdirSync(projectsDir, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name);\n console.log(`[pty-session ${this.id}] JSONL not found in expected dir. Available project dirs: ${dirs.join(', ')}`);\n } else {\n console.log(`[pty-session ${this.id}] projectsDir does not exist: ${projectsDir}`);\n }\n } catch { /* ignore */ }\n }\n await new Promise(r => setTimeout(r, 500));\n }\n console.warn(`[pty-session ${this.id}] JSONL not found after 60s — using PTY output parsing`);\n }\n\n private startTailingJsonl(): void {\n if (!this.jsonlPath) return;\n\n this.readNewJsonlLines();\n\n try {\n this.jsonlWatcher = fs.watch(\n path.dirname(this.jsonlPath),\n (eventType, filename) => {\n if (filename === path.basename(this.jsonlPath!)) {\n this.readNewJsonlLines();\n }\n },\n );\n } catch (err) {\n console.warn(`[pty-session ${this.id}] fs.watch failed:`, err);\n }\n }\n\n private readNewJsonlLines(): void {\n if (!this.jsonlPath) return;\n\n try {\n const stat = fs.statSync(this.jsonlPath);\n if (stat.size <= this.jsonlOffset) return;\n\n const fd = fs.openSync(this.jsonlPath, 'r');\n const buf = Buffer.alloc(stat.size - this.jsonlOffset);\n fs.readSync(fd, buf, 0, buf.length, this.jsonlOffset);\n fs.closeSync(fd);\n this.jsonlOffset = stat.size;\n\n const text = buf.toString('utf8');\n for (const line of text.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const evt: JsonlEvent = JSON.parse(trimmed);\n this.processJsonlEvent(evt);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // file might be temporarily unavailable\n }\n }\n\n /** Process a JSONL event — marks usedJsonl to disable PTY parsing */\n private processJsonlEvent(evt: JsonlEvent): void {\n // Once JSONL events arrive, disable PTY output parsing\n this.usedJsonl = true;\n\n if (!this.sessionId && evt.sessionId) {\n this.sessionId = evt.sessionId;\n }\n\n switch (evt.type) {\n case 'user': {\n const text = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n : '';\n this.history.push({\n role: 'user',\n content: text,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'user', content: text } as SseEvent);\n if (this.history.filter(m => m.role === 'user').length === 1 && text) {\n this.title = text.slice(0, 60);\n this.emit('title-change', this.title);\n }\n break;\n }\n case 'assistant': {\n const content = evt.message?.content;\n if (!Array.isArray(content)) break;\n\n const texts = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n .trim();\n\n const tools: ToolUseInfo[] = content\n .filter((c: any) => c.type === 'tool_use')\n .map((c: any) => ({\n name: c.name,\n input: c.input || {},\n }));\n\n if (texts || tools.length) {\n this.history.push({\n role: 'assistant',\n content: texts,\n toolUse: tools.length ? tools : undefined,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n }\n\n if (texts) {\n this.emit('message', { type: 'assistant_text', content: texts } as SseEvent);\n }\n for (const t of tools) {\n this.emit('message', { type: 'assistant_tool', tool: t } as SseEvent);\n }\n break;\n }\n case 'system': {\n if (evt.subtype === 'tool_result') {\n const lastAssistant = [...this.history].reverse().find(m => m.role === 'assistant' && m.toolUse?.length);\n if (lastAssistant?.toolUse?.length) {\n const lastTool = lastAssistant.toolUse[lastAssistant.toolUse.length - 1];\n const resultText = typeof evt.message?.content === 'string'\n ? evt.message.content : '';\n lastTool.result = resultText.slice(0, 500);\n }\n const resultContent = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content.map((c: any) => c.text || '').join('')\n : '';\n this.emit('message', { type: 'system', content: resultContent.slice(0, 200) } as SseEvent);\n } else if (evt.subtype === 'turn_duration') {\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: evt.durationMs } as SseEvent);\n }\n break;\n }\n }\n }\n\n // ── Lifecycle ─────────────────────────────────────────\n\n kill(): void {\n this.killed = true;\n this.cleanup();\n if (this.pty) {\n try { this.pty.kill(); } catch { /* already dead */ }\n this.pty = null;\n }\n this.status = 'exited';\n }\n\n private cleanup(): void {\n if (this.jsonlWatcher) {\n this.jsonlWatcher.close();\n this.jsonlWatcher = null;\n }\n }\n\n getInfo(): {\n id: string;\n title: string;\n status: SessionStatus;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n lastMessagePreview: string;\n sessionId: string | null;\n } {\n const lastMsg = this.history[this.history.length - 1];\n return {\n id: this.id,\n title: this.title,\n status: this.status,\n createdAt: this.spawnTime,\n lastActivityAt: this.lastActivityAt,\n messageCount: this.history.length,\n lastMessagePreview: lastMsg ? lastMsg.content.slice(0, 80) : '',\n sessionId: this.sessionId,\n };\n }\n\n getHistory(): ChatMessage[] {\n return [...this.history];\n }\n}\n","/**\n * CC Prompter — Asset Loader\n *\n * 运行时从包根目录读取 panel.html 和 inject.js。\n * 兼容 ESM(import.meta.url)和 CJS(__dirname)。\n *\n * 解析策略:\n * - tsup 构建后:资产文件复制到 dist/,__dirname 是 dist/\n * - Vite 直接引用 TS:__dirname 是 src/,资产文件在上一级\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\n// Resolve __dirname in both ESM and CJS contexts\nconst _dirname = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\n/** Resolve asset path — checks dist/ (build) then parent (dev) */\nfunction assetPath(filename: string): string {\n // 1. Same dir as this file (dist/ after build, or src/ in dev)\n const here = join(_dirname, filename);\n if (existsSync(here)) return here;\n\n // 2. Parent dir (package root — when running from src/ via vite)\n const parent = join(_dirname, '..', filename);\n if (existsSync(parent)) return parent;\n\n // 3. Fallback — let it throw with a clear message\n throw new Error(\n `[cc-prompter] Asset not found: ${filename}\\n` +\n ` Tried: ${here}\\n` +\n ` Tried: ${parent}\\n` +\n ` __dirname: ${_dirname}`\n );\n}\n\nexport function getPanelHtml(): string {\n return readFileSync(assetPath('panel.html'), 'utf8');\n}\n\nexport function getInjectScript(): string {\n return readFileSync(assetPath('inject.js'), 'utf8');\n}\n"],"mappings":"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","names":["require","resolve","import_url","import_meta","express","resolve"]}
package/dist/index.js CHANGED
@@ -78,25 +78,41 @@ function cwdToProjectDir(cwd) {
78
78
  }
79
79
  function findRecentJsonl(cwd, afterMs) {
80
80
  const projectsDir = findClaudeProjectsDir();
81
+ if (!fs.existsSync(projectsDir)) return null;
82
+ const candidates = [];
83
+ const collectFromDir = (dir) => {
84
+ try {
85
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith(".jsonl"));
86
+ for (const f of files) {
87
+ const fp = path.join(dir, f);
88
+ try {
89
+ const stat = fs.statSync(fp);
90
+ if (stat.mtimeMs > afterMs) {
91
+ candidates.push({ path: fp, mtime: stat.mtimeMs });
92
+ }
93
+ } catch {
94
+ }
95
+ }
96
+ } catch {
97
+ }
98
+ };
81
99
  const projectSubdir = cwdToProjectDir(cwd);
82
100
  const targetDir = path.join(projectsDir, projectSubdir);
83
- if (!fs.existsSync(targetDir)) return null;
84
- const files = fs.readdirSync(targetDir).filter((f) => f.endsWith(".jsonl"));
85
- let best = null;
86
- for (const f of files) {
87
- const fp = path.join(targetDir, f);
101
+ collectFromDir(targetDir);
102
+ if (candidates.length === 0) {
88
103
  try {
89
- const stat = fs.statSync(fp);
90
- if (stat.mtimeMs > afterMs) {
91
- if (!best || stat.mtimeMs > best.mtime) {
92
- best = { path: fp, mtime: stat.mtimeMs };
104
+ const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
105
+ for (const entry of entries) {
106
+ if (entry.isDirectory() && entry.name !== projectSubdir) {
107
+ collectFromDir(path.join(projectsDir, entry.name));
93
108
  }
94
109
  }
95
110
  } catch {
96
- continue;
97
111
  }
98
112
  }
99
- return best?.path || null;
113
+ if (candidates.length === 0) return null;
114
+ candidates.sort((a, b) => b.mtime - a.mtime);
115
+ return candidates[0].path;
100
116
  }
101
117
  function sessionIdFromJsonlPath(jsonPath) {
102
118
  const base = path.basename(jsonPath, ".jsonl");
@@ -106,6 +122,9 @@ function sessionIdFromJsonlPath(jsonPath) {
106
122
  function stripAnsi(s) {
107
123
  return s.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\].*?(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[\?]?[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\[[0-9;]*m/g, "").replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b[^[\]()]?.?/g, "").replace(/\r/g, "");
108
124
  }
125
+ function stripSpinner(s) {
126
+ return s.replace(/[✻✶✽✢·●✳◇◆▸▹⏵⏶]+/g, "").replace(/\w{3,}ing[…\s]*\(\d{1,3}s?\)?/g, "").replace(/\w{3,}ing…/g, "").replace(/\s{2,}/g, " ").trim();
127
+ }
109
128
  var PtySession = class extends EventEmitter {
110
129
  id;
111
130
  cwd;
@@ -142,6 +161,12 @@ var PtySession = class extends EventEmitter {
142
161
  // last emitted progress text
143
162
  interrupted = false;
144
163
  // set when user sends interrupt
164
+ lastSpinnerSec = 0;
165
+ // highest spinner seconds counter seen
166
+ lastSpinnerAt = 0;
167
+ // timestamp when spinner was last active
168
+ emittedTools = /* @__PURE__ */ new Set();
169
+ // dedup tool call emissions
145
170
  constructor(id, cwd) {
146
171
  super();
147
172
  this.id = id;
@@ -255,6 +280,9 @@ var PtySession = class extends EventEmitter {
255
280
  this.ptyDoneEmitted = false;
256
281
  this.lastProgress = "";
257
282
  this.interrupted = false;
283
+ this.lastSpinnerSec = 0;
284
+ this.lastSpinnerAt = 0;
285
+ this.emittedTools.clear();
258
286
  if (!this.messageSentAt) {
259
287
  this.messageSentAt = Date.now();
260
288
  console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);
@@ -316,14 +344,24 @@ var PtySession = class extends EventEmitter {
316
344
  * - Timing: (Xs · ↓NNN tokens)
317
345
  */
318
346
  parseBusyOutput() {
347
+ if (this.busyBuffer.length > 3e3) {
348
+ this.busyBuffer = this.busyBuffer.slice(-2e3);
349
+ }
319
350
  this.emitProgress();
351
+ const secMatches = [...this.busyBuffer.matchAll(/\w{3,}ing[…\s]*\((\d{1,3})s?/g)];
352
+ for (const m of secMatches) {
353
+ const sec = parseInt(m[1]);
354
+ if (sec > this.lastSpinnerSec) {
355
+ this.lastSpinnerSec = sec;
356
+ this.lastSpinnerAt = Date.now();
357
+ }
358
+ }
320
359
  const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);
321
360
  if (respMatch) {
322
361
  let raw = respMatch[1];
323
362
  raw = raw.replace(/[✳✶✻✽✢·].*$/s, "").trim();
324
363
  raw = raw.replace(/─{3,}.*$/s, "").trim();
325
- raw = raw.replace(/Brewed for.*$/s, "").trim();
326
- raw = raw.replace(/Sautéed for.*$/s, "").trim();
364
+ raw = raw.replace(/\w{3,}ed (?:for|in) \d{1,4}s?.*$/s, "").trim();
327
365
  raw = raw.replace(/esctointerrupt.*$/s, "").trim();
328
366
  if (raw.length > this.ptyResponseText.length) {
329
367
  this.ptyResponseText = raw;
@@ -332,14 +370,34 @@ var PtySession = class extends EventEmitter {
332
370
  }
333
371
  const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\(([^)]+)\)/);
334
372
  if (toolCallMatch) {
335
- const toolName = toolCallMatch[1];
336
- const filePath = toolCallMatch[2];
337
- this.emit("message", {
338
- type: "assistant_tool",
339
- tool: { name: toolName, input: { file: filePath } }
340
- });
373
+ const sig = `${toolCallMatch[1]}:${toolCallMatch[2]}`;
374
+ if (!this.emittedTools.has(sig)) {
375
+ this.emittedTools.add(sig);
376
+ this.emit("message", {
377
+ type: "assistant_tool",
378
+ tool: { name: toolCallMatch[1], input: { file: toolCallMatch[2] } }
379
+ });
380
+ }
381
+ }
382
+ const winToolMatch = this.busyBuffer.match(/● (Reading|Editing|Writing|Searching|Bashing)[^\n]*\n\s*⎿\s*(.+)/);
383
+ if (!toolCallMatch && winToolMatch) {
384
+ const filePath = winToolMatch[2].trim();
385
+ const sig = `${winToolMatch[1]}:${filePath}`;
386
+ if (!this.emittedTools.has(sig)) {
387
+ this.emittedTools.add(sig);
388
+ this.emit("message", {
389
+ type: "assistant_tool",
390
+ tool: { name: winToolMatch[1], input: { file: filePath } }
391
+ });
392
+ }
341
393
  }
342
- if (!this.ptyDoneEmitted && /(?:Brewed|Sautéed) for/.test(this.busyBuffer)) {
394
+ if (this.ptyDoneEmitted) return;
395
+ const cleanBuf = stripSpinner(this.busyBuffer);
396
+ const hasDoneMarker = /\b\w+ed (?:for|in) \d{1,4}s?\b/i.test(cleanBuf);
397
+ const spinnerTimeout = this.lastSpinnerAt > 0 && Date.now() - this.lastSpinnerAt > 1e4 && this.ptyResponseEmitted > 0;
398
+ if (hasDoneMarker || spinnerTimeout) {
399
+ const reason = hasDoneMarker ? "completion marker" : `spinner timeout (${Math.round((Date.now() - this.lastSpinnerAt) / 1e3)}s since last activity)`;
400
+ console.log(`[pty-session ${this.id}] detected done via ${reason}`);
343
401
  this.ptyDoneEmitted = true;
344
402
  if (this.ptyResponseText.length > this.ptyResponseEmitted) {
345
403
  this.emitIncrementalText();
@@ -347,7 +405,7 @@ var PtySession = class extends EventEmitter {
347
405
  if (this.ptyResponseEmitted === 0) {
348
406
  const finalMatch = this.busyBuffer.match(/⏺(.+)/s);
349
407
  if (finalMatch) {
350
- let text = finalMatch[1].replace(/[✳✶✻✽✢·].*$/s, "").replace(/─{3,}.*$/s, "").replace(/Brewed for.*$/s, "").replace(/esctointerrupt.*$/s, "").trim();
408
+ let text = finalMatch[1].replace(/[✳✶✻✽✢·].*$/s, "").replace(/─{3,}.*$/s, "").replace(/\w{3,}ed (?:for|in) \d{1,4}s?.*$/s, "").replace(/esctointerrupt.*$/s, "").trim();
351
409
  if (text.length > 0) {
352
410
  this.emitUserIfNeeded();
353
411
  this.history.push({
@@ -360,7 +418,7 @@ var PtySession = class extends EventEmitter {
360
418
  }
361
419
  }
362
420
  }
363
- const durMatch = this.busyBuffer.match(/Brewed for (\d+)s/);
421
+ const durMatch = cleanBuf.match(/\w+ed (?:for|in) (\d{1,4})s?/i);
364
422
  const durationMs = durMatch ? parseInt(durMatch[1]) * 1e3 : 0;
365
423
  if (this.history.filter((m) => m.role === "user").length <= 1 && this.lastUserContent) {
366
424
  this.title = this.lastUserContent.slice(0, 60);
@@ -370,33 +428,6 @@ var PtySession = class extends EventEmitter {
370
428
  this.lastActivityAt = Date.now();
371
429
  this.emit("message", { type: "done", durationMs });
372
430
  }
373
- if (!this.ptyDoneEmitted && this.status === "busy" && Date.now() - this.busySince > 5e3 && this.ptyResponseEmitted > 0) {
374
- const promptIndicators = [
375
- /for shortcuts/,
376
- /\/effort/,
377
- /refactor/
378
- ];
379
- for (const re of promptIndicators) {
380
- if (re.test(this.ptyBuffer)) {
381
- console.log(`[pty-session ${this.id}] detected prompt return \u2192 done (fallback)`);
382
- this.ptyDoneEmitted = true;
383
- if (this.ptyResponseText.length > this.ptyResponseEmitted) {
384
- this.emitIncrementalText();
385
- }
386
- if (this.history.filter((m) => m.role === "user").length <= 1 && this.lastUserContent) {
387
- this.title = this.lastUserContent.slice(0, 60);
388
- this.emit("title-change", this.title);
389
- }
390
- this.status = "ready";
391
- this.lastActivityAt = Date.now();
392
- this.emit("message", { type: "done", durationMs: 0 });
393
- return;
394
- }
395
- }
396
- }
397
- if (!this.ptyDoneEmitted && this.status === "busy" && Date.now() - this.busySince > 1e4 && Math.random() < 0.02) {
398
- console.log(`[pty-session ${this.id}] still busy after ${Math.round((Date.now() - this.busySince) / 1e3)}s, buffer tail: ${JSON.stringify(this.busyBuffer.slice(-300))}`);
399
- }
400
431
  }
401
432
  /** Emit only the newly arrived characters (incremental streaming) */
402
433
  emitIncrementalText() {
@@ -475,6 +506,9 @@ var PtySession = class extends EventEmitter {
475
506
  // ── JSONL Discovery & Parsing (structured events) ─────
476
507
  async discoverJsonl() {
477
508
  const searchStart = this.messageSentAt - 2e3;
509
+ const projectsDir = findClaudeProjectsDir();
510
+ const expectedSubdir = cwdToProjectDir(this.cwd);
511
+ console.log(`[pty-session ${this.id}] JSONL discovery: projectsDir=${projectsDir}, expected=${expectedSubdir}, cwd=${this.cwd}`);
478
512
  for (let i = 0; i < 120; i++) {
479
513
  if (this.killed) return;
480
514
  const jsonl = findRecentJsonl(this.cwd, searchStart);
@@ -485,6 +519,17 @@ var PtySession = class extends EventEmitter {
485
519
  this.startTailingJsonl();
486
520
  return;
487
521
  }
522
+ if (i === 10) {
523
+ try {
524
+ if (fs.existsSync(projectsDir)) {
525
+ const dirs = fs.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
526
+ console.log(`[pty-session ${this.id}] JSONL not found in expected dir. Available project dirs: ${dirs.join(", ")}`);
527
+ } else {
528
+ console.log(`[pty-session ${this.id}] projectsDir does not exist: ${projectsDir}`);
529
+ }
530
+ } catch {
531
+ }
532
+ }
488
533
  await new Promise((r) => setTimeout(r, 500));
489
534
  }
490
535
  console.warn(`[pty-session ${this.id}] JSONL not found after 60s \u2014 using PTY output parsing`);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vite-plugin.ts","../src/sidecar.ts","../src/pty-session.ts","../src/assets.ts"],"sourcesContent":["/**\n * CC Prompter — Vite Plugin\n *\n * 为 Vite dev server 提供:\n * 1. 内置 code-inspector-plugin(Shift+Alt 悬停定位源码)\n * 2. 启动 Sidecar Express server 管理 PTY sessions(自动选择空闲端口)\n * 3. 注入轻量脚本监听 code-inspector 事件,弹出 Claude 面板\n * 4. 提供 /__panel/ HTML 给 iframe\n *\n * 仅在 dev 模式生效,build 时不注入脚本。\n *\n * 用法:plugins: [ccPromptPlugin()]\n * 等同于同时配置 codeInspectorPlugin + cc-prompt-plugin。\n */\n\nimport type { Plugin } from 'vite';\nimport type { Server } from 'http';\nimport { codeInspectorPlugin } from 'code-inspector-plugin';\nimport { startSidecar } from './sidecar.js';\nimport { getPanelHtml, getInjectScript } from './assets.js';\n\nexport interface CcPromptOptions {\n /** Sidecar 启动端口,默认 3456(被占用时自动 +1) */\n port?: number;\n /** 项目根目录,默认 vite config.root */\n root?: string;\n /** 是否启用 code-inspector,默认 true */\n inspector?: boolean;\n}\n\nexport function ccPromptPlugin(options?: CcPromptOptions): Plugin[] {\n const enableInspector = options?.inspector !== false;\n let projectRoot = process.cwd();\n let sidecarServer: Server | null = null;\n let actualPort = 0;\n\n const startPort = options?.port || 3456;\n\n let cleanedUp = false;\n function cleanup() {\n if (cleanedUp) return;\n cleanedUp = true;\n if (sidecarServer) {\n sidecarServer.close();\n sidecarServer = null;\n }\n }\n\n // ── 1. code-inspector plugin ──\n const inspectorPlugin = enableInspector\n ? codeInspectorPlugin({\n bundler: 'vite',\n behavior: {\n locate: false,\n copy: false,\n },\n hideDomPathAttr: true,\n hideConsole: true,\n })\n : null;\n\n // ── 2. cc-prompter plugin ──\n const promptPlugin: Plugin = {\n name: 'cc-prompt-plugin',\n\n configResolved(config) {\n projectRoot = options?.root || config.root;\n },\n\n configureServer(server) {\n // Start sidecar — auto-picks available port\n sidecarServer = startSidecar(projectRoot, { startPort });\n\n // Wait for sidecar to actually start, then grab port\n const checkPort = () => {\n const addr = sidecarServer?.address();\n if (addr && typeof addr === 'object') {\n actualPort = addr.port;\n console.log(`[cc-prompter] Sidecar port: ${actualPort}`);\n }\n };\n setTimeout(checkPort, 200);\n setTimeout(checkPort, 1000);\n\n // Expose actual sidecar port to inject script\n server.middlewares.use('/__cc-port', (req, res) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.end(String(actualPort || startPort));\n });\n\n // Clean up on dev server close\n server.httpServer?.on('close', cleanup);\n process.on('SIGTERM', () => { cleanup(); process.exit(0); });\n process.on('SIGINT', () => { cleanup(); process.exit(0); });\n process.on('exit', cleanup);\n },\n\n // Only inject in dev mode (ctx.server is undefined during build)\n transformIndexHtml: {\n order: 'post',\n handler(html, ctx) {\n if (!ctx.server) return html;\n const script = getInjectScript();\n return html.replace('</body>', `<script>${script}</script></body>`);\n },\n },\n\n closeBundle() {\n cleanup();\n },\n };\n\n // Return composed plugin array\n const plugins: Plugin[] = [promptPlugin];\n if (inspectorPlugin) plugins.unshift(inspectorPlugin);\n return plugins;\n}\n","/**\n * CC Prompter — Sidecar API Server\n *\n * Express server 运行在端口 3456,管理 PTY session 生命周期。\n * 提供 REST API + SSE 流式响应。\n */\n\nimport express from 'express';\nimport { createServer, type Server } from 'http';\nimport { PtySession } from './pty-session.js';\nimport { getPanelHtml } from './assets.js';\nimport type {\n SessionInfo,\n CreateSessionRequest,\n SendMessageRequest,\n SendCommandRequest,\n SseEvent,\n} from './types.js';\n\n// ── Session Manager ─────────────────────────────────────\n\nclass SessionManager {\n private sessions = new Map<string, PtySession>();\n private counter = 0;\n\n async create(cwd: string): Promise<PtySession> {\n const id = `s${++this.counter}-${Date.now().toString(36)}`;\n const session = new PtySession(id, cwd);\n this.sessions.set(id, session);\n\n // Clean up on exit\n session.on('exit', () => {\n // Keep in map for history, but mark exited\n });\n\n await session.spawn();\n return session;\n }\n\n get(id: string): PtySession | undefined {\n return this.sessions.get(id);\n }\n\n list(): SessionInfo[] {\n return Array.from(this.sessions.values()).map(s => {\n const info = s.getInfo();\n return {\n id: info.id,\n title: info.title,\n status: info.status,\n createdAt: info.createdAt,\n lastActivityAt: info.lastActivityAt,\n messageCount: info.messageCount,\n lastMessagePreview: info.lastMessagePreview,\n };\n });\n }\n\n destroy(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n session.kill();\n this.sessions.delete(id);\n return true;\n }\n\n destroyAll(): void {\n for (const session of this.sessions.values()) {\n session.kill();\n }\n this.sessions.clear();\n }\n}\n\n// ── Sidecar Server ──────────────────────────────────────\n\nexport interface SidecarOptions {\n startPort?: number;\n}\n\nexport function startSidecar(projectRoot: string, options?: SidecarOptions): Server {\n const startPort = options?.startPort || 3456;\n const app = express();\n app.use(express.json());\n\n const manager = new SessionManager();\n\n // ── CORS for iframe ──\n app.use((req, res, next) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.sendStatus(204);\n return;\n }\n next();\n });\n\n // ── Panel HTML (served to iframe) ──\n app.get('/__panel/', (_req, res) => {\n const html = getPanelHtml();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Content-Length', Buffer.byteLength(html));\n res.end(html);\n });\n\n // ── Panel favicon (no-op) ──\n app.get('/favicon.ico', (_req, res) => {\n res.sendStatus(204);\n });\n\n // ── List sessions ──\n app.get('/api/sessions', (_req, res) => {\n res.json(manager.list());\n });\n\n // ── Create session ──\n app.post<Record<string, string>, any, CreateSessionRequest>('/api/sessions', async (req, res) => {\n try {\n const cwd = req.body?.cwd || projectRoot;\n const session = await manager.create(cwd);\n res.json(session.getInfo());\n } catch (err: any) {\n console.error('[cc-prompter] Failed to create session:', err);\n res.status(500).json({ error: err.message, stack: err.stack });\n }\n });\n\n // ── Send message (SSE stream) ──\n app.post<Record<string, string>, any, SendMessageRequest>(\n '/api/sessions/:id/message',\n async (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n if (session.status === 'exited') {\n res.status(410).json({ error: 'Session exited' });\n return;\n }\n if (session.status === 'busy') {\n res.status(409).json({ error: 'Session busy' });\n return;\n }\n\n const { content, sourceInfo } = req.body;\n if (!content) {\n res.status(400).json({ error: 'Missing content' });\n return;\n }\n\n // Build prompt with optional source context (single-line to avoid multi-line input mode)\n let prompt = content;\n if (sourceInfo) {\n const relPath = sourceInfo.path;\n const parts = [\n `[source: ${relPath}:${sourceInfo.line}:${sourceInfo.column}]`,\n ];\n if (sourceInfo.elementInfo) {\n parts.push(`[element: ${sourceInfo.elementInfo}]`);\n }\n prompt = parts.join(' ') + ' ' + content;\n }\n\n // SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n // Forward session events as SSE\n const onMessage = (evt: SseEvent) => {\n res.write(`data: ${JSON.stringify(evt)}\\n\\n`);\n\n // Stop streaming on 'done'\n if (evt.type === 'done') {\n cleanup();\n }\n };\n\n const onError = (err: Error) => {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n };\n\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n session.removeListener('message', onMessage);\n session.removeListener('error', onError);\n res.end();\n };\n\n session.on('message', onMessage);\n session.on('error', onError);\n\n // Client disconnect — delay registration to avoid premature close\n // (Express/Node HTTP can fire 'close' early on SSE connections)\n let cleanedUp = false;\n setTimeout(() => {\n req.on('close', () => {\n if (!cleanedUp) {\n cleanup();\n }\n });\n }, 3000);\n\n // Send to PTY\n try {\n await session.sendMessage(prompt);\n } catch (err: any) {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n }\n },\n );\n\n // ── Send command ──\n app.post<Record<string, string>, any, SendCommandRequest>(\n '/api/sessions/:id/command',\n (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n\n const { command } = req.body;\n if (!command) {\n res.status(400).json({ error: 'Missing command' });\n return;\n }\n\n try {\n session.sendCommand(command);\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n },\n );\n\n // ── Interrupt session (Escape key) ──\n app.post('/api/sessions/:id/interrupt', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n try {\n session.interrupt();\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n });\n\n // ── Delete session ──\n app.delete('/api/sessions/:id', (req, res) => {\n if (manager.destroy(req.params.id)) {\n res.json({ ok: true });\n } else {\n res.status(404).json({ error: 'Session not found' });\n }\n });\n\n // ── Session history ──\n app.get('/api/sessions/:id/history', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session.getHistory());\n });\n\n const server = createServer(app);\n const MAX_PORT = startPort + 10;\n\n function tryListen(port: number): Promise<Server> {\n return new Promise((resolve, reject) => {\n server.listen(port, () => {\n console.log(`[cc-prompter] Sidecar running on http://localhost:${port}`);\n resolve(server);\n });\n server.on('error', (err: any) => {\n if (err.code === 'EADDRINUSE' && port < MAX_PORT) {\n console.log(`[cc-prompter] Port ${port} in use, trying ${port + 1}...`);\n tryListen(port + 1).then(resolve, reject);\n } else {\n reject(err);\n }\n });\n });\n }\n\n // Fire-and-forget listen (returns server immediately for API compat)\n tryListen(startPort).catch((err) => {\n console.error(`[cc-prompter] Failed to start sidecar:`, err.message);\n });\n\n // Graceful shutdown\n server.on('close', () => {\n manager.destroyAll();\n });\n\n return server;\n}\n","/**\n * CC Prompter — PTY Session\n *\n * 每个实例管理一个常驻的 claude CLI 进程(通过 node-pty)。\n *\n * 输出解析策略(双通道):\n * 1. PTY 输出解析(主要)— 实时从 TUI 输出提取响应文本\n * 2. JSONL transcript(辅助)— 结构化事件,用于 tool_use 等\n *\n * 就绪检测:解析 PTY 输出中的提示符(\"for shortcuts\"、\"/effort\")。\n */\n\nimport { createRequire } from 'module';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nconst _metaUrl = typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url);\nconst require = createRequire(_metaUrl);\nimport { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { IPty } from 'node-pty-prebuilt-multiarch';\nimport type {\n SessionStatus,\n ChatMessage,\n ToolUseInfo,\n JsonlEvent,\n SseEvent,\n} from './types.js';\n\n// Try multiple PTY packages for cross-platform compatibility\nconst PTY_PACKAGES = [\n 'node-pty-prebuilt-multiarch', // macOS / Linux prebuilt\n '@homebridge/node-pty-prebuilt-multiarch', // Better Windows support\n 'node-pty', // Original (needs build tools)\n];\n\nlet _ptyModule: any = null;\nfunction loadPty(): any {\n if (_ptyModule) return _ptyModule;\n const errors: string[] = [];\n for (const pkg of PTY_PACKAGES) {\n try {\n const mod = require(pkg);\n // Verify native binary actually exists — require() can succeed without it\n const modDir = path.dirname(require.resolve(pkg + '/package.json'));\n const buildDir = path.join(modDir, 'build', 'Release');\n if (!fs.existsSync(buildDir) || fs.readdirSync(buildDir).filter(f => f.endsWith('.node')).length === 0) {\n errors.push(`${pkg}: no native binary in ${buildDir}`);\n continue;\n }\n console.log(`[cc-prompter] Loaded PTY from: ${pkg}`);\n _ptyModule = mod;\n return _ptyModule;\n } catch (err: any) {\n errors.push(`${pkg}: ${err.message}`);\n }\n }\n throw new Error(\n `No PTY module available. Tried:\\n${errors.map(e => ' ' + e).join('\\n')}\\n` +\n `Install one: npm install node-pty-prebuilt-multiarch (macOS/Linux) or @homebridge/node-pty-prebuilt-multiarch (Windows)`\n );\n}\n\n// ── Helpers ─────────────────────────────────────────────\n\nfunction resolveClaudeBin(cwd: string): { file: string; args: string[] } {\n // Windows: find node.exe + claude.js directly (bypass broken .cmd wrapper)\n if (process.platform === 'win32') {\n // Check local install first\n const localJs = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(localJs)) {\n return { file: process.execPath, args: [localJs] };\n }\n // Check global npm install (nvm4w puts global modules next to node.exe)\n const globalPrefix = path.dirname(process.execPath);\n const globalJs = path.join(globalPrefix, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(globalJs)) {\n return { file: process.execPath, args: [globalJs] };\n }\n // Check npm global prefix via APPDATA (standard npm location)\n const appDataJs = path.join(process.env.APPDATA || '', 'npm/node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(appDataJs)) {\n return { file: process.execPath, args: [appDataJs] };\n }\n // Fallback: try cmd.exe\n return { file: process.env.COMSPEC || 'cmd.exe', args: ['/c', 'claude'] };\n }\n\n // macOS/Linux\n const local = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude');\n if (fs.existsSync(local)) return { file: local, args: [] };\n return { file: 'claude', args: [] };\n}\n\nfunction findClaudeProjectsDir(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n\n/** Convert a cwd path to the Claude projects directory name */\nfunction cwdToProjectDir(cwd: string): string {\n // Normalize path separators and handle both Unix and Windows paths\n // D:/code/agentplat → -D--code-agentplat\n // /Users/ryan/demo → -Users-ryan-demo\n const normalized = cwd.replace(/\\\\/g, '/').replace(/^[A-Za-z]:/, m => '-' + m[0].toLowerCase());\n return normalized.replace(/^\\//, '').replace(/\\/+/g, '-').replace(/^-+/, '');\n}\n\n/** Scan for the most recently modified .jsonl in a specific project dir */\nfunction findRecentJsonl(cwd: string, afterMs: number): string | null {\n const projectsDir = findClaudeProjectsDir();\n const projectSubdir = cwdToProjectDir(cwd);\n const targetDir = path.join(projectsDir, projectSubdir);\n\n if (!fs.existsSync(targetDir)) return null;\n\n const files = fs.readdirSync(targetDir).filter(f => f.endsWith('.jsonl'));\n let best: { path: string; mtime: number } | null = null;\n for (const f of files) {\n const fp = path.join(targetDir, f);\n try {\n const stat = fs.statSync(fp);\n if (stat.mtimeMs > afterMs) {\n if (!best || stat.mtimeMs > best.mtime) {\n best = { path: fp, mtime: stat.mtimeMs };\n }\n }\n } catch { continue; }\n }\n return best?.path || null;\n}\n\nfunction sessionIdFromJsonlPath(jsonPath: string): string | null {\n const base = path.basename(jsonPath, '.jsonl');\n const match = base.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);\n return match ? match[0] : null;\n}\n\n/** Strip ANSI escape sequences for analysis */\nfunction stripAnsi(s: string): string {\n return s\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\].*?(?:\\x07|\\x1b\\\\)/g, '')\n .replace(/\\x1b\\[[\\?]?[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\[[0-9;]*m/g, '')\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b[^[\\]()]?.?/g, '')\n .replace(/\\r/g, '');\n}\n\n// ── PtySession ──────────────────────────────────────────\n\nexport class PtySession extends EventEmitter {\n readonly id: string;\n readonly cwd: string;\n status: SessionStatus = 'spawning';\n\n private pty: IPty | null = null;\n private jsonlPath: string | null = null;\n private sessionId: string | null = null;\n private history: ChatMessage[] = [];\n private jsonlOffset = 0;\n private jsonlWatcher: fs.FSWatcher | null = null;\n private spawnTime: number;\n private messageSentAt = 0;\n private busySince = 0; // timestamp when last message was sent (for grace period)\n private title = 'New Session';\n private lastActivityAt: number;\n private killed = false;\n private ptyBuffer = ''; // accumulated for prompt detection\n private jsonlDiscoverPromise: Promise<void> | null = null;\n\n // ── PTY streaming fields ──\n private busyBuffer = ''; // accumulated during busy state\n private lastUserContent = ''; // last user message text\n private ptyResponseText = ''; // extracted response text so far\n private ptyResponseEmitted = 0; // chars already emitted\n private usedJsonl = false; // JSONL events received this turn\n private ptyDoneEmitted = false;\n private lastProgress = ''; // last emitted progress text\n private interrupted = false; // set when user sends interrupt\n\n constructor(id: string, cwd: string) {\n super();\n this.id = id;\n this.cwd = cwd;\n this.spawnTime = Date.now();\n this.lastActivityAt = this.spawnTime;\n }\n\n /** Spawn the claude process via PTY */\n async spawn(): Promise<void> {\n const ptyModule = loadPty();\n const { file, args } = resolveClaudeBin(this.cwd);\n\n console.log(`[pty-session ${this.id}] spawning: ${file} ${args.join(' ')} cwd: ${this.cwd}`);\n\n this.pty = ptyModule.spawn(file, args, {\n name: 'xterm-256color',\n cols: 120,\n rows: 30,\n cwd: this.cwd,\n env: { ...process.env } as Record<string, string>,\n });\n\n console.log(`[pty-session ${this.id}] PID: ${this.pty.pid}`);\n\n // Watch PTY output\n this.pty.onData((data: string) => {\n // Debug: log first 500 chars of output to diagnose spawn failures\n if (this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] output: ${JSON.stringify(data.substring(0, 500))}`);\n }\n const clean = stripAnsi(data);\n this.ptyBuffer += clean;\n\n this.detectPrompt();\n\n // Parse for streaming response when busy and JSONL not active\n if (this.status === 'busy' && !this.usedJsonl) {\n this.busyBuffer += clean;\n this.parseBusyOutput();\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n console.log(`[pty-session ${this.id}] exited with code: ${exitCode}`);\n this.status = 'exited';\n this.lastActivityAt = Date.now();\n this.emit('exit', exitCode);\n this.cleanup();\n });\n }\n\n // ── Prompt Detection ──────────────────────────────────\n\n private detectPrompt(): void {\n const indicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n\n for (const re of indicators) {\n if (re.test(this.ptyBuffer) && this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] detected prompt → ready`);\n this.status = 'ready';\n this.emit('ready');\n return;\n }\n }\n\n // After interrupt, detect prompt returning to finish the stream\n if (this.interrupted && this.status === 'busy') {\n for (const re of indicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt after interrupt → done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n }\n\n private async waitUntilReady(timeoutMs = 30_000): Promise<void> {\n if (this.status === 'ready') return;\n if (this.status === 'exited') throw new Error('Session exited');\n\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n const timer = setInterval(() => {\n if (this.status === 'ready') {\n clearInterval(timer);\n resolve();\n } else if (this.status === 'exited') {\n clearInterval(timer);\n reject(new Error('Session exited while waiting'));\n } else if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout waiting for session to be ready'));\n }\n }, 100);\n });\n }\n\n // ── Send Message ──────────────────────────────────────\n\n async sendMessage(content: string): Promise<void> {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status === 'busy') {\n throw new Error('Session busy');\n }\n\n // Wait for claude prompt to be ready\n if (this.status !== 'ready') {\n console.log(`[pty-session ${this.id}] waiting for prompt before sending message...`);\n await this.waitUntilReady();\n }\n\n await new Promise(r => setTimeout(r, 200));\n\n this.status = 'busy';\n this.lastActivityAt = Date.now();\n this.busySince = Date.now();\n\n // Reset streaming state for this turn\n this.busyBuffer = '';\n this.ptyBuffer = ''; // Clear accumulated prompt text to avoid false done detection\n this.lastUserContent = content;\n this.ptyResponseText = '';\n this.ptyResponseEmitted = 0;\n this.usedJsonl = false;\n this.ptyDoneEmitted = false;\n this.lastProgress = '';\n this.interrupted = false;\n\n // JSONL discovery in background\n if (!this.messageSentAt) {\n this.messageSentAt = Date.now();\n console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);\n this.jsonlDiscoverPromise = this.discoverJsonl();\n }\n\n console.log(`[pty-session ${this.id}] writing to PTY: ${JSON.stringify(content.slice(0, 100))}`);\n\n // Write content + double Enter for reliable submission\n this.pty.write(content + '\\r');\n await new Promise(r => setTimeout(r, 150));\n this.pty.write('\\r');\n }\n\n /** Send a slash command to the PTY */\n sendCommand(command: string): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n\n this.pty.write(command + '\\r');\n\n if (command === '/new') {\n this.history = [];\n this.title = 'New Session';\n this.jsonlPath = null;\n this.jsonlOffset = 0;\n this.jsonlWatcher?.close();\n this.jsonlWatcher = null;\n this.sessionId = null;\n this.messageSentAt = 0;\n this.status = 'ready';\n this.ptyBuffer = '';\n }\n }\n\n /** Send Escape to PTY to interrupt current generation */\n interrupt(): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status !== 'busy') return;\n this.interrupted = true;\n this.pty.write('\\x1b');\n\n // Safety timeout: if prompt detection fails, force finish after 5s\n setTimeout(() => {\n if (this.interrupted && this.status === 'busy') {\n console.log(`[pty-session ${this.id}] interrupt timeout → force done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n }\n }, 5000);\n }\n\n // ── PTY Output Parsing (streaming fallback) ───────────\n\n /**\n * Parse PTY output during busy state to extract streaming response.\n *\n * Claude Code TUI patterns:\n * - Spinner frames: ✳ ✶ ✻ ✽ ✢ · (ignore — just animation)\n * - Response text: ⏺<text> or ●<text>\n * - Tool use: ⚡<tool_name> or ✢ editing <file>\n * - Completion: \"Brewed for Xs\" (ONLY reliable indicator)\n * - ⚠️ ❯ appears in input echo too — NOT a completion signal!\n * - Timing: (Xs · ↓NNN tokens)\n */\n private parseBusyOutput(): void {\n // ── 0. Extract progress for UI feedback ──\n this.emitProgress();\n\n // ── 1. Try to extract response text ──\n const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);\n if (respMatch) {\n let raw = respMatch[1];\n raw = raw.replace(/[✳✶✻✽✢·].*$/s, '').trim();\n raw = raw.replace(/─{3,}.*$/s, '').trim();\n raw = raw.replace(/Brewed for.*$/s, '').trim();\n raw = raw.replace(/Sautéed for.*$/s, '').trim();\n raw = raw.replace(/esctointerrupt.*$/s, '').trim();\n\n if (raw.length > this.ptyResponseText.length) {\n this.ptyResponseText = raw;\n this.emitIncrementalText();\n }\n }\n\n // ── 2. Extract tool calls (Update/Read/Edit) ──\n const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/);\n if (toolCallMatch) {\n const toolName = toolCallMatch[1];\n const filePath = toolCallMatch[2];\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: toolName, input: { file: filePath } },\n } as SseEvent);\n }\n\n // ── 3. Detect completion ──\n if (!this.ptyDoneEmitted && /(?:Brewed|Sautéed) for/.test(this.busyBuffer)) {\n this.ptyDoneEmitted = true;\n\n // Final flush: emit any remaining text\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // If no response was emitted at all, try one more aggressive extraction\n if (this.ptyResponseEmitted === 0) {\n const finalMatch = this.busyBuffer.match(/⏺(.+)/s);\n if (finalMatch) {\n let text = finalMatch[1]\n .replace(/[✳✶✻✽✢·].*$/s, '')\n .replace(/─{3,}.*$/s, '')\n .replace(/Brewed for.*$/s, '')\n .replace(/esctointerrupt.*$/s, '')\n .trim();\n if (text.length > 0) {\n this.emitUserIfNeeded();\n this.history.push({\n role: 'assistant', content: text, timestamp: Date.now(),\n });\n this.emit('message', { type: 'assistant_text', content: text } as SseEvent);\n this.ptyResponseEmitted = text.length;\n }\n }\n }\n\n // Extract duration\n const durMatch = this.busyBuffer.match(/Brewed for (\\d+)s/);\n const durationMs = durMatch ? parseInt(durMatch[1]) * 1000 : 0;\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs } as SseEvent);\n }\n\n // ── 3b. Detect completion via prompt return (for Windows / non-English) ──\n // Only check after 5s AND after we've seen actual response content.\n // Claude TUI always shows \"for shortcuts\" in the header — we must distinguish\n // between \"header redraw during processing\" and \"Claude actually finished\".\n if (!this.ptyDoneEmitted && this.status === 'busy'\n && Date.now() - this.busySince > 5000\n && this.ptyResponseEmitted > 0) {\n const promptIndicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n for (const re of promptIndicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt return → done (fallback)`);\n this.ptyDoneEmitted = true;\n\n // Final flush\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n\n // Debug: log busyBuffer tail periodically while stuck in busy\n if (!this.ptyDoneEmitted && this.status === 'busy'\n && Date.now() - this.busySince > 10000\n && Math.random() < 0.02) {\n console.log(`[pty-session ${this.id}] still busy after ${Math.round((Date.now() - this.busySince) / 1000)}s, buffer tail: ${JSON.stringify(this.busyBuffer.slice(-300))}`);\n }\n }\n\n /** Emit only the newly arrived characters (incremental streaming) */\n private emitIncrementalText(): void {\n const newText = this.ptyResponseText.slice(this.ptyResponseEmitted);\n if (newText.length === 0) return;\n\n // First chunk → emit user message + start assistant\n if (this.ptyResponseEmitted === 0) {\n this.emitUserIfNeeded();\n }\n\n this.ptyResponseEmitted = this.ptyResponseText.length;\n this.emit('message', { type: 'assistant_text', content: newText } as SseEvent);\n }\n\n /**\n * Extract and emit progress updates from busyBuffer.\n *\n * Parses PTY output for Claude Code's progress indicators:\n * - \"Thinking for Xs, reading N files\"\n * - \"Thought for Xs, read N files\"\n * - \"Crafting… (Xs · ↓NN tokens)\"\n * - \"Update(file)\" / \"Read(file)\"\n * - \"⎿ Removed N lines\"\n * - \"(Xs · ↓NN tokens)\" timing\n */\n private emitProgress(): void {\n // Strip spinner chars + noise, collapse whitespace\n const text = this.busyBuffer\n .replace(/[✳✶✻✽✢·][a-zA-Z0-9…]{0,4}/g, '')\n .replace(/\\s+/g, ' ');\n\n // Ordered by specificity — last match wins (most recent progress)\n const patterns: [RegExp, (m: RegExpMatchArray) => string][] = [\n // Thinking phase\n [/Thinking for (\\d+s)[^─]{0,60}(reading \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Thought completed\n [/Thought for (\\d+s)[^─]{0,60}(read \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Tool call: Update(file) / Read(file)\n [/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/, (m) => {\n return m[1] + ': ' + m[2].split('/').slice(-2).join('/');\n }],\n // Tool result: ⎿ Removed N lines\n [/⎿\\s*(Removed|Added|Modified|Created)\\s+(\\d+)\\s+(lines?)/, (m) => {\n return m[1] + ' ' + m[2] + ' ' + m[3];\n }],\n // Crafting with timing\n [/Crafting[^─]{0,30}\\(\\d+s[^)]*\\)/, (m) => {\n return m[0].replace(/\\s+/g, ' ').trim();\n }],\n // Simple timing: (5s · ↓9 tokens)\n [/\\((\\d+s)\\s*·\\s*[↓↑]\\s*(\\d+)\\s*tokens?\\)/, (m) => {\n return m[1] + ' · ' + m[2] + ' tokens';\n }],\n ];\n\n // Find the last matching pattern\n let progress = '';\n for (const [re, fn] of patterns) {\n const m = text.match(re);\n if (m) progress = fn(m);\n }\n\n // Emit only if changed\n if (progress && progress !== this.lastProgress) {\n this.lastProgress = progress;\n this.emit('message', { type: 'progress', content: progress } as SseEvent);\n }\n }\n\n /** Emit user message event (only if not already emitted this turn) */\n private emitUserIfNeeded(): void {\n if (!this.lastUserContent) return;\n // Check if user message already in history\n const already = this.history.some(\n m => m.role === 'user' && m.content === this.lastUserContent,\n );\n if (!already) {\n this.history.push({\n role: 'user',\n content: this.lastUserContent,\n timestamp: Date.now(),\n });\n this.emit('message', { type: 'user', content: this.lastUserContent } as SseEvent);\n }\n }\n\n // ── JSONL Discovery & Parsing (structured events) ─────\n\n private async discoverJsonl(): Promise<void> {\n const searchStart = this.messageSentAt - 2000;\n\n // Poll for up to 60 seconds (120 × 500ms)\n for (let i = 0; i < 120; i++) {\n if (this.killed) return;\n const jsonl = findRecentJsonl(this.cwd, searchStart);\n if (jsonl) {\n this.jsonlPath = jsonl;\n this.sessionId = sessionIdFromJsonlPath(jsonl) || null;\n console.log(`[pty-session ${this.id}] found JSONL: ${jsonl} (session: ${this.sessionId})`);\n this.startTailingJsonl();\n return;\n }\n await new Promise(r => setTimeout(r, 500));\n }\n console.warn(`[pty-session ${this.id}] JSONL not found after 60s — using PTY output parsing`);\n }\n\n private startTailingJsonl(): void {\n if (!this.jsonlPath) return;\n\n this.readNewJsonlLines();\n\n try {\n this.jsonlWatcher = fs.watch(\n path.dirname(this.jsonlPath),\n (eventType, filename) => {\n if (filename === path.basename(this.jsonlPath!)) {\n this.readNewJsonlLines();\n }\n },\n );\n } catch (err) {\n console.warn(`[pty-session ${this.id}] fs.watch failed:`, err);\n }\n }\n\n private readNewJsonlLines(): void {\n if (!this.jsonlPath) return;\n\n try {\n const stat = fs.statSync(this.jsonlPath);\n if (stat.size <= this.jsonlOffset) return;\n\n const fd = fs.openSync(this.jsonlPath, 'r');\n const buf = Buffer.alloc(stat.size - this.jsonlOffset);\n fs.readSync(fd, buf, 0, buf.length, this.jsonlOffset);\n fs.closeSync(fd);\n this.jsonlOffset = stat.size;\n\n const text = buf.toString('utf8');\n for (const line of text.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const evt: JsonlEvent = JSON.parse(trimmed);\n this.processJsonlEvent(evt);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // file might be temporarily unavailable\n }\n }\n\n /** Process a JSONL event — marks usedJsonl to disable PTY parsing */\n private processJsonlEvent(evt: JsonlEvent): void {\n // Once JSONL events arrive, disable PTY output parsing\n this.usedJsonl = true;\n\n if (!this.sessionId && evt.sessionId) {\n this.sessionId = evt.sessionId;\n }\n\n switch (evt.type) {\n case 'user': {\n const text = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n : '';\n this.history.push({\n role: 'user',\n content: text,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'user', content: text } as SseEvent);\n if (this.history.filter(m => m.role === 'user').length === 1 && text) {\n this.title = text.slice(0, 60);\n this.emit('title-change', this.title);\n }\n break;\n }\n case 'assistant': {\n const content = evt.message?.content;\n if (!Array.isArray(content)) break;\n\n const texts = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n .trim();\n\n const tools: ToolUseInfo[] = content\n .filter((c: any) => c.type === 'tool_use')\n .map((c: any) => ({\n name: c.name,\n input: c.input || {},\n }));\n\n if (texts || tools.length) {\n this.history.push({\n role: 'assistant',\n content: texts,\n toolUse: tools.length ? tools : undefined,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n }\n\n if (texts) {\n this.emit('message', { type: 'assistant_text', content: texts } as SseEvent);\n }\n for (const t of tools) {\n this.emit('message', { type: 'assistant_tool', tool: t } as SseEvent);\n }\n break;\n }\n case 'system': {\n if (evt.subtype === 'tool_result') {\n const lastAssistant = [...this.history].reverse().find(m => m.role === 'assistant' && m.toolUse?.length);\n if (lastAssistant?.toolUse?.length) {\n const lastTool = lastAssistant.toolUse[lastAssistant.toolUse.length - 1];\n const resultText = typeof evt.message?.content === 'string'\n ? evt.message.content : '';\n lastTool.result = resultText.slice(0, 500);\n }\n const resultContent = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content.map((c: any) => c.text || '').join('')\n : '';\n this.emit('message', { type: 'system', content: resultContent.slice(0, 200) } as SseEvent);\n } else if (evt.subtype === 'turn_duration') {\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: evt.durationMs } as SseEvent);\n }\n break;\n }\n }\n }\n\n // ── Lifecycle ─────────────────────────────────────────\n\n kill(): void {\n this.killed = true;\n this.cleanup();\n if (this.pty) {\n try { this.pty.kill(); } catch { /* already dead */ }\n this.pty = null;\n }\n this.status = 'exited';\n }\n\n private cleanup(): void {\n if (this.jsonlWatcher) {\n this.jsonlWatcher.close();\n this.jsonlWatcher = null;\n }\n }\n\n getInfo(): {\n id: string;\n title: string;\n status: SessionStatus;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n lastMessagePreview: string;\n sessionId: string | null;\n } {\n const lastMsg = this.history[this.history.length - 1];\n return {\n id: this.id,\n title: this.title,\n status: this.status,\n createdAt: this.spawnTime,\n lastActivityAt: this.lastActivityAt,\n messageCount: this.history.length,\n lastMessagePreview: lastMsg ? lastMsg.content.slice(0, 80) : '',\n sessionId: this.sessionId,\n };\n }\n\n getHistory(): ChatMessage[] {\n return [...this.history];\n }\n}\n","/**\n * CC Prompter — Asset Loader\n *\n * 运行时从包根目录读取 panel.html 和 inject.js。\n * 兼容 ESM(import.meta.url)和 CJS(__dirname)。\n *\n * 解析策略:\n * - tsup 构建后:资产文件复制到 dist/,__dirname 是 dist/\n * - Vite 直接引用 TS:__dirname 是 src/,资产文件在上一级\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\n// Resolve __dirname in both ESM and CJS contexts\nconst _dirname = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\n/** Resolve asset path — checks dist/ (build) then parent (dev) */\nfunction assetPath(filename: string): string {\n // 1. Same dir as this file (dist/ after build, or src/ in dev)\n const here = join(_dirname, filename);\n if (existsSync(here)) return here;\n\n // 2. Parent dir (package root — when running from src/ via vite)\n const parent = join(_dirname, '..', filename);\n if (existsSync(parent)) return parent;\n\n // 3. Fallback — let it throw with a clear message\n throw new Error(\n `[cc-prompter] Asset not found: ${filename}\\n` +\n ` Tried: ${here}\\n` +\n ` Tried: ${parent}\\n` +\n ` __dirname: ${_dirname}`\n );\n}\n\nexport function getPanelHtml(): string {\n return readFileSync(assetPath('panel.html'), 'utf8');\n}\n\nexport function getInjectScript(): string {\n return readFileSync(assetPath('inject.js'), 'utf8');\n}\n"],"mappings":"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","names":["require","resolve","existsSync","join","dirname","fileURLToPath","resolve"]}
1
+ {"version":3,"sources":["../src/vite-plugin.ts","../src/sidecar.ts","../src/pty-session.ts","../src/assets.ts"],"sourcesContent":["/**\n * CC Prompter — Vite Plugin\n *\n * 为 Vite dev server 提供:\n * 1. 内置 code-inspector-plugin(Shift+Alt 悬停定位源码)\n * 2. 启动 Sidecar Express server 管理 PTY sessions(自动选择空闲端口)\n * 3. 注入轻量脚本监听 code-inspector 事件,弹出 Claude 面板\n * 4. 提供 /__panel/ HTML 给 iframe\n *\n * 仅在 dev 模式生效,build 时不注入脚本。\n *\n * 用法:plugins: [ccPromptPlugin()]\n * 等同于同时配置 codeInspectorPlugin + cc-prompt-plugin。\n */\n\nimport type { Plugin } from 'vite';\nimport type { Server } from 'http';\nimport { codeInspectorPlugin } from 'code-inspector-plugin';\nimport { startSidecar } from './sidecar.js';\nimport { getPanelHtml, getInjectScript } from './assets.js';\n\nexport interface CcPromptOptions {\n /** Sidecar 启动端口,默认 3456(被占用时自动 +1) */\n port?: number;\n /** 项目根目录,默认 vite config.root */\n root?: string;\n /** 是否启用 code-inspector,默认 true */\n inspector?: boolean;\n}\n\nexport function ccPromptPlugin(options?: CcPromptOptions): Plugin[] {\n const enableInspector = options?.inspector !== false;\n let projectRoot = process.cwd();\n let sidecarServer: Server | null = null;\n let actualPort = 0;\n\n const startPort = options?.port || 3456;\n\n let cleanedUp = false;\n function cleanup() {\n if (cleanedUp) return;\n cleanedUp = true;\n if (sidecarServer) {\n sidecarServer.close();\n sidecarServer = null;\n }\n }\n\n // ── 1. code-inspector plugin ──\n const inspectorPlugin = enableInspector\n ? codeInspectorPlugin({\n bundler: 'vite',\n behavior: {\n locate: false,\n copy: false,\n },\n hideDomPathAttr: true,\n hideConsole: true,\n })\n : null;\n\n // ── 2. cc-prompter plugin ──\n const promptPlugin: Plugin = {\n name: 'cc-prompt-plugin',\n\n configResolved(config) {\n projectRoot = options?.root || config.root;\n },\n\n configureServer(server) {\n // Start sidecar — auto-picks available port\n sidecarServer = startSidecar(projectRoot, { startPort });\n\n // Wait for sidecar to actually start, then grab port\n const checkPort = () => {\n const addr = sidecarServer?.address();\n if (addr && typeof addr === 'object') {\n actualPort = addr.port;\n console.log(`[cc-prompter] Sidecar port: ${actualPort}`);\n }\n };\n setTimeout(checkPort, 200);\n setTimeout(checkPort, 1000);\n\n // Expose actual sidecar port to inject script\n server.middlewares.use('/__cc-port', (req, res) => {\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.end(String(actualPort || startPort));\n });\n\n // Clean up on dev server close\n server.httpServer?.on('close', cleanup);\n process.on('SIGTERM', () => { cleanup(); process.exit(0); });\n process.on('SIGINT', () => { cleanup(); process.exit(0); });\n process.on('exit', cleanup);\n },\n\n // Only inject in dev mode (ctx.server is undefined during build)\n transformIndexHtml: {\n order: 'post',\n handler(html, ctx) {\n if (!ctx.server) return html;\n const script = getInjectScript();\n return html.replace('</body>', `<script>${script}</script></body>`);\n },\n },\n\n closeBundle() {\n cleanup();\n },\n };\n\n // Return composed plugin array\n const plugins: Plugin[] = [promptPlugin];\n if (inspectorPlugin) plugins.unshift(inspectorPlugin);\n return plugins;\n}\n","/**\n * CC Prompter — Sidecar API Server\n *\n * Express server 运行在端口 3456,管理 PTY session 生命周期。\n * 提供 REST API + SSE 流式响应。\n */\n\nimport express from 'express';\nimport { createServer, type Server } from 'http';\nimport { PtySession } from './pty-session.js';\nimport { getPanelHtml } from './assets.js';\nimport type {\n SessionInfo,\n CreateSessionRequest,\n SendMessageRequest,\n SendCommandRequest,\n SseEvent,\n} from './types.js';\n\n// ── Session Manager ─────────────────────────────────────\n\nclass SessionManager {\n private sessions = new Map<string, PtySession>();\n private counter = 0;\n\n async create(cwd: string): Promise<PtySession> {\n const id = `s${++this.counter}-${Date.now().toString(36)}`;\n const session = new PtySession(id, cwd);\n this.sessions.set(id, session);\n\n // Clean up on exit\n session.on('exit', () => {\n // Keep in map for history, but mark exited\n });\n\n await session.spawn();\n return session;\n }\n\n get(id: string): PtySession | undefined {\n return this.sessions.get(id);\n }\n\n list(): SessionInfo[] {\n return Array.from(this.sessions.values()).map(s => {\n const info = s.getInfo();\n return {\n id: info.id,\n title: info.title,\n status: info.status,\n createdAt: info.createdAt,\n lastActivityAt: info.lastActivityAt,\n messageCount: info.messageCount,\n lastMessagePreview: info.lastMessagePreview,\n };\n });\n }\n\n destroy(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n session.kill();\n this.sessions.delete(id);\n return true;\n }\n\n destroyAll(): void {\n for (const session of this.sessions.values()) {\n session.kill();\n }\n this.sessions.clear();\n }\n}\n\n// ── Sidecar Server ──────────────────────────────────────\n\nexport interface SidecarOptions {\n startPort?: number;\n}\n\nexport function startSidecar(projectRoot: string, options?: SidecarOptions): Server {\n const startPort = options?.startPort || 3456;\n const app = express();\n app.use(express.json());\n\n const manager = new SessionManager();\n\n // ── CORS for iframe ──\n app.use((req, res, next) => {\n res.header('Access-Control-Allow-Origin', '*');\n res.header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');\n res.header('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.sendStatus(204);\n return;\n }\n next();\n });\n\n // ── Panel HTML (served to iframe) ──\n app.get('/__panel/', (_req, res) => {\n const html = getPanelHtml();\n res.setHeader('Content-Type', 'text/html; charset=utf-8');\n res.setHeader('Content-Length', Buffer.byteLength(html));\n res.end(html);\n });\n\n // ── Panel favicon (no-op) ──\n app.get('/favicon.ico', (_req, res) => {\n res.sendStatus(204);\n });\n\n // ── List sessions ──\n app.get('/api/sessions', (_req, res) => {\n res.json(manager.list());\n });\n\n // ── Create session ──\n app.post<Record<string, string>, any, CreateSessionRequest>('/api/sessions', async (req, res) => {\n try {\n const cwd = req.body?.cwd || projectRoot;\n const session = await manager.create(cwd);\n res.json(session.getInfo());\n } catch (err: any) {\n console.error('[cc-prompter] Failed to create session:', err);\n res.status(500).json({ error: err.message, stack: err.stack });\n }\n });\n\n // ── Send message (SSE stream) ──\n app.post<Record<string, string>, any, SendMessageRequest>(\n '/api/sessions/:id/message',\n async (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n if (session.status === 'exited') {\n res.status(410).json({ error: 'Session exited' });\n return;\n }\n if (session.status === 'busy') {\n res.status(409).json({ error: 'Session busy' });\n return;\n }\n\n const { content, sourceInfo } = req.body;\n if (!content) {\n res.status(400).json({ error: 'Missing content' });\n return;\n }\n\n // Build prompt with optional source context (single-line to avoid multi-line input mode)\n let prompt = content;\n if (sourceInfo) {\n const relPath = sourceInfo.path;\n const parts = [\n `[source: ${relPath}:${sourceInfo.line}:${sourceInfo.column}]`,\n ];\n if (sourceInfo.elementInfo) {\n parts.push(`[element: ${sourceInfo.elementInfo}]`);\n }\n prompt = parts.join(' ') + ' ' + content;\n }\n\n // SSE headers\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no');\n res.flushHeaders();\n\n // Forward session events as SSE\n const onMessage = (evt: SseEvent) => {\n res.write(`data: ${JSON.stringify(evt)}\\n\\n`);\n\n // Stop streaming on 'done'\n if (evt.type === 'done') {\n cleanup();\n }\n };\n\n const onError = (err: Error) => {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n };\n\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n session.removeListener('message', onMessage);\n session.removeListener('error', onError);\n res.end();\n };\n\n session.on('message', onMessage);\n session.on('error', onError);\n\n // Client disconnect — delay registration to avoid premature close\n // (Express/Node HTTP can fire 'close' early on SSE connections)\n let cleanedUp = false;\n setTimeout(() => {\n req.on('close', () => {\n if (!cleanedUp) {\n cleanup();\n }\n });\n }, 3000);\n\n // Send to PTY\n try {\n await session.sendMessage(prompt);\n } catch (err: any) {\n res.write(`data: ${JSON.stringify({ type: 'error', content: err.message })}\\n\\n`);\n cleanup();\n }\n },\n );\n\n // ── Send command ──\n app.post<Record<string, string>, any, SendCommandRequest>(\n '/api/sessions/:id/command',\n (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n\n const { command } = req.body;\n if (!command) {\n res.status(400).json({ error: 'Missing command' });\n return;\n }\n\n try {\n session.sendCommand(command);\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n },\n );\n\n // ── Interrupt session (Escape key) ──\n app.post('/api/sessions/:id/interrupt', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n try {\n session.interrupt();\n res.json({ ok: true });\n } catch (err: any) {\n res.status(500).json({ error: err.message });\n }\n });\n\n // ── Delete session ──\n app.delete('/api/sessions/:id', (req, res) => {\n if (manager.destroy(req.params.id)) {\n res.json({ ok: true });\n } else {\n res.status(404).json({ error: 'Session not found' });\n }\n });\n\n // ── Session history ──\n app.get('/api/sessions/:id/history', (req, res) => {\n const session = manager.get(req.params.id);\n if (!session) {\n res.status(404).json({ error: 'Session not found' });\n return;\n }\n res.json(session.getHistory());\n });\n\n const server = createServer(app);\n const MAX_PORT = startPort + 10;\n\n function tryListen(port: number): Promise<Server> {\n return new Promise((resolve, reject) => {\n server.listen(port, () => {\n console.log(`[cc-prompter] Sidecar running on http://localhost:${port}`);\n resolve(server);\n });\n server.on('error', (err: any) => {\n if (err.code === 'EADDRINUSE' && port < MAX_PORT) {\n console.log(`[cc-prompter] Port ${port} in use, trying ${port + 1}...`);\n tryListen(port + 1).then(resolve, reject);\n } else {\n reject(err);\n }\n });\n });\n }\n\n // Fire-and-forget listen (returns server immediately for API compat)\n tryListen(startPort).catch((err) => {\n console.error(`[cc-prompter] Failed to start sidecar:`, err.message);\n });\n\n // Graceful shutdown\n server.on('close', () => {\n manager.destroyAll();\n });\n\n return server;\n}\n","/**\n * CC Prompter — PTY Session\n *\n * 每个实例管理一个常驻的 claude CLI 进程(通过 node-pty)。\n *\n * 输出解析策略(双通道):\n * 1. PTY 输出解析(主要)— 实时从 TUI 输出提取响应文本\n * 2. JSONL transcript(辅助)— 结构化事件,用于 tool_use 等\n *\n * 就绪检测:解析 PTY 输出中的提示符(\"for shortcuts\"、\"/effort\")。\n */\n\nimport { createRequire } from 'module';\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nconst _metaUrl = typeof __filename !== 'undefined' ? __filename : fileURLToPath(import.meta.url);\nconst require = createRequire(_metaUrl);\nimport { EventEmitter } from 'events';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport type { IPty } from 'node-pty-prebuilt-multiarch';\nimport type {\n SessionStatus,\n ChatMessage,\n ToolUseInfo,\n JsonlEvent,\n SseEvent,\n} from './types.js';\n\n// Try multiple PTY packages for cross-platform compatibility\nconst PTY_PACKAGES = [\n 'node-pty-prebuilt-multiarch', // macOS / Linux prebuilt\n '@homebridge/node-pty-prebuilt-multiarch', // Better Windows support\n 'node-pty', // Original (needs build tools)\n];\n\nlet _ptyModule: any = null;\nfunction loadPty(): any {\n if (_ptyModule) return _ptyModule;\n const errors: string[] = [];\n for (const pkg of PTY_PACKAGES) {\n try {\n const mod = require(pkg);\n // Verify native binary actually exists — require() can succeed without it\n const modDir = path.dirname(require.resolve(pkg + '/package.json'));\n const buildDir = path.join(modDir, 'build', 'Release');\n if (!fs.existsSync(buildDir) || fs.readdirSync(buildDir).filter(f => f.endsWith('.node')).length === 0) {\n errors.push(`${pkg}: no native binary in ${buildDir}`);\n continue;\n }\n console.log(`[cc-prompter] Loaded PTY from: ${pkg}`);\n _ptyModule = mod;\n return _ptyModule;\n } catch (err: any) {\n errors.push(`${pkg}: ${err.message}`);\n }\n }\n throw new Error(\n `No PTY module available. Tried:\\n${errors.map(e => ' ' + e).join('\\n')}\\n` +\n `Install one: npm install node-pty-prebuilt-multiarch (macOS/Linux) or @homebridge/node-pty-prebuilt-multiarch (Windows)`\n );\n}\n\n// ── Helpers ─────────────────────────────────────────────\n\nfunction resolveClaudeBin(cwd: string): { file: string; args: string[] } {\n // Windows: find node.exe + claude.js directly (bypass broken .cmd wrapper)\n if (process.platform === 'win32') {\n // Check local install first\n const localJs = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(localJs)) {\n return { file: process.execPath, args: [localJs] };\n }\n // Check global npm install (nvm4w puts global modules next to node.exe)\n const globalPrefix = path.dirname(process.execPath);\n const globalJs = path.join(globalPrefix, 'node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(globalJs)) {\n return { file: process.execPath, args: [globalJs] };\n }\n // Check npm global prefix via APPDATA (standard npm location)\n const appDataJs = path.join(process.env.APPDATA || '', 'npm/node_modules/@anthropic-ai/claude-code/bin/claude.js');\n if (fs.existsSync(appDataJs)) {\n return { file: process.execPath, args: [appDataJs] };\n }\n // Fallback: try cmd.exe\n return { file: process.env.COMSPEC || 'cmd.exe', args: ['/c', 'claude'] };\n }\n\n // macOS/Linux\n const local = path.resolve(cwd, 'node_modules/@anthropic-ai/claude-code/bin/claude');\n if (fs.existsSync(local)) return { file: local, args: [] };\n return { file: 'claude', args: [] };\n}\n\nfunction findClaudeProjectsDir(): string {\n return path.join(os.homedir(), '.claude', 'projects');\n}\n\n/** Convert a cwd path to the Claude projects directory name */\nfunction cwdToProjectDir(cwd: string): string {\n // Normalize path separators and handle both Unix and Windows paths\n // D:/code/agentplat → -D--code-agentplat\n // /Users/ryan/demo → -Users-ryan-demo\n const normalized = cwd.replace(/\\\\/g, '/').replace(/^[A-Za-z]:/, m => '-' + m[0].toLowerCase());\n return normalized.replace(/^\\//, '').replace(/\\/+/g, '-').replace(/^-+/, '');\n}\n\n/** Scan for the most recently modified .jsonl in project dirs */\nfunction findRecentJsonl(cwd: string, afterMs: number): string | null {\n const projectsDir = findClaudeProjectsDir();\n if (!fs.existsSync(projectsDir)) return null;\n\n // Collect candidate JSONL files from target dir and all subdirectories\n const candidates: { path: string; mtime: number }[] = [];\n\n const collectFromDir = (dir: string) => {\n try {\n const files = fs.readdirSync(dir).filter(f => f.endsWith('.jsonl'));\n for (const f of files) {\n const fp = path.join(dir, f);\n try {\n const stat = fs.statSync(fp);\n if (stat.mtimeMs > afterMs) {\n candidates.push({ path: fp, mtime: stat.mtimeMs });\n }\n } catch { /* skip */ }\n }\n } catch { /* dir might not exist */ }\n };\n\n // Try exact match first\n const projectSubdir = cwdToProjectDir(cwd);\n const targetDir = path.join(projectsDir, projectSubdir);\n collectFromDir(targetDir);\n\n // Fallback: scan ALL project subdirectories (handles Windows path format differences)\n if (candidates.length === 0) {\n try {\n const entries = fs.readdirSync(projectsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== projectSubdir) {\n collectFromDir(path.join(projectsDir, entry.name));\n }\n }\n } catch { /* ignore */ }\n }\n\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.mtime - a.mtime);\n return candidates[0].path;\n}\n\nfunction sessionIdFromJsonlPath(jsonPath: string): string | null {\n const base = path.basename(jsonPath, '.jsonl');\n const match = base.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);\n return match ? match[0] : null;\n}\n\n/** Strip ANSI escape sequences for analysis */\nfunction stripAnsi(s: string): string {\n return s\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\].*?(?:\\x07|\\x1b\\\\)/g, '')\n .replace(/\\x1b\\[[\\?]?[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b\\[[0-9;]*m/g, '')\n .replace(/\\x1b\\[[0-9;]*[a-zA-Z]/g, '')\n .replace(/\\x1b[^[\\]()]?.?/g, '')\n .replace(/\\r/g, '');\n}\n\n/** Strip Claude TUI spinner noise from PTY output */\nfunction stripSpinner(s: string): string {\n return s\n .replace(/[✻✶✽✢·●✳◇◆▸▹⏵⏶]+/g, '')\n .replace(/\\w{3,}ing[…\\s]*\\(\\d{1,3}s?\\)?/g, '') // \"Gesticulating… (28s)\"\n .replace(/\\w{3,}ing…/g, '') // \"Topsy-turvying…\"\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\n// ── PtySession ──────────────────────────────────────────\n\nexport class PtySession extends EventEmitter {\n readonly id: string;\n readonly cwd: string;\n status: SessionStatus = 'spawning';\n\n private pty: IPty | null = null;\n private jsonlPath: string | null = null;\n private sessionId: string | null = null;\n private history: ChatMessage[] = [];\n private jsonlOffset = 0;\n private jsonlWatcher: fs.FSWatcher | null = null;\n private spawnTime: number;\n private messageSentAt = 0;\n private busySince = 0; // timestamp when last message was sent (for grace period)\n private title = 'New Session';\n private lastActivityAt: number;\n private killed = false;\n private ptyBuffer = ''; // accumulated for prompt detection\n private jsonlDiscoverPromise: Promise<void> | null = null;\n\n // ── PTY streaming fields ──\n private busyBuffer = ''; // accumulated during busy state\n private lastUserContent = ''; // last user message text\n private ptyResponseText = ''; // extracted response text so far\n private ptyResponseEmitted = 0; // chars already emitted\n private usedJsonl = false; // JSONL events received this turn\n private ptyDoneEmitted = false;\n private lastProgress = ''; // last emitted progress text\n private interrupted = false; // set when user sends interrupt\n private lastSpinnerSec = 0; // highest spinner seconds counter seen\n private lastSpinnerAt = 0; // timestamp when spinner was last active\n private emittedTools = new Set<string>(); // dedup tool call emissions\n\n constructor(id: string, cwd: string) {\n super();\n this.id = id;\n this.cwd = cwd;\n this.spawnTime = Date.now();\n this.lastActivityAt = this.spawnTime;\n }\n\n /** Spawn the claude process via PTY */\n async spawn(): Promise<void> {\n const ptyModule = loadPty();\n const { file, args } = resolveClaudeBin(this.cwd);\n\n console.log(`[pty-session ${this.id}] spawning: ${file} ${args.join(' ')} cwd: ${this.cwd}`);\n\n this.pty = ptyModule.spawn(file, args, {\n name: 'xterm-256color',\n cols: 120,\n rows: 30,\n cwd: this.cwd,\n env: { ...process.env } as Record<string, string>,\n });\n\n console.log(`[pty-session ${this.id}] PID: ${this.pty.pid}`);\n\n // Watch PTY output\n this.pty.onData((data: string) => {\n // Debug: log first 500 chars of output to diagnose spawn failures\n if (this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] output: ${JSON.stringify(data.substring(0, 500))}`);\n }\n const clean = stripAnsi(data);\n this.ptyBuffer += clean;\n\n this.detectPrompt();\n\n // Parse for streaming response when busy and JSONL not active\n if (this.status === 'busy' && !this.usedJsonl) {\n this.busyBuffer += clean;\n this.parseBusyOutput();\n }\n });\n\n this.pty.onExit(({ exitCode }) => {\n console.log(`[pty-session ${this.id}] exited with code: ${exitCode}`);\n this.status = 'exited';\n this.lastActivityAt = Date.now();\n this.emit('exit', exitCode);\n this.cleanup();\n });\n }\n\n // ── Prompt Detection ──────────────────────────────────\n\n private detectPrompt(): void {\n const indicators = [\n /for shortcuts/,\n /\\/effort/,\n /refactor/,\n ];\n\n for (const re of indicators) {\n if (re.test(this.ptyBuffer) && this.status === 'spawning') {\n console.log(`[pty-session ${this.id}] detected prompt → ready`);\n this.status = 'ready';\n this.emit('ready');\n return;\n }\n }\n\n // After interrupt, detect prompt returning to finish the stream\n if (this.interrupted && this.status === 'busy') {\n for (const re of indicators) {\n if (re.test(this.ptyBuffer)) {\n console.log(`[pty-session ${this.id}] detected prompt after interrupt → done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n return;\n }\n }\n }\n }\n\n private async waitUntilReady(timeoutMs = 30_000): Promise<void> {\n if (this.status === 'ready') return;\n if (this.status === 'exited') throw new Error('Session exited');\n\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n const timer = setInterval(() => {\n if (this.status === 'ready') {\n clearInterval(timer);\n resolve();\n } else if (this.status === 'exited') {\n clearInterval(timer);\n reject(new Error('Session exited while waiting'));\n } else if (Date.now() > deadline) {\n clearInterval(timer);\n reject(new Error('Timeout waiting for session to be ready'));\n }\n }, 100);\n });\n }\n\n // ── Send Message ──────────────────────────────────────\n\n async sendMessage(content: string): Promise<void> {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status === 'busy') {\n throw new Error('Session busy');\n }\n\n // Wait for claude prompt to be ready\n if (this.status !== 'ready') {\n console.log(`[pty-session ${this.id}] waiting for prompt before sending message...`);\n await this.waitUntilReady();\n }\n\n await new Promise(r => setTimeout(r, 200));\n\n this.status = 'busy';\n this.lastActivityAt = Date.now();\n this.busySince = Date.now();\n\n // Reset streaming state for this turn\n this.busyBuffer = '';\n this.ptyBuffer = ''; // Clear accumulated prompt text to avoid false done detection\n this.lastUserContent = content;\n this.ptyResponseText = '';\n this.ptyResponseEmitted = 0;\n this.usedJsonl = false;\n this.ptyDoneEmitted = false;\n this.lastProgress = '';\n this.interrupted = false;\n this.lastSpinnerSec = 0;\n this.lastSpinnerAt = 0;\n this.emittedTools.clear();\n\n // JSONL discovery in background\n if (!this.messageSentAt) {\n this.messageSentAt = Date.now();\n console.log(`[pty-session ${this.id}] first message, starting JSONL discovery`);\n this.jsonlDiscoverPromise = this.discoverJsonl();\n }\n\n console.log(`[pty-session ${this.id}] writing to PTY: ${JSON.stringify(content.slice(0, 100))}`);\n\n // Write content + double Enter for reliable submission\n this.pty.write(content + '\\r');\n await new Promise(r => setTimeout(r, 150));\n this.pty.write('\\r');\n }\n\n /** Send a slash command to the PTY */\n sendCommand(command: string): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n\n this.pty.write(command + '\\r');\n\n if (command === '/new') {\n this.history = [];\n this.title = 'New Session';\n this.jsonlPath = null;\n this.jsonlOffset = 0;\n this.jsonlWatcher?.close();\n this.jsonlWatcher = null;\n this.sessionId = null;\n this.messageSentAt = 0;\n this.status = 'ready';\n this.ptyBuffer = '';\n }\n }\n\n /** Send Escape to PTY to interrupt current generation */\n interrupt(): void {\n if (!this.pty || this.status === 'exited') {\n throw new Error('Session not active');\n }\n if (this.status !== 'busy') return;\n this.interrupted = true;\n this.pty.write('\\x1b');\n\n // Safety timeout: if prompt detection fails, force finish after 5s\n setTimeout(() => {\n if (this.interrupted && this.status === 'busy') {\n console.log(`[pty-session ${this.id}] interrupt timeout → force done`);\n this.interrupted = false;\n this.ptyDoneEmitted = true;\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: 0 } as SseEvent);\n }\n }, 5000);\n }\n\n // ── PTY Output Parsing (streaming fallback) ───────────\n\n /**\n * Parse PTY output during busy state to extract streaming response.\n *\n * Claude Code TUI patterns:\n * - Spinner frames: ✳ ✶ ✻ ✽ ✢ · (ignore — just animation)\n * - Response text: ⏺<text> or ●<text>\n * - Tool use: ⚡<tool_name> or ✢ editing <file>\n * - Completion: \"Brewed for Xs\" (ONLY reliable indicator)\n * - ⚠️ ❯ appears in input echo too — NOT a completion signal!\n * - Timing: (Xs · ↓NNN tokens)\n */\n private parseBusyOutput(): void {\n // ── 0. Sliding window — prevent unbounded buffer growth from spinner frames ──\n // Windows ConPTY sends character-by-character spinner updates, causing massive buffers.\n // Keep only the last 2000 chars so completion markers and recent tool calls stay visible.\n if (this.busyBuffer.length > 3000) {\n this.busyBuffer = this.busyBuffer.slice(-2000);\n }\n\n // ── 1. Extract progress for UI feedback ──\n this.emitProgress();\n\n // ── 2. Track spinner activity (detect Claude still working) ──\n // Extract seconds counter from spinner text: \"Gesticulating… (28s)\", \"Seasoning… (21s)\"\n // On Windows, these verbs are random (Gesticulating, Seasoning, Topsy-turvying, etc.)\n const secMatches = [...this.busyBuffer.matchAll(/\\w{3,}ing[…\\s]*\\((\\d{1,3})s?/g)];\n for (const m of secMatches) {\n const sec = parseInt(m[1]);\n if (sec > this.lastSpinnerSec) {\n this.lastSpinnerSec = sec;\n this.lastSpinnerAt = Date.now();\n }\n }\n\n // ── 3. Try to extract response text ──\n const respMatch = this.busyBuffer.match(/⏺([一-鿿 -〿＀-￯].+)/s);\n if (respMatch) {\n let raw = respMatch[1];\n raw = raw.replace(/[✳✶✻✽✢·].*$/s, '').trim();\n raw = raw.replace(/─{3,}.*$/s, '').trim();\n raw = raw.replace(/\\w{3,}ed (?:for|in) \\d{1,4}s?.*$/s, '').trim();\n raw = raw.replace(/esctointerrupt.*$/s, '').trim();\n\n if (raw.length > this.ptyResponseText.length) {\n this.ptyResponseText = raw;\n this.emitIncrementalText();\n }\n }\n\n // ── 4. Extract tool calls (with dedup to prevent infinite re-emission) ──\n // macOS format: ⏺ Read(file) | Windows format: ● Reading 1 file… ⎿ file\n const toolCallMatch = this.busyBuffer.match(/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/);\n if (toolCallMatch) {\n const sig = `${toolCallMatch[1]}:${toolCallMatch[2]}`;\n if (!this.emittedTools.has(sig)) {\n this.emittedTools.add(sig);\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: toolCallMatch[1], input: { file: toolCallMatch[2] } },\n } as SseEvent);\n }\n }\n // Windows format: \"● Reading 1 file…\\n ⎿ src\\\\components\\\\Foo.tsx\"\n const winToolMatch = this.busyBuffer.match(/● (Reading|Editing|Writing|Searching|Bashing)[^\\n]*\\n\\s*⎿\\s*(.+)/);\n if (!toolCallMatch && winToolMatch) {\n const filePath = winToolMatch[2].trim();\n const sig = `${winToolMatch[1]}:${filePath}`;\n if (!this.emittedTools.has(sig)) {\n this.emittedTools.add(sig);\n this.emit('message', {\n type: 'assistant_tool',\n tool: { name: winToolMatch[1], input: { file: filePath } },\n } as SseEvent);\n }\n }\n\n // ── 5. Detect completion ──\n if (this.ptyDoneEmitted) return;\n\n // 5a. Explicit completion marker: \"Brewed for Xs\", \"Sautéed for Xs\", etc.\n // Use broad pattern since Claude uses random creative verbs.\n const cleanBuf = stripSpinner(this.busyBuffer);\n const hasDoneMarker = /\\b\\w+ed (?:for|in) \\d{1,4}s?\\b/i.test(cleanBuf);\n\n // 5b. Spinner timeout: spinner was active, then stopped for 10+ seconds.\n // On Windows ConPTY, Claude's TUI sends spinner frames continuously while working.\n // If spinner stops, Claude has finished (or errored out).\n const spinnerTimeout = this.lastSpinnerAt > 0\n && Date.now() - this.lastSpinnerAt > 10000\n && this.ptyResponseEmitted > 0;\n\n if (hasDoneMarker || spinnerTimeout) {\n const reason = hasDoneMarker\n ? 'completion marker'\n : `spinner timeout (${Math.round((Date.now() - this.lastSpinnerAt) / 1000)}s since last activity)`;\n console.log(`[pty-session ${this.id}] detected done via ${reason}`);\n this.ptyDoneEmitted = true;\n\n // Final flush: emit any remaining text\n if (this.ptyResponseText.length > this.ptyResponseEmitted) {\n this.emitIncrementalText();\n }\n\n // If no response was emitted at all, try one more aggressive extraction\n if (this.ptyResponseEmitted === 0) {\n const finalMatch = this.busyBuffer.match(/⏺(.+)/s);\n if (finalMatch) {\n let text = finalMatch[1]\n .replace(/[✳✶✻✽✢·].*$/s, '')\n .replace(/─{3,}.*$/s, '')\n .replace(/\\w{3,}ed (?:for|in) \\d{1,4}s?.*$/s, '')\n .replace(/esctointerrupt.*$/s, '')\n .trim();\n if (text.length > 0) {\n this.emitUserIfNeeded();\n this.history.push({\n role: 'assistant', content: text, timestamp: Date.now(),\n });\n this.emit('message', { type: 'assistant_text', content: text } as SseEvent);\n this.ptyResponseEmitted = text.length;\n }\n }\n }\n\n // Extract duration\n const durMatch = cleanBuf.match(/\\w+ed (?:for|in) (\\d{1,4})s?/i);\n const durationMs = durMatch ? parseInt(durMatch[1]) * 1000 : 0;\n\n // Update title from first user message\n if (this.history.filter(m => m.role === 'user').length <= 1 && this.lastUserContent) {\n this.title = this.lastUserContent.slice(0, 60);\n this.emit('title-change', this.title);\n }\n\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs } as SseEvent);\n }\n }\n\n /** Emit only the newly arrived characters (incremental streaming) */\n private emitIncrementalText(): void {\n const newText = this.ptyResponseText.slice(this.ptyResponseEmitted);\n if (newText.length === 0) return;\n\n // First chunk → emit user message + start assistant\n if (this.ptyResponseEmitted === 0) {\n this.emitUserIfNeeded();\n }\n\n this.ptyResponseEmitted = this.ptyResponseText.length;\n this.emit('message', { type: 'assistant_text', content: newText } as SseEvent);\n }\n\n /**\n * Extract and emit progress updates from busyBuffer.\n *\n * Parses PTY output for Claude Code's progress indicators:\n * - \"Thinking for Xs, reading N files\"\n * - \"Thought for Xs, read N files\"\n * - \"Crafting… (Xs · ↓NN tokens)\"\n * - \"Update(file)\" / \"Read(file)\"\n * - \"⎿ Removed N lines\"\n * - \"(Xs · ↓NN tokens)\" timing\n */\n private emitProgress(): void {\n // Strip spinner chars + noise, collapse whitespace\n const text = this.busyBuffer\n .replace(/[✳✶✻✽✢·][a-zA-Z0-9…]{0,4}/g, '')\n .replace(/\\s+/g, ' ');\n\n // Ordered by specificity — last match wins (most recent progress)\n const patterns: [RegExp, (m: RegExpMatchArray) => string][] = [\n // Thinking phase\n [/Thinking for (\\d+s)[^─]{0,60}(reading \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Thought completed\n [/Thought for (\\d+s)[^─]{0,60}(read \\d+ file[^)]*)?/, (m) => {\n return m[0].replace(/\\s+/g, ' ').replace(/\\s*\\(ctrl.*$/, '').trim();\n }],\n // Tool call: Update(file) / Read(file)\n [/⏺(Update|Read|Edit|Write|Bash)\\(([^)]+)\\)/, (m) => {\n return m[1] + ': ' + m[2].split('/').slice(-2).join('/');\n }],\n // Tool result: ⎿ Removed N lines\n [/⎿\\s*(Removed|Added|Modified|Created)\\s+(\\d+)\\s+(lines?)/, (m) => {\n return m[1] + ' ' + m[2] + ' ' + m[3];\n }],\n // Crafting with timing\n [/Crafting[^─]{0,30}\\(\\d+s[^)]*\\)/, (m) => {\n return m[0].replace(/\\s+/g, ' ').trim();\n }],\n // Simple timing: (5s · ↓9 tokens)\n [/\\((\\d+s)\\s*·\\s*[↓↑]\\s*(\\d+)\\s*tokens?\\)/, (m) => {\n return m[1] + ' · ' + m[2] + ' tokens';\n }],\n ];\n\n // Find the last matching pattern\n let progress = '';\n for (const [re, fn] of patterns) {\n const m = text.match(re);\n if (m) progress = fn(m);\n }\n\n // Emit only if changed\n if (progress && progress !== this.lastProgress) {\n this.lastProgress = progress;\n this.emit('message', { type: 'progress', content: progress } as SseEvent);\n }\n }\n\n /** Emit user message event (only if not already emitted this turn) */\n private emitUserIfNeeded(): void {\n if (!this.lastUserContent) return;\n // Check if user message already in history\n const already = this.history.some(\n m => m.role === 'user' && m.content === this.lastUserContent,\n );\n if (!already) {\n this.history.push({\n role: 'user',\n content: this.lastUserContent,\n timestamp: Date.now(),\n });\n this.emit('message', { type: 'user', content: this.lastUserContent } as SseEvent);\n }\n }\n\n // ── JSONL Discovery & Parsing (structured events) ─────\n\n private async discoverJsonl(): Promise<void> {\n const searchStart = this.messageSentAt - 2000;\n const projectsDir = findClaudeProjectsDir();\n const expectedSubdir = cwdToProjectDir(this.cwd);\n console.log(`[pty-session ${this.id}] JSONL discovery: projectsDir=${projectsDir}, expected=${expectedSubdir}, cwd=${this.cwd}`);\n\n // Poll for up to 60 seconds (120 × 500ms)\n for (let i = 0; i < 120; i++) {\n if (this.killed) return;\n const jsonl = findRecentJsonl(this.cwd, searchStart);\n if (jsonl) {\n this.jsonlPath = jsonl;\n this.sessionId = sessionIdFromJsonlPath(jsonl) || null;\n console.log(`[pty-session ${this.id}] found JSONL: ${jsonl} (session: ${this.sessionId})`);\n this.startTailingJsonl();\n return;\n }\n // Log available dirs after 5s for debugging\n if (i === 10) {\n try {\n if (fs.existsSync(projectsDir)) {\n const dirs = fs.readdirSync(projectsDir, { withFileTypes: true })\n .filter(d => d.isDirectory())\n .map(d => d.name);\n console.log(`[pty-session ${this.id}] JSONL not found in expected dir. Available project dirs: ${dirs.join(', ')}`);\n } else {\n console.log(`[pty-session ${this.id}] projectsDir does not exist: ${projectsDir}`);\n }\n } catch { /* ignore */ }\n }\n await new Promise(r => setTimeout(r, 500));\n }\n console.warn(`[pty-session ${this.id}] JSONL not found after 60s — using PTY output parsing`);\n }\n\n private startTailingJsonl(): void {\n if (!this.jsonlPath) return;\n\n this.readNewJsonlLines();\n\n try {\n this.jsonlWatcher = fs.watch(\n path.dirname(this.jsonlPath),\n (eventType, filename) => {\n if (filename === path.basename(this.jsonlPath!)) {\n this.readNewJsonlLines();\n }\n },\n );\n } catch (err) {\n console.warn(`[pty-session ${this.id}] fs.watch failed:`, err);\n }\n }\n\n private readNewJsonlLines(): void {\n if (!this.jsonlPath) return;\n\n try {\n const stat = fs.statSync(this.jsonlPath);\n if (stat.size <= this.jsonlOffset) return;\n\n const fd = fs.openSync(this.jsonlPath, 'r');\n const buf = Buffer.alloc(stat.size - this.jsonlOffset);\n fs.readSync(fd, buf, 0, buf.length, this.jsonlOffset);\n fs.closeSync(fd);\n this.jsonlOffset = stat.size;\n\n const text = buf.toString('utf8');\n for (const line of text.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const evt: JsonlEvent = JSON.parse(trimmed);\n this.processJsonlEvent(evt);\n } catch {\n // skip malformed lines\n }\n }\n } catch {\n // file might be temporarily unavailable\n }\n }\n\n /** Process a JSONL event — marks usedJsonl to disable PTY parsing */\n private processJsonlEvent(evt: JsonlEvent): void {\n // Once JSONL events arrive, disable PTY output parsing\n this.usedJsonl = true;\n\n if (!this.sessionId && evt.sessionId) {\n this.sessionId = evt.sessionId;\n }\n\n switch (evt.type) {\n case 'user': {\n const text = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n : '';\n this.history.push({\n role: 'user',\n content: text,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'user', content: text } as SseEvent);\n if (this.history.filter(m => m.role === 'user').length === 1 && text) {\n this.title = text.slice(0, 60);\n this.emit('title-change', this.title);\n }\n break;\n }\n case 'assistant': {\n const content = evt.message?.content;\n if (!Array.isArray(content)) break;\n\n const texts = content\n .filter((c: any) => c.type === 'text')\n .map((c: any) => c.text)\n .join('\\n')\n .trim();\n\n const tools: ToolUseInfo[] = content\n .filter((c: any) => c.type === 'tool_use')\n .map((c: any) => ({\n name: c.name,\n input: c.input || {},\n }));\n\n if (texts || tools.length) {\n this.history.push({\n role: 'assistant',\n content: texts,\n toolUse: tools.length ? tools : undefined,\n timestamp: evt.timestamp ? new Date(evt.timestamp).getTime() : Date.now(),\n });\n }\n\n if (texts) {\n this.emit('message', { type: 'assistant_text', content: texts } as SseEvent);\n }\n for (const t of tools) {\n this.emit('message', { type: 'assistant_tool', tool: t } as SseEvent);\n }\n break;\n }\n case 'system': {\n if (evt.subtype === 'tool_result') {\n const lastAssistant = [...this.history].reverse().find(m => m.role === 'assistant' && m.toolUse?.length);\n if (lastAssistant?.toolUse?.length) {\n const lastTool = lastAssistant.toolUse[lastAssistant.toolUse.length - 1];\n const resultText = typeof evt.message?.content === 'string'\n ? evt.message.content : '';\n lastTool.result = resultText.slice(0, 500);\n }\n const resultContent = typeof evt.message?.content === 'string'\n ? evt.message.content\n : Array.isArray(evt.message?.content)\n ? evt.message.content.map((c: any) => c.text || '').join('')\n : '';\n this.emit('message', { type: 'system', content: resultContent.slice(0, 200) } as SseEvent);\n } else if (evt.subtype === 'turn_duration') {\n this.status = 'ready';\n this.lastActivityAt = Date.now();\n this.emit('message', { type: 'done', durationMs: evt.durationMs } as SseEvent);\n }\n break;\n }\n }\n }\n\n // ── Lifecycle ─────────────────────────────────────────\n\n kill(): void {\n this.killed = true;\n this.cleanup();\n if (this.pty) {\n try { this.pty.kill(); } catch { /* already dead */ }\n this.pty = null;\n }\n this.status = 'exited';\n }\n\n private cleanup(): void {\n if (this.jsonlWatcher) {\n this.jsonlWatcher.close();\n this.jsonlWatcher = null;\n }\n }\n\n getInfo(): {\n id: string;\n title: string;\n status: SessionStatus;\n createdAt: number;\n lastActivityAt: number;\n messageCount: number;\n lastMessagePreview: string;\n sessionId: string | null;\n } {\n const lastMsg = this.history[this.history.length - 1];\n return {\n id: this.id,\n title: this.title,\n status: this.status,\n createdAt: this.spawnTime,\n lastActivityAt: this.lastActivityAt,\n messageCount: this.history.length,\n lastMessagePreview: lastMsg ? lastMsg.content.slice(0, 80) : '',\n sessionId: this.sessionId,\n };\n }\n\n getHistory(): ChatMessage[] {\n return [...this.history];\n }\n}\n","/**\n * CC Prompter — Asset Loader\n *\n * 运行时从包根目录读取 panel.html 和 inject.js。\n * 兼容 ESM(import.meta.url)和 CJS(__dirname)。\n *\n * 解析策略:\n * - tsup 构建后:资产文件复制到 dist/,__dirname 是 dist/\n * - Vite 直接引用 TS:__dirname 是 src/,资产文件在上一级\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\n\n// Resolve __dirname in both ESM and CJS contexts\nconst _dirname = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url));\n\n/** Resolve asset path — checks dist/ (build) then parent (dev) */\nfunction assetPath(filename: string): string {\n // 1. Same dir as this file (dist/ after build, or src/ in dev)\n const here = join(_dirname, filename);\n if (existsSync(here)) return here;\n\n // 2. Parent dir (package root — when running from src/ via vite)\n const parent = join(_dirname, '..', filename);\n if (existsSync(parent)) return parent;\n\n // 3. Fallback — let it throw with a clear message\n throw new Error(\n `[cc-prompter] Asset not found: ${filename}\\n` +\n ` Tried: ${here}\\n` +\n ` Tried: ${parent}\\n` +\n ` __dirname: ${_dirname}`\n );\n}\n\nexport function getPanelHtml(): string {\n return readFileSync(assetPath('panel.html'), 'utf8');\n}\n\nexport function getInjectScript(): string {\n return readFileSync(assetPath('inject.js'), 'utf8');\n}\n"],"mappings":"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","names":["require","resolve","existsSync","join","dirname","fileURLToPath","resolve"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-prompter",
3
- "version": "0.1.11",
3
+ "version": "0.2.1",
4
4
  "description": "Vite plugin — Shift+Alt click element → Claude Code edits code → page auto-refreshes",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",