@oh-my-pi/pi-coding-agent 1.341.0 → 2.1.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 (158) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +1 -1
  3. package/examples/custom-tools/subagent/index.ts +1 -1
  4. package/package.json +10 -9
  5. package/src/bun-imports.d.ts +16 -0
  6. package/src/cli/args.ts +5 -6
  7. package/src/cli/file-processor.ts +3 -3
  8. package/src/cli/list-models.ts +2 -2
  9. package/src/cli/plugin-cli.ts +1 -1
  10. package/src/cli/session-picker.ts +2 -2
  11. package/src/cli/update-cli.ts +273 -0
  12. package/src/cli.ts +1 -1
  13. package/src/config.ts +23 -75
  14. package/src/core/agent-session.ts +158 -16
  15. package/src/core/auth-storage.ts +2 -3
  16. package/src/core/bash-executor.ts +50 -10
  17. package/src/core/compaction/branch-summarization.ts +5 -5
  18. package/src/core/compaction/compaction.ts +3 -3
  19. package/src/core/compaction/index.ts +3 -3
  20. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  21. package/src/core/custom-commands/index.ts +15 -0
  22. package/src/core/custom-commands/loader.ts +232 -0
  23. package/src/core/custom-commands/types.ts +112 -0
  24. package/src/core/custom-tools/index.ts +3 -3
  25. package/src/core/custom-tools/loader.ts +10 -8
  26. package/src/core/custom-tools/types.ts +11 -6
  27. package/src/core/custom-tools/wrapper.ts +2 -1
  28. package/src/core/exec.ts +22 -12
  29. package/src/core/export-html/index.ts +38 -123
  30. package/src/core/export-html/template.css +0 -7
  31. package/src/core/export-html/template.html +3 -4
  32. package/src/core/export-html/template.macro.ts +24 -0
  33. package/src/core/file-mentions.ts +54 -0
  34. package/src/core/hooks/index.ts +5 -5
  35. package/src/core/hooks/loader.ts +21 -16
  36. package/src/core/hooks/runner.ts +6 -6
  37. package/src/core/hooks/tool-wrapper.ts +2 -2
  38. package/src/core/hooks/types.ts +12 -15
  39. package/src/core/index.ts +6 -6
  40. package/src/core/logger.ts +112 -0
  41. package/src/core/mcp/client.ts +3 -3
  42. package/src/core/mcp/config.ts +1 -1
  43. package/src/core/mcp/index.ts +12 -12
  44. package/src/core/mcp/loader.ts +2 -2
  45. package/src/core/mcp/manager.ts +6 -6
  46. package/src/core/mcp/tool-bridge.ts +3 -3
  47. package/src/core/mcp/transports/http.ts +1 -1
  48. package/src/core/mcp/transports/index.ts +2 -2
  49. package/src/core/mcp/transports/stdio.ts +1 -1
  50. package/src/core/messages.ts +22 -0
  51. package/src/core/model-registry.ts +2 -2
  52. package/src/core/model-resolver.ts +2 -2
  53. package/src/core/plugins/doctor.ts +1 -1
  54. package/src/core/plugins/index.ts +6 -6
  55. package/src/core/plugins/installer.ts +4 -4
  56. package/src/core/plugins/loader.ts +4 -9
  57. package/src/core/plugins/manager.ts +5 -5
  58. package/src/core/plugins/paths.ts +3 -3
  59. package/src/core/sdk.ts +77 -35
  60. package/src/core/session-manager.ts +6 -6
  61. package/src/core/settings-manager.ts +16 -3
  62. package/src/core/skills.ts +5 -5
  63. package/src/core/slash-commands.ts +60 -45
  64. package/src/core/system-prompt.ts +6 -6
  65. package/src/core/title-generator.ts +2 -2
  66. package/src/core/tools/bash.ts +32 -155
  67. package/src/core/tools/context.ts +2 -2
  68. package/src/core/tools/edit-diff.ts +3 -3
  69. package/src/core/tools/edit.ts +18 -5
  70. package/src/core/tools/exa/company.ts +3 -3
  71. package/src/core/tools/exa/index.ts +16 -17
  72. package/src/core/tools/exa/linkedin.ts +3 -3
  73. package/src/core/tools/exa/mcp-client.ts +9 -9
  74. package/src/core/tools/exa/render.ts +5 -5
  75. package/src/core/tools/exa/researcher.ts +3 -3
  76. package/src/core/tools/exa/search.ts +6 -5
  77. package/src/core/tools/exa/types.ts +5 -6
  78. package/src/core/tools/exa/websets.ts +3 -3
  79. package/src/core/tools/find.ts +3 -3
  80. package/src/core/tools/grep.ts +3 -3
  81. package/src/core/tools/index.ts +48 -34
  82. package/src/core/tools/ls.ts +4 -4
  83. package/src/core/tools/lsp/client.ts +161 -90
  84. package/src/core/tools/lsp/config.ts +1 -1
  85. package/src/core/tools/lsp/edits.ts +2 -2
  86. package/src/core/tools/lsp/index.ts +15 -13
  87. package/src/core/tools/lsp/render.ts +2 -2
  88. package/src/core/tools/lsp/rust-analyzer.ts +3 -3
  89. package/src/core/tools/lsp/utils.ts +1 -1
  90. package/src/core/tools/notebook.ts +1 -1
  91. package/src/core/tools/output.ts +175 -0
  92. package/src/core/tools/read.ts +7 -7
  93. package/src/core/tools/renderers.ts +92 -13
  94. package/src/core/tools/review.ts +268 -0
  95. package/src/core/tools/task/agents.ts +22 -38
  96. package/src/core/tools/task/bundled-agents/reviewer.md +52 -37
  97. package/src/core/tools/task/commands.ts +31 -10
  98. package/src/core/tools/task/discovery.ts +2 -2
  99. package/src/core/tools/task/executor.ts +145 -28
  100. package/src/core/tools/task/index.ts +78 -30
  101. package/src/core/tools/task/model-resolver.ts +30 -20
  102. package/src/core/tools/task/parallel.ts +1 -1
  103. package/src/core/tools/task/render.ts +219 -30
  104. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  105. package/src/core/tools/task/types.ts +36 -2
  106. package/src/core/tools/web-fetch.ts +5 -3
  107. package/src/core/tools/web-search/auth.ts +1 -1
  108. package/src/core/tools/web-search/index.ts +17 -15
  109. package/src/core/tools/web-search/providers/anthropic.ts +2 -2
  110. package/src/core/tools/web-search/providers/exa.ts +3 -5
  111. package/src/core/tools/web-search/providers/perplexity.ts +1 -1
  112. package/src/core/tools/web-search/render.ts +3 -3
  113. package/src/core/tools/write.ts +4 -4
  114. package/src/index.ts +29 -18
  115. package/src/main.ts +50 -33
  116. package/src/migrations.ts +3 -3
  117. package/src/modes/index.ts +5 -5
  118. package/src/modes/interactive/components/armin.ts +1 -1
  119. package/src/modes/interactive/components/assistant-message.ts +1 -1
  120. package/src/modes/interactive/components/bash-execution.ts +4 -4
  121. package/src/modes/interactive/components/bordered-loader.ts +2 -2
  122. package/src/modes/interactive/components/branch-summary-message.ts +2 -2
  123. package/src/modes/interactive/components/compaction-summary-message.ts +2 -2
  124. package/src/modes/interactive/components/diff.ts +1 -1
  125. package/src/modes/interactive/components/dynamic-border.ts +1 -1
  126. package/src/modes/interactive/components/footer.ts +5 -5
  127. package/src/modes/interactive/components/hook-editor.ts +2 -2
  128. package/src/modes/interactive/components/hook-input.ts +2 -2
  129. package/src/modes/interactive/components/hook-message.ts +3 -3
  130. package/src/modes/interactive/components/hook-selector.ts +2 -2
  131. package/src/modes/interactive/components/model-selector.ts +281 -59
  132. package/src/modes/interactive/components/oauth-selector.ts +3 -3
  133. package/src/modes/interactive/components/plugin-settings.ts +4 -4
  134. package/src/modes/interactive/components/queue-mode-selector.ts +2 -2
  135. package/src/modes/interactive/components/session-selector.ts +4 -4
  136. package/src/modes/interactive/components/settings-defs.ts +1 -1
  137. package/src/modes/interactive/components/settings-selector.ts +5 -5
  138. package/src/modes/interactive/components/show-images-selector.ts +2 -2
  139. package/src/modes/interactive/components/theme-selector.ts +2 -2
  140. package/src/modes/interactive/components/thinking-selector.ts +2 -2
  141. package/src/modes/interactive/components/tool-execution.ts +26 -8
  142. package/src/modes/interactive/components/tree-selector.ts +3 -3
  143. package/src/modes/interactive/components/user-message-selector.ts +2 -2
  144. package/src/modes/interactive/components/user-message.ts +1 -1
  145. package/src/modes/interactive/components/welcome.ts +2 -2
  146. package/src/modes/interactive/interactive-mode.ts +86 -42
  147. package/src/modes/interactive/theme/theme.ts +15 -17
  148. package/src/modes/print-mode.ts +4 -3
  149. package/src/modes/rpc/rpc-client.ts +4 -4
  150. package/src/modes/rpc/rpc-mode.ts +22 -12
  151. package/src/modes/rpc/rpc-types.ts +3 -3
  152. package/src/utils/changelog.ts +2 -2
  153. package/src/utils/clipboard.ts +1 -1
  154. package/src/utils/shell-snapshot.ts +218 -0
  155. package/src/utils/shell.ts +93 -13
  156. package/src/utils/tools-manager.ts +1 -1
  157. package/examples/custom-tools/subagent/agents/reviewer.md +0 -35
  158. package/src/core/tools/exa/logger.ts +0 -56
