@oh-my-pi/pi-coding-agent 1.340.0 → 2.0.1337

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 (153) hide show
  1. package/CHANGELOG.md +115 -1
  2. package/README.md +1 -1
  3. package/examples/custom-tools/subagent/index.ts +1 -1
  4. package/package.json +5 -3
  5. package/src/cli/args.ts +13 -6
  6. package/src/cli/file-processor.ts +3 -3
  7. package/src/cli/list-models.ts +2 -2
  8. package/src/cli/plugin-cli.ts +1 -1
  9. package/src/cli/session-picker.ts +2 -2
  10. package/src/cli.ts +1 -1
  11. package/src/config.ts +3 -3
  12. package/src/core/agent-session.ts +189 -29
  13. package/src/core/bash-executor.ts +50 -10
  14. package/src/core/compaction/branch-summarization.ts +5 -5
  15. package/src/core/compaction/compaction.ts +3 -3
  16. package/src/core/compaction/index.ts +3 -3
  17. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  18. package/src/core/custom-commands/index.ts +15 -0
  19. package/src/core/custom-commands/loader.ts +232 -0
  20. package/src/core/custom-commands/types.ts +112 -0
  21. package/src/core/custom-tools/index.ts +3 -3
  22. package/src/core/custom-tools/loader.ts +10 -8
  23. package/src/core/custom-tools/types.ts +11 -6
  24. package/src/core/custom-tools/wrapper.ts +2 -1
  25. package/src/core/exec.ts +22 -12
  26. package/src/core/export-html/index.ts +5 -5
  27. package/src/core/file-mentions.ts +54 -0
  28. package/src/core/hooks/index.ts +5 -5
  29. package/src/core/hooks/loader.ts +21 -16
  30. package/src/core/hooks/runner.ts +6 -6
  31. package/src/core/hooks/tool-wrapper.ts +2 -2
  32. package/src/core/hooks/types.ts +12 -15
  33. package/src/core/index.ts +6 -6
  34. package/src/core/logger.ts +112 -0
  35. package/src/core/mcp/client.ts +3 -3
  36. package/src/core/mcp/config.ts +1 -1
  37. package/src/core/mcp/index.ts +12 -12
  38. package/src/core/mcp/loader.ts +2 -2
  39. package/src/core/mcp/manager.ts +6 -6
  40. package/src/core/mcp/tool-bridge.ts +3 -3
  41. package/src/core/mcp/transports/http.ts +1 -1
  42. package/src/core/mcp/transports/index.ts +2 -2
  43. package/src/core/mcp/transports/stdio.ts +1 -1
  44. package/src/core/messages.ts +22 -0
  45. package/src/core/model-registry.ts +2 -2
  46. package/src/core/model-resolver.ts +103 -2
  47. package/src/core/plugins/doctor.ts +1 -1
  48. package/src/core/plugins/index.ts +6 -6
  49. package/src/core/plugins/installer.ts +4 -4
  50. package/src/core/plugins/loader.ts +4 -9
  51. package/src/core/plugins/manager.ts +5 -5
  52. package/src/core/plugins/paths.ts +3 -3
  53. package/src/core/sdk.ts +127 -52
  54. package/src/core/session-manager.ts +123 -20
  55. package/src/core/settings-manager.ts +106 -22
  56. package/src/core/skills.ts +5 -5
  57. package/src/core/slash-commands.ts +60 -45
  58. package/src/core/system-prompt.ts +6 -6
  59. package/src/core/title-generator.ts +94 -0
  60. package/src/core/tools/bash.ts +33 -157
  61. package/src/core/tools/context.ts +2 -2
  62. package/src/core/tools/edit-diff.ts +5 -5
  63. package/src/core/tools/edit.ts +60 -9
  64. package/src/core/tools/exa/company.ts +3 -3
  65. package/src/core/tools/exa/index.ts +16 -17
  66. package/src/core/tools/exa/linkedin.ts +3 -3
  67. package/src/core/tools/exa/mcp-client.ts +9 -9
  68. package/src/core/tools/exa/render.ts +5 -5
  69. package/src/core/tools/exa/researcher.ts +3 -3
  70. package/src/core/tools/exa/search.ts +6 -5
  71. package/src/core/tools/exa/types.ts +5 -6
  72. package/src/core/tools/exa/websets.ts +3 -3
  73. package/src/core/tools/find.ts +3 -3
  74. package/src/core/tools/grep.ts +6 -5
  75. package/src/core/tools/index.ts +114 -40
  76. package/src/core/tools/ls.ts +4 -4
  77. package/src/core/tools/lsp/client.ts +204 -108
  78. package/src/core/tools/lsp/config.ts +709 -35
  79. package/src/core/tools/lsp/edits.ts +2 -2
  80. package/src/core/tools/lsp/index.ts +432 -30
  81. package/src/core/tools/lsp/render.ts +2 -2
  82. package/src/core/tools/lsp/rust-analyzer.ts +3 -3
  83. package/src/core/tools/lsp/types.ts +5 -0
  84. package/src/core/tools/lsp/utils.ts +1 -1
  85. package/src/core/tools/notebook.ts +1 -1
  86. package/src/core/tools/output.ts +175 -0
  87. package/src/core/tools/read.ts +7 -7
  88. package/src/core/tools/renderers.ts +92 -13
  89. package/src/core/tools/review.ts +268 -0
  90. package/src/core/tools/task/agents.ts +1 -1
  91. package/src/core/tools/task/bundled-agents/explore.md +1 -1
  92. package/src/core/tools/task/bundled-agents/reviewer.md +53 -38
  93. package/src/core/tools/task/discovery.ts +2 -2
  94. package/src/core/tools/task/executor.ts +145 -28
  95. package/src/core/tools/task/index.ts +78 -30
  96. package/src/core/tools/task/model-resolver.ts +72 -13
  97. package/src/core/tools/task/parallel.ts +1 -1
  98. package/src/core/tools/task/render.ts +219 -30
  99. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  100. package/src/core/tools/task/types.ts +36 -2
  101. package/src/core/tools/web-fetch.ts +5 -3
  102. package/src/core/tools/web-search/auth.ts +1 -1
  103. package/src/core/tools/web-search/index.ts +17 -15
  104. package/src/core/tools/web-search/providers/anthropic.ts +2 -2
  105. package/src/core/tools/web-search/providers/exa.ts +3 -5
  106. package/src/core/tools/web-search/providers/perplexity.ts +1 -1
  107. package/src/core/tools/web-search/render.ts +3 -3
  108. package/src/core/tools/write.ts +70 -7
  109. package/src/index.ts +33 -17
  110. package/src/main.ts +60 -34
  111. package/src/migrations.ts +3 -3
  112. package/src/modes/index.ts +5 -5
  113. package/src/modes/interactive/components/armin.ts +1 -1
  114. package/src/modes/interactive/components/assistant-message.ts +1 -1
  115. package/src/modes/interactive/components/bash-execution.ts +4 -4
  116. package/src/modes/interactive/components/bordered-loader.ts +2 -2
  117. package/src/modes/interactive/components/branch-summary-message.ts +2 -2
  118. package/src/modes/interactive/components/compaction-summary-message.ts +2 -2
  119. package/src/modes/interactive/components/diff.ts +1 -1
  120. package/src/modes/interactive/components/dynamic-border.ts +1 -1
  121. package/src/modes/interactive/components/footer.ts +5 -5
  122. package/src/modes/interactive/components/hook-editor.ts +2 -2
  123. package/src/modes/interactive/components/hook-input.ts +2 -2
  124. package/src/modes/interactive/components/hook-message.ts +3 -3
  125. package/src/modes/interactive/components/hook-selector.ts +2 -2
  126. package/src/modes/interactive/components/model-selector.ts +341 -41
  127. package/src/modes/interactive/components/oauth-selector.ts +3 -3
  128. package/src/modes/interactive/components/plugin-settings.ts +4 -4
  129. package/src/modes/interactive/components/queue-mode-selector.ts +2 -2
  130. package/src/modes/interactive/components/session-selector.ts +24 -11
  131. package/src/modes/interactive/components/settings-defs.ts +51 -3
  132. package/src/modes/interactive/components/settings-selector.ts +13 -16
  133. package/src/modes/interactive/components/show-images-selector.ts +2 -2
  134. package/src/modes/interactive/components/theme-selector.ts +2 -2
  135. package/src/modes/interactive/components/thinking-selector.ts +2 -2
  136. package/src/modes/interactive/components/tool-execution.ts +44 -8
  137. package/src/modes/interactive/components/tree-selector.ts +5 -5
  138. package/src/modes/interactive/components/user-message-selector.ts +2 -2
  139. package/src/modes/interactive/components/user-message.ts +1 -1
  140. package/src/modes/interactive/components/welcome.ts +42 -5
  141. package/src/modes/interactive/interactive-mode.ts +169 -48
  142. package/src/modes/interactive/theme/theme.ts +8 -7
  143. package/src/modes/print-mode.ts +4 -3
  144. package/src/modes/rpc/rpc-client.ts +4 -4
  145. package/src/modes/rpc/rpc-mode.ts +21 -11
  146. package/src/modes/rpc/rpc-types.ts +3 -3
  147. package/src/utils/changelog.ts +2 -2
  148. package/src/utils/clipboard.ts +1 -1
  149. package/src/utils/shell-snapshot.ts +218 -0
  150. package/src/utils/shell.ts +93 -13
  151. package/src/utils/tools-manager.ts +1 -1
  152. package/examples/custom-tools/subagent/agents/reviewer.md +0 -35
  153. package/src/core/tools/exa/logger.ts +0 -56
