@oh-my-pi/pi-coding-agent 10.6.1 → 11.0.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 (86) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +80 -79
  3. package/docs/compaction.md +182 -149
  4. package/docs/config-usage.md +141 -78
  5. package/docs/custom-tools.md +45 -16
  6. package/docs/extension-loading.md +56 -954
  7. package/docs/extensions.md +192 -51
  8. package/docs/hooks.md +109 -70
  9. package/docs/python-repl.md +52 -19
  10. package/docs/rpc.md +43 -19
  11. package/docs/sdk.md +270 -211
  12. package/docs/session-tree-plan.md +60 -417
  13. package/docs/session.md +104 -39
  14. package/docs/skills.md +59 -95
  15. package/docs/theme.md +139 -110
  16. package/docs/tree.md +42 -33
  17. package/docs/tui.md +226 -80
  18. package/package.json +8 -9
  19. package/src/capability/index.ts +3 -4
  20. package/src/cli/args.ts +4 -4
  21. package/src/cli/grep-cli.ts +1 -1
  22. package/src/commit/agentic/index.ts +4 -3
  23. package/src/commit/git/index.ts +2 -3
  24. package/src/commit/map-reduce/index.ts +2 -1
  25. package/src/config/prompt-templates.ts +2 -0
  26. package/src/config/settings-schema.ts +30 -7
  27. package/src/config/settings.ts +0 -14
  28. package/src/config.ts +2 -2
  29. package/src/discovery/agents.ts +36 -0
  30. package/src/discovery/index.ts +1 -0
  31. package/src/exa/mcp-client.ts +3 -3
  32. package/src/ipy/executor.ts +5 -7
  33. package/src/ipy/gateway-coordinator.ts +1 -1
  34. package/src/ipy/kernel.ts +20 -15
  35. package/src/ipy/prelude.py +1 -1
  36. package/src/ipy/runtime.ts +7 -6
  37. package/src/lsp/lspmux.ts +3 -3
  38. package/src/main.ts +6 -8
  39. package/src/mcp/tool-bridge.ts +19 -9
  40. package/src/modes/components/assistant-message.ts +2 -2
  41. package/src/modes/components/hook-editor.ts +4 -4
  42. package/src/modes/components/settings-defs.ts +37 -2
  43. package/src/modes/components/tool-execution.ts +7 -7
  44. package/src/modes/controllers/command-controller.ts +2 -2
  45. package/src/modes/controllers/event-controller.ts +4 -7
  46. package/src/modes/controllers/input-controller.ts +4 -4
  47. package/src/modes/controllers/selector-controller.ts +1 -0
  48. package/src/modes/interactive-mode.ts +3 -5
  49. package/src/modes/rpc/rpc-mode.ts +8 -9
  50. package/src/patch/index.ts +6 -6
  51. package/src/prompts/agents/explore.md +2 -2
  52. package/src/prompts/agents/frontmatter.md +5 -5
  53. package/src/prompts/agents/plan.md +3 -2
  54. package/src/prompts/agents/reviewer.md +1 -1
  55. package/src/prompts/system/system-prompt.md +1 -3
  56. package/src/sdk.ts +13 -9
  57. package/src/session/agent-session.ts +6 -4
  58. package/src/session/compaction/compaction.ts +3 -3
  59. package/src/session/session-manager.ts +8 -9
  60. package/src/ssh/connection-manager.ts +4 -4
  61. package/src/system-prompt.ts +2 -6
  62. package/src/task/agents.ts +1 -1
  63. package/src/task/executor.ts +31 -8
  64. package/src/task/index.ts +14 -35
  65. package/src/task/omp-command.ts +3 -1
  66. package/src/task/output-manager.ts +20 -6
  67. package/src/task/parallel.ts +3 -3
  68. package/src/task/render.ts +16 -2
  69. package/src/task/types.ts +13 -20
  70. package/src/task/worktree.ts +3 -3
  71. package/src/tools/ask.ts +3 -8
  72. package/src/tools/fetch.ts +2 -2
  73. package/src/tools/gemini-image.ts +5 -6
  74. package/src/tools/grep.ts +5 -5
  75. package/src/tools/index.ts +12 -5
  76. package/src/tools/read.ts +1 -1
  77. package/src/tools/todo-write.ts +2 -3
  78. package/src/utils/frontmatter.ts +1 -1
  79. package/src/utils/image-resize.ts +1 -1
  80. package/src/utils/timings.ts +3 -2
  81. package/src/web/scrapers/github.ts +2 -2
  82. package/src/web/scrapers/utils.ts +2 -3
  83. package/src/web/scrapers/youtube.ts +2 -3
  84. package/src/web/search/auth.ts +5 -6
  85. package/src/web/search/providers/anthropic.ts +3 -2
  86. package/src/utils/terminal-notify.ts +0 -37
@@ -1,10 +1,9 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import path from "node:path";
3
2
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
4
3
  import { StringEnum } from "@oh-my-pi/pi-ai";
5
4
  import type { Component } from "@oh-my-pi/pi-tui";
6
5
  import { Text } from "@oh-my-pi/pi-tui";
