@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.
Files changed (78) hide show
  1. package/README.md +485 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +21 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/export-html.d.ts +7 -0
  7. package/dist/export-html.d.ts.map +1 -0
  8. package/dist/export-html.js +650 -0
  9. package/dist/export-html.js.map +1 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +4 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/main.d.ts +2 -0
  15. package/dist/main.d.ts.map +1 -0
  16. package/dist/main.js +514 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/session-manager.d.ts +70 -0
  19. package/dist/session-manager.d.ts.map +1 -0
  20. package/dist/session-manager.js +323 -0
  21. package/dist/session-manager.js.map +1 -0
  22. package/dist/tools/bash.d.ts +7 -0
  23. package/dist/tools/bash.d.ts.map +1 -0
  24. package/dist/tools/bash.js +130 -0
  25. package/dist/tools/bash.js.map +1 -0
  26. package/dist/tools/edit.d.ts +9 -0
  27. package/dist/tools/edit.d.ts.map +1 -0
  28. package/dist/tools/edit.js +207 -0
  29. package/dist/tools/edit.js.map +1 -0
  30. package/dist/tools/index.d.ts +19 -0
  31. package/dist/tools/index.d.ts.map +1 -0
  32. package/dist/tools/index.js +10 -0
  33. package/dist/tools/index.js.map +1 -0
  34. package/dist/tools/read.d.ts +9 -0
  35. package/dist/tools/read.d.ts.map +1 -0
  36. package/dist/tools/read.js +165 -0
  37. package/dist/tools/read.js.map +1 -0
  38. package/dist/tools/write.d.ts +8 -0
  39. package/dist/tools/write.d.ts.map +1 -0
  40. package/dist/tools/write.js +81 -0
  41. package/dist/tools/write.js.map +1 -0
  42. package/dist/tui/assistant-message.d.ts +11 -0
  43. package/dist/tui/assistant-message.d.ts.map +1 -0
  44. package/dist/tui/assistant-message.js +53 -0
  45. package/dist/tui/assistant-message.js.map +1 -0
  46. package/dist/tui/custom-editor.d.ts +10 -0
  47. package/dist/tui/custom-editor.d.ts.map +1 -0
  48. package/dist/tui/custom-editor.js +24 -0
  49. package/dist/tui/custom-editor.js.map +1 -0
  50. package/dist/tui/footer.d.ts +11 -0
  51. package/dist/tui/footer.d.ts.map +1 -0
  52. package/dist/tui/footer.js +101 -0
  53. package/dist/tui/footer.js.map +1 -0
  54. package/dist/tui/model-selector.d.ts +23 -0
  55. package/dist/tui/model-selector.d.ts.map +1 -0
  56. package/dist/tui/model-selector.js +157 -0
  57. package/dist/tui/model-selector.js.map +1 -0
  58. package/dist/tui/session-selector.d.ts +37 -0
  59. package/dist/tui/session-selector.d.ts.map +1 -0
  60. package/dist/tui/session-selector.js +176 -0
  61. package/dist/tui/session-selector.js.map +1 -0
  62. package/dist/tui/thinking-selector.d.ts +11 -0
  63. package/dist/tui/thinking-selector.d.ts.map +1 -0
  64. package/dist/tui/thinking-selector.js +48 -0
  65. package/dist/tui/thinking-selector.js.map +1 -0
  66. package/dist/tui/tool-execution.d.ts +26 -0
  67. package/dist/tui/tool-execution.d.ts.map +1 -0
  68. package/dist/tui/tool-execution.js +246 -0
  69. package/dist/tui/tool-execution.js.map +1 -0
  70. package/dist/tui/tui-renderer.d.ts +44 -0
  71. package/dist/tui/tui-renderer.d.ts.map +1 -0
  72. package/dist/tui/tui-renderer.js +539 -0
  73. package/dist/tui/tui-renderer.js.map +1 -0
  74. package/dist/tui/user-message.d.ts +9 -0
  75. package/dist/tui/user-message.d.ts.map +1 -0
  76. package/dist/tui/user-message.js +18 -0
  77. package/dist/tui/user-message.js.map +1 -0
  78. 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, "&lt;")
37
+ .replace(/>/g, "&gt;")
38
+ .replace(/"/g, "&quot;")
39
+ .replace(/'/g, "&#039;");
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