@mariozechner/pi-coding-agent 0.6.2
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 +485 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +21 -0
- package/dist/cli.js.map +1 -0
- package/dist/export-html.d.ts +7 -0
- package/dist/export-html.d.ts.map +1 -0
- package/dist/export-html.js +650 -0
- package/dist/export-html.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +514 -0
- package/dist/main.js.map +1 -0
- package/dist/session-manager.d.ts +70 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +323 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/tools/bash.d.ts +7 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +130 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +9 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +207 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/read.d.ts +9 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +165 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/write.d.ts +8 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +81 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/tui/assistant-message.d.ts +11 -0
- package/dist/tui/assistant-message.d.ts.map +1 -0
- package/dist/tui/assistant-message.js +53 -0
- package/dist/tui/assistant-message.js.map +1 -0
- package/dist/tui/custom-editor.d.ts +10 -0
- package/dist/tui/custom-editor.d.ts.map +1 -0
- package/dist/tui/custom-editor.js +24 -0
- package/dist/tui/custom-editor.js.map +1 -0
- package/dist/tui/footer.d.ts +11 -0
- package/dist/tui/footer.d.ts.map +1 -0
- package/dist/tui/footer.js +101 -0
- package/dist/tui/footer.js.map +1 -0
- package/dist/tui/model-selector.d.ts +23 -0
- package/dist/tui/model-selector.d.ts.map +1 -0
- package/dist/tui/model-selector.js +157 -0
- package/dist/tui/model-selector.js.map +1 -0
- package/dist/tui/session-selector.d.ts +37 -0
- package/dist/tui/session-selector.d.ts.map +1 -0
- package/dist/tui/session-selector.js +176 -0
- package/dist/tui/session-selector.js.map +1 -0
- package/dist/tui/thinking-selector.d.ts +11 -0
- package/dist/tui/thinking-selector.d.ts.map +1 -0
- package/dist/tui/thinking-selector.js +48 -0
- package/dist/tui/thinking-selector.js.map +1 -0
- package/dist/tui/tool-execution.d.ts +26 -0
- package/dist/tui/tool-execution.d.ts.map +1 -0
- package/dist/tui/tool-execution.js +246 -0
- package/dist/tui/tool-execution.js.map +1 -0
- package/dist/tui/tui-renderer.d.ts +44 -0
- package/dist/tui/tui-renderer.d.ts.map +1 -0
- package/dist/tui/tui-renderer.js +539 -0
- package/dist/tui/tui-renderer.js.map +1 -0
- package/dist/tui/user-message.d.ts +9 -0
- package/dist/tui/user-message.d.ts.map +1 -0
- package/dist/tui/user-message.js +18 -0
- package/dist/tui/user-message.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { basename, dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
// Get version from package.json
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
|
9
|
+
const VERSION = packageJson.version;
|
|
10
|
+
/**
|
|
11
|
+
* TUI Color scheme (matching exact RGB values from TUI components)
|
|
12
|
+
*/
|
|
13
|
+
const COLORS = {
|
|
14
|
+
// Backgrounds
|
|
15
|
+
userMessageBg: "rgb(52, 53, 65)", // Dark slate
|
|
16
|
+
toolPendingBg: "rgb(40, 40, 50)", // Dark blue-gray
|
|
17
|
+
toolSuccessBg: "rgb(40, 50, 40)", // Dark green
|
|
18
|
+
toolErrorBg: "rgb(60, 40, 40)", // Dark red
|
|
19
|
+
bodyBg: "rgb(24, 24, 30)", // Very dark background
|
|
20
|
+
containerBg: "rgb(30, 30, 36)", // Slightly lighter container
|
|
21
|
+
// Text colors (matching chalk colors)
|
|
22
|
+
text: "rgb(229, 229, 231)", // Light gray (close to white)
|
|
23
|
+
textDim: "rgb(161, 161, 170)", // Dimmed gray
|
|
24
|
+
cyan: "rgb(103, 232, 249)", // Cyan for paths
|
|
25
|
+
green: "rgb(34, 197, 94)", // Green for success
|
|
26
|
+
red: "rgb(239, 68, 68)", // Red for errors
|
|
27
|
+
yellow: "rgb(234, 179, 8)", // Yellow for warnings
|
|
28
|
+
italic: "rgb(161, 161, 170)", // Gray italic for thinking
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Escape HTML special characters
|
|
32
|
+
*/
|
|
33
|
+
function escapeHtml(text) {
|
|
34
|
+
return text
|
|
35
|
+
.replace(/&/g, "&")
|
|
36
|
+
.replace(/</g, "<")
|
|
37
|
+
.replace(/>/g, ">")
|
|
38
|
+
.replace(/"/g, """)
|
|
39
|
+
.replace(/'/g, "'");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Shorten path with tilde notation
|
|
43
|
+
*/
|
|
44
|
+
function shortenPath(path) {
|
|
45
|
+
const home = homedir();
|
|
46
|
+
if (path.startsWith(home)) {
|
|
47
|
+
return "~" + path.slice(home.length);
|
|
48
|
+
}
|
|
49
|
+
return path;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Replace tabs with 3 spaces
|
|
53
|
+
*/
|
|
54
|
+
function replaceTabs(text) {
|
|
55
|
+
return text.replace(/\t/g, " ");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Format tool execution matching TUI ToolExecutionComponent
|
|
59
|
+
*/
|
|
60
|
+
function formatToolExecution(toolName, args, result) {
|
|
61
|
+
let html = "";
|
|
62
|
+
const isError = result?.isError || false;
|
|
63
|
+
const bgColor = result ? (isError ? COLORS.toolErrorBg : COLORS.toolSuccessBg) : COLORS.toolPendingBg;
|
|
64
|
+
// Get text output from result
|
|
65
|
+
const getTextOutput = () => {
|
|
66
|
+
if (!result)
|
|
67
|
+
return "";
|
|
68
|
+
const textBlocks = result.content.filter((c) => c.type === "text");
|
|
69
|
+
return textBlocks.map((c) => c.text).join("\n");
|
|
70
|
+
};
|
|
71
|
+
// Format based on tool type (matching TUI logic exactly)
|
|
72
|
+
if (toolName === "bash") {
|
|
73
|
+
const command = args?.command || "";
|
|
74
|
+
html = `<div class="tool-command">$ ${escapeHtml(command || "...")}</div>`;
|
|
75
|
+
if (result) {
|
|
76
|
+
const output = getTextOutput().trim();
|
|
77
|
+
if (output) {
|
|
78
|
+
const lines = output.split("\n");
|
|
79
|
+
const maxLines = 5;
|
|
80
|
+
const displayLines = lines.slice(0, maxLines);
|
|
81
|
+
const remaining = lines.length - maxLines;
|
|
82
|
+
if (remaining > 0) {
|
|
83
|
+
// Truncated output - make it expandable
|
|
84
|
+
html += '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
|
85
|
+
html += '<div class="output-preview">';
|
|
86
|
+
for (const line of displayLines) {
|
|
87
|
+
html += `<div>${escapeHtml(line)}</div>`;
|
|
88
|
+
}
|
|
89
|
+
html += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
|
90
|
+
html += "</div>";
|
|
91
|
+
html += '<div class="output-full">';
|
|
92
|
+
for (const line of lines) {
|
|
93
|
+
html += `<div>${escapeHtml(line)}</div>`;
|
|
94
|
+
}
|
|
95
|
+
html += "</div>";
|
|
96
|
+
html += "</div>";
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Short output - show all
|
|
100
|
+
html += '<div class="tool-output">';
|
|
101
|
+
for (const line of displayLines) {
|
|
102
|
+
html += `<div>${escapeHtml(line)}</div>`;
|
|
103
|
+
}
|
|
104
|
+
html += "</div>";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (toolName === "read") {
|
|
110
|
+
const path = shortenPath(args?.file_path || args?.path || "");
|
|
111
|
+
html = `<div class="tool-header"><span class="tool-name">read</span> <span class="tool-path">${escapeHtml(path || "...")}</span></div>`;
|
|
112
|
+
if (result) {
|
|
113
|
+
const output = getTextOutput();
|
|
114
|
+
const lines = output.split("\n");
|
|
115
|
+
const maxLines = 10;
|
|
116
|
+
const displayLines = lines.slice(0, maxLines);
|
|
117
|
+
const remaining = lines.length - maxLines;
|
|
118
|
+
if (remaining > 0) {
|
|
119
|
+
// Truncated output - make it expandable
|
|
120
|
+
html += '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
|
121
|
+
html += '<div class="output-preview">';
|
|
122
|
+
for (const line of displayLines) {
|
|
123
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
124
|
+
}
|
|
125
|
+
html += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
|
126
|
+
html += "</div>";
|
|
127
|
+
html += '<div class="output-full">';
|
|
128
|
+
for (const line of lines) {
|
|
129
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
130
|
+
}
|
|
131
|
+
html += "</div>";
|
|
132
|
+
html += "</div>";
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Short output - show all
|
|
136
|
+
html += '<div class="tool-output">';
|
|
137
|
+
for (const line of displayLines) {
|
|
138
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
139
|
+
}
|
|
140
|
+
html += "</div>";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else if (toolName === "write") {
|
|
145
|
+
const path = shortenPath(args?.file_path || args?.path || "");
|
|
146
|
+
const fileContent = args?.content || "";
|
|
147
|
+
const lines = fileContent ? fileContent.split("\n") : [];
|
|
148
|
+
const totalLines = lines.length;
|
|
149
|
+
html = `<div class="tool-header"><span class="tool-name">write</span> <span class="tool-path">${escapeHtml(path || "...")}</span>`;
|
|
150
|
+
if (totalLines > 10) {
|
|
151
|
+
html += ` <span class="line-count">(${totalLines} lines)</span>`;
|
|
152
|
+
}
|
|
153
|
+
html += "</div>";
|
|
154
|
+
if (fileContent) {
|
|
155
|
+
const maxLines = 10;
|
|
156
|
+
const displayLines = lines.slice(0, maxLines);
|
|
157
|
+
const remaining = lines.length - maxLines;
|
|
158
|
+
if (remaining > 0) {
|
|
159
|
+
// Truncated output - make it expandable
|
|
160
|
+
html += '<div class="tool-output expandable" onclick="this.classList.toggle(\'expanded\')">';
|
|
161
|
+
html += '<div class="output-preview">';
|
|
162
|
+
for (const line of displayLines) {
|
|
163
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
164
|
+
}
|
|
165
|
+
html += `<div class="expand-hint">... (${remaining} more lines) - click to expand</div>`;
|
|
166
|
+
html += "</div>";
|
|
167
|
+
html += '<div class="output-full">';
|
|
168
|
+
for (const line of lines) {
|
|
169
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
170
|
+
}
|
|
171
|
+
html += "</div>";
|
|
172
|
+
html += "</div>";
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Short output - show all
|
|
176
|
+
html += '<div class="tool-output">';
|
|
177
|
+
for (const line of displayLines) {
|
|
178
|
+
html += `<div>${escapeHtml(replaceTabs(line))}</div>`;
|
|
179
|
+
}
|
|
180
|
+
html += "</div>";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (result) {
|
|
184
|
+
const output = getTextOutput().trim();
|
|
185
|
+
if (output) {
|
|
186
|
+
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (toolName === "edit") {
|
|
191
|
+
const path = shortenPath(args?.file_path || args?.path || "");
|
|
192
|
+
html = `<div class="tool-header"><span class="tool-name">edit</span> <span class="tool-path">${escapeHtml(path || "...")}</span></div>`;
|
|
193
|
+
// Show diff if available from result.details.diff
|
|
194
|
+
if (result?.details?.diff) {
|
|
195
|
+
const diffLines = result.details.diff.split("\n");
|
|
196
|
+
html += '<div class="tool-diff">';
|
|
197
|
+
for (const line of diffLines) {
|
|
198
|
+
if (line.startsWith("+")) {
|
|
199
|
+
html += `<div class="diff-line-new">${escapeHtml(line)}</div>`;
|
|
200
|
+
}
|
|
201
|
+
else if (line.startsWith("-")) {
|
|
202
|
+
html += `<div class="diff-line-old">${escapeHtml(line)}</div>`;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
html += `<div class="diff-line-context">${escapeHtml(line)}</div>`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
html += "</div>";
|
|
209
|
+
}
|
|
210
|
+
if (result) {
|
|
211
|
+
const output = getTextOutput().trim();
|
|
212
|
+
if (output) {
|
|
213
|
+
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// Generic tool
|
|
219
|
+
html = `<div class="tool-header"><span class="tool-name">${escapeHtml(toolName)}</span></div>`;
|
|
220
|
+
html += `<div class="tool-output"><pre>${escapeHtml(JSON.stringify(args, null, 2))}</pre></div>`;
|
|
221
|
+
if (result) {
|
|
222
|
+
const output = getTextOutput();
|
|
223
|
+
if (output) {
|
|
224
|
+
html += `<div class="tool-output"><div>${escapeHtml(output)}</div></div>`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return { html, bgColor };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Format a message as HTML (matching TUI component styling)
|
|
232
|
+
*/
|
|
233
|
+
function formatMessage(message, toolResultsMap) {
|
|
234
|
+
let html = "";
|
|
235
|
+
if (message.role === "user") {
|
|
236
|
+
const userMsg = message;
|
|
237
|
+
let textContent = "";
|
|
238
|
+
if (typeof userMsg.content === "string") {
|
|
239
|
+
textContent = userMsg.content;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const textBlocks = userMsg.content.filter((c) => c.type === "text");
|
|
243
|
+
textContent = textBlocks.map((c) => c.text).join("");
|
|
244
|
+
}
|
|
245
|
+
if (textContent.trim()) {
|
|
246
|
+
html += `<div class="user-message">${escapeHtml(textContent).replace(/\n/g, "<br>")}</div>`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (message.role === "assistant") {
|
|
250
|
+
const assistantMsg = message;
|
|
251
|
+
// Render text and thinking content
|
|
252
|
+
for (const content of assistantMsg.content) {
|
|
253
|
+
if (content.type === "text" && content.text.trim()) {
|
|
254
|
+
html += `<div class="assistant-text">${escapeHtml(content.text.trim()).replace(/\n/g, "<br>")}</div>`;
|
|
255
|
+
}
|
|
256
|
+
else if (content.type === "thinking" && content.thinking.trim()) {
|
|
257
|
+
html += `<div class="thinking-text">${escapeHtml(content.thinking.trim()).replace(/\n/g, "<br>")}</div>`;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Render tool calls with their results
|
|
261
|
+
for (const content of assistantMsg.content) {
|
|
262
|
+
if (content.type === "toolCall") {
|
|
263
|
+
const toolResult = toolResultsMap.get(content.id);
|
|
264
|
+
const { html: toolHtml, bgColor } = formatToolExecution(content.name, content.arguments, toolResult);
|
|
265
|
+
html += `<div class="tool-execution" style="background-color: ${bgColor}">${toolHtml}</div>`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Show error/abort status if no tool calls
|
|
269
|
+
const hasToolCalls = assistantMsg.content.some((c) => c.type === "toolCall");
|
|
270
|
+
if (!hasToolCalls) {
|
|
271
|
+
if (assistantMsg.stopReason === "aborted") {
|
|
272
|
+
html += '<div class="error-text">Aborted</div>';
|
|
273
|
+
}
|
|
274
|
+
else if (assistantMsg.stopReason === "error") {
|
|
275
|
+
const errorMsg = assistantMsg.errorMessage || "Unknown error";
|
|
276
|
+
html += `<div class="error-text">Error: ${escapeHtml(errorMsg)}</div>`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return html;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Export session to a self-contained HTML file matching TUI visual style
|
|
284
|
+
*/
|
|
285
|
+
export function exportSessionToHtml(sessionManager, state, outputPath) {
|
|
286
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
287
|
+
const timestamp = new Date().toISOString();
|
|
288
|
+
// Use session filename + .html if no output path provided
|
|
289
|
+
if (!outputPath) {
|
|
290
|
+
const sessionBasename = basename(sessionFile, ".jsonl");
|
|
291
|
+
outputPath = `${sessionBasename}.html`;
|
|
292
|
+
}
|
|
293
|
+
// Read and parse session data
|
|
294
|
+
const sessionContent = readFileSync(sessionFile, "utf8");
|
|
295
|
+
const lines = sessionContent.trim().split("\n");
|
|
296
|
+
let sessionHeader = null;
|
|
297
|
+
const messages = [];
|
|
298
|
+
const toolResultsMap = new Map();
|
|
299
|
+
for (const line of lines) {
|
|
300
|
+
try {
|
|
301
|
+
const entry = JSON.parse(line);
|
|
302
|
+
if (entry.type === "session") {
|
|
303
|
+
sessionHeader = entry;
|
|
304
|
+
}
|
|
305
|
+
else if (entry.type === "message") {
|
|
306
|
+
messages.push(entry.message);
|
|
307
|
+
// Build map of tool call ID to result
|
|
308
|
+
if (entry.message.role === "toolResult") {
|
|
309
|
+
toolResultsMap.set(entry.message.toolCallId, entry.message);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
// Skip malformed lines
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Generate messages HTML
|
|
318
|
+
let messagesHtml = "";
|
|
319
|
+
for (const message of messages) {
|
|
320
|
+
if (message.role !== "toolResult") {
|
|
321
|
+
// Skip toolResult messages as they're rendered with their tool calls
|
|
322
|
+
messagesHtml += formatMessage(message, toolResultsMap);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
// Generate HTML (matching TUI aesthetic)
|
|
326
|
+
const html = `<!DOCTYPE html>
|
|
327
|
+
<html lang="en">
|
|
328
|
+
<head>
|
|
329
|
+
<meta charset="UTF-8">
|
|
330
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
331
|
+
<title>Session Export - ${basename(sessionFile)}</title>
|
|
332
|
+
<style>
|
|
333
|
+
* {
|
|
334
|
+
margin: 0;
|
|
335
|
+
padding: 0;
|
|
336
|
+
box-sizing: border-box;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
body {
|
|
340
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
341
|
+
font-size: 14px;
|
|
342
|
+
line-height: 1.6;
|
|
343
|
+
color: ${COLORS.text};
|
|
344
|
+
background: ${COLORS.bodyBg};
|
|
345
|
+
padding: 24px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.container {
|
|
349
|
+
max-width: 1200px;
|
|
350
|
+
margin: 0 auto;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.header {
|
|
354
|
+
margin-bottom: 24px;
|
|
355
|
+
padding: 16px;
|
|
356
|
+
background: ${COLORS.containerBg};
|
|
357
|
+
border-radius: 4px;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.header h1 {
|
|
361
|
+
font-size: 16px;
|
|
362
|
+
font-weight: bold;
|
|
363
|
+
margin-bottom: 12px;
|
|
364
|
+
color: ${COLORS.cyan};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.header-info {
|
|
368
|
+
display: flex;
|
|
369
|
+
flex-direction: column;
|
|
370
|
+
gap: 6px;
|
|
371
|
+
font-size: 13px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.info-item {
|
|
375
|
+
color: ${COLORS.textDim};
|
|
376
|
+
display: flex;
|
|
377
|
+
align-items: baseline;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.info-label {
|
|
381
|
+
font-weight: 600;
|
|
382
|
+
margin-right: 8px;
|
|
383
|
+
min-width: 80px;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.info-value {
|
|
387
|
+
color: ${COLORS.text};
|
|
388
|
+
flex: 1;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.messages {
|
|
392
|
+
display: flex;
|
|
393
|
+
flex-direction: column;
|
|
394
|
+
gap: 16px;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* User message - matching TUI UserMessageComponent */
|
|
398
|
+
.user-message {
|
|
399
|
+
background: ${COLORS.userMessageBg};
|
|
400
|
+
padding: 12px 16px;
|
|
401
|
+
border-radius: 4px;
|
|
402
|
+
white-space: pre-wrap;
|
|
403
|
+
word-wrap: break-word;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Assistant text - matching TUI AssistantMessageComponent */
|
|
407
|
+
.assistant-text {
|
|
408
|
+
padding: 12px 16px;
|
|
409
|
+
white-space: pre-wrap;
|
|
410
|
+
word-wrap: break-word;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* Thinking text - gray italic */
|
|
414
|
+
.thinking-text {
|
|
415
|
+
padding: 12px 16px;
|
|
416
|
+
color: ${COLORS.italic};
|
|
417
|
+
font-style: italic;
|
|
418
|
+
white-space: pre-wrap;
|
|
419
|
+
word-wrap: break-word;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/* Tool execution - matching TUI ToolExecutionComponent */
|
|
423
|
+
.tool-execution {
|
|
424
|
+
padding: 12px 16px;
|
|
425
|
+
border-radius: 4px;
|
|
426
|
+
margin-top: 8px;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.tool-header {
|
|
430
|
+
font-weight: bold;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.tool-name {
|
|
434
|
+
font-weight: bold;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.tool-path {
|
|
438
|
+
color: ${COLORS.cyan};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.line-count {
|
|
442
|
+
color: ${COLORS.textDim};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.tool-command {
|
|
446
|
+
font-weight: bold;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.tool-output {
|
|
450
|
+
margin-top: 12px;
|
|
451
|
+
color: ${COLORS.textDim};
|
|
452
|
+
white-space: pre-wrap;
|
|
453
|
+
font-family: inherit;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.tool-output > div {
|
|
457
|
+
line-height: 1.4;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.tool-output pre {
|
|
461
|
+
margin: 0;
|
|
462
|
+
font-family: inherit;
|
|
463
|
+
color: inherit;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* Expandable tool output */
|
|
467
|
+
.tool-output.expandable {
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.tool-output.expandable:hover {
|
|
472
|
+
opacity: 0.9;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.tool-output.expandable .output-full {
|
|
476
|
+
display: none;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.tool-output.expandable.expanded .output-preview {
|
|
480
|
+
display: none;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.tool-output.expandable.expanded .output-full {
|
|
484
|
+
display: block;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.expand-hint {
|
|
488
|
+
color: ${COLORS.cyan};
|
|
489
|
+
font-style: italic;
|
|
490
|
+
margin-top: 4px;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/* System prompt section */
|
|
494
|
+
.system-prompt {
|
|
495
|
+
background: rgb(60, 55, 40);
|
|
496
|
+
padding: 12px 16px;
|
|
497
|
+
border-radius: 4px;
|
|
498
|
+
margin-bottom: 16px;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.system-prompt-header {
|
|
502
|
+
font-weight: bold;
|
|
503
|
+
color: ${COLORS.yellow};
|
|
504
|
+
margin-bottom: 8px;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.system-prompt-content {
|
|
508
|
+
color: ${COLORS.textDim};
|
|
509
|
+
white-space: pre-wrap;
|
|
510
|
+
word-wrap: break-word;
|
|
511
|
+
font-size: 13px;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.tools-list {
|
|
515
|
+
background: rgb(60, 55, 40);
|
|
516
|
+
padding: 12px 16px;
|
|
517
|
+
border-radius: 4px;
|
|
518
|
+
margin-bottom: 16px;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.tools-header {
|
|
522
|
+
font-weight: bold;
|
|
523
|
+
color: ${COLORS.yellow};
|
|
524
|
+
margin-bottom: 8px;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.tools-content {
|
|
528
|
+
color: ${COLORS.textDim};
|
|
529
|
+
font-size: 13px;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.tool-item {
|
|
533
|
+
margin: 4px 0;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.tool-item-name {
|
|
537
|
+
font-weight: bold;
|
|
538
|
+
color: ${COLORS.text};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Diff styling */
|
|
542
|
+
.tool-diff {
|
|
543
|
+
margin-top: 12px;
|
|
544
|
+
font-size: 13px;
|
|
545
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
|
546
|
+
overflow-x: auto;
|
|
547
|
+
max-width: 100%;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.diff-line-old {
|
|
551
|
+
color: ${COLORS.red};
|
|
552
|
+
white-space: pre;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.diff-line-new {
|
|
556
|
+
color: ${COLORS.green};
|
|
557
|
+
white-space: pre;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.diff-line-context {
|
|
561
|
+
color: ${COLORS.textDim};
|
|
562
|
+
white-space: pre;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* Error text */
|
|
566
|
+
.error-text {
|
|
567
|
+
color: ${COLORS.red};
|
|
568
|
+
padding: 12px 16px;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.footer {
|
|
572
|
+
margin-top: 48px;
|
|
573
|
+
padding: 20px;
|
|
574
|
+
text-align: center;
|
|
575
|
+
color: ${COLORS.textDim};
|
|
576
|
+
font-size: 12px;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
@media print {
|
|
580
|
+
body {
|
|
581
|
+
background: white;
|
|
582
|
+
color: black;
|
|
583
|
+
}
|
|
584
|
+
.tool-execution {
|
|
585
|
+
border: 1px solid #ddd;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
</style>
|
|
589
|
+
</head>
|
|
590
|
+
<body>
|
|
591
|
+
<div class="container">
|
|
592
|
+
<div class="header">
|
|
593
|
+
<h1>pi v${VERSION}</h1>
|
|
594
|
+
<div class="header-info">
|
|
595
|
+
<div class="info-item">
|
|
596
|
+
<span class="info-label">Session:</span>
|
|
597
|
+
<span class="info-value">${escapeHtml(sessionHeader?.id || "unknown")}</span>
|
|
598
|
+
</div>
|
|
599
|
+
<div class="info-item">
|
|
600
|
+
<span class="info-label">Date:</span>
|
|
601
|
+
<span class="info-value">${sessionHeader?.timestamp ? new Date(sessionHeader.timestamp).toLocaleString() : timestamp}</span>
|
|
602
|
+
</div>
|
|
603
|
+
<div class="info-item">
|
|
604
|
+
<span class="info-label">Model:</span>
|
|
605
|
+
<span class="info-value">${escapeHtml(sessionHeader?.model || state.model.id)}</span>
|
|
606
|
+
</div>
|
|
607
|
+
<div class="info-item">
|
|
608
|
+
<span class="info-label">Messages:</span>
|
|
609
|
+
<span class="info-value">${messages.filter((m) => m.role !== "toolResult").length}</span>
|
|
610
|
+
</div>
|
|
611
|
+
<div class="info-item">
|
|
612
|
+
<span class="info-label">Directory:</span>
|
|
613
|
+
<span class="info-value">${escapeHtml(shortenPath(sessionHeader?.cwd || process.cwd()))}</span>
|
|
614
|
+
</div>
|
|
615
|
+
<div class="info-item">
|
|
616
|
+
<span class="info-label">Thinking:</span>
|
|
617
|
+
<span class="info-value">${escapeHtml(sessionHeader?.thinkingLevel || state.thinkingLevel)}</span>
|
|
618
|
+
</div>
|
|
619
|
+
</div>
|
|
620
|
+
</div>
|
|
621
|
+
|
|
622
|
+
<div class="system-prompt">
|
|
623
|
+
<div class="system-prompt-header">System Prompt</div>
|
|
624
|
+
<div class="system-prompt-content">${escapeHtml(sessionHeader?.systemPrompt || state.systemPrompt)}</div>
|
|
625
|
+
</div>
|
|
626
|
+
|
|
627
|
+
<div class="tools-list">
|
|
628
|
+
<div class="tools-header">Available Tools</div>
|
|
629
|
+
<div class="tools-content">
|
|
630
|
+
${state.tools
|
|
631
|
+
.map((tool) => `<div class="tool-item"><span class="tool-item-name">${escapeHtml(tool.name)}</span> - ${escapeHtml(tool.description)}</div>`)
|
|
632
|
+
.join("")}
|
|
633
|
+
</div>
|
|
634
|
+
</div>
|
|
635
|
+
|
|
636
|
+
<div class="messages">
|
|
637
|
+
${messagesHtml}
|
|
638
|
+
</div>
|
|
639
|
+
|
|
640
|
+
<div class="footer">
|
|
641
|
+
Generated by pi coding-agent on ${new Date().toLocaleString()}
|
|
642
|
+
</div>
|
|
643
|
+
</div>
|
|
644
|
+
</body>
|
|
645
|
+
</html>`;
|
|
646
|
+
// Write HTML file
|
|
647
|
+
writeFileSync(outputPath, html, "utf8");
|
|
648
|
+
return outputPath;
|
|
649
|
+
}
|
|
650
|
+
//# sourceMappingURL=export-html.js.map
|