@oh-my-pi/pi-coding-agent 14.6.5 → 14.7.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 +47 -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 +7 -7
- package/src/autoresearch/index.ts +48 -44
- 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 +39 -0
- package/src/edit/line-hash.ts +34 -4
- package/src/edit/modes/hashline.ts +221 -7
- 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/main.ts +3 -3
- package/src/memories/index.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/custom-editor.ts +4 -5
- 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/controllers/input-controller.ts +3 -1
- package/src/modes/interactive-mode.ts +24 -0
- 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/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/hashline.md +24 -6
- 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/session-manager.ts +57 -0
- package/src/session/streaming-output.ts +1 -1
- package/src/system-prompt.ts +35 -3
- package/src/task/executor.ts +4 -3
- package/src/tools/fetch.ts +4 -4
- package/src/tools/gh.ts +187 -0
- package/src/tools/image-gen.ts +3 -1
- 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 +388 -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/system-prompt.ts
CHANGED
|
@@ -13,7 +13,9 @@ import type { SkillsSettings } from "./config/settings";
|
|
|
13
13
|
import { type ContextFile, loadCapability, type SystemPrompt as SystemPromptFile } from "./discovery";
|
|
14
14
|
import { loadSkills, type Skill } from "./extensibility/skills";
|
|
15
15
|
import customSystemPromptTemplate from "./prompts/system/custom-system-prompt.md" with { type: "text" };
|
|
16
|
+
import projectPromptTemplate from "./prompts/system/project-prompt.md" with { type: "text" };
|
|
16
17
|
import systemPromptTemplate from "./prompts/system/system-prompt.md" with { type: "text" };
|
|
18
|
+
import { buildWorkspaceTree, type WorkspaceTree } from "./workspace-tree";
|
|
17
19
|
|
|
18
20
|
interface AlwaysApplyRule {
|
|
19
21
|
name: string;
|
|
@@ -409,12 +411,20 @@ export interface BuildSystemPromptOptions {
|
|
|
409
411
|
secretsEnabled?: boolean;
|
|
410
412
|
/** Pre-loaded AGENTS.md search (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
411
413
|
agentsMdSearch?: AgentsMdSearch | Promise<AgentsMdSearch>;
|
|
414
|
+
/** Pre-loaded workspace tree (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
415
|
+
workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Result of building provider-facing system prompt messages. */
|
|
419
|
+
export interface BuildSystemPromptResult {
|
|
420
|
+
/** Ordered system prompt blocks. Providers should preserve entries as distinct messages/blocks. */
|
|
421
|
+
systemPrompt: string[];
|
|
412
422
|
}
|
|
413
423
|
|
|
414
424
|
/** Build the system prompt with tools, guidelines, and context */
|
|
415
|
-
export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<
|
|
425
|
+
export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<BuildSystemPromptResult> {
|
|
416
426
|
if ($env.NULL_PROMPT === "true") {
|
|
417
|
-
return
|
|
427
|
+
return { systemPrompt: [] };
|
|
418
428
|
}
|
|
419
429
|
|
|
420
430
|
const {
|
|
@@ -435,6 +445,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
435
445
|
eagerTasks = false,
|
|
436
446
|
secretsEnabled = false,
|
|
437
447
|
agentsMdSearch: providedAgentsMdSearch,
|
|
448
|
+
workspaceTree: providedWorkspaceTree,
|
|
438
449
|
} = options;
|
|
439
450
|
const resolvedCwd = cwd ?? getProjectDir();
|
|
440
451
|
|
|
@@ -449,6 +460,10 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
449
460
|
providedAgentsMdSearch !== undefined
|
|
450
461
|
? Promise.resolve(providedAgentsMdSearch)
|
|
451
462
|
: logger.time("buildAgentsMdSearch", buildAgentsMdSearch, resolvedCwd);
|
|
463
|
+
const workspaceTreePromise =
|
|
464
|
+
providedWorkspaceTree !== undefined
|
|
465
|
+
? Promise.resolve(providedWorkspaceTree)
|
|
466
|
+
: logger.time("buildWorkspaceTree", buildWorkspaceTree, resolvedCwd);
|
|
452
467
|
const skillsPromise: Promise<Skill[]> =
|
|
453
468
|
providedSkills !== undefined
|
|
454
469
|
? Promise.resolve(providedSkills)
|
|
@@ -463,6 +478,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
463
478
|
contextFilesPromise,
|
|
464
479
|
agentsMdSearchPromise,
|
|
465
480
|
skillsPromise,
|
|
481
|
+
workspaceTreePromise,
|
|
466
482
|
]).then(
|
|
467
483
|
([
|
|
468
484
|
resolvedCustomPrompt,
|
|
@@ -471,6 +487,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
471
487
|
contextFiles,
|
|
472
488
|
agentsMdSearch,
|
|
473
489
|
skills,
|
|
490
|
+
workspaceTree,
|
|
474
491
|
]) => ({
|
|
475
492
|
resolvedCustomPrompt,
|
|
476
493
|
resolvedAppendPrompt,
|
|
@@ -478,6 +495,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
478
495
|
contextFiles,
|
|
479
496
|
agentsMdSearch,
|
|
480
497
|
skills,
|
|
498
|
+
workspaceTree,
|
|
481
499
|
}),
|
|
482
500
|
);
|
|
483
501
|
})();
|
|
@@ -501,6 +519,12 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
501
519
|
pattern: `AGENTS.md depth ${AGENTS_MD_MIN_DEPTH}-${AGENTS_MD_MAX_DEPTH}`,
|
|
502
520
|
files: [],
|
|
503
521
|
};
|
|
522
|
+
let workspaceTree: WorkspaceTree = {
|
|
523
|
+
rootPath: resolvedCwd,
|
|
524
|
+
rendered: "",
|
|
525
|
+
truncated: false,
|
|
526
|
+
totalLines: 0,
|
|
527
|
+
};
|
|
504
528
|
let skills: Skill[] = providedSkills ?? [];
|
|
505
529
|
|
|
506
530
|
if (prepResult.type === "timeout") {
|
|
@@ -524,6 +548,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
524
548
|
contextFiles = dedupeExactContextFiles(prepResult.value.contextFiles);
|
|
525
549
|
agentsMdSearch = prepResult.value.agentsMdSearch;
|
|
526
550
|
skills = prepResult.value.skills;
|
|
551
|
+
workspaceTree = prepResult.value.workspaceTree;
|
|
527
552
|
}
|
|
528
553
|
|
|
529
554
|
const date = new Date().toISOString().slice(0, 10);
|
|
@@ -578,6 +603,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
578
603
|
environment,
|
|
579
604
|
contextFiles,
|
|
580
605
|
agentsMdSearch,
|
|
606
|
+
workspaceTree,
|
|
581
607
|
skills: filteredSkills,
|
|
582
608
|
rules: rules ?? [],
|
|
583
609
|
alwaysApplyRules: injectedAlwaysApplyRules,
|
|
@@ -599,5 +625,11 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
599
625
|
rendered += `\n\n<critical>\nThe \`${reportToolIssueToolName}\` tool is available for automated QA. If ANY tool you call returns output that is unexpected, incorrect, malformed, or otherwise inconsistent with what you anticipated given the tool's described behavior and your parameters, call \`${reportToolIssueToolName}\` with the tool name and a concise description of the discrepancy. Do not hesitate to report — false positives are acceptable.\n</critical>`;
|
|
600
626
|
}
|
|
601
627
|
|
|
602
|
-
|
|
628
|
+
const systemPrompt = [rendered];
|
|
629
|
+
const projectPrompt = resolvedCustomPrompt ? "" : prompt.render(projectPromptTemplate, data).trim();
|
|
630
|
+
if (projectPrompt) {
|
|
631
|
+
systemPrompt.push(projectPrompt);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return { systemPrompt };
|
|
603
635
|
}
|
package/src/task/executor.ts
CHANGED
|
@@ -967,9 +967,9 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
967
967
|
contextFiles: options.contextFiles,
|
|
968
968
|
skills: options.skills,
|
|
969
969
|
promptTemplates: options.promptTemplates,
|
|
970
|
-
systemPrompt: defaultPrompt =>
|
|
970
|
+
systemPrompt: defaultPrompt => [
|
|
971
971
|
prompt.render(subagentSystemPromptTemplate, {
|
|
972
|
-
base: defaultPrompt,
|
|
972
|
+
base: defaultPrompt.join("\n\n"),
|
|
973
973
|
agent: agent.systemPrompt,
|
|
974
974
|
worktree: worktree ?? "",
|
|
975
975
|
outputSchema: normalizedOutputSchema,
|
|
@@ -977,6 +977,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
977
977
|
ircPeers: ircEnabled ? renderIrcPeerRoster(id) : "",
|
|
978
978
|
ircSelfId: ircEnabled ? id : "",
|
|
979
979
|
}),
|
|
980
|
+
],
|
|
980
981
|
sessionManager,
|
|
981
982
|
hasUI: false,
|
|
982
983
|
spawns: spawnsEnv,
|
|
@@ -1016,7 +1017,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1016
1017
|
}
|
|
1017
1018
|
|
|
1018
1019
|
session.sessionManager.appendSessionInit({
|
|
1019
|
-
systemPrompt: session.agent.state.systemPrompt,
|
|
1020
|
+
systemPrompt: session.agent.state.systemPrompt.join("\n\n"),
|
|
1020
1021
|
task,
|
|
1021
1022
|
tools: session.getActiveToolNames(),
|
|
1022
1023
|
outputSchema,
|
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,
|
package/src/tools/image-gen.ts
CHANGED
|
@@ -1125,7 +1125,9 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1125
1125
|
headers: {
|
|
1126
1126
|
"Content-Type": "application/json",
|
|
1127
1127
|
Authorization: `Bearer ${apiKey.apiKey}`,
|
|
1128
|
-
"
|
|
1128
|
+
"HTTP-Referer": "https://github.com/can1357/oh-my-pi",
|
|
1129
|
+
"X-OpenRouter-Title": "Oh-My-Pi",
|
|
1130
|
+
"X-OpenRouter-Categories": "cli-agent",
|
|
1129
1131
|
},
|
|
1130
1132
|
body: JSON.stringify(requestBody),
|
|
1131
1133
|
signal: requestSignal,
|
|
@@ -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)) {
|