@@ -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
 
@@ -6,8 +6,8 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
6
6
  import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@oh-my-pi/pi-ai";
7
7
  import chalk from "chalk";
8
8
  import { minimatch } from "minimatch";
9
- import { isValidThinkingLevel } from "../cli/args.js";
10
- import type { ModelRegistry } from "./model-registry.js";
9
+ import { isValidThinkingLevel } from "../cli/args";
10
+ import type { ModelRegistry } from "./model-registry";
11
11
 
12
12
  /** Default model IDs for each known provider */
13
13
  export const defaultModelPerProvider: Record<KnownProvider, string> = {
@@ -1,4 +1,4 @@
1
- import type { DoctorCheck } from "./types.js";
1
+ import type { DoctorCheck } from "./types";
2
2
 
3
3
  export async function runDoctorChecks(): Promise<DoctorCheck[]> {
4
4
  const checks: DoctorCheck[] = [];
@@ -1,5 +1,5 @@
1
1
  // Plugin system exports
2
- export { formatDoctorResults, runDoctorChecks } from "./doctor.js";
2
+ export { formatDoctorResults, runDoctorChecks } from "./doctor";
3
3
  export {
4
4
  getAllPluginCommandPaths,
5
5
  getAllPluginHookPaths,
@@ -9,16 +9,16 @@ export {
9
9
  resolvePluginCommandPaths,
10
10
  resolvePluginHookPaths,
11
11
  resolvePluginToolPaths,
12
- } from "./loader.js";
13
- export { PluginManager, parseSettingValue, validateSetting } from "./manager.js";
14
- export { extractPackageName, formatPluginSpec, parsePluginSpec } from "./parser.js";
12
+ } from "./loader";
13
+ export { PluginManager, parseSettingValue, validateSetting } from "./manager";
14
+ export { extractPackageName, formatPluginSpec, parsePluginSpec } from "./parser";
15
15
  export {
16
16
  getPluginsDir,
17
17
  getPluginsLockfile,
18
18
  getPluginsNodeModules,
19
19
  getPluginsPackageJson,
20
20
  getProjectPluginOverrides,
21
- } from "./paths.js";
21
+ } from "./paths";
22
22
  export type {
23
23
  BooleanSetting,
24
24
  DoctorCheck,
@@ -35,4 +35,4 @@ export type {
35
35
  PluginSettingType,
36
36
  ProjectPluginOverrides,
37
37
  StringSetting,
38
- } from "./types.js";
38
+ } from "./types";
@@ -1,7 +1,7 @@
1
- import { mkdir } from "fs/promises";
2
- import { join, resolve } from "path";
3
- import { getAgentDir } from "../../config.js";
4
- import type { InstalledPlugin } from "./types.js";
1
+ import { mkdir } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { getAgentDir } from "../../config";
4
+ import type { InstalledPlugin } from "./types";
5
5
 
6
6
  const PLUGINS_DIR = join(getAgentDir(), "plugins");
7
7
 
@@ -5,15 +5,10 @@
5
5
  * based on manifest entries and enabled features.
6
6
  */
7
7
 
8
- import { existsSync, readFileSync } from "fs";
9
- import { join } from "path";
10
- import {
11
- getPluginsLockfile,
12
- getPluginsNodeModules,
13
- getPluginsPackageJson,
14
- getProjectPluginOverrides,
15
- } from "./paths.js";
16
- import type { InstalledPlugin, PluginManifest, PluginRuntimeConfig, ProjectPluginOverrides } from "./types.js";
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { getPluginsLockfile, getPluginsNodeModules, getPluginsPackageJson, getProjectPluginOverrides } from "./paths";
11
+ import type { InstalledPlugin, PluginManifest, PluginRuntimeConfig, ProjectPluginOverrides } from "./types";
17
12
 
18
13
  // =============================================================================
19
14
  // Runtime Config Loading
@@ -1,13 +1,13 @@
1
- import { existsSync, lstatSync, mkdirSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from "fs";
2
- import { join, resolve } from "path";
3
- import { extractPackageName, parsePluginSpec } from "./parser.js";
1
+ import { existsSync, lstatSync, mkdirSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { extractPackageName, parsePluginSpec } from "./parser";
4
4
  import {
5
5
  getPluginsDir,
6
6
  getPluginsLockfile,
7
7
  getPluginsNodeModules,
8
8
  getPluginsPackageJson,
9
9
  getProjectPluginOverrides,
10
- } from "./paths.js";
10
+ } from "./paths";
11
11
  import type {
12
12
  DoctorCheck,
13
13
  DoctorOptions,
@@ -17,7 +17,7 @@ import type {
17
17
  PluginRuntimeConfig,
18
18
  PluginSettingSchema,
19
19
  ProjectPluginOverrides,
20
- } from "./types.js";
20
+ } from "./types";
21
21
 
22
22
  // =============================================================================
23
23
  // Validation
@@ -1,6 +1,6 @@
1
- import { homedir } from "os";
2
- import { join } from "path";
3
- import { CONFIG_DIR_NAME } from "../../config.js";
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { CONFIG_DIR_NAME } from "../../config";
4
4
 
5
5
  // =============================================================================
6
6
  // Plugin Directory Paths
package/src/core/sdk.ts CHANGED
@@ -29,34 +29,38 @@
29
29
  * ```
30
30
  */
31
31
 
32
+ import { join } from "node:path";
32
33
  import { Agent, type ThinkingLevel } from "@oh-my-pi/pi-agent-core";
33
34
  import type { Model } from "@oh-my-pi/pi-ai";
34
- import { join } from "path";
35
- import { getAgentDir } from "../config.js";
36
- import { AgentSession } from "./agent-session.js";
37
- import { AuthStorage } from "./auth-storage.js";
35
+ import { getAgentDir } from "../config";
36
+ import { AgentSession } from "./agent-session";
37
+ import { AuthStorage } from "./auth-storage";
38
+ import {
39
+ type CustomCommandsLoadResult,
40
+ loadCustomCommands as loadCustomCommandsInternal,
41
+ } from "./custom-commands/index";
38
42
  import {
39
43
  type CustomToolsLoadResult,
40
44
  discoverAndLoadCustomTools,
41
45
  type LoadedCustomTool,
42
46
  wrapCustomTools,
43
- } from "./custom-tools/index.js";
44
- import type { CustomTool } from "./custom-tools/types.js";
45
- import { discoverAndLoadHooks, HookRunner, type LoadedHook, wrapToolsWithHooks } from "./hooks/index.js";
46
- import type { HookFactory } from "./hooks/types.js";
47
- import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index.js";
48
- import { convertToLlm } from "./messages.js";
49
- import { ModelRegistry } from "./model-registry.js";
50
- import { SessionManager } from "./session-manager.js";
51
- import { type Settings, SettingsManager, type SkillsSettings } from "./settings-manager.js";
52
- import { loadSkills as loadSkillsInternal, type Skill } from "./skills.js";
53
- import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./slash-commands.js";
47
+ } from "./custom-tools/index";
48
+ import type { CustomTool } from "./custom-tools/types";
49
+ import { discoverAndLoadHooks, HookRunner, type LoadedHook, wrapToolsWithHooks } from "./hooks/index";
50
+ import type { HookFactory } from "./hooks/types";
51
+ import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp/index";
52
+ import { convertToLlm } from "./messages";
53
+ import { ModelRegistry } from "./model-registry";
54
+ import { SessionManager } from "./session-manager";
55
+ import { type CommandsSettings, type Settings, SettingsManager, type SkillsSettings } from "./settings-manager";
56
+ import { loadSkills as loadSkillsInternal, type Skill } from "./skills";
57
+ import { type FileSlashCommand, loadSlashCommands as loadSlashCommandsInternal } from "./slash-commands";
54
58
  import {
55
59
  buildSystemPrompt as buildSystemPromptInternal,
56
60
  loadProjectContextFiles as loadContextFilesInternal,
57
- } from "./system-prompt.js";
58
- import { time } from "./timings.js";
59
- import { createToolContextStore } from "./tools/context.js";
61
+ } from "./system-prompt";
62
+ import { time } from "./timings";
63
+ import { createToolContextStore } from "./tools/context";
60
64
  import {
61
65
  allTools,
62
66
  applyBashInterception,
@@ -80,7 +84,7 @@ import {
80
84
  type Tool,
81
85
  warmupLspServers,
82
86
  writeTool,
83
- } from "./tools/index.js";
87
+ } from "./tools/index";
84
88
 
85
89
  // Types
86
90
 
@@ -127,6 +131,9 @@ export interface CreateAgentSessionOptions {
127
131
  /** Enable MCP server discovery from .mcp.json files. Default: true */
128
132
  enableMCP?: boolean;
129
133
 
134
+ /** Tool names explicitly requested (enables disabled-by-default tools) */
135
+ explicitTools?: string[];
136
+
130
137
  /** Session manager. Default: SessionManager.create(cwd) */
131
138
  sessionManager?: SessionManager;
132
139
 
@@ -153,13 +160,14 @@ export interface CreateAgentSessionResult {
153
160
 
154
161
  // Re-exports
155
162
 
156
- export type { CustomTool } from "./custom-tools/types.js";
157
- export type { HookAPI, HookCommandContext, HookContext, HookFactory } from "./hooks/types.js";
158
- export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp/index.js";
159
- export type { Settings, SkillsSettings } from "./settings-manager.js";
160
- export type { Skill } from "./skills.js";
161
- export type { FileSlashCommand } from "./slash-commands.js";
162
- export type { Tool } from "./tools/index.js";
163
+ export type { CustomCommand, CustomCommandFactory } from "./custom-commands/types";
164
+ export type { CustomTool } from "./custom-tools/types";
165
+ export type { HookAPI, HookCommandContext, HookContext, HookFactory } from "./hooks/types";
166
+ export type { MCPManager, MCPServerConfig, MCPServerConnection, MCPToolsLoadResult } from "./mcp/index";
167
+ export type { Settings, SkillsSettings } from "./settings-manager";
168
+ export type { Skill } from "./skills";
169
+ export type { FileSlashCommand } from "./slash-commands";
170
+ export type { Tool } from "./tools/index";
163
171
 
164
172
  export {
165
173
  // Pre-built tools (use process.cwd())
@@ -278,10 +286,29 @@ export function discoverContextFiles(cwd?: string, agentDir?: string): Array<{ p
278
286
  /**
279
287
  * Discover slash commands from cwd and agentDir.
280
288
  */
281
- export function discoverSlashCommands(cwd?: string, agentDir?: string): FileSlashCommand[] {
289
+ export function discoverSlashCommands(
290
+ cwd?: string,
291
+ agentDir?: string,
292
+ settings?: CommandsSettings,
293
+ ): FileSlashCommand[] {
282
294
  return loadSlashCommandsInternal({
283
295
  cwd: cwd ?? process.cwd(),
284
296
  agentDir: agentDir ?? getDefaultAgentDir(),
297
+ enableClaudeUser: settings?.enableClaudeUser,
298
+ enableClaudeProject: settings?.enableClaudeProject,
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Discover custom commands (TypeScript slash commands) from cwd and agentDir.
304
+ */
305
+ export async function discoverCustomTSCommands(cwd?: string, agentDir?: string): Promise<CustomCommandsLoadResult> {
306
+ const resolvedCwd = cwd ?? process.cwd();
307
+ const resolvedAgentDir = agentDir ?? getDefaultAgentDir();
308
+
309
+ return loadCustomCommandsInternal({
310
+ cwd: resolvedCwd,
311
+ agentDir: resolvedAgentDir,
285
312
  });
286
313
  }
287
314
 
@@ -555,12 +582,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
555
582
  const contextFiles = options.contextFiles ?? discoverContextFiles(cwd, agentDir);
556
583
  time("discoverContextFiles");
557
584
 
558
- // Hook runner - created early for hooks
559
- let hookRunner: HookRunner | undefined;
585
+ // Hook runner - always created (needed for custom command context even without hooks)
586
+ let loadedHooks: LoadedHook[] = [];
560
587
  if (options.hooks !== undefined) {
561
588
  if (options.hooks.length > 0) {
562
- const loadedHooks = createLoadedHooksFromDefinitions(options.hooks);
563
- hookRunner = new HookRunner(loadedHooks, cwd, sessionManager, modelRegistry);
589
+ loadedHooks = createLoadedHooksFromDefinitions(options.hooks);
564
590
  }
565
591
  } else {
566
592
  // Discover hooks, merging with additional paths
@@ -570,10 +596,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
570
596
  for (const { path, error } of errors) {
571
597
  console.error(`Failed to load hook "${path}": ${error}`);
572
598
  }
573
- if (hooks.length > 0) {
574
- hookRunner = new HookRunner(hooks, cwd, sessionManager, modelRegistry);
575
- }
599
+ loadedHooks = hooks;
576
600
  }
601
+ const hookRunner = new HookRunner(loadedHooks, cwd, sessionManager, modelRegistry);
577
602
 
578
603
  const sessionContext = {
579
604
  getSessionFile: () => sessionManager.getSessionFile() ?? null,
@@ -695,6 +720,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
695
720
  };
696
721
 
697
722
  let allToolsArray: Tool[] = [...builtInTools, ...wrappedCustomTools];
723
+
724
+ // Filter out hidden tools unless explicitly requested
725
+ if (options.explicitTools) {
726
+ const explicitSet = new Set(options.explicitTools);
727
+ allToolsArray = allToolsArray.filter((tool) => !tool.hidden || explicitSet.has(tool.name));
728
+ } else {
729
+ allToolsArray = allToolsArray.filter((tool) => !tool.hidden);
730
+ }
698
731
  time("combineTools");
699
732
 
700
733
  // Apply bash interception to redirect common shell patterns to proper tools (if enabled)
@@ -730,9 +763,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
730
763
  systemPrompt = options.systemPrompt(defaultPrompt);
731
764
  }
732
765
 
733
- const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir);
766
+ const commandsSettings = settingsManager.getCommandsSettings();
767
+ const slashCommands = options.slashCommands ?? discoverSlashCommands(cwd, agentDir, commandsSettings);
734
768
  time("discoverSlashCommands");
735
769
 
770
+ // Discover custom commands (TypeScript slash commands)
771
+ const customCommandsResult = await loadCustomCommandsInternal({ cwd, agentDir });
772
+ time("discoverCustomCommands");
773
+ for (const { path, error } of customCommandsResult.errors) {
774
+ console.error(`Failed to load custom command "${path}": ${error}`);
775
+ }
776
+
736
777
  agent = new Agent({
737
778
  initialState: {
738
779
  systemPrompt,
@@ -782,6 +823,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
782
823
  fileCommands: slashCommands,
783
824
  hookRunner,
784
825
  customTools: customToolsResult.tools,
826
+ customCommands: customCommandsResult.commands,
785
827
  skillsSettings: settingsManager.getSkillsSettings(),
786
828
  modelRegistry,
787
829
  });