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 +49 -2
- package/dist/index.cjs +94 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +94 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,55 @@
|
|
|
1
1
|
# CC Prompter
|
|
2
2
|
|
|
3
|
-
|
|
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
|

|
|
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
|
-
|
|
121
|
-
|
|
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
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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 (
|
|
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(
|
|
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 =
|
|
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`);
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
-
|
|
84
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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 (
|
|
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(
|
|
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 =
|
|
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"]}
|