@oh-my-pi/pi-coding-agent 15.10.2 → 15.10.3
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 +46 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +7 -1
- package/dist/types/edit/index.d.ts +0 -1
- package/dist/types/lsp/index.d.ts +0 -5
- package/dist/types/main.d.ts +11 -0
- package/dist/types/modes/components/assistant-message.d.ts +0 -9
- package/dist/types/modes/components/late-diagnostics-message.d.ts +20 -0
- package/dist/types/modes/components/read-tool-group.d.ts +6 -0
- package/dist/types/modes/components/session-selector.d.ts +16 -7
- package/dist/types/modes/components/tool-execution.d.ts +0 -18
- package/dist/types/modes/types.d.ts +4 -0
- package/dist/types/session/messages.d.ts +11 -8
- package/dist/types/session/yield-queue.d.ts +10 -1
- package/dist/types/tools/eval-render.d.ts +0 -1
- package/dist/types/tools/index.d.ts +31 -0
- package/dist/types/tools/path-utils.d.ts +5 -1
- package/dist/types/tools/read.d.ts +2 -1
- package/dist/types/tools/render-utils.d.ts +3 -1
- package/dist/types/tools/renderers.d.ts +0 -15
- package/dist/types/tools/write.d.ts +0 -2
- package/dist/types/tui/code-cell.d.ts +0 -2
- package/dist/types/tui/hyperlink.d.ts +5 -7
- package/dist/types/tui/output-block.d.ts +0 -18
- package/package.json +9 -9
- package/src/cli/gallery-cli.ts +4 -0
- package/src/cli/gallery-fixtures/codeintel.ts +0 -1
- package/src/cli/gallery-fixtures/fs.ts +68 -1
- package/src/cli/gallery-fixtures/types.ts +8 -1
- package/src/commit/agentic/agent.ts +1 -0
- package/src/edit/hashline/diff.ts +86 -0
- package/src/edit/hashline/execute.ts +14 -1
- package/src/edit/index.ts +31 -17
- package/src/edit/renderer.ts +116 -31
- package/src/eval/js/shared/prelude.txt +26 -10
- package/src/internal-urls/docs-index.generated.ts +4 -4
- package/src/lsp/index.ts +128 -52
- package/src/main.ts +54 -14
- package/src/modes/components/assistant-message.ts +3 -15
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/plan-review-overlay.ts +26 -5
- package/src/modes/components/read-tool-group.ts +415 -35
- package/src/modes/components/session-selector.ts +89 -35
- package/src/modes/components/tool-execution.ts +7 -49
- package/src/modes/components/transcript-container.ts +108 -32
- package/src/modes/controllers/event-controller.ts +6 -1
- package/src/modes/controllers/input-controller.ts +10 -2
- package/src/modes/types.ts +4 -0
- package/src/modes/utils/ui-helpers.ts +26 -5
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/plan-mode-active.md +56 -72
- package/src/prompts/tools/eval.md +3 -1
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/sdk.ts +59 -1
- package/src/session/agent-session.ts +5 -3
- package/src/session/messages.ts +21 -14
- package/src/session/session-manager.ts +2 -2
- package/src/session/yield-queue.ts +20 -2
- package/src/task/executor.ts +1 -0
- package/src/tiny/title-client.ts +6 -1
- package/src/tools/bash.ts +0 -7
- package/src/tools/eval-render.ts +4 -23
- package/src/tools/find.ts +148 -106
- package/src/tools/index.ts +32 -0
- package/src/tools/path-utils.ts +19 -22
- package/src/tools/read.ts +16 -8
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/renderers.ts +0 -15
- package/src/tools/ssh.ts +0 -1
- package/src/tools/todo.ts +1 -0
- package/src/tools/write.ts +3 -12
- package/src/tui/code-cell.ts +1 -6
- package/src/tui/hyperlink.ts +13 -23
- package/src/tui/output-block.ts +2 -97
package/src/tui/hyperlink.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* sequences when the active terminal supports hyperlinks and the user setting
|
|
6
6
|
* permits it. Falls back to plain text when disabled.
|
|
7
7
|
*/
|
|
8
|
+
import * as url from "node:url";
|
|
8
9
|
import { TERMINAL } from "@oh-my-pi/pi-tui";
|
|
9
10
|
import { settings } from "../config/settings";
|
|
10
11
|
import {
|
|
@@ -28,21 +29,12 @@ function buildLinkId(uri: string): string {
|
|
|
28
29
|
return (h >>> 0).toString(16).padStart(8, "0");
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
/** Build a `file://` URI
|
|
32
|
-
function buildFileUri(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const encoded = normalized
|
|
38
|
-
.split("/")
|
|
39
|
-
.map(segment => encodeURIComponent(segment))
|
|
40
|
-
.join("/");
|
|
41
|
-
const params: string[] = [];
|
|
42
|
-
if (opts?.line !== undefined) params.push(`line=${opts.line}`);
|
|
43
|
-
if (opts?.col !== undefined) params.push(`col=${opts.col}`);
|
|
44
|
-
const query = params.length > 0 ? `?${params.join("&")}` : "";
|
|
45
|
-
return `${prefix}${encoded}${query}`;
|
|
32
|
+
/** Build a properly encoded `file://` URI with optional line/col query params. */
|
|
33
|
+
function buildFileUri(filePath: string, opts?: { line?: number; col?: number }): string {
|
|
34
|
+
const uri = url.pathToFileURL(filePath);
|
|
35
|
+
if (opts?.line !== undefined) uri.searchParams.set("line", String(opts.line));
|
|
36
|
+
if (opts?.col !== undefined) uri.searchParams.set("col", String(opts.col));
|
|
37
|
+
return uri.href;
|
|
46
38
|
}
|
|
47
39
|
|
|
48
40
|
/**
|
|
@@ -104,21 +96,19 @@ export function urlHyperlink(url: string, displayText: string): string {
|
|
|
104
96
|
}
|
|
105
97
|
|
|
106
98
|
/**
|
|
107
|
-
* Wrap `displayText` in an OSC 8 hyperlink pointing at
|
|
99
|
+
* Wrap `displayText` in an OSC 8 hyperlink pointing at a filesystem path.
|
|
108
100
|
*
|
|
109
101
|
* Returns `displayText` unchanged when hyperlinks are disabled or when
|
|
110
102
|
* the text already contains an OSC 8 sequence (prevents double-wrapping).
|
|
103
|
+
* Relative paths resolve against the current working directory before URI
|
|
104
|
+
* encoding so the OSC 8 target is always a valid `file://` URL.
|
|
111
105
|
*
|
|
112
|
-
*
|
|
113
|
-
* produce invalid `file://` URIs and are accepted silently to avoid runtime
|
|
114
|
-
* errors in renderer hot paths.
|
|
115
|
-
*
|
|
116
|
-
* @param absPath - Absolute filesystem path
|
|
106
|
+
* @param filePath - Filesystem path
|
|
117
107
|
* @param displayText - Text to render as the hyperlink anchor (may contain ANSI codes)
|
|
118
108
|
* @param opts - Optional line/col position appended as `?line=N&col=M` query params
|
|
119
109
|
*/
|
|
120
|
-
export function fileHyperlink(
|
|
121
|
-
return wrapHyperlink(buildFileUri(
|
|
110
|
+
export function fileHyperlink(filePath: string, displayText: string, opts?: { line?: number; col?: number }): string {
|
|
111
|
+
return wrapHyperlink(buildFileUri(filePath, opts), displayText);
|
|
122
112
|
}
|
|
123
113
|
|
|
124
114
|
/**
|
package/src/tui/output-block.ts
CHANGED
|
@@ -17,8 +17,6 @@ export interface OutputBlockOptions {
|
|
|
17
17
|
width: number;
|
|
18
18
|
applyBg?: boolean;
|
|
19
19
|
contentPaddingLeft?: number;
|
|
20
|
-
/** Animate the border with a sweeping dark segment (pending/running state). */
|
|
21
|
-
animate?: boolean;
|
|
22
20
|
/** Override the state-derived border color. Used for muted "legacy" tool
|
|
23
21
|
* frames that should not visually compete with framed-output tools. */
|
|
24
22
|
borderColor?: ThemeColor;
|
|
@@ -37,59 +35,6 @@ export function isFramedBlockComponent(component: Component): boolean {
|
|
|
37
35
|
return (component as FramedBlockComponent)[FRAMED_BLOCK_COMPONENT] === true;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
const BORDER_SHIMMER_TICK_MS = 1000 / 30;
|
|
41
|
-
/** Duration of one full left↔right↔left bounce of the bottom-edge segment, in
|
|
42
|
-
* ms. Position is derived from the wall clock against this fixed cycle so a
|
|
43
|
-
* resize only nudges the segment proportionally instead of teleporting it. */
|
|
44
|
-
const BORDER_BOUNCE_MS = 3000;
|
|
45
|
-
/** Length, in border cells, of the moving segment. */
|
|
46
|
-
const BORDER_SEGMENT_LEN = 8;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Monotonic frame counter for animated borders, quantized to the TUI's ~30fps
|
|
50
|
-
* render cap so the cache key advances once per animation frame — fine enough
|
|
51
|
-
* for a smooth segment sweep, coarse enough to coalesce multiple render passes
|
|
52
|
-
* land inside the same frame.
|
|
53
|
-
*/
|
|
54
|
-
export function borderShimmerTick(): number {
|
|
55
|
-
return Math.floor(Date.now() / BORDER_SHIMMER_TICK_MS);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Ease-in-out so the segment decelerates into and accelerates out of each wall. */
|
|
59
|
-
function easeInOutQuad(t: number): number {
|
|
60
|
-
return t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Column of the travelling segment's center on the bottom edge for a box of
|
|
65
|
-
* inner width `W` at time `now`. The segment bounces left → right → left across
|
|
66
|
-
* the bottom border: a triangle wave over one full there-and-back cycle, eased
|
|
67
|
-
* per leg so it slows as it nears each wall before reversing. Position is
|
|
68
|
-
* derived from the wall clock against a fixed cycle, so a resize shifts the
|
|
69
|
-
* center proportionally — no reset.
|
|
70
|
-
*/
|
|
71
|
-
export function borderSegmentHeadCol(W: number, now: number): number {
|
|
72
|
-
if (W <= 1) return 0;
|
|
73
|
-
const phase = (((now % BORDER_BOUNCE_MS) + BORDER_BOUNCE_MS) % BORDER_BOUNCE_MS) / BORDER_BOUNCE_MS;
|
|
74
|
-
// Triangle: 0→1 rightward over the first half, 1→0 leftward over the second.
|
|
75
|
-
const leg = phase < 0.5 ? phase * 2 : 2 - phase * 2;
|
|
76
|
-
return easeInOutQuad(leg) * (W - 1);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Scale a truecolor foreground escape toward black by `factor`. Returns
|
|
81
|
-
* undefined for 256-color escapes (no RGB to scale) so callers fall back to a
|
|
82
|
-
* dimmer theme color.
|
|
83
|
-
*/
|
|
84
|
-
function darkenFgAnsi(ansi: string, factor: number): string | undefined {
|
|
85
|
-
const m = /38;2;(\d+);(\d+);(\d+)/.exec(ansi);
|
|
86
|
-
if (!m) return undefined;
|
|
87
|
-
const r = Math.round(Number(m[1]) * factor);
|
|
88
|
-
const g = Math.round(Number(m[2]) * factor);
|
|
89
|
-
const b = Math.round(Number(m[3]) * factor);
|
|
90
|
-
return `\x1b[38;2;${r};${g};${b}m`;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
38
|
type BlockRow =
|
|
94
39
|
| { kind: "bar"; leftChar: string; rightChar: string; label?: string; meta?: string }
|
|
95
40
|
| { kind: "bottom"; leftChar: string; rightChar: string }
|
|
@@ -135,8 +80,7 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
135
80
|
const contentWidth = Math.max(0, lineWidth - visibleWidth(v) - contentPaddingLeft - visibleWidth(v));
|
|
136
81
|
const contentLeftPadding = contentPaddingLeft > 0 ? padding(contentPaddingLeft) : "";
|
|
137
82
|
|
|
138
|
-
// ── Layout pass: collect row descriptors
|
|
139
|
-
// known before the moving segment is positioned. ──
|
|
83
|
+
// ── Layout pass: collect row descriptors before emitting the bordered lines. ──
|
|
140
84
|
const rows: BlockRow[] = [];
|
|
141
85
|
rows.push({
|
|
142
86
|
kind: "bar",
|
|
@@ -185,39 +129,6 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
185
129
|
rows.push({ kind: "bottom", leftChar: theme.boxSharp.bottomLeft, rightChar: theme.boxSharp.bottomRight });
|
|
186
130
|
|
|
187
131
|
const H = rows.length;
|
|
188
|
-
const W = lineWidth;
|
|
189
|
-
const animate = (options.animate ?? false) && (state === "running" || state === "pending") && W >= 2 && H >= 2;
|
|
190
|
-
|
|
191
|
-
// ── Segment geometry: one dark run bounces left ↔ right along the bottom
|
|
192
|
-
// edge only. The top, interior separators, and side borders stay the flat
|
|
193
|
-
// accent color. ──
|
|
194
|
-
const segLen = animate ? Math.min(BORDER_SEGMENT_LEN, W) : 0;
|
|
195
|
-
const head = animate ? borderSegmentHeadCol(W, Date.now()) : 0;
|
|
196
|
-
const segHalf = segLen / 2;
|
|
197
|
-
const segAnsi = animate ? (darkenFgAnsi(theme.getFgAnsi(borderColor), 0.4) ?? theme.getFgAnsi("borderMuted")) : "";
|
|
198
|
-
const seg = (text: string) => `${segAnsi}${text}\x1b[39m`;
|
|
199
|
-
|
|
200
|
-
// A bottom-edge column is lit when it lies within half a segment of the
|
|
201
|
-
// travelling center.
|
|
202
|
-
const isLit = (col: number): boolean => Math.abs(col - head) < segHalf;
|
|
203
|
-
// Color a run of bottom-edge glyphs starting at column `startCol`, grouping
|
|
204
|
-
// consecutive same-state cells so each run emits a single escape pair.
|
|
205
|
-
const colorEdge = (glyphs: string, startCol: number): string => {
|
|
206
|
-
let out = "";
|
|
207
|
-
let runLit: boolean | null = null;
|
|
208
|
-
let buf = "";
|
|
209
|
-
for (let i = 0; i < glyphs.length; i++) {
|
|
210
|
-
const lit = isLit(startCol + i);
|
|
211
|
-
if (lit !== runLit) {
|
|
212
|
-
if (runLit !== null) out += (runLit ? seg : border)(buf);
|
|
213
|
-
buf = "";
|
|
214
|
-
runLit = lit;
|
|
215
|
-
}
|
|
216
|
-
buf += glyphs[i];
|
|
217
|
-
}
|
|
218
|
-
if (runLit !== null) out += (runLit ? seg : border)(buf);
|
|
219
|
-
return out;
|
|
220
|
-
};
|
|
221
132
|
|
|
222
133
|
const renderBar = (row: { leftChar: string; rightChar: string; label?: string; meta?: string }): string => {
|
|
223
134
|
const leftGlyphs = `${row.leftChar}${cap}`;
|
|
@@ -245,11 +156,7 @@ export function renderOutputBlock(options: OutputBlockOptions, theme: Theme): st
|
|
|
245
156
|
const rightGlyph = row.rightChar;
|
|
246
157
|
const fillCount = Math.max(0, lineWidth - visibleWidth(leftGlyphs) - visibleWidth(rightGlyph));
|
|
247
158
|
const fillGlyphs = h.repeat(fillCount);
|
|
248
|
-
|
|
249
|
-
const leftStr = colorEdge(leftGlyphs, 0);
|
|
250
|
-
const fillStr = colorEdge(fillGlyphs, visibleWidth(leftGlyphs));
|
|
251
|
-
const rightStr = colorEdge(rightGlyph, lineWidth - visibleWidth(rightGlyph));
|
|
252
|
-
return `${leftStr}${fillStr}${rightStr}`;
|
|
159
|
+
return `${border(leftGlyphs)}${border(fillGlyphs)}${border(rightGlyph)}`;
|
|
253
160
|
};
|
|
254
161
|
|
|
255
162
|
const renderContent = (inner: string): string => `${border(v)}${contentLeftPadding}${inner}${border(v)}`;
|
|
@@ -302,8 +209,6 @@ export class CachedOutputBlock {
|
|
|
302
209
|
h.optional(options.state);
|
|
303
210
|
h.optional(options.borderColor);
|
|
304
211
|
h.bool(options.applyBg ?? true);
|
|
305
|
-
h.bool(options.animate ?? false);
|
|
306
|
-
if (options.animate) h.u32(borderShimmerTick());
|
|
307
212
|
if (options.sections) {
|
|
308
213
|
for (const s of options.sections) {
|
|
309
214
|
h.optional(s.label);
|