@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.
- package/CHANGELOG.md +44 -0
- package/README.md +80 -79
- package/docs/compaction.md +182 -149
- package/docs/config-usage.md +141 -78
- package/docs/custom-tools.md +45 -16
- package/docs/extension-loading.md +56 -954
- package/docs/extensions.md +192 -51
- package/docs/hooks.md +109 -70
- package/docs/python-repl.md +52 -19
- package/docs/rpc.md +43 -19
- package/docs/sdk.md +270 -211
- package/docs/session-tree-plan.md +60 -417
- package/docs/session.md +104 -39
- package/docs/skills.md +59 -95
- package/docs/theme.md +139 -110
- package/docs/tree.md +42 -33
- package/docs/tui.md +226 -80
- package/package.json +8 -9
- package/src/capability/index.ts +3 -4
- package/src/cli/args.ts +4 -4
- package/src/cli/grep-cli.ts +1 -1
- package/src/commit/agentic/index.ts +4 -3
- package/src/commit/git/index.ts +2 -3
- package/src/commit/map-reduce/index.ts +2 -1
- package/src/config/prompt-templates.ts +2 -0
- package/src/config/settings-schema.ts +30 -7
- package/src/config/settings.ts +0 -14
- package/src/config.ts +2 -2
- package/src/discovery/agents.ts +36 -0
- package/src/discovery/index.ts +1 -0
- package/src/exa/mcp-client.ts +3 -3
- package/src/ipy/executor.ts +5 -7
- package/src/ipy/gateway-coordinator.ts +1 -1
- package/src/ipy/kernel.ts +20 -15
- package/src/ipy/prelude.py +1 -1
- package/src/ipy/runtime.ts +7 -6
- package/src/lsp/lspmux.ts +3 -3
- package/src/main.ts +6 -8
- package/src/mcp/tool-bridge.ts +19 -9
- package/src/modes/components/assistant-message.ts +2 -2
- package/src/modes/components/hook-editor.ts +4 -4
- package/src/modes/components/settings-defs.ts +37 -2
- package/src/modes/components/tool-execution.ts +7 -7
- package/src/modes/controllers/command-controller.ts +2 -2
- package/src/modes/controllers/event-controller.ts +4 -7
- package/src/modes/controllers/input-controller.ts +4 -4
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/interactive-mode.ts +3 -5
- package/src/modes/rpc/rpc-mode.ts +8 -9
- package/src/patch/index.ts +6 -6
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/frontmatter.md +5 -5
- package/src/prompts/agents/plan.md +3 -2
- package/src/prompts/agents/reviewer.md +1 -1
- package/src/prompts/system/system-prompt.md +1 -3
- package/src/sdk.ts +13 -9
- package/src/session/agent-session.ts +6 -4
- package/src/session/compaction/compaction.ts +3 -3
- package/src/session/session-manager.ts +8 -9
- package/src/ssh/connection-manager.ts +4 -4
- package/src/system-prompt.ts +2 -6
- package/src/task/agents.ts +1 -1
- package/src/task/executor.ts +31 -8
- package/src/task/index.ts +14 -35
- package/src/task/omp-command.ts +3 -1
- package/src/task/output-manager.ts +20 -6
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +16 -2
- package/src/task/types.ts +13 -20
- package/src/task/worktree.ts +3 -3
- package/src/tools/ask.ts +3 -8
- package/src/tools/fetch.ts +2 -2
- package/src/tools/gemini-image.ts +5 -6
- package/src/tools/grep.ts +5 -5
- package/src/tools/index.ts +12 -5
- package/src/tools/read.ts +1 -1
- package/src/tools/todo-write.ts +2 -3
- package/src/utils/frontmatter.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/timings.ts +3 -2
- package/src/web/scrapers/github.ts +2 -2
- package/src/web/scrapers/utils.ts +2 -3
- package/src/web/scrapers/youtube.ts +2 -3
- package/src/web/search/auth.ts +5 -6
- package/src/web/search/providers/anthropic.ts +3 -2
- package/src/utils/terminal-notify.ts +0 -37
package/src/tools/todo-write.ts
CHANGED
|
@@ -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 :
|
|
74
|
+
id: item.id && item.id.trim().length > 0 ? item.id : Snowflake.next(),
|
|
76
75
|
content,
|
|
77
76
|
status: normalizeTodoStatus(item.status),
|
|
78
77
|
};
|
package/src/utils/frontmatter.ts
CHANGED
|
@@ -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
|
|
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:
|
package/src/utils/timings.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Central timing instrumentation for startup profiling.
|
|
3
|
-
* Enable with
|
|
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 =
|
|
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 =
|
|
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-${
|
|
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}-${
|
|
199
|
+
const tmpBase = path.join(tmpDir, `yt-${yt.videoId}-${Snowflake.next()}`);
|
|
201
200
|
|
|
202
201
|
try {
|
|
203
202
|
// Try manual subtitles first (English preferred)
|
package/src/web/search/auth.ts
CHANGED
|
@@ -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,
|
|
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 =
|
|
125
|
-
const searchBaseUrl =
|
|
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 =
|
|
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,
|
|
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
|
|
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
|
-
}
|