@oh-my-pi/pi-coding-agent 2.3.1337 → 3.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 (117) hide show
  1. package/CHANGELOG.md +72 -34
  2. package/README.md +100 -100
  3. package/docs/compaction.md +8 -8
  4. package/docs/config-usage.md +113 -0
  5. package/docs/custom-tools.md +8 -8
  6. package/docs/extension-loading.md +58 -58
  7. package/docs/hooks.md +11 -11
  8. package/docs/rpc.md +4 -4
  9. package/docs/sdk.md +14 -14
  10. package/docs/session-tree-plan.md +1 -1
  11. package/docs/session.md +2 -2
  12. package/docs/skills.md +16 -16
  13. package/docs/theme.md +9 -9
  14. package/docs/tui.md +1 -1
  15. package/examples/README.md +1 -1
  16. package/examples/custom-tools/README.md +4 -4
  17. package/examples/custom-tools/subagent/README.md +13 -13
  18. package/examples/custom-tools/subagent/agents.ts +2 -2
  19. package/examples/custom-tools/subagent/index.ts +5 -5
  20. package/examples/hooks/README.md +3 -3
  21. package/examples/hooks/auto-commit-on-exit.ts +1 -1
  22. package/examples/hooks/custom-compaction.ts +1 -1
  23. package/examples/sdk/01-minimal.ts +1 -1
  24. package/examples/sdk/04-skills.ts +1 -1
  25. package/examples/sdk/05-tools.ts +1 -1
  26. package/examples/sdk/08-slash-commands.ts +1 -1
  27. package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
  28. package/examples/sdk/README.md +2 -2
  29. package/package.json +13 -11
  30. package/src/capability/context-file.ts +40 -0
  31. package/src/capability/extension.ts +48 -0
  32. package/src/capability/hook.ts +40 -0
  33. package/src/capability/index.ts +616 -0
  34. package/src/capability/instruction.ts +37 -0
  35. package/src/capability/mcp.ts +52 -0
  36. package/src/capability/prompt.ts +35 -0
  37. package/src/capability/rule.ts +52 -0
  38. package/src/capability/settings.ts +35 -0
  39. package/src/capability/skill.ts +49 -0
  40. package/src/capability/slash-command.ts +40 -0
  41. package/src/capability/system-prompt.ts +35 -0
  42. package/src/capability/tool.ts +38 -0
  43. package/src/capability/types.ts +166 -0
  44. package/src/cli/args.ts +2 -2
  45. package/src/cli/plugin-cli.ts +24 -19
  46. package/src/cli/update-cli.ts +10 -10
  47. package/src/config.ts +290 -6
  48. package/src/core/auth-storage.ts +32 -9
  49. package/src/core/bash-executor.ts +1 -1
  50. package/src/core/custom-commands/loader.ts +44 -50
  51. package/src/core/custom-tools/index.ts +1 -0
  52. package/src/core/custom-tools/loader.ts +67 -69
  53. package/src/core/custom-tools/types.ts +10 -1
  54. package/src/core/hooks/loader.ts +13 -42
  55. package/src/core/index.ts +0 -1
  56. package/src/core/logger.ts +7 -7
  57. package/src/core/mcp/client.ts +1 -1
  58. package/src/core/mcp/config.ts +94 -146
  59. package/src/core/mcp/index.ts +0 -4
  60. package/src/core/mcp/loader.ts +26 -22
  61. package/src/core/mcp/manager.ts +18 -23
  62. package/src/core/mcp/tool-bridge.ts +9 -1
  63. package/src/core/mcp/types.ts +2 -0
  64. package/src/core/model-registry.ts +25 -8
  65. package/src/core/plugins/installer.ts +1 -1
  66. package/src/core/plugins/loader.ts +17 -11
  67. package/src/core/plugins/manager.ts +2 -2
  68. package/src/core/plugins/paths.ts +12 -7
  69. package/src/core/plugins/types.ts +3 -3
  70. package/src/core/sdk.ts +48 -27
  71. package/src/core/session-manager.ts +4 -4
  72. package/src/core/settings-manager.ts +45 -21
  73. package/src/core/skills.ts +208 -293
  74. package/src/core/slash-commands.ts +34 -165
  75. package/src/core/system-prompt.ts +58 -65
  76. package/src/core/timings.ts +2 -2
  77. package/src/core/tools/lsp/config.ts +38 -17
  78. package/src/core/tools/task/agents.ts +21 -0
  79. package/src/core/tools/task/artifacts.ts +1 -1
  80. package/src/core/tools/task/bundled-agents/reviewer.md +2 -1
  81. package/src/core/tools/task/bundled-agents/task.md +1 -0
  82. package/src/core/tools/task/commands.ts +30 -107
  83. package/src/core/tools/task/discovery.ts +75 -66
  84. package/src/core/tools/task/executor.ts +25 -10
  85. package/src/core/tools/task/index.ts +35 -10
  86. package/src/core/tools/task/model-resolver.ts +27 -25
  87. package/src/core/tools/task/types.ts +6 -2
  88. package/src/core/tools/web-fetch.ts +3 -3
  89. package/src/core/tools/web-search/auth.ts +40 -34
  90. package/src/core/tools/web-search/index.ts +1 -1
  91. package/src/core/tools/web-search/providers/anthropic.ts +1 -1
  92. package/src/discovery/agents-md.ts +75 -0
  93. package/src/discovery/builtin.ts +646 -0
  94. package/src/discovery/claude.ts +623 -0
  95. package/src/discovery/cline.ts +102 -0
  96. package/src/discovery/codex.ts +571 -0
  97. package/src/discovery/cursor.ts +264 -0
  98. package/src/discovery/gemini.ts +368 -0
  99. package/src/discovery/github.ts +120 -0
  100. package/src/discovery/helpers.test.ts +127 -0
  101. package/src/discovery/helpers.ts +249 -0
  102. package/src/discovery/index.ts +84 -0
  103. package/src/discovery/mcp-json.ts +127 -0
  104. package/src/discovery/vscode.ts +99 -0
  105. package/src/discovery/windsurf.ts +216 -0
  106. package/src/main.ts +14 -13
  107. package/src/migrations.ts +24 -3
  108. package/src/modes/interactive/components/hook-editor.ts +1 -1
  109. package/src/modes/interactive/components/plugin-settings.ts +1 -1
  110. package/src/modes/interactive/components/settings-defs.ts +38 -2
  111. package/src/modes/interactive/components/settings-selector.ts +1 -0
  112. package/src/modes/interactive/components/welcome.ts +2 -2
  113. package/src/modes/interactive/interactive-mode.ts +233 -16
  114. package/src/modes/interactive/theme/theme-schema.json +1 -1
  115. package/src/utils/clipboard.ts +1 -1
  116. package/src/utils/shell-snapshot.ts +2 -2
  117. package/src/utils/shell.ts +7 -7