7
- import { logger } from "@oh-my-pi/pi-utils";
6
+ import { logger, Snowflake } from "@oh-my-pi/pi-utils";
8
7
  import { Type } from "@sinclair/typebox";
9
8
  import chalk from "chalk";
10
9
  import { renderPromptTemplate } from "../config/prompt-templates";
@@ -72,7 +71,7 @@ function normalizeTodos(items: Array<{ id?: string; content?: string; status?: s
72
71
  throw new Error("Todo content cannot be empty.");
73
72
  }
74
73
  return {
75
- id: item.id && item.id.trim().length > 0 ? item.id : randomUUID(),
74
+ id: item.id && item.id.trim().length > 0 ? item.id : Snowflake.next(),
76
75
  content,
77
76
  status: normalizeTodoStatus(item.status),
78
77
  };
@@ -18,7 +18,7 @@ export class FrontmatterError extends Error {
18
18
  error: Error,
19
19
  public readonly source?: unknown,
20
20
  ) {
21
- super(`Failed to parse YAML frontmatter: ${error.message}`, { cause: error });
21
+ super(`Failed to parse YAML frontmatter (${source}): ${error.message}`, { cause: error });
22
22
  this.name = "FrontmatterError";
23
23
  }
24
24
 
@@ -40,7 +40,7 @@ function pickSmaller(
40
40
  * Resize an image to fit within the specified max dimensions and file size.
41
41
  * Returns the original image if it already fits within the limits.
42
42
  *
43
- * Uses Photon (Rust/WASM) for image processing. If Photon is not available,
43
+ * Uses Photon for image processing. If Photon is not available,
44
44
  * returns the original image unchanged.
45
45
  *
46
46
  * Strategy for staying under maxBytes:
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Central timing instrumentation for startup profiling.
3
- * Enable with OMP_TIMING=1 or PI_TIMING=1 environment variable.
3
+ * Enable with PI_TIMING=1 or PI_TIMING=1 environment variable.
4
4
  */
5
+ import { $env } from "@oh-my-pi/pi-utils";
5
6
 
6
- const ENABLED = process.env.OMP_TIMING === "1";
7
+ const ENABLED = $env.PI_TIMING === "1";
7
8
  const timings: Array<{ label: string; ms: number }> = [];
8
9
  let lastTime = Date.now();
9
10
 
@@ -1,4 +1,4 @@
1
- import { ptree } from "@oh-my-pi/pi-utils";
1
+ import { $env, ptree } from "@oh-my-pi/pi-utils";
2
2
  import type { RenderResult, SpecialHandler } from "./types";
3
3
  import { finalizeOutput, loadPage } from "./types";
4
4
 
@@ -85,7 +85,7 @@ export async function fetchGitHubApi(
85
85
  };
86
86
 
87
87
  // Use GITHUB_TOKEN if available
88
- const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
88
+ const token = $env.GITHUB_TOKEN || $env.GH_TOKEN;
89
89
  if (token) {
90
90
  headers.Authorization = `Bearer ${token}`;
91
91
  }
@@ -1,8 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- import { ptree } from "@oh-my-pi/pi-utils";
5
- import { nanoid } from "nanoid";
4
+ import { ptree, Snowflake } from "@oh-my-pi/pi-utils";
6
5
  import { ensureTool } from "../../utils/tools-manager";
7
6
 
8
7
  const MAX_BYTES = 50 * 1024 * 1024; // 50MB for binary files
@@ -42,7 +41,7 @@ export async function convertWithMarkitdown(
42
41
  // Write to temp file with extension hint
43
42
  const ext = extensionHint || ".bin";
44
43
  const tmpDir = os.tmpdir();
45
- const tmpFile = path.join(tmpDir, `omp-convert-${nanoid()}${ext}`);
44
+ const tmpFile = path.join(tmpDir, `omp-convert-${Snowflake.next()}${ext}`);
46
45
 
47
46
  if (content.length > MAX_BYTES) {
48
47
  return { content: "", ok: false, error: `content exceeds ${MAX_BYTES} bytes` };
@@ -1,8 +1,7 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as os from "node:os";
3
3
  import path from "node:path";
4
- import { ptree } from "@oh-my-pi/pi-utils";
5
- import { nanoid } from "nanoid";
4
+ import { ptree, Snowflake } from "@oh-my-pi/pi-utils";
6
5
  import { throwIfAborted } from "../../tools/tool-errors";
7
6
  import { ensureTool } from "../../utils/tools-manager";
8
7
  import type { RenderResult, SpecialHandler } from "./types";
@@ -197,7 +196,7 @@ export const handleYouTube: SpecialHandler = async (
197
196
 
198
197
  // Create temp directory for subtitle download
199
198
  const tmpDir = os.tmpdir();
200
- const tmpBase = path.join(tmpDir, `yt-${yt.videoId}-${nanoid()}`);
199
+ const tmpBase = path.join(tmpDir, `yt-${yt.videoId}-${Snowflake.next()}`);
201
200
 
202
201
  try {
203
202
  // Try manual subtitles first (English preferred)
@@ -8,8 +8,8 @@
8
8
  * 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback
9
9
  */
10
10
  import * as path from "node:path";
11
- import { buildAnthropicHeaders as buildProviderAnthropicHeaders, getEnv, getEnvApiKey } from "@oh-my-pi/pi-ai";
12
- import { logger } from "@oh-my-pi/pi-utils";
11
+ import { buildAnthropicHeaders as buildProviderAnthropicHeaders, getEnvApiKey } from "@oh-my-pi/pi-ai";
12
+ import { $env, logger } from "@oh-my-pi/pi-utils";
13
13
  import { getAgentDbPath, getConfigDirPaths } from "../../config";
14
14
  import { AgentStorage } from "../../session/agent-storage";
15
15
  import type { AuthCredential, AuthCredentialEntry, AuthStorageData } from "../../session/auth-storage";
@@ -17,7 +17,6 @@ import { migrateJsonStorage } from "../../session/storage-migration";
17
17
  import type { AnthropicAuthConfig, AnthropicOAuthCredential, ModelsJson } from "./types";
18
18
 
19
19
  const DEFAULT_BASE_URL = "https://api.anthropic.com";
20
- export { getEnv };
21
20
 
22
21
  /**
23
22
  * Reads and parses a JSON file safely.
@@ -121,8 +120,8 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
121
120
  const configDirs = getConfigDirPaths("", { project: false });
122
121
 
123
122
  // 1. Explicit search-specific env vars
124
- const searchApiKey = getEnv("ANTHROPIC_SEARCH_API_KEY");
125
- const searchBaseUrl = getEnv("ANTHROPIC_SEARCH_BASE_URL");
123
+ const searchApiKey = $env.ANTHROPIC_SEARCH_API_KEY;
124
+ const searchBaseUrl = $env.ANTHROPIC_SEARCH_BASE_URL;
126
125
  if (searchApiKey) {
127
126
  return {
128
127
  apiKey: searchApiKey,
@@ -177,7 +176,7 @@ export async function findAnthropicAuth(): Promise<AnthropicAuthConfig | null> {
177
176
 
178
177
  // 4. Generic ANTHROPIC_API_KEY fallback
179
178
  const apiKey = getEnvApiKey("anthropic");
180
- const baseUrl = getEnv("ANTHROPIC_BASE_URL");
179
+ const baseUrl = $env.ANTHROPIC_BASE_URL;
181
180
  if (apiKey) {
182
181
  return {
183
182
  apiKey,
@@ -4,7 +4,8 @@
4
4
  * Uses Claude's built-in web_search_20250305 tool to search the web.
5
5
  * Returns synthesized answers with citations and source metadata.
6
6
  */
7
- import { applyClaudeToolPrefix, buildAnthropicSystemBlocks, getEnv, stripClaudeToolPrefix } from "@oh-my-pi/pi-ai";
7
+ import { applyClaudeToolPrefix, buildAnthropicSystemBlocks, stripClaudeToolPrefix } from "@oh-my-pi/pi-ai";
8
+ import { $env } from "@oh-my-pi/pi-utils";
8
9
  import { buildAnthropicHeaders, buildAnthropicUrl, findAnthropicAuth } from "../../../web/search/auth";
9
10
  import type {
10
11
  AnthropicApiResponse,
@@ -42,7 +43,7 @@ export interface AnthropicSearchParams {
42
43
  * @returns Model identifier string
43
44
  */
44
45
  function getModel(): string {
45
- return getEnv("ANTHROPIC_SEARCH_MODEL") ?? DEFAULT_MODEL;
46
+ return $env.ANTHROPIC_SEARCH_MODEL ?? DEFAULT_MODEL;
46
47
  }
47
48
 
48
49
  /**
@@ -1,37 +0,0 @@
1
- export type NotificationProtocol = "bell" | "osc99" | "osc9";
2
-
3
- export function detectNotificationProtocol(): NotificationProtocol {
4
- const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || "";
5
- const term = process.env.TERM?.toLowerCase() || "";
6
-
7
- if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
8
- return "osc99";
9
- }
10
-
11
- if (process.env.GHOSTTY_RESOURCES_DIR || termProgram === "ghostty" || term.includes("ghostty")) {
12
- return "osc9";
13
- }
14
-
15
- if (process.env.WEZTERM_PANE || termProgram === "wezterm") {
16
- return "osc9";
17
- }
18
-
19
- if (process.env.ITERM_SESSION_ID || termProgram === "iterm.app") {
20
- return "osc9";
21
- }
22
-
23
- return "bell";
24
- }
25
-
26
- export function sendNotification(protocol: NotificationProtocol, message: string): void {
27
- const payload =
28
- protocol === "osc99" ? `\x1b]99;;${message}\x1b\\` : protocol === "osc9" ? `\x1b]9;${message}\x1b\\` : "\x07";
29
-
30
- process.stdout.write(payload);
31
- }
32
-
33
- export function isNotificationSuppressed(): boolean {
34
- const value = process.env.OMP_NOTIFICATIONS?.trim().toLowerCase();
35
- if (!value) return false;
36
- return value === "off" || value === "0" || value === "false";
37
- }