@oh-my-pi/pi-coding-agent 14.6.6 → 14.7.1
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 +50 -0
- package/examples/hooks/handoff.ts +1 -1
- package/examples/hooks/qna.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +7 -4
- package/examples/sdk/README.md +1 -1
- package/package.json +12 -12
- package/src/autoresearch/index.ts +48 -44
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/read-cli.ts +58 -0
- package/src/cli.ts +1 -0
- package/src/commands/read.ts +40 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/analysis/conventional.ts +1 -1
- package/src/commit/analysis/summary.ts +1 -1
- package/src/commit/changelog/generate.ts +1 -1
- package/src/commit/map-reduce/map-phase.ts +1 -1
- package/src/commit/map-reduce/reduce-phase.ts +1 -1
- package/src/config/settings-schema.ts +49 -0
- package/src/config/settings.ts +71 -1
- package/src/dap/client.ts +1 -0
- package/src/discovery/builtin.ts +34 -9
- package/src/edit/line-hash.ts +34 -4
- package/src/edit/modes/hashline.ts +352 -8
- package/src/edit/streaming.ts +4 -1
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/extensions/runner.ts +3 -3
- package/src/extensibility/extensions/types.ts +4 -4
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/main.ts +13 -18
- package/src/memories/index.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/read-tool-group.ts +4 -9
- package/src/modes/components/tool-execution.ts +4 -0
- package/src/modes/controllers/event-controller.ts +2 -0
- package/src/modes/interactive-mode.ts +19 -12
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/utils/context-usage.ts +12 -5
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/plan-mode-active.md +7 -3
- package/src/prompts/system/plan-mode-approved.md +5 -0
- package/src/prompts/system/project-prompt.md +36 -0
- package/src/prompts/system/system-prompt.md +0 -29
- package/src/prompts/tools/github.md +1 -0
- package/src/prompts/tools/read.md +15 -14
- package/src/sdk.ts +29 -28
- package/src/session/agent-session.ts +20 -12
- package/src/session/compaction/branch-summarization.ts +1 -1
- package/src/session/compaction/compaction.ts +3 -3
- package/src/session/session-dump-format.ts +10 -5
- package/src/session/streaming-output.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +2 -2
- package/src/system-prompt.ts +35 -3
- package/src/task/executor.ts +4 -3
- package/src/task/isolation-backend.ts +22 -0
- package/src/tools/fetch.ts +4 -4
- package/src/tools/gh.ts +187 -0
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/path-utils.ts +11 -0
- package/src/tools/read.ts +393 -204
- package/src/tools/search.ts +1 -1
- package/src/tools/sqlite-reader.ts +1 -1
- package/src/utils/commit-message-generator.ts +1 -1
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/workspace-tree.ts +396 -0
package/src/tools/fetch.ts
CHANGED
|
@@ -148,20 +148,20 @@ export interface ParsedReadUrlTarget {
|
|
|
148
148
|
limit?: number;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
export function parseReadUrlTarget(readPath: string
|
|
152
|
-
const embedded =
|
|
151
|
+
export function parseReadUrlTarget(readPath: string): ParsedReadUrlTarget | null {
|
|
152
|
+
const embedded = tryExtractEmbeddedUrlSelector(readPath);
|
|
153
153
|
const urlPath = embedded?.path ?? readPath;
|
|
154
154
|
if (!isReadableUrlPath(urlPath)) {
|
|
155
155
|
return null;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const selector =
|
|
158
|
+
const selector = embedded?.sel;
|
|
159
159
|
const raw = selector === "raw";
|
|
160
160
|
const lineMatch = selector ? URL_LINE_RANGE_RE.exec(selector) : null;
|
|
161
161
|
if (lineMatch) {
|
|
162
162
|
const startLine = Number.parseInt(lineMatch[1]!, 10);
|
|
163
163
|
if (startLine < 1) {
|
|
164
|
-
throw new ToolError("
|
|
164
|
+
throw new ToolError("URL line selector 0 is invalid; lines are 1-indexed. Use :L1.");
|
|
165
165
|
}
|
|
166
166
|
const sep = lineMatch[2];
|
|
167
167
|
const rhs = lineMatch[3] ? Number.parseInt(lineMatch[3], 10) : undefined;
|
package/src/tools/gh.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
4
5
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
@@ -151,6 +152,7 @@ const githubSchema = Type.Object({
|
|
|
151
152
|
[
|
|
152
153
|
"repo_view",
|
|
153
154
|
"issue_view",
|
|
155
|
+
"pr_create",
|
|
154
156
|
"pr_view",
|
|
155
157
|
"pr_diff",
|
|
156
158
|
"pr_checkout",
|
|
@@ -205,6 +207,53 @@ const githubSchema = Type.Object({
|
|
|
205
207
|
),
|
|
206
208
|
force: Type.Optional(Type.Boolean({ description: "reset existing local branch (pr_checkout)" })),
|
|
207
209
|
forceWithLease: Type.Optional(Type.Boolean({ description: "force-with-lease push (pr_push)" })),
|
|
210
|
+
title: Type.Optional(
|
|
211
|
+
Type.String({
|
|
212
|
+
description: "PR title (pr_create)",
|
|
213
|
+
examples: ["Fix login bug"],
|
|
214
|
+
}),
|
|
215
|
+
),
|
|
216
|
+
body: Type.Optional(
|
|
217
|
+
Type.String({
|
|
218
|
+
description: "PR body markdown (pr_create); mutually exclusive with fill",
|
|
219
|
+
}),
|
|
220
|
+
),
|
|
221
|
+
base: Type.Optional(
|
|
222
|
+
Type.String({
|
|
223
|
+
description: "PR base branch (pr_create); defaults to repo default branch",
|
|
224
|
+
examples: ["main"],
|
|
225
|
+
}),
|
|
226
|
+
),
|
|
227
|
+
head: Type.Optional(
|
|
228
|
+
Type.String({
|
|
229
|
+
description: "PR head branch (pr_create); defaults to current branch",
|
|
230
|
+
examples: ["feature/foo"],
|
|
231
|
+
}),
|
|
232
|
+
),
|
|
233
|
+
draft: Type.Optional(Type.Boolean({ description: "open PR as draft (pr_create)" })),
|
|
234
|
+
fill: Type.Optional(
|
|
235
|
+
Type.Boolean({
|
|
236
|
+
description: "auto-fill PR title/body from commits (pr_create); mutually exclusive with title/body",
|
|
237
|
+
}),
|
|
238
|
+
),
|
|
239
|
+
reviewer: Type.Optional(
|
|
240
|
+
Type.Array(Type.String(), {
|
|
241
|
+
description: "reviewers to request (pr_create); accepts users or org/team",
|
|
242
|
+
examples: [["octocat", "myorg/team"]],
|
|
243
|
+
}),
|
|
244
|
+
),
|
|
245
|
+
assignee: Type.Optional(
|
|
246
|
+
Type.Array(Type.String(), {
|
|
247
|
+
description: "assignees (pr_create); use @me for the authenticated user",
|
|
248
|
+
examples: [["@me"]],
|
|
249
|
+
}),
|
|
250
|
+
),
|
|
251
|
+
label: Type.Optional(
|
|
252
|
+
Type.Array(Type.String(), {
|
|
253
|
+
description: "labels to apply (pr_create)",
|
|
254
|
+
examples: [["bug", "enhancement"]],
|
|
255
|
+
}),
|
|
256
|
+
),
|
|
208
257
|
query: Type.Optional(
|
|
209
258
|
Type.String({
|
|
210
259
|
description: "search query (search_issues, search_prs, search_code, search_commits, search_repos)",
|
|
@@ -2063,6 +2112,8 @@ export class GithubTool implements AgentTool<typeof githubSchema, GhToolDetails>
|
|
|
2063
2112
|
return executeRepoView(this.session, params, signal);
|
|
2064
2113
|
case "issue_view":
|
|
2065
2114
|
return executeIssueView(this.session, params, signal);
|
|
2115
|
+
case "pr_create":
|
|
2116
|
+
return executePrCreate(this.session, params, signal);
|
|
2066
2117
|
case "pr_view":
|
|
2067
2118
|
return executePrView(this.session, params, signal);
|
|
2068
2119
|
case "pr_diff":
|
|
@@ -2442,6 +2493,142 @@ async function executePrPush(
|
|
|
2442
2493
|
);
|
|
2443
2494
|
}
|
|
2444
2495
|
|
|
2496
|
+
async function executePrCreate(
|
|
2497
|
+
session: ToolSession,
|
|
2498
|
+
params: GithubInput,
|
|
2499
|
+
signal: AbortSignal | undefined,
|
|
2500
|
+
): Promise<AgentToolResult<GhToolDetails>> {
|
|
2501
|
+
const repo = normalizeOptionalString(params.repo);
|
|
2502
|
+
const title = normalizeOptionalString(params.title);
|
|
2503
|
+
const body = params.body;
|
|
2504
|
+
const base = normalizeOptionalString(params.base);
|
|
2505
|
+
const head = normalizeOptionalString(params.head);
|
|
2506
|
+
const draft = params.draft ?? false;
|
|
2507
|
+
const fill = params.fill ?? false;
|
|
2508
|
+
const reviewers = normalizePrIdentifierList(params.reviewer);
|
|
2509
|
+
const assignees = normalizePrIdentifierList(params.assignee);
|
|
2510
|
+
const labels = normalizePrIdentifierList(params.label);
|
|
2511
|
+
|
|
2512
|
+
if (!fill && !title) {
|
|
2513
|
+
throw new ToolError("title is required unless fill is true");
|
|
2514
|
+
}
|
|
2515
|
+
if (fill && (title || body !== undefined)) {
|
|
2516
|
+
throw new ToolError("fill is mutually exclusive with title and body");
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
const args = ["pr", "create"];
|
|
2520
|
+
appendRepoFlag(args, repo);
|
|
2521
|
+
if (title) args.push("--title", title);
|
|
2522
|
+
if (base) args.push("--base", base);
|
|
2523
|
+
if (head) args.push("--head", head);
|
|
2524
|
+
if (draft) args.push("--draft");
|
|
2525
|
+
if (fill) args.push("--fill");
|
|
2526
|
+
for (const reviewer of reviewers) args.push("--reviewer", reviewer);
|
|
2527
|
+
for (const assignee of assignees) args.push("--assignee", assignee);
|
|
2528
|
+
for (const label of labels) args.push("--label", label);
|
|
2529
|
+
|
|
2530
|
+
let bodyDir: string | undefined;
|
|
2531
|
+
try {
|
|
2532
|
+
if (!fill) {
|
|
2533
|
+
if (body !== undefined && body.length > 0) {
|
|
2534
|
+
// Route through a temp file so multi-KB bodies stay clear of any
|
|
2535
|
+
// argv-length limits and shell-quoting hazards on uncommon platforms.
|
|
2536
|
+
bodyDir = await fs.mkdtemp(path.join(os.tmpdir(), "gh-pr-body-"));
|
|
2537
|
+
const bodyFile = path.join(bodyDir, "body.md");
|
|
2538
|
+
await Bun.write(bodyFile, body);
|
|
2539
|
+
args.push("--body-file", bodyFile);
|
|
2540
|
+
} else {
|
|
2541
|
+
// Avoid gh dropping into an interactive editor when no body is given.
|
|
2542
|
+
args.push("--body", "");
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
const output = await git.github.text(session.cwd, args, signal, {
|
|
2547
|
+
repoProvided: Boolean(repo),
|
|
2548
|
+
});
|
|
2549
|
+
const url =
|
|
2550
|
+
output
|
|
2551
|
+
.split("\n")
|
|
2552
|
+
.map(line => line.trim())
|
|
2553
|
+
.find(line => line.startsWith("https://github.com/")) ?? output.trim();
|
|
2554
|
+
const parsed = parsePullRequestUrl(url);
|
|
2555
|
+
const resolvedRepo = repo ?? parsed.repo;
|
|
2556
|
+
|
|
2557
|
+
let prView: GhPrViewData | undefined;
|
|
2558
|
+
if (resolvedRepo && parsed.prNumber !== undefined) {
|
|
2559
|
+
try {
|
|
2560
|
+
prView = await git.github.json<GhPrViewData>(
|
|
2561
|
+
session.cwd,
|
|
2562
|
+
[
|
|
2563
|
+
"pr",
|
|
2564
|
+
"view",
|
|
2565
|
+
String(parsed.prNumber),
|
|
2566
|
+
"--repo",
|
|
2567
|
+
resolvedRepo,
|
|
2568
|
+
"--json",
|
|
2569
|
+
GH_PR_FIELDS_NO_COMMENTS.join(","),
|
|
2570
|
+
],
|
|
2571
|
+
signal,
|
|
2572
|
+
{ repoProvided: true },
|
|
2573
|
+
);
|
|
2574
|
+
} catch {
|
|
2575
|
+
// Best-effort summary; PR creation already succeeded.
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
const text = formatPrCreateResult({
|
|
2580
|
+
url,
|
|
2581
|
+
prNumber: parsed.prNumber,
|
|
2582
|
+
data: prView,
|
|
2583
|
+
title,
|
|
2584
|
+
base,
|
|
2585
|
+
head,
|
|
2586
|
+
draft,
|
|
2587
|
+
});
|
|
2588
|
+
return buildTextResult(text, url || prView?.url);
|
|
2589
|
+
} finally {
|
|
2590
|
+
if (bodyDir) {
|
|
2591
|
+
await fs.rm(bodyDir, { recursive: true, force: true }).catch(() => {});
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
function formatPrCreateResult(options: {
|
|
2597
|
+
url: string;
|
|
2598
|
+
prNumber?: number;
|
|
2599
|
+
data?: GhPrViewData;
|
|
2600
|
+
title?: string;
|
|
2601
|
+
base?: string;
|
|
2602
|
+
head?: string;
|
|
2603
|
+
draft?: boolean;
|
|
2604
|
+
}): string {
|
|
2605
|
+
const number = options.prNumber ?? options.data?.number;
|
|
2606
|
+
const headerTitle = options.data?.title ?? options.title ?? "Untitled";
|
|
2607
|
+
const header =
|
|
2608
|
+
number !== undefined
|
|
2609
|
+
? `# Created Pull Request #${number}: ${headerTitle}`
|
|
2610
|
+
: `# Created Pull Request: ${headerTitle}`;
|
|
2611
|
+
const lines: string[] = [header, ""];
|
|
2612
|
+
pushLine(lines, "URL", options.url || options.data?.url);
|
|
2613
|
+
pushLine(lines, "State", options.data?.state);
|
|
2614
|
+
pushLine(lines, "Draft", options.data?.isDraft ?? options.draft);
|
|
2615
|
+
pushLine(lines, "Base", options.data?.baseRefName ?? options.base);
|
|
2616
|
+
pushLine(lines, "Head", options.data?.headRefName ?? options.head);
|
|
2617
|
+
pushLine(lines, "Author", formatAuthor(options.data?.author));
|
|
2618
|
+
pushLine(lines, "Created", options.data?.createdAt);
|
|
2619
|
+
pushLine(lines, "Labels", formatLabels(options.data?.labels));
|
|
2620
|
+
|
|
2621
|
+
const bodyText = normalizeText(options.data?.body);
|
|
2622
|
+
if (bodyText) {
|
|
2623
|
+
lines.push("");
|
|
2624
|
+
lines.push("## Body");
|
|
2625
|
+
lines.push("");
|
|
2626
|
+
lines.push(bodyText);
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
return lines.join("\n").trim();
|
|
2630
|
+
}
|
|
2631
|
+
|
|
2445
2632
|
async function executeSearchIssues(
|
|
2446
2633
|
session: ToolSession,
|
|
2447
2634
|
params: GithubInput,
|
|
@@ -127,7 +127,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
127
127
|
const response = await this.completeImageRequest(
|
|
128
128
|
model,
|
|
129
129
|
{
|
|
130
|
-
systemPrompt: prompt.render(inspectImageSystemPromptTemplate),
|
|
130
|
+
systemPrompt: [prompt.render(inspectImageSystemPromptTemplate)],
|
|
131
131
|
messages: [
|
|
132
132
|
{
|
|
133
133
|
role: "user",
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -337,7 +337,7 @@ export function formatTruncationMetaNotice(truncation: TruncationMeta): string {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
if (truncation.nextOffset != null) {
|
|
340
|
-
notice += `. Use
|
|
340
|
+
notice += `. Use :${truncation.nextOffset} to continue`;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
if (truncation.artifactId != null) {
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as url from "node:url";
|
|
|
5
5
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
6
6
|
|
|
7
7
|
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
8
|
+
const FILE_LINE_RANGE_RE = /^(?:L?\d+(?:[-+]L?\d+)?|raw)$/i;
|
|
8
9
|
const NARROW_NO_BREAK_SPACE = "\u202F";
|
|
9
10
|
const TOP_LEVEL_INTERNAL_URL_PREFIXES = [
|
|
10
11
|
"agent://",
|
|
@@ -102,6 +103,16 @@ export function expandPath(filePath: string): string {
|
|
|
102
103
|
return expandTilde(normalized);
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
export function splitPathAndSel(rawPath: string): { path: string; sel?: string } {
|
|
107
|
+
const colon = rawPath.lastIndexOf(":");
|
|
108
|
+
if (colon <= 0) return { path: rawPath };
|
|
109
|
+
|
|
110
|
+
const candidate = rawPath.slice(colon + 1);
|
|
111
|
+
if (!FILE_LINE_RANGE_RE.test(candidate)) return { path: rawPath };
|
|
112
|
+
|
|
113
|
+
return { path: rawPath.slice(0, colon), sel: candidate };
|
|
114
|
+
}
|
|
115
|
+
|
|
105
116
|
function assertNotInternalUrl(expanded: string, original: string): void {
|
|
106
117
|
for (const prefix of TOP_LEVEL_INTERNAL_URL_PREFIXES) {
|
|
107
118
|
if (expanded.startsWith(prefix)) {
|