@oh-my-pi/pi-coding-agent 1.337.0

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 (224) hide show
  1. package/CHANGELOG.md +1228 -0
  2. package/README.md +1041 -0
  3. package/docs/compaction.md +403 -0
  4. package/docs/custom-tools.md +541 -0
  5. package/docs/extension-loading.md +1004 -0
  6. package/docs/hooks.md +867 -0
  7. package/docs/rpc.md +1040 -0
  8. package/docs/sdk.md +994 -0
  9. package/docs/session-tree-plan.md +441 -0
  10. package/docs/session.md +240 -0
  11. package/docs/skills.md +290 -0
  12. package/docs/theme.md +637 -0
  13. package/docs/tree.md +197 -0
  14. package/docs/tui.md +341 -0
  15. package/examples/README.md +21 -0
  16. package/examples/custom-tools/README.md +124 -0
  17. package/examples/custom-tools/hello/index.ts +20 -0
  18. package/examples/custom-tools/question/index.ts +84 -0
  19. package/examples/custom-tools/subagent/README.md +172 -0
  20. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  21. package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
  22. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  23. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  24. package/examples/custom-tools/subagent/agents.ts +156 -0
  25. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  26. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  27. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  28. package/examples/custom-tools/subagent/index.ts +1002 -0
  29. package/examples/custom-tools/todo/index.ts +212 -0
  30. package/examples/hooks/README.md +56 -0
  31. package/examples/hooks/auto-commit-on-exit.ts +49 -0
  32. package/examples/hooks/confirm-destructive.ts +59 -0
  33. package/examples/hooks/custom-compaction.ts +116 -0
  34. package/examples/hooks/dirty-repo-guard.ts +52 -0
  35. package/examples/hooks/file-trigger.ts +41 -0
  36. package/examples/hooks/git-checkpoint.ts +53 -0
  37. package/examples/hooks/handoff.ts +150 -0
  38. package/examples/hooks/permission-gate.ts +34 -0
  39. package/examples/hooks/protected-paths.ts +30 -0
  40. package/examples/hooks/qna.ts +119 -0
  41. package/examples/hooks/snake.ts +343 -0
  42. package/examples/hooks/status-line.ts +40 -0
  43. package/examples/sdk/01-minimal.ts +22 -0
  44. package/examples/sdk/02-custom-model.ts +49 -0
  45. package/examples/sdk/03-custom-prompt.ts +44 -0
  46. package/examples/sdk/04-skills.ts +44 -0
  47. package/examples/sdk/05-tools.ts +90 -0
  48. package/examples/sdk/06-hooks.ts +61 -0
  49. package/examples/sdk/07-context-files.ts +36 -0
  50. package/examples/sdk/08-slash-commands.ts +42 -0
  51. package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
  52. package/examples/sdk/10-settings.ts +38 -0
  53. package/examples/sdk/11-sessions.ts +48 -0
  54. package/examples/sdk/12-full-control.ts +95 -0
  55. package/examples/sdk/README.md +154 -0
  56. package/package.json +81 -0
  57. package/src/cli/args.ts +246 -0
  58. package/src/cli/file-processor.ts +72 -0
  59. package/src/cli/list-models.ts +104 -0
  60. package/src/cli/plugin-cli.ts +650 -0
  61. package/src/cli/session-picker.ts +41 -0
  62. package/src/cli.ts +10 -0
  63. package/src/commands/init.md +20 -0
  64. package/src/config.ts +159 -0
  65. package/src/core/agent-session.ts +1900 -0
  66. package/src/core/auth-storage.ts +236 -0
  67. package/src/core/bash-executor.ts +196 -0
  68. package/src/core/compaction/branch-summarization.ts +343 -0
  69. package/src/core/compaction/compaction.ts +742 -0
  70. package/src/core/compaction/index.ts +7 -0
  71. package/src/core/compaction/utils.ts +154 -0
  72. package/src/core/custom-tools/index.ts +21 -0
  73. package/src/core/custom-tools/loader.ts +248 -0
  74. package/src/core/custom-tools/types.ts +169 -0
  75. package/src/core/custom-tools/wrapper.ts +28 -0
  76. package/src/core/exec.ts +129 -0
  77. package/src/core/export-html/index.ts +211 -0
  78. package/src/core/export-html/template.css +781 -0
  79. package/src/core/export-html/template.html +54 -0
  80. package/src/core/export-html/template.js +1185 -0
  81. package/src/core/export-html/vendor/highlight.min.js +1213 -0
  82. package/src/core/export-html/vendor/marked.min.js +6 -0
  83. package/src/core/hooks/index.ts +16 -0
  84. package/src/core/hooks/loader.ts +312 -0
  85. package/src/core/hooks/runner.ts +434 -0
  86. package/src/core/hooks/tool-wrapper.ts +99 -0
  87. package/src/core/hooks/types.ts +773 -0
  88. package/src/core/index.ts +52 -0
  89. package/src/core/mcp/client.ts +158 -0
  90. package/src/core/mcp/config.ts +154 -0
  91. package/src/core/mcp/index.ts +45 -0
  92. package/src/core/mcp/loader.ts +68 -0
  93. package/src/core/mcp/manager.ts +181 -0
  94. package/src/core/mcp/tool-bridge.ts +148 -0
  95. package/src/core/mcp/transports/http.ts +316 -0
  96. package/src/core/mcp/transports/index.ts +6 -0
  97. package/src/core/mcp/transports/stdio.ts +252 -0
  98. package/src/core/mcp/types.ts +220 -0
  99. package/src/core/messages.ts +189 -0
  100. package/src/core/model-registry.ts +317 -0
  101. package/src/core/model-resolver.ts +393 -0
  102. package/src/core/plugins/doctor.ts +59 -0
  103. package/src/core/plugins/index.ts +38 -0
  104. package/src/core/plugins/installer.ts +189 -0
  105. package/src/core/plugins/loader.ts +338 -0
  106. package/src/core/plugins/manager.ts +672 -0
  107. package/src/core/plugins/parser.ts +105 -0
  108. package/src/core/plugins/paths.ts +32 -0
  109. package/src/core/plugins/types.ts +190 -0
  110. package/src/core/sdk.ts +760 -0
  111. package/src/core/session-manager.ts +1128 -0
  112. package/src/core/settings-manager.ts +443 -0
  113. package/src/core/skills.ts +437 -0
  114. package/src/core/slash-commands.ts +248 -0
  115. package/src/core/system-prompt.ts +439 -0
  116. package/src/core/timings.ts +25 -0
  117. package/src/core/tools/ask.ts +211 -0
  118. package/src/core/tools/bash-interceptor.ts +120 -0
  119. package/src/core/tools/bash.ts +250 -0
  120. package/src/core/tools/context.ts +32 -0
  121. package/src/core/tools/edit-diff.ts +475 -0
  122. package/src/core/tools/edit.ts +208 -0
  123. package/src/core/tools/exa/company.ts +59 -0
  124. package/src/core/tools/exa/index.ts +64 -0
  125. package/src/core/tools/exa/linkedin.ts +59 -0
  126. package/src/core/tools/exa/logger.ts +56 -0
  127. package/src/core/tools/exa/mcp-client.ts +368 -0
  128. package/src/core/tools/exa/render.ts +196 -0
  129. package/src/core/tools/exa/researcher.ts +90 -0
  130. package/src/core/tools/exa/search.ts +337 -0
  131. package/src/core/tools/exa/types.ts +168 -0
  132. package/src/core/tools/exa/websets.ts +248 -0
  133. package/src/core/tools/find.ts +261 -0
  134. package/src/core/tools/grep.ts +555 -0
  135. package/src/core/tools/index.ts +202 -0
  136. package/src/core/tools/ls.ts +140 -0
  137. package/src/core/tools/lsp/client.ts +605 -0
  138. package/src/core/tools/lsp/config.ts +147 -0
  139. package/src/core/tools/lsp/edits.ts +101 -0
  140. package/src/core/tools/lsp/index.ts +804 -0
  141. package/src/core/tools/lsp/render.ts +447 -0
  142. package/src/core/tools/lsp/rust-analyzer.ts +145 -0
  143. package/src/core/tools/lsp/types.ts +463 -0
  144. package/src/core/tools/lsp/utils.ts +486 -0
  145. package/src/core/tools/notebook.ts +229 -0
  146. package/src/core/tools/path-utils.ts +61 -0
  147. package/src/core/tools/read.ts +240 -0
  148. package/src/core/tools/renderers.ts +540 -0
  149. package/src/core/tools/task/agents.ts +153 -0
  150. package/src/core/tools/task/artifacts.ts +114 -0
  151. package/src/core/tools/task/bundled-agents/browser.md +71 -0
  152. package/src/core/tools/task/bundled-agents/explore.md +82 -0
  153. package/src/core/tools/task/bundled-agents/plan.md +54 -0
  154. package/src/core/tools/task/bundled-agents/reviewer.md +59 -0
  155. package/src/core/tools/task/bundled-agents/task.md +53 -0
  156. package/src/core/tools/task/bundled-commands/architect-plan.md +10 -0
  157. package/src/core/tools/task/bundled-commands/implement-with-critic.md +11 -0
  158. package/src/core/tools/task/bundled-commands/implement.md +11 -0
  159. package/src/core/tools/task/commands.ts +213 -0
  160. package/src/core/tools/task/discovery.ts +208 -0
  161. package/src/core/tools/task/executor.ts +367 -0
  162. package/src/core/tools/task/index.ts +388 -0
  163. package/src/core/tools/task/model-resolver.ts +115 -0
  164. package/src/core/tools/task/parallel.ts +38 -0
  165. package/src/core/tools/task/render.ts +232 -0
  166. package/src/core/tools/task/types.ts +99 -0
  167. package/src/core/tools/truncate.ts +265 -0
  168. package/src/core/tools/web-fetch.ts +2370 -0
  169. package/src/core/tools/web-search/auth.ts +193 -0
  170. package/src/core/tools/web-search/index.ts +537 -0
  171. package/src/core/tools/web-search/providers/anthropic.ts +198 -0
  172. package/src/core/tools/web-search/providers/exa.ts +302 -0
  173. package/src/core/tools/web-search/providers/perplexity.ts +195 -0
  174. package/src/core/tools/web-search/render.ts +182 -0
  175. package/src/core/tools/web-search/types.ts +180 -0
  176. package/src/core/tools/write.ts +99 -0
  177. package/src/index.ts +176 -0
  178. package/src/main.ts +464 -0
  179. package/src/migrations.ts +135 -0
  180. package/src/modes/index.ts +43 -0
  181. package/src/modes/interactive/components/armin.ts +382 -0
  182. package/src/modes/interactive/components/assistant-message.ts +86 -0
  183. package/src/modes/interactive/components/bash-execution.ts +196 -0
  184. package/src/modes/interactive/components/bordered-loader.ts +41 -0
  185. package/src/modes/interactive/components/branch-summary-message.ts +42 -0
  186. package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
  187. package/src/modes/interactive/components/custom-editor.ts +122 -0
  188. package/src/modes/interactive/components/diff.ts +147 -0
  189. package/src/modes/interactive/components/dynamic-border.ts +25 -0
  190. package/src/modes/interactive/components/footer.ts +381 -0
  191. package/src/modes/interactive/components/hook-editor.ts +117 -0
  192. package/src/modes/interactive/components/hook-input.ts +64 -0
  193. package/src/modes/interactive/components/hook-message.ts +96 -0
  194. package/src/modes/interactive/components/hook-selector.ts +91 -0
  195. package/src/modes/interactive/components/model-selector.ts +247 -0
  196. package/src/modes/interactive/components/oauth-selector.ts +120 -0
  197. package/src/modes/interactive/components/plugin-settings.ts +479 -0
  198. package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
  199. package/src/modes/interactive/components/session-selector.ts +204 -0
  200. package/src/modes/interactive/components/settings-selector.ts +453 -0
  201. package/src/modes/interactive/components/show-images-selector.ts +45 -0
  202. package/src/modes/interactive/components/theme-selector.ts +62 -0
  203. package/src/modes/interactive/components/thinking-selector.ts +64 -0
  204. package/src/modes/interactive/components/tool-execution.ts +675 -0
  205. package/src/modes/interactive/components/tree-selector.ts +866 -0
  206. package/src/modes/interactive/components/user-message-selector.ts +159 -0
  207. package/src/modes/interactive/components/user-message.ts +18 -0
  208. package/src/modes/interactive/components/visual-truncate.ts +50 -0
  209. package/src/modes/interactive/components/welcome.ts +183 -0
  210. package/src/modes/interactive/interactive-mode.ts +2516 -0
  211. package/src/modes/interactive/theme/dark.json +101 -0
  212. package/src/modes/interactive/theme/light.json +98 -0
  213. package/src/modes/interactive/theme/theme-schema.json +308 -0
  214. package/src/modes/interactive/theme/theme.ts +998 -0
  215. package/src/modes/print-mode.ts +128 -0
  216. package/src/modes/rpc/rpc-client.ts +527 -0
  217. package/src/modes/rpc/rpc-mode.ts +483 -0
  218. package/src/modes/rpc/rpc-types.ts +203 -0
  219. package/src/utils/changelog.ts +99 -0
  220. package/src/utils/clipboard.ts +265 -0
  221. package/src/utils/fuzzy.ts +108 -0
  222. package/src/utils/mime.ts +30 -0
  223. package/src/utils/shell.ts +276 -0
  224. package/src/utils/tools-manager.ts +274 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Shared command execution utilities for hooks and custom tools.
