@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,182 @@
1
+ /**
2
+ * Web Search TUI Rendering
3
+ *
4
+ * Tree-based rendering with collapsed/expanded states for web search results.
5
+ */
6
+
7
+ import type { Component } from "@oh-my-pi/pi-tui";
8
+ import { Text } from "@oh-my-pi/pi-tui";
9
+ import type { Theme } from "../../../modes/interactive/theme/theme.js";
10
+ import type { RenderResultOptions } from "../../custom-tools/types.js";
11
+ import type { WebSearchResponse } from "./types.js";
12
+
13
+ // Tree formatting constants
14
+ const TREE_MID = "├─";
15
+ const TREE_END = "└─";
16
+ const TREE_PIPE = "│";
17
+ const TREE_SPACE = " ";
18
+ const TREE_HOOK = "⎿";
19
+
20
+ /** Truncate text to max length with ellipsis */
21
+ export function truncate(text: string, maxLen: number): string {
22
+ if (text.length <= maxLen) return text;
23
+ return `${text.slice(0, maxLen - 1)}…`;
24
+ }
25
+
26
+ /** Extract domain from URL */
27
+ export function getDomain(url: string): string {
28
+ try {
29
+ const u = new URL(url);
30
+ return u.hostname.replace(/^www\./, "");
31
+ } catch {
32
+ return url;
33
+ }
34
+ }
35
+
36
+ /** Format age string from seconds */
37
+ export function formatAge(ageSeconds: number | null | undefined): string {
38
+ if (!ageSeconds) return "";
39
+ const mins = Math.floor(ageSeconds / 60);
40
+ const hours = Math.floor(mins / 60);
41
+ const days = Math.floor(hours / 24);
42
+ const weeks = Math.floor(days / 7);
43
+ const months = Math.floor(days / 30);
44
+
45
+ if (months > 0) return `${months}mo ago`;
46
+ if (weeks > 0) return `${weeks}w ago`;
47
+ if (days > 0) return `${days}d ago`;
48
+ if (hours > 0) return `${hours}h ago`;
49
+ if (mins > 0) return `${mins}m ago`;
50
+ return "just now";
51
+ }
52
+
53
+ /** Get first N lines of text as preview */
54
+ export function getPreviewLines(text: string, maxLines: number, maxLineLen: number): string[] {
55
+ const lines = text.split("\n").filter((l) => l.trim());
56
+ return lines.slice(0, maxLines).map((l) => truncate(l.trim(), maxLineLen));
57
+ }
58
+
59
+ export interface WebSearchRenderDetails {
60
+ response: WebSearchResponse;
61
+ error?: string;
62
+ }
63
+
64
+ /** Render web search result with tree-based layout */
65
+ export function renderWebSearchResult(
66
+ result: { content: Array<{ type: string; text?: string }>; details?: WebSearchRenderDetails },
67
+ options: RenderResultOptions,
68
+ theme: Theme,
69
+ ): Component {
70
+ const { expanded } = options;
71
+ const details = result.details;
72
+
73
+ // Handle error case
74
+ if (details?.error) {
75
+ return new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
76
+ }
77
+
78
+ const response = details?.response;
79
+ if (!response) {
80
+ return new Text(theme.fg("error", "No response data"), 0, 0);
81
+ }
82
+
83
+ const sources = response.sources ?? [];
84
+ const sourceCount = sources.length;
85
+ const _modelName = response.model ?? response.provider;
86
+ const provider = response.provider;
87
+
88
+ // Build header: ● Web Search (provider/model) · N sources
89
+ const icon = sourceCount > 0 ? theme.fg("success", "●") : theme.fg("warning", "●");
90
+ const expandHint = expanded ? "" : theme.fg("dim", " (Ctrl+O to expand)");
91
+ const providerLabel = provider === "anthropic" ? "Anthropic" : "Perplexity";
92
+ let text = `${icon} ${theme.fg("toolTitle", "Web Search")} ${theme.fg("dim", `(${providerLabel})`)} · ${theme.fg(
93
+ "dim",
94
+ `${sourceCount} source${sourceCount !== 1 ? "s" : ""}`,
95
+ )}${expandHint}`;
96
+
97
+ // Get answer text
98
+ const contentText = response.answer ?? result.content[0]?.text ?? "";
99
+
100
+ if (!expanded) {
101
+ // Collapsed view: show 2-3 preview lines of answer
102
+ const previewLines = getPreviewLines(contentText, 3, 100);
103
+ for (const line of previewLines) {
104
+ text += `\n ${theme.fg("dim", TREE_PIPE)} ${theme.fg("dim", line)}`;
105
+ }
106
+ const totalLines = contentText.split("\n").filter((l) => l.trim()).length;
107
+ if (totalLines > 3) {
108
+ text += `\n ${theme.fg("dim", TREE_PIPE)} ${theme.fg("muted", `… ${totalLines - 3} more lines`)}`;
109
+ }
110
+
111
+ // Show source count summary
112
+ if (sourceCount > 0) {
113
+ text += `\n ${theme.fg("dim", TREE_END)} ${theme.fg(
114
+ "muted",
115
+ `${sourceCount} source${sourceCount !== 1 ? "s" : ""}`,
116
+ )}`;
117
+ }
118
+ } else {
119
+ // Expanded view: full answer + source tree
120
+ const answerLines = contentText.split("\n");
121
+ for (const line of answerLines) {
122
+ text += `\n ${theme.fg("dim", TREE_PIPE)} ${line}`;
123
+ }
124
+
125
+ // Render sources as tree
126
+ const hasRelatedQuestions = response.relatedQuestions && response.relatedQuestions.length > 0;
127
+
128
+ if (sourceCount > 0) {
129
+ text += `\n ${theme.fg("dim", TREE_PIPE)}`;
130
+ const sourcesBranch = hasRelatedQuestions ? TREE_MID : TREE_END;
131
+ text += `\n ${theme.fg("dim", sourcesBranch)} ${theme.fg("accent", "Sources")}`;
132
+
133
+ for (let i = 0; i < sources.length; i++) {
134
+ const src = sources[i];
135
+ const isLast = i === sources.length - 1;
136
+ const branch = isLast ? TREE_END : TREE_MID;
137
+ const cont = isLast ? TREE_SPACE : TREE_PIPE;
138
+ const indent = hasRelatedQuestions ? TREE_PIPE : TREE_SPACE;
139
+
140
+ // Title + domain + age
141
+ const title = truncate(src.title, 60);
142
+ const domain = getDomain(src.url);
143
+ const age = formatAge(src.ageSeconds) || src.publishedDate;
144
+ const agePart = age ? theme.fg("muted", ` · ${age}`) : "";
145
+
146
+ text += `\n ${theme.fg("dim", indent)} ${theme.fg("dim", branch)} ${theme.fg("accent", title)} ${theme.fg(
147
+ "dim",
148
+ `(${domain})`,
149
+ )}${agePart}`;
150
+ text += `\n ${theme.fg("dim", indent)} ${theme.fg("dim", `${cont} ${TREE_HOOK} `)}${theme.fg(
151
+ "mdLinkUrl",
152
+ src.url,
153
+ )}`;
154
+ }
155
+ }
156
+
157
+ // Render related questions (Perplexity only)
158
+ if (hasRelatedQuestions) {
159
+ text += `\n ${theme.fg("dim", TREE_END)} ${theme.fg("accent", "Related Questions")}`;
160
+ const questions = response.relatedQuestions!;
161
+ for (let i = 0; i < questions.length; i++) {
162
+ const question = questions[i];
163
+ const isLast = i === questions.length - 1;
164
+ const branch = isLast ? TREE_END : TREE_MID;
165
+ text += `\n ${theme.fg("dim", TREE_SPACE)} ${theme.fg("dim", branch)} ${theme.fg("muted", question)}`;
166
+ }
167
+ }
168
+ }
169
+
170
+ return new Text(text, 0, 0);
171
+ }
172
+
173
+ /** Render web search call (query preview) */
174
+ export function renderWebSearchCall(
175
+ args: { query: string; provider?: string; [key: string]: unknown },
176
+ theme: Theme,
177
+ ): Component {
178
+ const provider = args.provider ?? "auto";
179
+ const query = truncate(args.query, 80);
180
+ const text = `${theme.fg("toolTitle", "Web Search")} ${theme.fg("dim", `(${provider})`)} ${theme.fg("muted", query)}`;
181
+ return new Text(text, 0, 0);
182
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Web Search Types
3
+ *
4
+ * Unified types for web search responses across Anthropic and Perplexity providers.
5
+ */
6
+
7
+ /** Supported web search providers */
8
+ export type WebSearchProvider = "exa" | "anthropic" | "perplexity";
9
+
10
+ /** Source returned by search (all providers) */
11
+ export interface WebSearchSource {
12
+ title: string;
13
+ url: string;
14
+ snippet?: string;
15
+ /** ISO date string or relative ("2d ago") */
16
+ publishedDate?: string;
17
+ /** Age in seconds for consistent formatting */
18
+ ageSeconds?: number;
19
+ author?: string;
20
+ }
21
+
22
+ /** Citation with text reference (anthropic, perplexity) */
23
+ export interface WebSearchCitation {
24
+ url: string;
25
+ title: string;
26
+ citedText?: string;
27
+ }
28
+
29
+ /** Usage metrics */
30
+ export interface WebSearchUsage {
31
+ inputTokens?: number;
32
+ outputTokens?: number;
33
+ /** Anthropic: number of web search requests made */
34
+ searchRequests?: number;
35
+ /** Perplexity: combined token count */
36
+ totalTokens?: number;
37
+ }
38
+
39
+ /** Unified response across providers */
40
+ export interface WebSearchResponse {
41
+ provider: WebSearchProvider;
42
+ /** Synthesized answer text (anthropic, perplexity) */
43
+ answer?: string;
44
+ /** Search result sources */
45
+ sources: WebSearchSource[];
46
+ /** Text citations with context */
47
+ citations?: WebSearchCitation[];
48
+ /** Follow-up questions (perplexity) */
49
+ relatedQuestions?: string[];
50
+ /** Intermediate search queries (anthropic) */
51
+ searchQueries?: string[];
52
+ /** Token usage metrics */
53
+ usage?: WebSearchUsage;
54
+ /** Model used */
55
+ model?: string;
56
+ /** Request ID for debugging */
57
+ requestId?: string;
58
+ }
59
+
60
+ /** Auth configuration for Anthropic */
61
+ export interface AnthropicAuthConfig {
62
+ apiKey: string;
63
+ baseUrl: string;
64
+ isOAuth: boolean;
65
+ }
66
+
67
+ /** models.json structure for provider resolution */
68
+ export interface ModelsJson {
69
+ providers?: Record<
70
+ string,
71
+ {
72
+ baseUrl?: string;
73
+ apiKey?: string;
74
+ api?: string;
75
+ }
76
+ >;
77
+ }
78
+
79
+ /** auth.json structure for OAuth credentials */
80
+ export interface AuthJson {
81
+ anthropic?: {
82
+ type: "oauth";
83
+ access: string;
84
+ refresh?: string;
85
+ /** Expiry timestamp in milliseconds */
86
+ expires: number;
87
+ };
88
+ }
89
+
90
+ /** Anthropic API response types */
91
+ export interface AnthropicWebSearchResult {
92
+ type: "web_search_result";
93
+ title: string;
94
+ url: string;
95
+ encrypted_content: string;
96
+ page_age: string | null;
97
+ }
98
+
99
+ export interface AnthropicCitation {
100
+ type: "web_search_result_location";
101
+ url: string;
102
+ title: string;
103
+ cited_text: string;
104
+ encrypted_index: string;
105
+ }
106
+
107
+ export interface AnthropicContentBlock {
108
+ type: string;
109
+ /** Text content (for type="text") */
110
+ text?: string;
111
+ /** Citations in text block */
112
+ citations?: AnthropicCitation[];
113
+ /** Tool name (for type="server_tool_use") */
114
+ name?: string;
115
+ /** Tool input (for type="server_tool_use") */
116
+ input?: { query: string };
117
+ /** Search results (for type="web_search_tool_result") */
118
+ content?: AnthropicWebSearchResult[];
119
+ }
120
+
121
+ export interface AnthropicApiResponse {
122
+ id: string;
123
+ model: string;
124
+ content: AnthropicContentBlock[];
125
+ usage: {
126
+ input_tokens: number;
127
+ output_tokens: number;
128
+ cache_read_input_tokens?: number;
129
+ cache_creation_input_tokens?: number;
130
+ server_tool_use?: { web_search_requests: number };
131
+ };
132
+ }
133
+
134
+ /** Perplexity API types */
135
+ export interface PerplexityMessage {
136
+ role: "system" | "user" | "assistant";
137
+ content: string;
138
+ }
139
+
140
+ export interface PerplexityRequest {
141
+ model: string;
142
+ messages: PerplexityMessage[];
143
+ temperature?: number;
144
+ max_tokens?: number;
145
+ search_domain_filter?: string[];
146
+ search_recency_filter?: "day" | "week" | "month" | "year";
147
+ return_images?: boolean;
148
+ return_related_questions?: boolean;
149
+ search_context_size?: "low" | "medium" | "high";
150
+ }
151
+
152
+ export interface PerplexitySearchResult {
153
+ title: string;
154
+ url: string;
155
+ date?: string;
156
+ snippet?: string;
157
+ }
158
+
159
+ export interface PerplexityResponse {
160
+ id: string;
161
+ model: string;
162
+ created: number;
163
+ usage: {
164
+ prompt_tokens: number;
165
+ completion_tokens: number;
166
+ total_tokens: number;
167
+ search_context_size?: string;
168
+ };
169
+ citations?: string[];
170
+ search_results?: PerplexitySearchResult[];
171
+ related_questions?: string[];
172
+ choices: Array<{
173
+ index: number;
174
+ finish_reason: string;
175
+ message: {
176
+ role: string;
177
+ content: string;
178
+ };
179
+ }>;
180
+ }
@@ -0,0 +1,99 @@
1
+ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
2
+ import { Type } from "@sinclair/typebox";
3
+ import { mkdir, writeFile } from "fs/promises";
4
+ import { dirname } from "path";
5
+ import { resolveToCwd } from "./path-utils.js";
6
+
7
+ const writeSchema = Type.Object({
8
+ path: Type.String({ description: "Path to the file to write (relative or absolute)" }),
9
+ content: Type.String({ description: "Content to write to the file" }),
10
+ });
11
+
12
+ export function createWriteTool(cwd: string): AgentTool<typeof writeSchema> {
13
+ return {
14
+ name: "write",
15
+ label: "Write",
16
+ description: `Writes a file to the local filesystem.
17
+
18
+ Usage:
19
+ - This tool will overwrite the existing file if there is one at the provided path.
20
+ - If this is an existing file, you MUST use the read tool first to read the file's contents. This tool will fail if you did not read the file first.
21
+ - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
22
+ - NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
23
+ - Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`,
24
+ parameters: writeSchema,
25
+ execute: async (
26
+ _toolCallId: string,
27
+ { path, content }: { path: string; content: string },
28
+ signal?: AbortSignal,
29
+ ) => {
30
+ const absolutePath = resolveToCwd(path, cwd);
31
+ const dir = dirname(absolutePath);
32
+
33
+ return new Promise<{ content: Array<{ type: "text"; text: string }>; details: undefined }>(
34
+ (resolve, reject) => {
35
+ // Check if already aborted
36
+ if (signal?.aborted) {
37
+ reject(new Error("Operation aborted"));
38
+ return;
39
+ }
40
+
41
+ let aborted = false;
42
+
43
+ // Set up abort handler
44
+ const onAbort = () => {
45
+ aborted = true;
46
+ reject(new Error("Operation aborted"));
47
+ };
48
+
49
+ if (signal) {
50
+ signal.addEventListener("abort", onAbort, { once: true });
51
+ }
52
+
53
+ // Perform the write operation
54
+ (async () => {
55
+ try {
56
+ // Create parent directories if needed
57
+ await mkdir(dir, { recursive: true });
58
+
59
+ // Check if aborted before writing
60
+ if (aborted) {
61
+ return;
62
+ }
63
+
64
+ // Write the file
65
+ await writeFile(absolutePath, content, "utf-8");
66
+
67
+ // Check if aborted after writing
68
+ if (aborted) {
69
+ return;
70
+ }
71
+
72
+ // Clean up abort handler
73
+ if (signal) {
74
+ signal.removeEventListener("abort", onAbort);
75
+ }
76
+
77
+ resolve({
78
+ content: [{ type: "text", text: `Successfully wrote ${content.length} bytes to ${path}` }],
79
+ details: undefined,
80
+ });
81
+ } catch (error: any) {
82
+ // Clean up abort handler
83
+ if (signal) {
84
+ signal.removeEventListener("abort", onAbort);
85
+ }
86
+
87
+ if (!aborted) {
88
+ reject(error);
89
+ }
90
+ }
91
+ })();
92
+ },
93
+ );
94
+ },
95
+ };
96
+ }
97
+
98
+ /** Default write tool using process.cwd() - for backwards compatibility */
99
+ export const writeTool = createWriteTool(process.cwd());
package/src/index.ts ADDED
@@ -0,0 +1,176 @@
1
+ // Core session management
2
+
3
+ // Re-export TUI components for custom tool rendering
4
+ export { Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
5
+ export {
6
+ AgentSession,
7
+ type AgentSessionConfig,
8
+ type AgentSessionEvent,
9
+ type AgentSessionEventListener,
10
+ type ModelCycleResult,
11
+ type PromptOptions,
12
+ type SessionStats,
13
+ } from "./core/agent-session.js";
14
+ // Auth and model registry
15
+ export { type ApiKeyCredential, type AuthCredential, AuthStorage, type OAuthCredential } from "./core/auth-storage.js";
16
+ // Compaction
17
+ export {
18
+ type BranchPreparation,
19
+ type BranchSummaryResult,
20
+ type CollectEntriesResult,
21
+ type CompactionResult,
22
+ type CutPointResult,
23
+ calculateContextTokens,
24
+ collectEntriesForBranchSummary,
25
+ compact,
26
+ DEFAULT_COMPACTION_SETTINGS,
27
+ estimateTokens,
28
+ type FileOperations,
29
+ findCutPoint,
30
+ findTurnStartIndex,
31
+ type GenerateBranchSummaryOptions,
32
+ generateBranchSummary,
33
+ generateSummary,
34
+ getLastAssistantUsage,
35
+ prepareBranchEntries,
36
+ serializeConversation,
37
+ shouldCompact,
38
+ } from "./core/compaction/index.js";
39
+ // Custom tools
40
+ export type {
41
+ AgentToolUpdateCallback,
42
+ CustomTool,
43
+ CustomToolAPI,
44
+ CustomToolContext,
45
+ CustomToolFactory,
46
+ CustomToolSessionEvent,
47
+ CustomToolsLoadResult,
48
+ CustomToolUIContext,
49
+ ExecResult,
50
+ LoadedCustomTool,
51
+ RenderResultOptions,
52
+ } from "./core/custom-tools/index.js";
53
+ export { discoverAndLoadCustomTools, loadCustomTools } from "./core/custom-tools/index.js";
54
+ export type * from "./core/hooks/index.js";
55
+ // Hook system types and type guards
56
+ export {
57
+ isBashToolResult,
58
+ isEditToolResult,
59
+ isFindToolResult,
60
+ isGrepToolResult,
61
+ isLsToolResult,
62
+ isReadToolResult,
63
+ isWriteToolResult,
64
+ } from "./core/hooks/index.js";
65
+ export { convertToLlm } from "./core/messages.js";
66
+ export { ModelRegistry } from "./core/model-registry.js";
67
+ // SDK for programmatic usage
68
+ export {
69
+ type BuildSystemPromptOptions,
70
+ buildSystemPrompt,
71
+ type CreateAgentSessionOptions,
72
+ type CreateAgentSessionResult,
73
+ // Factory
74
+ createAgentSession,
75
+ createBashTool,
76
+ // Tool factories (for custom cwd)
77
+ createCodingTools,
78
+ createEditTool,
79
+ createFindTool,
80
+ createGrepTool,
81
+ createLsTool,
82
+ createReadOnlyTools,
83
+ createReadTool,
84
+ createWriteTool,
85
+ // Discovery
86
+ discoverAuthStorage,
87
+ discoverContextFiles,
88
+ discoverCustomTools,
89
+ discoverHooks,
90
+ discoverModels,
91
+ discoverSkills,
92
+ discoverSlashCommands,
93
+ type FileSlashCommand,
94
+ // Hook types
95
+ type HookAPI,
96
+ type HookContext,
97
+ type HookFactory,
98
+ loadSettings,
99
+ // Pre-built tools (use process.cwd())
100
+ readOnlyTools,
101
+ } from "./core/sdk.js";
102
+ export {
103
+ type BranchSummaryEntry,
104
+ buildSessionContext,
105
+ type CompactionEntry,
106
+ CURRENT_SESSION_VERSION,
107
+ type CustomEntry,
108
+ type CustomMessageEntry,
109
+ type FileEntry,
110
+ getLatestCompactionEntry,
111
+ type ModelChangeEntry,
112
+ migrateSessionEntries,
113
+ type NewSessionOptions,
114
+ parseSessionEntries,
115
+ type SessionContext,
116
+ type SessionEntry,
117
+ type SessionEntryBase,
118
+ type SessionHeader,
119
+ type SessionInfo,
120
+ SessionManager,
121
+ type SessionMessageEntry,
122
+ type ThinkingLevelChangeEntry,
123
+ } from "./core/session-manager.js";
124
+ export {
125
+ type CompactionSettings,
126
+ type RetrySettings,
127
+ type Settings,
128
+ SettingsManager,
129
+ type SkillsSettings,
130
+ } from "./core/settings-manager.js";
131
+ // Skills
132
+ export {
133
+ formatSkillsForPrompt,
134
+ type LoadSkillsFromDirOptions,
135
+ type LoadSkillsResult,
136
+ loadSkills,
137
+ loadSkillsFromDir,
138
+ type Skill,
139
+ type SkillFrontmatter,
140
+ type SkillWarning,
141
+ } from "./core/skills.js";
142
+ // Tools
143
+ export {
144
+ type BashToolDetails,
145
+ bashTool,
146
+ codingTools,
147
+ editTool,
148
+ type FindToolDetails,
149
+ findTool,
150
+ type GrepToolDetails,
151
+ grepTool,
152
+ type LsToolDetails,
153
+ lsTool,
154
+ type ReadToolDetails,
155
+ readTool,
156
+ type TruncationResult,
157
+ writeTool,
158
+ } from "./core/tools/index.js";
159
+ // Main entry point
160
+ export { main } from "./main.js";
161
+ // UI components for hooks and custom tools
162
+ export { BorderedLoader } from "./modes/interactive/components/bordered-loader.js";
163
+ // Theme utilities for custom tools
164
+ export { getMarkdownTheme } from "./modes/interactive/theme/theme.js";
165
+
166
+ // TypeBox helper for string enums (convenience for custom tools)
167
+ import { type TSchema, Type } from "@sinclair/typebox";
168
+ export function StringEnum<T extends readonly string[]>(
169
+ values: T,
170
+ options?: { description?: string; default?: T[number] },
171
+ ): TSchema {
172
+ return Type.Union(
173
+ values.map((v) => Type.Literal(v)),
174
+ options,
175
+ );
176
+ }