@oh-my-pi/pi-coding-agent 14.1.2 → 14.2.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 +47 -2
- package/package.json +8 -8
- package/scripts/build-binary.ts +61 -0
- package/src/autoresearch/helpers.ts +10 -0
- package/src/autoresearch/index.ts +1 -11
- package/src/autoresearch/tools/init-experiment.ts +1 -10
- package/src/autoresearch/tools/log-experiment.ts +1 -11
- package/src/autoresearch/tools/run-experiment.ts +1 -10
- package/src/bun-imports.d.ts +6 -0
- package/src/cli/plugin-cli.ts +23 -45
- package/src/commit/agentic/tools/propose-commit.ts +1 -14
- package/src/commit/agentic/tools/split-commit.ts +1 -15
- package/src/commit/utils.ts +15 -1
- package/src/config/model-registry.ts +3 -3
- package/src/config/prompt-templates.ts +4 -12
- package/src/config/settings-schema.ts +27 -2
- package/src/config/settings.ts +1 -1
- package/src/dap/session.ts +8 -2
- package/src/discovery/claude-plugins.ts +61 -6
- package/src/discovery/codex.ts +2 -15
- package/src/discovery/gemini.ts +2 -15
- package/src/discovery/helpers.ts +40 -1
- package/src/discovery/opencode.ts +2 -15
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +3 -14
- package/src/edit/index.ts +67 -3
- package/src/edit/modes/apply-patch.lark +19 -0
- package/src/edit/modes/apply-patch.ts +63 -0
- package/src/edit/modes/chunk.ts +6 -2
- package/src/edit/modes/hashline.ts +3 -3
- package/src/edit/modes/replace.ts +2 -13
- package/src/edit/read-file.ts +18 -0
- package/src/edit/renderer.ts +61 -33
- package/src/extensibility/extensions/compact-handler.ts +40 -0
- package/src/extensibility/extensions/runner.ts +11 -29
- package/src/extensibility/utils.ts +7 -1
- package/src/internal-urls/docs-index.generated.ts +9 -2
- package/src/lsp/client.ts +14 -5
- package/src/lsp/index.ts +53 -10
- package/src/lsp/render.ts +14 -2
- package/src/lsp/types.ts +2 -0
- package/src/main.ts +1 -0
- package/src/mcp/manager.ts +29 -48
- package/src/memories/index.ts +7 -1
- package/src/modes/acp/acp-agent.ts +3 -16
- package/src/modes/components/model-selector.ts +15 -24
- package/src/modes/components/plugin-settings.ts +16 -5
- package/src/modes/components/read-tool-group.ts +92 -9
- package/src/modes/components/settings-defs.ts +18 -0
- package/src/modes/components/settings-selector.ts +2 -6
- package/src/modes/components/tool-execution.ts +61 -28
- package/src/modes/controllers/event-controller.ts +3 -1
- package/src/modes/controllers/extension-ui-controller.ts +99 -150
- package/src/modes/controllers/selector-controller.ts +3 -12
- package/src/modes/interactive-mode.ts +4 -2
- package/src/modes/print-mode.ts +4 -22
- package/src/modes/rpc/rpc-mode.ts +18 -38
- package/src/modes/shared.ts +10 -1
- package/src/modes/utils/ui-helpers.ts +6 -2
- package/src/plan-mode/approved-plan.ts +5 -4
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/subagent-user-prompt.md +2 -2
- package/src/prompts/system/system-prompt.md +208 -243
- package/src/prompts/tools/apply-patch.md +67 -0
- package/src/prompts/tools/ast-edit.md +18 -23
- package/src/prompts/tools/ast-grep.md +25 -32
- package/src/prompts/tools/bash.md +11 -23
- package/src/prompts/tools/debug.md +8 -22
- package/src/prompts/tools/find.md +0 -4
- package/src/prompts/tools/grep.md +3 -5
- package/src/prompts/tools/hashline.md +16 -10
- package/src/prompts/tools/python.md +10 -14
- package/src/prompts/tools/read.md +17 -24
- package/src/prompts/tools/task.md +57 -21
- package/src/prompts/tools/todo-write.md +45 -67
- package/src/session/agent-session.ts +4 -4
- package/src/session/session-manager.ts +15 -7
- package/src/session/streaming-output.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +3 -14
- package/src/task/executor.ts +13 -34
- package/src/task/index.ts +82 -18
- package/src/task/simple-mode.ts +27 -0
- package/src/task/template.ts +17 -3
- package/src/task/types.ts +77 -30
- package/src/tools/ask.ts +2 -4
- package/src/tools/ast-edit.ts +41 -17
- package/src/tools/ast-grep.ts +8 -27
- package/src/tools/bash-skill-urls.ts +9 -7
- package/src/tools/bash.ts +66 -24
- package/src/tools/browser.ts +1 -1
- package/src/tools/fetch.ts +1 -14
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/find.ts +25 -29
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +1 -8
- package/src/tools/gh.ts +6 -13
- package/src/tools/grep.ts +103 -59
- package/src/tools/jtd-to-json-schema.ts +16 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/path-utils.ts +61 -5
- package/src/tools/plan-mode-guard.ts +6 -5
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +1 -1
- package/src/tools/render-utils.ts +38 -6
- package/src/tools/renderers.ts +1 -0
- package/src/tools/resolve.ts +12 -3
- package/src/tools/ssh.ts +3 -11
- package/src/tools/submit-result.ts +1 -13
- package/src/tools/todo-write.ts +137 -103
- package/src/tools/vim.ts +1 -1
- package/src/tools/write.ts +2 -23
- package/src/tui/code-cell.ts +12 -7
- package/src/utils/edit-mode.ts +3 -2
- package/src/utils/git.ts +1 -1
- package/src/vim/engine.ts +41 -58
- package/src/web/scrapers/crates-io.ts +1 -14
- package/src/web/scrapers/types.ts +13 -0
- package/src/web/search/providers/base.ts +13 -0
- package/src/web/search/providers/brave.ts +2 -5
- package/src/web/search/providers/codex.ts +20 -24
- package/src/web/search/providers/gemini.ts +39 -1
- package/src/web/search/providers/jina.ts +2 -5
- package/src/web/search/providers/kagi.ts +3 -8
- package/src/web/search/providers/kimi.ts +3 -7
- package/src/web/search/providers/parallel.ts +3 -8
- package/src/web/search/providers/synthetic.ts +3 -7
- package/src/web/search/providers/tavily.ts +15 -11
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +3 -7
package/src/tools/bash.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
|
9
9
|
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
10
10
|
import type { Theme } from "../modes/theme/theme";
|
|
11
11
|
import bashDescription from "../prompts/tools/bash.md" with { type: "text" };
|
|
12
|
-
import { DEFAULT_MAX_BYTES, TailBuffer } from "../session/streaming-output";
|
|
12
|
+
import { DEFAULT_MAX_BYTES, streamTailUpdates, TailBuffer } from "../session/streaming-output";
|
|
13
13
|
import { renderStatusLine } from "../tui";
|
|
14
14
|
import { CachedOutputBlock } from "../tui/output-block";
|
|
15
15
|
import { getSixelLineMask } from "../utils/sixel";
|
|
@@ -23,7 +23,7 @@ import { resolveToCwd } from "./path-utils";
|
|
|
23
23
|
import { formatToolWorkingDirectory, replaceTabs } from "./render-utils";
|
|
24
24
|
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
25
25
|
import { toolResult } from "./tool-result";
|
|
26
|
-
import { clampTimeout } from "./tool-timeouts";
|
|
26
|
+
import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
|
|
27
27
|
|
|
28
28
|
export const BASH_DEFAULT_PREVIEW_LINES = 10;
|
|
29
29
|
|
|
@@ -38,7 +38,7 @@ const bashSchemaBase = Type.Object({
|
|
|
38
38
|
"Additional environment variables passed to the command and rendered inline as shell assignments; prefer this for multiline or quote-heavy content",
|
|
39
39
|
}),
|
|
40
40
|
),
|
|
41
|
-
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds
|
|
41
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds", default: 300 })),
|
|
42
42
|
cwd: Type.Optional(Type.String({ description: "Working directory (default: cwd)" })),
|
|
43
43
|
head: Type.Optional(Type.Number({ description: "Return only first N lines of output" })),
|
|
44
44
|
tail: Type.Optional(Type.Number({ description: "Return only last N lines of output" })),
|
|
@@ -74,6 +74,7 @@ export interface BashToolInput {
|
|
|
74
74
|
export interface BashToolDetails {
|
|
75
75
|
meta?: OutputMeta;
|
|
76
76
|
timeoutSeconds?: number;
|
|
77
|
+
requestedTimeoutSeconds?: number;
|
|
77
78
|
async?: {
|
|
78
79
|
state: "running" | "completed" | "failed";
|
|
79
80
|
jobId: string;
|
|
@@ -219,6 +220,13 @@ function getBashEnvForDisplay(args: BashRenderArgs): Record<string, string> | un
|
|
|
219
220
|
if (partialEnv && args.env) return { ...partialEnv, ...args.env };
|
|
220
221
|
return args.env ?? partialEnv;
|
|
221
222
|
}
|
|
223
|
+
|
|
224
|
+
function formatTimeoutClampNotice(requestedTimeoutSec: number, effectiveTimeoutSec: number): string | undefined {
|
|
225
|
+
return requestedTimeoutSec !== effectiveTimeoutSec
|
|
226
|
+
? `Timeout clamped to ${effectiveTimeoutSec}s (requested ${requestedTimeoutSec}s; allowed range ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}s).`
|
|
227
|
+
: undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
222
230
|
/**
|
|
223
231
|
* Bash tool implementation.
|
|
224
232
|
*
|
|
@@ -289,9 +297,16 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
289
297
|
timeoutSec: number,
|
|
290
298
|
headLines?: number,
|
|
291
299
|
tailLines?: number,
|
|
300
|
+
options: { requestedTimeoutSec?: number; notices?: string[] } = {},
|
|
292
301
|
): AgentToolResult<BashToolDetails> {
|
|
293
|
-
const
|
|
302
|
+
const outputLines = [this.#formatResultOutput(result, headLines, tailLines)];
|
|
303
|
+
const notices = options.notices?.filter(Boolean) ?? [];
|
|
304
|
+
if (notices.length > 0) outputLines.push("", ...notices);
|
|
305
|
+
const outputText = outputLines.join("\n");
|
|
294
306
|
const details: BashToolDetails = { timeoutSeconds: timeoutSec };
|
|
307
|
+
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
308
|
+
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
309
|
+
}
|
|
295
310
|
const resultBuilder = toolResult(details).text(outputText).truncationFromSummary(result, { direction: "tail" });
|
|
296
311
|
this.#buildResultText(result, timeoutSec, outputText);
|
|
297
312
|
return resultBuilder.done();
|
|
@@ -302,16 +317,23 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
302
317
|
label: string,
|
|
303
318
|
previewText: string,
|
|
304
319
|
timeoutSec: number,
|
|
320
|
+
options: { requestedTimeoutSec?: number; notices?: string[] } = {},
|
|
305
321
|
): AgentToolResult<BashToolDetails> {
|
|
306
322
|
const details: BashToolDetails = {
|
|
307
323
|
timeoutSeconds: timeoutSec,
|
|
308
324
|
async: { state: "running", jobId, type: "bash" },
|
|
309
325
|
};
|
|
326
|
+
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
327
|
+
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
328
|
+
}
|
|
310
329
|
const lines: string[] = [];
|
|
311
330
|
const trimmedPreview = previewText.trimEnd();
|
|
312
331
|
if (trimmedPreview.length > 0) {
|
|
313
332
|
lines.push(trimmedPreview, "");
|
|
314
333
|
}
|
|
334
|
+
if (options.notices?.length) {
|
|
335
|
+
lines.push(...options.notices, "");
|
|
336
|
+
}
|
|
315
337
|
lines.push(`Background job ${jobId} started: ${label}`);
|
|
316
338
|
lines.push("Result will be delivered automatically when complete.");
|
|
317
339
|
lines.push(`Use \`poll\`, \`read jobs://${jobId}\`, or \`cancel_job\` if needed.`);
|
|
@@ -330,6 +352,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
330
352
|
commandCwd: string;
|
|
331
353
|
timeoutMs: number;
|
|
332
354
|
timeoutSec: number;
|
|
355
|
+
requestedTimeoutSec?: number;
|
|
356
|
+
timeoutClampNotice?: string;
|
|
333
357
|
headLines?: number;
|
|
334
358
|
tailLines?: number;
|
|
335
359
|
resolvedEnv?: Record<string, string>;
|
|
@@ -372,6 +396,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
372
396
|
options.timeoutSec,
|
|
373
397
|
options.headLines,
|
|
374
398
|
options.tailLines,
|
|
399
|
+
{
|
|
400
|
+
requestedTimeoutSec: options.requestedTimeoutSec,
|
|
401
|
+
notices: [options.timeoutClampNotice].filter((notice): notice is string => Boolean(notice)),
|
|
402
|
+
},
|
|
375
403
|
);
|
|
376
404
|
const finalText = this.#extractTextResult(finalResult);
|
|
377
405
|
latestText = finalText;
|
|
@@ -512,7 +540,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
512
540
|
: undefined;
|
|
513
541
|
|
|
514
542
|
// Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.
|
|
515
|
-
if (cwd?.includes("://")) {
|
|
543
|
+
if (cwd?.includes("://") || cwd?.includes("local:/")) {
|
|
516
544
|
cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
|
|
517
545
|
}
|
|
518
546
|
|
|
@@ -531,8 +559,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
531
559
|
}
|
|
532
560
|
|
|
533
561
|
// Clamp to reasonable range: 1s - 3600s (1 hour)
|
|
534
|
-
const
|
|
562
|
+
const requestedTimeoutSec = rawTimeout;
|
|
563
|
+
const timeoutSec = clampTimeout("bash", requestedTimeoutSec);
|
|
535
564
|
const timeoutMs = timeoutSec * 1000;
|
|
565
|
+
const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec);
|
|
536
566
|
|
|
537
567
|
if (asyncRequested) {
|
|
538
568
|
if (!this.session.asyncJobManager) {
|
|
@@ -543,13 +573,18 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
543
573
|
commandCwd,
|
|
544
574
|
timeoutMs,
|
|
545
575
|
timeoutSec,
|
|
576
|
+
requestedTimeoutSec,
|
|
577
|
+
timeoutClampNotice,
|
|
546
578
|
headLines,
|
|
547
579
|
tailLines,
|
|
548
580
|
resolvedEnv,
|
|
549
581
|
onUpdate,
|
|
550
582
|
startBackgrounded: true,
|
|
551
583
|
});
|
|
552
|
-
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec
|
|
584
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
585
|
+
requestedTimeoutSec,
|
|
586
|
+
notices: [timeoutClampNotice].filter((notice): notice is string => Boolean(notice)),
|
|
587
|
+
});
|
|
553
588
|
}
|
|
554
589
|
|
|
555
590
|
if (this.#autoBackgroundEnabled && !pty && this.session.asyncJobManager) {
|
|
@@ -560,6 +595,8 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
560
595
|
commandCwd,
|
|
561
596
|
timeoutMs,
|
|
562
597
|
timeoutSec,
|
|
598
|
+
requestedTimeoutSec,
|
|
599
|
+
timeoutClampNotice,
|
|
563
600
|
headLines,
|
|
564
601
|
tailLines,
|
|
565
602
|
resolvedEnv,
|
|
@@ -567,7 +604,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
567
604
|
startBackgrounded,
|
|
568
605
|
});
|
|
569
606
|
if (startBackgrounded) {
|
|
570
|
-
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec
|
|
607
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
608
|
+
requestedTimeoutSec,
|
|
609
|
+
notices: [timeoutClampNotice].filter((notice): notice is string => Boolean(notice)),
|
|
610
|
+
});
|
|
571
611
|
}
|
|
572
612
|
const waitResult = await this.#waitForManagedBashJob(job, autoBackgroundWaitMs, signal);
|
|
573
613
|
if (waitResult.kind === "completed") {
|
|
@@ -584,7 +624,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
584
624
|
throw new ToolAbortError(job.getLatestText() || "Command aborted");
|
|
585
625
|
}
|
|
586
626
|
job.setBackgrounded(true);
|
|
587
|
-
return this.#buildBackgroundStartResult(job.jobId, job.label, job.getLatestText(), timeoutSec
|
|
627
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, job.getLatestText(), timeoutSec, {
|
|
628
|
+
requestedTimeoutSec,
|
|
629
|
+
notices: [timeoutClampNotice].filter((notice): notice is string => Boolean(notice)),
|
|
630
|
+
});
|
|
588
631
|
}
|
|
589
632
|
|
|
590
633
|
// Track output for streaming updates (tail only)
|
|
@@ -612,15 +655,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
612
655
|
env: resolvedEnv,
|
|
613
656
|
artifactPath,
|
|
614
657
|
artifactId,
|
|
615
|
-
onChunk:
|
|
616
|
-
tailBuffer.append(chunk);
|
|
617
|
-
if (onUpdate) {
|
|
618
|
-
onUpdate({
|
|
619
|
-
content: [{ type: "text", text: tailBuffer.text() }],
|
|
620
|
-
details: {},
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
},
|
|
658
|
+
onChunk: streamTailUpdates(tailBuffer, onUpdate),
|
|
624
659
|
});
|
|
625
660
|
if (result.cancelled) {
|
|
626
661
|
if (signal?.aborted) {
|
|
@@ -631,7 +666,10 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
631
666
|
if (isInteractiveResult(result) && result.timedOut) {
|
|
632
667
|
throw new ToolError(normalizeResultOutput(result) || `Command timed out after ${timeoutSec} seconds`);
|
|
633
668
|
}
|
|
634
|
-
return this.#buildCompletedResult(result, timeoutSec, headLines, tailLines
|
|
669
|
+
return this.#buildCompletedResult(result, timeoutSec, headLines, tailLines, {
|
|
670
|
+
requestedTimeoutSec,
|
|
671
|
+
notices: [timeoutClampNotice].filter((notice): notice is string => Boolean(notice)),
|
|
672
|
+
});
|
|
635
673
|
}
|
|
636
674
|
}
|
|
637
675
|
|
|
@@ -708,12 +746,16 @@ export const bashToolRenderer = {
|
|
|
708
746
|
|
|
709
747
|
// Build truncation warning
|
|
710
748
|
const timeoutSeconds = details?.timeoutSeconds ?? renderContext?.timeout;
|
|
711
|
-
const
|
|
749
|
+
const requestedTimeoutSeconds = details?.requestedTimeoutSeconds;
|
|
750
|
+
const timeoutLabel =
|
|
712
751
|
typeof timeoutSeconds === "number"
|
|
713
|
-
?
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
752
|
+
? requestedTimeoutSeconds !== undefined && requestedTimeoutSeconds !== timeoutSeconds
|
|
753
|
+
? `Timeout: ${timeoutSeconds}s (requested ${requestedTimeoutSeconds}s clamped)`
|
|
754
|
+
: `Timeout: ${timeoutSeconds}s`
|
|
755
|
+
: undefined;
|
|
756
|
+
const timeoutLine =
|
|
757
|
+
timeoutLabel !== undefined
|
|
758
|
+
? uiTheme.fg("dim", `${uiTheme.format.bracketLeft}${timeoutLabel}${uiTheme.format.bracketRight}`)
|
|
717
759
|
: undefined;
|
|
718
760
|
let warningLine: string | undefined;
|
|
719
761
|
if (details?.meta?.truncation && !showingFullOutput) {
|
package/src/tools/browser.ts
CHANGED
|
@@ -411,7 +411,7 @@ const browserSchema = Type.Object({
|
|
|
411
411
|
value: Type.Optional(Type.String({ description: "Value to set (fill)" })),
|
|
412
412
|
attribute: Type.Optional(Type.String({ description: "Attribute name to read (get_attribute)" })),
|
|
413
413
|
key: Type.Optional(Type.String({ description: "Keyboard key to press (press)" })),
|
|
414
|
-
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds
|
|
414
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds", default: 30 })),
|
|
415
415
|
wait_until: Type.Optional(
|
|
416
416
|
StringEnum(["load", "domcontentloaded", "networkidle0", "networkidle2"], {
|
|
417
417
|
description: "Navigation wait condition (goto)",
|
package/src/tools/fetch.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { ensureTool } from "../utils/tools-manager";
|
|
|
18
18
|
import { extractWithParallel, findParallelApiKey, getParallelExtractContent } from "../web/parallel";
|
|
19
19
|
import { specialHandlers } from "../web/scrapers";
|
|
20
20
|
import type { RenderResult } from "../web/scrapers/types";
|
|
21
|
-
import { finalizeOutput, loadPage, MAX_OUTPUT_CHARS } from "../web/scrapers/types";
|
|
21
|
+
import { finalizeOutput, loadPage, looksLikeHtml, MAX_OUTPUT_CHARS } from "../web/scrapers/types";
|
|
22
22
|
import { convertWithMarkit, fetchBinary } from "../web/scrapers/utils";
|
|
23
23
|
import { applyListLimit } from "./list-limit";
|
|
24
24
|
import { formatStyledArtifactReference, type OutputMeta } from "./output-meta";
|
|
@@ -253,19 +253,6 @@ function isInlineImageMimeTypeSupported(mimeType: string): boolean {
|
|
|
253
253
|
return SUPPORTED_INLINE_IMAGE_MIME_TYPES.has(mimeType);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
/**
|
|
257
|
-
* Check if content looks like HTML
|
|
258
|
-
*/
|
|
259
|
-
function looksLikeHtml(content: string): boolean {
|
|
260
|
-
const trimmed = content.trim().toLowerCase();
|
|
261
|
-
return (
|
|
262
|
-
trimmed.startsWith("<!doctype") ||
|
|
263
|
-
trimmed.startsWith("<html") ||
|
|
264
|
-
trimmed.startsWith("<head") ||
|
|
265
|
-
trimmed.startsWith("<body")
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
256
|
/**
|
|
270
257
|
* Try fetching URL with .md appended (llms.txt convention)
|
|
271
258
|
*/
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a deduplicating recorder for relative file paths.
|
|
5
|
+
* Preserves insertion order in `list`; subsequent duplicates are ignored.
|
|
6
|
+
*/
|
|
7
|
+
export function createFileRecorder(): {
|
|
8
|
+
record: (relativePath: string) => void;
|
|
9
|
+
list: string[];
|
|
10
|
+
} {
|
|
11
|
+
const seen = new Set<string>();
|
|
12
|
+
const list: string[] = [];
|
|
13
|
+
return {
|
|
14
|
+
record(relativePath: string) {
|
|
15
|
+
if (!seen.has(relativePath)) {
|
|
16
|
+
seen.add(relativePath);
|
|
17
|
+
list.push(relativePath);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
list,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Strip a leading slash and, when the search scope is a directory, normalize
|
|
26
|
+
* Windows-style separators. For single-file scopes, fall back to the basename
|
|
27
|
+
* so tool output does not leak absolute paths.
|
|
28
|
+
*/
|
|
29
|
+
export function formatResultPath(filePath: string, isDirectory: boolean): string {
|
|
30
|
+
const cleanPath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
|
|
31
|
+
if (isDirectory) {
|
|
32
|
+
return cleanPath.replace(/\\/g, "/");
|
|
33
|
+
}
|
|
34
|
+
return path.basename(cleanPath);
|
|
35
|
+
}
|
package/src/tools/find.ts
CHANGED
|
@@ -29,9 +29,12 @@ import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
|
29
29
|
import { toolResult } from "./tool-result";
|
|
30
30
|
|
|
31
31
|
const findSchema = Type.Object({
|
|
32
|
-
pattern: Type.String({
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
pattern: Type.String({
|
|
33
|
+
description:
|
|
34
|
+
"Glob pattern including the search path (no separate path param), e.g. 'src/**/*.ts', 'lib/*.json'. Supports comma-separated lists like 'apps/,packages/,phases/'. Simple patterns like '*.ts' recurse from cwd.",
|
|
35
|
+
}),
|
|
36
|
+
hidden: Type.Optional(Type.Boolean({ description: "Include hidden files and directories", default: true })),
|
|
37
|
+
limit: Type.Optional(Type.Number({ description: "Max results", default: 1000 })),
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
export type FindToolInput = Static<typeof findSchema>;
|
|
@@ -126,6 +129,19 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
126
129
|
const includeHidden = hidden ?? true;
|
|
127
130
|
const timeoutSignal = AbortSignal.timeout(GLOB_TIMEOUT_MS);
|
|
128
131
|
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
132
|
+
const formatMatchPath = (matchPath: string, fileType?: natives.FileType): string => {
|
|
133
|
+
const hadTrailingSlash = matchPath.endsWith("/") || matchPath.endsWith("\\");
|
|
134
|
+
const absolutePath = path.isAbsolute(matchPath) ? matchPath : path.resolve(searchPath, matchPath);
|
|
135
|
+
let relativePath = path.relative(this.session.cwd, absolutePath).replace(/\\/g, "/");
|
|
136
|
+
if (relativePath.length === 0) {
|
|
137
|
+
relativePath = ".";
|
|
138
|
+
}
|
|
139
|
+
if ((fileType === natives.FileType.Dir || hadTrailingSlash) && !relativePath.endsWith("/")) {
|
|
140
|
+
relativePath += "/";
|
|
141
|
+
}
|
|
142
|
+
return relativePath;
|
|
143
|
+
};
|
|
144
|
+
|
|
129
145
|
const buildResult = (files: string[]): AgentToolResult<FindToolDetails> => {
|
|
130
146
|
if (files.length === 0) {
|
|
131
147
|
const details: FindToolDetails = { scopePath, fileCount: 0, files: [], truncated: false };
|
|
@@ -173,12 +189,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
173
189
|
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
174
190
|
limit: effectiveLimit,
|
|
175
191
|
});
|
|
176
|
-
const relativized = results.map(p =>
|
|
177
|
-
if (p.startsWith(searchPath)) {
|
|
178
|
-
return p.slice(searchPath.length + 1);
|
|
179
|
-
}
|
|
180
|
-
return path.relative(searchPath, p);
|
|
181
|
-
});
|
|
192
|
+
const relativized = results.map(p => formatMatchPath(p));
|
|
182
193
|
|
|
183
194
|
return buildResult(relativized);
|
|
184
195
|
}
|
|
@@ -222,12 +233,8 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
222
233
|
};
|
|
223
234
|
const onMatch = onUpdate
|
|
224
235
|
? (err: Error | null, match: natives.GlobMatch | null) => {
|
|
225
|
-
if (err || signal?.aborted || !match) return;
|
|
226
|
-
|
|
227
|
-
if (!relativePath) return;
|
|
228
|
-
if (match.fileType === natives.FileType.Dir && !relativePath.endsWith("/")) {
|
|
229
|
-
relativePath += "/";
|
|
230
|
-
}
|
|
236
|
+
if (err || signal?.aborted || !match?.path) return;
|
|
237
|
+
const relativePath = formatMatchPath(match.path, match.fileType);
|
|
231
238
|
onUpdateMatches.push(relativePath);
|
|
232
239
|
emitUpdate();
|
|
233
240
|
}
|
|
@@ -251,10 +258,7 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
251
258
|
);
|
|
252
259
|
|
|
253
260
|
try {
|
|
254
|
-
|
|
255
|
-
if (result.matches.length === 0 && !timeoutSignal.aborted) {
|
|
256
|
-
result = await doGlob(false);
|
|
257
|
-
}
|
|
261
|
+
const result = await doGlob(true);
|
|
258
262
|
// Sort by mtime descending (most recent first) in JS instead of native.
|
|
259
263
|
// This allows native glob to early-terminate at maxResults.
|
|
260
264
|
result.matches.sort((a, b) => (b.mtime ?? 0) - (a.mtime ?? 0));
|
|
@@ -273,19 +277,11 @@ export class FindTool implements AgentTool<typeof findSchema, FindToolDetails> {
|
|
|
273
277
|
const relativized: string[] = [];
|
|
274
278
|
for (const match of matches) {
|
|
275
279
|
throwIfAborted(signal);
|
|
276
|
-
|
|
277
|
-
if (!line) {
|
|
280
|
+
if (!match.path) {
|
|
278
281
|
continue;
|
|
279
282
|
}
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
let relativePath = line;
|
|
283
|
-
const isDirectory = match.fileType === natives.FileType.Dir;
|
|
284
|
-
if ((isDirectory || hadTrailingSlash) && !relativePath.endsWith("/")) {
|
|
285
|
-
relativePath += "/";
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
relativized.push(relativePath);
|
|
284
|
+
relativized.push(formatMatchPath(match.path, match.fileType));
|
|
289
285
|
}
|
|
290
286
|
|
|
291
287
|
return buildResult(relativized);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return the first 12 hex characters of a commit SHA, or undefined when the
|
|
3
|
+
* input is missing. Shared between GitHub tool argument normalization and the
|
|
4
|
+
* run-watch renderer.
|
|
5
|
+
*/
|
|
6
|
+
export function formatShortSha(value: string | undefined): string | undefined {
|
|
7
|
+
if (!value) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return value.slice(0, 12);
|
|
12
|
+
}
|
package/src/tools/gh-renderer.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
GhRunWatchViewDetails,
|
|
10
10
|
GhToolDetails,
|
|
11
11
|
} from "./gh";
|
|
12
|
+
import { formatShortSha } from "./gh-format";
|
|
12
13
|
import {
|
|
13
14
|
formatExpandHint,
|
|
14
15
|
formatStatusIcon,
|
|
@@ -29,14 +30,6 @@ const RUNNING_STATUSES = new Set(["in_progress"]);
|
|
|
29
30
|
const PENDING_STATUSES = new Set(["queued", "requested", "waiting", "pending"]);
|
|
30
31
|
const FALLBACK_WIDTH = 80;
|
|
31
32
|
|
|
32
|
-
function formatShortSha(value: string | undefined): string | undefined {
|
|
33
|
-
if (!value) {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return value.slice(0, 12);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
33
|
function getWatchHeader(watch: GhRunWatchViewDetails): string {
|
|
41
34
|
if (watch.mode === "run" && watch.run) {
|
|
42
35
|
if (watch.state === "watching") {
|
package/src/tools/gh.ts
CHANGED
|
@@ -14,6 +14,7 @@ import ghSearchIssuesDescription from "../prompts/tools/gh-search-issues.md" wit
|
|
|
14
14
|
import ghSearchPrsDescription from "../prompts/tools/gh-search-prs.md" with { type: "text" };
|
|
15
15
|
import * as git from "../utils/git";
|
|
16
16
|
import type { ToolSession } from ".";
|
|
17
|
+
import { formatShortSha } from "./gh-format";
|
|
17
18
|
import type { OutputMeta } from "./output-meta";
|
|
18
19
|
import { ToolError, throwIfAborted } from "./tool-errors";
|
|
19
20
|
import { toolResult } from "./tool-result";
|
|
@@ -148,7 +149,7 @@ const ghIssueViewSchema = Type.Object({
|
|
|
148
149
|
repo: Type.Optional(
|
|
149
150
|
Type.String({ description: "Repository in OWNER/REPO format. Omit when passing a full issue URL." }),
|
|
150
151
|
),
|
|
151
|
-
comments: Type.Optional(Type.Boolean({ description: "Include issue comments
|
|
152
|
+
comments: Type.Optional(Type.Boolean({ description: "Include issue comments.", default: true })),
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
const ghPrViewSchema = Type.Object({
|
|
@@ -161,7 +162,7 @@ const ghPrViewSchema = Type.Object({
|
|
|
161
162
|
repo: Type.Optional(
|
|
162
163
|
Type.String({ description: "Repository in OWNER/REPO format. Omit when passing a full pull request URL." }),
|
|
163
164
|
),
|
|
164
|
-
comments: Type.Optional(Type.Boolean({ description: "Include pull request comments
|
|
165
|
+
comments: Type.Optional(Type.Boolean({ description: "Include pull request comments.", default: true })),
|
|
165
166
|
});
|
|
166
167
|
|
|
167
168
|
const ghPrDiffSchema = Type.Object({
|
|
@@ -217,13 +218,13 @@ const ghPrPushSchema = Type.Object({
|
|
|
217
218
|
const ghSearchIssuesSchema = Type.Object({
|
|
218
219
|
query: Type.String({ description: "GitHub issue search query. Supports GitHub search syntax." }),
|
|
219
220
|
repo: Type.Optional(Type.String({ description: "Repository in OWNER/REPO format to scope the search." })),
|
|
220
|
-
limit: Type.Optional(Type.Number({ description: "Maximum results to return (
|
|
221
|
+
limit: Type.Optional(Type.Number({ description: "Maximum results to return (max: 50).", default: 10 })),
|
|
221
222
|
});
|
|
222
223
|
|
|
223
224
|
const ghSearchPrsSchema = Type.Object({
|
|
224
225
|
query: Type.String({ description: "GitHub pull request search query. Supports GitHub search syntax." }),
|
|
225
226
|
repo: Type.Optional(Type.String({ description: "Repository in OWNER/REPO format to scope the search." })),
|
|
226
|
-
limit: Type.Optional(Type.Number({ description: "Maximum results to return (
|
|
227
|
+
limit: Type.Optional(Type.Number({ description: "Maximum results to return (max: 50).", default: 10 })),
|
|
227
228
|
});
|
|
228
229
|
|
|
229
230
|
const ghRunWatchSchema = Type.Object({
|
|
@@ -239,7 +240,7 @@ const ghRunWatchSchema = Type.Object({
|
|
|
239
240
|
}),
|
|
240
241
|
),
|
|
241
242
|
tail: Type.Optional(
|
|
242
|
-
Type.Number({ description: "Number of log lines to include per failed job (
|
|
243
|
+
Type.Number({ description: "Number of log lines to include per failed job (max: 200).", default: 15 }),
|
|
243
244
|
),
|
|
244
245
|
});
|
|
245
246
|
|
|
@@ -545,14 +546,6 @@ function normalizeOptionalString(value: string | null | undefined): string | und
|
|
|
545
546
|
return normalized ? normalized : undefined;
|
|
546
547
|
}
|
|
547
548
|
|
|
548
|
-
function formatShortSha(value: string | undefined): string | undefined {
|
|
549
|
-
if (!value) {
|
|
550
|
-
return undefined;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return value.slice(0, 12);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
549
|
function requireNonEmpty(value: string | null | undefined, label: string): string {
|
|
557
550
|
const normalized = normalizeOptionalString(value);
|
|
558
551
|
if (!normalized) {
|