@@ -1,9 +1,9 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { basename, join } from "node:path";
1
3
  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";
4
+ import { APP_NAME, getExportTemplateDir } from "../../config";
5
+ import { getResolvedThemeColors, getThemeExportColors } from "../../modes/interactive/theme/theme";
6
+ import { SessionManager } from "../session-manager";
7
7
 
8
8
  // Cached minified assets (populated on first use)
9
9
  let cachedTemplate: string | null = null;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Auto-read file mentions from user prompts.
3
+ *
4
+ * When users reference files with @path syntax (e.g., "@src/foo.ts"),
5
+ * we automatically inject the file contents as a FileMentionMessage
6
+ * so the agent doesn't need to read them manually.
7
+ */
8
+
9
+ import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
10
+ import type { FileMentionMessage } from "./messages";
11
+ import { createReadTool } from "./tools/read";
12
+
13
+ /** Regex to match @filepath patterns in text */
14
+ const FILE_MENTION_REGEX = /@((?:[^\s@]+\/)*[^\s@]+\.[a-zA-Z0-9]+)/g;
15
+
16
+ /** Extract all @filepath mentions from text */
17
+ export function extractFileMentions(text: string): string[] {
18
+ const matches = [...text.matchAll(FILE_MENTION_REGEX)];
19
+ return [...new Set(matches.map((m) => m[1]))];
20
+ }
21
+
22
+ /**
23
+ * Generate a FileMentionMessage containing the contents of mentioned files.
24
+ * Returns empty array if no files could be read.
25
+ */
26
+ export async function generateFileMentionMessages(filePaths: string[], cwd: string): Promise<AgentMessage[]> {
27
+ if (filePaths.length === 0) return [];
28
+
29
+ const readTool = createReadTool(cwd);
30
+ const files: FileMentionMessage["files"] = [];
31
+
32
+ for (const filePath of filePaths) {
33
+ try {
34
+ const result = await readTool.execute("auto-read", { path: filePath });
35
+ const textContent = result.content.find((c) => c.type === "text");
36
+ if (textContent && textContent.type === "text") {
37
+ const lineCount = textContent.text.split("\n").length;
38
+ files.push({ path: filePath, content: textContent.text, lineCount });
39
+ }
40
+ } catch {
41
+ // File doesn't exist or isn't readable - skip silently
42
+ }
43
+ }
44
+
45
+ if (files.length === 0) return [];
46
+
47
+ const message: FileMentionMessage = {
48
+ role: "fileMention",
49
+ files,
50
+ timestamp: Date.now(),
51
+ };
52
+
53
+ return [message];
54
+ }
@@ -9,8 +9,8 @@ export {
9
9
  type NavigateTreeHandler,
10
10
  type NewSessionHandler,
11
11
  type SendMessageHandler,
12
- } from "./loader.js";
13
- export { execCommand, HookRunner, type HookErrorListener } from "./runner.js";
14
- export { wrapToolsWithHooks, wrapToolWithHooks } from "./tool-wrapper.js";
15
- export * from "./types.js";
16
- export type { ReadonlySessionManager } from "../session-manager.js";
12
+ } from "./loader";
13
+ export { execCommand, HookRunner, type HookErrorListener } from "./runner";
14
+ export { wrapToolsWithHooks, wrapToolWithHooks } from "./tool-wrapper";
15
+ export * from "./types";
16
+ export type { ReadonlySessionManager } from "../session-manager";
@@ -6,13 +6,14 @@ import * as fs from "node:fs";
6
6
  import * as os from "node:os";