@@ -309,7 +309,7 @@ function convertWithMarkitdown(
309
309
 
310
310
  // Write to temp file with extension hint
311
311
  const ext = extensionHint || ".bin";
312
- const tmpFile = path.join(os.tmpdir(), `pi-convert-${Date.now()}${ext}`);
312
+ const tmpFile = path.join(os.tmpdir(), `omp-convert-${Date.now()}${ext}`);
313
313
 
314
314
  try {
315
315
  fs.writeFileSync(tmpFile, content);
@@ -531,7 +531,7 @@ function parseFeedToMarkdown(content: string, maxItems = 10): string {
531
531
  * Render HTML to text using lynx
532
532
  */
533
533
  function renderWithLynx(html: string, timeout: number): { content: string; ok: boolean } {
534
- const tmpFile = path.join(os.tmpdir(), `pi-render-${Date.now()}.html`);
534
+ const tmpFile = path.join(os.tmpdir(), `omp-render-${Date.now()}.html`);
535
535
  try {
536
536
  fs.writeFileSync(tmpFile, html);
537
537
  // Convert path to file URL (handles Windows paths correctly)
@@ -712,7 +712,7 @@ async function fetchGitHubApi(endpoint: string, timeout: number): Promise<{ data
712
712
 
713
713
  const headers: Record<string, string> = {
714
714
  Accept: "application/vnd.github.v3+json",
715
- "User-Agent": "pi-web-fetch/1.0",
715
+ "User-Agent": "omp-web-fetch/1.0",
716
716
  };
717
717
 
718
718
  // Use GITHUB_TOKEN if available
@@ -3,13 +3,14 @@
3
3
  *
4
4
  * 4-tier auth resolution:
5
5
  * 1. ANTHROPIC_SEARCH_API_KEY / ANTHROPIC_SEARCH_BASE_URL env vars
6
- * 2. Provider with api="anthropic-messages" in ~/.pi/agent/models.json
7
- * 3. OAuth credentials in ~/.pi/agent/auth.json (with expiry check)
6
+ * 2. Provider with api="anthropic-messages" in ~/.omp/agent/models.json
7
+ * 3. OAuth credentials in ~/.omp/agent/auth.json (with expiry check)
8
8
  * 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
9
9
  */
10
10
 
11
11
  import * as os from "node:os";
12
12
  import * as path from "node:path";
13
+ import { getConfigDirPaths } from "../../../config";
13
14
  import type { AnthropicAuthConfig, AuthJson, ModelsJson } from "./types";
14
15
 
15
16
  const DEFAULT_BASE_URL = "https://api.anthropic.com";
@@ -83,7 +84,8 @@ export function isOAuthToken(apiKey: string): boolean {
83
84
  * 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
84
85
  */
85
86
  export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
86
- const piAgentDir = path.join(os.homedir(), ".pi", "agent");
87
+ // Get all config directories (user-level only) for fallback support
88
+ const configDirs = getConfigDirPaths("", { project: false });
87
89
 
88
90
  // 1. Explicit search-specific env vars
89
91
  const searchApiKey = await getEnv("ANTHROPIC_SEARCH_API_KEY");
@@ -96,41 +98,45 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
96
98
  };
97
99
  }
98
100
 
99
- // 2. Provider with api="anthropic-messages" in models.json
100
- const modelsJson = await readJson<ModelsJson>(path.join(piAgentDir, "models.json"));
101
- if (modelsJson?.providers) {
102
- // First pass: look for providers with actual API keys
103
- for (const [_name, provider] of Object.entries(modelsJson.providers)) {
104
- if (provider.api === "anthropic-messages" && provider.apiKey && provider.apiKey !== "none") {
105
- return {
106
- apiKey: provider.apiKey,
107
- baseUrl: provider.baseUrl ?? DEFAULT_BASE_URL,
108
- isOAuth: isOAuthToken(provider.apiKey),
109
- };
101
+ // 2. Provider with api="anthropic-messages" in models.json (check all config dirs)
102
+ for (const configDir of configDirs) {
103
+ const modelsJson = await readJson<ModelsJson>(path.join(configDir, "models.json"));
104
+ if (modelsJson?.providers) {
105
+ // First pass: look for providers with actual API keys
106
+ for (const [_name, provider] of Object.entries(modelsJson.providers)) {
107
+ if (provider.api === "anthropic-messages" && provider.apiKey && provider.apiKey !== "none") {
108
+ return {
109
+ apiKey: provider.apiKey,
110
+ baseUrl: provider.baseUrl ?? DEFAULT_BASE_URL,
111
+ isOAuth: isOAuthToken(provider.apiKey),
112
+ };
113
+ }
110
114
  }
111
- }
112
- // Second pass: check for proxy mode (baseUrl but apiKey="none")
113
- for (const [_name, provider] of Object.entries(modelsJson.providers)) {
114
- if (provider.api === "anthropic-messages" && provider.baseUrl) {
115
- return {
116
- apiKey: provider.apiKey ?? "",
117
- baseUrl: provider.baseUrl,
118
- isOAuth: false,
119
- };
115
+ // Second pass: check for proxy mode (baseUrl but apiKey="none")
116
+ for (const [_name, provider] of Object.entries(modelsJson.providers)) {
117
+ if (provider.api === "anthropic-messages" && provider.baseUrl) {
118
+ return {
119
+ apiKey: provider.apiKey ?? "",
120
+ baseUrl: provider.baseUrl,
121
+ isOAuth: false,
122
+ };
123
+ }
120
124
  }
121
125
  }
122
126
  }
123
127
 
124
- // 3. OAuth credentials in auth.json (with 5-minute expiry buffer)
125
- const authJson = await readJson<AuthJson>(path.join(piAgentDir, "auth.json"));
126
- if (authJson?.anthropic?.type === "oauth" && authJson.anthropic.access) {
127
- const expiryBuffer = 5 * 60 * 1000; // 5 minutes
128
- if (authJson.anthropic.expires > Date.now() + expiryBuffer) {
129
- return {
130
- apiKey: authJson.anthropic.access,
131
- baseUrl: DEFAULT_BASE_URL,
132
- isOAuth: true,
133
- };
128
+ // 3. OAuth credentials in auth.json (with 5-minute expiry buffer, check all config dirs)
129
+ for (const configDir of configDirs) {
130
+ const authJson = await readJson<AuthJson>(path.join(configDir, "auth.json"));
131
+ if (authJson?.anthropic?.type === "oauth" && authJson.anthropic.access) {
132
+ const expiryBuffer = 5 * 60 * 1000; // 5 minutes
133
+ if (authJson.anthropic.expires > Date.now() + expiryBuffer) {
134
+ return {
135
+ apiKey: authJson.anthropic.access,
136
+ baseUrl: DEFAULT_BASE_URL,
137
+ isOAuth: true,
138
+ };
139
+ }
134
140
  }
135
141
  }
136
142
 
@@ -163,7 +169,7 @@ export function buildAnthropicHeaders(auth: AnthropicAuthConfig): Record<string,
163
169
  "content-type": "application/json",
164
170
  "anthropic-dangerous-direct-browser-access": "true",
165
171
  "anthropic-beta": betas.join(","),
166
- "user-agent": "pi-coding-agent/1.0.0 (cli)",
172
+ "user-agent": "claude-code/2.0.20",
167
173
  "x-app": "cli",
168
174
  // Stainless SDK telemetry headers (required for OAuth)
169
175
  "x-stainless-arch": process.arch,
@@ -186,7 +186,7 @@ async function executeWebSearch(
186
186
  }
187
187
  }
188
188
 
189
- const WEB_SEARCH_DESCRIPTION = `Allows Pi to search the web and use the results to inform responses
189
+ const WEB_SEARCH_DESCRIPTION = `Allows OMP to search the web and use the results to inform responses
190
190
  - Provides up-to-date information for current events and recent data
191
191
  - Returns search result information formatted as search result blocks, including links as markdown hyperlinks
192
192
  - Use this tool for accessing information beyond Claude's knowledge cutoff
@@ -180,7 +180,7 @@ export async function searchAnthropic(params: AnthropicSearchParams): Promise<We
180
180
  const auth = await findAnthropicAuth();
181
181
  if (!auth) {
182
182
  throw new Error(
183
- "No Anthropic credentials found. Set ANTHROPIC_API_KEY or configure OAuth in ~/.pi/agent/auth.json",
183
+ "No Anthropic credentials found. Set ANTHROPIC_API_KEY or configure OAuth in ~/.omp/agent/auth.json",
184
184
  );
185
185
  }
186
186
 
@@ -0,0 +1,75 @@
1
+ /**
2
+ * AGENTS.md Provider
3
+ *
4
+ * Discovers standalone AGENTS.md files by walking up from cwd.
5
+ * This handles AGENTS.md files that live in project root (not in config directories
6
+ * like .codex/ or .gemini/, which are handled by their respective providers).
7
+ */
8
+
9
+ import { dirname, join, sep } from "node:path";
10
+ import { type ContextFile, contextFileCapability } from "../capability/context-file";
11
+ import { registerProvider } from "../capability/index";
12
+ import type { LoadContext, LoadResult } from "../capability/types";
13
+ import { calculateDepth, createSourceMeta } from "./helpers";
14
+
15
+ const PROVIDER_ID = "agents-md";
16
+ const DISPLAY_NAME = "AGENTS.md";
17
+ const MAX_DEPTH = 20; // Prevent walking up excessively far from cwd
18
+
19
+ /**
20
+ * Load standalone AGENTS.md files.
21
+ */
22
+ function loadAgentsMd(ctx: LoadContext): LoadResult<ContextFile> {
23
+ const items: ContextFile[] = [];
24
+ const warnings: string[] = [];
25
+
26
+ // Walk up from cwd looking for AGENTS.md files
27
+ let current = ctx.cwd;
28
+ let depth = 0;
29
+
30
+ while (depth < MAX_DEPTH) {
31
+ const candidate = join(current, "AGENTS.md");
32
+
33
+ if (ctx.fs.isFile(candidate)) {
34
+ // Skip if it's inside a config directory (handled by other providers)
35
+ const parent = dirname(candidate);
36
+ const baseName = parent.split(sep).pop() ?? "";
37
+
38
+ // Skip if inside .codex, .gemini, or other config dirs
39
+ if (!baseName.startsWith(".")) {
40
+ const content = ctx.fs.readFile(candidate);
41
+
42
+ if (content === null) {
43
+ warnings.push(`Failed to read: ${candidate}`);
44
+ } else {
45
+ const fileDir = dirname(candidate);
46
+ const calculatedDepth = calculateDepth(ctx.cwd, fileDir, sep);
47
+
48
+ items.push({
49
+ path: candidate,
50
+ content,
51
+ level: "project",
52
+ depth: calculatedDepth,
53
+ _source: createSourceMeta(PROVIDER_ID, candidate, "project"),
54
+ });
55
+ }
56
+ }
57
+ }
58
+
59
+ // Move to parent directory
60
+ const parent = dirname(current);
61
+ if (parent === current) break; // Reached filesystem root
62
+ current = parent;
63
+ depth++;
64
+ }
65
+
66
+ return { items, warnings };
67
+ }
68
+
69
+ registerProvider(contextFileCapability.id, {
70
+ id: PROVIDER_ID,
71
+ displayName: DISPLAY_NAME,
72
+ description: "Standalone AGENTS.md files (Codex/Gemini style)",
73
+ priority: 10,
74
+ load: loadAgentsMd,
75
+ });