3
+ */
4
+
5
+ import type { Subprocess } from "bun";
6
+
7
+ /**
8
+ * Options for executing shell commands.
9
+ */
10
+ export interface ExecOptions {
11
+ /** AbortSignal to cancel the command */
12
+ signal?: AbortSignal;
13
+ /** Timeout in milliseconds */
14
+ timeout?: number;
15
+ /** Working directory */
16
+ cwd?: string;
17
+ }
18
+
19
+ /**
20
+ * Result of executing a shell command.
21
+ */
22
+ export interface ExecResult {
23
+ stdout: string;
24
+ stderr: string;
25
+ code: number;
26
+ killed: boolean;
27
+ }
28
+
29
+ /**
30
+ * Execute a shell command and return stdout/stderr/code.
31
+ * Supports timeout and abort signal.
32
+ */
33
+ export async function execCommand(
34
+ command: string,
35
+ args: string[],
36
+ cwd: string,
37
+ options?: ExecOptions,
38
+ ): Promise<ExecResult> {
39
+ return new Promise((resolve) => {
40
+ const proc: Subprocess = Bun.spawn([command, ...args], {
41
+ cwd,
42
+ stdin: "ignore",
43
+ stdout: "pipe",
44
+ stderr: "pipe",
45
+ });
46
+
47
+ let stdout = "";
48
+ let stderr = "";
49
+ let killed = false;
50
+ let timeoutId: Timer | undefined;
51
+
52
+ const killProcess = () => {
53
+ if (!killed) {
54
+ killed = true;
55
+ proc.kill();
56
+ // Force kill after 5 seconds if first kill doesn't work
57
+ setTimeout(() => {
58
+ try {
59
+ proc.kill(9);
60
+ } catch {
61
+ // Ignore if already dead
62
+ }
63
+ }, 5000);
64
+ }
65
+ };
66
+
67
+ // Handle abort signal
68
+ if (options?.signal) {
69
+ if (options.signal.aborted) {
70
+ killProcess();
71
+ } else {
72
+ options.signal.addEventListener("abort", killProcess, { once: true });
73
+ }
74
+ }
75
+
76
+ // Handle timeout
77
+ if (options?.timeout && options.timeout > 0) {
78
+ timeoutId = setTimeout(() => {
79
+ killProcess();
80
+ }, options.timeout);
81
+ }
82
+
83
+ // Read streams asynchronously
84
+ (async () => {
85
+ try {
86
+ const stdoutReader = (proc.stdout as ReadableStream<Uint8Array>).getReader();
87
+ const stderrReader = (proc.stderr as ReadableStream<Uint8Array>).getReader();
88
+
89
+ const [stdoutResult, stderrResult] = await Promise.all([
90
+ (async () => {
91
+ const chunks: Uint8Array[] = [];
92
+ while (true) {
93
+ const { done, value } = await stdoutReader.read();
94
+ if (done) break;
95
+ chunks.push(value);
96
+ }
97
+ return Buffer.concat(chunks).toString();
98
+ })(),
99
+ (async () => {
100
+ const chunks: Uint8Array[] = [];
101
+ while (true) {
102
+ const { done, value } = await stderrReader.read();
103
+ if (done) break;
104
+ chunks.push(value);
105
+ }
106
+ return Buffer.concat(chunks).toString();
107
+ })(),
108
+ ]);
109
+
110
+ stdout = stdoutResult;
111
+ stderr = stderrResult;
112
+
113
+ const exitCode = await proc.exited;
114
+
115
+ if (timeoutId) clearTimeout(timeoutId);
116
+ if (options?.signal) {
117
+ options.signal.removeEventListener("abort", killProcess);
118
+ }
119
+ resolve({ stdout, stderr, code: exitCode ?? 0, killed });
120
+ } catch (_err) {
121
+ if (timeoutId) clearTimeout(timeoutId);
122
+ if (options?.signal) {
123
+ options.signal.removeEventListener("abort", killProcess);
124
+ }
125
+ resolve({ stdout, stderr, code: 1, killed });
126
+ }
127
+ })();
128
+ });
129
+ }
@@ -0,0 +1,211 @@
1
+ import type { AgentState } from "@oh-my-pi/pi-agent-core";
2
+ import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { basename, join } from "path";
4
+ import { APP_NAME, getExportTemplateDir } from "../../config.js";
5
+ import { getResolvedThemeColors, getThemeExportColors } from "../../modes/interactive/theme/theme.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+
8
+ export interface ExportOptions {
9
+ outputPath?: string;
10
+ themeName?: string;
11
+ }
12
+
13
+ /** Parse a color string to RGB values. Supports hex (#RRGGBB) and rgb(r,g,b) formats. */
14
+ function parseColor(color: string): { r: number; g: number; b: number } | undefined {
15
+ const hexMatch = color.match(/^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/);
16
+ if (hexMatch) {
17
+ return {
18
+ r: Number.parseInt(hexMatch[1], 16),
19
+ g: Number.parseInt(hexMatch[2], 16),
20
+ b: Number.parseInt(hexMatch[3], 16),
21
+ };
22
+ }
23
+ const rgbMatch = color.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
24
+ if (rgbMatch) {
25
+ return {
26
+ r: Number.parseInt(rgbMatch[1], 10),
27
+ g: Number.parseInt(rgbMatch[2], 10),
28
+ b: Number.parseInt(rgbMatch[3], 10),
29
+ };
30
+ }
31
+ return undefined;
32
+ }
33
+
34
+ /** Calculate relative luminance of a color (0-1, higher = lighter). */
35
+ function getLuminance(r: number, g: number, b: number): number {
36
+ const toLinear = (c: number) => {
37
+ const s = c / 255;
38
+ return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
39
+ };
40
+ return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
41
+ }
42
+
43
+ /** Adjust color brightness. Factor > 1 lightens, < 1 darkens. */
44
+ function adjustBrightness(color: string, factor: number): string {
45
+ const parsed = parseColor(color);
46
+ if (!parsed) return color;
47
+ const adjust = (c: number) => Math.min(255, Math.max(0, Math.round(c * factor)));
48
+ return `rgb(${adjust(parsed.r)}, ${adjust(parsed.g)}, ${adjust(parsed.b)})`;
49
+ }
50
+
51
+ /** Derive export background colors from a base color (e.g., userMessageBg). */
52
+ function deriveExportColors(baseColor: string): { pageBg: string; cardBg: string; infoBg: string } {
53
+ const parsed = parseColor(baseColor);
54
+ if (!parsed) {
55
+ return {
56
+ pageBg: "rgb(24, 24, 30)",
57
+ cardBg: "rgb(30, 30, 36)",
58
+ infoBg: "rgb(60, 55, 40)",
59
+ };
60
+ }
61
+
62
+ const luminance = getLuminance(parsed.r, parsed.g, parsed.b);
63
+ const isLight = luminance > 0.5;
64
+
65
+ if (isLight) {
66
+ return {
67
+ pageBg: adjustBrightness(baseColor, 0.96),
68
+ cardBg: baseColor,
69
+ infoBg: `rgb(${Math.min(255, parsed.r + 10)}, ${Math.min(255, parsed.g + 5)}, ${Math.max(0, parsed.b - 20)})`,
70
+ };
71
+ }
72
+ return {
73
+ pageBg: adjustBrightness(baseColor, 0.7),
74
+ cardBg: adjustBrightness(baseColor, 0.85),
75
+ infoBg: `rgb(${Math.min(255, parsed.r + 20)}, ${Math.min(255, parsed.g + 15)}, ${parsed.b})`,
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Generate CSS custom property declarations from theme colors.
81
+ */
82
+ function generateThemeVars(themeName?: string): string {
83
+ const colors = getResolvedThemeColors(themeName);
84
+ const lines: string[] = [];
85
+ for (const [key, value] of Object.entries(colors)) {
86
+ lines.push(`--${key}: ${value};`);
87
+ }
88
+
89
+ // Use explicit theme export colors if available, otherwise derive from userMessageBg
90
+ const themeExport = getThemeExportColors(themeName);
91
+ const userMessageBg = colors.userMessageBg || "#343541";
92
+ const derivedColors = deriveExportColors(userMessageBg);
93
+
94
+ lines.push(`--exportPageBg: ${themeExport.pageBg ?? derivedColors.pageBg};`);
95
+ lines.push(`--exportCardBg: ${themeExport.cardBg ?? derivedColors.cardBg};`);
96
+ lines.push(`--exportInfoBg: ${themeExport.infoBg ?? derivedColors.infoBg};`);
97
+
98
+ return lines.join("\n ");
99
+ }
100
+
101
+ interface SessionData {
102
+ header: ReturnType<SessionManager["getHeader"]>;
103
+ entries: ReturnType<SessionManager["getEntries"]>;
104
+ leafId: string | null;
105
+ systemPrompt?: string;
106
+ tools?: { name: string; description: string }[];
107
+ }
108
+
109
+ /**
110
+ * Core HTML generation logic shared by both export functions.
111
+ */
112
+ function generateHtml(sessionData: SessionData, themeName?: string): string {
113
+ const templateDir = getExportTemplateDir();
114
+ const template = readFileSync(join(templateDir, "template.html"), "utf-8");
115
+ const templateCss = readFileSync(join(templateDir, "template.css"), "utf-8");
116
+ const templateJs = readFileSync(join(templateDir, "template.js"), "utf-8");
117
+ const markedJs = readFileSync(join(templateDir, "vendor", "marked.min.js"), "utf-8");
118
+ const hljsJs = readFileSync(join(templateDir, "vendor", "highlight.min.js"), "utf-8");
119
+
120
+ const themeVars = generateThemeVars(themeName);
121
+ const colors = getResolvedThemeColors(themeName);
122
+ const exportColors = deriveExportColors(colors.userMessageBg || "#343541");
123
+ const bodyBg = exportColors.pageBg;
124
+ const containerBg = exportColors.cardBg;
125
+ const infoBg = exportColors.infoBg;
126
+
127
+ // Base64 encode session data to avoid escaping issues
128
+ const sessionDataBase64 = Buffer.from(JSON.stringify(sessionData)).toString("base64");
129
+
130
+ // Build the CSS with theme variables injected
131
+ const css = templateCss
132
+ .replace("{{THEME_VARS}}", themeVars)
133
+ .replace("{{BODY_BG}}", bodyBg)
134
+ .replace("{{CONTAINER_BG}}", containerBg)
135
+ .replace("{{INFO_BG}}", infoBg);
136
+
137
+ return template
138
+ .replace("{{CSS}}", css)
139
+ .replace("{{JS}}", templateJs)
140
+ .replace("{{SESSION_DATA}}", sessionDataBase64)
141
+ .replace("{{MARKED_JS}}", markedJs)
142
+ .replace("{{HIGHLIGHT_JS}}", hljsJs);
143
+ }
144
+
145
+ /**
146
+ * Export session to HTML using SessionManager and AgentState.
147
+ * Used by TUI's /export command.
148
+ */
149
+ export function exportSessionToHtml(sm: SessionManager, state?: AgentState, options?: ExportOptions | string): string {
150
+ const opts: ExportOptions = typeof options === "string" ? { outputPath: options } : options || {};
151
+
152
+ const sessionFile = sm.getSessionFile();
153
+ if (!sessionFile) {
154
+ throw new Error("Cannot export in-memory session to HTML");
155
+ }
156
+ if (!existsSync(sessionFile)) {
157
+ throw new Error("Nothing to export yet - start a conversation first");
158
+ }
159
+
160
+ const sessionData: SessionData = {
161
+ header: sm.getHeader(),
162
+ entries: sm.getEntries(),
163
+ leafId: sm.getLeafId(),
164
+ systemPrompt: state?.systemPrompt,
165
+ tools: state?.tools?.map((t) => ({ name: t.name, description: t.description })),
166
+ };
167
+
168
+ const html = generateHtml(sessionData, opts.themeName);
169
+
170
+ let outputPath = opts.outputPath;
171
+ if (!outputPath) {
172
+ const sessionBasename = basename(sessionFile, ".jsonl");
173
+ outputPath = `${APP_NAME}-session-${sessionBasename}.html`;
174
+ }
175
+
176
+ writeFileSync(outputPath, html, "utf8");
177
+ return outputPath;
178
+ }
179
+
180
+ /**
181
+ * Export session file to HTML (standalone, without AgentState).
182
+ * Used by CLI for exporting arbitrary session files.
183
+ */
184
+ export function exportFromFile(inputPath: string, options?: ExportOptions | string): string {
185
+ const opts: ExportOptions = typeof options === "string" ? { outputPath: options } : options || {};
186
+
187
+ if (!existsSync(inputPath)) {
188
+ throw new Error(`File not found: ${inputPath}`);
189
+ }
190
+
191
+ const sm = SessionManager.open(inputPath);
192
+
193
+ const sessionData: SessionData = {
194
+ header: sm.getHeader(),
195
+ entries: sm.getEntries(),
196
+ leafId: sm.getLeafId(),
197
+ systemPrompt: undefined,
198
+ tools: undefined,
199
+ };
200
+
201
+ const html = generateHtml(sessionData, opts.themeName);
202
+
203
+ let outputPath = opts.outputPath;
204
+ if (!outputPath) {
205
+ const inputBasename = basename(inputPath, ".jsonl");
206
+ outputPath = `${APP_NAME}-session-${inputBasename}.html`;
207
+ }
208
+
209
+ writeFileSync(outputPath, html, "utf8");
210
+ return outputPath;
211
+ }