7
7
  import * as path from "node:path";
8
8
  import * as typebox from "@sinclair/typebox";
9
- import { getAgentDir } from "../../config.js";
10
- import * as piCodingAgent from "../../index.js";
11
- import type { HookMessage } from "../messages.js";
12
- import { getAllPluginHookPaths } from "../plugins/loader.js";
13
- import type { SessionManager } from "../session-manager.js";
14
- import { execCommand } from "./runner.js";
15
- import type { ExecOptions, HookAPI, HookFactory, HookMessageRenderer, RegisteredCommand } from "./types.js";
9
+ import { getAgentDir } from "../../config";
10
+ import * as piCodingAgent from "../../index";
11
+ import { logger } from "../logger";
12
+ import type { HookMessage } from "../messages";
13
+ import { getAllPluginHookPaths } from "../plugins/loader";
14
+ import type { SessionManager } from "../session-manager";
15
+ import { execCommand } from "./runner";
16
+ import type { ExecOptions, HookAPI, HookFactory, HookMessageRenderer, RegisteredCommand } from "./types";
16
17
 
17
18
  /**
18
19
  * Generic handler function type.
@@ -131,12 +132,8 @@ function createHookAPI(
131
132
  setSendMessageHandler: (handler: SendMessageHandler) => void;
132
133
  setAppendEntryHandler: (handler: AppendEntryHandler) => void;
133
134
  } {
134
- let sendMessageHandler: SendMessageHandler = () => {
135
- // Default no-op until mode sets the handler
136
- };
137
- let appendEntryHandler: AppendEntryHandler = () => {
138
- // Default no-op until mode sets the handler
139
- };
135
+ let sendMessageHandler: SendMessageHandler | null = null;
136
+ let appendEntryHandler: AppendEntryHandler | null = null;
140
137
  const messageRenderers = new Map<string, HookMessageRenderer>();
141
138
  const commands = new Map<string, RegisteredCommand>();
142
139
 
@@ -144,14 +141,21 @@ function createHookAPI(
144
141
  // but the interface has specific overloads for type safety in hooks
145
142
  const api = {
146
143
  on(event: string, handler: HandlerFn): void {
147
- const list = handlers.get(event) ?? [];
148
- list.push(handler);
149
- handlers.set(event, list);
144
+ if (!handlers.has(event)) {
145
+ handlers.set(event, []);
146
+ }
147
+ handlers.get(event)!.push(handler);
150
148
  },
151
149
  sendMessage<T = unknown>(message: HookMessage<T>, triggerTurn?: boolean): void {
150
+ if (!sendMessageHandler) {
151
+ throw new Error("sendMessage handler not initialized");
152
+ }
152
153
  sendMessageHandler(message, triggerTurn);
153
154
  },
154
155
  appendEntry<T = unknown>(customType: string, data?: T): void {
156
+ if (!appendEntryHandler) {
157
+ throw new Error("appendEntry handler not initialized");
158
+ }
155
159
  appendEntryHandler(customType, data);
156
160
  },
157
161
  registerMessageRenderer<T = unknown>(customType: string, renderer: HookMessageRenderer<T>): void {
@@ -163,6 +167,7 @@ function createHookAPI(
163
167
  exec(command: string, args: string[], options?: ExecOptions) {
164
168
  return execCommand(command, args, options?.cwd ?? cwd, options);
165
169
  },
170
+ logger,
166
171
  typebox,
167
172
  pi: piCodingAgent,
168
173
  } as HookAPI;
@@ -4,9 +4,9 @@
4
4
 
5
5
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
6
6
  import type { Model } from "@oh-my-pi/pi-ai";
7
- import { theme } from "../../modes/interactive/theme/theme.js";
8
- import type { ModelRegistry } from "../model-registry.js";
9
- import type { SessionManager } from "../session-manager.js";
7
+ import { theme } from "../../modes/interactive/theme/theme";
8
+ import type { ModelRegistry } from "../model-registry";
9
+ import type { SessionManager } from "../session-manager";
10
10
  import type {
11
11
  AppendEntryHandler,
12
12
  BranchHandler,
@@ -14,7 +14,7 @@ import type {
14
14
  NavigateTreeHandler,
15
15
  NewSessionHandler,
16
16
  SendMessageHandler,
17
- } from "./loader.js";
17
+ } from "./loader";
18
18
  import type {
19
19
  BeforeAgentStartEvent,
20
20
  BeforeAgentStartEventResult,
@@ -32,7 +32,7 @@ import type {
32
32
  ToolCallEvent,
33
33
  ToolCallEventResult,
34
34
  ToolResultEventResult,
35
- } from "./types.js";
35
+ } from "./types";
36
36
 
37
37
  /**
38
38
  * Listener for hook errors.
@@ -40,7 +40,7 @@ import type {
40
40
  export type HookErrorListener = (error: HookError) => void;
41
41
 
42
42
  // Re-export execCommand for backward compatibility
43
- export { execCommand } from "../exec.js";
43
+ export { execCommand } from "../exec";
44
44
 
45
45
  /** No-op UI context used when no UI is available */
