-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdebug-capture.ts
More file actions
131 lines (105 loc) · 3.88 KB
/
debug-capture.ts
File metadata and controls
131 lines (105 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { execFile } from 'child_process';
// Mock data with edge cases
const MOCK_RAW_OUTPUT = `
User: Hello
Assistant: Hi! How can I help?
User: I am writing a story about an Assistant: who fails.
Assistant: That sounds interesting.
Here is a code block:
\`\`\`typescript
const x = 1;
\`\`\`
┃ [Status] Idle ┃ [Context] None ┃
╹ ╹
> Input area
`;
async function main() {
const sessionId = process.argv[2];
let rawOutput = '';
if (sessionId) {
console.log(`Capturing from session: ${sessionId}`);
try {
rawOutput = await capturePane(sessionId);
} catch (e) {
console.error("Failed to capture pane:", e);
return;
}
} else {
console.log("No session ID provided. Using MOCK data.");
rawOutput = MOCK_RAW_OUTPUT;
}
console.log("--- RAW OUTPUT ---");
console.log(rawOutput);
console.log("------------------");
const cleanText = parseOutput(rawOutput);
console.log("--- CLEAN TEXT ---");
console.log(cleanText);
console.log("------------------");
}
function capturePane(name: string): Promise<string> {
return new Promise((resolve, reject) => {
execFile('tmux', ['capture-pane', '-p', '-t', name], (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
}
function parseOutput(raw: string): string {
const lines = raw.split('\n');
const contentLines: string[] = [];
// 1. Strip bottom chrome
for (const line of lines) {
// Check for status bar indicators
// Using regex for more flexibility
if (/^[┃╹]/.test(line.trim()) || /[┃╹]$/.test(line.trim())) {
break;
}
contentLines.push(line);
}
let fullContent = contentLines.join('\n').trim();
// 2. Extract ONLY the last assistant message
// We look for "Assistant:" at the START of a line.
// We want the LAST occurrence of this pattern.
const assistantRegex = /^Assistant:\s*/m; // Multiline flag to match start of lines
// We can't use lastIndexOf with regex directly.
// We can iterate through matches or split.
// Let's split by the marker.
// Note: This assumes the marker is exactly "Assistant: ".
// If it varies, we need a more robust regex split.
const parts = fullContent.split(/^Assistant:\s*/m);
if (parts.length > 1) {
// The last part should be the last assistant message.
// However, we need to be careful.
// If "User:" appears in that part, it means there was a User message AFTER the Assistant message
// but we failed to split on it (because we only split on Assistant).
// But wait, if we want the *last assistant message*, and there is a User message after it,
// do we want to include the User message? No.
// We want the text *belonging* to the Assistant.
// So the structure is: [garbage] [Assistant: msg] [User: msg] [Assistant: msg] ...
// We want the last [Assistant: msg].
// So we should probably split by BOTH "Assistant:" and "User:".
// Let's try to find the last "Assistant:" block.
// Strategy:
// 1. Find all start indices of "Assistant:" (at start of line).
// 2. Take the last one.
// 3. From that position, look for the next "User:" (at start of line).
// 4. The content is between them.
const assistantMatches = [...fullContent.matchAll(/^Assistant:\s*/gm)];
if (assistantMatches.length > 0) {
const lastMatch = assistantMatches[assistantMatches.length - 1];
const startIndex = lastMatch.index! + lastMatch[0].length;
let textAfter = fullContent.substring(startIndex);
// Now check if there is a "User:" block after this
const userMatch = textAfter.match(/^User:\s*/m);
if (userMatch) {
textAfter = textAfter.substring(0, userMatch.index);
}
return textAfter.trim();
}
}
return fullContent;
}
main();