46
46
  const noOpUIContext: HookUIContext = {
@@ -3,8 +3,8 @@
3
3
  */
4
4
 
5
5
  import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
6
- import type { HookRunner } from "./runner.js";
7
- import type { ToolCallEventResult, ToolResultEventResult } from "./types.js";
6
+ import type { HookRunner } from "./runner";
7
+ import type { ToolCallEventResult, ToolResultEventResult } from "./types";
8
8
 
9
9
  /**
10
10
  * Wrap a tool with hook callbacks.
@@ -8,30 +8,25 @@
8
8
  import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
9
9
  import type { ImageContent, Message, Model, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
10
10
  import type { Component, TUI } from "@oh-my-pi/pi-tui";
11
- import type { Theme } from "../../modes/interactive/theme/theme.js";
12
- import type { CompactionPreparation, CompactionResult } from "../compaction/index.js";
13
- import type { ExecOptions, ExecResult } from "../exec.js";
14
- import type { HookMessage } from "../messages.js";
15
- import type { ModelRegistry } from "../model-registry.js";
11
+ import type { Theme } from "../../modes/interactive/theme/theme";
12
+ import type { CompactionPreparation, CompactionResult } from "../compaction/index";
13
+ import type { ExecOptions, ExecResult } from "../exec";
14
+ import type { Logger } from "../logger";
15
+ import type { HookMessage } from "../messages";
16
+ import type { ModelRegistry } from "../model-registry";
16
17
  import type {
17
18
  BranchSummaryEntry,
18
19
  CompactionEntry,
19
20
  ReadonlySessionManager,
20
21
  SessionEntry,
21
22
  SessionManager,
22
- } from "../session-manager.js";
23
+ } from "../session-manager";
23
24
 
24
- import type { EditToolDetails } from "../tools/edit.js";
25
- import type {
26
- BashToolDetails,
27
- FindToolDetails,
28
- GrepToolDetails,
29
- LsToolDetails,
30
- ReadToolDetails,
31
- } from "../tools/index.js";
25
+ import type { EditToolDetails } from "../tools/edit";
26
+ import type { BashToolDetails, FindToolDetails, GrepToolDetails, LsToolDetails, ReadToolDetails } from "../tools/index";
32
27
 
33
28
  // Re-export for backward compatibility
34
- export type { ExecOptions, ExecResult } from "../exec.js";
29
+ export type { ExecOptions, ExecResult } from "../exec";
35
30
 
36
31
  /**
37
32
  * UI context for hooks to request interactive UI from the harness.
@@ -747,6 +742,8 @@ export interface HookAPI {
747
742
  */
748
743
  exec(command: string, args: string[], options?: ExecOptions): Promise<ExecResult>;
749
744
 
745
+ /** File logger for error/warning/debug messages */
746
+ logger: Logger;
750
747
  /** Injected @sinclair/typebox module */
751
748
  typebox: typeof import("@sinclair/typebox");
752
749
  /** Injected pi-coding-agent exports */
package/src/core/index.ts CHANGED
@@ -10,9 +10,9 @@ export {
10
10
  type ModelCycleResult,
11
11
  type PromptOptions,
12
12
  type SessionStats,
13
- } from "./agent-session.js";
14
- export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor.js";
15
- export type { CompactionResult } from "./compaction/index.js";
13
+ } from "./agent-session";
14
+ export { type BashExecutorOptions, type BashResult, executeBash } from "./bash-executor";
15
+ export type { CompactionResult } from "./compaction/index";
16
16
  export {
17
17
  type CustomTool,
18
18
  type CustomToolAPI,
@@ -24,7 +24,7 @@ export {
24
24
  type LoadedCustomTool,
25
25
  loadCustomTools,
26
26
  type RenderResultOptions,
27
- } from "./custom-tools/index.js";
27
+ } from "./custom-tools/index";
28
28
  export {
29
29
  type HookAPI,
30
30
  type HookContext,
@@ -34,7 +34,7 @@ export {
34
34
  HookRunner,
35
35
  type HookUIContext,
36
36
  loadHooks,
37
- } from "./hooks/index.js";
37
+ } from "./hooks/index";
38
38
  export {
39
39
  createMCPManager,
40
40
  discoverAndLoadMCPTools,
@@ -49,4 +49,4 @@ export {
49
49
  type MCPToolDetails,
50
50
  type MCPToolsLoadResult,
51
51
  type MCPTransport,
52
- } from "./mcp/index.js";
52
+ } from "./mcp/index";
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Centralized file logger for pi.
3
+ *
4
+ * Logs to ~/.pi/logs/ with size-based rotation, supporting concurrent pi instances.
5
+ * Each log entry includes process.pid for traceability.
6
+ */
7
+
8
+ import { existsSync, mkdirSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ import winston from "winston";
12
+ import DailyRotateFile from "winston-daily-rotate-file";
13
+ import { CONFIG_DIR_NAME } from "../config";
14
+
15
+ /** Get the logs directory (~/.pi/logs/) */
16
+ function getLogsDir(): string {
17
+ return join(homedir(), CONFIG_DIR_NAME, "logs");
18
+ }
19
+
20
+ /** Ensure logs directory exists */
21
+ function ensureLogsDir(): string {
22
+ const logsDir = getLogsDir();
23
+ if (!existsSync(logsDir)) {
24
+ mkdirSync(logsDir, { recursive: true });
25
+ }
26
+ return logsDir;
27
+ }
28
+
29
+ /** Custom format that includes pid and flattens metadata */
30
+ const logFormat = winston.format.combine(
31
+ winston.format.timestamp({ format: "YYYY-MM-DDTHH:mm:ss.SSSZ" }),
32
+ winston.format.printf(({ timestamp, level, message, ...meta }) => {
33
+ const entry: Record<string, unknown> = {
34
+ timestamp,
35
+ level,
36
+ pid: process.pid,
37
+ message,
38
+ };
39
+ // Flatten metadata into entry
40
+ for (const [key, value] of Object.entries(meta)) {
41
+ if (key !== "level" && key !== "timestamp" && key !== "message") {
42
+ entry[key] = value;
43
+ }
44
+ }
45
+ return JSON.stringify(entry);
46
+ }),
47
+ );
48
+
49
+ /** Size-based rotating file transport */
50
+ const fileTransport = new DailyRotateFile({
51
+ dirname: ensureLogsDir(),
52
+ filename: "pi.%DATE%.log",
53
+ datePattern: "YYYY-MM-DD",
54
+ maxSize: "10m",
55
+ maxFiles: 5,
56
+ zippedArchive: true,
57
+ });
58
+
59
+ /** The winston logger instance */
60
+ const winstonLogger = winston.createLogger({
61
+ level: "debug",
62
+ format: logFormat,
63
+ transports: [fileTransport],
64
+ // Don't exit on error - logging failures shouldn't crash the app
65
+ exitOnError: false,
66
+ });
67
+
68
+ /** Logger type exposed to plugins and internal code */
69
+ export interface Logger {
70
+ error(message: string, context?: Record<string, unknown>): void;
71
+ warn(message: string, context?: Record<string, unknown>): void;
72
+ debug(message: string, context?: Record<string, unknown>): void;
73
+ }
74
+
75
+ /**
76
+ * Centralized logger for pi.
77
+ *
78
+ * Logs to ~/.pi/logs/pi.YYYY-MM-DD.log with size-based rotation.
79
+ * Safe for concurrent access from multiple pi instances.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * import { logger } from "../core/logger";
84
+ *
85
+ * logger.error("MCP request failed", { url, method });
86
+ * logger.warn("Theme file invalid, using fallback", { path });
87
+ * logger.debug("LSP fallback triggered", { reason });
88
+ * ```
89
+ */
90
+ export const logger: Logger = {
91
+ error(message: string, context?: Record<string, unknown>): void {
92
+ try {
93
+ winstonLogger.error(message, context);
94
+ } catch {
95
+ // Silently ignore logging failures
96
+ }
97
+ },
98
+ warn(message: string, context?: Record<string, unknown>): void {
99
+ try {
100
+ winstonLogger.warn(message, context);
101
+ } catch {
102
+ // Silently ignore logging failures
103
+ }
104
+ },
105
+ debug(message: string, context?: Record<string, unknown>): void {
106
+ try {
107
+ winstonLogger.debug(message, context);
108
+ } catch {
109
+ // Silently ignore logging failures
110
+ }
111
+ },
112
+ };
@@ -4,8 +4,8 @@
4
4
  * Handles connection initialization, tool listing, and tool calling.
5
5
  */
6
6
 
7
- import { createHttpTransport } from "./transports/http.js";
8
- import { createStdioTransport } from "./transports/stdio.js";
7
+ import { createHttpTransport } from "./transports/http";
8
+ import { createStdioTransport } from "./transports/stdio";
9
9
  import type {
10
10
  MCPHttpServerConfig,
11
11
  MCPInitializeParams,
@@ -20,7 +20,7 @@ import type {
20
20
  MCPToolDefinition,
21
21
  MCPToolsListResult,
22
22
  MCPTransport,
23
- } from "./types.js";
23
+ } from "./types";
24
24
 
25
25
  /** MCP protocol version we support */
26
26
  const PROTOCOL_VERSION = "2025-03-26";
@@ -8,7 +8,7 @@
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import { homedir } from "node:os";
10
10
  import { join } from "node:path";
11
- import type { MCPConfigFile, MCPServerConfig } from "./types.js";
11
+ import type { MCPConfigFile, MCPServerConfig } from "./types";
12
12
 
13
13
  /** Environment variable expansion pattern: ${VAR} or ${VAR:-default} */
14
14
  const ENV_VAR_PATTERN = /\$\{([^}:]+)(?::-([^}]*))?\}/g;
@@ -6,10 +6,10 @@
6
6
  */
7
7
 
8
8
  // Client
9
- export { callTool, connectToServer, disconnectServer, listTools, serverSupportsTools } from "./client.js";
9
+ export { callTool, connectToServer, disconnectServer, listTools, serverSupportsTools } from "./client";
10
10
 
11
11
  // Config
12
- export type { ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config.js";
12
+ export type { ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config";
13
13
  export {
14
14
  expandEnvVars,
15
15
  extractExaApiKey,
@@ -20,19 +20,19 @@ export {
20
20
  loadMCPConfigFile,
21
21
  mergeMCPConfigs,
22
22
  validateServerConfig,
23
- } from "./config.js";
23
+ } from "./config";
24
24
  // Loader (for SDK integration)
25
- export type { MCPToolsLoadOptions, MCPToolsLoadResult } from "./loader.js";
26
- export { discoverAndLoadMCPTools } from "./loader.js";
25
+ export type { MCPToolsLoadOptions, MCPToolsLoadResult } from "./loader";
26
+ export { discoverAndLoadMCPTools } from "./loader";
27
27
  // Manager
28
- export type { MCPDiscoverOptions, MCPLoadResult } from "./manager.js";
29
- export { createMCPManager, MCPManager } from "./manager.js";
28
+ export type { MCPDiscoverOptions, MCPLoadResult } from "./manager";
29
+ export { createMCPManager, MCPManager } from "./manager";
30
30
  // Tool bridge
31
- export type { MCPToolDetails } from "./tool-bridge.js";
32
- export { createMCPTool, createMCPToolName, createMCPTools, parseMCPToolName } from "./tool-bridge.js";
31
+ export type { MCPToolDetails } from "./tool-bridge";
32
+ export { createMCPTool, createMCPToolName, createMCPTools, parseMCPToolName } from "./tool-bridge";
33
33
  // Transports
34
- export { createHttpTransport, HttpTransport } from "./transports/http.js";
35
- export { createStdioTransport, StdioTransport } from "./transports/stdio.js";
34
+ export { createHttpTransport, HttpTransport } from "./transports/http";
35
+ export { createStdioTransport, StdioTransport } from "./transports/stdio";
36
36
  // Types
37
37
  export type {
38
38
  MCPConfigFile,
@@ -46,4 +46,4 @@ export type {
46
46
  MCPToolDefinition,
47
47
  MCPToolWithServer,
48
48
  MCPTransport,
49
- } from "./types.js";
49
+ } from "./types";
@@ -4,8 +4,8 @@
4
4
  * Integrates MCP tool discovery with the custom tools system.
5
5
  */
6
6
 
7
- import type { LoadedCustomTool } from "../custom-tools/types.js";
8
- import { type MCPLoadResult, MCPManager } from "./manager.js";
7
+ import type { LoadedCustomTool } from "../custom-tools/types";
8
+ import { type MCPLoadResult, MCPManager } from "./manager";
9
9
 
10
10
  /** Result from loading MCP tools */
11
11
  export interface MCPToolsLoadResult {
@@ -6,12 +6,12 @@
6
6
  */
7
7
 
8
8
  import type { TSchema } from "@sinclair/typebox";
9
- import type { CustomTool } from "../custom-tools/types.js";
10
- import { connectToServer, disconnectServer, listTools } from "./client.js";
11
- import { type LoadMCPConfigsOptions, loadAllMCPConfigs, validateServerConfig } from "./config.js";
12
- import type { MCPToolDetails } from "./tool-bridge.js";
13
- import { createMCPTools } from "./tool-bridge.js";
14
- import type { MCPServerConfig, MCPServerConnection } from "./types.js";
9
+ import type { CustomTool } from "../custom-tools/types";
10
+ import { connectToServer, disconnectServer, listTools } from "./client";
11
+ import { type LoadMCPConfigsOptions, loadAllMCPConfigs, validateServerConfig } from "./config";
12
+ import type { MCPToolDetails } from "./tool-bridge";
13
+ import { createMCPTools } from "./tool-bridge";
14
+ import type { MCPServerConfig, MCPServerConnection } from "./types";
15
15
 
16
16
  /** Result of loading MCP tools */
17
17
  export interface MCPLoadResult {
@@ -5,9 +5,9 @@
5
5
  */
6
6
 
7
7
  import type { TSchema } from "@sinclair/typebox";
8
- import type { CustomTool, CustomToolResult } from "../custom-tools/types.js";
9
- import { callTool } from "./client.js";
10
- import type { MCPContent, MCPServerConnection, MCPToolDefinition } from "./types.js";
8
+ import type { CustomTool, CustomToolResult } from "../custom-tools/types";
9
+ import { callTool } from "./client";
10
+ import type { MCPContent, MCPServerConnection, MCPToolDefinition } from "./types";
11
11
 
12
12
  /** Details included in MCP tool results for rendering */
13
13
  export interface MCPToolDetails {
@@ -5,7 +5,7 @@
5
5
  * Based on MCP spec 2025-03-26.
6
6
  */
7
7
 
8
- import type { JsonRpcResponse, MCPHttpServerConfig, MCPSseServerConfig, MCPTransport } from "../types.js";
8
+ import type { JsonRpcResponse, MCPHttpServerConfig, MCPSseServerConfig, MCPTransport } from "../types";
9
9
 
10
10
  /** Generate unique request ID */
11
11
  function generateId(): string {
@@ -2,5 +2,5 @@
2
2
  * MCP transport exports.
3
3
  */
4
4
 
5
- export { createHttpTransport, HttpTransport } from "./http.js";
6
- export { createStdioTransport, StdioTransport } from "./stdio.js";
5
+ export { createHttpTransport, HttpTransport } from "./http";
6
+ export { createStdioTransport, StdioTransport } from "./stdio";
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { type Subprocess, spawn } from "bun";
9
- import type { JsonRpcResponse, MCPStdioServerConfig, MCPTransport } from "../types.js";
9
+ import type { JsonRpcResponse, MCPStdioServerConfig, MCPTransport } from "../types";
10
10
 
11
11
  /** Generate unique request ID */
12
12
  function generateId(): string {
@@ -64,6 +64,19 @@ export interface CompactionSummaryMessage {
64
64
  timestamp: number;
65
65
  }
66
66
 
67
+ /**
68
+ * Message type for auto-read file mentions via @filepath syntax.
69
+ */
70
+ export interface FileMentionMessage {
71
+ role: "fileMention";
72
+ files: Array<{
73
+ path: string;
74
+ content: string;
75
+ lineCount: number;
76
+ }>;
77
+ timestamp: number;
78
+ }
79
+
67
80
  // Extend CustomAgentMessages via declaration merging
68
81
  declare module "@oh-my-pi/pi-agent-core" {
69
82
  interface CustomAgentMessages {
@@ -71,6 +84,7 @@ declare module "@oh-my-pi/pi-agent-core" {
71
84
  hookMessage: HookMessage;
72
85
  branchSummary: BranchSummaryMessage;
73
86
  compactionSummary: CompactionSummaryMessage;
87
+ fileMention: FileMentionMessage;
74
88
  }
75
89
  }
76
90
 
@@ -175,6 +189,14 @@ export function convertToLlm(messages: AgentMessage[]): Message[] {
175
189
  ],
176
190
  timestamp: m.timestamp,
177
191
  };
192
+ case "fileMention": {
193
+ const fileContents = m.files.map((f) => `<file path="${f.path}">\n${f.content}\n</file>`).join("\n\n");
194
+ return {
195
+ role: "user",
196
+ content: [{ type: "text" as const, text: `<system-reminder>\n${fileContents}\n</system-reminder>` }],
197
+ timestamp: m.timestamp,
198
+ };
199
+ }
178
200
  case "user":
179
201
  case "assistant":
180
202
  case "toolResult":
@@ -2,6 +2,7 @@
2
2
  * Model registry - manages built-in and custom models, provides API key resolution.
3
3
  */
4
4
 
5
+ import { existsSync, readFileSync } from "node:fs";
5
6
  import {
6
7
  type Api,
7
8
  getGitHubCopilotBaseUrl,
@@ -13,8 +14,7 @@ import {
13
14
  } from "@oh-my-pi/pi-ai";
14
15
  import { type Static, Type } from "@sinclair/typebox";
15
16
  import AjvModule from "ajv";
16
- import { existsSync, readFileSync } from "fs";
17
- import type { AuthStorage } from "./auth-storage.js";
17
+ import type { AuthStorage } from "./auth-storage";
18
18
 
19
19
  const Ajv = (AjvModule as any).default || AjvModule;
20
20